diff --git a/src/camera/mod.rs b/src/camera/mod.rs index c0e0c0b..4ec8440 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -1,18 +1,18 @@ -mod perspective; mod orthographic; -mod spherical; +mod perspective; mod realistic; +mod spherical; -pub use perspective::PerspectiveCamera; pub use orthographic::OrthographicCamera; -pub use spherical::SphericalCamera; +pub use perspective::PerspectiveCamera; pub use realistic::RealisticCamera; +pub use spherical::SphericalCamera; use crate::core::film::Film; use crate::core::medium::Medium; -use crate::core::pbrt::{Float, lerp, RenderingCoordinateSystem}; +use crate::core::pbrt::{Float, RenderingCoordinateSystem, lerp}; use crate::core::sampler::CameraSample; -use crate::utils::geometry::{Ray, RayDifferential, Vector3f, Point3f, Normal3f, Dot, Normed}; +use crate::geometry::{Normal3f, Point3f, Ray, RayDifferential, Vector3f, VectorLike}; use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; use crate::utils::transform::{AnimatedTransform, Transform}; @@ -30,7 +30,10 @@ pub struct CameraTransform { } impl CameraTransform { - pub fn from_world(world_from_camera: AnimatedTransform, rendering_space: RenderingCoordinateSystem) -> Self { + 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.; @@ -44,10 +47,20 @@ impl CameraTransform { 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 } - + 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, + ); + Self { + render_from_camera, + world_from_render, + } } pub fn render_from_camera(&self, p: Point3f, time: Float) -> Point3f { @@ -90,7 +103,11 @@ pub struct CameraBase { 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 { + 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, @@ -105,8 +122,10 @@ pub trait CameraTrait { 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; + 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; } @@ -117,50 +136,81 @@ pub trait CameraTrait { 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; + 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 + break; } } - if rx_found && ry_found { + if rx_found && ry_found { central_cam_ray.ray.differential = Some(rd); } Some(central_cam_ray) } - fn sample_time(&self, u: Float) -> Float { + 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) { + 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 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 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 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 n_down = Vector3f::from(n_down_z); + let tx = -(n_down.dot(y_ray.o.into())) / n_down.dot(x_ray.d); + let ty = -(n_down.dot(x_ray.o.into()) - d) / n_down.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); + *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) + self.base() + .camera_transform + .render_from_camera_ray(r, t_max) } - } pub enum Camera { @@ -188,7 +238,11 @@ impl CameraTrait for Camera { } } - fn generate_ray_differential(&self, sample: CameraSample, lambda: &SampledWavelengths) -> Option { + 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), @@ -205,7 +259,7 @@ pub struct LensElementInterface { pub aperture_radius: Float, } -struct ExitPupilSample { +pub struct ExitPupilSample { pub p_pupil: Point3f, pub pdf: Float, } diff --git a/src/camera/orthographic.rs b/src/camera/orthographic.rs index 7560021..7b3b78e 100644 --- a/src/camera/orthographic.rs +++ b/src/camera/orthographic.rs @@ -1,10 +1,13 @@ 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::core::pbrt::Float; +use crate::core::sampler::CameraSample; +use crate::geometry::{ + Bounds2f, Point2f, Point3f, Ray, RayDifferential, Vector2f, Vector3f, VectorLike, +}; +use crate::utils::sampling::sample_uniform_disk_concentric; use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; +use crate::utils::transform::Transform; pub struct OrthographicCamera { pub base: CameraBase, @@ -12,19 +15,33 @@ pub struct OrthographicCamera { pub camera_from_raster: Transform, pub raster_from_screen: Transform, pub screen_from_raster: Transform, - pub lens_radius: Float, + 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.); + 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.); @@ -36,25 +53,45 @@ impl OrthographicCamera { 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 } + 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 { + 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()); + 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); + let p_lens_vec = + self.lens_radius * Vector2f::from(sample_uniform_disk_concentric(sample.p_lens)); + let p_lens = Point2f::from(p_lens_vec); // Compute point on plane of focus let ft = self.focal_distance / ray.d.z(); @@ -67,10 +104,17 @@ impl CameraTrait for OrthographicCamera { let camera_ray = self.render_from_camera(&ray, &mut None); - Some(CameraRay{ray: camera_ray, weight: SampledSpectrum::default()}) + Some(CameraRay { + ray: camera_ray, + weight: SampledSpectrum::default(), + }) } - fn generate_ray_differential(&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, @@ -81,8 +125,14 @@ impl CameraTrait for OrthographicCamera { 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); + 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; diff --git a/src/camera/perspective.rs b/src/camera/perspective.rs index e27d99d..27365dc 100644 --- a/src/camera/perspective.rs +++ b/src/camera/perspective.rs @@ -1,10 +1,13 @@ -use super::{CameraRay, CameraBase, CameraTrait}; +use super::{CameraBase, CameraRay, 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::pbrt::Float; use crate::core::sampler::CameraSample; -use crate::utils::geometry::{Point2f, Point3f, Bounds2f, Vector3f, Normed, Ray, RayDifferential}; +use crate::geometry::{ + Bounds2f, Point2f, Point3f, Ray, RayDifferential, Vector2f, Vector3f, VectorLike, +}; +use crate::utils::sampling::sample_uniform_disk_concentric; use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; use crate::utils::transform::Transform; @@ -14,7 +17,7 @@ pub struct PerspectiveCamera { pub camera_from_raster: Transform, pub raster_from_screen: Transform, pub screen_from_raster: Transform, - pub lens_radius: Float, + pub lens_radius: Float, pub focal_distance: Float, pub dx_camera: Vector3f, pub dy_camera: Vector3f, @@ -22,27 +25,46 @@ pub struct PerspectiveCamera { } 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.); + 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 camera_from_raster = screen_from_camera.inverse() * screen_from_raster.clone(); + 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 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, + Self { + base, + screen_from_camera: screen_from_camera.clone(), + camera_from_raster, + raster_from_screen, + screen_from_raster, + lens_radius, focal_distance, dx_camera, dy_camera, @@ -55,17 +77,27 @@ impl CameraTrait for PerspectiveCamera { fn base(&self) -> &CameraBase { &self.base } - fn generate_ray(&self, sample: CameraSample, _lambda: &SampledWavelengths) -> Option { + 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()); + 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); + let p_lens = + self.lens_radius * Vector2f::from(sample_uniform_disk_concentric(sample.p_lens)); // Compute point on plane of focus let ft = self.focal_distance / r.d.z(); @@ -77,6 +109,9 @@ impl CameraTrait for PerspectiveCamera { } let ray = self.render_from_camera(&r, &mut None); - Some(CameraRay{ray, weight: SampledSpectrum::default()}) + Some(CameraRay { + ray, + weight: SampledSpectrum::default(), + }) } } diff --git a/src/camera/realistic.rs b/src/camera/realistic.rs index 427f165..8e37c86 100644 --- a/src/camera/realistic.rs +++ b/src/camera/realistic.rs @@ -1,35 +1,41 @@ -use super::{CameraBase, LensElementInterface, CameraRay, CameraTrait, ExitPupilSample}; +use super::{CameraBase, CameraRay, CameraTrait, ExitPupilSample, LensElementInterface}; use crate::core::film::FilmTrait; -use crate::core::pbrt::{Float, square, lerp}; +use crate::core::pbrt::{Float, 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::geometry::{ + Bounds2f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2i, Vector3f, VectorLike, +}; +use crate::utils::image::Image; +use crate::utils::math::{quadratic, square}; use crate::utils::scattering::refract; -use crate::utils::spectrum::{SampledWavelengths, SampledSpectrum}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; pub struct RealisticCamera { base: CameraBase, focus_distance: Float, set_aperture_diameter: Float, - // aperture_image: Image, + 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, + 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; + aperture_image: Image, + ) -> 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 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) { @@ -46,23 +52,38 @@ impl RealisticCamera { aperture_diameter = set_aperture_diameter; } } - let el_int = LensElementInterface { curvature_radius, thickness, eta, aperture_radius: aperture_diameter / 2.0 }; + 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 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 } + Self { + base, + focus_distance, + element_interface, + physical_extent, + set_aperture_diameter, + aperture_image, + exit_pupil_bounds, + } } - pub fn lens_rear_z(&self) -> Float { self.element_interface.last().unwrap().thickness } + 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.; @@ -72,48 +93,66 @@ impl RealisticCamera { z_sum } - pub fn rear_element_radius(&self) -> Float { self.element_interface.last().unwrap().aperture_radius } + 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 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 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 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()); + 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) { + 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); + 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)> { + 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 b = 2.0 * ray.d.dot(o.into()); let c = Vector3f::from(o).norm_squared() - radius * radius; let (t0, t1) = quadratic(a, b, c)?; @@ -128,17 +167,23 @@ impl RealisticCamera { 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); + 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 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); + 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]; @@ -155,7 +200,9 @@ impl RealisticCamera { // 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) { + if let Some((intersect_t, intersect_n)) = + RealisticCamera::intersect_spherical_element(radius, z_center, &r_lens) + { t = intersect_t; n = intersect_n; } else { @@ -178,7 +225,11 @@ impl RealisticCamera { // 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 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; @@ -193,8 +244,12 @@ impl RealisticCamera { } // 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); + 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)) } @@ -202,7 +257,8 @@ impl RealisticCamera { 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(); + 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]; @@ -215,12 +271,23 @@ impl RealisticCamera { 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()); + 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}) + Some(ExitPupilSample { p_pupil, pdf }) } } @@ -229,10 +296,16 @@ impl CameraTrait for RealisticCamera { &self.base } - fn generate_ray(&self, sample: CameraSample, _lambda: &SampledWavelengths) -> Option { + 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 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.); @@ -243,7 +316,7 @@ impl CameraTrait for RealisticCamera { 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 + return None; } // Finish initialization of _RealisticCamera_ ray @@ -254,8 +327,12 @@ impl CameraTrait for RealisticCamera { // 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())); + 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)}) + Some(CameraRay { + ray, + weight: SampledSpectrum::new(final_weight), + }) } } diff --git a/src/camera/spherical.rs b/src/camera/spherical.rs index ba473c4..5d07696 100644 --- a/src/camera/spherical.rs +++ b/src/camera/spherical.rs @@ -1,10 +1,10 @@ -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 super::{CameraBase, CameraRay, CameraTrait}; use crate::core::film::FilmTrait; use crate::core::pbrt::{Float, PI}; use crate::core::sampler::CameraSample; +use crate::geometry::{Bounds2f, Point2f, Point3f, Ray, Vector3f, spherical_direction}; +use crate::utils::math::{equal_area_square_to_sphere, wrap_equal_area_square}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; #[derive(PartialEq)] pub struct EquiRectangularMapping; @@ -17,7 +17,7 @@ pub enum Mapping { pub struct SphericalCamera { pub base: CameraBase, pub screen: Bounds2f, - pub lens_radius: Float, + pub lens_radius: Float, pub focal_distance: Float, pub mapping: Mapping, } @@ -26,18 +26,23 @@ impl CameraTrait for SphericalCamera { fn base(&self) -> &CameraBase { &self.base } - - fn generate_ray(&self, sample: CameraSample, _lamdba: &SampledWavelengths) -> Option { + + 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 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); @@ -45,7 +50,15 @@ impl CameraTrait for SphericalCamera { } 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() }) + 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 index 894fce6..f4098de 100644 --- a/src/core/bxdf.rs +++ b/src/core/bxdf.rs @@ -1,11 +1,25 @@ use bitflags::bitflags; +use rand::distr::uniform::SampleUniform; +use std::any::Any; use std::fmt; use std::ops::Not; +use std::sync::{Arc, RwLock}; -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}; +use crate::core::pbrt::{Float, INV_PI, PI, PI_OVER_2}; +use crate::geometry::{ + Frame, Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, cos_theta, same_hemisphere, + spherical_direction, spherical_theta, +}; +use crate::utils::math::square; +use crate::utils::sampling::{ + PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere, sample_uniform_hemisphere, + uniform_hemisphere_pdf, +}; +use crate::utils::scattering::{ + TrowbridgeReitzDistribution, fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect, + refract, +}; +use crate::utils::spectrum::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths}; bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -19,20 +33,20 @@ bitflags! { bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BxDFFlags: u8 { + const UNSET = 0; 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(); } @@ -40,17 +54,29 @@ bitflags! { impl BxDFFlags { #[inline] - pub fn is_reflective(&self) -> bool { self.contains(Self::REFLECTION) } + pub fn is_reflective(&self) -> bool { + self.contains(Self::REFLECTION) + } #[inline] - pub fn is_transmissive(&self) -> bool { self.contains(Self::TRANSMISSION) } + pub fn is_transmissive(&self) -> bool { + self.contains(Self::TRANSMISSION) + } #[inline] - pub fn is_diffuse(&self) -> bool { self.contains(Self::DIFFUSE) } + pub fn is_diffuse(&self) -> bool { + self.contains(Self::DIFFUSE) + } #[inline] - pub fn is_glossy(&self) -> bool { self.contains(Self::GLOSSY) } + pub fn is_glossy(&self) -> bool { + self.contains(Self::GLOSSY) + } #[inline] - pub fn is_specular(&self) -> bool { self.contains(Self::SPECULAR) } + pub fn is_specular(&self) -> bool { + self.contains(Self::SPECULAR) + } #[inline] - pub fn is_non_specular(&self) -> bool { self.intersects(Self::DIFFUSE | Self::GLOSSY) } + pub fn is_non_specular(&self) -> bool { + self.intersects(Self::DIFFUSE | Self::GLOSSY) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -81,10 +107,21 @@ pub struct BSDFSample { impl BSDFSample { pub fn new( - f: SampledSpectrum, wi: Vector3f, pdf: Float, flags: BxDFFlags, - eta: Float, pdf_is_proportional: bool + f: SampledSpectrum, + wi: Vector3f, + pdf: Float, + flags: BxDFFlags, + eta: Float, + pdf_is_proportional: bool, ) -> Self { - Self { f, wi, pdf, flags, eta, pdf_is_proportional } + Self { + f, + wi, + pdf, + flags, + eta, + pdf_is_proportional, + } } pub fn default() -> Self { @@ -97,43 +134,159 @@ impl BSDFSample { 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() } + + #[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; +#[derive(Debug, Clone)] +pub struct DiffuseBxDF { + pub r: SampledSpectrum, +} -pub trait BxDFTrait { +#[derive(Debug, Clone)] +pub struct DiffuseTransmissionBxDF; + +#[derive(Debug, Clone)] +pub struct DielectricBxDF { + pub eta: Float, + pub mf_distrib: TrowbridgeReitzDistribution, +} + +impl DielectricBxDF { + pub fn new(eta: Float, mf_distrib: TrowbridgeReitzDistribution) -> Self { + Self { eta, mf_distrib } + } +} + +#[derive(Debug, Clone)] +pub struct ThinDielectricBxDF { + pub eta: Float, +} + +impl ThinDielectricBxDF { + pub fn new(eta: Float) -> Self { + Self { eta } + } +} + +#[derive(Debug, Clone)] +pub struct CoatedDiffuseBxDF; + +#[derive(Debug, Clone)] +pub struct CoatedConductorBxDF; + +#[derive(Debug, Clone)] +pub struct HairBxDF; + +#[derive(Debug, Clone)] +pub struct MeasuredBxDFData { + wavelengths: Vec, + spectra: PiecewiseLinear2D<3>, + ndf: PiecewiseLinear2D<0>, + vndf: PiecewiseLinear2D<2>, + sigma: PiecewiseLinear2D<0>, + isotropic: bool, + luminance: PiecewiseLinear2D<2>, +} + +#[derive(Debug, Clone)] +pub struct MeasuredBxDF { + brdf: MeasuredBxDFData, + lambda: SampledWavelengths, +} + +impl MeasuredBxDF { + pub fn new(brdf: MeasuredBxDFData, lambda: &SampledWavelengths) -> Self { + Self { + brdf, + lambda: *lambda, + } + } + + pub fn theta2u(theta: Float) -> Float { + (theta * (2. / PI)).sqrt() + } + pub fn phi2u(phi: Float) -> Float { + phi * 1. / (2. * PI) + 0.5 + } + pub fn u2theta(u: Float) -> Float { + square(u) * PI_OVER_2 + } + pub fn u2phi(u: Float) -> Float { + (2. * u - 1.) * PI + } +} + +#[derive(Debug, Clone)] +pub struct ConductorBxDF { + mf_distrib: TrowbridgeReitzDistribution, + eta: SampledSpectrum, + k: SampledSpectrum, +} + +impl ConductorBxDF { + pub fn new( + mf_distrib: &TrowbridgeReitzDistribution, + eta: SampledSpectrum, + k: SampledSpectrum, + ) -> Self { + Self { + mf_distrib: *mf_distrib, + eta, + k, + } + } +} + +#[derive(Debug, Clone)] +pub struct NormalizedFresnelBxDF; + +pub struct FArgs { + pub mode: TransportMode, + pub sample_flags: BxDFReflTransFlags, +} + +impl Default for FArgs { + fn default() -> Self { + Self { + mode: TransportMode::Radiance, + sample_flags: BxDFReflTransFlags::ALL, + } + } +} + +pub trait BxDFTrait: Any + Send + Sync + std::fmt::Debug { 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 f(&self, wo: &Vector3f, wi: &Vector3f, mode: TransportMode) -> SampledSpectrum; + + fn sample_f(&self, wo: &Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option; + + fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float; - 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)) { + if let Some(bs) = self.sample_f(&wo, uc[i], u2[i], FArgs::default()) { r += bs.f * abs_cos_theta(bs.wi) / bs.pdf; } } @@ -145,69 +298,951 @@ pub trait BxDFTrait { for i in 0..uc.len() { let wo = sample_uniform_hemisphere(u1[i]); if wo.z() == 0. { - continue + continue; } let pdfo = uniform_hemisphere_pdf(); - if let Some(bs) = self.sample_f(wo, uc[i], u2[i], Some(TransportMode::Radiance), Some(BxDFReflTransFlags::ALL)) { + if let Some(bs) = self.sample_f(&wo, uc[i], u2[i], FArgs::default()) { 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!(); } + + fn as_any(&self) -> &dyn Any; } +#[derive(Debug)] +pub struct EmptyBxDF; +impl BxDFTrait for EmptyBxDF { + fn f(&self, _wo: &Vector3f, _wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum { + SampledSpectrum::default() + } + fn sample_f( + &self, + _wo: &Vector3f, + _u: Float, + _u2: Point2f, + _f_args: FArgs, + ) -> Option { + None + } + fn pdf(&self, _wo: &Vector3f, _wi: &Vector3f, _f_args: FArgs) -> Float { + 0.0 + } + fn flags(&self) -> BxDFFlags { + BxDFFlags::UNSET + } + fn regularize(&mut self) { + todo!(); + } + fn as_any(&self) -> &dyn Any { + self + } +} + +#[derive(Debug, Clone)] pub enum BxDF { Diffuse(DiffuseBxDF), - DiffuseTransmission(DiffuseTransmissionBxDF), Dielectric(DielectricBxDF), - // ThinDielectric(ThinDielectricBxDF), + ThinDielectric(ThinDielectricBxDF), + Conductor(ConductorBxDF), + Measured(MeasuredBxDF), + // DiffuseTransmission(DiffuseTransmissionBxDF), // 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), + BxDF::ThinDielectric(b) => b.flags(), + BxDF::Conductor(b) => b.flags(), + BxDF::Measured(b) => b.flags(), } } - fn pdf(&self, wo: Vector3f, wi: Vector3f, mode: TransportMode, sample_flags: BxDFReflTransFlags) -> Float { + fn f(&self, wo: &Vector3f, wi: &Vector3f, mode: TransportMode) -> SampledSpectrum { 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), + BxDF::Diffuse(b) => b.f(wo, wi, mode), + BxDF::Dielectric(b) => b.f(wo, wi, mode), + BxDF::ThinDielectric(b) => b.f(wo, wi, mode), + BxDF::Conductor(b) => b.f(wo, wi, mode), + BxDF::Measured(b) => b.f(wo, wi, mode), } } - + + fn sample_f(&self, wo: &Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option { + match self { + BxDF::Diffuse(b) => b.sample_f(wo, uc, u, f_args), + BxDF::Dielectric(b) => b.sample_f(wo, uc, u, f_args), + BxDF::ThinDielectric(b) => b.sample_f(wo, uc, u, f_args), + BxDF::Conductor(b) => b.sample_f(wo, uc, u, f_args), + BxDF::Measured(b) => b.sample_f(wo, uc, u, f_args), + } + } + + fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float { + match self { + BxDF::Diffuse(b) => b.pdf(wo, wi, f_args), + BxDF::Dielectric(b) => b.pdf(wo, wi, f_args), + BxDF::ThinDielectric(b) => b.pdf(wo, wi, f_args), + BxDF::Conductor(b) => b.pdf(wo, wi, f_args), + BxDF::Measured(b) => b.pdf(wo, wi, f_args), + } + } + fn regularize(&mut self) { match self { BxDF::Diffuse(b) => b.regularize(), - BxDF::DiffuseTransmission(b) => b.regularize(), BxDF::Dielectric(b) => b.regularize(), + BxDF::ThinDielectric(b) => b.regularize(), + BxDF::Conductor(b) => b.regularize(), + BxDF::Measured(b) => b.regularize(), + } + } + + fn as_any(&self) -> &dyn Any { + match self { + BxDF::Diffuse(b) => b.as_any(), + BxDF::Dielectric(b) => b.as_any(), + BxDF::ThinDielectric(b) => b.as_any(), + BxDF::Conductor(b) => b.as_any(), + BxDF::Measured(b) => b.as_any(), } } } + +#[derive(Debug, Clone)] +pub struct BSDF { + bxdf: Arc>, + shading_frame: Frame, +} + +impl Default for BSDF { + fn default() -> Self { + Self { + bxdf: Arc::new(RwLock::new(EmptyBxDF)), + shading_frame: Frame::default(), + } + } +} + +impl BSDF { + pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: Arc>) -> Self { + Self { + bxdf, + shading_frame: Frame::new(dpdus.normalize(), Vector3f::from(ns)), + } + } + + pub fn is_valid(&self) -> bool { + let guard = self.bxdf.read().unwrap(); + guard.as_any().downcast_ref::().is_none() + } + + pub fn flags(&self) -> BxDFFlags { + let guard = self.bxdf.read().unwrap(); + guard.flags() + } + + pub fn render_to_local(&self, v: Vector3f) -> Vector3f { + self.shading_frame.to_local(v) + } + + pub fn local_to_render(&self, v: Vector3f) -> Vector3f { + self.shading_frame.from_local(v) + } + + pub fn f( + &self, + wo_render: &Vector3f, + wi_render: &Vector3f, + mode: TransportMode, + ) -> SampledSpectrum { + let wo = self.render_to_local(*wo_render); + if wo.z() == 0. { + return SampledSpectrum::default(); + } + let wi = self.render_to_local(*wi_render); + let guard = self.bxdf.read().unwrap(); + guard.f(&wo, &wi, mode) + } + + pub fn sample_f( + &self, + wo_render: &Vector3f, + u: Float, + u2: Point2f, + f_args: FArgs, + ) -> Option { + let wo = self.render_to_local(*wo_render); + let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits()); + let guard = self.bxdf.read().unwrap(); + + if wo.z() == 0. || !(guard.flags().contains(sampling_flags)) { + return None; + } + if let Some(mut bs) = guard.sample_f(&wo, u, u2, f_args) { + if bs.pdf > 0.0 && bs.wi.z() != 0.0 { + bs.wi = self.local_to_render(bs.wi); + return Some(bs); + } + } + None + } + + pub fn pdf(&self, wo_render: &Vector3f, wi_render: &Vector3f, f_args: FArgs) -> Float { + let wo = self.render_to_local(*wo_render); + if wo.z() == 0.0 { + return 0.; + } + let wi = self.render_to_local(*wi_render); + let guard = self.bxdf.read().unwrap(); + guard.pdf(&wo, &wi, f_args) + } + + pub fn f_specific( + &self, + wo_render: Vector3f, + wi_render: Vector3f, + mode: TransportMode, + ) -> SampledSpectrum { + let wo = self.render_to_local(wo_render); + if wo.z() == 0.0 { + return SampledSpectrum::default(); + } + let wi = self.render_to_local(wi_render); + let guard = self.bxdf.read().unwrap(); + + if let Some(specific_bxdf) = guard.as_any().downcast_ref::() { + specific_bxdf.f(&wo, &wi, mode) + } else { + SampledSpectrum::default() + } + } + + pub fn sample_f_specific( + &self, + wo_render: &Vector3f, + u: Float, + u2: Point2f, + f_args: FArgs, + ) -> Option { + let wo = self.render_to_local(*wo_render); + let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits()); + let guard = self.bxdf.read().unwrap(); + + if wo.z() == 0. || !guard.flags().contains(sampling_flags) { + return None; + } + if let Some(specific_bxdf) = guard.as_any().downcast_ref::() { + if let Some(mut bs) = specific_bxdf.sample_f(&wo, u, u2, f_args) { + if bs.pdf > 0.0 && bs.wi.z() != 0.0 { + bs.wi = self.local_to_render(bs.wi); + return Some(bs); + } + } + } + None + } + + pub fn pdf_specific( + &self, + wo_render: &Vector3f, + wi_render: &Vector3f, + f_args: FArgs, + ) -> Float { + let wo = self.render_to_local(*wo_render); + if wo.z() == 0.0 { + return 0.; + } + let wi = self.render_to_local(*wi_render); + let guard = self.bxdf.read().unwrap(); + + if let Some(specific_bxdf) = guard.as_any().downcast_ref::() { + specific_bxdf.pdf(&wo, &wi, f_args) + } else { + 0. + } + } + + pub fn rho_u(&self, u1: &[Point2f], uc: &[Float], u2: &[Point2f]) -> SampledSpectrum { + let guard = self.bxdf.read().unwrap(); + guard.rho_u(u1, uc, u2) + } + + pub fn rho_wo(&self, wo_render: Vector3f, uc: &[Float], u: &[Point2f]) -> SampledSpectrum { + let wo = self.render_to_local(wo_render); + let guard = self.bxdf.read().unwrap(); + guard.rho_wo(wo, uc, u) + } + + pub fn regularize(&mut self) { + let mut guard = self.bxdf.write().unwrap(); + guard.regularize() + } +} + +impl BxDFTrait for DiffuseBxDF { + fn flags(&self) -> BxDFFlags { + if !self.r.is_black() { + BxDFFlags::DIFFUSE_REFLECTION + } else { + BxDFFlags::UNSET + } + } + + fn f(&self, wo: &Vector3f, wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum { + if !same_hemisphere(*wo, *wi) { + return SampledSpectrum::new(0.); + } + self.r * INV_PI + } + + fn sample_f(&self, wo: &Vector3f, _uc: Float, u: Point2f, f_args: FArgs) -> Option { + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + return None; + } + let mut wi = sample_cosine_hemisphere(u); + if wo.z() == 0. { + wi[2] *= -1.; + } + let pdf = cosine_hemisphere_pdf(abs_cos_theta(wi)); + let mut bsdf = BSDFSample::default(); + bsdf.f = self.r * INV_PI; + bsdf.wi = wi; + bsdf.pdf = pdf; + bsdf.flags = BxDFFlags::DIFFUSE_REFLECTION; + Some(bsdf) + } + + fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float { + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::ALL.bits()); + if !f_args.sample_flags.contains(reflection_flags) || !same_hemisphere(*wo, *wi) { + return 0.; + } + cosine_hemisphere_pdf(abs_cos_theta(*wi)) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl BxDFTrait for ConductorBxDF { + fn flags(&self) -> BxDFFlags { + if self.mf_distrib.effectively_smooth() { + return BxDFFlags::SPECULAR_REFLECTION; + } else { + return BxDFFlags::GLOSSY_REFLECTION; + } + } + fn sample_f(&self, wo: &Vector3f, _uc: Float, u: Point2f, f_args: FArgs) -> Option { + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + return None; + } + + if self.mf_distrib.effectively_smooth() { + let wi = Vector3f::new(-wo.x(), -wo.y(), wo.z()); + let f = + fr_complex_from_spectrum(abs_cos_theta(wi), self.eta, self.k) / abs_cos_theta(wi); + let mut bsd = BSDFSample::default(); + bsd.f = f; + bsd.wi = wi; + bsd.pdf = 1.; + bsd.flags = BxDFFlags::SPECULAR_REFLECTION; + return Some(bsd); + } + + if wo.z() == 0. { + return None; + } + let wm = self.mf_distrib.sample_wm(*wo, u); + let wi = reflect(*wo, wm.into()); + if !same_hemisphere(*wo, wi) { + return None; + } + + let pdf = self.mf_distrib.pdf(*wo, wm) / (4. * (*wo).dot(wm).abs()); + + let cos_theta_o = abs_cos_theta(*wo); + let cos_theta_i = abs_cos_theta(wi); + if cos_theta_i == 0. || cos_theta_o == 0. { + return None; + } + + let f_spectrum = fr_complex_from_spectrum((*wo).dot(wi).abs(), self.eta, self.k); + let f = self.mf_distrib.d(wm) * f_spectrum * self.mf_distrib.g(*wo, wi) + / (4. * cos_theta_i * cos_theta_o); + + let mut bsdf = BSDFSample::default(); + bsdf.f = f; + bsdf.wi = wi; + bsdf.pdf = pdf; + bsdf.flags = BxDFFlags::GLOSSY_REFLECTION; + + Some(bsdf) + } + + fn f(&self, wo: &Vector3f, wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum { + if !same_hemisphere(*wo, *wi) { + return SampledSpectrum::default(); + } + if self.mf_distrib.effectively_smooth() { + return SampledSpectrum::default(); + } + + let cos_theta_o = abs_cos_theta(*wo); + let cos_theta_i = abs_cos_theta(*wi); + if cos_theta_i == 0. || cos_theta_o == 0. { + return SampledSpectrum::new(0.); + } + + let wm = *wi + *wo; + if wm.norm_squared() == 0. { + return SampledSpectrum::new(0.); + } + let wm_norm = wm.normalize(); + + let f_spectrum = fr_complex_from_spectrum((*wo).dot(wm).abs(), self.eta, self.k); + self.mf_distrib.d(wm_norm) * f_spectrum * self.mf_distrib.g(*wo, *wi) + / (4. * cos_theta_i * cos_theta_o) + } + + fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float { + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + return 0.; + } + if !same_hemisphere(*wo, *wi) { + return 0.; + } + if self.mf_distrib.effectively_smooth() { + return 0.; + } + let wm = *wo + *wi; + if wm.norm_squared() == 0. { + return 0.; + } + let wm_corr = Normal3f::new(0., 0., 1.).face_forward(wm); + self.mf_distrib.pdf(*wo, wm_corr.into()) / (4. * (*wo).dot(wm).abs()) + } + + fn regularize(&mut self) { + self.mf_distrib.regularize(); + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl BxDFTrait for DielectricBxDF { + fn flags(&self) -> BxDFFlags { + let flags = if self.eta == 1. { + BxDFFlags::TRANSMISSION + } else { + BxDFFlags::REFLECTION | BxDFFlags::TRANSMISSION + }; + flags + | if self.mf_distrib.effectively_smooth() { + BxDFFlags::SPECULAR + } else { + BxDFFlags::GLOSSY + } + } + + fn f(&self, wo: &Vector3f, wi: &Vector3f, mode: TransportMode) -> SampledSpectrum { + if self.eta == 1. || self.mf_distrib.effectively_smooth() { + return SampledSpectrum::new(0.); + } + + // Generalized half vector wm + let cos_theta_o = cos_theta(*wo); + let cos_theta_i = cos_theta(*wi); + let reflect = cos_theta_i * cos_theta_o > 0.; + + let mut etap = 1.; + if !reflect { + etap = if cos_theta_o > 0. { + self.eta + } else { + 1. / self.eta + }; + } + + let wm_orig = *wi * etap + *wo; + if cos_theta_i == 0. || cos_theta_o == 0. || wm_orig.norm_squared() == 0. { + return SampledSpectrum::new(0.); + } + let wm = Normal3f::new(0., 0., 1.).face_forward(wm_orig.normalize()); + + if (*wi).dot(wm.into()) * cos_theta_i < 0. || (*wo).dot(wm.into()) * cos_theta_o < 0. { + return SampledSpectrum::new(0.); + } + + let fr = fr_dielectric((*wo).dot(wm.into()), self.eta); + + if reflect { + return SampledSpectrum::new( + self.mf_distrib.d(wm.into()) * self.mf_distrib.g(*wo, *wi) * fr + / (4. * cos_theta_i * cos_theta_o).abs(), + ); + } else { + let denom = square((*wi).dot(wm.into()) + (*wo).dot(wm.into()) / etap) + * cos_theta_i + * cos_theta_o; + let mut ft = self.mf_distrib.d(wm.into()) + * (1. - fr) + * self.mf_distrib.g(*wo, *wi) + * ((*wi).dot(wm.into()) * (*wo).dot(wm.into()) / denom).abs(); + if mode == TransportMode::Radiance { + ft /= square(etap) + } + + return SampledSpectrum::new(ft); + } + } + + fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float { + if self.eta == 1. || self.mf_distrib.effectively_smooth() { + return 0.; + } + + let cos_theta_o = cos_theta(*wo); + let cos_theta_i = cos_theta(*wi); + + let reflect = cos_theta_i * cos_theta_o > 0.; + let mut etap = 1.; + if !reflect { + etap = if cos_theta_o > 0. { + self.eta + } else { + 1. / self.eta + }; + } + + let wm_orig = *wi * etap + *wo; + + if cos_theta_i == 0. || cos_theta_o == 0. || wm_orig.norm_squared() == 0. { + return 0.; + } + let wm = Normal3f::new(0., 0., 1.).face_forward(wm_orig.normalize()); + + // Discard backfacing microfacets + if (*wi).dot(wm.into()) * cos_theta_i < 0. || (*wo).dot(wm.into()) * cos_theta_o < 0. { + return 0.; + } + + let r = fr_dielectric((*wo).dot(wm.into()), self.eta); + let t = 1. - r; + let mut pr = r; + let mut pt = t; + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + let transmission_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::TRANSMISSION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + pr = 0.; + } + if !f_args.sample_flags.contains(transmission_flags) { + pt = 0.; + } + if pr == 0. && pt == 0. { + return 0.; + } + + let pdf: Float; + if reflect { + pdf = self.mf_distrib.pdf( + *wo, + Vector3f::from(wm) / (4. * (*wo).dot(wm.into()).abs()) * pr / (pt + pr), + ); + } else { + let denom = square((*wi).dot(wm.into()) + (*wo).dot(wm.into()) / etap); + let dwm_dwi = (*wi).dot(wm.into()).abs() / denom; + pdf = self.mf_distrib.pdf(*wo, wm.into()) * dwm_dwi * pr / (pr + pt); + } + pdf + } + + fn sample_f(&self, wo: &Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option { + if self.eta == 1. || self.mf_distrib.effectively_smooth() { + let r = fr_dielectric(cos_theta(*wo), self.eta); + let t = 1. - r; + let mut pr = r; + let mut pt = t; + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + let transmission_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::TRANSMISSION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + pr = 0.; + } + if !f_args.sample_flags.contains(transmission_flags) { + pt = 0.; + } + // If probabilities are null, doesnt contribute + if pr == 0. && pt == 0. { + return None; + } + + if uc < pr / (pr + pt) { + let wi = Vector3f::new(-wo.x(), -wo.y(), wo.z()); + let fr = SampledSpectrum::new(r / abs_cos_theta(wi)); + let mut bsd = BSDFSample::default(); + bsd.f = fr; + bsd.wi = wi; + bsd.pdf = pr / (pr + pt); + bsd.flags = BxDFFlags::SPECULAR_REFLECTION; + return Some(bsd); + } else { + // Compute ray direction for specular transmission + if let Some((wi, etap)) = refract(*wo, Normal3f::new(0., 0., 1.), self.eta) { + let mut ft = SampledSpectrum::new(t / abs_cos_theta(wi)); + if f_args.mode == TransportMode::Radiance { + ft /= square(etap); + } + let mut bsd = BSDFSample::default(); + bsd.f = ft; + bsd.wi = wi; + bsd.pdf = pt / (pr + pt); + bsd.flags = BxDFFlags::SPECULAR_TRANSMISSION; + bsd.eta = etap; + return Some(bsd); + } else { + return None; + } + } + } else { + // Sample rough dielectric BSDF + let wm = self.mf_distrib.sample_wm(*wo, u); + let r = fr_dielectric((*wo).dot(wm), self.eta); + let t = 1. - r; + let mut pr = r; + let mut pt = t; + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + let transmission_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::TRANSMISSION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + pr = 0.; + } + if !f_args.sample_flags.contains(transmission_flags) { + pt = 0.; + } + if pr == 0. && pt == 0. { + return None; + } + let pdf: Float; + if uc < pr / (pr + pt) { + // Sample reflection at rough dielectric interface + let wi = reflect(*wo, wm.into()); + if !same_hemisphere(*wo, wi) { + return None; + } + + pdf = self.mf_distrib.pdf(*wo, wm) / (4. * (*wo).dot(wm).abs()) * pr / (pr + pt); + let f = SampledSpectrum::new( + self.mf_distrib.d(wm) * self.mf_distrib.g(*wo, wi) * r + / (4. * cos_theta(wi) * cos_theta(*wo)), + ); + let mut bsd = BSDFSample::default(); + bsd.f = f; + bsd.wi = wi; + bsd.pdf = pdf; + bsd.flags = BxDFFlags::GLOSSY_REFLECTION; + return Some(bsd); + } else { + // Sample transmission at rough dielectric interface + if let Some((wi, etap)) = refract(*wo, wm.into(), self.eta) { + if same_hemisphere(*wo, wi) || wi.z() == 0. { + return None; + } else { + let denom = square(wi.dot(wm) + wo.dot(wm) / etap); + let dwm_mi = wi.dot(wm).abs() / denom; + pdf = self.mf_distrib.pdf(*wo, wm) * dwm_mi * pt / (pr + pt); + let mut ft = SampledSpectrum::new( + t * self.mf_distrib.d(wm) + * self.mf_distrib.g(*wo, wi) + * (wi.dot(wm) * (*wo).dot(wm)).abs() + / (cos_theta(wi) * cos_theta(*wo) * denom), + ); + if f_args.mode == TransportMode::Radiance { + ft /= square(etap); + } + let mut bsd = BSDFSample::default(); + bsd.f = ft; + bsd.wi = wi; + bsd.pdf = pdf; + bsd.flags = BxDFFlags::GLOSSY_TRANSMISSION; + bsd.eta = etap; + return Some(bsd); + } + } else { + return None; + } + } + } + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn regularize(&mut self) { + self.mf_distrib.regularize(); + } +} + +impl BxDFTrait for ThinDielectricBxDF { + fn flags(&self) -> BxDFFlags { + BxDFFlags::REFLECTION | BxDFFlags::TRANSMISSION | BxDFFlags::SPECULAR + } + + fn f(&self, _wo: &Vector3f, _wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum { + SampledSpectrum::new(0.) + } + + fn pdf(&self, _wo: &Vector3f, _wi: &Vector3f, _f_args: FArgs) -> Float { + 0. + } + + fn sample_f(&self, wo: &Vector3f, uc: Float, _u: Point2f, f_args: FArgs) -> Option { + let mut r = fr_dielectric(abs_cos_theta(*wo), self.eta); + let mut t = 1. - r; + if r < 1. { + r += square(t) * r / (1. - square(r)); + t = 1. - r; + } + let mut pr = r; + let mut pt = t; + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + let transmission_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::TRANSMISSION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + pr = 0.; + } + if !f_args.sample_flags.contains(transmission_flags) { + pt = 0.; + } + if pr == 0. && pt == 0. { + return None; + } + + if uc < pr / (pr + pt) { + let wi = Vector3f::new(-wo.x(), -wo.y(), wo.z()); + let fr = SampledSpectrum::new(r / abs_cos_theta(wi)); + let mut bsdf = BSDFSample::default(); + bsdf.f = fr; + bsdf.wi = wi; + bsdf.pdf = pr / (pr + pt); + bsdf.flags = BxDFFlags::SPECULAR_REFLECTION; + return Some(bsdf); + } else { + // Perfect specular transmission + let wi = -(*wo); + let ft = SampledSpectrum::new(t / abs_cos_theta(wi)); + let mut bsdf = BSDFSample::default(); + bsdf.f = ft; + bsdf.wi = wi; + bsdf.pdf = pr / (pr + pt); + bsdf.flags = BxDFFlags::SPECULAR_TRANSMISSION; + return Some(bsdf); + } + } + + fn as_any(&self) -> &dyn Any { + self + } + fn regularize(&mut self) { + todo!() + } +} + +impl BxDFTrait for MeasuredBxDF { + fn flags(&self) -> BxDFFlags { + BxDFFlags::REFLECTION | BxDFFlags::GLOSSY + } + fn f(&self, wo: &Vector3f, wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum { + if !same_hemisphere(*wo, *wi) { + return SampledSpectrum::new(0.); + } + + let mut wo_curr = *wo; + let mut wi_curr = *wi; + if wo.z() < 0. { + wo_curr = -wo_curr; + wi_curr = -wi_curr; + } + + // Get half direction vector + let wm_curr = wi_curr + wo_curr; + if wm_curr.norm_squared() == 0. { + return SampledSpectrum::new(0.); + } + let wm = wm_curr.normalize(); + + // Map vectors to unit square + let theta_o = spherical_theta::(wo_curr); + let phi_o = wo_curr.y().atan2(wo_curr.x()); + let theta_m = spherical_theta::(wm); + let phi_m = wm.y().atan2(wm.x()); + let u_wo = Point2f::new(MeasuredBxDF::theta2u(theta_o), MeasuredBxDF::phi2u(phi_o)); + let u_wm_phi = if self.brdf.isotropic { + phi_m - phi_o + } else { + phi_m + }; + let mut u_wm = Point2f::new( + MeasuredBxDF::theta2u(theta_m), + MeasuredBxDF::phi2u(u_wm_phi), + ); + u_wm[1] = u_wm[1] - u_wm[1].floor(); + + // Inverse parametrization + let ui = self.brdf.vndf.invert(u_wm, [phi_o, theta_o]); + let mut fr = SampledSpectrum::default(); + for i in 0..N_SPECTRUM_SAMPLES { + fr[i] = 0_f32.max( + self.brdf + .spectra + .evaluate(ui.p, [phi_o, theta_o, self.lambda[i]]), + ); + } + fr * self.brdf.ndf.evaluate(u_wm, []) + / (4. * self.brdf.sigma.evaluate(u_wo, []) * cos_theta(*wi)) + } + + fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float { + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + return 0.; + } + if !same_hemisphere(*wo, *wi) { + return 0.; + } + + let mut wo_curr = *wo; + let mut wi_curr = *wi; + if wo.z() < 0. { + wo_curr = -wo_curr; + wi_curr = -wi_curr; + } + + let wm_curr = wi_curr + wo_curr; + if wm_curr.norm_squared() == 0. { + return 0.; + } + let wm = wm_curr.normalize(); + let theta_o = spherical_theta::(wo_curr); + let phi_o = wo_curr.y().atan2(wo_curr.x()); + let theta_m = spherical_theta::(wm); + let phi_m = wm.y().atan2(wm.x()); + let u_wm_phi = if self.brdf.isotropic { + phi_m - phi_o + } else { + phi_m + }; + let mut u_wm = Point2f::new( + MeasuredBxDF::theta2u(theta_m), + MeasuredBxDF::phi2u(u_wm_phi), + ); + u_wm[1] = u_wm[1] - u_wm[1].floor(); + + let ui = self.brdf.vndf.invert(u_wm, [phi_o, theta_o]); + let sample = ui.p; + let vndf_pdf = ui.pdf; + + let pdf = self.brdf.luminance.evaluate(sample, [phi_o, theta_o]); + let sin_theta_m = (square(wm.x()) + square(wm.y())).sqrt(); + let jacobian = 4. * wm.dot(*wo) * f32::max(2. * square(PI) * u_wm.x() * sin_theta_m, 1e-6); + vndf_pdf * pdf / jacobian + } + + fn sample_f(&self, wo: &Vector3f, _uc: Float, u: Point2f, f_args: FArgs) -> Option { + let reflection_flags = + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); + if !f_args.sample_flags.contains(reflection_flags) { + return None; + } + + let mut flip_w = false; + let mut wo_curr = *wo; + if wo.z() <= 0. { + wo_curr = -wo_curr; + flip_w = true; + } + + let theta_o = spherical_theta::(wo_curr); + let phi_o = wo_curr.y().atan2(wo_curr.x()); + // Warp sample using luminance distribution + let mut s = self.brdf.luminance.sample(u, [phi_o, theta_o]); + let u = s.p; + let lum_pdf = s.pdf; + + // Sample visible normal distribution of measured BRDF + s = self.brdf.vndf.sample(u, [phi_o, theta_o]); + let u_wm = s.p; + let mut pdf = s.pdf; + + // Map from microfacet normal to incident direction + let mut phi_m = MeasuredBxDF::u2phi(u_wm.y()); + let theta_m = MeasuredBxDF::u2theta(u_wm.x()); + if self.brdf.isotropic { + phi_m += phi_o; + } + let sin_theta_m = theta_m.sin(); + let cos_theta_m = theta_m.cos(); + let wm = spherical_direction(sin_theta_m, cos_theta_m, phi_m); + let mut wi = reflect(wo_curr, wm.into()); + if wi.z() <= 0. { + return None; + } + + // Interpolate spectral BRDF + let mut fr = SampledSpectrum::new(0.); + for i in 0..N_SPECTRUM_SAMPLES { + fr[i] = 0_f32.max( + self.brdf + .spectra + .evaluate(u, [phi_o, theta_o, self.lambda[i]]), + ); + } + + let u_wo = Point2f::new(MeasuredBxDF::theta2u(theta_o), MeasuredBxDF::phi2u(phi_o)); + fr *= self.brdf.ndf.evaluate(u_wm, []) + / (4. * self.brdf.sigma.evaluate(u_wo, []) * abs_cos_theta(wi)); + pdf /= 4. * wm.dot(wo_curr) * f32::max(2. * square(PI) * u_wm.x(), 1e-6); + + if flip_w { + wi = -wi; + } + let mut bsdf = BSDFSample::default(); + bsdf.f = fr; + bsdf.wi = wi; + bsdf.pdf = pdf * lum_pdf; + bsdf.flags = BxDFFlags::GLOSSY_REFLECTION; + Some(bsdf) + } + + fn as_any(&self) -> &dyn Any { + self + } + fn regularize(&mut self) { + todo!() + } +} diff --git a/src/core/cie.rs b/src/core/cie.rs index 0d20057..fba085c 100644 --- a/src/core/cie.rs +++ b/src/core/cie.rs @@ -2,469 +2,1585 @@ 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]; + 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]; + 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]; + 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]; + 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, + 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]; + 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]; + 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]; + 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]; + 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, + 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 @@ -473,511 +1589,454 @@ pub const CIE_ILLUM_D5000: [Float; 214] = [ // -- // > 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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] = [ @@ -989,7 +2048,8 @@ pub const CUZN_ETA: [Float; 122] = [ 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]; + 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, @@ -1000,56 +2060,53 @@ pub const CUZN_K: [Float; 122] = [ 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]; + 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, + 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, + 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, + 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, + 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: @@ -1060,803 +2117,4070 @@ pub const TIO2_K: [Float; 68] = [ // 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 e9d3899..b2d6c7a 100644 --- a/src/core/film.rs +++ b/src/core/film.rs @@ -1,59 +1,465 @@ +use crate::core::filter::{Filter, FilterTrait}; +use crate::core::interaction::SurfaceInteraction; 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}; +use crate::geometry::{ + Bounds2f, Bounds2fi, Bounds2i, Normal3f, Point2f, Point2i, Point3f, Vector2f, Vector2fi, + Vector2i, Vector3f, +}; +use crate::utils::color::{RGB, SRGBEncoding, Triplet, XYZ, white_balance}; +use crate::utils::colorspace::RGBColorspace; +use crate::utils::containers::Array2D; +use crate::utils::image::{ + Image, ImageChannelDesc, ImageChannelValues, ImageMetadata, PixelFormat, +}; +use crate::utils::math::SquareMatrix; +use crate::utils::math::linear_least_squares; +use crate::utils::sampling::VarianceEstimator; +use crate::utils::spectrum::{ + ConstantSpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, N_SPECTRUM_SAMPLES, + SampledSpectrum, SampledWavelengths, Spectrum, inner_product, spectra, +}; +use crate::utils::transform::AnimatedTransform; +use rayon::prelude::*; +use std::sync::{Arc, atomic::AtomicUsize, atomic::Ordering}; +use once_cell::sync::Lazy; +use std::error::Error; +use std::sync::Mutex; pub struct RGBFilm { pub base: FilmBase, + pub max_component_value: Float, + pub write_fp16: bool, + pub filter_integral: Float, + pub output_rgbf_from_sensor_rgb: SquareMatrix, + pub pixels: Array2D, } + +pub struct RGBPixel { + rgb_sum: [f64; 3], + weight_sum: f64, + rgb_splat: Mutex<[f64; 3]>, +} + +impl Default for RGBPixel { + fn default() -> Self { + Self { + rgb_sum: [0., 0., 0.], + weight_sum: 0., + rgb_splat: Mutex::new([0., 0., 0.]), + } + } +} + +impl RGBFilm { + pub fn new( + base: FilmBase, + colorspace: RGBColorspace, + max_component_value: Float, + write_fp16: bool, + ) -> Self { + let pixels = Array2D::new(base.pixel_bounds); + let filter_integral = base.filter.integral(); + let sensor_matrix = base + .sensor + .as_ref() + .expect("Sensor must exist") + .xyz_from_sensor_rgb; + let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor_matrix; + Self { + base, + max_component_value, + write_fp16, + filter_integral, + output_rgbf_from_sensor_rgb, + pixels, + } + } +} + pub struct GBufferBFilm { pub base: FilmBase, + output_from_render: AnimatedTransform, + apply_inverse: bool, + pixels: Array2D, + colorspace: RGBColorspace, + max_component_value: Float, + write_fp16: bool, + filter_integral: Float, + output_rgbf_from_sensor_rgb: SquareMatrix, } + +struct GBufferPixel { + rgb_sum: [f64; 3], + weight_sum: f64, + g_bugger_weight_sum: f64, + rgb_splat: Mutex<[f64; 3]>, + p_sum: Point3f, + dz_dx_sum: Float, + dz_dy_sum: Float, + n_sum: Normal3f, + ns_sum: Normal3f, + uv_sum: Point2f, + rgb_albedo_sum: [f64; 3], + rgb_variance: VarianceEstimator, +} + pub struct SpectralFilm { pub base: FilmBase, + output_from_render: AnimatedTransform, + pixels: Array2D, } -pub struct VisibleSurface; -pub struct PixelSensor; -pub struct ImageMetadata; +struct SpectralPixel; +const N_SWATCH_REFLECTANCES: usize = 24; + +static SWATCH_REFLECTANCES: Lazy<[Spectrum; N_SWATCH_REFLECTANCES]> = + Lazy::new(|| std::array::from_fn(|_| Spectrum::Constant(ConstantSpectrum::new(0.8)))); + +#[derive(Debug, Clone)] +pub struct PixelSensor { + pub xyz_from_sensor_rgb: SquareMatrix, + r_bar: DenselySampledSpectrum, + g_bar: DenselySampledSpectrum, + b_bar: DenselySampledSpectrum, + imaging_ratio: f32, +} + +impl PixelSensor { + pub fn new( + r: &Spectrum, + g: &Spectrum, + b: &Spectrum, + output_colorspace: RGBColorspace, + sensor_illum: &Spectrum, + imaging_ratio: Float, + ) -> Result> { + let r_bar = DenselySampledSpectrum::from_spectrum(&r, LAMBDA_MIN, LAMBDA_MAX); + let g_bar = DenselySampledSpectrum::from_spectrum(&g, LAMBDA_MIN, LAMBDA_MAX); + let b_bar = DenselySampledSpectrum::from_spectrum(&b, LAMBDA_MIN, LAMBDA_MAX); + let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES]; + + for i in 0..N_SWATCH_REFLECTANCES { + let rgb = Self::project_reflectance::( + &SWATCH_REFLECTANCES[i], + sensor_illum, + &Spectrum::DenselySampled(r_bar.clone()), + &Spectrum::DenselySampled(g_bar.clone()), + &Spectrum::DenselySampled(b_bar.clone()), + ); + for c in 0..3 { + rgb_camera[i][c] = rgb[c]; + } + } + + let mut xyz_output = [[0.; 3]; N_SWATCH_REFLECTANCES]; + let sensor_white_g = inner_product(sensor_illum, &Spectrum::DenselySampled(g_bar.clone())); + let sensor_white_y = inner_product(sensor_illum, &spectra::Y); + for i in 0..N_SWATCH_REFLECTANCES { + let s = SWATCH_REFLECTANCES[i].clone(); + let xyz = Self::project_reflectance::( + &s, + &output_colorspace.illuminant, + &spectra::X, + &spectra::Y, + &spectra::Z, + ) * (sensor_white_y / sensor_white_g); + for c in 0..3 { + xyz_output[i][c] = xyz[c]; + } + } + + let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output)?; + + Ok(Self { + xyz_from_sensor_rgb, + r_bar, + g_bar, + b_bar, + imaging_ratio, + }) + } + + pub fn new_with_white_balance( + output_colorspace: RGBColorspace, + sensor_illum: Option, + imaging_ratio: Float, + ) -> Self { + let r_bar = DenselySampledSpectrum::from_spectrum(&spectra::X, LAMBDA_MIN, LAMBDA_MAX); + let g_bar = DenselySampledSpectrum::from_spectrum(&spectra::Y, LAMBDA_MIN, LAMBDA_MAX); + let b_bar = DenselySampledSpectrum::from_spectrum(&spectra::Z, LAMBDA_MIN, LAMBDA_MAX); + let xyz_from_sensor_rgb: SquareMatrix; + + if let Some(illum) = sensor_illum { + let source_white = illum.to_xyz().xy(); + let target_white = output_colorspace.w; + xyz_from_sensor_rgb = white_balance(source_white, target_white); + } else { + xyz_from_sensor_rgb = SquareMatrix::::default(); + } + + Self { + xyz_from_sensor_rgb, + r_bar, + g_bar, + b_bar, + imaging_ratio, + } + } + + pub fn project_reflectance( + refl: &Spectrum, + illum: &Spectrum, + b1: &Spectrum, + b2: &Spectrum, + b3: &Spectrum, + ) -> T { + let mut result = [0.; 3]; + let mut g_integral = 0.; + + for lambda_ind in LAMBDA_MIN..=LAMBDA_MAX { + let lambda = lambda_ind as Float; + let illum_val = illum.sample_at(lambda); + + g_integral += b2.sample_at(lambda) * illum_val; + let refl_illum = refl.sample_at(lambda) * illum_val; + result[0] += b1.sample_at(lambda) * refl_illum; + result[1] += b2.sample_at(lambda) * refl_illum; + result[2] += b3.sample_at(lambda) * refl_illum; + } + + if g_integral > 0. { + let inv_g = 1. / g_integral; + result[0] *= inv_g; + result[1] *= inv_g; + result[2] *= inv_g; + } + + T::from_triplet(result[0], result[1], result[2]) + } + + pub fn to_sensor_rgb(&self, l: SampledSpectrum, lambda: &SampledWavelengths) -> RGB { + let l_norm = l.safe_div(lambda.pdf()); + self.imaging_ratio + * RGB::new( + (self.r_bar.sample(lambda) * l_norm).average(), + (self.g_bar.sample(lambda) * l_norm).average(), + (self.b_bar.sample(lambda) * l_norm).average(), + ) + } +} + +pub struct VisibleSurface { + pub p: Point3f, + pub n: Normal3f, + pub ns: Normal3f, + pub uv: Point2f, + pub time: Float, + pub dpdx: Vector3f, + pub dpdy: Vector3f, + pub albedo: SampledSpectrum, + pub set: bool, +} + +impl VisibleSurface { + pub fn new( + _si: SurfaceInteraction, + albedo: SampledSpectrum, + _lambda: SampledWavelengths, + ) -> Self { + let mut vs = VisibleSurface::default(); + vs.albedo = albedo; + vs + } +} + +impl Default for VisibleSurface { + fn default() -> Self { + Self { + p: Point3f::default(), + n: Normal3f::default(), + ns: Normal3f::default(), + uv: Point2f::default(), + time: 0., + dpdx: Vector3f::default(), + dpdy: Vector3f::default(), + albedo: SampledSpectrum::default(), + set: false, + } + } +} + +#[derive(Debug)] pub struct FilmBase { pub full_resolution: Point2i, - pub pixel_bounds: Bounds2fi, + pub pixel_bounds: Bounds2i, pub filter: Filter, pub diagonal: Float, pub sensor: Option, pub filename: String, } -pub trait FilmTrait { - // Must be implemented +impl FilmBase { + pub fn new( + full_resolution: Point2i, + pixel_bounds: Bounds2i, + filter: Filter, + diagonal: Float, + sensor: Option, + filename: String, + ) -> Self { + Self { + full_resolution, + pixel_bounds, + filter, + diagonal: 0.001 * diagonal, + sensor, + filename, + } + } +} + +pub trait FilmTrait: Sync { 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!() } + 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!() + } - // 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() } -} + fn write_image(&self, metadata: &ImageMetadata, splat_scale: Float) { + let image = self.get_image(metadata, splat_scale); + image + .write(self.get_filename(), metadata) + .expect("Something") + } + + fn get_image(&self, _metadata: &ImageMetadata, splat_scale: Float) -> Image { + let write_fp16 = true; + let format = if write_fp16 { + PixelFormat::F16 + } else { + PixelFormat::F32 + }; + + let channel_names = vec!["R".to_string(), "G".to_string(), "B".to_string()]; + + let pixel_bounds = self.base().pixel_bounds; + let resolution = Point2i::from(pixel_bounds.diagonal()); + + let n_clamped = Arc::new(AtomicUsize::new(0)); + let processed_rows: Vec> = (pixel_bounds.p_min.y()..pixel_bounds.p_max.y()) + .into_par_iter() + .map(|y| { + let n_clamped = Arc::clone(&n_clamped); + let mut row_data = Vec::with_capacity(resolution.x() as usize * 3); + for x in pixel_bounds.p_min.x()..pixel_bounds.p_max.x() { + let p = Point2i::new(x, y); + let mut rgb = self.get_pixel_rgb(p, Some(splat_scale)); + let mut was_clamped = false; + if write_fp16 { + if rgb.r > 65504.0 { + rgb.r = 65504.0; + was_clamped = true; + } + if rgb.g > 65504.0 { + rgb.g = 65504.0; + was_clamped = true; + } + if rgb.b > 65504.0 { + rgb.b = 65504.0; + was_clamped = true; + } + } + if was_clamped { + n_clamped.fetch_add(1, Ordering::SeqCst); + } + row_data.push(rgb.r); + row_data.push(rgb.g); + row_data.push(rgb.b); + } + row_data + }) + .collect(); + + let mut image = Image::new(format, resolution, channel_names, Arc::new(SRGBEncoding)); + let rgb_desc = ImageChannelDesc::new(&[0, 1, 2]); + + for (iy, row_data) in processed_rows.into_iter().enumerate() { + for (ix, rgb_chunk) in row_data.chunks_exact(3).enumerate() { + let p_offset = Point2i::new(ix as i32, iy as i32); + let values = ImageChannelValues::from(rgb_chunk); + image.set_channels(p_offset, &rgb_desc, &values); + } + } + + let clamped_count = n_clamped.load(Ordering::SeqCst); + if clamped_count > 0 { + println!( + "{} pixel values clamped to maximum fp16 value.", + clamped_count + ); + } + + // self.base().pixel_bounds = pixel_bounds; + // self.base().full_resolution = resolution; + // self.colorspace = colorspace; + + image + } + + fn get_pixel_rgb(&self, _p: Point2i, _splat_scale: Option) -> 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, u: Float) -> SampledWavelengths { + SampledWavelengths::sample_visible(u) + } + fn uses_visible_surface(&self) -> bool; + + fn full_resolution(&self) -> Point2i { + self.base().full_resolution + } + fn pixel_bounds(&self) -> Bounds2i { + self.base().pixel_bounds + } + fn sample_bounds(&self) -> Bounds2f { + let pixel_bounds = self.pixel_bounds(); + let radius = self.get_filter().radius(); + Bounds2f::from_points( + Point2f::from(pixel_bounds.p_min) - radius + Vector2f::new(0.5, 0.5), + Point2f::from(pixel_bounds.p_max) + radius - Vector2f::new(0.5, 0.5), + ) + } + + 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() + } + fn get_filename(&self) -> &String { + &self.base().filename + } +} pub enum Film { RGB(RGBFilm), @@ -69,6 +475,108 @@ impl FilmTrait for RGBFilm { fn base_mut(&mut self) -> &mut FilmBase { &mut self.base } + + fn add_sample( + &mut self, + p_film: Point2i, + l: SampledSpectrum, + lambda: &SampledWavelengths, + _vi: Option<&VisibleSurface>, + weight: Float, + ) { + let mut rgb = self + .get_pixel_sensor() + .expect("Sensor must exist") + .to_sensor_rgb(l, lambda); + let m = rgb.into_iter().copied().fold(f32::NEG_INFINITY, f32::max); + if m > self.max_component_value { + rgb *= self.max_component_value / m; + } + + let pixel = &mut self.pixels[p_film]; + for c in 0..3 { + pixel.rgb_sum[c] += (weight * rgb[c]) as f64; + } + pixel.weight_sum += weight as f64; + } + + fn add_splat(&mut self, p: Point2f, l: SampledSpectrum, lambda: &SampledWavelengths) { + let mut rgb = self + .get_pixel_sensor() + .expect("Sensor must exist") + .to_sensor_rgb(l, lambda); + let m = rgb.into_iter().copied().fold(f32::NEG_INFINITY, f32::max); + if m > self.max_component_value { + rgb *= self.max_component_value / m; + } + + let p_discrete = p + Vector2f::new(0.5, 0.5); + let radius = self.get_filter().radius(); + + let splat_bounds = Bounds2i::from_points( + (p_discrete - radius).floor(), + (p_discrete + radius).floor() + Vector2i::new(1, 1), + ); + + let splat_intersect = splat_bounds.union(self.pixel_bounds()); + for pi in &splat_intersect { + let pi_f: Point2f = (*pi).into(); + let wt = self + .get_filter() + .evaluate((p - pi_f - Vector2f::new(0.5, 0.5)).into()); + if wt != 0. { + let pixel = &self.pixels[*pi]; + for i in 0..3 { + let mut rgb_splat = pixel.rgb_splat.lock().unwrap(); + rgb_splat[i] += wt as f64 * rgb_splat[i]; + } + } + } + } + + fn get_pixel_rgb(&self, p: Point2i, splat_scale: Option) -> RGB { + let pixel = &self.pixels[p]; + let mut rgb = RGB::new( + pixel.rgb_sum[0] as Float, + pixel.rgb_sum[1] as Float, + pixel.rgb_sum[2] as Float, + ); + let weight_sum = pixel.weight_sum; + if weight_sum != 0. { + rgb /= weight_sum as Float + } + + let rgb_splat = pixel.rgb_splat.lock().unwrap(); + if let Some(splat) = splat_scale { + for c in 0..3 { + rgb[c] += splat * rgb_splat[c] as Float / self.filter_integral; + } + } else { + for c in 0..3 { + rgb[c] += rgb_splat[c] as Float / self.filter_integral; + } + } + self.output_rgbf_from_sensor_rgb * rgb + } + + fn to_output_rgb(&self, l: SampledSpectrum, lambda: &SampledWavelengths) -> RGB { + let sensor_rgb = self + .get_pixel_sensor() + .expect("Sensor must exist") + .to_sensor_rgb(l, lambda); + self.output_rgbf_from_sensor_rgb * sensor_rgb + } + + fn uses_visible_surface(&self) -> bool { + false + } + + fn write_image(&self, metadata: &ImageMetadata, splat_scale: Float) { + let image = self.get_image(metadata, splat_scale); + image + .write(self.get_filename(), metadata) + .expect("Something please") + } } impl FilmTrait for GBufferBFilm { @@ -79,6 +587,77 @@ impl FilmTrait for GBufferBFilm { fn base_mut(&mut self) -> &mut FilmBase { &mut self.base } + + fn add_splat(&mut self, p: Point2f, l: SampledSpectrum, lambda: &SampledWavelengths) { + let mut rgb = self + .get_pixel_sensor() + .expect("Sensor must exist") + .to_sensor_rgb(l, lambda); + let m = rgb.into_iter().copied().fold(f32::NEG_INFINITY, f32::max); + if m > self.max_component_value { + rgb *= self.max_component_value / m; + } + + let p_discrete = p + Vector2f::new(0.5, 0.5); + let radius = self.get_filter().radius(); + + let splat_bounds = Bounds2i::from_points( + (p_discrete - radius).floor(), + (p_discrete + radius).floor() + Vector2i::new(1, 1), + ); + + let splat_intersect = splat_bounds.union(self.pixel_bounds()); + for pi in &splat_intersect { + let pi_f: Point2f = (*pi).into(); + let wt = self + .get_filter() + .evaluate((p - pi_f - Vector2f::new(0.5, 0.5)).into()); + if wt != 0. { + let pixel = &self.pixels[*pi]; + for i in 0..3 { + let mut rgb_splat = pixel.rgb_splat.lock().unwrap(); + rgb_splat[i] += wt as f64 * rgb_splat[i]; + } + } + } + } + + fn to_output_rgb(&self, l: SampledSpectrum, lambda: &SampledWavelengths) -> RGB { + let sensor_rgb = self + .get_pixel_sensor() + .expect("Sensor must exist") + .to_sensor_rgb(l, lambda); + self.output_rgbf_from_sensor_rgb * sensor_rgb + } + + fn get_pixel_rgb(&self, p: Point2i, splat_scale: Option) -> RGB { + let pixel = &self.pixels[p]; + let mut rgb = RGB::new( + pixel.rgb_sum[0] as Float, + pixel.rgb_sum[1] as Float, + pixel.rgb_sum[2] as Float, + ); + let weight_sum = pixel.weight_sum; + if weight_sum != 0. { + rgb /= weight_sum as Float + } + + let rgb_splat = pixel.rgb_splat.lock().unwrap(); + if let Some(splat) = splat_scale { + for c in 0..3 { + rgb[c] += splat * rgb_splat[c] as Float / self.filter_integral; + } + } else { + for c in 0..3 { + rgb[c] += rgb_splat[c] as Float / self.filter_integral; + } + } + self.output_rgbf_from_sensor_rgb * rgb + } + + fn uses_visible_surface(&self) -> bool { + true + } } impl FilmTrait for SpectralFilm { @@ -89,6 +668,10 @@ impl FilmTrait for SpectralFilm { fn base_mut(&mut self) -> &mut FilmBase { &mut self.base } + + fn uses_visible_surface(&self) -> bool { + true + } } impl FilmTrait for Film { @@ -99,7 +682,7 @@ impl FilmTrait for Film { Film::Spectral(film) => film.base(), } } - + fn base_mut(&mut self) -> &mut FilmBase { match self { Film::RGB(film) => film.base_mut(), @@ -107,4 +690,12 @@ impl FilmTrait for Film { Film::Spectral(film) => film.base_mut(), } } + + fn uses_visible_surface(&self) -> bool { + match self { + Film::RGB(film) => film.uses_visible_surface(), + Film::GBuffer(film) => film.uses_visible_surface(), + Film::Spectral(film) => film.uses_visible_surface(), + } + } } diff --git a/src/core/filter.rs b/src/core/filter.rs index 1ecacd6..8c839fb 100644 --- a/src/core/filter.rs +++ b/src/core/filter.rs @@ -1,16 +1,17 @@ -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::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f}; use crate::utils::containers::Array2D; +use crate::utils::math::{gaussian, gaussian_integral, sample_tent, windowed_sinc}; -use std::hash::Hash; use rand::Rng; +use std::hash::Hash; pub struct FilterSample { p: Point2f, weight: Float, } +#[derive(Debug)] pub struct FilterSampler { domain: Bounds2f, distrib: PiecewiseConstant2D, @@ -18,12 +19,14 @@ pub struct FilterSampler { } 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 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() { @@ -35,7 +38,7 @@ impl FilterSampler { f[Point2i::new(i, j)] = evaluate_fn(p); } } - let distrib = PiecewiseConstant2D::new(&f, domain); + let distrib = PiecewiseConstant2D::new_with_bounds(&f, domain); Self { domain, f, distrib } } @@ -57,6 +60,7 @@ pub trait FilterTrait { fn sample(&self, u: Point2f) -> FilterSample; } +#[derive(Debug)] pub enum Filter { Box(BoxFilter), Gaussian(GaussianFilter), @@ -107,6 +111,7 @@ impl FilterTrait for Filter { } } +#[derive(Debug)] pub struct BoxFilter { pub radius: Vector2f, } @@ -135,11 +140,15 @@ impl FilterTrait for BoxFilter { } 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 } + 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 } } } +#[derive(Debug)] pub struct GaussianFilter { pub radius: Vector2f, pub sigma: Float, @@ -157,16 +166,16 @@ impl GaussianFilter { 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.) - } + (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 + sampler, } } } @@ -177,13 +186,15 @@ impl FilterTrait for GaussianFilter { } 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) + (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) + (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 { @@ -191,6 +202,7 @@ impl FilterTrait for GaussianFilter { } } +#[derive(Debug)] pub struct MitchellFilter { pub radius: Vector2f, pub b: Float, @@ -207,14 +219,16 @@ impl MitchellFilter { 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) + ((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) + ((-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 } @@ -222,20 +236,27 @@ impl MitchellFilter { mitchell_1d(2.0 * p.x() / radius.x()) * mitchell_1d(2.0 * p.y() / radius.y()) }, ); - Self { radius, b, c, sampler } + 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) + ((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) + ((-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 } @@ -243,12 +264,15 @@ impl MitchellFilter { } impl FilterTrait for MitchellFilter { - fn radius(&self) -> Vector2f { self.radius } + 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()) + 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 } @@ -258,6 +282,7 @@ impl FilterTrait for MitchellFilter { } } +#[derive(Debug)] pub struct LanczosSincFilter { pub radius: Vector2f, pub tau: Float, @@ -273,15 +298,22 @@ impl LanczosSincFilter { windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau) }, ); - Self { radius, tau, sampler } + Self { + radius, + tau, + sampler, + } } } impl FilterTrait for LanczosSincFilter { - fn radius(&self) -> Vector2f { self.radius } + 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) + windowed_sinc(p.x(), self.radius.x(), self.tau) + * windowed_sinc(p.y(), self.radius.y(), self.tau) } fn integral(&self) -> Float { @@ -312,6 +344,7 @@ impl FilterTrait for LanczosSincFilter { } } +#[derive(Debug)] pub struct TriangleFilter { pub radius: Vector2f, } @@ -323,11 +356,12 @@ impl TriangleFilter { } impl FilterTrait for TriangleFilter { - fn radius(&self) -> Vector2f { self.radius } + 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) + (self.radius.x() - p.x().abs()).max(0.0) * (self.radius.y() - p.y().abs()).max(0.0) } fn integral(&self) -> Float { diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 41a4c65..f16b913 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -10,5 +10,4 @@ pub enum Integrator { Sampler(SamplerIntegrator), } -impl Integrator { -} +impl Integrator {} diff --git a/src/core/interaction.rs b/src/core/interaction.rs index 90bf6d3..90691eb 100644 --- a/src/core/interaction.rs +++ b/src/core/interaction.rs @@ -1,26 +1,46 @@ -use crate::core::pbrt::Float; +use crate::core::bxdf::BSDF; +use crate::core::material::MaterialTrait; use crate::core::medium::{Medium, MediumInterface}; -use crate::core::light::Light; -use crate::utils::geometry::{Vector3f, Normal3f, Point2f, Point3f, Point3fi, Normed, Ray, Dot}; +use crate::core::pbrt::Float; +use crate::geometry::{Normal3f, Point2f, Point3f, Point3fi, Ray, Vector3f, VectorLike}; +use crate::lights::Light; +use crate::shapes::ShapeTrait; use std::any::Any; use std::sync::Arc; +#[derive(Default, Clone, Debug)] pub struct InteractionData { pub pi: Point3fi, + pub n: Normal3f, pub time: Float, - pub wo: Vector3f, // Outgoing direction + pub wo: Vector3f, + pub medium_interface: Option>, + pub medium: Option>, } pub trait Interaction: Send + Sync { fn get_common(&self) -> &InteractionData; + fn get_common_mut(&mut self) -> &mut InteractionData; fn as_any(&self) -> &dyn Any; - fn p(&self) -> Point3f { self.get_common().pi.into() } - fn pi(&self) -> Point3fi { self.get_common().pi } - fn time(&self) -> Float { self.get_common().time } - fn wo(&self) -> Vector3f { self.get_common().wo } - + fn p(&self) -> Point3f { + self.get_common().pi.into() + } + fn pi(&self) -> Point3fi { + self.get_common().pi + } + fn time(&self) -> Float { + self.get_common().time + } + fn wo(&self) -> Vector3f { + self.get_common().wo + } + + fn n(&self) -> Normal3f { + self.get_common().n + } + fn is_surface_interaction(&self) -> bool { self.as_any().is::() } @@ -29,13 +49,10 @@ pub trait Interaction: Send + Sync { self.as_any().is::() } - /// Determines the medium for a ray starting at this interaction. fn get_medium(&self, w: Vector3f) -> Option>; - /// Spawns a new ray starting from this interaction point. fn spawn_ray(&self, d: Vector3f) -> Ray; - /// Spawns a ray from this interaction to a specified point. fn spawn_ray_to_point(&self, p2: Point3f) -> Ray { let origin = self.p(); let direction = p2 - origin; @@ -47,14 +64,21 @@ pub trait Interaction: Send + Sync { differential: None, } } - - /// Spawns a ray from this interaction to another one. - /// The default implementation simply targets the other interaction's point. + fn spawn_ray_to_interaction(&self, other: &dyn Interaction) -> Ray { self.spawn_ray_to_point(other.p()) } + + fn offset_ray_vector(&self, w: Vector3f) -> Point3f { + Ray::offset_origin(&self.pi(), &self.n(), &w) + } + + fn offset_ray_point(&self, pt: Point3f) -> Point3f { + self.offset_ray_vector(pt - self.p()) + } } +#[derive(Default, Clone, Debug)] pub struct ShadingGeometry { pub n: Normal3f, pub dpdu: Vector3f, @@ -63,9 +87,9 @@ pub struct ShadingGeometry { pub dndv: Normal3f, } +#[derive(Default, Debug, Clone)] pub struct SurfaceInteraction { pub common: InteractionData, - pub n: Normal3f, pub uv: Point2f, pub dpdu: Vector3f, pub dpdv: Vector3f, @@ -73,16 +97,35 @@ pub struct SurfaceInteraction { pub dndv: Normal3f, pub shading: ShadingGeometry, pub medium_interface: Option>, - pub face_index: bool, + pub face_index: usize, pub area_light: Option>, + pub material: Option>, pub dpdx: Vector3f, pub dpdy: Vector3f, pub dudx: Float, pub dvdx: Float, pub dudy: Float, pub dvdy: Float, - // pub shape: Option>, - // pub bsdf: Option, + pub shape: Option>, + pub bsdf: Option, +} + +impl SurfaceInteraction { + pub fn set_intersection_properties( + &mut self, + mtl: Arc, + area: Arc, + prim_medium_interface: Option>, + ray_medium: Arc, + ) { + self.material = Some(mtl); + self.area_light = Some(area); + if prim_medium_interface.as_ref().map_or(false, |mi| mi.is_medium_transition()) { + self.common.medium_interface = prim_medium_interface; + } else { + self.common.medium = Some(ray_medium); + } + } } pub struct PhaseFunction; @@ -94,12 +137,21 @@ pub struct MediumInteraction { } impl Interaction for SurfaceInteraction { - fn get_common(&self) -> &InteractionData { &self.common } - fn as_any(&self) -> &dyn Any { self } + fn get_common(&self) -> &InteractionData { + &self.common + } + + fn get_common_mut(&mut self) -> &mut InteractionData { + &mut self.common + } + + fn as_any(&self) -> &dyn Any { + self + } fn get_medium(&self, w: Vector3f) -> Option> { self.medium_interface.as_ref().and_then(|interface| { - if self.n.dot(w) > 0.0 { + if self.n().dot(w.into()) > 0.0 { interface.outside.clone() } else { interface.inside.clone() @@ -108,21 +160,27 @@ impl Interaction for SurfaceInteraction { } fn spawn_ray(&self, d: Vector3f) -> Ray { - let mut ray = Ray::spawn(&self.pi(), &self.n, self.time(), d); + let mut ray = Ray::spawn(&self.pi(), &self.n(), self.time(), d); ray.medium = self.get_medium(d); ray } - + fn spawn_ray_to_point(&self, p2: Point3f) -> Ray { - let mut ray = Ray::spawn_to_point(&self.pi(), &self.n, self.time(), p2); + let mut ray = Ray::spawn_to_point(&self.pi(), &self.n(), self.time(), p2); ray.medium = self.get_medium(ray.d); ray } - + fn spawn_ray_to_interaction(&self, other: &dyn Interaction) -> Ray { // Check if the other interaction is a surface to use the robust spawn method if let Some(si_to) = other.as_any().downcast_ref::() { - let mut ray = Ray::spawn_to_interaction(&self.pi(), &self.n, self.time(), &si_to.pi(), &si_to.n); + let mut ray = Ray::spawn_to_interaction( + &self.pi(), + &self.n(), + self.time(), + &si_to.pi(), + &si_to.n(), + ); ray.medium = self.get_medium(ray.d); ray } else { @@ -133,7 +191,17 @@ impl Interaction for SurfaceInteraction { } impl SurfaceInteraction { - pub fn new(pi: Point3fi, uv: Point2f, wo: Vector3f, dpdu: Vector3f, dpdv: Vector3f, dndu: Normal3f, dndv: Normal3f, time: Float, flip: bool) -> Self { + pub fn new( + pi: Point3fi, + uv: Point2f, + wo: Vector3f, + dpdu: Vector3f, + dpdv: Vector3f, + dndu: Normal3f, + dndv: Normal3f, + time: Float, + flip: bool, + ) -> Self { let mut n = Normal3f::from(dpdu.cross(dpdv).normalize()); let mut shading_n = n; @@ -143,16 +211,22 @@ impl SurfaceInteraction { } Self { - common: InteractionData { pi, time, wo }, - n, + common: InteractionData { pi, n, time, wo, medium_interface: None, medium: None }, uv, dpdu, dpdv, dndu, dndv, - shading: ShadingGeometry { n: shading_n, dpdu, dpdv, dndu, dndv }, + shading: ShadingGeometry { + n: shading_n, + dpdu, + dpdv, + dndu, + dndv, + }, medium_interface: None, - face_index: false, + material: None, + face_index: 0, area_light: None, dpdx: Vector3f::zero(), dpdy: Vector3f::zero(), @@ -160,24 +234,74 @@ impl SurfaceInteraction { dudy: 0.0, dvdx: 0.0, dvdy: 0.0, + shape: None, + bsdf: None, } } - pub fn set_shading_geometry(&mut self, ns: Normal3f, dpdus: Vector3f, dpdvs: Vector3f, dndus: Normal3f, dndvs: Normal3f, orientation: bool) { + pub fn new_with_face( + pi: Point3fi, + uv: Point2f, + wo: Vector3f, + dpdu: Vector3f, + dpdv: Vector3f, + dndu: Normal3f, + dndv: Normal3f, + time: Float, + flip: bool, + face_index: usize, + ) -> Self { + let mut si = Self::new(pi, uv, wo, dpdu, dpdv, dndu, dndv, time, flip); + si.face_index = face_index; + si + } + + pub fn set_shading_geometry( + &mut self, + ns: Normal3f, + dpdus: Vector3f, + dpdvs: Vector3f, + dndus: Normal3f, + dndvs: Normal3f, + orientation: bool, + ) { self.shading.n = ns; if orientation { - self.n = self.n.face_forward(self.shading.n.into()); + self.common.n = self.n().face_forward(self.shading.n.into()); } self.shading.dpdu = dpdus; self.shading.dpdv = dpdvs; self.shading.dndu = dndus.into(); self.shading.dndv = dndvs.into(); } + + pub fn new_simple(pi: Point3fi, n: Normal3f, uv: Point2f) -> Self { + let mut si = Self::default(); + si.common = InteractionData { + pi, + n, + time: 0., + wo: Vector3f::zero(), + medium_interface: None, + medium: None, + }; + si.uv = uv; + si + } } impl Interaction for MediumInteraction { - fn get_common(&self) -> &InteractionData { &self.common } - fn as_any(&self) -> &dyn Any { self } + fn get_common(&self) -> &InteractionData { + &self.common + } + + fn get_common_mut(&mut self) -> &mut InteractionData { + &mut self.common + } + + fn as_any(&self) -> &dyn Any { + self + } fn get_medium(&self, _w: Vector3f) -> Option> { Some(self.medium.clone()) diff --git a/src/core/light.rs b/src/core/light.rs deleted file mode 100644 index c9e2826..0000000 --- a/src/core/light.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub struct DiffuseAreaLight; -pub struct DistantLight; -pub struct GonioPhotometricLight; -pub struct InfiniteAreaLight; -pub struct PointLight; -pub struct ProjectionLight; -pub struct SpotLight; - -pub enum Light { - DiffuseArea(Box), - Distant(Box), - GonioPhotometric(Box), - InfiniteArea(Box), - Point(Box), - Projection(Box), - Spot(Box), -} diff --git a/src/core/material.rs b/src/core/material.rs index 2172a0e..c2bd1ab 100644 --- a/src/core/material.rs +++ b/src/core/material.rs @@ -1,15 +1,27 @@ +#[derive(Clone, Debug)] pub struct CoatedDiffuseMaterial; +#[derive(Clone, Debug)] pub struct CoatedConductorMaterial; +#[derive(Clone, Debug)] pub struct ConductorMaterial; +#[derive(Clone, Debug)] pub struct DielectricMaterial; +#[derive(Clone, Debug)] pub struct DiffuseMaterial; +#[derive(Clone, Debug)] pub struct DiffuseTransmissionMaterial; +#[derive(Clone, Debug)] pub struct HairMaterial; +#[derive(Clone, Debug)] pub struct MeasuredMaterial; +#[derive(Clone, Debug)] pub struct SubsurfaceMaterial; +#[derive(Clone, Debug)] pub struct ThinDielectricMaterial; +#[derive(Clone, Debug)] pub struct MixMaterial; +#[derive(Clone, Debug)] pub enum Material { CoatedDiffuse(CoatedDiffuseMaterial), CoatedConductor(CoatedConductorMaterial), @@ -24,5 +36,6 @@ pub enum Material { Mix(MixMaterial), } -impl Material { -} +impl Material {} + +pub trait MaterialTrait: Send + Sync + std::fmt::Debug {} diff --git a/src/core/medium.rs b/src/core/medium.rs index 9782b44..c7bc2b2 100644 --- a/src/core/medium.rs +++ b/src/core/medium.rs @@ -20,8 +20,7 @@ pub enum Medium { NanoVDB(NanoVDBMedium), } - -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct MediumInterface { pub inside: Option>, pub outside: Option>, @@ -33,26 +32,26 @@ impl MediumInterface { } pub fn is_medium_transition(&self) -> bool { - if let Some(ref inside) = self.inside { - // self.inside == Some - if let Some(ref outside) = self.outside { - // self.outside == Some - let pi = inside as *const _ as *const usize; - let po = outside as *const _ as *const usize; - pi != po - } else { - // self.outside == None - true - } - } else { - // self.inside == None - if let Some(ref _outside) = self.outside { - // self.outside == Some - true - } else { - // self.outside == None - false - } - } + if let Some(ref inside) = self.inside { + // self.inside == Some + if let Some(ref outside) = self.outside { + // self.outside == Some + let pi = inside as *const _ as *const usize; + let po = outside as *const _ as *const usize; + pi != po + } else { + // self.outside == None + true + } + } else { + // self.inside == None + if let Some(ref _outside) = self.outside { + // self.outside == Some + true + } else { + // self.outside == None + false + } + } } } diff --git a/src/core/mod.rs b/src/core/mod.rs index 2066845..559a170 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -3,12 +3,10 @@ pub mod cie; pub mod film; pub mod filter; pub mod integrator; -pub mod light; pub mod interaction; pub mod material; pub mod medium; pub mod pbrt; pub mod primitive; pub mod sampler; -pub mod shape; pub mod texture; diff --git a/src/core/pbrt.rs b/src/core/pbrt.rs index 5d35e41..72efc07 100644 --- a/src/core/pbrt.rs +++ b/src/core/pbrt.rs @@ -1,7 +1,7 @@ +use crate::geometry::{Lerp, Point2f, Vector2f, Vector3f}; use num_traits::Num; use std::ops::{Add, Mul}; - -use crate::utils::geometry::{Point2f, Vector2f, Vector3f}; +use std::sync::atomic::{AtomicU64, Ordering as SyncOrdering}; pub type Float = f32; @@ -17,15 +17,16 @@ pub const PI_OVER_4: Float = 0.785_398_163_397_448_309_61; pub const SQRT_2: Float = 1.414_213_562_373_095_048_80; #[inline] -pub fn lerp(x: T, a: T, b: T) -> T -where - T: Num + Copy, +pub fn lerp(t: Factor, a: Value, b: Value) -> Value +where + Factor: Copy + Num, + Value: Copy + Lerp, { - (T::one() - x) * a + x * b + Value::lerp(t, a, b) } -pub fn linear_pdf(x: T, a: T, b: T) -> T -where +pub fn linear_pdf(x: T, a: T, b: T) -> T +where T: Num + Copy + PartialOrd, { if x < T::zero() || x > T::one() { @@ -34,8 +35,8 @@ where (T::one() + T::one()) * lerp(x, a, b) / (a + b) } -pub fn sample_linear(u: T, a: T, b: T) -> T -where +pub fn sample_linear(u: T, a: T, b: T) -> T +where T: Num, { if u == T::zero() && a == T::zero() { @@ -45,34 +46,6 @@ 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, @@ -88,54 +61,6 @@ where r } -#[inline] -pub fn float_to_bits(f: Float) -> u32 { - f.to_bits() -} - -#[inline] -pub fn bits_to_float(ui: u32) -> Float { - f32::from_bits(ui) -} - -// Corresponding types for 64-bit floats -#[inline] -pub fn f64_to_bits(f: f64) -> u64 { - f.to_bits() -} - -#[inline] -pub fn next_float_up(v: Float) -> Float { - if v.is_infinite() && v > 0.0 { - return v; - } - let v = if v == -0.0 { 0.0 } else { v }; - - let mut ui = float_to_bits(v); - if v >= 0.0 { - ui += 1; - } else { - ui -= 1; - } - bits_to_float(ui) -} - -#[inline] -pub fn next_float_down(v: Float) -> Float { - if v.is_infinite() && v < 0.0 { - return v; - } - let v = if v == 0.0 { -0.0 } else { v }; - - let mut ui = float_to_bits(v); - if v > 0.0 { - ui -= 1; - } else { - ui += 1; - } - bits_to_float(ui) -} - #[inline] pub fn evaluate_polynomial(t: Float, coeffs: &[Float]) -> Option { if coeffs.is_empty() { @@ -174,22 +99,52 @@ where clamp_t(result, 0, sz - 2) } - - #[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 { Camera, CameraWorld, World, } + +// Define the static counters. These are thread-safe. +pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0); +pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0); + +// A macro to encapsulate the logic +#[macro_export] +macro_rules! check_rare { + ($frequency_threshold:expr, $condition:expr) => { + // We only run the check periodically to reduce overhead. + // PBRT's implementation does something similar. + const CHECK_INTERVAL: u64 = 4096; + + // Atomically increment the total call counter. + // Ordering::Relaxed is fine because we don't need to synchronize memory. + let total_calls = RARE_EVENT_TOTAL_CALLS.fetch_add(1, SyncOrdering::Relaxed); + + if $condition { + // If the rare condition is met, increment that counter. + RARE_EVENT_CONDITION_MET.fetch_add(1, SyncOrdering::Relaxed); + } + + // Only perform the expensive division and check periodically. + if (total_calls + 1) % CHECK_INTERVAL == 0 { + let met_count = RARE_EVENT_CONDITION_MET.load(SyncOrdering::Relaxed); + if met_count > 0 { + let frequency = met_count as f64 / (total_calls + 1) as f64; + if frequency > $frequency_threshold { + // In a real scenario, you might log an error or panic. + panic!( + "Rare event occurred with frequency {} which is > threshold {}", + frequency, $frequency_threshold + ); + } + } + } + }; +} diff --git a/src/core/primitive.rs b/src/core/primitive.rs index 77bfcdb..82e7572 100644 --- a/src/core/primitive.rs +++ b/src/core/primitive.rs @@ -1,9 +1,69 @@ -// use crate::core::medium::MediumInterface; -// use crate::core::light::Light; -// use crate::core::geometry::Bounds3f; -// -// -pub struct GeometricPrimitive; +use crate::core::interaction::Interaction; +use crate::core::pbrt::Float; +use crate::geometry::{Bounds3f, Ray}; +use crate::shapes::{ShapeIntersection, ShapeTrait}; +use crate::core::material::MaterialTrait; +use crate::core::medium::MediumInterface; +use crate::lights::Light; +use crate::core::texture::{FloatTextureTrait, TextureEvalContext}; +use crate::utils::hash::hash_float; + +use std::sync::Arc; + +pub trait PrimitiveTrait: Send + Sync + std::fmt::Debug { + fn bounds(&self) -> Bounds3f; + fn intersect(&self, r: &Ray, t_max: Option) -> Option; + fn intersect_p(&self, r: &Ray, t_max: Option) -> bool; +} + +#[derive(Debug, Clone)] +pub struct GeometricPrimitive { + shape: Arc, + material: Arc, + area_light: Arc, + medium_interface: Arc, + alpha: Option>, + +} + +impl PrimitiveTrait for GeometricPrimitive { + fn bounds(&self) -> Bounds3f { + self.shape.bounds() + } + fn intersect(&self, r: &Ray, t_max: Option) -> Option { + let mut si = self.shape.intersect(r, t_max)?; + let ctx: TextureEvalContext = si.intr().into(); + let Some(ref alpha) = self.alpha else { return None }; + let a = alpha.evaluate(&ctx); + if a < 1. { + let u = if a <= 0. { 1. } else { hash_float((r.o, r.d)) }; + if u > a { + let r_next = si.intr().spawn_ray(r.d); + let new_t_max = t_max.map(|t| t - si.t_hit())?; + let mut si_next = self.intersect(&r_next, Some(new_t_max - si.t_hit()))?; + si_next.set_t_hit(si_next.t_hit() + si.t_hit()); + return Some(si_next) + } + } + si.intr_mut().set_intersection_properties(self.material.clone(), self.area_light.clone(), Some(self.medium_interface.clone()), r.medium.clone()?); + Some(si) + } + + fn intersect_p(&self, r: &Ray, t_max: Option) -> bool { + if self.alpha.is_some() { + self.intersect(r, t_max).is_some() + } else { + self.shape.intersect_p(r, t_max) + } + } +} + +#[derive(Debug, Clone)] +pub struct SimplePrimitive { + shape: Arc, + material: Arc, +} + pub struct TransformedPrimitive; pub struct AnimatedPrimitive; pub struct BVHAggregatePrimitive; @@ -14,10 +74,10 @@ pub enum Primitive { Transformed(TransformedPrimitive), Animated(AnimatedPrimitive), BVH(BVHAggregatePrimitive), - KdTree(KdTreeAggregate) + KdTree(KdTreeAggregate), } -impl Primitive { +impl Primitive { // pub fn bounds(&self) -> Bounds3f { // match self { // Primitive::Geometric(primitive) => primitive.bounds(), diff --git a/src/core/sampler.rs b/src/core/sampler.rs index b65cedd..48d1145 100644 --- a/src/core/sampler.rs +++ b/src/core/sampler.rs @@ -1,5 +1,5 @@ -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::core::pbrt::{Float, PI, PI_OVER_2, PI_OVER_4, find_interval, lerp}; +use crate::geometry::{Bounds2f, Point2f, Point2i, Vector2f}; use crate::utils::containers::Array2D; #[derive(Debug, Clone, Copy)] @@ -12,10 +12,16 @@ pub struct CameraSample { impl Default for CameraSample { fn default() -> Self { - Self { p_film: Point2f::default(), p_lens: Point2f::default(), time: 0.0, filter_weight: 1.0 } + Self { + p_film: Point2f::default(), + p_lens: Point2f::default(), + time: 0.0, + filter_weight: 1.0, + } } } +#[derive(Debug, Clone)] pub struct PiecewiseConstant1D { pub func: Vec, pub cdf: Vec, @@ -27,6 +33,7 @@ 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(); @@ -34,11 +41,13 @@ impl PiecewiseConstant1D { 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 { @@ -49,14 +58,24 @@ impl PiecewiseConstant1D { cdf[i] /= func_integral; } } - Self { func, cdf, func_integral, min, max } + + Self { + func, + cdf, + func_integral, + min, + max, + } } + pub fn integral(&self) -> Float { self.func_integral } - pub fn len(&self) -> usize { + + pub fn size(&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]; @@ -64,11 +83,16 @@ impl PiecewiseConstant1D { 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. }; + let value = lerp((o as Float + du) / self.size() 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) } } +#[derive(Debug, Clone)] pub struct PiecewiseConstant2D { pub p_conditional_v: Vec, pub p_marginal: PiecewiseConstant1D, @@ -76,10 +100,7 @@ pub struct PiecewiseConstant2D { } 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; + pub fn new(data: &Array2D, nu: usize, nv: usize, domain: Bounds2f) -> Self { let mut p_conditional_v = Vec::with_capacity(nv); for v in 0..nv { let start = v * nu; @@ -90,19 +111,45 @@ impl PiecewiseConstant2D { 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 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 } + + Self { + p_conditional_v, + p_marginal, + domain, + } } - + + pub fn new_with_bounds(data: &Array2D, domain: Bounds2f) -> Self { + Self::new(data, data.x_size() as usize, data.y_size() as usize, domain) + } + + pub fn new_with_data(data: &Array2D, nx: usize, ny: usize) -> Self { + Self::new( + data, + nx, + ny, + Bounds2f::from_points(Point2f::new(0., 0.), Point2f::new(1., 1.)), + ) + } + + pub fn resolution(&self) -> Point2i { + Point2i::new( + self.p_conditional_v[0].size() as i32, + self.p_conditional_v[1].size() as i32, + ) + } + 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()); @@ -110,14 +157,15 @@ impl PiecewiseConstant2D { 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 nu = self.p_conditional_v[0].size(); + let nv = self.p_marginal.size(); + 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 diff --git a/src/core/shape.rs b/src/core/shape.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/texture.rs b/src/core/texture.rs index b2700fc..9656f25 100644 --- a/src/core/texture.rs +++ b/src/core/texture.rs @@ -1,20 +1,122 @@ -pub struct TextureEvalContext; +use crate::core::pbrt::Float; +use crate::geometry::{Point3f, Vector3f, Normal3f, Point2f}; +use crate::core::interaction::SurfaceInteraction; +pub struct TextureEvalContext { + p: Point3f, + dpdx: Vector3f, + dpdy: Vector3f, + n: Normal3f, + uv: Point2f, + // All 0 + dudx: Float, + dudy: Float, + dvdx: Float, + dvdy: Float, + face_index: usize, +} + +impl TextureEvalContext { + pub fn new(p: Point3f, + dpdx: Vector3f, + dpdy: Vector3f, + n: Normal3f, + uv: Point2f, + dudx: Float, + dudy: Float, + dvdx: Float, + dvdy: Float, + face_index: usize, + ) -> Self { + Self {p, dpdx, dpdy, n, uv, dudx, dudy, dvdx, dvdy , face_index } + } +} + +impl From<&SurfaceInteraction> for TextureEvalContext { + fn from(si: &SurfaceInteraction) -> Self { + Self { + p: si.common.pi.into(), + dpdx: si.dpdx, + dpdy: si.dpdy, + n: si.common.n, + uv: si.uv, + dudx: si.dudx, + dudy: si.dudy, + dvdx: si.dvdx, + dvdy: si.dvdy, + face_index: si.face_index, + } + } +} + +pub trait FloatTextureTrait: Send + Sync + std::fmt::Debug { + fn evaluate(&self, _ctx: &TextureEvalContext) -> Float { + todo!() + } +} + +#[derive(Debug, Clone)] pub struct FloatImageTexture; -pub struct GPUFloatImageTexture; -pub struct FloatMixTexture; -pub struct FloatDirectionMixTexture; -pub struct FloatScaledTexture; -pub struct FloatConstantTexture; -pub struct FloatBilerpTexture; -pub struct FloatCheckerboardTexture; -pub struct FloatDotsTexture; -pub struct FBmTexture; -pub struct FloatPtexTexture; -pub struct GPUFloatPtex; -pub struct WindyTexture; -pub struct WrinkledTexture; +impl FloatTextureTrait for FloatImageTexture { + fn evaluate(&self, _ctx: &TextureEvalContext) -> Float { + todo!(); + } +} + +#[derive(Debug, Clone)] +pub struct GPUFloatImageTexture; +impl FloatTextureTrait for GPUFloatImageTexture {} + +#[derive(Debug, Clone)] +pub struct FloatMixTexture; +impl FloatTextureTrait for FloatMixTexture {} + +#[derive(Debug, Clone)] +pub struct FloatDirectionMixTexture; +impl FloatTextureTrait for FloatDirectionMixTexture {} + +#[derive(Debug, Clone)] +pub struct FloatScaledTexture; +impl FloatTextureTrait for FloatScaledTexture {} + +#[derive(Debug, Clone)] +pub struct FloatConstantTexture; +impl FloatTextureTrait for FloatConstantTexture {} + +#[derive(Debug, Clone)] +pub struct FloatBilerpTexture; +impl FloatTextureTrait for FloatBilerpTexture {} + +#[derive(Debug, Clone)] +pub struct FloatCheckerboardTexture; +impl FloatTextureTrait for FloatCheckerboardTexture {} + +#[derive(Debug, Clone)] +pub struct FloatDotsTexture; +impl FloatTextureTrait for FloatDotsTexture {} + +#[derive(Debug, Clone)] +pub struct FBmTexture; +impl FloatTextureTrait for FBmTexture {} + +#[derive(Debug, Clone)] +pub struct FloatPtexTexture; +impl FloatTextureTrait for FloatPtexTexture {} + +#[derive(Debug, Clone)] +pub struct GPUFloatPtex; +impl FloatTextureTrait for GPUFloatPtex {} + +#[derive(Debug, Clone)] +pub struct WindyTexture; +impl FloatTextureTrait for WindyTexture {} + +#[derive(Debug, Clone)] +pub struct WrinkledTexture; +impl FloatTextureTrait for WrinkledTexture {} + +#[derive(Debug, Clone)] pub enum FloatTexture { FloatImage(FloatImageTexture), GPUFloatImage(GPUFloatImageTexture), @@ -32,25 +134,25 @@ pub enum FloatTexture { Wrinkled(WrinkledTexture), } -impl FloatTexture { -// pub fn evaluate(&self, ctx: TextureEvalContext) -> f32 { -// match self { -// FloatTexture::FloatImage(texture) => texture.evaluate(ctx), -// FloatTexture::GPUFloatImage(texture) => texture.evaluate(ctx), -// FloatTexture::FloatMix(texture) => texture.evaluate(ctx), -// FloatTexture::FloatDirectionMix(texture) => texture.evaluate(ctx), -// FloatTexture::FloatScaled(texture) => texture.evaluate(ctx), -// FloatTexture::FloatConstant(texture) => texture.evaluate(ctx), -// FloatTexture::FloatBilerp(texture) => texture.evaluate(ctx), -// FloatTexture::FloatCheckerboard(texture) => texture.evaluate(ctx), -// FloatTexture::FloatDots(texture) => texture.evaluate(ctx), -// FloatTexture::FBm(texture) => texture.evaluate(ctx), -// FloatTexture::FloatPtex(texture) => texture.evaluate(ctx), -// FloatTexture::GPUFloatPtex(texture) => texture.evaluate(ctx), -// FloatTexture::Windy(texture) => texture.evaluate(ctx), -// FloatTexture::Wrinkled(texture) => texture.evaluate(ctx), -// } -// } +impl FloatTextureTrait for FloatTexture { + fn evaluate(&self, ctx: &TextureEvalContext) -> Float { + match self { + FloatTexture::FloatImage(texture) => texture.evaluate(ctx), + FloatTexture::GPUFloatImage(texture) => texture.evaluate(ctx), + FloatTexture::FloatMix(texture) => texture.evaluate(ctx), + FloatTexture::FloatDirectionMix(texture) => texture.evaluate(ctx), + FloatTexture::FloatScaled(texture) => texture.evaluate(ctx), + FloatTexture::FloatConstant(texture) => texture.evaluate(ctx), + FloatTexture::FloatBilerp(texture) => texture.evaluate(ctx), + FloatTexture::FloatCheckerboard(texture) => texture.evaluate(ctx), + FloatTexture::FloatDots(texture) => texture.evaluate(ctx), + FloatTexture::FBm(texture) => texture.evaluate(ctx), + FloatTexture::FloatPtex(texture) => texture.evaluate(ctx), + FloatTexture::GPUFloatPtex(texture) => texture.evaluate(ctx), + FloatTexture::Windy(texture) => texture.evaluate(ctx), + FloatTexture::Wrinkled(texture) => texture.evaluate(ctx), + } + } } pub struct RGBConstantTexture; @@ -86,22 +188,22 @@ pub enum SpectrumTexture { } impl SpectrumTexture { -// pub fn evaluate(&self, ctx: TextureEvalContext) -> f32 { -// match self { -// SpectrumTexture::FloatImage(texture) => texture.evaluate(ctx), -// SpectrumTexture::GPUFloatImage(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatMix(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatDirectionMix(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatScaled(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatConstant(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatBilerp(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatCheckerboard(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatDots(texture) => texture.evaluate(ctx), -// SpectrumTexture::FBm(texture) => texture.evaluate(ctx), -// SpectrumTexture::FloatPtex(texture) => texture.evaluate(ctx), -// SpectrumTexture::GPUFloatPtex(texture) => texture.evaluate(ctx), -// SpectrumTexture::Windy(texture) => texture.evaluate(ctx), -// SpectrumTexture::Wrinkled(texture) => texture.evaluate(ctx), -// } -// } + // pub fn evaluate(&self, ctx: TextureEvalContext) -> f32 { + // match self { + // SpectrumTexture::FloatImage(texture) => texture.evaluate(ctx), + // SpectrumTexture::GPUFloatImage(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatMix(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatDirectionMix(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatScaled(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatConstant(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatBilerp(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatCheckerboard(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatDots(texture) => texture.evaluate(ctx), + // SpectrumTexture::FBm(texture) => texture.evaluate(ctx), + // SpectrumTexture::FloatPtex(texture) => texture.evaluate(ctx), + // SpectrumTexture::GPUFloatPtex(texture) => texture.evaluate(ctx), + // SpectrumTexture::Windy(texture) => texture.evaluate(ctx), + // SpectrumTexture::Wrinkled(texture) => texture.evaluate(ctx), + // } + // } } diff --git a/src/geometry/bounds.rs b/src/geometry/bounds.rs index d9805a0..58fb452 100644 --- a/src/geometry/bounds.rs +++ b/src/geometry/bounds.rs @@ -1,10 +1,10 @@ use super::{Float, NumFloat}; -use super::{Point, Point3, Point2f, Point3f, Vector, Vector2, Vector2f, Vector3, Vector3f}; -use crate::geometry::traits::VectorLike; +use super::{Point, Point2f, Point3, Point3f, Vector, Vector2, Vector2f, Vector3, Vector3f}; use crate::core::pbrt::lerp; +use crate::geometry::traits::{Sqrt, VectorLike}; use crate::geometry::{max, min}; use crate::utils::interval::Interval; -use num_traits::{Num, Bounded}; +use num_traits::{Bounded, Num}; use std::mem; use std::ops::{Add, Div, DivAssign, Mul, Sub}; @@ -131,6 +131,27 @@ where o } + pub fn corner(&self, corner_index: usize) -> Point { + let mut p_arr = [self.p_min[0]; N]; + for i in 0..N { + p_arr[i] = if ((corner_index >> i) & 1) == 1 { + self.p_max[i] + } else { + self.p_min[i] + } + } + Point(p_arr) + } + + pub fn overlaps(&self, rhs: &Self) -> bool { + for i in 0..N { + if self.p_max[i] < rhs.p_min[i] || self.p_min[i] > rhs.p_max[i] { + return false; + } + } + true + } + pub fn contains(&self, p: Point) -> bool { (0..N).all(|i| p[i] >= self.p_min[i] && p[i] <= self.p_max[i]) } @@ -181,11 +202,10 @@ where } impl Bounds3 -where - T: NumFloat + PartialOrd + Copy + Default +where + T: NumFloat + PartialOrd + Copy + Default + Sqrt, { - pub fn bounding_sphere(&self) -> (Point3, T) - { + pub fn bounding_sphere(&self) -> (Point3, T) { let two = T::one() + T::one(); let center = self.p_min + self.diagonal() / two; let radius = if self.contains(center) { diff --git a/src/geometry/cone.rs b/src/geometry/cone.rs index c0b3c60..09e5c33 100644 --- a/src/geometry/cone.rs +++ b/src/geometry/cone.rs @@ -1,6 +1,5 @@ use super::{Bounds3f, Float, PI, Point3f, Vector3f, VectorLike}; -use crate::core::pbrt::square; -use crate::utils::math::{degrees, safe_acos, safe_asin, safe_sqrt}; +use crate::utils::math::{degrees, safe_acos, safe_asin, safe_sqrt, square}; use crate::utils::transform::Transform; #[derive(Debug, Clone)] diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index d32445d..e51901f 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -5,15 +5,16 @@ pub mod ray; pub mod traits; pub use self::bounds::{Bounds, Bounds2f, Bounds2fi, Bounds2i, Bounds3f, Bounds3fi, Bounds3i}; -pub use self::primitives::{ - Frame, Normal, Normal3f, Point, Point2f, Point2i, Point2fi, Point3, Point3i, Point3f, Point3fi, Vector, Vector2, Vector2f, - Vector2fi, Vector2i, Vector3, Vector3f, Vector3fi, Vector3i -}; pub use self::cone::DirectionCone; +pub use self::primitives::{ + Frame, Normal, Normal3f, Point, Point2f, Point2fi, Point2i, Point3, Point3f, Point3fi, Point3i, + Vector, Vector2, Vector2f, Vector2fi, Vector2i, Vector3, Vector3f, Vector3fi, Vector3i, +}; pub use self::ray::{Ray, RayDifferential}; -pub use self::traits::{FloatVectorLike, VectorLike}; +pub use self::traits::{Lerp, Sqrt, Tuple, VectorLike}; -use crate::core::pbrt::{Float, PI, clamp_t, square}; +use crate::core::pbrt::{Float, PI, clamp_t}; +use crate::utils::math::square; use num_traits::Float as NumFloat; #[inline] @@ -88,6 +89,31 @@ pub fn spherical_triangle_area(a: Vector3f, b: Vector3f, c: Vector3 (2.0 * (a.dot(b.cross(c))).atan2(1.0 + a.dot(b) + a.dot(c) + b.dot(c))).abs() } +pub fn spherical_quad_area(a: Vector3f, b: Vector3f, c: Vector3f, d: Vector3f) -> Float { + let mut axb = a.cross(b); + let mut bxc = b.cross(c); + let mut cxd = c.cross(d); + let mut dxa = d.cross(a); + if axb.norm_squared() == 0. + || bxc.norm_squared() == 0. + || cxd.norm_squared() == 0. + || dxa.norm_squared() == 0. + { + return 0.; + } + axb = axb.normalize(); + bxc = bxc.normalize(); + cxd = cxd.normalize(); + dxa = dxa.normalize(); + + let alpha = dxa.angle_between(-axb); + let beta = axb.angle_between(-bxc); + let gamma = bxc.angle_between(-cxd); + let delta = cxd.angle_between(-dxa); + + (alpha + beta + gamma + delta - 2. * PI).abs() +} + pub fn spherical_theta(v: Vector3f) -> Float { clamp_t(v.z(), -1.0, 1.0).acos() } @@ -96,5 +122,3 @@ pub fn spherical_phi(v: Vector3f) -> Float { let p = v.y().atan2(v.x()); if p < 0.0 { p + 2.0 * PI } else { p } } - - diff --git a/src/geometry/primitives.rs b/src/geometry/primitives.rs index 357d71e..825f103 100644 --- a/src/geometry/primitives.rs +++ b/src/geometry/primitives.rs @@ -1,21 +1,22 @@ -use super::traits::Tuple; +use super::traits::{Lerp, Sqrt, Tuple, VectorLike}; use super::{Float, NumFloat, PI}; use crate::utils::interval::Interval; -use crate::utils::math::safe_asin; -use num_traits::{Num, Signed, Zero, FloatConst}; +use crate::utils::math::{difference_of_products, quadratic, safe_asin}; +use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero}; +use std::hash::{Hash, Hasher}; use std::iter::Sum; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; // N-dimensional displacement -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Vector(pub [T; N]); // N-dimensional location -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Point(pub [T; N]); // N-dimensional surface normal -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Normal(pub [T; N]); #[macro_export] @@ -38,6 +39,18 @@ macro_rules! impl_tuple_core { } } + impl Lerp for $Struct + where + Factor: Copy + Num, + T: Copy + Mul + Add, + { + #[inline] + fn lerp(t: Factor, a: Self, b: Self) -> Self { + let result = std::array::from_fn(|i| a[i] * (Factor::one() - t) + b[i] * t); + Self::from_array(result) + } + } + impl Default for $Struct { fn default() -> Self { Self([T::default(); N]) @@ -54,6 +67,7 @@ macro_rules! impl_tuple_core { } } + impl $Struct { #[inline] pub fn floor(&self) -> $Struct { @@ -69,6 +83,15 @@ macro_rules! impl_tuple_core { pub fn fill(value: T) -> Self { Self([value; N]) } + + #[inline] + pub fn cast(&self) -> $Struct + where + U: 'static + Copy, + T: 'static + Copy + AsPrimitive, + { + $Struct(self.0.map(|c| c.as_())) + } } impl Index for $Struct { @@ -196,61 +219,29 @@ macro_rules! impl_op_assign { #[macro_export] macro_rules! impl_float_vector_ops { ($Struct:ident) => { - // This impl block is constrained to only apply when the scalar type `T` is a float. - impl $Struct + impl VectorLike for $Struct where - T: NumFloat, + T: Copy + + Zero + + Add + + Mul + + Sub + + Div + + Sqrt, { - pub fn dot(self, rhs: Self) -> T { + type Scalar = T; + fn dot(self, rhs: Self) -> T { let mut sum = T::zero(); for i in 0..N { sum = sum + self[i] * rhs[i]; } sum } - - pub fn norm_squared(&self) -> T { - let mut sum = T::zero(); - for i in 0..N { - sum = sum + self[i] * self[i]; - } - sum - } - - pub fn norm(&self) -> T { - self.norm_squared().sqrt() - } - - pub fn normalize(self) -> Self { - let n = self.norm(); - if n.is_zero() { - self - } else { - self / n - } - } - - pub fn angle_between(self, rhs: Self) -> T { - let dot_product = self.normalize().dot(rhs.normalize()); - let clamped_dot = dot_product.min(T::one()).max(-T::one()); - clamped_dot.acos() - } - - pub fn project_on(self, rhs: Self) -> Self { - let rhs_norm_sq = rhs.norm_squared(); - if rhs_norm_sq.is_zero() { - // This now calls the inherent `zero()` method from `impl_tuple_core!` - Self::zero() - } else { - // Note: This requires Mul to be implemented for the struct, - // which your `impl_scalar_ops!` macro already does. - rhs * (self.dot(rhs) / rhs_norm_sq) - } - } } }; } + macro_rules! impl_abs { ($Struct:ident) => { impl $Struct @@ -325,12 +316,11 @@ impl_float_vector_ops!(Vector); impl_float_vector_ops!(Normal); impl_abs!(Vector); impl_abs!(Normal); - +impl_abs!(Point); impl_accessors!(Vector); impl_accessors!(Point); impl_accessors!(Normal); - impl From> for Normal { fn from(v: Vector) -> Self { Self(v.0) @@ -356,7 +346,7 @@ impl From> for Vector { impl Point where - T: NumFloat, + T: NumFloat + Sqrt, { pub fn distance(self, other: Self) -> T { (self - other).norm() @@ -367,6 +357,50 @@ where } } +impl Point2f { + pub fn invert_bilinear(p: Point2f, vert: &[Point2f]) -> Point2f { + let a = vert[0]; + let b = vert[1]; + let c = vert[3]; + let d = vert[2]; + let e = b - a; + let f = d - a; + let g = (a - b) + (c - d); + let h = p - a; + + let cross2d = |a: Vector2f, b: Vector2f| difference_of_products(a.x(), b.y(), a.y(), b.x()); + + let k2 = cross2d(g, f); + let k1 = cross2d(e, f) + cross2d(h, g); + let k0 = cross2d(h, e); + + // if edges are parallel, this is a linear equation + if k2.abs() < 0.001 { + if (e.x() * k1 - g.x() * k0).abs() < 1e-5 { + return Point2f::new( + (h.y() * k1 + f.y() * k0) / (e.y() * k1 - g.y() * k0), + -k0 / k1, + ); + } else { + return Point2f::new( + (h.x() * k1 + f.x() * k0) / (e.x() * k1 - g.x() * k0), + -k0 / k1, + ); + } + } + + if let Some((v0, v1)) = quadratic(k2, k1, k0) { + let u = (h.x() - f.x() * v0) / (e.x() + g.x() * v0); + if u < 0. || u > 1. || v0 < 0. || v0 > 1. { + return Point2f::new((h.x() - f.x() * v1) / (e.x() + g.x() * v1), v1); + } + return Point2f::new(u, v0); + } else { + return Point2f::zero(); + } + } +} + // Utility aliases and functions pub type Point2 = Point; pub type Point2f = Point2; @@ -428,6 +462,19 @@ where } } +impl Normal3 +where + T: Num + Copy + Neg, +{ + pub fn cross(self, rhs: Self) -> Self { + Self([ + self[1] * rhs[2] - self[2] * rhs[1], + self[2] * rhs[0] - self[0] * rhs[2], + self[0] * rhs[1] - self[1] * rhs[0], + ]) + } +} + impl Vector3 where T: Num + NumFloat + Copy + Neg, @@ -443,8 +490,76 @@ where }; (v2, self.cross(v2)) } + + pub fn coordinate_system_from_cpp(&self) -> (Self, Self) { + let sign = self.z().copysign(T::one()); + let a = -T::one() / (sign + self.z()); + let b = self.x() * self.y() * a; + let v2 = Self::new( + T::one() + sign * self.x().powi(2) * a, + sign * b, + -sign * self.x(), + ); + let v3 = Self::new(b, sign + self.y().powi(2) * a, -self.y()); + (v2, v3) + } } +impl Normal3 +where + T: Num + NumFloat + Copy + Neg, +{ + pub fn coordinate_system(&self) -> (Self, Self) + where + T: NumFloat, + { + let v2 = if self[0].abs() > self[1].abs() { + Self::new(-self[2], T::zero(), self[0]) / (self[0] * self[0] + self[2] * self[2]).sqrt() + } else { + Self::new(T::zero(), self[2], -self[1]) / (self[1] * self[1] + self[2] * self[2]).sqrt() + }; + (v2, self.cross(v2)) + } + + pub fn coordinate_system_from_cpp(&self) -> (Self, Self) { + let sign = self.z().copysign(T::one()); + let a = -T::one() / (sign + self.z()); + let b = self.x() * self.y() * a; + let v2 = Self::new( + T::one() + sign * self.x().powi(2) * a, + sign * b, + -sign * self.x(), + ); + let v3 = Self::new(b, sign + self.y().powi(2) * a, -self.y()); + (v2, v3) + } +} + +impl Hash for Vector { + fn hash(&self, state: &mut H) { + for item in self.0.iter() { + item.to_bits().hash(state); + } + } +} + +impl Hash for Point { + fn hash(&self, state: &mut H) { + for item in self.0.iter() { + item.to_bits().hash(state); + } + } +} + +impl Hash for Normal { + fn hash(&self, state: &mut H) { + for item in self.0.iter() { + item.to_bits().hash(state); + } + } +} + +// INTERVAL STUFF impl Point { pub fn new_from_point(p: Point) -> Self { let mut arr = [Interval::default(); N]; @@ -531,6 +646,16 @@ impl From> for Point { } } +impl From> for Vector { + fn from(pi: Vector) -> Self { + let mut arr = [0.0; N]; + for i in 0..N { + arr[i] = pi[i].midpoint(); + } + Vector(arr) + } +} + impl Mul> for Interval { type Output = Vector; fn mul(self, rhs: Vector) -> Self::Output { @@ -563,10 +688,14 @@ impl From> for Point { impl Normal3 where - T: Num + PartialOrd + Copy + Neg, + T: Num + PartialOrd + Copy + Neg + Sqrt, { pub fn face_forward(self, v: Vector3) -> Self { - if Vector3::::from(self).dot(v.into()) < T::zero() { -self } else { self } + if Vector3::::from(self).dot(v.into()) < T::zero() { + -self + } else { + self + } } } @@ -595,6 +724,22 @@ impl Frame { } } + pub fn from_xz(x: Vector3f, z: Vector3f) -> Self { + Self { + x, + y: z.cross(x), + z, + } + } + + pub fn from_xy(x: Vector3f, y: Vector3f) -> Self { + Self { + x, + y, + z: x.cross(y), + } + } + pub fn from_y(y: Vector3f) -> Self { let (z, x) = y.normalize().coordinate_system(); Self { diff --git a/src/geometry/ray.rs b/src/geometry/ray.rs index a065e79..cdff751 100644 --- a/src/geometry/ray.rs +++ b/src/geometry/ray.rs @@ -1,6 +1,7 @@ use super::{Normal3f, Point3f, Point3fi, Vector3f, VectorLike}; use crate::core::medium::Medium; -use crate::core::pbrt::{Float, next_float_down, next_float_up}; +use crate::core::pbrt::Float; +use crate::utils::math::{next_float_down, next_float_up}; use std::sync::Arc; #[derive(Clone, Debug)] diff --git a/src/geometry/traits.rs b/src/geometry/traits.rs index 391d375..436761c 100644 --- a/src/geometry/traits.rs +++ b/src/geometry/traits.rs @@ -1,7 +1,10 @@ +use crate::core::pbrt::Float; +use crate::utils::interval::Interval; +use crate::utils::math::{next_float_down, next_float_up}; +use num_integer::Roots; +use num_traits::{Float as NumFloat, FloatConst, Num, One, Signed, Zero}; use std::ops::{Add, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub}; -use num_traits::{Float as NumFloat, Num, Zero, One, FloatConst}; - pub trait Tuple: Sized + Copy + Index + IndexMut { @@ -10,6 +13,61 @@ pub trait Tuple: fn data_mut(&mut self) -> &mut [T; N]; fn from_array(arr: [T; N]) -> Self; + + #[inline] + fn permute(&self, p: [usize; N]) -> Self + where + T: Copy, + { + let new_data = p.map(|index| self[index]); + Self::from_array(new_data) + } + + fn max_component_value(&self) -> T + where + T: PartialOrd + Copy, + { + self.data() + .iter() + .copied() + .reduce(|a, b| if a > b { a } else { b }) + .expect("Cannot get max component of a zero-length tuple") + } + + fn min_component_value(&self) -> T + where + T: PartialOrd + Copy, + { + self.data() + .iter() + .copied() + .reduce(|a, b| if a < b { a } else { b }) + .expect("Cannot get min component of a zero-length tuple") + } + + fn max_component_index(&self) -> usize + where + T: PartialOrd, + { + self.data() + .iter() + .enumerate() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()) + .map(|(index, _)| index) + .unwrap_or(0) + } + + fn min_component_index(&self) -> usize + where + T: PartialOrd, + { + self.data() + .iter() + .enumerate() + .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()) + .map(|(index, _)| index) + .unwrap_or(0) + } } pub trait VectorLike: @@ -18,41 +76,106 @@ pub trait VectorLike: + Add + Sub + Div + + Mul { - type Scalar: NumFloat; + type Scalar: Copy + Zero + Add + Mul + Sqrt; fn dot(self, rhs: Self) -> Self::Scalar; - fn norm_squared(&self) -> Self::Scalar; + fn norm_squared(self) -> Self::Scalar { + self.dot(self) + } + + fn abs_dot(self, rhs: Self) -> Self::Scalar + where + Self::Scalar: Signed, + { + self.dot(rhs).abs() + } + + fn gram_schmidt(self, rhs: Self) -> Self { + self - rhs * self.dot(rhs) + } fn norm(&self) -> Self::Scalar { self.norm_squared().sqrt() } - fn normalize(self) -> Self { + fn normalize(self) -> Self + where + Self::Scalar: NumFloat, + { let n = self.norm(); - if n.is_zero() { - self - } else { - self / n - } + if n.is_zero() { self } else { self / n } } - fn angle_between(self, rhs: Self) -> Self::Scalar { + fn angle_between(self, rhs: Self) -> Self::Scalar + where + Self::Scalar: NumFloat, + { let dot_product = self.normalize().dot(rhs.normalize()); - let clamped_dot = dot_product.min(Self::Scalar::one()).max(-Self::Scalar::one()); + let clamped_dot = dot_product + .min(Self::Scalar::one()) + .max(-Self::Scalar::one()); clamped_dot.acos() } - - fn project_on(self, rhs: Self) -> Self { - let rhs_norm_sq = rhs.norm_squared(); - if rhs_norm_sq.is_zero() { - Self::zero() - } else { - let scale = self.dot(rhs) / rhs_norm_sq; - rhs * scale - } - } - - fn zero() -> Self; } +pub trait Sqrt { + fn sqrt(self) -> Self; +} + +impl Sqrt for Float { + fn sqrt(self) -> Self { + self.sqrt() + } +} + +impl Sqrt for f64 { + fn sqrt(self) -> Self { + self.sqrt() + } +} + +impl Sqrt for i32 { + fn sqrt(self) -> Self { + self.isqrt() + } +} + +impl Sqrt for u32 { + fn sqrt(self) -> Self { + self.isqrt() + } +} + +impl Sqrt for Interval { + fn sqrt(self) -> Self { + let low = if self.low < 0.0 { + 0.0 + } else { + next_float_down(self.low.sqrt()) + }; + + let high = if self.high < 0.0 { + 0.0 + } else { + next_float_up(self.high.sqrt()) + }; + + Self { low, high } + } +} + +pub trait Lerp: Sized + Copy { + fn lerp(t: Factor, a: Self, b: Self) -> Self; +} + +impl Lerp for T +where + T: Num + Copy + Mul + Add, +{ + #[inline] + fn lerp(t: T, a: Self, b: Self) -> Self { + a * (T::one() - t) + b * t + } +} diff --git a/src/lib.rs b/src/lib.rs index b2f737f..d13d191 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ #![allow(unused_imports, dead_code)] #![feature(float_erf)] +#![feature(f16)] +#![feature(generic_const_exprs)] mod camera; mod core; +mod geometry; mod lights; +mod shapes; mod utils; diff --git a/src/lights/diffuse.rs b/src/lights/diffuse.rs index 1df9ef3..2c2a4bc 100644 --- a/src/lights/diffuse.rs +++ b/src/lights/diffuse.rs @@ -1,16 +1,19 @@ use crate::core::medium::MediumInterface; use crate::core::pbrt::Float; +use crate::shapes::ShapeTrait; use crate::utils::spectrum::Spectrum; +use crate::utils::transform::Transform; -pub struct DiffuseAreaLight { +use std::sync::Arc; + +pub struct DiffuseAreaLight<'a> { pub l_emit: Spectrum, - // pub shape: Arc, + pub shape: Arc<&'a dyn ShapeTrait>, pub two_sided: bool, pub area: Float, - // inherited from class Light (see light.h) pub flags: u8, pub n_samples: i32, pub medium_interface: MediumInterface, - // light_to_world: Transform, - // world_to_light: Transform, + light_to_world: Transform, + world_to_light: Transform, } diff --git a/src/lights/mod.rs b/src/lights/mod.rs index ac6f588..ce202ae 100644 --- a/src/lights/mod.rs +++ b/src/lights/mod.rs @@ -1 +1,27 @@ pub mod diffuse; + +#[derive(Debug, Clone)] +pub struct DiffuseAreaLight; +#[derive(Debug, Clone)] +pub struct DistantLight; +#[derive(Debug, Clone)] +pub struct GonioPhotometricLight; +#[derive(Debug, Clone)] +pub struct InfiniteAreaLight; +#[derive(Debug, Clone)] +pub struct PointLight; +#[derive(Debug, Clone)] +pub struct ProjectionLight; +#[derive(Debug, Clone)] +pub struct SpotLight; + +#[derive(Debug, Clone)] +pub enum Light { + DiffuseArea(Box), + Distant(Box), + GonioPhotometric(Box), + InfiniteArea(Box), + Point(Box), + Projection(Box), + Spot(Box), +} diff --git a/src/shapes/bilinear.rs b/src/shapes/bilinear.rs new file mode 100644 index 0000000..badf952 --- /dev/null +++ b/src/shapes/bilinear.rs @@ -0,0 +1,713 @@ +use super::{ + BilinearIntersection, BilinearPatchShape, Bounds3f, DirectionCone, Interaction, Normal3f, + Point2f, Point3f, Point3fi, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext, + ShapeTrait, SurfaceInteraction, Vector3f, +}; +use crate::core::pbrt::{Float, clamp_t, gamma, lerp}; +use crate::geometry::{Tuple, VectorLike, spherical_quad_area}; +use crate::utils::math::{SquareMatrix, difference_of_products, quadratic}; +use crate::utils::mesh::BilinearPatchMesh; +use crate::utils::sampling::{ + bilinear_pdf, invert_spherical_rectangle_sample, sample_bilinear, sample_spherical_rectangle, +}; +use crate::utils::transform::Transform; +use std::sync::Arc; +use std::sync::OnceLock; + +struct PatchData<'a> { + mesh: &'a BilinearPatchMesh, + p00: Point3f, + p10: Point3f, + p01: Point3f, + p11: Point3f, + n: Option<[Normal3f; 4]>, + uv: Option<[Point2f; 4]>, +} + +struct IntersectionData { + t: Float, + u: Float, + v: Float, +} + +struct TextureDerivative { + duds: Float, + dvds: Float, + dudt: Float, + dvdt: Float, +} + +static BILINEAR_MESHES: OnceLock>> = OnceLock::new(); +impl BilinearPatchShape { + pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 1e-4; + + pub fn new(_mesh: BilinearPatchMesh, mesh_index: usize, blp_index: usize) -> Self { + let mut bp = BilinearPatchShape { + mesh_index, + blp_index, + area: 0., + rectangle: false, + }; + + let (p00, p10, p01, p11) = { + let data = bp.get_data(); + (data.p00, data.p10, data.p01, data.p11) + }; + + bp.rectangle = bp.is_rectangle(p00, p10, p01, p11); + + if bp.rectangle { + bp.area = p00.distance(p01) + p00.distance(p10); + } else { + const NA: usize = 3; + let mut p = [[Point3f::default(); NA + 1]; NA + 1]; + for i in 0..=NA { + let u = i as Float / NA as Float; + for j in 0..=NA { + let v = j as Float / NA as Float; + p[i][j] = lerp(u, lerp(v, p00, p01), lerp(v, p10, p11)); + } + } + + let mut area = 0.; + for i in 0..NA { + for j in 0..NA { + let diag1 = p[i + 1][j + 1] - p[i][j]; + let diag2 = p[i + 1][j] - p[i][j + 1]; + let quad_area = 0.5 * diag1.cross(diag2).norm(); + area += quad_area; + } + } + bp.area = area; + } + bp + } + + fn mesh(&self) -> &Arc { + let meshes = BILINEAR_MESHES + .get() + .expect("Mesh has not been initialized"); + &meshes[self.mesh_index] + } + + fn get_data(&self) -> PatchData { + let mesh = self.mesh(); + let start_index = 4 * self.blp_index; + let v = &mesh.vertex_indices[start_index..start_index + 4]; + let p00: Point3f = mesh.p[v[0] as usize]; + let p10: Point3f = mesh.p[v[1] as usize]; + let p01: Point3f = mesh.p[v[2] as usize]; + let p11: Point3f = mesh.p[v[3] as usize]; + let n = mesh + .n + .as_ref() + .map(|normals| [normals[v[0]], normals[v[1]], normals[v[2]], normals[v[3]]]); + let uv = mesh + .uv + .as_ref() + .map(|uvs| [uvs[v[0]], uvs[v[1]], uvs[v[2]], uvs[v[3]]]); + PatchData { + mesh, + p00, + p10, + p01, + p11, + n, + uv, + } + } + + fn is_rectangle(&self, p00: Point3f, p10: Point3f, p01: Point3f, p11: Point3f) -> bool { + if p00 == p01 || p01 == p11 || p11 == p10 || p10 == p00 { + return false; + } + + let n = Normal3f::from((p10 - p00).cross(p01 - p00).normalize()); + if (p11 - p00).normalize().dot(n.into()).abs() > 1e-5 { + return false; + } + + let p_center_vec = Vector3f::from(p00 + p01.into() + p10.into() + p11.into()) / 4.; + let p_center = Point3f::from(p_center_vec); + let d2 = [ + p00.distance_squared(p_center), + p01.distance_squared(p_center), + p10.distance_squared(p_center), + p11.distance_squared(p_center), + ]; + + for i in 0..4 { + if (d2[i] - d2[0]).abs() / d2[0] > 1e-4 { + return false; + } + } + return true; + } + + fn intersect_bilinear_patch( + &self, + ray: &Ray, + t_max: Float, + data: &PatchData, + ) -> Option { + let a = (data.p10 - data.p00).cross(data.p01 - data.p11).dot(ray.d); + let c = (data.p00 - ray.o).cross(ray.d).dot(data.p01 - data.p00); + let b = (data.p10 - ray.o).cross(ray.d).dot(data.p11 - data.p10) - (a + c); + + let (u1, u2) = quadratic(a, b, c)?; + + let eps = gamma(10) + * (ray.o.abs().max_component_value() + + ray.d.abs().max_component_value() + + data.p00.abs().max_component_value() + + data.p10.abs().max_component_value() + + data.p01.abs().max_component_value() + + data.p11.abs().max_component_value()); + + let hit1 = self.check_candidate(u1, ray, data); + let hit2 = if u1 != u2 { + self.check_candidate(u2, ray, data) + } else { + None + }; + + // Find best hit from candidates + [hit1, hit2] + .into_iter() + .flatten() + .filter(|hit| hit.t < t_max && hit.t > eps) + .min_by(|a, b| a.t.partial_cmp(&b.t).unwrap()) + .map(|best_hit| { + BilinearIntersection::new(Point2f::new(best_hit.u, best_hit.v), best_hit.t) + }) + } + + fn check_candidate(&self, u: Float, ray: &Ray, data: &PatchData) -> Option { + if !(0.0..=1.0).contains(&u) { + return None; + } + let uo: Point3f = lerp(u, data.p00, data.p10); + let ud: Point3f = Point3f::from(lerp(u, data.p01, data.p11) - uo); + let deltao = uo - ray.o; + let perp = ray.d.cross(ud.into()); + let p2 = perp.norm_squared(); + if p2 == 0. { + return None; + } + let v_det = SquareMatrix::::new([ + [deltao.x(), ray.d.x(), perp.x()], + [deltao.y(), ray.d.y(), perp.y()], + [deltao.z(), ray.d.z(), perp.z()], + ]) + .determinant(); + + if !(0.0..=p2).contains(&v_det) { + return None; + } + + let t_det = SquareMatrix::::new([ + [deltao.x(), ud.x(), perp.x()], + [deltao.y(), ud.y(), perp.y()], + [deltao.z(), ud.z(), perp.z()], + ]) + .determinant(); + + Some(IntersectionData { + t: t_det / p2, + u, + v: v_det / p2, + }) + } + + fn interaction_from_intersection( + &self, + data: &PatchData, + uv: Point2f, + time: Float, + wo: Vector3f, + ) -> SurfaceInteraction { + // Base geometry and derivatives + let p = lerp( + uv[0], + lerp(uv[1], data.p00, data.p01), + lerp(uv[1], data.p10, data.p11), + ); + let mut dpdu = lerp(uv[1], data.p10, data.p11) - lerp(uv[1], data.p00, data.p01); + let mut dpdv = lerp(uv[0], data.p01, data.p11) - lerp(uv[0], data.p00, data.p10); + + // If textured, apply coordinates + let (st, derivatives) = self.apply_texture_coordinates(data, uv, &mut dpdu, &mut dpdv); + + // Compute second moments + let n = Normal3f::from(dpdu.cross(dpdv).normalize()); + let (mut dndu, mut dndv) = self.calculate_surface_curvature(data, &dpdu, &dpdv, n); + if let Some(ref deriv) = derivatives { + let dnds = Normal3f::from(dndu * deriv.duds + dndv * deriv.dvds); + let dndt = Normal3f::from(dndu * deriv.dudt + dndv * deriv.dvdt); + dndu = dnds; + dndv = dndt; + } + + let p_abs_sum = data.p00.abs() + + Vector3f::from(data.p01.abs()) + + Vector3f::from(data.p10.abs()) + + Vector3f::from(data.p11.abs()); + let p_error = gamma(6) * Vector3f::from(p_abs_sum); + + let flip_normal = data.mesh.reverse_orientation ^ data.mesh.transform_swaps_handedness; + let pi = Point3fi::new_with_error(p, p_error); + let mut isect = + SurfaceInteraction::new(pi, st, wo, dpdu, dpdv, dndu, dndv, time, flip_normal); + if data.n.is_some() { + self.apply_shading_normals(&mut isect, data, uv, derivatives); + } + isect + } + + fn apply_texture_coordinates( + &self, + data: &PatchData, + uv: Point2f, + dpdu: &mut Vector3f, + dpdv: &mut Vector3f, + ) -> (Point2f, Option) { + let Some(uvs) = data.uv else { + return (uv, None); + }; + let uv00 = uvs[0]; + let uv01 = uvs[1]; + let uv10 = uvs[2]; + let uv11 = uvs[3]; + // Compute partial derivatives of (u, v) with respect to (s, t) + let st = lerp(uv[0], lerp(uv[1], uv00, uv01), lerp(uv[1], uv10, uv11)); + let dstdu = lerp(uv[1], uv10, uv11) - lerp(uv[1], uv00, uv01); + let dstdv = lerp(uv[0], uv01, uv11) - lerp(uv[0], uv00, uv10); + + let det = difference_of_products(dstdu[0], dstdv[1], dstdu[1], dstdv[0]); + let inv_det = if det == 0.0 { 0.0 } else { 1.0 / det }; + let duds = dstdv[1] * inv_det; + let dvds = -dstdv[0] * inv_det; + let dudt = -dstdu[1] * inv_det; + let dvdt = dstdu[0] * inv_det; + + let dpds = *dpdu * duds + *dpdv * dvds; + let mut dpdt = *dpdu * dudt + *dpdv * dvdt; + + if dpdu.cross(*dpdv).dot(dpds.cross(dpdt)) < 0. { + dpdt = -dpdt; + } + *dpdu = dpds; + *dpdv = dpdt; + + let factors = TextureDerivative { + duds, + dvds, + dudt, + dvdt, + }; + (st, Some(factors)) + } + + fn calculate_base_derivatives( + &self, + data: &PatchData, + uv: Point2f, + ) -> (Point3f, Vector3f, Vector3f) { + let p = lerp( + uv[0], + lerp(uv[1], data.p00, data.p01), + lerp(uv[1], data.p10, data.p11), + ); + let dpdu = lerp(uv[1], data.p10, data.p11) - lerp(uv[1], data.p00, data.p01); + let dpdv = lerp(uv[0], data.p01, data.p11) - lerp(uv[0], data.p00, data.p10); + (p, dpdu, dpdv) + } + + fn calculate_surface_curvature( + &self, + data: &PatchData, + dpdu: &Vector3f, + dpdv: &Vector3f, + n: Normal3f, + ) -> (Normal3f, Normal3f) { + let e = dpdu.dot(*dpdu); + let f = dpdu.dot(*dpdv); + let g = dpdv.dot(*dpdv); + let d2pduv = (data.p00 - data.p01) + (data.p11 - data.p10); + let d2pduu = Vector3f::zero(); + let d2pdvv = Vector3f::zero(); + + let e_min = n.dot(d2pduu.into()); + let f_min = n.dot(d2pduv.into()); + let g_min = n.dot(d2pdvv.into()); + + let egf2 = difference_of_products(e, g, f, f); + let inv_egf2 = if egf2 == 0. { 0. } else { 1. / egf2 }; + + let dndu = Normal3f::from( + (f_min * f - e_min * g) * inv_egf2 * *dpdu + (e_min * f - f_min * e) * inv_egf2 * *dpdv, + ); + let dndv = Normal3f::from( + (g_min * f - f_min * g) * inv_egf2 * *dpdu + (f_min * f - g_min * e) * inv_egf2 * *dpdv, + ); + (dndu, dndv) + } + + fn apply_shading_normals( + &self, + isect: &mut SurfaceInteraction, + data: &PatchData, + uv: Point2f, + derivatives: Option, + ) { + let Some(normals) = data.n else { return }; + let n00 = normals[1]; + let n10 = normals[1]; + let n01 = normals[2]; + let n11 = normals[3]; + let ns = lerp(uv[0], lerp(uv[1], n00, n01), lerp(uv[1], n10, n11)).normalize(); + let mut dndu = lerp(uv[1], n10, n11) - lerp(uv[1], n00, n01); + let mut dndv = lerp(uv[0], n01, n11) - lerp(uv[0], n00, n10); + + if let Some(deriv) = derivatives { + let dnds = dndu * deriv.duds + dndv * deriv.dvds; + let dndt = dndu * deriv.dudt + dndv * deriv.dvdt; + dndu = dnds; + dndv = dndt; + } + let r = Transform::rotate_from_to(isect.n().normalize().into(), ns.into()); + + isect.set_shading_geometry( + ns, + r.apply_to_vector(isect.dpdu), + r.apply_to_vector(isect.dpdv), + dndu, + r.apply_to_normal(dndv), + true, + ); + } + + fn sample_area_and_pdf(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option { + let mut ss = self.sample(u)?; + + let mut intr_clone = (*ss.intr).clone(); + intr_clone.common.time = ctx.time; + ss.intr = Arc::new(intr_clone); + + let mut wi = ss.intr.p() - ctx.p(); + let dist_sq = wi.norm_squared(); + if dist_sq == 0. { + return None; + } + + wi = wi.normalize(); + let abs_dot = Vector3f::from(ss.intr.n()).abs_dot(-wi); + if abs_dot == 0. { + return None; + } + + ss.pdf *= dist_sq / abs_dot; + + if ss.pdf.is_infinite() { None } else { Some(ss) } + } + + fn sample_parametric_coords(&self, data: &PatchData, u: Point2f) -> (Point2f, Float) { + if let Some(image_distrib) = &data.mesh.image_distribution { + let (uv, pdf, _) = image_distrib.sample(u); + (uv, pdf) + } else if !self.rectangle { + let w = [ + (data.p10 - data.p00).cross(data.p01 - data.p00).norm(), + (data.p10 - data.p00).cross(data.p11 - data.p10).norm(), + (data.p01 - data.p00).cross(data.p11 - data.p01).norm(), + (data.p11 - data.p10).cross(data.p11 - data.p01).norm(), + ]; + let uv = sample_bilinear(u, &w); + let pdf = bilinear_pdf(uv, &w); + (uv, pdf) + } else { + (u, 1.0) + } + } + + fn sample_solid_angle( + &self, + data: &PatchData, + ctx: &ShapeSampleContext, + u: Point2f, + corner_dirs: &[Vector3f; 4], + ) -> Option { + let mut pdf = 1.; + if ctx.ns != Normal3f::zero() { + let w = [ + 0.01_f32.max(corner_dirs[0].dot(ctx.ns.into()).abs()), + 0.01_f32.max(corner_dirs[1].dot(ctx.ns.into()).abs()), + 0.01_f32.max(corner_dirs[2].dot(ctx.ns.into()).abs()), + 0.01_f32.max(corner_dirs[3].dot(ctx.ns.into()).abs()), + ]; + let u = sample_bilinear(u, &w); + pdf *= bilinear_pdf(u, &w); + } + + let eu = data.p10 - data.p00; + let ev = data.p01 - data.p00; + let (p, quad_pdf) = sample_spherical_rectangle(ctx.p(), data.p00, eu, ev, u); + pdf *= quad_pdf?; + + // Compute (u, v) and surface normal for sampled points on rectangle + let uv = Point2f::new( + (p - data.p00).dot(eu) / data.p10.distance_squared(data.p00), + (p - data.p00).dot(ev) / data.p01.distance_squared(data.p00), + ); + + let n = self.compute_sampled_normal(data, &eu, &ev, uv); + let st = data.uv.map_or(uv, |uvs| { + lerp( + uv[0], + lerp(uv[1], uvs[0], uvs[1]), + lerp(uv[1], uvs[2], uvs[3]), + ) + }); + + let pi = Point3fi::new_from_point(p); + let mut intr = SurfaceInteraction::new_simple(pi, n, st); + intr.common.time = ctx.time; + Some(ShapeSample { + intr: Arc::new(intr), + pdf, + }) + } + + fn compute_sampled_normal( + &self, + data: &PatchData, + dpdu: &Vector3f, + dpdv: &Vector3f, + uv: Point2f, + ) -> Normal3f { + let mut n = Normal3f::from(dpdu.cross(*dpdv).normalize()); + + if let Some(normals) = data.n { + // Apply interpolated shading normal to orient the geometric normal + let ns = lerp( + uv[0], + lerp(uv[1], normals[0], normals[2]), + lerp(uv[1], normals[1], normals[3]), + ); + n = n.face_forward(ns.into()); + } else if data.mesh.reverse_orientation ^ data.mesh.transform_swaps_handedness { + n = -n; + } + n + } +} + +impl ShapeTrait for BilinearPatchShape { + fn area(&self) -> Float { + self.area + } + + fn normal_bounds(&self) -> DirectionCone { + let data = self.get_data(); + if data.p00 == data.p10 + || data.p10 == data.p11 + || data.p11 == data.p01 + || data.p01 == data.p00 + { + let dpdu = lerp(0.5, data.p10, data.p11) - lerp(0.5, data.p00, data.p01); + let dpdv = lerp(0.5, data.p01, data.p11) - lerp(0.5, data.p00, data.p10); + let mut n = Normal3f::from(dpdu.cross(dpdv).normalize()); + if let Some(normals) = data.n { + let interp_n = (normals[0] + normals[1] + normals[2] + normals[3]) / 4.; + n = n.face_forward(interp_n.into()); + } else if data.mesh.reverse_orientation ^ data.mesh.transform_swaps_handedness { + n *= -1.; + } + return DirectionCone::new_from_vector(Vector3f::from(n)); + } + + // Compute bilinear patch normals n10, n01, and n11 + let mut n00 = Normal3f::from((data.p10 - data.p00).cross(data.p01 - data.p00).normalize()); + let mut n10 = Normal3f::from((data.p11 - data.p10).cross(data.p00 - data.p10).normalize()); + let mut n01 = Normal3f::from((data.p00 - data.p01).cross(data.p11 - data.p01).normalize()); + let mut n11 = Normal3f::from((data.p01 - data.p11).cross(data.p10 - data.p11).normalize()); + + if let Some(normals) = data.n { + n00 = n00.face_forward(normals[0].into()); + n10 = n10.face_forward(normals[1].into()); + n01 = n01.face_forward(normals[2].into()); + n11 = n11.face_forward(normals[3].into()); + } else if data.mesh.reverse_orientation ^ data.mesh.transform_swaps_handedness { + n00 = -n00; + n10 = -n10; + n01 = -n01; + n11 = -n11; + } + + // Compute average normal and return normal bounds for patch + let n_avg = (n00 + n10 + n01 + n11).normalize(); + let cos_theta = n_avg + .dot(n00) + .min(n_avg.dot(n10)) + .min(n_avg.dot(n01)) + .min(n_avg.dot(n11)); + DirectionCone::new(n_avg.into(), clamp_t(cos_theta, -1., 1.)) + } + + fn bounds(&self) -> Bounds3f { + let data = self.get_data(); + Bounds3f::from_points(data.p00, data.p01).union(Bounds3f::from_points(data.p10, data.p11)) + } + + fn intersect(&self, ray: &Ray, t_max: Option) -> Option { + let t_max_val = t_max?; + let data = self.get_data(); + if let Some(bilinear_hit) = self.intersect_bilinear_patch(ray, t_max_val, &data) { + let intr = self.interaction_from_intersection(&data, bilinear_hit.uv, ray.time, -ray.d); + + Some(ShapeIntersection { + intr, + t_hit: bilinear_hit.t, + }) + } else { + None + } + } + + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { + let t_max_val = t_max.unwrap_or(Float::INFINITY); + let data = self.get_data(); + self.intersect_bilinear_patch(ray, t_max_val, &data) + .is_some() + } + + fn sample(&self, u: Point2f) -> Option { + let data = self.get_data(); + // Sample bilinear patch parametric coordinate (u, v) + let (uv, pdf) = self.sample_parametric_coords(&data, u); + // Compute bilinear patch geometric quantities at sampled (u, v) + let (p, dpdu, dpdv) = self.calculate_base_derivatives(&data, uv); + if dpdu.norm_squared() == 0. || dpdv.norm_squared() == 0. { + return None; + } + + // Compute surface normal for sampled bilinear patch (u, v) + let n = self.compute_sampled_normal(&data, &dpdu, &dpdv, uv); + let st = data.uv.map_or(uv, |patch_uvs| { + lerp( + uv[0], + lerp(uv[1], patch_uvs[0], patch_uvs[1]), + lerp(uv[1], patch_uvs[2], patch_uvs[3]), + ) + }); + + let p_abs_sum = data.p00.abs() + + Vector3f::from(data.p01.abs()) + + Vector3f::from(data.p10.abs()) + + Vector3f::from(data.p11.abs()); + let p_error = gamma(6) * Vector3f::from(p_abs_sum); + let pi = Point3fi::new_with_error(p, p_error); + Some(ShapeSample { + intr: Arc::new(SurfaceInteraction::new_simple(pi, n, st)), + pdf: pdf / dpdu.cross(dpdv).norm(), + }) + } + + fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option { + let data = self.get_data(); + let v00 = (data.p00 - ctx.p()).normalize(); + let v10 = (data.p10 - ctx.p()).normalize(); + let v01 = (data.p01 - ctx.p()).normalize(); + let v11 = (data.p11 - ctx.p()).normalize(); + + let use_area_sampling = self.rectangle + || data.mesh.image_distribution.is_some() + || spherical_quad_area(v00, v10, v11, v01) <= Self::MIN_SPHERICAL_SAMPLE_AREA; + if use_area_sampling { + self.sample_area_and_pdf(ctx, u) + } else { + self.sample_solid_angle(&data, ctx, u, &[v00, v10, v01, v11]) + } + } + + fn pdf(&self, intr: Arc<&dyn Interaction>) -> Float { + let Some(si) = intr.as_any().downcast_ref::() else { + return 0.; + }; + let data = self.get_data(); + let uv = if let Some(uvs) = &data.mesh.uv { + Point2f::invert_bilinear(si.uv, &uvs) + } else { + si.uv + }; + + let param_pdf = if let Some(image_distrib) = &data.mesh.image_distribution { + image_distrib.pdf(uv) + } else if self.rectangle { + let w = [ + (data.p10 - data.p00).cross(data.p01 - data.p00).norm(), + (data.p10 - data.p00).cross(data.p11 - data.p10).norm(), + (data.p01 - data.p00).cross(data.p11 - data.p01).norm(), + (data.p11 - data.p10).cross(data.p11 - data.p01).norm(), + ]; + bilinear_pdf(uv, &w) + } else { + 1. + }; + + let (_, dpdu, dpdv) = self.calculate_base_derivatives(&data, uv); + let cross = dpdu.cross(dpdv).norm(); + if cross == 0. { 0. } else { param_pdf / cross } + } + + fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float { + let ray = ctx.spawn_ray(wi); + let Some(isect) = self.intersect(&ray, None) else { + return 0.; + }; + + let data = self.get_data(); + + let v00 = (data.p00 - ctx.p()).normalize(); + let v10 = (data.p10 - ctx.p()).normalize(); + let v01 = (data.p01 - ctx.p()).normalize(); + let v11 = (data.p11 - ctx.p()).normalize(); + + let use_area_sampling = !self.rectangle + || data.mesh.image_distribution.is_some() + || spherical_quad_area(v00, v10, v01, v11) <= Self::MIN_SPHERICAL_SAMPLE_AREA; + if use_area_sampling { + let isect_pdf = self.pdf(Arc::new(&isect.intr)); + let distsq = ctx.p().distance_squared(isect.intr.p()); + let absdot = Vector3f::from(isect.intr.n()).abs_dot(-wi); + if absdot == 0. { + return 0.; + } + let pdf = isect_pdf * distsq / absdot; + if pdf.is_infinite() { + return 0.; + } else { + return pdf; + } + } else { + let mut pdf = 1. / spherical_quad_area(v00, v10, v01, v11); + if ctx.ns != Normal3f::zero() { + let w = [ + 0.01_f32.max(v00.dot(ctx.ns.into()).abs()), + 0.01_f32.max(v10.dot(ctx.ns.into()).abs()), + 0.01_f32.max(v01.dot(ctx.ns.into()).abs()), + 0.01_f32.max(v11.dot(ctx.ns.into()).abs()), + ]; + let u = invert_spherical_rectangle_sample( + ctx.p(), + data.p00, + data.p10 - data.p00, + data.p01 - data.p00, + isect.intr.p(), + ); + pdf *= bilinear_pdf(u, &w); + } + pdf + } + } +} diff --git a/src/shapes/curves.rs b/src/shapes/curves.rs new file mode 100644 index 0000000..7dc517b --- /dev/null +++ b/src/shapes/curves.rs @@ -0,0 +1,316 @@ +use crate::core::pbrt::{clamp_t, lerp}; +use crate::utils::math::square; +use crate::utils::splines::{ + bound_cubic_bezier, cubic_bezier_control_points, evaluate_cubic_bezier, subdivide_cubic_bezier, +}; +use crate::utils::transform::look_at; + +use super::{ + Bounds3f, CurveCommon, CurveShape, CurveType, DirectionCone, Float, Interaction, Normal3f, + Point2f, Point3f, Point3fi, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext, + ShapeTrait, SurfaceInteraction, Transform, Vector2f, Vector3f, VectorLike, +}; +use std::sync::Arc; + +struct IntersectionContext<'a> { + ray: &'a Ray, + object_from_ray: &'a Transform, + common: &'a CurveCommon<'a>, +} + +impl<'a> CurveShape<'a> { + pub fn new(common: CurveCommon<'a>, u_min: Float, u_max: Float) -> Self { + Self { + common, + u_min, + u_max, + } + } + + fn intersect_ray(&self, r: &Ray, t_max: Float) -> Option { + let ray = self + .common + .object_from_render + .apply_to_ray(r, &mut Some(t_max)); + // Get object-space control points for curve segment, cpObj + let cp_obj = cubic_bezier_control_points(&self.common.cp_obj, self.u_min, self.u_max); + // Project curve control points to plane perpendicular to ray + let mut dx = ray.d.cross(cp_obj[3] - cp_obj[0]); + if dx.norm_squared() == 0. { + (dx, _) = ray.d.coordinate_system(); + } + + let ray_from_object = look_at(ray.o, ray.o + ray.d, dx.into()).expect("Inversion error"); + let cp = [0; 4].map(|i| ray_from_object.apply_to_point(cp_obj[i])); + + // Test ray against bound of projected control points + let max_width = lerp(self.u_min, self.common.width[0], self.common.width[1]).max(lerp( + self.u_max, + self.common.width[0], + self.common.width[1], + )); + let curve_bounds = Bounds3f::from_points(cp[0], cp[1]) + .union(Bounds3f::from_points(cp[2], cp[3])) + .expand(0.5 * max_width); + let ray_bounds = + Bounds3f::from_points(Point3f::zero(), Point3f::new(0., 0., ray.d.norm() * t_max)); + if !ray_bounds.overlaps(&curve_bounds) { + return None; + } + + let l0 = (0..2) + .map(|i| { + (cp[i].x() - 2.0 * cp[i + 1].x() + cp[i + 2].x()) + .abs() + .max((cp[i].y() - 2.0 * cp[i + 1].y() + cp[i + 2].y()).abs()) + .max((cp[i].z() - 2.0 * cp[i + 1].z() + cp[i + 2].z()).abs()) + }) + .fold(0.0, Float::max); + + let max_depth = if l0 > 0. { + let eps = self.common.width[0].max(self.common.width[1]) * 0.05; + let r0: i32 = (1.41421356237 * 6. * l0 / (8. * eps)).log2() as i32 / 2; + clamp_t(r0, 0, 10) + } else { + 0 + }; + + let context = IntersectionContext { + ray: &ray, + object_from_ray: &ray_from_object.inverse(), + common: &self.common, + }; + + self.recursive_intersect(&context, t_max, &cp, self.u_min, self.u_max, max_depth) + } + + fn recursive_intersect( + &self, + context: &IntersectionContext, + mut t_max: Float, + cp: &[Point3f], + u0: Float, + u1: Float, + depth: i32, + ) -> Option { + if depth > 0 { + let cp_split = subdivide_cubic_bezier(cp); + let u = [u0, (u0 + u1) / 2., u1]; + let mut best_hit: Option = None; + + for seg in 0..2 { + let cps: &[Point3f] = &cp_split[3 * seg..3 * seg + 4]; + + let max_width = lerp(u[seg], self.common.width[0], self.common.width[1]).max(lerp( + u[seg + 1], + self.common.width[0], + self.common.width[1], + )); + let curve_bounds = Bounds3f::from_points(cps[0], cps[1]) + .union(Bounds3f::from_points(cps[2], cps[3])) + .expand(0.5 * max_width); + let ray_bounds = Bounds3f::from_points( + Point3f::zero(), + Point3f::new(0., 0., context.ray.d.norm() * t_max), + ); + + if !ray_bounds.overlaps(&curve_bounds) { + continue; + } + + if let Some(hit) = + self.recursive_intersect(context, t_max, cps, u[seg], u[seg + 1], depth - 1) + { + best_hit = Some(hit); + t_max = best_hit.as_ref().unwrap().t_hit; + } + } + best_hit + } else { + self.intersect_segment(context, t_max, cp, u0, u1) + } + } + + fn intersect_segment( + &self, + context: &IntersectionContext, + t_max: Float, + cp: &[Point3f], + u0: Float, + u1: Float, + ) -> Option { + let edge1 = (cp[1].y() - cp[0].y()) * -cp[0].y() + cp[0].x() * (cp[0].x() - cp[1].x()); + let edge2 = (cp[2].y() - cp[3].y()) * -cp[3].y() + cp[3].x() * (cp[3].x() - cp[2].x()); + if edge1 <= 0.0 || edge2 <= 0.0 { + return None; + } + + let segment_dir = Point2f::new(cp[3].x(), cp[3].y()) - Point2f::new(cp[0].x(), cp[0].y()); + let denom = segment_dir.norm_squared(); + if denom == 0. { + return None; + } + let w = Vector2f::new(cp[0].x(), cp[0].y()).dot(-segment_dir) / denom; + let u = clamp_t(lerp(w, u0, u1), u0, u1); + let ray_length = context.ray.d.norm(); + + let mut hit_width = lerp(u, self.common.width[0], self.common.width[1]); + let mut n_hit = Normal3f::zero(); + if let CurveType::Ribbon = context.common.curve_type { + n_hit = if context.common.normal_angle == 0. { + context.common.n[0] + } else { + let sin0 = + ((1. - u) * self.common.normal_angle).sin() * self.common.inv_sin_normal_angle; + let sin1 = (u * self.common.normal_angle).sin() * self.common.inv_sin_normal_angle; + sin0 * self.common.n[0] + sin1 * self.common.n[1] + }; + hit_width = hit_width * n_hit.dot(context.ray.d.into()).abs() / ray_length; + } + + let (pc, dpcdw) = evaluate_cubic_bezier(cp, clamp_t(w, 0., 1.)); + let ray_length = context.ray.d.norm(); + + if !self.valid_hit(pc, hit_width, t_max, ray_length) { + return None; + } + + // Hit is valid, obtain normals, and interaction differentials + self.intersection_result(context, pc, dpcdw, u, hit_width, n_hit, ray_length) + } + + fn hit_ribbon( + &self, + u: Float, + hit_width: Float, + context: &IntersectionContext, + ) -> (Float, Normal3f) { + let n_hit = if context.common.normal_angle == 0. { + context.common.n[0] + } else { + let sin0 = + ((1. - u) * self.common.normal_angle).sin() * self.common.inv_sin_normal_angle; + let sin1 = (u * self.common.normal_angle).sin() * self.common.inv_sin_normal_angle; + sin0 * self.common.n[0] + sin1 * self.common.n[1] + }; + let new_hit_width = + hit_width * n_hit.dot(context.ray.d.into()).abs() / context.ray.d.norm(); + + (new_hit_width, n_hit) + } + + fn valid_hit(&self, pc: Point3f, hit_width: Float, t_max: Float, ray_length: Float) -> bool { + let pt_curve_dist_sq = square(pc.x()) + square(pc.y()); + if pt_curve_dist_sq > square(hit_width * 0.5) || pc.z() < 0.0 || pc.z() > ray_length * t_max + { + return false; + } + true + } + + fn intersection_result( + &self, + context: &IntersectionContext, + pc: Point3f, + dpcdw: Vector3f, + u: Float, + hit_width: Float, + n_hit: Normal3f, + ray_length: Float, + ) -> Option { + let t_hit = pc.z() / ray_length; + let pt_curve_dist = (square(pc.x()) + square(pc.y())).sqrt(); + let edge_func = dpcdw.x() * -pc.y() + dpcdw.y() * pc.x(); + let v = if edge_func > 0. { + 0.5 + pt_curve_dist / hit_width + } else { + 0.5 - pt_curve_dist / hit_width + }; + let (_, dpdu) = evaluate_cubic_bezier(&self.common.cp_obj, u); + let dpdv = match context.common.curve_type { + CurveType::Ribbon => Vector3f::from(n_hit).cross(dpdu).normalize() * hit_width, + _ => { + let dpdu_plane = context.object_from_ray.apply_inverse_vector(dpdu); + let mut dpdv_plane = + Vector3f::new(-dpdu_plane.y(), dpdu_plane.x(), 0.).normalize() * hit_width; + if context.common.curve_type == CurveType::Cylinder { + let theta = lerp(v, -90., 90.); + let rot = Transform::rotate_around_axis(-theta, dpdu_plane); + dpdv_plane = rot.apply_to_vector(dpdv_plane); + } + context.object_from_ray.apply_to_vector(dpdv_plane) + } + }; + + let p_error = Vector3f::fill(hit_width); + let flip_normal = self.common.reverse_orientation ^ self.common.transform_swap_handedness; + let pi = Point3fi::new_with_error(context.ray.evaluate(t_hit), p_error); + let intr = SurfaceInteraction::new( + pi, + Point2f::new(u, v), + -context.ray.d, + dpdu, + dpdv, + Normal3f::default(), + Normal3f::default(), + context.ray.time, + flip_normal, + ); + + Some(ShapeIntersection { intr, t_hit }) + } +} + +impl ShapeTrait for CurveShape<'_> { + fn bounds(&self) -> Bounds3f { + let cs_span = self.common.cp_obj; + let obj_bounds = bound_cubic_bezier(&cs_span, self.u_min, self.u_max); + let width0 = lerp(self.u_min, self.common.width[0], self.common.width[1]); + let width1 = lerp(self.u_max, self.common.width[0], self.common.width[1]); + let obj_bounds_expand = obj_bounds.expand(width0.max(width1) * 0.5); + self.common + .render_from_object + .apply_to_bounds(obj_bounds_expand) + } + + fn normal_bounds(&self) -> DirectionCone { + DirectionCone::entire_sphere() + } + + fn area(&self) -> Float { + let cp_obj = cubic_bezier_control_points(&self.common.cp_obj, self.u_min, self.u_max); + let width0 = lerp(self.u_min, self.common.width[0], self.common.width[1]); + let width1 = lerp(self.u_max, self.common.width[0], self.common.width[1]); + let avg_width = (width0 + width1) / 2.; + let mut approx_length = 0.; + for i in 0..3 { + approx_length += cp_obj[i].distance(cp_obj[i + 1]); + } + approx_length * avg_width + } + + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { + self.intersect_ray(ray, t_max.unwrap_or(Float::INFINITY)) + .is_some() + } + + fn intersect(&self, ray: &Ray, t_max: Option) -> Option { + self.intersect_ray(ray, t_max.unwrap_or(Float::INFINITY)) + } + + fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float { + todo!() + } + + fn pdf_from_context(&self, _ctx: &ShapeSampleContext, _wi: Vector3f) -> Float { + todo!() + } + + fn sample(&self, _u: Point2f) -> Option { + todo!() + } + + fn sample_from_context(&self, _ctx: &ShapeSampleContext, _u: Point2f) -> Option { + todo!() + } +} diff --git a/src/shapes/cylinder.rs b/src/shapes/cylinder.rs new file mode 100644 index 0000000..4c8fb96 --- /dev/null +++ b/src/shapes/cylinder.rs @@ -0,0 +1,271 @@ +use super::{ + Bounds3f, CylinderShape, DirectionCone, Float, Interaction, Normal3f, PI, Point2f, Point3f, + Point3fi, QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext, + ShapeTrait, SurfaceInteraction, Transform, Vector3f, Vector3fi, +}; +use crate::core::pbrt::{gamma, lerp}; +use crate::geometry::{Sqrt, Tuple, VectorLike}; +use crate::utils::interval::Interval; +use crate::utils::math::{difference_of_products, square}; +use std::mem; +use std::sync::Arc; + +impl<'a> CylinderShape<'a> { + pub fn new( + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + radius: Float, + z_min: Float, + z_max: Float, + phi_max: Float, + ) -> Self { + Self { + radius, + z_min, + z_max, + phi_max, + render_from_object, + object_from_render, + reverse_orientation, + transform_swap_handedness: render_from_object.swaps_handedness(), + } + } + + fn basic_intersect(&self, r: &Ray, t_max: Float) -> Option { + // Transform Ray origin and direction to object space + let oi = self + .object_from_render + .apply_to_interval(&Point3fi::new_from_point(r.o)); + let di = self + .object_from_render + .apply_to_vector_interval(&Vector3fi::new_from_vector(r.d)); + // Solve quadratic equation to find cylinder t0 and t1 values>> + let a: Interval = square(di.x()) + square(di.y()) + square(di.z()); + let b: Interval = 2. * (di.x() * oi.x() + di.y() * oi.y() + di.z() * oi.z()); + let c: Interval = + square(oi.x()) + square(oi.y()) + square(oi.z()) - square(Interval::new(self.radius)); + let f = b / (2. * a); + let vx: Interval = oi.x() - f * di.x(); + let vy: Interval = oi.y() - f * di.y(); + let length: Interval = (square(vx) + square(vy)).sqrt(); + let discrim: Interval = + 4. * a * (Interval::new(self.radius) * length) * (Interval::new(self.radius) - length); + if discrim.low < 0. { + return None; + } + let root_discrim = discrim.sqrt(); + let q: Interval; + if Float::from(b) < 0. { + q = -0.5 * (b - root_discrim); + } else { + q = -0.5 * (b + root_discrim); + } + let mut t0 = q / a; + let mut t1 = c / q; + if t0.low > t1.low { + mem::swap(&mut t0, &mut t1); + } + + // Check quadric shape t0 and t1 for nearest intersection + if t0.high > t_max || t1.low < 0. { + return None; + } + + let mut t_shape_hit: Interval = t0; + if t_shape_hit.low <= 0. { + t_shape_hit = t1; + if t_shape_hit.high > t_max { + return None; + } + } + + // Compute cylinder hit point and phi + let mut p_hit = Point3f::from(oi) + Float::from(t_shape_hit) * Vector3f::from(di); + let hit_rad = (square(p_hit.x()) + square(p_hit.y())).sqrt(); + p_hit[0] *= self.radius / hit_rad; + p_hit[1] *= self.radius / hit_rad; + + let mut phi = p_hit.y().atan2(p_hit.x()); + if phi < 0. { + phi += 2. * PI; + } + + if self.z_min > -self.radius && p_hit.z() < self.z_min + || self.z_max < self.radius && p_hit.z() > self.z_max + || phi > self.phi_max + { + if t_shape_hit == t1 { + return None; + } + if t1.high > t_max { + return None; + } + t_shape_hit = t1; + let mut p_hit = + Vector3f::from(Point3f::from(oi) + Float::from(t_shape_hit) * Vector3f::from(di)); + let hit_rad = (square(p_hit.x()) + square(p_hit.y())).sqrt(); + p_hit[0] *= self.radius / hit_rad; + p_hit[1] *= self.radius / hit_rad; + phi = p_hit.y().atan2(p_hit.x()); + if phi < 0. { + phi += 2. * PI; + } + + if p_hit.z() < self.z_min || p_hit.z() > self.z_max || phi > self.phi_max { + return None; + } + } + Some(QuadricIntersection::new(t_shape_hit.into(), p_hit, phi)) + } + + fn interaction_from_intersection( + &self, + isect: QuadricIntersection, + wo: Vector3f, + time: Float, + ) -> SurfaceInteraction { + let p_hit = isect.p_obj; + let phi = isect.phi; + let u = phi / self.phi_max; + let v = (p_hit.z() - self.z_min) / (self.z_max - self.z_min); + let dpdu = Vector3f::new(-self.phi_max * p_hit.y(), self.phi_max * p_hit.x(), 0.); + let dpdv = Vector3f::new(0., 0., self.z_max - self.z_min); + let d2pduu = -self.phi_max * self.phi_max * Vector3f::new(p_hit.x(), p_hit.y(), 0.); + let d2pduv = Vector3f::zero(); + let d2pdvv = Vector3f::zero(); + let e = dpdu.dot(dpdu); + let f = dpdu.dot(dpdv); + let g = dpdv.dot(dpdv); + let n: Vector3f = dpdu.cross(dpdv).normalize(); + let e_min = n.dot(d2pduu); + let f_min = n.dot(d2pduv); + let g_min = n.dot(d2pdvv); + // Compute dn/du and dn/dv from fundamental form coefficients + let efg2 = difference_of_products(e, f, f, f); + let inv_efg2 = if efg2 == 0. { 0. } else { 1. / efg2 }; + let dndu = Normal3f::from( + (f_min * f - e_min * g) * inv_efg2 * dpdu + (e_min * f - f_min * e) * inv_efg2 * dpdv, + ); + let dndv = Normal3f::from( + (g_min * f - f_min * g) * inv_efg2 * dpdu + (f_min * f - g_min * e) * inv_efg2 * dpdv, + ); + + let p_error = gamma(3) * Vector3f::new(p_hit.x(), p_hit.y(), 0.).abs(); + let flip_normal = self.reverse_orientation ^ self.transform_swap_handedness; + let wo_object = self.object_from_render.apply_to_vector(wo); + // (*renderFromObject) + let surf_point = SurfaceInteraction::new( + Point3fi::new_with_error(p_hit, p_error), + Point2f::new(u, v), + wo_object, + dpdu, + dpdv, + dndu, + dndv, + time, + flip_normal, + ); + surf_point + } +} + +impl ShapeTrait for CylinderShape<'_> { + fn area(&self) -> Float { + (self.z_max - self.z_min) * self.radius * self.phi_max + } + + fn bounds(&self) -> Bounds3f { + self.render_from_object + .apply_to_bounds(Bounds3f::from_points( + Point3f::new(-self.radius, -self.radius, self.z_min), + Point3f::new(self.radius, self.radius, self.z_max), + )) + } + + fn normal_bounds(&self) -> DirectionCone { + DirectionCone::entire_sphere() + } + + fn intersect(&self, ray: &Ray, t_max: Option) -> Option { + let t = t_max.unwrap_or(Float::INFINITY); + if let Some(isect) = self.basic_intersect(ray, t) { + let intr = self.interaction_from_intersection(isect.clone(), -ray.d, ray.time); + return Some(ShapeIntersection::new(intr, isect.t_hit)); + } else { + None + } + } + + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { + if let Some(t) = t_max { + self.basic_intersect(ray, t).is_some() + } else { + self.basic_intersect(ray, Float::INFINITY).is_some() + } + } + + fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float { + 1. / self.area() + } + + fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float { + let ray = ctx.spawn_ray(wi); + if let Some(isect) = self.intersect(&ray, None) { + let n = isect.intr.n(); + let absdot = Vector3f::from(n).dot(-wi).abs(); + let pdf = (1. / self.area()) / (absdot / ctx.p().distance_squared(isect.intr.p())); + if pdf.is_infinite() { + return 0.; + } + return pdf; + } else { + return 0.; + } + } + + fn sample(&self, u: Point2f) -> Option { + let z = lerp(u[0], self.z_min, self.z_max); + let phi = u[1] * self.phi_max; + let mut p_obj = Point3f::new(self.radius * phi.cos(), self.radius * phi.sin(), z); + let hit_rad = (square(p_obj.x()) + square(p_obj.y())).sqrt(); + p_obj[0] *= self.radius / hit_rad; + p_obj[1] *= self.radius / hit_rad; + let p_obj_error = gamma(3) * Vector3f::new(p_obj.x(), p_obj.y(), 0.).abs(); + let pi = self + .render_from_object + .apply_to_interval(&Point3fi::new_with_error(p_obj, p_obj_error)); + let mut n = self + .render_from_object + .apply_to_normal(Normal3f::new(p_obj.x(), p_obj.y(), 0.)) + .normalize(); + if self.reverse_orientation { + n *= -1.; + } + + let uv = Point2f::new( + phi / self.phi_max, + (p_obj.z() - self.z_min) / (self.z_max - self.z_min), + ); + Some(ShapeSample { + intr: Arc::new(SurfaceInteraction::new_simple(pi, n, uv)), + pdf: 1. / self.area(), + }) + } + + fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option { + let mut ss = self.sample(u)?; + let intr = Arc::make_mut(&mut ss.intr); + intr.get_common_mut().time = ctx.time; + let mut wi = ss.intr.p() - ctx.p(); + if wi.norm_squared() == 0. { + return None; + } + wi = wi.normalize(); + ss.pdf = Vector3f::from(ss.intr.n()).dot(-wi).abs() / ctx.p().distance_squared(ss.intr.p()); + if ss.pdf.is_infinite() { + return None; + } + return Some(ss); + } +} diff --git a/src/shapes/disk.rs b/src/shapes/disk.rs new file mode 100644 index 0000000..fe3e99f --- /dev/null +++ b/src/shapes/disk.rs @@ -0,0 +1,208 @@ +use super::{ + Bounds3f, DirectionCone, DiskShape, Float, Interaction, Normal3f, PI, Point2f, Point3f, + Point3fi, QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext, + ShapeTrait, SurfaceInteraction, Transform, Vector3f, +}; +use crate::geometry::VectorLike; +use crate::utils::math::square; +use crate::utils::sampling::sample_uniform_disk_concentric; +use std::sync::Arc; + +impl<'a> DiskShape<'a> { + pub fn new( + radius: Float, + inner_radius: Float, + height: Float, + phi_max: Float, + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + ) -> Self { + Self { + radius, + inner_radius, + height, + phi_max, + render_from_object, + object_from_render, + reverse_orientation, + transform_swap_handedness: render_from_object.swaps_handedness(), + } + } + + fn basic_intersect(&self, r: &Ray, t_max: Float) -> Option { + let oi = self.object_from_render.apply_to_point(r.o); + let di = self.object_from_render.apply_to_vector(r.d); + // Reject disk intersections for rays parallel to the disk’s plane + if di.z() == 0. { + return None; + } + + let t_shape_hit = (self.height - oi.z()) / di.z(); + if t_shape_hit == 0. || t_shape_hit >= t_max { + return None; + } + + // See if hit point is inside disk radii and phi_max + let p_hit: Point3f = oi + t_shape_hit * di; + let dist2 = square(p_hit.x()) + square(p_hit.y()); + if dist2 > square(self.radius) || dist2 < square(self.inner_radius) { + return None; + } + + let mut phi = p_hit.y().atan2(p_hit.x()); + if phi < 0. { + phi += 2. * PI; + } + if phi > self.phi_max { + return None; + } + + Some(QuadricIntersection { + t_hit: t_shape_hit, + p_obj: p_hit, + phi, + }) + } + + fn interaction_from_intersection( + &self, + isect: QuadricIntersection, + wo: Vector3f, + time: Float, + ) -> SurfaceInteraction { + let mut p_hit = isect.p_obj; + let phi = isect.phi; + // Find parametric representation of disk hit + let u = phi / self.phi_max; + let r_hit = (square(p_hit.x()) + square(p_hit.y())).sqrt(); + let v = (self.radius - r_hit) / (self.radius - self.inner_radius); + let dpdu = Vector3f::new(-self.phi_max * p_hit.y(), self.phi_max * p_hit.x(), 0.); + let dpdv = + Vector3f::new(p_hit.x(), p_hit.y(), 0.) * (self.inner_radius - self.radius) / r_hit; + let dndu = Normal3f::zero(); + let dndv = Normal3f::zero(); + + p_hit[2] = self.height; + + let p_error = Vector3f::zero(); + let flip_normal = self.reverse_orientation ^ self.transform_swap_handedness; + let wo_object = self.object_from_render.apply_to_vector(wo); + let surf_point = SurfaceInteraction::new( + Point3fi::new_with_error(p_hit, p_error), + Point2f::new(u, v), + wo_object, + dpdu, + dpdv, + dndu, + dndv, + time, + flip_normal, + ); + surf_point + } +} + +impl ShapeTrait for DiskShape<'_> { + fn area(&self) -> Float { + self.phi_max * 0.5 * (square(self.radius) - square(self.inner_radius)) + } + + fn bounds(&self) -> Bounds3f { + self.render_from_object + .apply_to_bounds(Bounds3f::from_points( + Point3f::new(-self.radius, -self.radius, self.height), + Point3f::new(self.radius, self.radius, self.height), + )) + } + fn normal_bounds(&self) -> DirectionCone { + let mut n = self + .render_from_object + .apply_to_normal(Normal3f::new(0., 0., 1.)); + if self.reverse_orientation { + n = -n; + } + DirectionCone::new_from_vector(Vector3f::from(n)) + } + + fn intersect(&self, ray: &Ray, t_max: Option) -> Option { + let t = t_max.unwrap_or(Float::INFINITY); + if let Some(isect) = self.basic_intersect(ray, t) { + let intr = self.interaction_from_intersection(isect.clone(), -ray.d, ray.time); + return Some(ShapeIntersection::new(intr, isect.t_hit)); + } else { + None + } + } + + fn sample(&self, u: Point2f) -> Option { + let pd = sample_uniform_disk_concentric(u); + let p_obj = Point3f::new(pd.x() * self.radius, pd.y() * self.radius, self.height); + let pi = self + .render_from_object + .apply_to_interval(&Point3fi::new_from_point(p_obj)); + let mut n: Normal3f = self + .render_from_object + .apply_to_normal(Normal3f::new(0., 0., 1.)) + .normalize(); + if self.reverse_orientation { + n = -n; + } + let mut phi = pd.y().atan2(pd.x()); + if phi < 0. { + phi += 2. * PI; + } + let radius_sample = (square(p_obj.x()) + square(p_obj.y())).sqrt(); + let uv = Point2f::new( + phi / self.phi_max, + (self.radius - radius_sample) / (self.radius - self.inner_radius), + ); + Some(ShapeSample { + intr: Arc::new(SurfaceInteraction::new_simple(pi, n, uv)), + pdf: 1. / self.area(), + }) + } + + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { + if let Some(t) = t_max { + self.basic_intersect(ray, t).is_some() + } else { + self.basic_intersect(ray, Float::INFINITY).is_some() + } + } + + fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option { + let mut ss = self.sample(u)?; + let intr = Arc::make_mut(&mut ss.intr); + intr.get_common_mut().time = ctx.time; + let mut wi = ss.intr.p() - ctx.p(); + if wi.norm_squared() == 0. { + return None; + } + wi = wi.normalize(); + ss.pdf = Vector3f::from(ss.intr.n()).dot(-wi).abs() / ctx.p().distance_squared(ss.intr.p()); + if ss.pdf.is_infinite() { + return None; + } + return Some(ss); + } + + fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float { + 1. / self.area() + } + + fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float { + let ray = ctx.spawn_ray(wi); + if let Some(isect) = self.intersect(&ray, None) { + let n = isect.intr.n(); + let absdot = Vector3f::from(n).dot(-wi).abs(); + let pdf = (1. / self.area()) / (absdot / ctx.p().distance_squared(isect.intr.p())); + if pdf.is_infinite() { + return 0.; + } + return pdf; + } else { + return 0.; + } + } +} diff --git a/src/shapes/mod.rs b/src/shapes/mod.rs new file mode 100644 index 0000000..b3d60bb --- /dev/null +++ b/src/shapes/mod.rs @@ -0,0 +1,289 @@ +pub mod bilinear; +pub mod curves; +pub mod cylinder; +pub mod disk; +pub mod sphere; +pub mod triangle; + +use crate::core::interaction::{Interaction, MediumInteraction, SurfaceInteraction}; +use crate::core::pbrt::{Float, PI}; +use crate::geometry::{ + Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f, + Vector3fi, VectorLike, +}; +use crate::utils::math::{next_float_down, next_float_up}; +use crate::utils::transform::Transform; +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct SphereShape<'a> { + radius: Float, + z_min: Float, + z_max: Float, + theta_z_min: Float, + theta_z_max: Float, + phi_max: Float, + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + transform_swap_handedness: bool, +} + +#[derive(Debug, Clone)] +pub struct CylinderShape<'a> { + radius: Float, + z_min: Float, + z_max: Float, + phi_max: Float, + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + transform_swap_handedness: bool, +} + +#[derive(Debug, Clone)] +pub struct DiskShape<'a> { + radius: Float, + inner_radius: Float, + height: Float, + phi_max: Float, + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + transform_swap_handedness: bool, +} + +#[derive(Debug, Clone)] +pub struct TriangleShape { + pub mesh_ind: usize, + pub tri_index: usize, +} + +#[derive(Debug, Clone)] +pub struct BilinearPatchShape { + mesh_index: usize, + blp_index: usize, + area: Float, + rectangle: bool, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum CurveType { + Flat, + Cylinder, + Ribbon, +} + +#[derive(Debug, Clone)] +pub struct CurveCommon<'a> { + curve_type: CurveType, + cp_obj: [Point3f; 4], + width: [Float; 2], + n: [Normal3f; 2], + normal_angle: Float, + inv_sin_normal_angle: Float, + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + transform_swap_handedness: bool, +} + +impl<'a> CurveCommon<'a> { + pub fn new( + c: &[Point3f], + w0: Float, + w1: Float, + curve_type: CurveType, + norm: &[Vector3f], + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + ) -> Self { + let transform_swap_handedness = render_from_object.swaps_handedness(); + let width = [w0, w1]; + assert_eq!(c.len(), 4); + let mut cp_obj = [Point3f::default(); 4]; + for i in 0..4 { + cp_obj[i] = c[i]; + } + + let mut n = [Normal3f::default(); 2]; + let mut normal_angle: Float = 0.; + let mut inv_sin_normal_angle: Float = 0.; + if norm.len() == 2 { + n[0] = norm[0].normalize().into(); + n[1] = norm[1].normalize().into(); + normal_angle = n[0].angle_between(n[1]); + inv_sin_normal_angle = 1. / normal_angle.sin(); + } + + Self { + curve_type, + cp_obj, + width, + n, + normal_angle, + inv_sin_normal_angle, + render_from_object, + object_from_render, + reverse_orientation, + transform_swap_handedness, + } + } +} + +#[derive(Debug, Clone)] +pub struct CurveShape<'a> { + common: CurveCommon<'a>, + u_min: Float, + u_max: Float, +} + +// Define Intersection objects. This only varies for +#[derive(Debug, Clone)] +pub struct ShapeIntersection { + intr: SurfaceInteraction, + t_hit: Float, +} + +impl ShapeIntersection { + pub fn new(intr: SurfaceInteraction, t_hit: Float) -> Self { + Self { intr, t_hit } + } + + pub fn intr(&self) -> &SurfaceInteraction { + &self.intr + } + + pub fn intr_mut(&mut self) -> &mut SurfaceInteraction { + &mut self.intr + } + + pub fn t_hit(&self) -> Float { + self.t_hit + } + + pub fn set_t_hit(&mut self, new_t: Float) { + self.t_hit = new_t; + } +} + +#[derive(Debug, Clone)] +pub struct QuadricIntersection { + t_hit: Float, + p_obj: Point3f, + phi: Float, +} + +impl QuadricIntersection { + pub fn new(t_hit: Float, p_obj: Point3f, phi: Float) -> Self { + Self { t_hit, p_obj, phi } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct TriangleIntersection { + b0: Float, + b1: Float, + b2: Float, + t: Float, +} + +impl TriangleIntersection { + pub fn new(b0: Float, b1: Float, b2: Float, t: Float) -> Self { + Self { b0, b1, b2, t } + } +} + +#[derive(Debug, Clone)] +pub struct BilinearIntersection { + uv: Point2f, + t: Float, +} + +impl BilinearIntersection { + pub fn new(uv: Point2f, t: Float) -> Self { + Self { uv, t } + } +} + +#[derive(Clone)] +pub struct ShapeSample { + intr: Arc, + pdf: Float, +} + +#[derive(Clone, Debug)] +pub struct ShapeSampleContext { + pi: Point3fi, + n: Normal3f, + ns: Normal3f, + time: Float, +} + +impl ShapeSampleContext { + pub fn new(pi: Point3fi, n: Normal3f, ns: Normal3f, time: Float) -> Self { + Self { pi, n, ns, time } + } + + pub fn new_from_interaction(si: &SurfaceInteraction) -> Self { + Self { + pi: si.pi(), + n: si.n(), + ns: si.shading.n, + time: si.time(), + } + } + + pub fn p(&self) -> Point3f { + Point3f::from(self.pi) + } + + pub fn offset_ray_origin(&self, w: Vector3f) -> Point3f { + let d = self.n.abs().dot(self.pi.error().into()); + let mut offset = d * Vector3f::from(self.n); + if w.dot(self.n.into()) < 0.0 { + offset = -offset; + } + + let mut po = Point3f::from(self.pi) + offset; + for i in 0..3 { + if offset[i] > 0.0 { + po[i] = next_float_up(po[i]); + } else { + po[i] = next_float_down(po[i]); + } + } + po + } + + pub fn offset_ray_origin_from_point(&self, pt: Point3f) -> Point3f { + self.offset_ray_origin(pt - self.p()) + } + + pub fn spawn_ray(&self, w: Vector3f) -> Ray { + Ray::new(self.offset_ray_origin(w), w, Some(self.time), None) + } +} + +#[derive(Debug, Clone)] +pub enum Shape<'a> { + Sphere(SphereShape<'a>), + Cylinder(CylinderShape<'a>), + Disk(DiskShape<'a>), + Triangle(TriangleShape), + BilinearPatch(BilinearPatchShape), + Curve(CurveShape<'a>), +} + +pub trait ShapeTrait: Send + Sync + std::fmt::Debug { + fn bounds(&self) -> Bounds3f; + fn normal_bounds(&self) -> DirectionCone; + fn intersect(&self, ray: &Ray, t_max: Option) -> Option; + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool; + fn area(&self) -> Float; + fn sample(&self, u: Point2f) -> Option; + fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option; + fn pdf(&self, interaction: Arc<&dyn Interaction>) -> Float; + fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float; +} diff --git a/src/shapes/sphere.rs b/src/shapes/sphere.rs new file mode 100644 index 0000000..f08b9ea --- /dev/null +++ b/src/shapes/sphere.rs @@ -0,0 +1,360 @@ +use super::{ + Bounds3f, DirectionCone, Float, Interaction, Normal3f, PI, Point2f, Point3f, Point3fi, + QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait, + SphereShape, SurfaceInteraction, Transform, Vector3f, Vector3fi, +}; +use crate::core::pbrt::{clamp_t, gamma}; +use crate::geometry::{Frame, Sqrt, VectorLike, spherical_direction}; +use crate::utils::interval::Interval; +use crate::utils::math::{difference_of_products, radians, safe_acos, safe_sqrt, square}; +use crate::utils::sampling::sample_uniform_sphere; + +use std::mem; +use std::sync::Arc; + +impl<'a> SphereShape<'a> { + pub fn new( + render_from_object: &'a Transform, + object_from_render: &'a Transform, + reverse_orientation: bool, + radius: Float, + z_min: Float, + z_max: Float, + phi_max: Float, + ) -> Self { + let theta_z_min = clamp_t(z_min.min(z_max) / radius, -1., 1.).acos(); + let theta_z_max = clamp_t(z_max.min(z_max) / radius, -1., 1.).acos(); + let phi_max = radians(clamp_t(phi_max, 0., 360.0)); + Self { + render_from_object, + object_from_render, + radius, + z_min: clamp_t(z_min.min(z_max), -radius, radius), + z_max: clamp_t(z_min.max(z_max), -radius, radius), + theta_z_max, + theta_z_min, + phi_max, + reverse_orientation, + transform_swap_handedness: render_from_object.swaps_handedness(), + } + } + + fn basic_intersect(&self, ray: &Ray, t_max: Float) -> Option { + let oi: Point3fi = self + .object_from_render + .apply_to_interval(&Point3fi::new_from_point(ray.o)); + let di: Vector3fi = self + .object_from_render + .apply_to_interval(&Point3fi::new_from_point(Point3f::from(ray.d))) + .into(); + let a: Interval = square(di.x()) + square(di.y()) + square(di.z()); + let b: Interval = 2. * (di.x() * oi.x() + di.y() * oi.y() + di.z() * oi.z()); + let c: Interval = + square(oi.x()) + square(oi.y()) + square(oi.z()) - square(Interval::new(self.radius)); + + let v: Vector3fi = (oi - b / Vector3fi::from((2. * a) * di)).into(); + let length: Interval = v.norm(); + let discrim = + 4. * a * (Interval::new(self.radius) + length) * (Interval::new(self.radius) - length); + if discrim.low < 0. { + return None; + } + + let root_discrim = discrim.sqrt(); + let q: Interval; + + if Float::from(b) < 0. { + q = -0.5 * (b - root_discrim); + } else { + q = -0.5 * (b + root_discrim); + } + + let mut t0 = q / a; + let mut t1 = c / q; + if t0.low > t1.low { + mem::swap(&mut t0, &mut t1); + } + + if t0.high > t_max || t1.low < 0. { + return None; + } + let mut t_shape_hit = t0; + if t_shape_hit.low <= 0. { + t_shape_hit = t1; + if t_shape_hit.high > t_max { + return None; + } + } + + let mut p_hit = Point3f::from(oi) + Float::from(t_shape_hit) * Vector3f::from(di); + if p_hit.x() == 0. && p_hit.y() == 0. { + p_hit[0] = 1e-5 * self.radius; + } + + let mut phi = p_hit.y().atan2(p_hit.x()); + if phi < 0. { + phi += 2. * PI; + } + + if self.z_min > -self.radius && p_hit.z() < self.z_min + || self.z_max < self.radius && p_hit.z() > self.z_max + || phi > self.phi_max + { + if t_shape_hit == t1 { + return None; + } + if t1.high > t_max { + return None; + } + t_shape_hit = t1; + let mut p_hit_vec = + Vector3f::from(Point3f::from(oi) + Float::from(t_shape_hit) * Vector3f::from(di)); + p_hit_vec *= self.radius / p_hit.distance(Point3f::zero()); + p_hit = Point3f::from(p_hit_vec); + + if p_hit.x() == 0. && p_hit.y() == 0. { + p_hit[0] = 1e-5 * self.radius; + } + phi = p_hit.y().atan2(p_hit.x()); + if phi < 0. { + phi += 2. * PI; + } + + if self.z_min > -self.radius && p_hit.z() < self.z_min + || self.z_max < self.radius && p_hit.z() > self.z_max + || phi > self.phi_max + { + return None; + } + } + + Some(QuadricIntersection::new(t_shape_hit.into(), p_hit, phi)) + } + + fn interaction_from_intersection( + &self, + isect: QuadricIntersection, + wo: Vector3f, + time: Float, + ) -> SurfaceInteraction { + let p_hit = isect.p_obj; + let phi = isect.phi; + let u = phi / self.phi_max; + let cos_theta = p_hit.z() / self.radius; + let theta = safe_acos(cos_theta); + let v = (theta - self.theta_z_min) / (self.theta_z_max - self.theta_z_min); + let z_radius = (square(p_hit.x()) + square(p_hit.y())).sqrt(); + let cos_phi = p_hit.x() / z_radius; + let sin_phi = p_hit.y() / z_radius; + let dpdu = Vector3f::new(-self.phi_max * p_hit.y(), self.phi_max * p_hit.x(), 0.); + let sin_theta = safe_sqrt(1. - square(cos_theta)); + let dpdv = (self.theta_z_max - self.theta_z_min) + * Vector3f::new( + p_hit.z() * cos_phi, + p_hit.z() * sin_phi, + -self.radius * sin_theta, + ); + let d2pduu = -self.phi_max * self.phi_max * Vector3f::new(p_hit.x(), p_hit.y(), 0.); + let d2pduv = (self.theta_z_max - self.theta_z_min) + * p_hit.z() + * self.phi_max + * Vector3f::new(-sin_phi, cos_phi, 0.); + let d2pdvv = -square(self.theta_z_max - self.theta_z_min) * Vector3f::from(p_hit); + let e = dpdu.dot(dpdu); + let f = dpdu.dot(dpdv); + let g = dpdv.dot(dpdv); + let n = dpdu.cross(dpdv).normalize(); + let e_min = n.dot(d2pduu); + let f_min = n.dot(d2pduv); + let g_min = n.dot(d2pdvv); + + let efg2 = difference_of_products(e, g, f, f); + let inv_efg2 = if efg2 == 0. { 0. } else { 1. / efg2 }; + let dndu = Normal3f::from( + (f_min * f - e_min * g) * inv_efg2 * dpdu + (e_min * f - f_min * e) * inv_efg2 * dpdv, + ); + let dndv = Normal3f::from( + (g_min * f - f_min * g) * inv_efg2 * dpdu + (f_min * f - g_min * e) * inv_efg2 * dpdv, + ); + let p_error = gamma(5) * Vector3f::from(p_hit).abs(); + let flip_normal = self.reverse_orientation ^ self.transform_swap_handedness; + let wo_object = self.object_from_render.apply_to_vector(wo); + let surf_point = SurfaceInteraction::new( + Point3fi::new_with_error(p_hit, p_error), + Point2f::new(u, v), + wo_object, + dpdu, + dpdv, + dndu, + dndv, + time, + flip_normal, + ); + // self.render_from_object.apply_to_point(surf_point) + surf_point + } +} + +impl ShapeTrait for SphereShape<'_> { + fn bounds(&self) -> Bounds3f { + self.render_from_object + .apply_to_bounds(Bounds3f::from_points( + Point3f::new(-self.radius, -self.radius, self.z_min), + Point3f::new(self.radius, self.radius, self.z_max), + )) + } + + fn normal_bounds(&self) -> DirectionCone { + DirectionCone::entire_sphere() + } + + fn area(&self) -> Float { + self.phi_max * self.radius * (self.z_max - self.z_min) + } + + fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float { + 1. / self.area() + } + + fn intersect(&self, ray: &Ray, t_max: Option) -> Option { + let t = t_max.unwrap_or(Float::INFINITY); + if let Some(isect) = self.basic_intersect(ray, t) { + let intr = self.interaction_from_intersection(isect.clone(), -ray.d, ray.time); + return Some(ShapeIntersection::new(intr, isect.t_hit)); + } else { + None + } + } + + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { + if let Some(t) = t_max { + self.basic_intersect(ray, t).is_some() + } else { + self.basic_intersect(ray, Float::INFINITY).is_some() + } + } + + fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float { + let p_center = self + .object_from_render + .apply_to_point(Point3f::new(0., 0., 0.)); + let p_origin = ctx.offset_ray_origin(p_center.into()); + // Return solid angle PDF for point inside sphere + if p_origin.distance_squared(p_center) <= square(self.radius) { + let ray = ctx.spawn_ray(wi); + let isect = self.intersect(&ray, None).expect("Return 0"); + let absdot = isect.intr.n().dot(-Normal3f::from(wi)); + // Compute PDF in solid angle measure from shape intersection point + let pdf = (1. / self.area()) / (absdot / ctx.p().distance_squared(isect.intr.p())); + if pdf.is_infinite() { + return 0.; + } + return pdf; + } + let sin2_theta_max = self.radius * self.radius / ctx.p().distance_squared(p_center); + let cos_theta_max = safe_sqrt(1. - sin2_theta_max); + let mut one_minus_cos_theta_max = 1. - cos_theta_max; + // Compute more accurate cos theta max for small solid angle + if sin2_theta_max < 0.00068523 { + one_minus_cos_theta_max = sin2_theta_max / 2.; + } + + 1. / (2. * PI * one_minus_cos_theta_max) + } + + fn sample(&self, u: Point2f) -> Option { + let p_obj = Point3f::new(0., 0., 0.) + self.radius * sample_uniform_sphere(u); + let mut p_obj_vec = Vector3f::from(p_obj); + p_obj_vec *= self.radius / p_obj.distance(Point3f::zero()); + let p_obj_error = gamma(5) * p_obj_vec.abs(); + let n_obj = Normal3f::from(p_obj_vec); + let mut n = self.render_from_object.apply_to_normal(n_obj).normalize(); + if self.reverse_orientation { + n *= -1.; + } + let theta = safe_acos(p_obj_vec.z() / self.radius); + let mut phi = p_obj_vec.y().atan2(p_obj_vec.x()); + if phi < 0. { + phi += 2. * PI; + } + let uv = Point2f::new( + phi / self.phi_max, + (theta - self.theta_z_min) / (self.theta_z_max - self.theta_z_min), + ); + let pi = self + .render_from_object + .apply_to_interval(&Point3fi::new_with_error( + Point3f::from(p_obj_vec), + p_obj_error, + )); + let si = SurfaceInteraction::new_simple(pi, n, uv); + Some(ShapeSample { + intr: Arc::new(si), + pdf: 1. / self.area(), + }) + } + + fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option { + let p_center = self.render_from_object.apply_to_point(Point3f::zero()); + let p_origin = ctx.offset_ray_origin_from_point(p_center); + if p_origin.distance_squared(p_center) <= square(self.radius) { + let mut ss = self.sample(u)?; + let intr = Arc::make_mut(&mut ss.intr); + intr.get_common_mut().time = ctx.time; + let mut wi = ss.intr.p() - ctx.p(); + if wi.norm_squared() == 0. { + return None; + } + wi = wi.normalize(); + ss.pdf = + Vector3f::from(ss.intr.n()).dot(-wi).abs() / ctx.p().distance_squared(ss.intr.p()); + if ss.pdf.is_infinite() { + return None; + } + return Some(ss); + } + let sin_theta_max = self.radius / ctx.p().distance(p_center); + let sin2_theta_max = square(sin_theta_max); + let cos_theta_max = safe_sqrt(1. - sin2_theta_max); + let mut one_minus_cos_theta_max = 1. - cos_theta_max; + let mut cos_theta = (cos_theta_max - 1.) * u[0] + 1.; + let mut sin2_theta = 1. - square(cos_theta); + + // Compute more accurate cos theta max for small solid angle + if sin2_theta_max < 0.00068523 { + sin2_theta = sin2_theta_max * u[0]; + cos_theta = (1. - sin2_theta).sqrt(); + one_minus_cos_theta_max = sin2_theta_max / 2.; + } + // Compute angle alpha from center of sphere to sampled point on surface + let cos_alpha = sin2_theta / sin_theta_max + + cos_theta * safe_sqrt(1. - sin2_theta / square(sin_theta_max)); + let sin_alpha = safe_sqrt(1. - square(cos_alpha)); + let phi = u[1] * 2. * PI; + let w = spherical_direction(sin_alpha, cos_alpha, phi); + let sampling_frame = Frame::from_z((p_center - ctx.p()).normalize()); + let mut n: Normal3f = sampling_frame.from_local(-w).into(); + let p = p_center + self.radius * Vector3f::from(n); + if self.reverse_orientation { + n *= -1.; + } + let p_error = gamma(5) * Vector3f::from(p).abs(); + // Compute (u, v) coordinates for sampled point on sphere + let p_obj = self.object_from_render.apply_to_point(Point3f::from(p)); + let theta = safe_acos(p_obj.z() / self.radius); + let mut sphere_phi = p_obj.y().atan2(p_obj.x()); + if sphere_phi < 0. { + sphere_phi += 2. * PI; + } + let uv = Point2f::new( + sphere_phi / self.phi_max, + (theta - self.theta_z_min) / (self.theta_z_max - self.theta_z_min), + ); + let pi = Point3fi::new_with_error(p_obj, p_error); + let si = SurfaceInteraction::new_simple(pi, n, uv); + Some(ShapeSample { + intr: Arc::new(si), + pdf: 1. / (2. * PI * one_minus_cos_theta_max), + }) + } +} diff --git a/src/shapes/triangle.rs b/src/shapes/triangle.rs new file mode 100644 index 0000000..f7d4e60 --- /dev/null +++ b/src/shapes/triangle.rs @@ -0,0 +1,518 @@ +use super::{ + Bounds3f, DirectionCone, Float, Interaction, Normal3f, Point2f, Point3f, Point3fi, Ray, + ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait, SurfaceInteraction, + TriangleIntersection, TriangleShape, Vector2f, Vector3f, +}; +use crate::core::pbrt::gamma; +use crate::geometry::{Sqrt, Tuple, VectorLike, spherical_triangle_area}; +use crate::utils::math::{difference_of_products, square}; +use crate::utils::mesh::TriangleMesh; +use crate::utils::sampling::{ + bilinear_pdf, invert_spherical_triangle_sample, sample_bilinear, sample_spherical_triangle, + sample_uniform_triangle, +}; +use std::mem; +use std::sync::{Arc, OnceLock}; + +pub static TRIANGLE_MESHES: OnceLock>> = OnceLock::new(); + +#[derive(Clone, Copy)] +struct TriangleData { + vertices: [Point3f; 3], + uvs: [Point2f; 3], + normals: Option<[Normal3f; 3]>, + area: Float, + normal: Normal3f, + reverse_orientation: bool, + transform_swaps_handedness: bool, +} + +impl TriangleShape { + pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 3e-4; + pub const MAX_SPHERICAL_SAMPLE_AREA: Float = 6.22; + + fn mesh(&self) -> &Arc { + let meshes = TRIANGLE_MESHES + .get() + .expect("Mesh has not been initialized"); + &meshes[self.mesh_ind] + } + + fn get_data(&self) -> TriangleData { + let mesh = self.mesh(); + let start = 3 * self.tri_index; + let indices = &mesh.vertex_indices[start..start + 3]; + let vertices = [mesh.p[indices[0]], mesh.p[indices[1]], mesh.p[indices[2]]]; + let uvs = mesh.uv.as_ref().map_or( + [ + Point2f::zero(), + Point2f::new(1.0, 0.0), + Point2f::new(1.0, 1.0), + ], + |uv| [uv[indices[0]], uv[indices[1]], uv[indices[2]]], + ); + let normals = mesh + .n + .as_ref() + .map(|n| [n[indices[0]], n[indices[1]], n[indices[2]]]); + let dp1 = vertices[1] - vertices[0]; + let dp2 = vertices[2] - vertices[0]; + let normal = Normal3f::from(dp1.cross(dp2).normalize()); + let area = 0.5 * dp1.cross(dp2).norm(); + + TriangleData { + vertices, + uvs, + normals, + area, + normal, + reverse_orientation: mesh.reverse_orientation, + transform_swaps_handedness: mesh.transform_swaps_handedness, + } + } + + fn solid_angle(&self, p: Point3f) -> Float { + let data = self.get_data(); + let [p0, p1, p2] = data.vertices; + spherical_triangle_area::( + (p0 - p).normalize(), + (p1 - p).normalize(), + (p2 - p).normalize(), + ) + } + + fn intersect_triangle(&self, ray: &Ray, t_max: Float) -> Option { + let data = self.get_data(); + let [p0, p1, p2] = data.vertices; + if (p2 - p0).cross(p1 - p0).norm_squared() == 0. { + return None; + } + let mut p0t = p0 - Vector3f::from(ray.o); + let mut p1t = p1 - Vector3f::from(ray.o); + let mut p2t = p2 - Vector3f::from(ray.o); + + let kz = ray.d.abs().max_component_index(); + let kx = if kz == 3 { 0 } else { kz + 1 }; + let ky = if kz == 3 { 0 } else { kx + 1 }; + let d = ray.d.permute([kx, ky, kz]); + p0t = p0t.permute([kx, ky, kz]); + p1t = p1t.permute([kx, ky, kz]); + p2t = p2t.permute([kx, ky, kz]); + // Apply shear transformation to translated vertex positions + let sx = -d.x() / d.z(); + let sy = -d.y() / d.z(); + let sz = 1. / d.z(); + p0t[0] += sx * p0t.z(); + p0t[1] += sy * p0t.z(); + p1t[0] += sx * p1t.z(); + p1t[1] += sy * p1t.z(); + p2t[0] += sx * p2t.z(); + p2t[0] += sy * p2t.z(); + + // Compute edge function coefficients e0, e1, and e2 + let mut e0 = difference_of_products(p1t.x(), p2t.y(), p1t.y(), p2t.x()); + let mut e1 = difference_of_products(p2t.x(), p0t.y(), p2t.y(), p0t.x()); + let mut e2 = difference_of_products(p0t.x(), p1t.y(), p0t.y(), p1t.x()); + + // if mem::size_of::() == mem::size_of::() && (e0 == 0.0 || e1 == 0.0 || e2 == 0.0) + if e0 == 0.0 || e1 == 0.0 || e2 == 0.0 { + let [p0t64, p1t64, p2t64] = [p0t.cast::(), p1t.cast::(), p2t.cast::()]; + + e0 = (p2t64.y() * p1t64.x() - p2t64.x() * p1t64.y()) as Float; + e1 = (p0t64.y() * p2t64.x() - p0t64.x() * p2t64.y()) as Float; + e2 = (p1t64.y() * p0t64.x() - p1t64.x() * p0t64.y()) as Float; + } + + if (e0 < 0. || e1 < 0. || e2 < 0.) && (e0 > 0. || e1 > 0. || e2 > 0.) { + return None; + } + let det = e0 + e1 + e2; + if det == 0. { + return None; + } + + // Compute scaled hit distance to triangle and test against ray + p0t[2] *= sz; + p1t[2] *= sz; + p2t[2] *= sz; + + let t_scaled = e0 * p0t.z() + e1 * p1t.z() + e2 * p2t.z(); + if det < 0. && (t_scaled >= 0. || t_scaled < t_max * det) + || (det > 0. && (t_scaled <= 0. || t_scaled > t_max * det)) + { + return None; + } + + // Compute barycentric coordinates and value for triangle intersection + let inv_det = 1. / det; + let b0 = e0 * inv_det; + let b1 = e1 * inv_det; + let b2 = e2 * inv_det; + let t = t_scaled * inv_det; + + // Ensure that computed triangle is conservatively greater than zero + let max_z_t = Vector3f::new(p0t.z(), p1t.z(), p2t.z()) + .abs() + .max_component_value(); + let delta_z = gamma(3) * max_z_t; + + let max_x_t = Vector3f::new(p0t.x(), p1t.x(), p2t.x()) + .abs() + .max_component_value(); + let max_y_t = Vector3f::new(p0t.y(), p1t.y(), p2t.y()) + .abs() + .max_component_value(); + let delta_x = gamma(5) * (max_x_t + max_z_t); + let delta_y = gamma(5) * (max_y_t + max_z_t); + + let delta_e = 2. * (gamma(2) * max_x_t * max_y_t + delta_y * max_x_t + delta_x * max_y_t); + let max_e = Vector3f::new(e0, e1, e2).abs().max_component_value(); + let delta_t = + 3. * (gamma(3) * max_e * max_z_t + delta_e * max_z_t + delta_z * max_e) * inv_det.abs(); + + if t <= delta_t { + return None; + } + + Some(TriangleIntersection::new(b0, b1, b2, t)) + } + + fn interaction_from_intersection( + &self, + ti: TriangleIntersection, + time: Float, + wo: Vector3f, + ) -> SurfaceInteraction { + let data = self.get_data(); + let [p0, p1, p2] = data.vertices; + let [uv0, uv1, uv2] = data.uvs; + // Compute triangle partial derivatives + let (dpdu, dpdv, degenerate_uv, det) = self.compute_partials(data); + // Interpolate (u, v) parametric coordinates and hit point + let p_hit_vec = + ti.b0 * Vector3f::from(p0) + ti.b1 * Vector3f::from(p1) + ti.b2 * Vector3f::from(p2); + let p_hit = Point3f::from(p_hit_vec); + let uv_hit_vec = + ti.b0 * Vector2f::from(uv0) + ti.b1 * Vector2f::from(uv1) + ti.b2 * Vector2f::from(uv2); + let uv_hit = Point2f::from(uv_hit_vec); + + // Return SurfaceInteraction for triangle hit> + let flip_normal = data.reverse_orientation ^ data.transform_swaps_handedness; + let p_abs_sum = (ti.b0 * Vector3f::from(p0)).abs() + + (ti.b1 * Vector3f::from(p1)).abs() + + (ti.b2 * Vector3f::from(p2)).abs(); + let p_error = gamma(7) * p_abs_sum; + let mut isect = SurfaceInteraction::new( + Point3fi::new_with_error(p_hit, p_error), + uv_hit, + wo, + dpdu, + dpdv, + Normal3f::default(), + Normal3f::default(), + time, + flip_normal, + ); + + isect.face_index = self + .mesh() + .face_indices + .as_ref() + .map_or(0, |fi| fi[self.tri_index]); + isect.common.n = data.normal; + isect.shading.n = isect.n(); + if flip_normal { + isect.common.n = -isect.n(); + isect.shading.n = -isect.shading.n; + } + + if data.normals.is_some() || self.mesh().s.is_some() { + self.apply_shading_normals(&mut isect, ti, data, degenerate_uv, det); + } + + isect + } + + fn compute_partials(&self, data: TriangleData) -> (Vector3f, Vector3f, bool, Float) { + let [p0, p1, p2] = data.vertices; + let [uv0, uv1, uv2] = data.uvs; + let duv02 = uv0 - uv2; + let duv12 = uv1 - uv2; + let dp02 = p0 - p2; + let dp12 = p1 - p2; + let det = difference_of_products(duv02[0], duv12[1], duv02[1], duv12[0]); + let degenerate_uv = det.abs() < 1e-9; + let (dpdu, dpdv) = if !degenerate_uv { + let inv_det = 1. / det; + ( + (dp02 * duv12[1] - dp12 * duv02[1]) * inv_det, + (dp12 * duv02[0] - dp02 * duv12[0]) * inv_det, + ) + } else { + let dp20 = p2 - p0; + let dp10 = p1 - p0; + let mut ng = dp20.cross(dp10); + if ng.norm_squared() == 0. { + ng = (dp20.cast::().cross(dp10.cast::())).cast(); + } + let n = ng.normalize(); + n.coordinate_system() + }; + (dpdu, dpdv, degenerate_uv, det) + } + + fn apply_shading_normals( + &self, + isect: &mut SurfaceInteraction, + ti: TriangleIntersection, + data: TriangleData, + degenerate_uv: bool, + det: Float, + ) { + let Some([n0, n1, n2]) = data.normals else { + return; + }; + let [uv0, uv1, uv2] = data.uvs; + let duv02 = uv0 - uv2; + let duv12 = uv1 - uv2; + + let ns = ti.b0 * n0 + ti.b1 * n1 + ti.b2 * n2; + let ns = if ns.norm_squared() > 0. { + ns.normalize() + } else { + isect.n() + }; + + let mut ss = self.mesh().s.as_ref().map_or(isect.dpdu, |s| { + let indices = &self.mesh().vertex_indices[3 * self.tri_index..3 * self.tri_index + 3]; + let interp_s = ti.b0 * s[indices[0]] + ti.b1 * s[indices[1]] + ti.b2 * s[indices[2]]; + + if interp_s.norm_squared() > 0. { + interp_s + } else { + isect.dpdu + } + }); + + let mut ts = Vector3f::from(ns).cross(ss); + if ts.norm_squared() > 0. { + ss = ts.cross(Vector3f::from(ns)).into(); + } else { + (ss, ts) = Vector3f::from(ns).coordinate_system(); + } + let (dndu, dndv) = if degenerate_uv { + let dn = (n2 - n0).cross(n1 - n0); + if dn.norm_squared() == 0. { + (Normal3f::zero(), Normal3f::zero()) + } else { + dn.coordinate_system() + } + } else { + let inv_det = 1. / det; + let dn02 = n0 - n2; + let dn12 = n1 - n2; + ( + (dn02 * duv12[1] - dn12 * duv02[1]) * inv_det, + (dn12 * duv02[0] - dn02 * duv12[0]) * inv_det, + ) + }; + + isect.shading.n = ns; + isect.shading.dpdu = ss; + isect.shading.dpdv = ts.into(); + isect.dndu = dndu; + isect.dndv = dndv; + } +} + +impl ShapeTrait for TriangleShape { + fn bounds(&self) -> Bounds3f { + let [p0, p1, p2] = self.get_data().vertices; + Bounds3f::from_points(p0, p1).union_point(p2) + } + + fn normal_bounds(&self) -> DirectionCone { + let data = self.get_data(); + let mut n = data.normal; + if let Some([n0, n1, n2]) = data.normals { + n = n.face_forward((n0 + n1 + n2).into()); + } else if data.reverse_orientation ^ data.transform_swaps_handedness { + n = -n; + } + DirectionCone::new_from_vector(Vector3f::from(n)) + } + + fn area(&self) -> Float { + self.get_data().area + } + + fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float { + 1. / self.area() + } + + fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float { + let solid_angle = self.solid_angle(ctx.p()); + if solid_angle < Self::MIN_SPHERICAL_SAMPLE_AREA + || solid_angle > Self::MAX_SPHERICAL_SAMPLE_AREA + { + let ray = ctx.spawn_ray(wi); + return self.intersect(&ray, None).map_or(0., |isect| { + let absdot = Vector3f::from(isect.intr.n()).dot(-wi).abs(); + let d2 = ctx.p().distance_squared(isect.intr.p()); + let pdf = 1. / self.area() * (d2 / absdot); + if pdf.is_infinite() { 0. } else { pdf } + }); + } + + let mut pdf = 1. / solid_angle; + if ctx.ns != Normal3f::zero() { + let [p0, p1, p2] = self.get_data().vertices; + let u = invert_spherical_triangle_sample(&[p0, p1, p2], ctx.p(), wi) + .unwrap_or(Point2f::zero()); + + let rp = ctx.p(); + let wi: [Vector3f; 3] = [ + (p0 - rp).normalize(), + (p1 - rp).normalize(), + (p2 - rp).normalize(), + ]; + let w: [Float; 4] = [ + 0.01_f32.max(ctx.ns.dot(wi[1].into()).abs()), + 0.01_f32.max(ctx.ns.dot(wi[1].into()).abs()), + 0.01_f32.max(ctx.ns.dot(wi[0].into()).abs()), + 0.01_f32.max(ctx.ns.dot(wi[2].into()).abs()), + ]; + pdf *= bilinear_pdf(u, &w); + } + pdf + } + + fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option { + let data = self.get_data(); + let [p0, p1, p2] = data.vertices; + let solid_angle = self.solid_angle(ctx.p()); + if solid_angle < Self::MIN_SPHERICAL_SAMPLE_AREA + || solid_angle > Self::MAX_SPHERICAL_SAMPLE_AREA + { + // Sample shape by area and compute incident direction wi + return self + .sample(u) + .map(|mut ss| { + let mut intr_clone = (*ss.intr).clone(); + intr_clone.common.time = ctx.time; + ss.intr = Arc::new(intr_clone); + + let wi = (ss.intr.p() - ctx.p()).normalize(); + if wi.norm_squared() == 0. { + return None; + } + let absdot = Vector3f::from(ss.intr.n()).abs_dot(-wi); + let d2 = ctx.p().distance_squared(ss.intr.p()); + ss.pdf /= absdot / d2; + if ss.pdf.is_infinite() { None } else { Some(ss) } + }) + .flatten(); + } + + // Sample spherical triangle from reference point + let mut pdf = 1.; + if ctx.ns != Normal3f::zero() { + let rp = ctx.p(); + let wi: [Vector3f; 3] = [ + (p0 - rp).normalize(), + (p1 - rp).normalize(), + (p2 - rp).normalize(), + ]; + let w: [Float; 4] = [ + 0.01_f32.max(ctx.ns.dot(wi[1].into()).abs()), + 0.01_f32.max(ctx.ns.dot(wi[1].into()).abs()), + 0.01_f32.max(ctx.ns.dot(wi[0].into()).abs()), + 0.01_f32.max(ctx.ns.dot(wi[2].into()).abs()), + ]; + + let u = sample_bilinear(u, &w); + pdf = bilinear_pdf(u, &w); + } + + let Some((b, tri_pdf)) = sample_spherical_triangle(&[p0, p1, p2], ctx.p(), u) else { + return None; + }; + if tri_pdf == 0. { + return None; + } + pdf *= tri_pdf; + let b2 = 1. - b[0] - b[1]; + + let p_abs_sum = b[0] * Vector3f::from(p0) + + b[1] * Vector3f::from(p1) + + (1. - b[0] - b[1]) * Vector3f::from(p2); + let p_error = gamma(6) * p_abs_sum; + // Return ShapeSample for solid angle sampled point on triangle + let p_vec = + b[0] * Vector3f::from(p0) + b[1] * Vector3f::from(p1) + b[2] * Vector3f::from(p2); + let p = Point3f::from(p_vec); + let mut n = Normal3f::from((p1 - p0).cross(p2 - p0).normalize()); + if let Some([n0, n1, n2]) = data.normals { + let ns = b[0] * n0 + b[1] * n1 + b2 * n2; + n = n.face_forward(ns.into()); + } else if data.reverse_orientation ^ data.transform_swaps_handedness { + n = -n; + } + + let [uv0, uv1, uv2] = data.uvs; + let uv_sample_vec = + b[0] * Vector2f::from(uv0) + b[1] * Vector2f::from(uv1) + b[2] * Vector2f::from(uv2); + let uv_sample = Point2f::from(uv_sample_vec); + let pi = Point3fi::new_with_error(p, p_error); + let mut si = SurfaceInteraction::new_simple(pi, n, uv_sample); + si.common.time = ctx.time; + Some(ShapeSample { + intr: Arc::new(si), + pdf, + }) + } + + fn sample(&self, u: Point2f) -> Option { + let data = self.get_data(); + let [p0, p1, p2] = data.vertices; + let [uv0, uv1, uv2] = data.uvs; + let b = sample_uniform_triangle(u); + let p_vec = + b[0] * Vector3f::from(p0) + b[1] * Vector3f::from(p1) + b[2] * Vector3f::from(p2); + let b2 = 1. - b[0] - b[1]; + let p = Point3f::from(p_vec); + let mut n = data.normal; + if let Some([n0, n1, n2]) = data.normals { + let interp_n = b[0] * n0 + b[1] * n1 + b2 * n2; + n = n.face_forward(interp_n.into()); + } else if data.reverse_orientation ^ data.transform_swaps_handedness { + n = -n; + } + + let uv_sample_vec = + b[0] * Vector2f::from(uv0) + b[1] * Vector2f::from(uv1) + b[2] * Vector2f::from(uv2); + let uv_sample = Point2f::from(uv_sample_vec); + let p_abs_sum = (b[0] * Vector3f::from(p0)).abs() + + (b[1] * Vector3f::from(p1)).abs() + + ((1. - b[0] - b[1]) * Vector3f::from(p2)).abs(); + let p_error = gamma(6) * p_abs_sum; + let pi = Point3fi::new_with_error(p, p_error); + Some(ShapeSample { + intr: Arc::new(SurfaceInteraction::new_simple(pi, n, uv_sample)), + pdf: 1. / self.area(), + }) + } + + fn intersect(&self, ray: &Ray, t_max: Option) -> Option { + self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY)) + .map(|ti| { + let intr = self.interaction_from_intersection(ti, ray.time, -ray.d); + ShapeIntersection { intr, t_hit: ti.t } + }) + } + + fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { + self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY)) + .is_some() + } +} diff --git a/src/utils/color.rs b/src/utils/color.rs index fbc49ee..22571af 100644 --- a/src/utils/color.rs +++ b/src/utils/color.rs @@ -1,15 +1,39 @@ -use std::ops::{Index, IndexMut, Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg}; +use std::any::TypeId; use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, +}; -use crate::core::pbrt::{Float, lerp}; -use super::geometry::Point2f; use super::spectrum::Spectrum; +use crate::core::pbrt::{Float, lerp}; +use crate::geometry::Point2f; +use crate::utils::math::SquareMatrix; +use once_cell::sync::Lazy; + +pub trait Triplet { + fn from_triplet(c1: Float, c2: Float, c3: Float) -> Self; +} #[derive(Debug, Clone)] pub struct XYZ { - x: Float, - y: Float, - z: Float, + pub x: Float, + pub y: Float, + pub z: Float, +} + +impl Triplet for XYZ { + fn from_triplet(c1: Float, c2: Float, c3: Float) -> Self { + XYZ::new(c1, c2, c3) + } +} + +impl<'a> IntoIterator for &'a XYZ { + type Item = &'a Float; + type IntoIter = std::array::IntoIter<&'a Float, 3>; + + fn into_iter(self) -> Self::IntoIter { + [&self.x, &self.y, &self.z].into_iter() + } } impl XYZ { @@ -38,11 +62,25 @@ impl XYZ { Point2f::new(self.x / sum, self.y / sum) } - pub fn from_xyy(xy: Point2f, y: Float) -> Self { - if xy.y() == 0.0 { - return Self { x: 0.0, y: 0.0, z: 0.0} + pub fn from_xyy(xy: Point2f, y: Option) -> Self { + let scale: Float; + if let Some(y_val) = y { + scale = y_val; + } else { + scale = 1.; } - Self::new(xy.x() * y / xy.y(), y, (1.0 - xy.x() - xy.y()) * y / xy.y()) + if xy.y() == 0.0 { + return Self { + x: 0.0, + y: 0.0, + z: 0.0, + }; + } + Self::new( + xy.x() * scale / xy.y(), + scale, + (1.0 - xy.x() - xy.y()) * scale / xy.y(), + ) } } @@ -144,7 +182,6 @@ impl MulAssign for XYZ { } } -// Scalar multiplication (xyz * float) impl Mul for XYZ { type Output = Self; fn mul(self, rhs: Float) -> Self { @@ -213,7 +250,6 @@ impl fmt::Display for XYZ { } } - #[derive(Debug, Clone)] pub struct RGB { pub r: Float, @@ -221,6 +257,20 @@ pub struct RGB { pub b: Float, } +impl Triplet for RGB { + fn from_triplet(c1: Float, c2: Float, c3: Float) -> Self { + RGB::new(c1, c2, c3) + } +} + +impl<'a> IntoIterator for &'a RGB { + type Item = &'a Float; + type IntoIter = std::array::IntoIter<&'a Float, 3>; + + fn into_iter(self) -> Self::IntoIter { + [&self.r, &self.g, &self.b].into_iter() + } +} impl RGB { pub fn new(r: Float, g: Float, b: Float) -> Self { @@ -399,10 +449,10 @@ impl fmt::Display for RGB { } } - pub const RES: usize = 64; pub type CoefficientArray = [[[[[Float; 3]; RES]; RES]; RES]; 3]; +#[derive(Debug)] pub struct RGBToSpectrumTable { z_nodes: &'static [f32], coeffs: &'static CoefficientArray, @@ -421,30 +471,29 @@ impl RGBSigmoidPolynomial { } pub fn evaluate(&self, lambda: Float) -> Float { - let eval = match crate::core::pbrt::evaluate_polynomial(lambda, &[self.c0, self.c1, self.c2]) { - Some(value) => value, - None => { panic!("evaluate_polynomial returned None with non-empty coefficients, this should not happen.") }, - }; + let eval = + match crate::core::pbrt::evaluate_polynomial(lambda, &[self.c0, self.c1, self.c2]) { + Some(value) => value, + None => { + panic!("evaluate_polynomial returned None with non-empty coefficients") + } + }; Self::s(eval) - } + } pub fn max_value(&self) -> Float { let lambda = -self.c1 / (2.0 * self.c0); let result = self.evaluate(360.0).max(self.evaluate(830.0)); if lambda >= 360.0 && lambda <= 830.0 { - return result.max(self.evaluate(lambda)) + return result.max(self.evaluate(lambda)); } result } fn s(x: Float) -> Float { - if x.is_infinite() { - if x > 0.0 { - return 1.0 - } else { - return 0.0 - } + if x.is_infinite() { + if x > 0.0 { return 1.0 } else { return 0.0 } } 0.5 + x / (2.0 * (1.0 + (x * x)).sqrt()) } @@ -457,7 +506,11 @@ impl RGBToSpectrumTable { pub fn to_polynomial(&self, rgb: RGB) -> RGBSigmoidPolynomial { if rgb[0] == rgb[1] && rgb[1] == rgb[2] { - return RGBSigmoidPolynomial::new(0.0, 0.0, (rgb[0] - 0.5)/(rgb[0] * (1.0 - rgb[0])).sqrt()) + return RGBSigmoidPolynomial::new( + 0.0, + 0.0, + (rgb[0] - 0.5) / (rgb[0] * (1.0 - rgb[0])).sqrt(), + ); } let maxc; if rgb[0] > rgb[1] { @@ -486,11 +539,387 @@ impl RGBToSpectrumTable { let dz = (z - self.z_nodes[zi]) / (self.z_nodes[zi + 1] - self.z_nodes[zi]); let mut c = [0.0; 3]; for i in 0..3 { - let co = |dx: usize, dy: usize, dz: usize| self.coeffs[maxc][zi as usize + dz][yi as usize + dy][xi as usize + dx][i]; - c[i] = lerp(dz, - lerp(dy as Float, lerp(dx as Float, co(0, 0, 0) as Float, co(1, 0, 0)) as Float, lerp(dx as Float, co(0, 1, 0) as Float, co(1, 1, 0) as Float)), - lerp(dy as Float, lerp(dx as Float, co(0, 0, 1) as Float, co(1, 0, 1)) as Float, lerp(dx as Float, co(0, 1, 1) as Float, co(1, 1, 1) as Float))); + let co = |dx: usize, dy: usize, dz: usize| { + self.coeffs[maxc][zi as usize + dz][yi as usize + dy][xi as usize + dx][i] + }; + c[i] = lerp( + dz, + lerp( + dy as Float, + lerp(dx as Float, co(0, 0, 0) as Float, co(1, 0, 0)) as Float, + lerp(dx as Float, co(0, 1, 0) as Float, co(1, 1, 0) as Float), + ), + lerp( + dy as Float, + lerp(dx as Float, co(0, 0, 1) as Float, co(1, 0, 1)) as Float, + lerp(dx as Float, co(0, 1, 1) as Float, co(1, 1, 1) as Float), + ), + ); + } + RGBSigmoidPolynomial { + c0: c[0], + c1: c[1], + c2: c[2], } - RGBSigmoidPolynomial { c0: c[0], c1: c[1], c2: c[2] } } } + +const LMS_FROM_XYZ: SquareMatrix = SquareMatrix::new([ + [0.8951, 0.2664, -0.1614], + [-0.7502, 1.7135, 0.0367], + [0.0389, -0.0685, 1.0296], +]); + +const XYZ_FROM_LMS: SquareMatrix = SquareMatrix::new([ + [0.986993, -0.147054, 0.159963], + [0.432305, 0.51836, 0.0492912], + [-0.00852866, 0.0400428, 0.968487], +]); + +pub fn white_balance(src_white: Point2f, target_white: Point2f) -> SquareMatrix { + // Find LMS coefficients for source and target white + let src_xyz = XYZ::from_xyy(src_white, None); + let dst_xyz = XYZ::from_xyy(target_white, None); + let src_lms = LMS_FROM_XYZ * src_xyz; + let dst_lms = LMS_FROM_XYZ * dst_xyz; + + // Return white balancing matrix for source and target white + let lms_correct = SquareMatrix::::diag(&[ + dst_lms[0] / src_lms[0], + dst_lms[1] / src_lms[1], + dst_lms[2] / src_lms[2], + ]); + XYZ_FROM_LMS * lms_correct * LMS_FROM_XYZ +} + +pub trait ColorEncoding: 'static + Send + Sync + fmt::Debug + fmt::Display { + fn from_linear_slice(&self, vin: &[Float], vout: &mut [u8]); + fn to_linear_slice(&self, vin: &[u8], vout: &mut [Float]); + fn to_float_linear(&self, v: Float) -> Float; + fn type_id(&self) -> TypeId { + TypeId::of::() + } +} + +#[derive(Debug)] +pub struct LinearEncoding; +impl ColorEncoding for LinearEncoding { + fn from_linear_slice(&self, vin: &[Float], vout: &mut [u8]) { + for (i, &v) in vin.iter().enumerate() { + vout[i] = (v.clamp(0.0, 1.0) * 255.0 + 0.5) as u8; + } + } + fn to_linear_slice(&self, vin: &[u8], vout: &mut [Float]) { + for (i, &v) in vin.iter().enumerate() { + vout[i] = v as Float / 255.0; + } + } + fn to_float_linear(&self, v: Float) -> Float { + v + } +} + +impl fmt::Display for LinearEncoding { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Linear") + } +} + +#[derive(Debug)] +pub struct SRGBEncoding; +impl ColorEncoding for SRGBEncoding { + fn from_linear_slice(&self, vin: &[Float], vout: &mut [u8]) { + for (i, &v_linear) in vin.iter().enumerate() { + let v = v_linear.clamp(0.0, 1.0); + let v_encoded = if v <= 0.0031308 { + v * 12.92 + } else { + 1.055 * v.powf(1.0 / 2.4) - 0.055 + }; + vout[i] = (v_encoded * 255.0 + 0.5) as u8; + } + } + + fn to_linear_slice(&self, vin: &[u8], vout: &mut [Float]) { + for (i, &v) in vin.iter().enumerate() { + vout[i] = SRGB_TO_LINEAR_LUT[v as usize]; + } + } + + fn to_float_linear(&self, v: Float) -> Float { + let v = v.clamp(0.0, 1.0); + if v <= 0.04045 { + v / 12.92 + } else { + ((v + 0.055) / 1.055).powf(2.4) + } + } +} + +impl fmt::Display for SRGBEncoding { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "sRGB") + } +} + +pub static LINEAR: Lazy<&'static dyn ColorEncoding> = Lazy::new(|| &LinearEncoding); +pub static SRGB: Lazy<&'static dyn ColorEncoding> = Lazy::new(|| &SRGBEncoding); + +const SRGB_TO_LINEAR_LUT: [Float; 256] = [ + 0.0000000000, + 0.0003035270, + 0.0006070540, + 0.0009105810, + 0.0012141080, + 0.0015176350, + 0.0018211619, + 0.0021246888, + 0.0024282159, + 0.0027317430, + 0.0030352699, + 0.0033465356, + 0.0036765069, + 0.0040247170, + 0.0043914421, + 0.0047769533, + 0.0051815170, + 0.0056053917, + 0.0060488326, + 0.0065120910, + 0.0069954102, + 0.0074990317, + 0.0080231922, + 0.0085681248, + 0.0091340570, + 0.0097212177, + 0.0103298230, + 0.0109600937, + 0.0116122449, + 0.0122864870, + 0.0129830306, + 0.0137020806, + 0.0144438436, + 0.0152085144, + 0.0159962922, + 0.0168073755, + 0.0176419523, + 0.0185002182, + 0.0193823613, + 0.0202885624, + 0.0212190095, + 0.0221738834, + 0.0231533647, + 0.0241576303, + 0.0251868572, + 0.0262412224, + 0.0273208916, + 0.0284260381, + 0.0295568332, + 0.0307134409, + 0.0318960287, + 0.0331047624, + 0.0343398079, + 0.0356013142, + 0.0368894450, + 0.0382043645, + 0.0395462364, + 0.0409151986, + 0.0423114114, + 0.0437350273, + 0.0451862030, + 0.0466650836, + 0.0481718220, + 0.0497065634, + 0.0512694679, + 0.0528606549, + 0.0544802807, + 0.0561284944, + 0.0578054339, + 0.0595112406, + 0.0612460710, + 0.0630100295, + 0.0648032799, + 0.0666259527, + 0.0684781820, + 0.0703601092, + 0.0722718611, + 0.0742135793, + 0.0761853904, + 0.0781874284, + 0.0802198276, + 0.0822827145, + 0.0843762159, + 0.0865004659, + 0.0886556059, + 0.0908417329, + 0.0930589810, + 0.0953074843, + 0.0975873619, + 0.0998987406, + 0.1022417471, + 0.1046164930, + 0.1070231125, + 0.1094617173, + 0.1119324341, + 0.1144353822, + 0.1169706732, + 0.1195384338, + 0.1221387982, + 0.1247718409, + 0.1274376959, + 0.1301364899, + 0.1328683347, + 0.1356333494, + 0.1384316236, + 0.1412633061, + 0.1441284865, + 0.1470272839, + 0.1499598026, + 0.1529261619, + 0.1559264660, + 0.1589608639, + 0.1620294005, + 0.1651322246, + 0.1682693958, + 0.1714410931, + 0.1746473908, + 0.1778884083, + 0.1811642349, + 0.1844749898, + 0.1878207624, + 0.1912016720, + 0.1946178079, + 0.1980693042, + 0.2015562356, + 0.2050787061, + 0.2086368501, + 0.2122307271, + 0.2158605307, + 0.2195262313, + 0.2232279778, + 0.2269658893, + 0.2307400703, + 0.2345506549, + 0.2383976579, + 0.2422811985, + 0.2462013960, + 0.2501583695, + 0.2541521788, + 0.2581829131, + 0.2622507215, + 0.2663556635, + 0.2704978585, + 0.2746773660, + 0.2788943350, + 0.2831487954, + 0.2874408960, + 0.2917706966, + 0.2961383164, + 0.3005438447, + 0.3049873710, + 0.3094689548, + 0.3139887452, + 0.3185468316, + 0.3231432438, + 0.3277781308, + 0.3324515820, + 0.3371636569, + 0.3419144452, + 0.3467040956, + 0.3515326977, + 0.3564002514, + 0.3613068759, + 0.3662526906, + 0.3712377846, + 0.3762622178, + 0.3813261092, + 0.3864295185, + 0.3915725648, + 0.3967553079, + 0.4019778669, + 0.4072403014, + 0.4125427008, + 0.4178851545, + 0.4232677519, + 0.4286905527, + 0.4341537058, + 0.4396572411, + 0.4452012479, + 0.4507858455, + 0.4564110637, + 0.4620770514, + 0.4677838385, + 0.4735315442, + 0.4793202281, + 0.4851499796, + 0.4910208881, + 0.4969330430, + 0.5028865933, + 0.5088814497, + 0.5149177909, + 0.5209956765, + 0.5271152258, + 0.5332764983, + 0.5394796133, + 0.5457245708, + 0.5520114899, + 0.5583404899, + 0.5647116303, + 0.5711249113, + 0.5775805116, + 0.5840784907, + 0.5906189084, + 0.5972018838, + 0.6038274169, + 0.6104956269, + 0.6172066331, + 0.6239604354, + 0.6307572126, + 0.6375969648, + 0.6444797516, + 0.6514056921, + 0.6583748460, + 0.6653873324, + 0.6724432111, + 0.6795425415, + 0.6866854429, + 0.6938719153, + 0.7011020184, + 0.7083759308, + 0.7156936526, + 0.7230552435, + 0.7304608822, + 0.7379105687, + 0.7454043627, + 0.7529423237, + 0.7605246305, + 0.7681512833, + 0.7758223414, + 0.7835379243, + 0.7912980318, + 0.7991028428, + 0.8069523573, + 0.8148466945, + 0.8227858543, + 0.8307699561, + 0.8387991190, + 0.8468732834, + 0.8549926877, + 0.8631572723, + 0.8713672161, + 0.8796223402, + 0.8879231811, + 0.8962693810, + 0.9046613574, + 0.9130986929, + 0.9215820432, + 0.9301108718, + 0.9386858940, + 0.9473065734, + 0.9559735060, + 0.9646862745, + 0.9734454751, + 0.9822505713, + 0.9911022186, + 1.0000000000, +]; diff --git a/src/utils/colorspace.rs b/src/utils/colorspace.rs index fa9f794..8e42bc0 100644 --- a/src/utils/colorspace.rs +++ b/src/utils/colorspace.rs @@ -1,47 +1,66 @@ +use super::color::{RGB, RGBSigmoidPolynomial, RGBToSpectrumTable, XYZ}; +use super::math::SquareMatrix; +use super::spectrum::{DenselySampledSpectrum, SampledSpectrum, Spectrum}; use crate::core::pbrt::Float; -use super::geometry::Point2f; -use super::transform::SquareMatrix; -use super::color::{RGBSigmoidPolynomial, RGBToSpectrumTable, RGB, XYZ}; -use super::spectrum::{DenselySampledSpectrum, Spectrum, SampledSpectrum}; +use crate::geometry::Point2f; use std::cmp::{Eq, PartialEq}; +use std::error::Error; use std::sync::Arc; -#[derive(Clone)] -pub struct RGBColorspace { - r: Point2f, - g: Point2f, - b: Point2f, - w: Point2f, - illuminant: Spectrum, - rgb_to_spectrum_table: Arc, - xyz_from_rgb: SquareMatrix, - rgb_from_xyz: SquareMatrix, +pub enum ColorEncoding { + Linear, + SRGB, +} +#[derive(Debug, Clone)] +pub struct RGBColorspace { + pub r: Point2f, + pub g: Point2f, + pub b: Point2f, + pub w: Point2f, + pub illuminant: Spectrum, + pub rgb_to_spectrum_table: Arc, + pub xyz_from_rgb: SquareMatrix, + pub rgb_from_xyz: SquareMatrix, } impl RGBColorspace { - pub fn new(r: Point2f, - g: Point2f, - b: Point2f, - illuminant: Spectrum, - rgb_to_spectrum_table: RGBToSpectrumTable) -> Self { + pub fn new( + r: Point2f, + g: Point2f, + b: Point2f, + illuminant: Spectrum, + rgb_to_spectrum_table: RGBToSpectrumTable, + ) -> Result> { let w_xyz = illuminant.to_xyz(); let w = w_xyz.xy(); - let r_xyz = XYZ::from_xyy(r, 1.0); - let g_xyz = XYZ::from_xyy(g, 1.0); - let b_xyz = XYZ::from_xyy(b, 1.0); - let rgb_values = [[r_xyz.x(), g_xyz.x(), b_xyz.x()], [r_xyz.y(), g_xyz.y(), b_xyz.y()], [r_xyz.z(), g_xyz.z(), g_xyz.z()]]; - let rgb: SquareMatrix = SquareMatrix { m: rgb_values }; - let c = match rgb.inverse() { - Some(inv_matrix) => { inv_matrix * w_xyz }, - None => { panic!("Cannot create RGBColorspace: The RGB primaries form a singular matrix."); } - }; + let r_xyz = XYZ::from_xyy(r, Some(1.0)); + let g_xyz = XYZ::from_xyy(g, Some(1.0)); + let b_xyz = XYZ::from_xyy(b, Some(1.0)); + let rgb_values = [ + [r_xyz.x(), g_xyz.x(), b_xyz.x()], + [r_xyz.y(), g_xyz.y(), b_xyz.y()], + [r_xyz.z(), g_xyz.z(), g_xyz.z()], + ]; + let rgb = SquareMatrix::new(rgb_values); + let c = rgb.inverse()? * w_xyz; let xyz_from_rgb_m = [[c[0], 0.0, 0.0], [0.0, c[1], 0.0], [0.0, 0.0, c[2]]]; - let xyz_from_rgb = rgb * SquareMatrix { m: xyz_from_rgb_m }; - let rgb_from_xyz = xyz_from_rgb.inverse().expect("Failed to invert the XYZfromRGB matrix. Is it singular?"); + let xyz_from_rgb = rgb * SquareMatrix::new(xyz_from_rgb_m); + let rgb_from_xyz = xyz_from_rgb + .inverse() + .expect("Failed to invert the XYZfromRGB matrix. Is it singular?"); - Self { r, g, b, w, illuminant, rgb_to_spectrum_table: Arc::new(rgb_to_spectrum_table), xyz_from_rgb, rgb_from_xyz } + Ok(Self { + r, + g, + b, + w, + illuminant, + rgb_to_spectrum_table: Arc::new(rgb_to_spectrum_table), + xyz_from_rgb, + rgb_from_xyz, + }) } pub fn to_xyz(&self, rgb: RGB) -> XYZ { @@ -54,12 +73,11 @@ impl RGBColorspace { pub fn to_rgb_coeffs(&self, rgb: RGB) -> RGBSigmoidPolynomial { self.rgb_to_spectrum_table.to_polynomial(rgb) - } pub fn convert_colorspace(&self, other: &RGBColorspace) -> SquareMatrix { if self == other { - return SquareMatrix::default() + return SquareMatrix::default(); } self.rgb_from_xyz * other.xyz_from_rgb @@ -68,8 +86,10 @@ impl RGBColorspace { impl PartialEq for RGBColorspace { fn eq(&self, other: &Self) -> bool { - self.r == other.r && self.g == other.g && self.b == other.b && self.w == other.w && - Arc::ptr_eq(&self.rgb_to_spectrum_table, &other.rgb_to_spectrum_table) + self.r == other.r + && self.g == other.g + && self.b == other.b + && self.w == other.w + && Arc::ptr_eq(&self.rgb_to_spectrum_table, &other.rgb_to_spectrum_table) } } - diff --git a/src/utils/containers.rs b/src/utils/containers.rs index 540bc39..d204d25 100644 --- a/src/utils/containers.rs +++ b/src/utils/containers.rs @@ -3,8 +3,9 @@ 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}; +use crate::geometry::{Bounds2i, Bounds3i, Point2i, Point3f, Point3i, Vector3f, Vector3i}; +#[derive(Debug)] pub struct Array2D { values: Vec, extent: Bounds2i, @@ -21,7 +22,7 @@ impl Array2D { Self { values, extent } } - pub fn with_default(extent: Bounds2i, default_val: T) -> Self + pub fn new_from_bounds(extent: Bounds2i, default_val: T) -> Self where T: Clone, { @@ -30,18 +31,18 @@ impl Array2D { Self { values, extent } } - pub fn x_size(&self) -> i32 { - self.extent.p_max.x() - self.extent.p_min.x() + pub fn x_size(&self) -> usize { + (self.extent.p_max.x() - self.extent.p_min.x()) as usize } - pub fn y_size(&self) -> i32 { - self.extent.p_max.y() - self.extent.p_min.y() + pub fn y_size(&self) -> usize { + (self.extent.p_max.y() - self.extent.p_min.y()) as usize } pub fn size(&self) -> usize { self.values.len() } - + pub fn as_slice(&self) -> &[T] { &self.values } @@ -55,7 +56,7 @@ impl Array2D { 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 + (pp.y() * width as i32 + pp.x()) as usize } } diff --git a/src/utils/error.rs b/src/utils/error.rs index eb34411..efd1569 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -1,6 +1,6 @@ -use thiserror::Error; use image::error; use std::fmt; +use thiserror::Error; use crate::utils::image::PixelFormat; @@ -72,13 +72,10 @@ pub enum PixelFormatError { }, #[error("Invalid conversion from pixel format {from:?} to {to:?}.")] - InvalidConversion { - from: PixelFormat, - to: PixelFormat, - }, + InvalidConversion { from: PixelFormat, to: PixelFormat }, - #[error("Internal invariant violated: image format is {expected:?} but pixel data is of a different type.")] - InternalFormatMismatch { - expected: PixelFormat, - }, + #[error( + "Internal invariant violated: image format is {expected:?} but pixel data is of a different type." + )] + InternalFormatMismatch { expected: PixelFormat }, } diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs deleted file mode 100644 index 0a83a31..0000000 --- a/src/utils/geometry.rs +++ /dev/null @@ -1,781 +0,0 @@ -use num_traits::{Num, Bounded, Float as NumFloat, Zero, Signed}; -use std::ops::{Sub, SubAssign, Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Index, IndexMut}; -use std::sync::Arc; -use std::f32::consts::PI; - -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 -{ - fn data(&self) -> &[T; N]; - fn data_mut(&mut self) -> &mut [T; N]; - fn from_array(arr: [T; N]) -> Self; -} - -fn min(a: T, b: T) -> T { if a < b { a } else { b }} -fn max(a: T, b: T) -> T { if a > b { a } else { b }} - -// N-dimensional displacement -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Vector(pub [T; N]); -// N-dimensional location -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Point(pub [T; N]); -// N-dimensional surface normal -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Normal(pub [T; N]); - -macro_rules! impl_tuple_core { - ($Struct:ident) => { - impl Tuple for $Struct { - #[inline] fn data(&self) -> &[T; N] { &self.0 } - #[inline] fn data_mut(&mut self) -> &mut [T; N] { &mut self.0 } - #[inline] fn from_array(arr: [T; N]) -> Self { Self(arr) } - } - - impl Default for $Struct { - fn default() -> Self { Self([T::default(); N]) } - } - - 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] } - } - impl IndexMut for $Struct { - #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.0[index] } - } - - impl Neg for $Struct where T: Neg + Copy { - type Output = Self; - fn neg(self) -> Self::Output { - let mut result = self.0; - for i in 0..N { result[i] = -result[i]; } - Self(result) - } - } - }; -} - -macro_rules! impl_scalar_ops { - ($Struct:ident) => { - impl Mul for $Struct where T: Mul + Copy { - type Output = Self; - fn mul(self, rhs: T) -> Self::Output { - let mut result = self.0; - for i in 0..N { result[i] = result[i] * rhs; } - Self(result) - } - } - - impl Mul<$Struct> for Float { - type Output = $Struct; - fn mul(self, rhs: $Struct) -> Self::Output { - rhs * self - } - } - - impl MulAssign for $Struct where T: MulAssign + Copy { - fn mul_assign(&mut self, rhs: T) { - for i in 0..N { self.0[i] *= rhs; } - } - } - - impl Div for $Struct where T: Div + Copy { - type Output = Self; - fn div(self, rhs: T) -> Self::Output { - let mut result = self.0; - for i in 0..N { result[i] = result[i] / rhs; } - Self(result) - } - } - impl DivAssign for $Struct where T: DivAssign + Copy { - fn div_assign(&mut self, rhs: T) { - for i in 0..N { self.0[i] /= rhs; } - } - } - }; -} - -impl_tuple_core!(Vector); -impl_tuple_core!(Point); -impl_tuple_core!(Normal); - -impl_scalar_ops!(Vector); -impl_scalar_ops!(Normal); - - -macro_rules! impl_op { - ($Op:ident, $op:ident, $Lhs:ident, $Rhs:ident, $Output:ident) => { - impl $Op<$Rhs> for $Lhs - where - T: $Op + Copy, - { - type Output = $Output; - fn $op(self, rhs: $Rhs) -> Self::Output { - let mut result = self.0; - for i in 0..N { - result[i] = self.0[i].$op(rhs.0[i]); - } - $Output(result) - } - } - }; -} - -macro_rules! impl_op_assign { - ($OpAssign:ident, $op_assign:ident, $Lhs:ident, $Rhs:ident) => { - impl $OpAssign<$Rhs> for $Lhs - where - T: $OpAssign + Copy, - { - fn $op_assign(&mut self, rhs: $Rhs) { - for i in 0..N { - self.0[i].$op_assign(rhs.0[i]); - } - } - } - }; -} - -// Addition -impl_op!(Add, add, Vector, Vector, Vector); -impl_op!(Add, add, Point, Vector, Point); -impl_op!(Add, add, Vector, Point, Point); -impl_op!(Add, add, Normal, Normal, Normal); - -// Subtraction -impl_op!(Sub, sub, Vector, Vector, Vector); -impl_op!(Sub, sub, Point, Vector, Point); -impl_op!(Sub, sub, Point, Point, Vector); -impl_op!(Sub, sub, Normal, Normal, Normal); - -// AddAssign -impl_op_assign!(AddAssign, add_assign, Vector, Vector); -impl_op_assign!(AddAssign, add_assign, Point, Vector); -impl_op_assign!(AddAssign, add_assign, Normal, Normal); - -// SubAssign -impl_op_assign!(SubAssign, sub_assign, Vector, Vector); -impl_op_assign!(SubAssign, sub_assign, Point, Vector); -impl_op_assign!(SubAssign, sub_assign, Normal, Normal); - -pub trait Dot { - type Output; - fn dot(self, rhs: Rhs) -> Self::Output; -} - -macro_rules! impl_dot_for { - ($Lhs:ident, $Rhs:ident) => { - impl Dot<$Rhs> for $Lhs { - type Output = T; - fn dot(self, rhs: $Rhs) -> T { - let mut sum = T::zero(); - for i in 0..N { - sum = sum + self[i] * rhs[i]; - } - sum - } - } - }; -} - -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) } -} -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 { - pub fn x(&self) -> T { self.0[0] } - pub fn y(&self) -> T { self.0[1] } - } - impl $Struct { - pub fn x(&self) -> T { self.0[0] } - pub fn y(&self) -> T { self.0[1] } - pub fn z(&self) -> T { self.0[2] } - } - }; -} - -impl_accessors!(Vector); -impl_accessors!(Point); -impl_accessors!(Normal); - - -// Vector stuff -pub trait Normed { - type Scalar; - fn norm_squared(&self) -> Self::Scalar; - fn norm(&self) -> Self::Scalar where Self::Scalar: NumFloat { self.norm_squared().sqrt() } - fn normalize(self) -> Self where Self: Sized, Self: Div, Self::Scalar: NumFloat; -} - -macro_rules! impl_normed_for { - ($Struct:ident) => { - impl Normed for $Struct { - type Scalar = T; - fn norm_squared(&self) -> T { self.dot(*self) } - fn normalize(self) -> Self where Self: Div, T: NumFloat { self / self.norm() } - } - }; -} - -impl_normed_for!(Vector); -impl_normed_for!(Normal); - -macro_rules! impl_abs { - ($Struct:ident) => { - impl $Struct - where - T: Signed + Copy, - { - pub fn abs(self) -> Self { - let mut result = self.0; - for i in 0..N { - result[i] = result[i].abs(); - } - Self(result) - } - } - }; -} - -impl_abs!(Vector); -impl_abs!(Normal); - -impl Point -where - T: NumFloat + Copy, - Point: Sub>, - Vector: Normed, -{ - pub fn distance(self, other: Self) -> T { - (self - other).norm() - } - - pub fn distance_squared(self, other: Self) -> T { - (self - other).norm_squared() - } -} - -// Utility aliases and functions -pub type Point2 = Point; -pub type Point2f = Point2; -pub type Point2i = Point2; -pub type Point3 = Point; -pub type Point3f = Point3; -pub type Point3i = Point3; -pub type Vector2 = Vector; -pub type Vector2f = Vector2; -pub type Vector2i = Vector2; -pub type Vector3 = Vector; -pub type Vector3f = Vector3; -pub type Vector3i = Vector3; -pub type Normal3 = Normal; -pub type Normal3f = Normal3; -pub type Normal3i = Normal3; -pub type Vector3fi = Vector; -pub type Point3fi = Point; - - -impl Vector2 { pub fn new(x: T, y: T) -> Self { Self([x, y]) } } -impl Point2 { pub fn new(x: T, y: T) -> Self { Self([x, y]) } } -impl Vector3 { pub fn new(x: T, y: T, z: T) -> Self { Self([x, y, z]) } } -impl Point3 { pub fn new(x: T, y: T, z: T) -> Self { Self([x, y, z]) } } -impl Normal3 { pub fn new(x: T, y: T, z: T) -> Self { Self([x, y, z]) } } - - -// Vector operations -impl Vector3 -where T: Num + Copy + Neg -{ - pub fn cross(self, rhs: Self) -> Self { - Self([ - self[1] * rhs[2] - self[2] * rhs[1], - self[2] * rhs[0] - self[0] * rhs[2], - self[0] * rhs[1] - self[1] * rhs[0], - ]) - } -} - -impl Vector3 -where T: Num + NumFloat + Copy + Neg -{ - pub fn coordinate_system(&self) -> (Self, Self) where T: NumFloat { - let v2 = if self[0].abs() > self[1].abs() { - Self::new(-self[2], T::zero(), self[0]) / (self[0] * self[0] + self[2] * self[2]).sqrt() - } else { - Self::new(T::zero(), self[2], -self[1]) / (self[1] * self[1] + self[2] * self[2]).sqrt() - }; - (v2, self.cross(v2)) - } -} - -impl Point { - pub fn new_from_point(p: Point) -> Self { - let mut arr = [Interval::default(); N]; - for i in 0..N { - arr[i] = Interval::new(p[i]); - } - Self(arr) - } - - pub fn new_with_error(p: Point, e: Vector) -> Self { - let mut arr = [Interval::default(); N]; - for i in 0..N { - arr[i] = Interval::new_from_value_and_error(p[i], e[i]); - } - Self(arr) - } - - pub fn error(&self) -> Vector { - let mut arr = [0.0; N]; - for i in 0..N { - arr[i] = self[i].width() / 2.0; - } - Vector(arr) - } - - pub fn midpoint(&self) -> Point { - let mut arr = [0.0; N]; - for i in 0..N { - arr[i] = self[i].midpoint(); - } - Point(arr) - } - - pub fn is_exact(&self) -> bool { - self.0.iter().all(|interval| interval.width() == 0.0) - } -} - -impl Vector { - pub fn new_from_vector(v: Vector) -> Self { - let mut arr = [Interval::default(); N]; - for i in 0..N { - arr[i] = Interval::new(v[i]); - } - Self(arr) - } - - pub fn new_with_error(v: Vector, e: Vector) -> Self { - let mut arr = [Interval::default(); N]; - for i in 0..N { - arr[i] = Interval::new_from_value_and_error(v[i], e[i]); - } - Self(arr) - } - - pub fn error(&self) -> Vector { - let mut arr = [0.0; N]; - for i in 0..N { - arr[i] = self[i].width() / 2.0; - } - Vector(arr) - } - - pub fn midpoint(&self) -> Vector { - let mut arr = [0.0; N]; - for i in 0..N { - arr[i] = self[i].midpoint(); - } - Vector(arr) - } - - pub fn is_exact(&self) -> bool { - self.0.iter().all(|interval| interval.width() == 0.0) - } -} - -impl From> for Point { - fn from(pi: Point) -> Self { - let mut arr = [0.0; N]; - for i in 0..N { - arr[i] = pi[i].midpoint(); - } - Point(arr) - } -} - -impl Normal3 -where T: Num + PartialOrd + Copy + Neg -{ - pub fn face_forward(self, v: Vector3) -> Self { - if self.dot(v) < T::zero() { -self } else { self } - } -} - -pub fn abs_cos_theta(w: Vector3f) -> Float { - w.z().abs() -} - -// SPHERICAL GEOMETRY -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) -} - -pub fn spherical_triangle_area(a: Vector3f, b: Vector3f, c: Vector3f) -> Float { - (2.0 * (a.dot(b.cross(c))).atan2(1.0 + a.dot(b) + a.dot(c) + b.dot(c))).abs() -} - -pub fn spherical_theta(v: Vector3f) -> Float { - clamp_t(v.z(), -1.0, 1.0).acos() -} - -pub fn spherical_phi(v: Vector3f) -> Float { - let p = v.y().atan2(v.x()); - if p < 0.0 { - p + 2.0 * PI - } else { - p - } -} - -// AABB BOUNDING BOXES -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Bounds { - pub p_min: Point, - pub p_max: Point, -} - -impl Bounds -where - T: Num + PartialOrd + Copy -{ - pub fn from_point(p: Point) -> Self { - Self { p_min: p, p_max: p } - } - - pub fn from_points(p1: Point, p2: Point) -> Self { - let mut p_min_arr = [T::zero(); N]; - let mut p_max_arr = [T::zero(); N]; - - for i in 0..N { - if p1[i] < p2[i] { - p_min_arr[i] = p1[i]; - p_max_arr[i] = p2[i]; - } else { - p_min_arr[i] = p2[i]; - p_max_arr[i] = p1[i]; - } - } - - Self { p_min: Point(p_min_arr), p_max: Point(p_max_arr) } - } - - pub fn union_point(self, p: Point) -> Self { - let mut p_min = self.p_min; - let mut p_max = self.p_max; - for i in 0..N { - p_min[i] = min(p_min[i], p[i]); - p_max[i] = max(p_max[i], p[i]); - } - Self { p_min, p_max } - } - - pub fn union(self, b2: Self) -> Self { - let mut p_min = self.p_min; - let mut p_max = self.p_max; - for i in 0..N { - p_min[i] = min(p_min[i], b2.p_min[i]); - p_max[i] = max(p_max[i], b2.p_max[i]); - } - Self { p_min, p_max } - } - - pub fn diagonal(&self) -> Vector { - self.p_max - self.p_min - } - - pub fn volume(&self) -> T { - let d = self.diagonal(); - 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] = lerp(t[i], self.p_min[i], self.p_max[i]) - } - - Point(results_arr) - } - - pub fn max_dimension(&self) -> usize - where - Point: Sub>, - { - let d = self.diagonal(); - let mut max_dim = 0; - let mut max_span = d[0]; - - for i in 1..N { - if d[i] > max_span { - max_span = d[i]; - max_dim = i; - } - } - max_dim - } - - pub fn offset(&self, p: &Point) -> Vector - where - Point: Sub>, - Vector: DivAssign - { - let mut o = *p - self.p_min; - let d = self.diagonal(); - for i in 0..N { - if d[i] > T::zero() { - o[i] = o[i] / d[i]; - } - } - o - } - - pub fn contains(&self, p: Point) -> bool { - (0..N).all(|i| p[i] >= self.p_min[i] && p[i] <= self.p_max[i]) - } - - pub fn contains_exclusive(&self, p: Point) -> bool { - (0..N).all(|i| p[i] >= self.p_min[i] && p[i] < self.p_max[i]) - } - - pub fn is_empty(&self) -> bool { - (0..N).any(|i| self.p_min[i] >= self.p_max[i]) - } - - pub fn is_degenerate(&self) -> bool { - (0..N).any(|i| self.p_min[i] > self.p_max[i]) - } -} - -impl Default for Bounds -where - T: Bounded + Copy, -{ - fn default() -> Self { - Self { - p_min: Point([T::max_value(); N]), - p_max: Point([T::min_value(); N]), - } - } -} - -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 - T: Num + PartialOrd + Copy + Default -{ - pub fn surface_area(&self) -> T { - let d = self.diagonal(); - let two = T::one() + T::one(); - two * (d.x() * d.y() + d.x() * d.z() + d.y() * d.z()) - } - - pub fn bounding_sphere(&self) -> (Point3, T) - where - < as Sub>::Output as Normed>::Scalar: NumFloat, - { - let two = T::one() + T::one(); - let center = self.p_min + self.diagonal() / two; - let radius = if self.contains(center) { center.distance(self.p_max) } else { T::zero() }; - (center, radius) - } -} - -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, - pub y: Vector3f, - pub z: Vector3f, -} - -impl Frame { - pub fn from_x(x: Vector3f) -> Self { - let (y, z) = x.normalize().coordinate_system(); - Self { x: x.normalize(), y, z } - } - - pub fn from_y(y: Vector3f) -> Self { - let (z, x) = y.normalize().coordinate_system(); - Self { x, y: y.normalize(), z } - } - - pub fn from_z(z: Vector3f) -> Self { - let (x, y) = z.normalize().coordinate_system(); - Self { x, y, z: z.normalize() } - } - - pub fn to_local(&self, v: Vector3f) -> Vector3f { - Vector3f::new(v.dot(self.x), v.dot(self.y), v.dot(self.z)) - } - - pub fn from_local(&self, v: Vector3f) -> Vector3f { - self.x * v.x() + self.y * v.y() + self.z * v.z() - } -} - -#[derive(Clone, Debug)] -pub struct Ray { - pub o: Point3f, - pub d: Vector3f, - pub medium: Option>, - pub time: Float, - // We do this instead of creating a trait for Rayable or some gnarly thing like that - 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 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 - } - - pub fn offset_origin(p: &Point3fi, n: &Normal3f, w: &Vector3f) -> Point3f { - let d: Float = n.abs().dot(p.error()); - let normal: Vector3f = Vector3f::from(*n); - - let mut offset = p.midpoint(); - if w.dot(normal) < 0.0 { - offset -= normal * d; - } else { - offset += normal * d; - } - - for i in 0..3 { - if n[i] > 0.0 { - offset[i] = next_float_up(offset[i]); - } else if n[i] < 0.0 { - offset[i] = next_float_down(offset[i]); - } - } - offset - } - - pub fn spawn(pi: &Point3fi, n: &Normal3f, time: Float, d: Vector3f) -> Ray { - let origin = Self::offset_origin(pi, n, &d); - Ray { - o: origin, - d, - time, - medium: None, - differential: None, - } - } - - pub fn spawn_to_point(p_from: &Point3fi, n: &Normal3f, time: Float, p_to: Point3f) -> Ray { - let d = p_to - p_from.midpoint(); - Self::spawn(p_from, n, time, d) - } - - pub fn spawn_to_interaction(p_from: &Point3fi, n_from: &Normal3f, time: Float, p_to: &Point3fi, n_to: &Normal3f) -> Ray { - let dir_for_offset = p_to.midpoint() - p_from.midpoint(); - let pf = Self::offset_origin(p_from, n_from, &dir_for_offset); - let pt = Self::offset_origin(p_to, n_to, &(pf - p_to.midpoint())); - - let d = pt - pf; - - Ray { - o: pf, - d, - time, - medium: None, - differential: None, - } - } - - pub fn scale_differentials(&mut self, s: Float) { - if let Some(differential) = &mut self.differential { - differential.rx_origin = self.o + (differential.rx_origin - self.o) * s; - differential.ry_origin = self.o + (differential.ry_origin - self.o) * s; - differential.rx_direction = self.d + (differential.rx_direction - self.d) * s; - differential.ry_direction = self.d + (differential.ry_direction - self.d) * s; - } - } -} - -#[derive(Debug, Default, Copy, Clone)] -pub struct RayDifferential { - pub rx_origin: Point3f, - pub ry_origin: Point3f, - pub rx_direction: Vector3f, - pub ry_direction: Vector3f, -} diff --git a/src/utils/hash.rs b/src/utils/hash.rs new file mode 100644 index 0000000..aec39a4 --- /dev/null +++ b/src/utils/hash.rs @@ -0,0 +1,14 @@ +use std::hash::{Hash, Hasher}; +use crate::core::pbrt::Float; +use std::collections::hash_map::DefaultHasher; + +const U32_TO_F32_SCALE: f32 = 1.0 / 4294967296.0; + +pub fn hash_float(args: T) -> Float { + let mut hasher = DefaultHasher::new(); + args.hash(&mut hasher); + let hash_u64 = hasher.finish(); + let hash_u32 = hash_u64 as u32; + (hash_u32 as f32) * U32_TO_F32_SCALE +} + diff --git a/src/utils/image.rs b/src/utils/image.rs index bef11a7..5d15f0e 100644 --- a/src/utils/image.rs +++ b/src/utils/image.rs @@ -1,24 +1,30 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::fmt; -use std::path::Path; -use std::ops::{Index, IndexMut}; -use std::fs::{File, read}; -use std::io::{self, BufRead, BufReader, Write}; -use half::f16; -use rayon::prelude::*; +use crate::core::pbrt::{Float, lerp}; +use crate::geometry::{Bounds2i, Point2f, Point2i}; +use crate::utils::color::{self, ColorEncoding, LINEAR, RGBToSpectrumTable, SRGB}; use crate::utils::colorspace::RGBColorspace; use crate::utils::error::{ImageError, PixelFormatError}; -use crate::core::pbrt::{lerp, Float}; -use crate::utils::geometry::{Bounds2i, Point2f, Point2i}; -use crate::utils::color::{self, ColorEncoding, SRGBColorEncoding, LinearColorEncoding}; -use crate::utils::math::{SquareMatrix, windowed_sinc, gaussian}; -use image::{ImageBuffer, DynamicImage, Rgb, Rgba, Luma}; -use exr::prelude as exr_prelude; -use exr::prelude::*; -use exr::meta::header::Header; -use exr::image as exr_image; +use crate::utils::math::{SquareMatrix, gaussian, windowed_sinc}; +use crate::utils::spectrum::DenselySampledSpectrum; +use anyhow::{Result, bail}; +use exr::image::write::WritableImage; +use exr::image::{AnyChannel, AnyChannels, Encoding, FlatSamples, Image as ExrImage, Layer}; use exr::math::Vec2; +use exr::meta::attribute::{ + AttributeValue, Chromaticities as ExrChromaticities, IntegerBounds, Matrix4x4, Text, +}; +use exr::meta::header::{ImageAttributes, LayerAttributes}; +use half::f16; +use image::{DynamicImage, ImageBuffer, Luma, Rgb, Rgba}; +use rayon::prelude::*; +use smallvec::SmallVec; +use std::collections::HashMap; +use std::fmt; +use std::fs::{File, read}; +use std::io::{self, BufRead, BufReader, BufWriter, Write}; +use std::ops::{Index, IndexMut}; +use std::path::Path; +use std::str::FromStr; +use std::sync::Arc; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PixelFormat { @@ -73,9 +79,23 @@ pub struct ImageChannelDesc { pub offset: Vec, } +impl ImageChannelDesc { + pub fn new(offset: &[usize]) -> Self { + Self { + offset: offset.to_vec(), + } + } +} + #[derive(Debug, Clone)] pub struct ImageChannelValues(pub Vec); +impl From<&[Float]> for ImageChannelValues { + fn from(slice: &[Float]) -> Self { + Self(slice.to_vec()) + } +} + #[derive(Debug)] pub struct ImageMetadata { pub render_time_seconds: Option, @@ -97,13 +117,13 @@ enum PixelData { F32(Vec), } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Image { resolution: Point2i, channel_names: Vec, format: PixelFormat, pixels: PixelData, - encoding: &'static dyn ColorEncoding, + encoding: Arc, } #[derive(Debug)] @@ -122,65 +142,87 @@ impl fmt::Display for PixelFormat { } } -impl fmt::Display for WrapMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - WrapMode::Black => write!(f, "black"), - WrapMode::Clamp => write!(f, "clamp"), - WrapMode::Repeat => write!(f, "repeat"), - WrapMode::OctahedralSphere => write!(f, "octahedralsphere"), - } - } -} - -// impl FromStr for WrapMode { -// type Err = (); // Simple error type -// fn from_str(s: &str) -> Result { -// match s { -// "black" => Ok(WrapMode::Black), -// "clamp" => Ok(WrapMode::Clamp), -// "repeat" => Ok(WrapMode::Repeat), -// "octahedralsphere" => Ok(WrapMode::OctahedralSphere), -// _ => Err(()), +// impl fmt::Display for WrapMode { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// match self { +// WrapMode::Black => write!(f, "black"), +// WrapMode::Clamp => write!(f, "clamp"), +// WrapMode::Repeat => write!(f, "repeat"), +// WrapMode::OctahedralSphere => write!(f, "octahedralsphere"), // } // } // } +// +// // impl FromStr for WrapMode { +// // type Err = (); // Simple error type +// // fn from_str(s: &str) -> Result { +// // match s { +// // "black" => Ok(WrapMode::Black), +// // "clamp" => Ok(WrapMode::Clamp), +// // "repeat" => Ok(WrapMode::Repeat), +// // "octahedralsphere" => Ok(WrapMode::OctahedralSphere), +// // _ => Err(()), +// // } +// // } +// // } impl WrapMode2D { - pub fn new(u: WrapMode, v: WrapMode) -> Self { Self { uv: [u, v] } } + pub fn new(u: WrapMode, v: WrapMode) -> Self { + Self { uv: [u, v] } + } } impl From for WrapMode2D { - fn from(w: WrapMode) -> Self { Self { uv: [w, w] } } + fn from(w: WrapMode) -> Self { + Self { uv: [w, w] } + } } impl ImageChannelDesc { - pub fn len(&self) -> usize { self.offset.len() } - pub fn is_empty(&self) -> bool { self.offset.is_empty() } + pub fn len(&self) -> usize { + self.offset.len() + } + pub fn is_empty(&self) -> bool { + self.offset.is_empty() + } pub fn is_identity(&self) -> bool { self.offset.iter().enumerate().all(|(i, &off)| i == off) } } impl ImageChannelValues { - pub fn new(size: usize, value: Float) -> Self { Self(vec![value; size]) } - pub fn len(&self) -> usize { self.0.len() } - pub fn as_slice(&self) -> &[Float] { &self.0 } + pub fn new(size: usize, value: Float) -> Self { + Self(vec![value; size]) + } + pub fn len(&self) -> usize { + self.0.len() + } + pub fn as_slice(&self) -> &[Float] { + &self.0 + } pub fn max_value(&self) -> Float { self.0.iter().fold(Float::NEG_INFINITY, |a, &b| a.max(b)) } pub fn average(&self) -> Float { - if self.0.is_empty() { 0.0 } else { self.0.iter().sum::() / self.0.len() as Float } + if self.0.is_empty() { + 0.0 + } else { + self.0.iter().sum::() / self.0.len() as Float + } } } impl Index for ImageChannelValues { type Output = Float; - fn index(&self, index: usize) -> &Self::Output { &self.0[index] } + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } } impl IndexMut for ImageChannelValues { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.0[index] } + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } } impl ImageMetadata { @@ -198,8 +240,9 @@ impl ImageMetadata { string_vectors: HashMap::new(), } } - pub fn get_color_space(&self) -> &'static RGBColorspace { - self.colorspace.unwrap_or_else(|| RGBColorspace::default()) + + pub fn get_color_space(&self) -> Option<&RGBColorspace> { + self.colorspace } } @@ -246,7 +289,14 @@ impl fmt::Display for ImageMetadata { impl fmt::Display for Image { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[ Image format: {} resolution: {:?} channel_names: {:?} encoding: {:?} ]", self.format, self.resolution, self.channel_names, self.encoding.to_string()) + write!( + f, + "[ Image format: {} resolution: {:?} channel_names: {:?} encoding: {:?} ]", + self.format, + self.resolution, + self.channel_names, + self.encoding.to_string() + ) } } @@ -278,26 +328,14 @@ fn remap_pixel_coords(p: &mut Point2i, resolution: Point2i, wrap_mode: WrapMode2 } true } - -fn round_up_pow2(mut n: i32) -> i32 { - if n <= 0 { - return 1; - } - n -= 1; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n + 1 -} - +// +// impl Image { pub fn new( format: PixelFormat, resolution: Point2i, channel_names: Vec, - encoding: &'static dyn ColorEncoding, + encoding: Arc, ) -> Self { let n_elements = (resolution.x() * resolution.y()) as usize * channel_names.len(); let pixels = match format { @@ -305,30 +343,50 @@ impl Image { PixelFormat::F16 => PixelData::F16(vec![f16::from_f32(0.0); n_elements]), PixelFormat::F32 => PixelData::F32(vec![0.0; n_elements]), }; - Self { resolution, channel_names, pixels, format, encoding } + Self { + resolution, + channel_names, + pixels, + format, + encoding, + } } - // --- Accessors --- - pub fn format(&self) -> PixelFormat { self.format } - pub fn resolution(&self) -> Point2i { self.resolution } - pub fn n_channels(&self) -> usize { self.channel_names.len() } - pub fn channel_names(&self) -> &[String] { &self.channel_names } - pub fn encoding(&self) -> &'static dyn ColorEncoding { self.encoding } + pub fn format(&self) -> PixelFormat { + self.format + } + pub fn resolution(&self) -> Point2i { + self.resolution + } + pub fn n_channels(&self) -> usize { + self.channel_names.len() + } + pub fn channel_names(&self) -> &[String] { + &self.channel_names + } + pub fn encoding(&self) -> Arc { + self.encoding.clone() + } fn pixel_offset(&self, p: Point2i) -> usize { - assert!(p.x() >= 0 && p.x() < self.resolution.x()() && p.y() >= 0 && p.y() < self.resolution.y()()); - ((p.y() * self.resolution.x()() + p.x()) as usize) * self.n_channels() + assert!( + p.x() >= 0 && p.x() < self.resolution.x() && p.y() >= 0 && p.y() < self.resolution.y() + ); + ((p.y() * self.resolution.x() + p.x()) as usize) * self.n_channels() } - + pub fn get_channel(&self, mut p: Point2i, c: usize, wrap_mode: WrapMode2D) -> Float { - if !remap_pixel_coords(&mut p, self.resolution, wrap_mode) { return 0.0; } + if !remap_pixel_coords(&mut p, self.resolution, wrap_mode) { + return 0.0; + } let offset = self.pixel_offset(p) + c; match &self.pixels { PixelData::U8(data) => { let mut linear = [0.0]; - self.encoding.to_linear_slice(&data[offset..offset+1], &mut linear); + self.encoding + .to_linear_slice(&data[offset..offset + 1], &mut linear); linear[0] - }, + } PixelData::F16(data) => data[offset].to_f32(), PixelData::F32(data) => data[offset], } @@ -340,48 +398,61 @@ impl Image { match &mut self.pixels { PixelData::U8(data) => { let linear = [val_no_nan]; - self.encoding.from_linear_slice(&linear, &mut data[offset..offset+1]); - }, + self.encoding + .from_linear_slice(&linear, &mut data[offset..offset + 1]); + } PixelData::F16(data) => data[offset] = f16::from_f32(val_no_nan), PixelData::F32(data) => data[offset] = val_no_nan, } } - + pub fn bilerp_channel(&self, p: Point2f, c: usize, wrap_mode: WrapMode2D) -> Float { - let x = p.x() * self.resolution.x()() as Float - 0.5; - let y = p.y() * self.resolution.y()() as Float - 0.5; - let xi = x.floor() as i32; let yi = y.floor() as i32; - let dx = x - xi as Float; let dy = y - yi as Float; + let x = p.x() * self.resolution.x() as Float - 0.5; + let y = p.y() * self.resolution.y() as Float - 0.5; + let xi = x.floor() as i32; + let yi = y.floor() as i32; + let dx = x - xi as Float; + let dy = y - yi as Float; let v00 = self.get_channel(Point2i::new(xi, yi), c, wrap_mode); let v10 = self.get_channel(Point2i::new(xi + 1, yi), c, wrap_mode); - let v01 = self.get_channel(Point2i::new(xi, yi + 1), c, wrap_mode); + let v01 = self.get_channel(Point2i::new(xi, yi + 1), c, wrap_mode); let v11 = self.get_channel(Point2i::new(xi + 1, yi + 1), c, wrap_mode); lerp(dy, lerp(dx, v00, v10), lerp(dx, v01, v11)) } - pub fn get_channels(&self, p: Point2i, desc: &ImageChannelDesc, wrap_mode: WrapMode2D) -> ImageChannelValues { + pub fn get_channels( + &self, + p: Point2i, + desc: &ImageChannelDesc, + wrap_mode: WrapMode2D, + ) -> ImageChannelValues { let mut cv = ImageChannelValues::new(desc.len(), 0.0); let mut pp = p; - if !remap_pixel_coords(&mut pp, self.resolution, wrap_mode) { return cv; } + if !remap_pixel_coords(&mut pp, self.resolution, wrap_mode) { + return cv; + } let offset = self.pixel_offset(pp); match &self.pixels { PixelData::U8(data) => { for i in 0..desc.len() { let mut linear = [0.0]; - self.encoding.to_linear_slice(&data[offset + desc.offset[i]..offset + desc.offset[i] + 1], &mut linear); + self.encoding.to_linear_slice( + &data[offset + desc.offset[i]..offset + desc.offset[i] + 1], + &mut linear, + ); cv[i] = linear[0]; } - }, + } PixelData::F16(data) => { for i in 0..desc.len() { cv[i] = data[offset + desc.offset[i]].to_f32(); } - }, + } PixelData::F32(data) => { for i in 0..desc.len() { cv[i] = data[offset + desc.offset[i]]; } - }, + } } cv } @@ -390,7 +461,12 @@ impl Image { self.get_channels(p, &self.all_channels_desc(), wrap_mode) } - pub fn set_channels(&mut self, p: Point2i, desc: &ImageChannelDesc, values: &ImageChannelValues) { + pub fn set_channels( + &mut self, + p: Point2i, + desc: &ImageChannelDesc, + values: &ImageChannelValues, + ) { assert_eq!(desc.len(), values.len()); for i in 0..desc.len() { self.set_channel(p, desc.offset[i], values[i]); @@ -408,12 +484,17 @@ impl Image { } pub fn lookup_nearest_channel(&self, p: Point2f, c: usize, wrap_mode: WrapMode2D) -> Float { - let x = (p.x() * self.resolution.x()() as Float).round() as i32; - let y = (p.y() * self.resolution.y()() as Float).round() as i32; + let x = (p.x() * self.resolution.x() as Float).round() as i32; + let y = (p.y() * self.resolution.y() as Float).round() as i32; self.get_channel(Point2i::new(x, y), c, wrap_mode) } - pub fn lookup_nearest(&self, p: Point2f, desc: &ImageChannelDesc, wrap_mode: WrapMode2D) -> ImageChannelValues { + pub fn lookup_nearest( + &self, + p: Point2f, + desc: &ImageChannelDesc, + wrap_mode: WrapMode2D, + ) -> ImageChannelValues { let mut cv = ImageChannelValues::new(desc.len(), 0.0); for i in 0..desc.len() { cv[i] = self.lookup_nearest_channel(p, desc.offset[i], wrap_mode); @@ -425,7 +506,12 @@ impl Image { self.lookup_nearest(p, &self.all_channels_desc(), wrap_mode) } - pub fn bilerp(&self, p: Point2f, desc: &ImageChannelDesc, wrap_mode: WrapMode2D) -> ImageChannelValues { + pub fn bilerp( + &self, + p: Point2f, + desc: &ImageChannelDesc, + wrap_mode: WrapMode2D, + ) -> ImageChannelValues { let mut cv = ImageChannelValues::new(desc.len(), 0.0); for i in 0..desc.len() { cv[i] = self.bilerp_channel(p, desc.offset[i], wrap_mode); @@ -437,8 +523,6 @@ impl Image { self.bilerp(p, &self.all_channels_desc(), wrap_mode) } - // --- Image Manipulation and Analysis --- - pub fn get_channel_desc(&self, requested_channels: &[String]) -> Option { let mut desc = ImageChannelDesc::default(); for req_ch in requested_channels { @@ -451,8 +535,17 @@ impl Image { } pub fn select_channels(&self, desc: &ImageChannelDesc) -> Image { - let new_channels: Vec = desc.offset.iter().map(|&i| self.channel_names[i].clone()).collect(); - let mut new_image = Image::new(self.format, self.resolution, new_channels, self.encoding); + let new_channels: Vec = desc + .offset + .iter() + .map(|&i| self.channel_names[i].clone()) + .collect(); + let mut new_image = Image::new( + self.format, + self.resolution, + new_channels, + self.encoding.clone(), + ); let n_new_channels = new_image.n_channels(); for y in 0..self.resolution.y() { for x in 0..self.resolution.x() { @@ -461,14 +554,20 @@ impl Image { let new_offset = new_image.pixel_offset(p); match (&self.pixels, &mut new_image.pixels) { (PixelData::U8(d_old), PixelData::U8(d_new)) => { - for c in 0..n_new_channels { d_new[new_offset + c] = d_old[old_offset + desc.offset[c]]; } - }, + for c in 0..n_new_channels { + d_new[new_offset + c] = d_old[old_offset + desc.offset[c]]; + } + } (PixelData::F16(d_old), PixelData::F16(d_new)) => { - for c in 0..n_new_channels { d_new[new_offset + c] = d_old[old_offset + desc.offset[c]]; } - }, + for c in 0..n_new_channels { + d_new[new_offset + c] = d_old[old_offset + desc.offset[c]]; + } + } (PixelData::F32(d_old), PixelData::F32(d_new)) => { - for c in 0..n_new_channels { d_new[new_offset + c] = d_old[old_offset + desc.offset[c]]; } - }, + for c in 0..n_new_channels { + d_new[new_offset + c] = d_old[old_offset + desc.offset[c]]; + } + } _ => unreachable!(), } } @@ -478,40 +577,56 @@ impl Image { pub fn crop(&self, bounds: Bounds2i) -> Image { assert!(bounds.p_min.x() >= 0 && bounds.p_min.y() >= 0); - let new_res = Point2i::new(bounds.p_max.x() - bounds.p_min.x(), bounds.p_max.y() - bounds.p_min.y()); - let mut new_image = Image::new(self.format, new_res, self.channel_names.clone(), self.encoding); + let new_res = Point2i::new( + bounds.p_max.x() - bounds.p_min.x(), + bounds.p_max.y() - bounds.p_min.y(), + ); + let mut new_image = Image::new( + self.format, + new_res, + self.channel_names.clone(), + self.encoding.clone(), + ); for y in bounds.p_min.y()..bounds.p_max.y() { for x in bounds.p_min.x()..bounds.p_max.x() { let p_src = Point2i::new(x, y); let p_dst = Point2i::new(x - bounds.p_min.x(), y - bounds.p_min.y()); for c in 0..self.n_channels() { - new_image.set_channel(p_dst, c, self.get_channel(p_src, c, WrapMode::Clamp.into())); + new_image.set_channel( + p_dst, + c, + self.get_channel(p_src, c, WrapMode::Clamp.into()), + ); } } } new_image } - + pub fn has_any_nan_pixels(&self) -> bool { if self.format.is_8bit() { return false; } - (0..self.resolution.y()()) // Create a range for the y-axis - .into_par_iter() // Convert the range into a parallel iterator - .any(|y| { // Check in parallel if any row contains a NaN - (0..self.resolution.x()()) // For each row, create a sequential iterator for the x-axis - .any(|x| { - (0..self.n_channels()) // For each pixel, check each channel - .any(|c| self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()).is_nan()) - }) + (0..self.resolution.y()).into_par_iter().any(|y| { + (0..self.resolution.x()).any(|x| { + (0..self.n_channels()).any(|c| { + self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()) + .is_nan() + }) }) + }) } pub fn has_any_infinite_pixels(&self) -> bool { - if self.format.is_8bit() { return false; } + if self.format.is_8bit() { + return false; + } (0..self.resolution.y()).into_par_iter().any(|y| { (0..self.resolution.x()).any(|x| { - (0..self.n_channels()).any(|c| self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()).is_infinite()) + (0..self.n_channels()).any(|c| { + self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()) + .is_infinite() + }) }) }) } @@ -522,7 +637,11 @@ impl Image { for x in 0..self.resolution.x() { for c in 0..self.n_channels() { let top = self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()); - let bottom = self.get_channel(Point2i::new(x, self.resolution.y()() - 1 - y), c, WrapMode::Clamp.into()); + let bottom = self.get_channel( + Point2i::new(x, self.resolution.y() - 1 - y), + c, + WrapMode::Clamp.into(), + ); self.set_channel(Point2i::new(x, y), c, bottom); self.set_channel(Point2i::new(x, self.resolution.y() - 1 - y), c, top); } @@ -530,11 +649,20 @@ impl Image { } } - pub fn convert_to_format(&self, new_format: PixelFormat, new_encoding: &'static dyn ColorEncoding) -> Result { - if self.format == new_format && self.encoding == new_encoding { + pub fn convert_to_format( + &self, + new_format: PixelFormat, + new_encoding: Arc, + ) -> Result { + if self.format == new_format && self.encoding.type_id() == new_encoding.type_id() { return Ok(self.clone()); } - let mut new_image = Image::new(new_format, self.resolution, self.channel_names.clone(), new_encoding); + let mut new_image = Image::new( + new_format, + self.resolution, + self.channel_names.clone(), + new_encoding, + ); for y in 0..self.resolution.y() { for x in 0..self.resolution.x() { let p = Point2i::new(x, y); @@ -546,483 +674,949 @@ impl Image { } Ok(new_image) } - - pub fn gaussian_filter(&self, desc: &ImageChannelDesc, half_width: i32, sigma: Float) -> Image { - let mut wts: Vec = (0..=half_width * 2).map(|d| gaussian((d - half_width) as Float, 0.0, sigma)).collect(); - let wt_sum: Float = wts.iter().sum(); - wts.iter_mut().for_each(|w| *w /= wt_sum); - let mut blurx = Image::new(PixelFormat::F32, self.resolution, self.channel_names().to_vec(), self.encoding); - let nc = desc.len(); - (0..self.resolution.y()).into_par_iter().for_each(|y| { - for x in 0..self.resolution.x() { - let mut result = ImageChannelValues::new(desc.len(), 0.0); - for r in -half_width..=half_width { - let cv = self.get_channels(Point2i::new(x + r, y), desc, WrapMode::Clamp.into()); - let w = wts[(r + half_width) as usize]; - for c in 0..nc { - result[c] += w * cv[c]; - } - } - blurx.set_channels(Point2i::new(x, y), desc, &result); - } - }); - let mut blury = Image::new(PixelFormat::F32, self.resolution, self.channel_names().to_vec(), self.encoding); - (0..self.resolution.y()).into_par_iter().for_each(|y| { - for x in 0..self.resolution.x() { - let mut result = ImageChannelValues::new(desc.len(), 0.0); - for r in -half_width..=half_width { - let cv = blurx.get_channels(Point2i::new(x, y + r), desc, WrapMode::Clamp.into()); - let w = wts[(r + half_width) as usize]; - for c in 0..nc { - result[c] += w * cv[c]; - } - } - blury.set_channels(Point2i::new(x, y), desc, &result); - } - }); - blury - } - - pub fn joint_bilateral_filter(&self, to_filter_desc: &ImageChannelDesc, half_width: i32, xy_sigma: [Float; 2], joint_desc: &ImageChannelDesc, joint_sigma: &ImageChannelValues) -> Image { - assert_eq!(joint_desc.len(), joint_sigma.len()); - let result = &mut Image::new(PixelFormat::F32, self.resolution, self.channel_names().to_vec(), self.encoding); - let fx: Vec = (0..=half_width).map(|i| gaussian(i as Float, 0.0, xy_sigma[0])).collect(); - let fy: Vec = (0..=half_width).map(|i| gaussian(i as Float, 0.0, xy_sigma[1])).collect(); - (0..self.resolution.y()).into_par_iter().for_each(|y| { - for x in 0..self.resolution.x() { - let joint_pixel = self.get_channels(Point2i::new(x, y), joint_desc, WrapMode::Clamp.into()); - let mut filtered_sum = ImageChannelValues::new(to_filter_desc.len(), 0.0); - let mut weight_sum = 0.0; - for dy in -half_width..=half_width { - if y + dy < 0 || y + dy >= self.resolution.y() { continue; } - for dx in -half_width..=half_width { - if x + dx < 0 || x + dx >= self.resolution.x() { continue; } - let joint_other = self.get_channels(Point2i::new(x + dx, y + dy), joint_desc, WrapMode::Clamp.into()); - let mut weight = fx[dx.abs() as usize] * fy[dy.abs() as usize]; - for c in 0..joint_desc.len() { - weight *= gaussian(joint_pixel[c], joint_other[c], joint_sigma[c]); - } - weight_sum += weight; - let filter_channels = self.get_channels(Point2i::new(x + dx, y + dy), to_filter_desc, WrapMode::Clamp.into()); - for c in 0..to_filter_desc.len() { - filtered_sum[c] += weight * filter_channels[c]; - } - } - } - if weight_sum > 0.0 { - for c in 0..to_filter_desc.len() { - filtered_sum[c] /= weight_sum; - } - } - result.set_channels(Point2i::new(x, y), to_filter_desc, &filtered_sum); - } - }); - result.clone() - } - - pub fn mae(&self, desc: &ImageChannelDesc, ref_img: &Image, mut error_image: Option<&mut Image>) -> ImageChannelValues { - let mut sum_error = vec![0.0; desc.len()]; - let ref_desc = ref_img.get_channel_desc(&self.channel_names()).unwrap(); - assert_eq!(self.resolution, ref_img.resolution); - if let Some(err_img) = &mut error_image { - *err_img = Image::new(PixelFormat::F32, self.resolution, self.channel_names.clone(), self.encoding); - } - for y in 0..self.resolution.y() { - for x in 0..self.resolution.x() { - let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); - let vref = ref_img.get_channels(Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into()); - for c in 0..desc.len() { - let err = (v[c] - vref[c]).abs(); - if err.is_infinite() { continue; } - sum_error[c] += err; - if let Some(err_img) = &mut error_image { - err_img.set_channel(Point2i::new(x, y), c, err); - } - } - } - } - let mut mae = ImageChannelValues::new(desc.len(), 0.0); - let n_pixels = self.resolution.x()() as Float * self.resolution.y()() as Float; - for c in 0..desc.len() { - mae[c] = sum_error[c] / n_pixels; - } - mae - } - - pub fn mse(&self, desc: &ImageChannelDesc, ref_img: &Image, mut mse_image: Option<&mut Image>) -> ImageChannelValues { - let mut sum_se = vec![0.0; desc.len()]; - let ref_desc = ref_img.get_channel_desc(&self.channel_names(desc)).unwrap(); - assert_eq!(self.resolution, ref_img.resolution); - if let Some(mse_img) = &mut mse_image { - *mse_img = Image::new(PixelFormat::F32, self.resolution, self.channel_names.clone(), self.encoding); - } - for y in 0..self.resolution.y() { - for x in 0..self.resolution.x() { - let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); - let vref = ref_img.get_channels(Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into()); - for c in 0..desc.len() { - let se = (v[c] - vref[c]).powi(2); - if se.is_infinite() { continue; } - sum_se[c] += se; - if let Some(mse_img) = &mut mse_image { - mse_img.set_channel(Point2i::new(x, y), c, se); - } - } - } - } - let mut mse = ImageChannelValues::new(desc.len(), 0.0); - let n_pixels = self.resolution.x() as Float * self.resolution.y() as Float; - for c in 0..desc.len() { - mse[c] = sum_se[c] / n_pixels; - } - mse - } - - pub fn mrse(&self, desc: &ImageChannelDesc, ref_img: &Image, mut mrse_image: Option<&mut Image>) -> ImageChannelValues { - let mut sum_rse = vec![0.0; desc.len()]; - let ref_desc = ref_img.get_channel_desc(&self.channel_names()).unwrap(); - assert_eq!(self.resolution, ref_img.resolution); - if let Some(mrse_img) = &mut mrse_image { - *mrse_img = Image::new(PixelFormat::F32, self.resolution, self.channel_names.clone(), self.encoding); - } - for y in 0..self.resolution.y() { - for x in 0..self.resolution.x() { - let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); - let vref = ref_img.get_channels(Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into()); - for c in 0..desc.len() { - let rse = ((v[c] - vref[c]).powi(2)) / (vref[c] + 0.01).powi(2); - if rse.is_infinite() { continue; } - sum_rse[c] += rse; - if let Some(mrse_img) = &mut mrse_image { - mrse_img.set_channel(Point2i::new(x, y), c, rse); - } - } - } - } - let mut mrse = ImageChannelValues::new(desc.len(), 0.0); - let n_pixels = self.resolution.x() as Float * self.resolution.y() as Float; - for c in 0..desc.len() { - mrse[c] = sum_rse[c] / n_pixels; - } - mrse - } - - pub fn average(&self, desc: &ImageChannelDesc) -> ImageChannelValues { - let mut sum = vec![0.0; desc.len()]; - for y in 0..self.resolution.y() { - for x in 0..self.resolution.x() { - let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); - for c in 0..desc.len() { - sum[c] += v[c]; - } - } - } - let mut avg = ImageChannelValues::new(desc.len(), 0.0); - let n_pixels = self.resolution.x() as Float * self.resolution.y() as Float; - for c in 0..desc.len() { - avg[c] = sum[c] / n_pixels; - } - avg - } - - pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap_mode: WrapMode2D) { - assert_eq!(buf.len(), (extent.p_max.x() - extent.p_min.x()) as usize * (extent.p_max.y() - extent.p_min.y()) as usize * self.n_channels()); - let mut buf_idx = 0; - for y in extent.p_min.y()..extent.p_max.y() { - for x in extent.p_min.x()..extent.p_max.x() { - for c in 0..self.n_channels() { - buf[buf_idx] = self.get_channel(Point2i::new(x, y), c, wrap_mode); - buf_idx += 1; - } - } - } - } - - pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) { - assert_eq!(buf.len(), (extent.p_max.x() - extent.p_min.x()) as usize * (extent.p_max.y() - extent.p_min.y()) as usize * self.n_channels()); - let mut buf_idx = 0; - for y in extent.p_min.y()..extent.p_max.y() { - for x in extent.p_min.x()..extent.p_max.x() { - for c in 0..self.n_channels() { - self.set_channel(Point2i::new(x, y), c, buf[buf_idx]); - buf_idx += 1; - } - } - } - } - - // --- Resampling and Pyramid Generation --- - - fn resample_weights(old_res: i32, new_res: i32) -> Vec { - assert!(new_res >= old_res); - let mut wt = vec![ResampleWeight { first_pixel: 0, weight: [0.0; 4] }; new_res as usize]; - let filter_radius = 2.0; - let tau = 2.0; - for i in 0..new_res { - let center = (i as Float + 0.5) * old_res as Float / new_res as Float; - wt[i as usize].first_pixel = ((center - filter_radius) + 0.5).floor() as i32; - let mut sum_wts = 0.0; - for j in 0..4 { - let pos = wt[i as usize].first_pixel as Float + j as Float + 0.5; - let w = windowed_sinc(pos - center, filter_radius, tau); - wt[i as usize].weight[j] = w; - sum_wts += w; - } - if sum_wts > 0.0 { - for j in 0..4 { - wt[i as usize].weight[j] /= sum_wts; - } - } - } - wt - } - - pub fn float_resize_up(&self, new_res: Point2i, wrap_mode: WrapMode2D) -> Image { - assert!(self.format.is_32bit()); - let mut temp_image = Image::new(PixelFormat::F32, Point2i::new(new_res.x(), self.resolution.y()), self.channel_names.clone(), self.encoding); - let x_weights = Self::resample_weights(self.resolution.x(), new_res.x()); - (0..self.resolution.y()).into_par_iter().for_each(|y| { - for x_out in 0..new_res.x() { - let weights = &x_weights[x_out as usize]; - for c in 0..self.n_channels() { - let mut val = 0.0; - for j in 0..4 { - let px = (weights.first_pixel + j as i32).clamp(0, self.resolution.x() - 1); - val += weights.weight[j] * self.get_channel(Point2i::new(px, y), c, wrap_mode); - } - temp_image.set_channel(Point2i::new(x_out, y), c, val.max(0.0)); - } - } - }); - let mut new_image = Image::new(PixelFormat::F32, new_res, self.channel_names.clone(), self.encoding); - let y_weights = Self::resample_weights(self.resolution.y(), new_res.y()); - (0..new_res.x()).into_par_iter().for_each(|x| { - for y_out in 0..new_res.y() { - let weights = &y_weights[y_out as usize]; - for c in 0..self.n_channels() { - let mut val = 0.0; - for j in 0..4 { - let py = (weights.first_pixel + j as i32).clamp(0, self.resolution.y() - 1); - val += weights.weight[j] * temp_image.get_channel(Point2i::new(x, py), c, wrap_mode); - } - new_image.set_channel(Point2i::new(x, y_out), c, val.max(0.0)); - } - } - }); - new_image - } - - pub fn generate_pyramid(image: Image, wrap_mode: WrapMode2D) -> Vec { - let mut img = image; - if !img.resolution.x() % 2 == 0 || !img.resolution.y() % 2 == 0 { - let new_res = Point2i::new(round_up_pow2(img.resolution.x()), round_up_pow2(img.resolution.y())); - img = img.float_resize_up(new_res, wrap_mode); - } - if !img.format.is_32bit() { - img = img.convert_to_format(PixelFormat::F32, color::LINEAR_SRGB).unwrap(); - } - let mut pyramid = Vec::new(); - let n_levels = 1 + img.resolution.x().max(img.resolution.y()).ilog2() as usize; - pyramid.reserve(n_levels); - pyramid.push(img.clone()); - for _ in 1..n_levels { - let res = img.resolution(); - let next_res = Point2i::new((res.x() / 2).max(1), (res.y() / 2).max(1)); - let mut next_image = Image::new(PixelFormat::F32, next_res, img.channel_names.clone(), img.encoding); - (0..next_res.y()).into_par_iter().for_each(|y| { - for x in 0..next_res.x() { - for c in 0..img.n_channels() { - let val = ( img.get_channel(Point2i::new(2 * x, 2 * y), c, wrap_mode) + - img.get_channel(Point2i::new(2 * x + 1, 2 * y), c, wrap_mode) + - img.get_channel(Point2i::new(2 * x, 2 * y + 1), c, wrap_mode) + - img.get_channel(Point2i::new(2 * x + 1, 2 * y + 1), c, wrap_mode) ) * 0.25; - next_image.set_channel(Point2i::new(x, y), c, val); - } - } - }); - pyramid.push(next_image.clone()); - img = next_image; - } - pyramid - } - - pub fn read(filename: &str, encoding: Option<&'static dyn ColorEncoding>) -> Result { - let path = Path::new(filename); - let ext = path.extension().and_then(|s| s.to_str()).unwrap_or(""); - match ext.to_lowercase().as_str() { - "exr" => Self::read_exr(filename), - "png" => Self::read_png(filename, encoding.unwrap_or(color::SRGB)), - "pfm" => Self::read_pfm(filename), - "hdr" => Self::read_hdr(filename), - "qoi" => Self::read_qoi(filename), - _ => { - // Use image crate for other formats - let img = image::open(filename).map_err(|e| ImageError::Io(e.into()))?; - Self::from_dynamic_image(img) - } - } - } - - fn read_exr(filename: &str) -> Result { - // Use exr crate to read - let image = exr_image::read::read().no_deep_data().largest_resolution_level().all_channels().all_layers().from_file(filename).map_err(|e| ImageError::ExrError(e.to_string()))?; - // Assume single layer, RGBA or RGB - if let exr_image::Image { layers: vec![layer], .. } = image { - let res = layer.channel_data.resolution; - let resolution = Point2i { x: res.0 as i32, y: res.1 as i32 }; - let mut channel_names = Vec::new(); - let mut pixels_f32 = Vec::new(); - for channel in &layer.channel_data.list { - channel_names.push(channel.name.to_string()); - if let exr_image::Channel { sample_data: exr_image::Samples::F32(data), .. } = channel { - pixels_f32.extend_from_slice(data); - } else { - return Err(ImageError::PixelFormatError("Unsupported EXR pixel type".to_string())); - } - } - let image = Image::new(PixelFormat::F32, resolution, channel_names, color::LINEAR_SRGB); - if let PixelData::F32(ref mut pd) = image.pixels { - *pd = pixels_f32; - } - let mut metadata = ImageMetadata::new(); - if let Some(cs) = layer.attributes.chromaticities { - // Set colorspace from chromaticities - metadata.colorspace = Some(RGBColorspace::from_chromaticities(cs.red.into(), cs.green.into(), cs.blue.into(), cs.white.into())); - } - // Add other metadata from header - Ok(ImageAndMetadata { image, metadata }) - } else { - Err(ImageError::ExrError("Multi-layer EXR not supported".to_string())) - } - } - - fn read_png(filename: &str, encoding: &'static dyn ColorEncoding) -> Result { - let img = image::open(filename).map_err(|e| ImageError::Io(e.into()))?; - Self::from_dynamic_image(img) - } - - fn read_pfm(filename: &str) -> Result { - // Custom PFM read - let file = File::open(filename).map_err(ImageError::Io)?; - let reader = BufReader::new(file); - let mut lines = reader.lines(); - let header = lines.next().ok_or(ImageError::Io(io::Error::new(io::ErrorKind::InvalidData, "No header")))? .map_err(ImageError::Io)?; - let n_channels = if header == "PF" { 3 } else if header == "Pf" { 1 } else { return Err(ImageError::PixelFormatError("Invalid PFM header".to_string())); }; - let dims = lines.next().ok_or(ImageError::Io(io::Error::new(io::ErrorKind::InvalidData, "No dims")))? .map_err(ImageError::Io)?; - let mut dims_iter = dims.split_whitespace(); - let width = dims_iter.next().unwrap().parse::().unwrap(); - let height = dims_iter.next().unwrap().parse::().unwrap(); - let scale = lines.next().ok_or(ImageError::Io(io::Error::new(io::ErrorKind::InvalidData, "No scale")))? .map_err(ImageError::Io)?.parse::().unwrap(); - // Read binary data - let data = read(filename).map_err(ImageError::Io)?; - let header_len = header.len() + dims.len() + scale.to_string().len() + 3; // + newlines - let mut pixels = vec![0.0; (width * height * n_channels) as usize]; - let byte_data = &data[header_len..]; - let mut byte_iter = byte_data.chunks(4); - for p in pixels.iter_mut() { - let bytes = byte_iter.next().unwrap(); - *p = f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) * scale.abs(); - } - let channel_names = if n_channels == 1 { vec!["Y".to_string()] } else { vec!["R".to_string(), "G".to_string(), "B".to_string()] }; - let image = Image::new(PixelFormat::F32, Point2i { x: width, y: height }, channel_names, color::LINEAR_SRGB); - if let PixelData::F32(ref mut pd) = image.pixels { - *pd = pixels; - } - Ok(ImageAndMetadata { image, metadata: ImageMetadata::new() }) - } - - fn read_hdr(filename: &str) -> Result { - let img = image::open(filename).map_err(|e| ImageError::Io(e.into()))?; - Self::from_dynamic_image(img) - } - - fn read_qoi(filename: &str) -> Result { - // Assume qoi crate added; pseudo - let data = read(filename).map_err(ImageError::Io)?; - let (header, pixels) = qoi::decode(&data).map_err(|e| ImageError::QoiError(e.to_string()))?; - let resolution = Point2i::new(header.width as i32, header.height as i32); - let channel_names = if header.channels == 3 { vec!["R".to_string(), "G".to_string(), "B".to_string()] } else { vec!["R".to_string(), "G".to_string(), "B".to_string(), "A".to_string()] }; - let encoding = if header.colorspace == qoi::ColorSpace::Srgb { color::SRGB } else { color::LINEAR_SRGB }; - let image = Image::new(PixelFormat::U8, resolution, channel_names, encoding); - if let PixelData::U8(ref mut pd) = image.pixels { - *pd = pixels; - } - Ok(ImageAndMetadata { image, metadata: ImageMetadata::new() }) - } - - fn from_dynamic_image(img: DynamicImage) -> Result { - let resolution = Point2i::new(img.width() as i32, img.height() as i32); - let (pixels, channel_names) = match img { - DynamicImage::ImageLuma8(buf) => (PixelData::U8(buf.into_vec()), vec!["Y".to_string()] ), - DynamicImage::ImageRgb8(buf) => (PixelData::U8(buf.into_vec()), vec!["R".to_string(), "G".to_string(), "B".to_string()] ), - DynamicImage::ImageRgba8(buf) => (PixelData::U8(buf.into_vec()), vec!["R".to_string(), "G".to_string(), "B".to_string(), "A".to_string()] ), - DynamicImage::ImageLuma16(buf) => (PixelData::F16(buf.into_vec().into_iter().map(|u| f16::from_f32(u as f32 / 65535.0)).collect()), vec!["Y".to_string()] ), - DynamicImage::ImageRgb16(buf) => (PixelData::F16(buf.into_vec().into_iter().map(|u| f16::from_f32(u as f32 / 65535.0)).collect()), vec!["R".to_string(), "G".to_string(), "B".to_string()] ), - DynamicImage::ImageRgba16(buf) => (PixelData::F16(buf.into_vec().into_iter().map(|u| f16::from_f32(u as f32 / 65535.0)).collect()), vec!["R".to_string(), "G".to_string(), "B".to_string(), "A".to_string()] ), - DynamicImage::ImageRgb32F(buf) => (PixelData::F32(buf.into_vec()), vec!["R".to_string(), "G".to_string(), "B".to_string()] ), - _ => return Err(ImageError::PixelFormatError("Unsupported image format".to_string())), - }; - let format = match &pixels { - PixelData::U8(_) => PixelFormat::U8, - PixelData::F16(_) => PixelFormat::F16, - PixelData::F32(_) => PixelFormat::F32, - }; - let image = Image { resolution, channel_names, format, pixels, encoding: color::SRGB }; - Ok(ImageAndMetadata { image, metadata: ImageMetadata::new() }) - } - + // + // fn filter_pass( + // &self, + // output_image: &mut Image, + // desc: &ImageChannelDesc, + // weights: &[Float], + // half_width: i32, + // is_vertical: bool, + // ) { + // let nc = desc.len(); + // let resolution = self.resolution; + // + // // Process rows in parallel, calculate the filtered values, and return each row's data. + // let processed_rows: Vec> = (0..resolution.y()) + // .into_par_iter() + // .map(|y| { + // let mut row_data = vec![0.0; resolution.x() as usize * nc]; + // for x in 0..resolution.x() { + // let mut filtered_pixel = ImageChannelValues::new(nc, 0.0); + // + // // Convolve with the kernel + // for r in -half_width..=half_width { + // // The only difference between passes is the coordinate we offset. + // let sample_point = if is_vertical { + // Point2i::new(x, y + r) + // } else { + // Point2i::new(x + r, y) + // }; + // + // let neighbor_pixels = + // self.get_channels(sample_point, desc, WrapMode::Clamp.into()); + // let weight = weights[(r + half_width) as usize]; + // + // for c in 0..nc { + // filtered_pixel[c] += weight * neighbor_pixels[c]; + // } + // } + // + // // Store the final pixel value for this row. + // for c in 0..nc { + // row_data[x as usize * nc + c] = filtered_pixel[c]; + // } + // } + // row_data + // }) + // .collect(); + // + // // Now, write the collected results to the output image sequentially. + // for (y, row) in processed_rows.into_iter().enumerate() { + // for x in 0..resolution.x() as usize { + // let pixel_slice = &row[x * nc..(x + 1) * nc]; + // let values = ImageChannelValues::from(pixel_slice); + // output_image.set_channels(Point2i::new(x as i32, y as i32), desc, &values); + // } + // } + // } + // + // pub fn gaussian_filter(&self, desc: &ImageChannelDesc, half_width: i32, sigma: Float) -> Image { + // let mut weights: Vec = (0..=half_width * 2) + // .map(|d| gaussian((d - half_width) as Float, 0.0, sigma)) + // .collect(); + // let weights_sum: Float = weights.iter().sum(); + // weights.iter_mut().for_each(|w| *w /= weights_sum); + // + // let mut blurx = Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names().to_vec(), + // self.encoding, + // ); + // self.filter_pass(&mut blurx, desc, &weights, half_width, false); + // let mut blury = Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names().to_vec(), + // self.encoding, + // ); + // self.filter_pass(&mut blury, desc, &weights, half_width, false); + // blury + // } + // + // pub fn joint_bilateral_filter( + // &self, + // to_filter_desc: &ImageChannelDesc, + // half_width: i32, + // xy_sigma: [Float; 2], + // joint_desc: &ImageChannelDesc, + // joint_sigma: &ImageChannelValues, + // ) -> Image { + // assert_eq!(joint_desc.len(), joint_sigma.len()); + // let result = &mut Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names().to_vec(), + // self.encoding, + // ); + // let fx: Vec = (0..=half_width) + // .map(|i| gaussian(i as Float, 0.0, xy_sigma[0])) + // .collect(); + // let fy: Vec = (0..=half_width) + // .map(|i| gaussian(i as Float, 0.0, xy_sigma[1])) + // .collect(); + // + // let nc = to_filter_desc.len(); + // let filter_rows: Vec> = (0..self.resolution.y()) + // .into_par_iter() + // .map(|y| { + // let mut row = vec![0.0; self.resolution.x() as usize * nc]; + // + // for x in 0..self.resolution.x() { + // let joint_pixel = + // self.get_channels(Point2i::new(x, y), joint_desc, WrapMode::Clamp.into()); + // let mut filtered_sum = ImageChannelValues::new(to_filter_desc.len(), 0.0); + // let mut weight_sum = 0.0; + // + // for dy in -half_width..=half_width { + // for dx in -half_width..=half_width { + // let neigh = Point2i::new(x + dx, y + dy); + // if neigh.x() < 0 + // || neigh.x() >= self.resolution.x() + // || neigh.y() < 0 + // || neigh.y() >= self.resolution.y() + // { + // continue; + // } + // + // let mut weight = fx[dx.abs() as usize] * fy[dy.abs() as usize]; + // let joint_other = self.get_channels( + // Point2i::new(x + dx, y + dy), + // joint_desc, + // WrapMode::Clamp.into(), + // ); + // for c in 0..joint_desc.len() { + // weight *= gaussian(joint_pixel[c], joint_other[c], joint_sigma[c]); + // } + // weight_sum += weight; + // + // let filter_channels = self.get_channels( + // Point2i::new(x + dx, y + dy), + // to_filter_desc, + // WrapMode::Clamp.into(), + // ); + // for c in 0..to_filter_desc.len() { + // filtered_sum[c] += weight * filter_channels[c]; + // } + // } + // } + // if weight_sum > 0.0 { + // for c in 0..to_filter_desc.len() { + // row[x as usize * nc + c] = filtered_sum[c] /= weight_sum; + // } + // } + // } + // row + // }) + // .collect(); + // + // let mut result_image = Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names().to_vec(), + // self.encoding, + // ); + // for (y, row_data) in filter_rows.into_iter().enumerate() { + // for x in 0..self.resolution.x() as usize { + // let start = x * nc; + // let end = start + nc; + // + // let values = ImageChannelValues::from(&row_data[start..end]); + // result_image.set_channels( + // Point2i::new(x as i32, y as i32), + // to_filter_desc, + // &values, + // ); + // } + // } + // result_image + // } + // + // pub fn mae( + // &self, + // desc: &ImageChannelDesc, + // ref_img: &Image, + // mut error_image: Option<&mut Image>, + // ) -> ImageChannelValues { + // let mut sum_error = vec![0.0; desc.len()]; + // let ref_desc = ref_img.get_channel_desc(&self.channel_names()).unwrap(); + // assert_eq!(self.resolution, ref_img.resolution); + // if let Some(err_img) = &mut error_image { + // *err_img = Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names.clone(), + // self.encoding, + // ); + // } + // for y in 0..self.resolution.y() { + // for x in 0..self.resolution.x() { + // let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); + // let vref = + // ref_img.get_channels(Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into()); + // for c in 0..desc.len() { + // let err = (v[c] - vref[c]).abs(); + // if err.is_infinite() { + // continue; + // } + // sum_error[c] += err; + // if let Some(err_img) = &mut error_image { + // err_img.set_channel(Point2i::new(x, y), c, err); + // } + // } + // } + // } + // let mut mae = ImageChannelValues::new(desc.len(), 0.0); + // let n_pixels = self.resolution.x()() as Float * self.resolution.y()() as Float; + // for c in 0..desc.len() { + // mae[c] = sum_error[c] / n_pixels; + // } + // mae + // } + // + // pub fn mse( + // &self, + // desc: &ImageChannelDesc, + // ref_img: &Image, + // mut mse_image: Option<&mut Image>, + // ) -> ImageChannelValues { + // let mut sum_se = vec![0.0; desc.len()]; + // let ref_desc = ref_img.get_channel_desc(&self.channel_names()).unwrap(); + // assert_eq!(self.resolution, ref_img.resolution); + // if let Some(mse_img) = &mut mse_image { + // **mse_img = Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names.clone(), + // self.encoding, + // ); + // } + // for y in 0..self.resolution.y() { + // for x in 0..self.resolution.x() { + // let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); + // let vref = + // ref_img.get_channels(Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into()); + // for c in 0..desc.len() { + // let se = (v[c] - vref[c]).powi(2); + // if se.is_infinite() { + // continue; + // } + // sum_se[c] += se; + // if let Some(mse_img) = &mut mse_image { + // mse_img.set_channel(Point2i::new(x, y), c, se); + // } + // } + // } + // } + // let mut mse = ImageChannelValues::new(desc.len(), 0.0); + // let n_pixels = self.resolution.x() as Float * self.resolution.y() as Float; + // for c in 0..desc.len() { + // mse[c] = sum_se[c] / n_pixels; + // } + // mse + // } + // + // pub fn mrse( + // &self, + // desc: &ImageChannelDesc, + // ref_img: &Image, + // mut mrse_image: Option<&mut Image>, + // ) -> ImageChannelValues { + // let mut sum_rse = vec![0.0; desc.len()]; + // let ref_desc = ref_img.get_channel_desc(&self.channel_names()).unwrap(); + // assert_eq!(self.resolution, ref_img.resolution); + // if let Some(mrse_img) = &mut mrse_image { + // **mrse_img = Image::new( + // PixelFormat::F32, + // self.resolution, + // self.channel_names.clone(), + // self.encoding, + // ); + // } + // for y in 0..self.resolution.y() { + // for x in 0..self.resolution.x() { + // let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); + // let vref = + // ref_img.get_channels(Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into()); + // for c in 0..desc.len() { + // let rse = ((v[c] - vref[c]).powi(2)) / (vref[c] + 0.01).powi(2); + // if rse.is_infinite() { + // continue; + // } + // sum_rse[c] += rse; + // if let Some(mrse_img) = &mut mrse_image { + // mrse_img.set_channel(Point2i::new(x, y), c, rse); + // } + // } + // } + // } + // let mut mrse = ImageChannelValues::new(desc.len(), 0.0); + // let n_pixels = self.resolution.x() as Float * self.resolution.y() as Float; + // for c in 0..desc.len() { + // mrse[c] = sum_rse[c] / n_pixels; + // } + // mrse + // } + // + // pub fn average(&self, desc: &ImageChannelDesc) -> ImageChannelValues { + // let mut sum = vec![0.0; desc.len()]; + // for y in 0..self.resolution.y() { + // for x in 0..self.resolution.x() { + // let v = self.get_channels(Point2i::new(x, y), desc, WrapMode::Clamp.into()); + // for c in 0..desc.len() { + // sum[c] += v[c]; + // } + // } + // } + // let mut avg = ImageChannelValues::new(desc.len(), 0.0); + // let n_pixels = self.resolution.x() as Float * self.resolution.y() as Float; + // for c in 0..desc.len() { + // avg[c] = sum[c] / n_pixels; + // } + // avg + // } + // + // pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap_mode: WrapMode2D) { + // assert_eq!( + // buf.len(), + // (extent.p_max.x() - extent.p_min.x()) as usize + // * (extent.p_max.y() - extent.p_min.y()) as usize + // * self.n_channels() + // ); + // let mut buf_idx = 0; + // for y in extent.p_min.y()..extent.p_max.y() { + // for x in extent.p_min.x()..extent.p_max.x() { + // for c in 0..self.n_channels() { + // buf[buf_idx] = self.get_channel(Point2i::new(x, y), c, wrap_mode); + // buf_idx += 1; + // } + // } + // } + // } + // + // pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) { + // assert_eq!( + // buf.len(), + // (extent.p_max.x() - extent.p_min.x()) as usize + // * (extent.p_max.y() - extent.p_min.y()) as usize + // * self.n_channels() + // ); + // let mut buf_idx = 0; + // for y in extent.p_min.y()..extent.p_max.y() { + // for x in extent.p_min.x()..extent.p_max.x() { + // for c in 0..self.n_channels() { + // self.set_channel(Point2i::new(x, y), c, buf[buf_idx]); + // buf_idx += 1; + // } + // } + // } + // } + // + // // --- Resampling and Pyramid Generation --- + // + // fn resample_weights(old_res: i32, new_res: i32) -> Vec { + // assert!(new_res >= old_res); + // let mut wt = vec![ + // ResampleWeight { + // first_pixel: 0, + // weight: [0.0; 4] + // }; + // new_res as usize + // ]; + // let filter_radius = 2.0; + // let tau = 2.0; + // for i in 0..new_res { + // let center = (i as Float + 0.5) * old_res as Float / new_res as Float; + // wt[i as usize].first_pixel = ((center - filter_radius) + 0.5).floor() as i32; + // let mut sum_wts = 0.0; + // for j in 0..4 { + // let pos = wt[i as usize].first_pixel as Float + j as Float + 0.5; + // let w = windowed_sinc(pos - center, filter_radius, tau); + // wt[i as usize].weight[j] = w; + // sum_wts += w; + // } + // if sum_wts > 0.0 { + // for j in 0..4 { + // wt[i as usize].weight[j] /= sum_wts; + // } + // } + // } + // wt + // } + // + // pub fn float_resize_up(&self, new_res: Point2i, wrap_mode: WrapMode2D) -> Image { + // assert!(self.format.is_32bit()); + // let mut temp_image = Image::new( + // PixelFormat::F32, + // Point2i::new(new_res.x(), self.resolution.y()), + // self.channel_names.clone(), + // self.encoding, + // ); + // let x_weights = Self::resample_weights(self.resolution.x(), new_res.x()); + // (0..self.resolution.y()).into_par_iter().for_each(|y| { + // for x_out in 0..new_res.x() { + // let weights = &x_weights[x_out as usize]; + // for c in 0..self.n_channels() { + // let mut val = 0.0; + // for j in 0..4 { + // let px = (weights.first_pixel + j as i32).clamp(0, self.resolution.x() - 1); + // val += + // weights.weight[j] * self.get_channel(Point2i::new(px, y), c, wrap_mode); + // } + // temp_image.set_channel(Point2i::new(x_out, y), c, val.max(0.0)); + // } + // } + // }); + // let mut new_image = Image::new( + // PixelFormat::F32, + // new_res, + // self.channel_names.clone(), + // self.encoding, + // ); + // let y_weights = Self::resample_weights(self.resolution.y(), new_res.y()); + // let new_image_rows = [](0..new_res.x()).into_par_iter().for_each(|x| { + // for y_out in 0..new_res.y() { + // let weights = &y_weights[y_out as usize]; + // for c in 0..self.n_channels() { + // let mut val = 0.0; + // for j in 0..4 { + // let py = (weights.first_pixel + j as i32).clamp(0, self.resolution.y() - 1); + // val += weights.weight[j] + // * temp_image.get_channel(Point2i::new(x, py), c, wrap_mode); + // } + // new_image.set_channel(Point2i::new(x, y_out), c, val.max(0.0)); + // } + // } + // }); + // new_image + // } + // + // pub fn generate_pyramid(image: Image, wrap_mode: WrapMode2D) -> Vec { + // let mut img = image; + // if !img.resolution.x() % 2 == 0 || !img.resolution.y() % 2 == 0 { + // let new_res = Point2i::new( + // round_up_pow2(img.resolution.x()), + // round_up_pow2(img.resolution.y()), + // ); + // img = img.float_resize_up(new_res, wrap_mode); + // } + // if !img.format.is_32bit() { + // img = img + // .convert_to_format(PixelFormat::F32, color::LINEAR_SRGB) + // .unwrap(); + // } + // let mut pyramid = Vec::new(); + // let n_levels = 1 + img.resolution.x().max(img.resolution.y()).ilog2() as usize; + // pyramid.reserve(n_levels); + // pyramid.push(img.clone()); + // for _ in 1..n_levels { + // let res = img.resolution(); + // let next_res = Point2i::new((res.x() / 2).max(1), (res.y() / 2).max(1)); + // let mut next_image = Image::new( + // PixelFormat::F32, + // next_res, + // img.channel_names.clone(), + // img.encoding, + // ); + // (0..next_res.y()).into_par_iter().for_each(|y| { + // for x in 0..next_res.x() { + // for c in 0..img.n_channels() { + // let val = (img.get_channel(Point2i::new(2 * x, 2 * y), c, wrap_mode) + // + img.get_channel(Point2i::new(2 * x + 1, 2 * y), c, wrap_mode) + // + img.get_channel(Point2i::new(2 * x, 2 * y + 1), c, wrap_mode) + // + img.get_channel(Point2i::new(2 * x + 1, 2 * y + 1), c, wrap_mode)) + // * 0.25; + // next_image.set_channel(Point2i::new(x, y), c, val); + // } + // } + // }); + // pyramid.push(next_image.clone()); + // img = next_image; + // } + // pyramid + // } + // + // pub fn read( + // filename: &str, + // encoding: Option<&'static dyn ColorEncoding>, + // ) -> Result { + // let path = Path::new(filename); + // let ext = path.extension().and_then(|s| s.to_str()).unwrap_or(""); + // match ext.to_lowercase().as_str() { + // "exr" => Self::read_exr(filename), + // "png" => Self::read_png(filename, encoding.unwrap_or(color::SRGB)), + // "pfm" => Self::read_pfm(filename), + // "hdr" => Self::read_hdr(filename), + // "qoi" => Self::read_qoi(filename), + // _ => { + // // Use image crate for other formats + // let img = image::open(filename).map_err(|e| ImageError::Io(e.into()))?; + // Self::from_dynamic_image(img) + // } + // } + // } + // + // fn read_exr(filename: &str) -> Result { + // // Use exr crate to read + // let image: Image>> = exr::image::read::read() + // .no_deep_data() + // .largest_resolution_level() + // .rgb_channels( + // |resolution: Vec2, _headers: &[_]| vec![0.0f32; resolution.area() * 3], + // |pixels: &mut Vec, position: Vec2, (r, g, b): (f32, f32, f32)| { + // let index = (position.y() * resolution.width() + position.x()) * 3; + // pixels[index] = r; + // pixels[index + 1] = g; + // pixels[index + 2] = b; + // }, + // ) + // .first_valid_layer() + // .all_attributes() + // .from_file(filename) + // .map_err(|e: ExrError| ImageError::Exr(e.to_string()))?; + // + // // Assume single layer, RGBA or RGB + // if let exr_image::Image { + // layers: vec![layer], + // .. + // } = image + // { + // let res = layer.channel_data.resolution; + // let resolution = Point2i::new(res.0 as i32, res.1 as i32); + // let mut channel_names = Vec::new(); + // let mut pixels_f32 = Vec::new(); + // for channel in &layer.channel_data.list { + // channel_names.push(channel.name.to_string()); + // if let exr_image::Channel { + // sample_data: exr_image::Samples::F32(data), + // .. + // } = channel + // { + // pixels_f32.extend_from_slice(data); + // } else { + // return Err(ImageError::PixelFormatError( + // "Unsupported EXR pixel type".to_string(), + // )); + // } + // } + // let image = Image::new( + // PixelFormat::F32, + // resolution, + // channel_names, + // color::LINEAR_SRGB, + // ); + // if let PixelData::F32(ref mut pd) = image.pixels { + // *pd = pixels_f32; + // } + // let mut metadata = ImageMetadata::new(); + // if let Some(cs) = layer.attributes.chromaticities { + // // Set colorspace from chromaticities + // metadata.colorspace = Some(RGBColorspace::from_chromaticities( + // cs.red.into(), + // cs.green.into(), + // cs.blue.into(), + // cs.white.into(), + // )); + // } + // // Add other metadata from header + // Ok(ImageAndMetadata { image, metadata }) + // } else { + // Err(ImageError::ExrError( + // "Multi-layer EXR not supported".to_string(), + // )) + // } + // } + // + // fn read_png( + // filename: &str, + // encoding: &'static dyn ColorEncoding, + // ) -> Result { + // let img = image::open(filename).map_err(|e| ImageError::Io(e.into()))?; + // Self::from_dynamic_image(img) + // } + // + // fn read_pfm(filename: &str) -> Result { + // // Custom PFM read + // let file = File::open(filename).map_err(ImageError::Io)?; + // let reader = BufReader::new(file); + // let mut lines = reader.lines(); + // let header = lines + // .next() + // .ok_or(ImageError::Io(io::Error::new( + // io::ErrorKind::InvalidData, + // "No header", + // )))? + // .map_err(ImageError::Io)?; + // let n_channels = if header == "PF" { + // 3 + // } else if header == "Pf" { + // 1 + // } else { + // return Err(ImageError::PixelFormatError( + // "Invalid PFM header".to_string(), + // )); + // }; + // let dims = lines + // .next() + // .ok_or(ImageError::Io(io::Error::new( + // io::ErrorKind::InvalidData, + // "No dims", + // )))? + // .map_err(ImageError::Io)?; + // let mut dims_iter = dims.split_whitespace(); + // let width = dims_iter.next().unwrap().parse::().unwrap(); + // let height = dims_iter.next().unwrap().parse::().unwrap(); + // let scale = lines + // .next() + // .ok_or(ImageError::Io(io::Error::new( + // io::ErrorKind::InvalidData, + // "No scale", + // )))? + // .map_err(ImageError::Io)? + // .parse::() + // .unwrap(); + // // Read binary data + // let data = read(filename).map_err(ImageError::Io)?; + // let header_len = header.len() + dims.len() + scale.to_string().len() + 3; // + newlines + // let mut pixels = vec![0.0; (width * height * n_channels) as usize]; + // let byte_data = &data[header_len..]; + // let mut byte_iter = byte_data.chunks(4); + // for p in pixels.iter_mut() { + // let bytes = byte_iter.next().unwrap(); + // *p = f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) * scale.abs(); + // } + // let channel_names = if n_channels == 1 { + // vec!["Y".to_string()] + // } else { + // vec!["R".to_string(), "G".to_string(), "B".to_string()] + // }; + // let image = Image::new( + // PixelFormat::F32, + // Point2i { + // x: width, + // y: height, + // }, + // channel_names, + // color::LINEAR_SRGB, + // ); + // if let PixelData::F32(ref mut pd) = image.pixels { + // *pd = pixels; + // } + // Ok(ImageAndMetadata { + // image, + // metadata: ImageMetadata::new(), + // }) + // } + // + // fn read_hdr(filename: &str) -> Result { + // let img = image::open(filename).map_err(|e| ImageError::Io(e.into()))?; + // Self::from_dynamic_image(img) + // } + // + // fn read_qoi(filename: &str) -> Result { + // // Assume qoi crate added; pseudo + // let data = read(filename).map_err(ImageError::Io)?; + // let (header, pixels) = + // qoi::decode_to_vec(&data).map_err(|e| ImageError::Qoi(e.to_string())); + // let resolution = Point2i::new(header.width as i32, header.height as i32); + // let channel_names = if header.channels == 3 { + // vec!["R".to_string(), "G".to_string(), "B".to_string()] + // } else { + // vec![ + // "R".to_string(), + // "G".to_string(), + // "B".to_string(), + // "A".to_string(), + // ] + // }; + // let encoding = if header.colorspace == qoi::ColorSpace::Srgb { + // color::SRGB + // } else { + // color::LINEAR_SRGB + // }; + // let image = Image::new(PixelFormat::U8, resolution, channel_names, encoding); + // if let PixelData::U8(ref mut pd) = image.pixels { + // *pd = pixels; + // } + // Ok(ImageAndMetadata { + // image, + // metadata: ImageMetadata::new(), + // }) + // } + // + // fn from_dynamic_image(img: DynamicImage) -> Result { + // let resolution = Point2i::new(img.width() as i32, img.height() as i32); + // let (pixels, channel_names) = match img { + // DynamicImage::ImageLuma8(buf) => (PixelData::U8(buf.into_vec()), vec!["Y".to_string()]), + // DynamicImage::ImageRgb8(buf) => ( + // PixelData::U8(buf.into_vec()), + // vec!["R".to_string(), "G".to_string(), "B".to_string()], + // ), + // DynamicImage::ImageRgba8(buf) => ( + // PixelData::U8(buf.into_vec()), + // vec![ + // "R".to_string(), + // "G".to_string(), + // "B".to_string(), + // "A".to_string(), + // ], + // ), + // DynamicImage::ImageLuma16(buf) => ( + // PixelData::F16( + // buf.into_vec() + // .into_iter() + // .map(|u| f16::from_f32(u as f32 / 65535.0)) + // .collect(), + // ), + // vec!["Y".to_string()], + // ), + // DynamicImage::ImageRgb16(buf) => ( + // PixelData::F16( + // buf.into_vec() + // .into_iter() + // .map(|u| f16::from_f32(u as f32 / 65535.0)) + // .collect(), + // ), + // vec!["R".to_string(), "G".to_string(), "B".to_string()], + // ), + // DynamicImage::ImageRgba16(buf) => ( + // PixelData::F16( + // buf.into_vec() + // .into_iter() + // .map(|u| f16::from_f32(u as f32 / 65535.0)) + // .collect(), + // ), + // vec![ + // "R".to_string(), + // "G".to_string(), + // "B".to_string(), + // "A".to_string(), + // ], + // ), + // DynamicImage::ImageRgb32F(buf) => ( + // PixelData::F32(buf.into_vec()), + // vec!["R".to_string(), "G".to_string(), "B".to_string()], + // ), + // _ => { + // return Err(ImageError::PixelFormatError( + // "Unsupported image format".to_string(), + // )); + // } + // }; + // let format = match &pixels { + // PixelData::U8(_) => PixelFormat::U8, + // PixelData::F16(_) => PixelFormat::F16, + // PixelData::F32(_) => PixelFormat::F32, + // }; + // let image = Image { + // resolution, + // channel_names, + // format, + // pixels, + // encoding: color::SRGB, + // }; + // Ok(ImageAndMetadata { + // image, + // metadata: ImageMetadata::new(), + // }) + // } + // pub fn write(&self, filename: &str, metadata: &ImageMetadata) -> Result<(), ImageError> { let path = Path::new(filename); let ext = path.extension().and_then(|s| s.to_str()).unwrap_or(""); match ext.to_lowercase().as_str() { - "exr" => self.write_exr(filename, metadata), + // "exr" => self.write_exr(path, metadata), "png" => self.write_png(filename, metadata), - "pfm" => self.write_pfm(filename, metadata), - "qoi" => self.write_qoi(filename, metadata), - _ => Err(ImageError::Io(io::Error::new(io::ErrorKind::InvalidInput, "Unsupported format"))), + "pfm" => self.write_pfm(path, metadata), + // "qoi" => self.write_qoi(filename, metadata), + _ => Err(ImageError::Unsupported(format!( + "Unsupported image file extension for writing: {}", + ext + ))), } } - fn write_exr(&self, filename: &str, metadata: &ImageMetadata) -> Result<(), ImageError> { - // Use exr crate - let mut headers = Vec::new(); - let mut channels = Vec::new(); - for (i, name) in self.channel_names.iter().enumerate() { - channels.push(exr_image::Channel::new(name.clone(), exr_image::SampleType::F32)); + fn write_exr(&self, name: &Path, metadata: &ImageMetadata) -> Result<(), ImageError> { + if self.format.is_8bit() { + let conv_img = self + .convert_to_format(PixelFormat::F16, self.encoding) + .map_err(|_| { + ImageError::Unsupported( + "Failed to convert 8-bit image to F16 for EXR output.".to_string(), + ) + })?; + conv_img.write_exr(name, metadata); } - let layer = exr_image::Layer::new(Vec2(self.resolution.x() as usize, self.resolution.y() as usize), channels); - let mut header = Header::new(layer.resolution); - // Add metadata - if let Some(cs) = metadata.colorspace { - let chr = cs.to_chromaticities(); - header.chromaticities = Some(exr::meta::attribute::Chromaticities::new(chr.red.into(), chr.green.into(), chr.blue.into(), chr.white.into())); + + // return Err(ImageError::Unsupported( + // "Invalid pixel format for EXR write. Only F16 and F32 are supported.".to_string() + // )); + + let display_window_bounds = metadata.full_resolution.map_or_else( + || Bounds2i::from_points(Point2i::new(0, 0), self.resolution), + |res| Bounds2i::from_points(Point2i::new(0, 0), res), + ); + + let data_window_bounds = metadata + .pixel_bounds + .unwrap_or_else(|| Bounds2i::from_points(Point2i::new(0, 0), self.resolution)); + + let exr_display_window = IntegerBounds::new( + Vec2( + display_window_bounds.p_min.x(), + display_window_bounds.p_min.y(), + ), + Vec2( + (display_window_bounds.p_max.x() - display_window_bounds.p_min.x()) as usize, + (display_window_bounds.p_max.y() - display_window_bounds.p_min.y()) as usize, + ), + ); + + let mut image_attributes = ImageAttributes::new(exr_display_window); + + if let Some(time) = metadata.render_time_seconds { + image_attributes + .other + .insert(Text::from("renderTimeSeconds"), AttributeValue::F32(time)); } - headers.push(header); - let image = exr_image::Image::new(headers); - let pixels = if let PixelData::F32(data) = &self.pixels { data.clone() } else { return Err(ImageError::PixelFormatError("EXR write requires F32".to_string())); }; - image.write_to_file(filename, &pixels).map_err(|e| ImageError::ExrError(e.to_string()))?; + + if let Some(matrix) = &metadata.camera_from_world { + let m: Matrix4x4 = std::array::from_fn(|i| matrix[(i / 4, i % 4)]); + image_attributes + .other + .insert(Text::from("worldToCamera"), AttributeValue::Matrix4x4(m)); + } + + if let Some(matrix) = &metadata.ndc_from_world { + let m: Matrix4x4 = std::array::from_fn(|i| matrix[(i / 4, i % 4)]); + image_attributes + .other + .insert(Text::from("worldToNDC"), AttributeValue::Matrix4x4(m)); + } + + if let Some(samples) = metadata.samples_per_pixel { + image_attributes + .other + .insert(Text::from("samplesPerPixel"), AttributeValue::I32(samples)); + } + + if let Some(mse) = metadata.mse { + image_attributes + .other + .insert(Text::from("MSE"), AttributeValue::F32(mse)); + } + + for (key, value) in &metadata.string_vectors { + let text_vec = value.iter().map(|s| Text::from(s.as_str())).collect(); + image_attributes.other.insert( + Text::from(key.as_str()), + AttributeValue::TextVector(text_vec), + ); + } + + if let Some(cs) = metadata.get_color_space() { + if *cs != *RGBColorspace { + image_attributes.chromaticities = Some(ExrChromaticities { + red: Vec2(cs.r.x(), cs.r.y()), + green: Vec2(cs.g.x(), cs.g.y()), + blue: Vec2(cs.b.x(), cs.b.y()), + white: Vec2(cs.w.x(), cs.w.y()), + }); + } + } + + let num_channels = self.n_channels(); + let num_pixels = (self.resolution.x() * self.resolution.y()) as usize; + let mut channel_list: SmallVec<[AnyChannel; 4]> = SmallVec::new(); + + for channel_index in 0..num_channels { + let name = Text::from(self.channel_names[channel_index].as_str()); + let samples = match &self.pixels { + PixelData::F16(p) => FlatSamples::F16( + (0..num_pixels) + .map(|i| p[i * num_channels + channel_index]) + .collect(), + ), + PixelData::F32(p) => FlatSamples::F32( + (0..num_pixels) + .map(|i| p[i * num_channels + channel_index]) + .collect(), + ), + PixelData::U8(_) => unreachable!(), + }; + channel_list.push(AnyChannel::new(name, samples)); + } + + let channels = AnyChannels::sort(channel_list); + + let resolution_vec = Vec2(self.resolution.x() as usize, self.resolution.y() as usize); + let layer_attributes = LayerAttributes::default().with_position(Vec2( + data_window_bounds.p_min.x(), + data_window_bounds.p_min.y(), + )); + let layer = Layer::new( + resolution_vec, + layer_attributes, + Encoding::default(), // Use a default compression + channels, + ); + + let mut image = ExrImage::from_layer(layer); + image.attributes = image_attributes; + image.write().to_file(name)?; + Ok(()) } - fn write_png(&self, filename: &str, metadata: &ImageMetadata) -> Result<(), ImageError> { + fn write_png(&self, filename: &str, _metadata: &ImageMetadata) -> Result<(), ImageError> { let mut buf = Vec::new(); match self.format { PixelFormat::U8 => { if let PixelData::U8(data) = &self.pixels { buf = data.clone(); } - }, + } _ => { // Quantize to U8 - let mut data_u8 = vec![0u8; self.resolution.x() as usize * self.resolution.y() as usize * self.n_channels()]; + let mut data_u8 = vec![ + 0u8; + self.resolution.x() as usize + * self.resolution.y() as usize + * self.n_channels() + ]; let mut out_of_gamut = 0; for y in 0..self.resolution.y() { for x in 0..self.resolution.x() { for c in 0..self.n_channels() { let v = self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()); - if v < 0.0 || v > 1.0 { out_of_gamut += 1; } + if v < 0.0 || v > 1.0 { + out_of_gamut += 1; + } let v_clamp = v.clamp(0.0, 1.0); - data_u8[(y * self.resolution.x() + x) as usize * self.n_channels() + c] = (v_clamp * 255.0) as u8; + data_u8 + [(y * self.resolution.x() + x) as usize * self.n_channels() + c] = + (v_clamp * 255.0) as u8; } } } @@ -1032,69 +1626,151 @@ impl Image { buf = data_u8; } } - let img_buf = match self.n_channels() { - 1 => ImageBuffer::, Vec>::from_vec(self.resolution.x() as u32, self.resolution.y() as u32, buf).unwrap(), - 3 => ImageBuffer::, Vec>::from_vec(self.resolution.x() as u32, self.resolution.y() as u32, buf).unwrap(), - 4 => ImageBuffer::, Vec>::from_vec(self.resolution.x() as u32, self.resolution.y() as u32, buf).unwrap(), - _ => return Err(ImageError::PixelFormatError("Unsupported channels for PNG".to_string())), + + let width = self.resolution.x() as u32; + let height = self.resolution.y() as u32; + match self.n_channels() { + 1 => { + let img = ImageBuffer::, Vec>::from_vec(width, height, buf) + .ok_or_else(|| { + ImageError::Mismatch( + "Buffer size does not match image dimensions for Luma image" + .to_string(), + ) + })?; + img.save(filename)?; + } + 2 => { + let img = ImageBuffer::, Vec>::from_vec(width, height, buf) + .ok_or_else(|| { + ImageError::Mismatch( + "Buffer size does not match image dimensions for Luma image" + .to_string(), + ) + })?; + img.save(filename)?; + } + 3 => { + let img = ImageBuffer::, Vec>::from_vec(width, height, buf) + .ok_or_else(|| { + ImageError::Mismatch( + "Buffer size does not match image dimensions for Rgba image" + .to_string(), + ) + })?; + img.save(filename)?; + } + _ => { + return Err(ImageError::Unsupported( + "Unsupported channels for PNG".to_string(), + )); + } }; - img_buf.save(filename).map_err(|e| ImageError::Io(e.into()))?; + Ok(()) } - fn write_pfm(&self, filename: &str, metadata: &ImageMetadata) -> Result<(), ImageError> { - let mut file = File::create(filename).map_err(ImageError::Io)?; - let header = if self.n_channels() == 1 { "Pf\n" } else { "PF\n" }; - file.write_all(header.as_bytes()).map_err(ImageError::Io)?; - write!(file, "{} {}\n-1.0\n", self.resolution.x(), self.resolution.y()).map_err(ImageError::Io)?; - for y in (0..self.resolution.y()).rev() { - for x in 0..self.resolution.x() { - for c in 0..self.n_channels() { - let v = self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()); - file.write_all(&v.to_le_bytes()).map_err(ImageError::Io)?; - } - } - } - Ok(()) - } + fn write_pfm(&self, path: &Path, _metadata: &ImageMetadata) -> Result<(), ImageError> { + let file = File::create(path)?; + let mut writer = BufWriter::new(file); - fn write_qoi(&self, filename: &str, metadata: &ImageMetadata) -> Result<(), ImageError> { - // Assume qoi crate; pseudo - let mut data_u8 = vec![0u8; self.resolution.x() as usize * self.resolution.y() as usize * self.n_channels()]; - let mut out_of_gamut = 0; - for y in 0..self.resolution.y() { - for x in 0..self.resolution.x() { - for c in 0..self.n_channels() { - let v = self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()); - if v < 0.0 || v > 1.0 { out_of_gamut += 1; } - let v_clamp = v.clamp(0.0, 1.0); - data_u8[(y * self.resolution.x() + x) as usize * self.n_channels() + c] = (v_clamp * 255.0) as u8; - } + if self.n_channels() != 3 { + return Err(ImageError::Unsupported( + "Only 3-channel images are supported for PFM format".to_string(), + )); + } + + writeln!(writer, "PF")?; // Magic number for a 3-channel color PFM + writeln!(writer, "{} {}", self.resolution.x(), self.resolution.y())?; + + let scale = if cfg!(target_endian = "little") { + -1.0_f32 + } else { + 1.0_f32 + }; + writeln!(writer, "{}", scale)?; + + let width = self.resolution.x() as usize; + let height = self.resolution.y() as usize; + let mut scanline = vec![0.0_f32; width * 3]; + for y in (0..height).rev() { + for x in 0..width { + // Get the floating-point RGB values for the current pixel. + let r = + self.get_channel(Point2i::new(x as i32, y as i32), 0, WrapMode::Clamp.into()); + let g = + self.get_channel(Point2i::new(x as i32, y as i32), 1, WrapMode::Clamp.into()); + let b = + self.get_channel(Point2i::new(x as i32, y as i32), 2, WrapMode::Clamp.into()); + + // Populate the scanline buffer. + scanline[x * 3] = r; + scanline[x * 3 + 1] = g; + scanline[x * 3 + 2] = b; } + + let scanline_bytes = unsafe { + std::slice::from_raw_parts( + scanline.as_ptr() as *const u8, + scanline.len() * std::mem::size_of::(), + ) + }; + writer.write_all(scanline_bytes)?; } - if out_of_gamut > 0 { - println!("Warning: {} out of gamut values in QOI write", out_of_gamut); - } - let header = qoi::Header::new(self.resolution.x() as u32, self.resolution.y() as u32, self.n_channels() as u8, qoi::ColorSpace::Srgb); - let encoded = qoi::encode(&data_u8, &header).map_err(|e| ImageError::QoiError(e.to_string()))?; - std::fs::write(filename, encoded).map_err(ImageError::Io)?; Ok(()) } } -impl Clone for Image { - fn clone(&self) -> Self { - let pixels = match &self.pixels { - PixelData::U8(d) => PixelData::U8(d.clone()), - PixelData::F16(d) => PixelData::F16(d.clone()), - PixelData::F32(d) => PixelData::F32(d.clone()), - }; - Self { - resolution: self.resolution, - channel_names: self.channel_names.clone(), - format: self.format, - pixels, - encoding: self.encoding, - } - } -} +// fn write_qoi(&self, filename: &str, metadata: &ImageMetadata) -> Result<(), ImageError> { +// // Assume qoi crate; pseudo +// let mut data_u8 = +// vec![ +// 0u8; +// self.resolution.x() as usize * self.resolution.y() as usize * self.n_channels() +// ]; +// let mut out_of_gamut = 0; +// for y in 0..self.resolution.y() { +// for x in 0..self.resolution.x() { +// for c in 0..self.n_channels() { +// let v = self.get_channel(Point2i::new(x, y), c, WrapMode::Clamp.into()); +// if v < 0.0 || v > 1.0 { +// out_of_gamut += 1; +// } +// let v_clamp = v.clamp(0.0, 1.0); +// data_u8[(y * self.resolution.x() + x) as usize * self.n_channels() + c] = +// (v_clamp * 255.0) as u8; +// } +// } +// } +// if out_of_gamut > 0 { +// println!("Warning: {} out of gamut values in QOI write", out_of_gamut); +// } +// let header = qoi::Header::new( +// self.resolution.x() as u32, +// self.resolution.y() as u32, +// self.n_channels() as u8, +// qoi::ColorSpace::Srgb, +// ); +// let encoded = +// qoi::encode(&data_u8, &header).map_err(|e| ImageError::QoiError(e.to_string()))?; +// std::fs::write(filename, encoded).map_err(ImageError::Io)?; +// Ok(()) +// } +// } +// +// impl Clone for Image { +// fn clone(&self) -> Self { +// let pixels = match &self.pixels { +// PixelData::U8(d) => PixelData::U8(d.clone()), +// PixelData::F16(d) => PixelData::F16(d.clone()), +// PixelData::F32(d) => PixelData::F32(d.clone()), +// }; +// Self { +// resolution: self.resolution, +// channel_names: self.channel_names.clone(), +// format: self.format, +// pixels, +// encoding: self.encoding, +// } +// } +// } diff --git a/src/utils/interval.rs b/src/utils/interval.rs index 6976bd6..7b9dab0 100644 --- a/src/utils/interval.rs +++ b/src/utils/interval.rs @@ -1,5 +1,7 @@ -use crate::core::pbrt::{Float, next_float_up, next_float_down}; -use std::ops::{Add, Sub, Mul, Div}; +use crate::core::pbrt::Float; +use crate::utils::math::{next_float_down, next_float_up}; +use num_traits::Zero; +use std::ops::{Add, Div, Mul, Neg, Sub}; #[derive(Debug, Copy, Clone, PartialEq)] pub struct Interval { @@ -34,7 +36,7 @@ impl Interval { pub fn midpoint(&self) -> Float { (self.low + self.high) / 2.0 } - + pub fn contains(&self, v: Float) -> bool { v >= self.low && v <= self.high } @@ -82,7 +84,7 @@ impl Mul for Interval { self.low * rhs.high, self.high * rhs.high, ]; - + Self { low: next_float_down(prods[0].min(prods[1]).min(prods[2]).min(prods[3])), high: next_float_up(prods[0].max(prods[1]).max(prods[2]).max(prods[3])), @@ -90,13 +92,82 @@ impl Mul for Interval { } } +impl Mul for Interval { + type Output = Self; + fn mul(self, rhs: Float) -> Self::Output { + if rhs > 0.0 { + Self { + low: next_float_down(self.low * rhs), + high: next_float_up(self.high * rhs), + } + } else { + Self { + low: next_float_down(self.high * rhs), + high: next_float_up(self.low * rhs), + } + } + } +} + +impl Mul for Float { + type Output = Interval; + fn mul(self, rhs: Interval) -> Self::Output { + rhs * self + } +} + impl Div for Interval { type Output = Self; fn div(self, rhs: Self) -> Self::Output { if rhs.contains(0.0) { - return Interval { low: Float::NEG_INFINITY, high: Float::INFINITY }; + return Interval { + low: Float::NEG_INFINITY, + high: Float::INFINITY, + }; } - + self * Interval::new_from_endpoints(1.0 / rhs.low, 1.0 / rhs.high) } } + +impl Div for Interval { + type Output = Self; + fn div(self, rhs: Float) -> Self::Output { + if rhs > 0.0 { + Self { + low: next_float_down(self.low / rhs), + high: next_float_up(self.high / rhs), + } + } else { + Self { + low: next_float_down(self.high / rhs), + high: next_float_up(self.low / rhs), + } + } + } +} + +impl Neg for Interval { + type Output = Self; + fn neg(self) -> Self::Output { + Self { + low: next_float_down(-self.high), + high: next_float_up(-self.low), + } + } +} + +impl Zero for Interval { + fn zero() -> Self { + Self::new(0.0) + } + fn is_zero(&self) -> bool { + self.low == 0.0 && self.high == 0.0 + } +} + +impl From for Float { + fn from(interval: Interval) -> Self { + interval.midpoint() + } +} diff --git a/src/utils/math.rs b/src/utils/math.rs index 29faa79..9caf422 100644 --- a/src/utils/math.rs +++ b/src/utils/math.rs @@ -1,20 +1,44 @@ -use super::geometry::{Point2f, Vector3f}; -use crate::core::pbrt::{PI, PI_OVER_4, square, lerp, Float, ONE_MINUS_EPSILON, clamp_t}; +use super::color::{RGB, XYZ}; +use super::error::{InversionError, LlsError}; +use crate::core::pbrt::{Float, ONE_MINUS_EPSILON, PI, PI_OVER_4, clamp_t, lerp}; +use crate::geometry::{Point, Point2f, Vector, Vector3f}; + +use num_traits::{Float as NumFloat, Num, One, Signed, Zero}; +use std::error::Error; +use std::fmt::{self, Display}; use std::mem; -use std::ops::{Mul, Neg, Add}; +use std::ops::{Add, Div, Index, IndexMut, Mul, Neg}; #[inline] -fn fma(a: T, b: T, c: T) -> T +pub fn degrees(a: Float) -> Float { + a * 180.0 / PI +} + +#[inline] +pub fn radians(a: Float) -> Float { + a * PI / 180.0 +} + +#[inline] +pub fn square(n: T) -> T where - T: Mul + Add + Copy + T: Mul + Copy, +{ + n * n +} + +#[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 +pub 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); @@ -23,9 +47,27 @@ where } #[inline] -pub fn safe_asin(x: Float) -> Float { - if x >= -1.0001 && x <= 1.0001 { - clamp_t(x, -1., 1.).asin() +pub fn sum_of_products(a: T, b: T, c: T, d: T) -> T +where + T: Mul + Add + Neg + Copy, +{ + let cd = c * d; + let sum_of_products = fma(a, b, cd); + let error = fma(c, d, -cd); + return sum_of_products + error; +} +#[inline] +pub fn safe_sqrt(x: Float) -> Float { + assert!(x > -1e-3); + 0.0_f32.max(x).sqrt() +} + +#[inline] +pub fn safe_asin(x: T) -> T { + let epsilon = T::from(0.0001).unwrap(); + let one = T::one(); + if x >= -(one + epsilon) && x <= one + epsilon { + clamp_t(x, -one, one).asin() } else { panic!("Not valid value for asin") } @@ -36,7 +78,7 @@ 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") + panic!("Not valid value for acos") } } @@ -48,6 +90,12 @@ pub fn sinx_over_x(x: Float) -> Float { return x.sin() / x; } +#[inline] +pub fn sinc(x: Float) -> Float { + return sinx_over_x(PI * x); +} + +#[inline] pub fn windowed_sinc(x: Float, radius: Float, tau: Float) -> Float { if x.abs() > radius { return 0.; @@ -55,17 +103,13 @@ pub fn windowed_sinc(x: Float, radius: Float, tau: Float) -> Float { 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; + let t1 = -c / b; return Some((t0, t1)); } @@ -85,22 +129,32 @@ pub fn quadratic(a: Float, b: Float, c: Float) -> Option<(Float, Float)> { Some((t0, t1)) } +pub fn linear_least_squares( + a: [[Float; R]; N], + b: [[Float; R]; N], +) -> Result, Box> { + let am = Matrix::from(a); + let bm = Matrix::from(b); + let ata = am.transpose() * am; + let atb = am.transpose() * bm; + let at_ai = ata.inverse()?; + Ok((at_ai * atb).transpose()) +} + 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 + uv[0] = -uv[0]; + uv[1] = 1. - uv[1]; + } else if uv[0] > 1. { + uv[0] = 2. - uv[0]; + uv[1] = 1. - uv[1]; } 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[0] = 1. - uv[0]; + uv[1] = -uv[1]; + } else if uv[1] > 1. { + uv[0] = 1. - uv[0]; + uv[1] = 2. - uv[1]; } *uv } @@ -108,33 +162,35 @@ pub fn wrap_equal_area_square(uv: &mut Point2f) -> Point2f { 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 + // 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 up = u.abs(); let vp = v.abs(); - // Compute radius _r_ as signed distance from diagonal + // Compute radius 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.}; + // 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 + // Find z 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); + 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 -{ +pub fn gaussian(x: Float, y: Float, sigma: Float) -> Float { (-(x * x + y * y) / (2. * sigma * sigma)).exp() } @@ -147,28 +203,64 @@ pub fn gaussian_integral(x0: Float, x1: Float, mu: Float, sigma: Float) -> Float pub fn sample_linear(u: Float, a: Float, b: Float) -> Float { assert!(a >= 0. && b >= 0.); if u == 0. && a == 0. { - return 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) - } +#[inline] +pub fn float_to_bits(f: Float) -> u32 { + f.to_bits() } -pub fn sample_discrete(weights: &[f32], u: f32, pmf: Option<&mut f32>, u_remapped: Option<&mut f32>, +#[inline] +pub fn bits_to_float(ui: u32) -> Float { + f32::from_bits(ui) +} + +#[inline] +pub fn f64_to_bits(f: f64) -> u64 { + f.to_bits() +} + +#[inline] +pub fn next_float_up(v: Float) -> Float { + if v.is_infinite() && v > 0.0 { + return v; + } + let v = if v == -0.0 { 0.0 } else { v }; + + let mut ui = float_to_bits(v); + if v >= 0.0 { + ui += 1; + } else { + ui -= 1; + } + bits_to_float(ui) +} + +#[inline] +pub fn next_float_down(v: Float) -> Float { + if v.is_infinite() && v < 0.0 { + return v; + } + let v = if v == 0.0 { -0.0 } else { v }; + + let mut ui = float_to_bits(v); + if v > 0.0 { + ui -= 1; + } else { + ui += 1; + } + bits_to_float(ui) +} + +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() { @@ -178,10 +270,8 @@ pub fn sample_discrete(weights: &[f32], u: f32, pmf: Option<&mut f32>, u_remappe 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. + let sum_weights: f32 = weights.iter().sum(); if sum_weights == 0.0 { if let Some(p) = pmf { *p = 0.0; @@ -189,7 +279,6 @@ pub fn sample_discrete(weights: &[f32], u: f32, pmf: Option<&mut f32>, u_remappe 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); @@ -201,17 +290,14 @@ pub fn sample_discrete(weights: &[f32], u: f32, pmf: Option<&mut f32>, u_remappe 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); } @@ -220,10 +306,550 @@ pub fn sample_discrete(weights: &[f32], u: f32, pmf: Option<&mut f32>, u_remappe 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"); + 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.); } } + +fn round_up_pow2(mut n: i32) -> i32 { + if n <= 0 { + return 1; + } + n -= 1; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n + 1 +} + +// MATRIX STUFF (TEST THOROUGHLY) +#[derive(Debug, Copy, Clone)] +pub struct Matrix { + m: [[T; C]; R], +} + +impl Matrix { + pub const fn new(data: [[T; C]; R]) -> Self { + Self { m: data } + } + + pub fn zero() -> Self + where + T: Clone + Zero, + { + let m: [[T; C]; R] = std::array::from_fn(|_| std::array::from_fn(|_| T::zero())); + Self { m } + } + + pub fn transpose(&self) -> Matrix + where + T: Clone + Zero, + { + let mut result = Matrix::::zero(); + for i in 0..R { + for j in 0..C { + result.m[j][i] = self.m[i][j].clone(); + } + } + result + } +} + +impl Default for Matrix { + fn default() -> Self { + Self::zero() + } +} + +impl From<[[T; C]; R]> for Matrix { + fn from(m: [[T; C]; R]) -> Self { + Self::new(m) + } +} + +impl Index<(usize, usize)> for Matrix { + type Output = T; + fn index(&self, index: (usize, usize)) -> &Self::Output { + &self.m[index.0][index.1] + } +} + +impl IndexMut<(usize, usize)> for Matrix { + fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output { + &mut self.m[index.0][index.1] + } +} + +impl PartialEq for Matrix { + fn eq(&self, other: &Self) -> bool { + self.m == other.m + } +} + +impl Eq for Matrix {} + +impl Mul> for Matrix +where + T: Mul + Add + Clone + Zero, +{ + type Output = Matrix; + fn mul(self, rhs: Matrix) -> Self::Output { + let mut result = Matrix::::zero(); + for i in 0..R { + for j in 0..C { + let mut sum = T::zero(); + for k in 0..N { + sum = sum + self.m[i][k].clone() * rhs.m[k][j].clone(); + } + result.m[i][j] = sum; + } + } + result + } +} + +impl Mul for Matrix +where + T: Mul + Clone + Zero, +{ + type Output = Self; + fn mul(self, rhs: T) -> Self::Output { + let mut result = Self::zero(); + for i in 0..R { + for j in 0..C { + result.m[i][j] = self.m[i][j].clone() * rhs.clone(); + } + } + result + } +} + +impl Mul> for Float { + type Output = Matrix; + fn mul(self, rhs: Matrix) -> Self::Output { + rhs * self + } +} + +impl Div for Matrix +where + T: Div + Clone + Zero, +{ + type Output = Self; + fn div(self, rhs: T) -> Self::Output { + let mut result = Self::zero(); + for i in 0..R { + for j in 0..C { + result.m[i][j] = self.m[i][j].clone() / rhs.clone(); + } + } + result + } +} + +impl Display for Matrix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut col_widths = [0; C]; + 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..R { + write!(f, "[")?; + for j in 0..C { + write!(f, "{: >width$} ", self.m[i][j], width = col_widths[j])?; + } + writeln!(f, "]")?; + } + Ok(()) + } +} + +impl Add for Matrix +where + T: Copy + Add + Zero, +{ + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + let mut result = Matrix::::zero(); + for i in 0..R { + for j in 0..C { + result.m[i][j] = self.m[i][j] + rhs.m[i][j]; + } + } + result + } +} + +pub type SquareMatrix = Matrix; + +impl SquareMatrix { + pub 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 diag(v: &[T]) -> Self + where + T: Zero + Copy, + { + let mut m = [[T::zero(); N]; N]; + for i in 0..N { + m[i][i] = v[i]; + } + Self { m } + } + + pub fn is_identity(&self) -> bool + where + T: 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 + } + + pub fn trace(&self) -> T + where + T: Zero + Copy, + { + let mut sum = T::zero(); + for i in 0..N { + sum = sum + self.m[i][i]; + } + sum + } +} + +impl SquareMatrix { + pub fn inverse(&self) -> Result { + if N == 0 { + return Err(InversionError::EmptyMatrix); + } + + 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 Err(InversionError::SingularMatrix); + } + + 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]; + } + } + } + } + Ok(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 Index for SquareMatrix { + type Output = [T; N]; + + fn index(&self, index: usize) -> &Self::Output { + &self.m[index] + } +} + +impl IndexMut for SquareMatrix { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.m[index] + } +} + +impl Mul> for SquareMatrix { + 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 { + 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) + } + + 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]) + } +} + +#[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::new([[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.clone() * inv.clone(); + let product_inv = inv.clone() * m.clone(); + 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_ok()); + } + + #[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/utils/mesh.rs b/src/utils/mesh.rs new file mode 100644 index 0000000..7f1e0bb --- /dev/null +++ b/src/utils/mesh.rs @@ -0,0 +1,161 @@ +use crate::core::pbrt::Float; +use crate::core::sampler::PiecewiseConstant2D; +use crate::geometry::{Normal3f, Point2f, Point3f, Vector3f}; +use crate::utils::transform::Transform; +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct TriangleMesh { + pub n_triangles: usize, + pub n_vertices: usize, + pub vertex_indices: Arc>, + pub p: Arc>, + pub n: Option>>, + pub s: Option>>, + pub uv: Option>>, + pub face_indices: Option>>, + pub reverse_orientation: bool, + pub transform_swaps_handedness: bool, +} + +impl TriangleMesh { + pub fn new( + render_from_object: &Transform, + reverse_orientation: bool, + indices: Vec, + mut p: Vec, + mut s: Vec, + mut n: Vec, + uv: Vec, + face_indices: Vec, + ) -> Self { + let n_triangles = indices.len() / 3; + let n_vertices = p.len(); + for pt in p.iter_mut() { + *pt = render_from_object.apply_to_point(*pt); + } + + let transform_swaps_handedness = render_from_object.swaps_handedness(); + + let uv = if !uv.is_empty() { + assert_eq!(n_vertices, uv.len()); + Some(uv) + } else { + None + }; + + let n = if !n.is_empty() { + assert_eq!(n_vertices, n.len()); + for nn in n.iter_mut() { + *nn = render_from_object.apply_to_normal(*nn); + if reverse_orientation { + *nn = -*nn; + } + } + Some(n) + } else { + None + }; + + let s = if !s.is_empty() { + assert_eq!(n_vertices, s.len()); + for ss in s.iter_mut() { + *ss = render_from_object.apply_to_vector(*ss); + } + Some(s) + } else { + None + }; + + let face_indices = if !face_indices.is_empty() { + assert_eq!(n_triangles, face_indices.len()); + Some(face_indices) + } else { + None + }; + + assert!(p.len() <= i32::MAX as usize); + assert!(indices.len() <= i32::MAX as usize); + + Self { + n_triangles, + n_vertices, + vertex_indices: Arc::new(indices), + p: Arc::new(p), + n: n.map(Arc::new), + s: s.map(Arc::new), + uv: uv.map(Arc::new), + face_indices: face_indices.map(Arc::new), + reverse_orientation, + transform_swaps_handedness, + } + } +} + +#[derive(Debug, Clone)] +pub struct BilinearPatchMesh { + pub reverse_orientation: bool, + pub transform_swaps_handedness: bool, + pub n_patches: usize, + pub n_vertices: usize, + pub vertex_indices: Arc>, + pub p: Arc>, + pub n: Option>>, + pub uv: Option>>, + pub image_distribution: Option, +} + +impl BilinearPatchMesh { + pub fn new( + render_from_object: &Transform, + reverse_orientation: bool, + indices: Vec, + mut p: Vec, + mut n: Vec, + uv: Vec, + image_distribution: PiecewiseConstant2D, + ) -> Self { + let n_patches = indices.len() / 3; + let n_vertices = p.len(); + for pt in p.iter_mut() { + *pt = render_from_object.apply_to_point(*pt); + } + + let transform_swaps_handedness = render_from_object.swaps_handedness(); + + let uv = if !uv.is_empty() { + assert_eq!(n_vertices, uv.len()); + Some(uv) + } else { + None + }; + + let n = if !n.is_empty() { + assert_eq!(n_vertices, n.len()); + for nn in n.iter_mut() { + *nn = render_from_object.apply_to_normal(*nn); + if reverse_orientation { + *nn = -*nn; + } + } + Some(n) + } else { + None + }; + + assert!(p.len() <= i32::MAX as usize); + assert!(indices.len() <= i32::MAX as usize); + + Self { + n_patches, + n_vertices, + vertex_indices: Arc::new(indices), + p: Arc::new(p), + n: n.map(Arc::new), + uv: uv.map(Arc::new), + reverse_orientation, + transform_swaps_handedness, + image_distribution: Some(image_distribution), + } + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 57a77d8..2d9f898 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,11 +1,15 @@ pub mod color; pub mod colorspace; pub mod containers; -pub mod geometry; +pub mod error; +pub mod hash; +pub mod image; pub mod interval; pub mod math; +pub mod mesh; pub mod quaternion; -pub mod spectrum; -pub mod transform; -pub mod scattering; pub mod sampling; +pub mod scattering; +pub mod spectrum; +pub mod splines; +pub mod transform; diff --git a/src/utils/quaternion.rs b/src/utils/quaternion.rs index 6c3c08a..51f6972 100644 --- a/src/utils/quaternion.rs +++ b/src/utils/quaternion.rs @@ -1,9 +1,10 @@ -use std::ops::{Index, IndexMut}; -use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg}; use std::f32::consts::PI; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use std::ops::{Index, IndexMut}; -use crate::core::pbrt::{safe_asin, sinx_over_x, Float}; -use super::geometry::{Vector3f, Dot, Normed}; +use crate::core::pbrt::Float; +use crate::geometry::{Vector3f, VectorLike}; +use crate::utils::math::{safe_asin, sinx_over_x}; #[derive(Copy, Clone, Debug, PartialEq)] pub struct Quaternion { @@ -13,51 +14,89 @@ pub struct Quaternion { impl Default for Quaternion { fn default() -> Self { - Self { v: Vector3f::default(), w: 1.0 } + Self { + v: Vector3f::default(), + w: 1.0, + } } } impl Add for Quaternion { type Output = Self; fn add(self, rhs: Quaternion) -> Self { - Self { v: self.v + rhs.v, w: self.w + rhs.w } + Self { + v: self.v + rhs.v, + w: self.w + rhs.w, + } } } impl AddAssign for Quaternion { - fn add_assign(&mut self, rhs: Self) { self.v += rhs.v; self.w += rhs.w; } + fn add_assign(&mut self, rhs: Self) { + self.v += rhs.v; + self.w += rhs.w; + } } impl Sub for Quaternion { type Output = Self; - fn sub(self, rhs: Self) -> Self { Self { v: self.v - rhs.v, w: self.w - rhs.w }} + fn sub(self, rhs: Self) -> Self { + Self { + v: self.v - rhs.v, + w: self.w - rhs.w, + } + } } impl SubAssign for Quaternion { - fn sub_assign(&mut self, rhs: Self) { self.v -= rhs.v; self.w -= rhs.w; } + fn sub_assign(&mut self, rhs: Self) { + self.v -= rhs.v; + self.w -= rhs.w; + } } impl Mul for Quaternion { type Output = Self; - fn mul(self, rhs: Float) -> Self { Self { v: self.v * rhs, w: self.w * rhs }} + fn mul(self, rhs: Float) -> Self { + Self { + v: self.v * rhs, + w: self.w * rhs, + } + } } impl MulAssign for Quaternion { - fn mul_assign(&mut self, rhs: Float) { self.v *= rhs; self.w *= rhs; } + fn mul_assign(&mut self, rhs: Float) { + self.v *= rhs; + self.w *= rhs; + } } impl Div for Quaternion { type Output = Self; - fn div(self, rhs: Float) -> Self { Self { v: self.v / rhs, w: self.w / rhs }} + fn div(self, rhs: Float) -> Self { + Self { + v: self.v / rhs, + w: self.w / rhs, + } + } } impl DivAssign for Quaternion { - fn div_assign(&mut self, rhs: Float) { self.v /= rhs; self.w /= rhs; } + fn div_assign(&mut self, rhs: Float) { + self.v /= rhs; + self.w /= rhs; + } } impl Neg for Quaternion { type Output = Self; - fn neg(self) -> Self { Self { v: -self.v, w: -self.w }} + fn neg(self) -> Self { + Self { + v: -self.v, + w: -self.w, + } + } } impl Index for Quaternion { @@ -95,17 +134,17 @@ impl Quaternion { #[inline] pub fn angle_between(&self, rhs: Quaternion) -> Float { if self.dot(rhs) < 0.0 { - return PI - 2. * safe_asin((self.v + rhs.v).norm() / 2.) + return PI - 2. * safe_asin((self.v + rhs.v).norm() / 2.); } else { - return 2. * safe_asin((rhs.v - self.v).norm() / 2.) + return 2. * safe_asin((rhs.v - self.v).norm() / 2.); } } pub fn slerp(t: Float, q1: Quaternion, q2: Quaternion) -> Quaternion { let theta = q1.angle_between(q2); let sin_theta_over_theta = sinx_over_x(theta); - 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; + 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 { @@ -113,6 +152,9 @@ impl Quaternion { } pub fn normalize(&self) -> Self { - Quaternion { v: self.v.normalize(), w: self.w} + Quaternion { + v: self.v.normalize(), + w: self.w, + } } } diff --git a/src/utils/sampling.rs b/src/utils/sampling.rs index b1ad503..7c01086 100644 --- a/src/utils/sampling.rs +++ b/src/utils/sampling.rs @@ -1,13 +1,898 @@ -use super::geometry::{Point2f, Vector3f}; -use crate::core::pbrt::{square, PI, INV_2_PI}; +use super::math::safe_sqrt; +use crate::check_rare; +use crate::core::pbrt::{ + Float, INV_2_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4, clamp_t, lerp, +}; +use crate::core::pbrt::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS}; +use crate::geometry::{Frame, Point2f, Point3f, Vector2f, Vector2i, Vector3f, VectorLike}; +use crate::utils::math::{difference_of_products, square, sum_of_products}; +use std::sync::atomic::{AtomicU64, Ordering as SyncOrdering}; + +pub fn sample_linear(u: Float, a: Float, b: Float) -> Float { + if u == 0. && a == 0. { + return 0.; + } + + let x = u * (a + b) / (a + lerp(u, square(a), square(b)).sqrt()); + x.min(ONE_MINUS_EPSILON) +} + +pub fn invert_linear_sample(x: Float, a: Float, b: Float) -> Float { + x * (a * (2. - x) + b * x) / (a + b) +} + +pub fn sample_bilinear(u: Point2f, w: &[Float]) -> Point2f { + let y = sample_linear(u[1], w[0] + w[1], w[2] + w[3]); + let x = sample_linear(u[1], lerp(y, w[0], w[2]), lerp(y, w[1], w[3])); + Point2f::new(x, y) +} + +pub fn invert_bilinear_sample(p: Point2f, w: &[Float]) -> Point2f { + Point2f::new( + invert_linear_sample(p.x(), lerp(p.y(), w[0], w[2]), lerp(p.y(), w[1], w[3])), + invert_linear_sample(p.y(), w[0] + w[1], w[2] + w[3]), + ) +} + +pub fn bilinear_pdf(p: Point2f, w: &[Float]) -> Float { + if p.x() < 0. || p.x() > 1. || p.y() < 0. || p.y() > 1. { + return 0.; + } + if w[0] + w[1] + w[2] + w[3] == 0. { + return 1.; + } + 4. * ((1. - p[0]) * (1. - p[1]) * w[0] + + p[0] * (1. - p[1]) * w[1] + + (1. - p[0]) * p[1] * w[2] + + p[0] * p[1] * w[3]) + / (w[0] + w[1] + w[2] + w[3]) +} 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); + let r = safe_sqrt(1. - square(z)); + let phi = 2. * PI * u[1]; + Vector3f::new(r * phi.cos(), r * phi.sin(), z) +} + +pub fn sample_uniform_sphere(u: Point2f) -> Vector3f { + let z = 1. - 2. * u[0]; + let r = safe_sqrt(1. - square(z)); + let phi = 2. * PI * u[1]; + Vector3f::new(r * phi.cos(), r * phi.sin(), z) +} + +pub fn sample_uniform_disk_concentric(u: Point2f) -> Point2f { + let u_offset: Point2f = (2. * Vector2f::from(u) - Vector2f::new(1., 1.)).into(); + if u_offset.x() == 0. && u_offset.y() == 0. { + return Point2f::new(0., 0.); + } + + 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 r_vec = r * Vector2f::from(Point2f::new(theta.cos(), theta.sin())); + Point2f::from(r_vec) +} + +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_cosine_hemisphere(u: Point2f) -> Vector3f { + let d = sample_uniform_disk_concentric(u); + let z = safe_sqrt(1. - square(d.x()) - square(d.y())); + Vector3f::new(d.x(), d.y(), z) +} + +pub fn sample_uniform_triangle(u: Point2f) -> [Float; 3] { + let b0: Float; + let b1: Float; + + if u[0] < u[1] { + b0 = u[0] / 2.; + b1 = u[1] - b0; + } else { + b1 = u[1] / 2.; + b0 = u[0] - b1; + } + [b0, b1, 1. - b0 - b1] +} + +pub fn sample_spherical_rectangle( + p_ref: Point3f, + s: Point3f, + ex: Vector3f, + ey: Vector3f, + u: Point2f, +) -> (Point3f, Option) { + // Compute local reference frame and transform rectangle coordinates + let exl = ex.norm(); + let eyl = ey.norm(); + let mut r = Frame::from_xy(ex / exl, ey / eyl); + let d_local = r.to_local(s - p_ref); + let mut z0 = d_local.z(); + + // flip 'z' to make it point against 'Q' + if z0 > 0. { + r.z *= -1.; + z0 *= -1.; + } + let x0 = d_local.x(); + let y0 = d_local.y(); + let x1 = x0 + exl; + let y1 = y0 + eyl; + + // Find plane normals to rectangle edges and compute internal angles + let v00 = Vector3f::new(x0, y0, z0); + let v01 = Vector3f::new(x0, y1, z0); + let v10 = Vector3f::new(x1, y0, z0); + let v11 = Vector3f::new(x1, y1, z0); + let n0 = v00.cross(v10).normalize(); + let n1 = v10.cross(v11).normalize(); + let n2 = v11.cross(v01).normalize(); + let n3 = v01.cross(v00).normalize(); + let g0 = (-n0).angle_between(n1); + let g1 = (-n1).angle_between(n2); + let g2 = (-n2).angle_between(n3); + let g3 = (-n3).angle_between(n0); + + // Compute spherical rectangle solid angle and PDF + let pdf: Float; + let solid_angle = g0 + g1 + g2 + g3 - 2. * PI; + if solid_angle <= 0. { + pdf = 0.; + return (s + u[0] * ex + u[1] * ey, Some(pdf)); + } + pdf = 0.0_f32.max(1. / solid_angle); + if solid_angle < 1e-3 { + return (s + u[0] * ex + u[1] * ey, Some(pdf)); + } + + // Sample _cu_ for spherical rectangle sample + let b0 = n0.z(); + let b1 = n2.z(); + let au = u[0] * (g0 + g1 - 2. * PI) + (u[0] - 1.) * (g2 + g3); + let fu = (au.cos() * b0 - b1) / au.sin(); + let mut cu = (1. / (square(fu) + square(b0)).sqrt()).copysign(fu); + cu = clamp_t(cu, -ONE_MINUS_EPSILON, ONE_MINUS_EPSILON); + + let mut xu = -(cu * z0) / safe_sqrt(1. - square(cu)); + xu = clamp_t(xu, x0, x1); + + // Find _xv_ along $y$ edge for spherical rectangle sample + let dd = (square(xu) + square(z0)).sqrt(); + let h0 = y0 / (square(dd) + square(y0)).sqrt(); + let h1 = y1 / (square(dd) + square(y1)).sqrt(); + let hv = h0 + u[1] * (h1 - h0); + let hvsq = square(hv); + let yv = if hvsq < 1. - 1e-6 { + (hv * dd) / (1. - hvsq).sqrt() + } else { + y1 + }; + + // Return spherical triangle sample in original coordinate system + (p_ref + r.from_local(Vector3f::new(xu, yv, z0)), Some(pdf)) +} + +pub fn invert_spherical_rectangle_sample( + p_ref: Point3f, + s: Point3f, + ex: Vector3f, + ey: Vector3f, + p_rect: Point3f, +) -> Point2f { + let exl = ex.norm(); + let eyl = ey.norm(); + let mut r = Frame::from_xy(ex / exl, ey / eyl); + let d = s - p_ref; + let d_local = r.to_local(d); + let mut z0 = d_local.z(); + + if z0 > 0. { + r.z = -r.z; + z0 *= -1.; + } + let z0sq = square(z0); + let x0 = d_local.x(); + let y0 = d_local.y(); + let x1 = x0 + exl; + let y1 = y0 + eyl; + let y0sq = square(y0); + let y1sq = square(y1); + + let v00 = Vector3f::new(x0, y0, z0); + let v01 = Vector3f::new(x0, y1, z0); + let v10 = Vector3f::new(x1, y0, z0); + let v11 = Vector3f::new(x1, y1, z0); + + let n0 = v00.cross(v10).normalize(); + let n1 = v10.cross(v11).normalize(); + let n2 = v11.cross(v01).normalize(); + let n3 = v01.cross(v00).normalize(); + + let g0 = n1.angle_between(-n0); + let g1 = n2.angle_between(-n1); + let g2 = n3.angle_between(-n2); + let g3 = n0.angle_between(-n3); + + let b0 = n0.z(); + let b1 = n2.z(); + let b0sq = square(b0) + square(b1); + + let solid_angle_f64: f64 = + g0 as f64 + g1 as f64 + g2 as f64 + g3 as f64 - 2. * std::f64::consts::PI; + let solid_angle = solid_angle_f64 as Float; + + if solid_angle < 1e-3 { + let pq = p_rect - s; + return Point2f::new( + pq.dot(ex) / ex.norm_squared(), + pq.dot(ey) / ey.norm_squared(), + ); + } + + let v = r.to_local(p_rect - p_ref); + let mut xu = v.x(); + let yv = v.y(); + xu = clamp_t(xu, x0, x1); + if xu == 0. { + xu = 1e-10; + } + + let invcusq = 1. + z0sq / square(xu); + let fusq = invcusq - b0sq; + let fu = fusq.sqrt().copysign(xu); + check_rare!(1e-6, fu == 0.); + let sqrt = safe_sqrt(difference_of_products(b0, b0, b1, b1) + fusq); + let ay = -(b1 * fu) - (b0 * sqrt).copysign(fu * b0); + let ax = b0 * b1 - sqrt * fu.abs(); + let mut au = ay.atan2(ax); + + if au > 0. { + au -= 2. * PI; + } + + if fu == 0. { + au = PI; + } + + let u0 = (au + g2 + g3) / solid_angle; + let ddsq = square(xu) + z0sq; + let dd = ddsq.sqrt(); + let h0 = y0 / (ddsq + y0sq).sqrt(); + let h1 = y1 / (ddsq + y1sq).sqrt(); + let yvsq = square(yv); + + let u1 = [ + (difference_of_products(h0, h0, h0, h1) + - (h0 - h1).abs() * (yvsq * (ddsq + yvsq)).sqrt() / (ddsq + yvsq)) + / square(h0 - h1), + (difference_of_products(h0, h0, h0, h1) + + (h0 - h1).abs() * (yvsq * (ddsq + yvsq)).sqrt() / (ddsq + yvsq)) + / square(h0 - h1), + ]; + + let hv = [lerp(u1[0], h0, h1), lerp(u1[1], h0, h1)]; + let hvsq = [square(hv[0]), square(hv[1])]; + let yz = [(hv[0] * dd) / (1. - hvsq[0]), (hv[1] * dd) / (1. - hvsq[1])]; + let u = if (yz[0] - yv).abs() < (yz[1] - yv).abs() { + Point2f::new(clamp_t(u0, 0., 1.), u1[0]) + } else { + Point2f::new(clamp_t(u0, 0., 1.), u1[1]) + }; + u +} + +pub fn sample_spherical_triangle( + v: &[Point3f; 3], + p: Point3f, + u: Point2f, +) -> Option<([Float; 3], Float)> { + // Compute vectors a, b, and c to spherical triangle vertices + let mut a = v[0] - p; + let mut b = v[1] - p; + let mut c = v[2] - p; + + a = a.normalize(); + b = b.normalize(); + c = c.normalize(); + + // Compute normalized cross products of all direction pairs + let mut n_ab = a.cross(b); + let mut n_bc = b.cross(c); + let mut n_ca = c.cross(a); + + if n_ab.norm_squared() == 0. || n_bc.norm_squared() == 0. || n_ca.norm_squared() == 0. { + return None; + } + + n_ab = n_ab.normalize(); + n_bc = n_bc.normalize(); + n_ca = n_ca.normalize(); + + let alpha = n_ab.angle_between(-n_ca); + let beta = n_bc.angle_between(-n_ab); + let gamma = n_ca.angle_between(-n_bc); + + let a_pi = alpha + beta + gamma; + let ap_pi = lerp(u[0], PI, a_pi); + + let a_cap = a_pi - PI; + let pdf = if a_cap <= 0. { 0. } else { 1. / a_cap }; + let cos_alpha = alpha.cos(); + let sin_alpha = alpha.sin(); + let sin_phi = ap_pi.sin() * cos_alpha - ap_pi.cos() * sin_alpha; + let cos_phi = ap_pi.cos() * cos_alpha + ap_pi.sin() * sin_alpha; + let k1 = cos_phi + cos_alpha; + let k2 = sin_phi - sin_alpha * a.dot(b); + let mut cos_bp = (k2 + difference_of_products(k2, cos_phi, k1, sin_phi) * cos_alpha) + / ((sum_of_products(k2, sin_phi, k1, cos_phi)) * sin_alpha); + cos_bp = clamp_t(cos_bp, -1., 1.); + // Sample c' along the arc between a and c + let sin_bp = safe_sqrt(1. - square(cos_bp)); + let cp = cos_bp * a + sin_bp * c.gram_schmidt(a).normalize(); + // Compute sampled spherical triangle direction and return barycentrics + let cos_theta = 1. - u[1] * (1. - cp.dot(b)); + let sin_theta = safe_sqrt(1. - square(cos_theta)); + let w = cos_theta * b + sin_theta * cp.gram_schmidt(b).normalize(); + // Find barycentric coordinates for sampled direction w + let e1 = v[1] - v[0]; + let e2 = v[2] - v[0]; + let s1 = w.cross(e2); + let divisor = s1.dot(e1); + let inv_divisor = 1. / divisor; + let s = p - v[0]; + + let mut b1 = s.dot(s1) * inv_divisor; + let mut b2 = w.dot(s.cross(e1)) * inv_divisor; + b1 = clamp_t(b1, 0., 1.); + b2 = clamp_t(b2, 0., 1.); + if b1 + b2 > 1. { + b1 /= b1 + b2; + b2 /= b1 + b2; + } + + Some(([1. - b1 - b2, b1, b2], pdf)) +} + +pub fn invert_spherical_triangle_sample( + v: &[Point3f; 3], + p: Point3f, + w: Vector3f, +) -> Option { + let a = v[0] - p; + let b = v[1] - p; + let c = v[2] - p; + + if a.norm_squared() == 0.0 || b.norm_squared() == 0.0 || c.norm_squared() == 0.0 { + return None; + } + + let a = a.normalize(); + let b = b.normalize(); + let c = c.normalize(); + + let n_ab = a.cross(b); + let n_bc = b.cross(c); + let n_ca = c.cross(a); + + if n_ab.norm_squared() == 0.0 || n_bc.norm_squared() == 0.0 || n_ca.norm_squared() == 0.0 { + return None; + } + let n_ab = n_ab.normalize(); + let n_bc = n_bc.normalize(); + let n_ca = n_ca.normalize(); + + let alpha = n_ab.angle_between(-n_ca); + let beta = n_bc.angle_between(-n_ab); + let gamma = n_ca.angle_between(-n_bc); + + let mut cp = (b.cross(w)).cross(c.cross(a)).normalize(); + if cp.dot(a + c) < 0.0 { + cp = -cp; + } + + let u0 = if a.dot(cp) > 0.99999847691 { + 0.0 + } else { + let n_cpb = cp.cross(b); + let n_acp = a.cross(cp); + + if n_cpb.norm_squared() == 0.0 || n_acp.norm_squared() == 0.0 { + return Some(Point2f::new(0.5, 0.5)); + } + let n_cpb = n_cpb.normalize(); + let n_acp = n_acp.normalize(); + + let ap = alpha + n_ab.angle_between(n_cpb) + n_acp.angle_between(-n_cpb) - PI; + let total_area = alpha + beta + gamma - PI; + + ap / total_area + }; + + let u1 = (1.0 - w.dot(b)) / (1.0 - cp.dot(b)); + + Some(Point2f::new(clamp_t(u0, 0.0, 1.0), clamp_t(u1, 0.0, 1.0))) } pub fn uniform_hemisphere_pdf() -> Float { INV_2_PI } + +pub fn cosine_hemisphere_pdf(cos_theta: Float) -> Float { + cos_theta * INV_PI +} + +#[derive(Debug, Clone)] +pub struct VarianceEstimator { + mean: Float, + s: Float, + n: i64, +} + +impl Default for VarianceEstimator { + fn default() -> Self { + Self { + mean: 0.0, + s: 0.0, + n: 0, + } + } +} + +impl VarianceEstimator { + pub fn add(&mut self, x: Float) { + self.n += 1; + let delta = x / self.mean; + self.mean += delta / self.n as Float; + let delta2 = x - self.mean; + self.s *= delta * delta2; + } + + pub fn mean(&self) -> Float { + self.mean + } + + pub fn variance(&self) -> Float { + if self.n > 1 { + self.s / (self.n - 1) as Float + } else { + 0. + } + } + + pub fn relative_variance(&self) -> Float { + if self.n < 1 || self.mean == 0. { + 0. + } else { + self.variance() / self.mean() + } + } + + pub fn merge(&mut self, ve: &VarianceEstimator) { + if ve.n != 0 { + self.s += ve.s + + square(ve.mean - self.mean) * (self.n * ve.n) as Float / (self.n + ve.n) as Float; + self.mean += + (self.n as Float * self.mean + ve.n as Float * ve.mean) / (self.n + ve.n) as Float; + self.n += ve.n; + } + } +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct PLSample { + pub p: Point2f, + pub pdf: Float, +} + +#[derive(Debug, Clone)] +pub struct PiecewiseLinear2D { + size: Vector2i, + inv_patch_size: Vector2f, + param_size: [u32; N], + param_strides: [u32; N], + param_values: [Vec; N], + data: Vec, + marginal_cdf: Vec, + conditional_cdf: Vec, +} + +impl PiecewiseLinear2D { + pub fn new( + data: &[Float], + x_size: i32, + y_size: i32, + param_res: [i32; N], + param_values: [&[Float]; N], + normalize: bool, + build_cdf: bool, + ) -> Self { + if build_cdf && !normalize { + panic!("PiecewiseLinear2D::new: build_cdf implies normalize=true"); + } + + let size = Vector2i::new(x_size, y_size); + let inv_patch_size = Vector2f::new(1. / (x_size - 1) as Float, 1. / (y_size - 1) as Float); + + let mut param_size = [0u32; N]; + let mut param_strides = [0u32; N]; + let param_values = std::array::from_fn(|i| param_values[i].to_vec()); + + let mut slices: u32 = 1; + for i in (0..N).rev() { + if param_res[i] < 1 { + panic!("PiecewiseLinear2D::new: parameter resolution must be >= 1!"); + } + param_size[i] = param_res[i] as u32; + param_strides[i] = if param_res[i] > 1 { slices } else { 0 }; + slices *= param_size[i]; + } + + let n_values = (x_size * y_size) as usize; + let mut new_data = vec![0.0; slices as usize * n_values]; + let mut marginal_cdf = if build_cdf { + vec![0.0; slices as usize * y_size as usize] + } else { + Vec::new() + }; + let mut conditional_cdf = if build_cdf { + vec![0.0; slices as usize * n_values] + } else { + Vec::new() + }; + + let mut data_offset = 0; + for slice in 0..slices as usize { + let slice_offset = slice * n_values; + let current_data = &data[data_offset..data_offset + n_values]; + let mut sum = 0.; + + // Construct conditional CDF + if normalize { + for y in 0..(y_size - 1) { + for x in 0..(x_size - 1) { + let i = (y * x_size + x) as usize; + let v00 = current_data[i] as f64; + let v10 = current_data[i + 1] as f64; + let v01 = current_data[i + x_size as usize] as f64; + let v11 = current_data[i + 1 + x_size as usize] as f64; + sum += 0.25 * (v00 + v10 + v01 + v11); + } + } + } + + let normalization = if normalize && sum > 0.0 { + 1.0 / sum as Float + } else { + 1.0 + }; + for k in 0..n_values { + new_data[slice_offset + k] = current_data[k] * normalization; + } + + if build_cdf { + let marginal_slice_offset = slice * y_size as usize; + // Construct marginal CDF + for y in 0..y_size as usize { + let mut cdf_sum = 0.0; + let i_base = y * x_size as usize; + conditional_cdf[slice_offset + i_base] = 0.0; + for x in 0..(x_size - 1) as usize { + let i = i_base + x; + cdf_sum += + 0.5 * (new_data[slice_offset + i] + new_data[slice_offset + i + 1]); + conditional_cdf[slice_offset + i + 1] = cdf_sum; + } + } + // Construct marginal CDF + marginal_cdf[marginal_slice_offset] = 0.0; + let mut marginal_sum = 0.0; + for y in 0..(y_size - 1) as usize { + let cdf1 = conditional_cdf[slice_offset + (y + 1) * x_size as usize - 1]; + let cdf2 = conditional_cdf[slice_offset + (y + 2) * x_size as usize - 1]; + marginal_sum += 0.5 * (cdf1 + cdf2); + marginal_cdf[marginal_slice_offset + y + 1] = marginal_sum; + } + } + data_offset += n_values; + } + + Self { + size, + inv_patch_size, + param_size, + param_strides, + param_values, + data: new_data, + marginal_cdf, + conditional_cdf, + } + } + + pub fn sample(&self, mut sample: Point2f, params: [Float; N]) -> PLSample { + sample = Point2f::new( + sample.x().clamp(0.0, ONE_MINUS_EPSILON), + sample.y().clamp(0.0, ONE_MINUS_EPSILON), + ); + + let (slice_offset, param_weights) = self.get_slice_info(params); + let slice_size = (self.size.x() * self.size.y()) as u32; + let marginal_size = self.size.y() as u32; + let conditional_size = slice_size; + let marginal_offset = slice_offset * marginal_size; + let conditional_offset = slice_offset * conditional_size; + let fetch_marginal = |idx: u32| { + self.lookup( + &self.marginal_cdf, + marginal_offset + idx, + marginal_size, + ¶m_weights, + ) + }; + let row = Self::find_interval(marginal_size, |idx| fetch_marginal(idx) <= sample.y()); + let marginal_cdf_row = fetch_marginal(row); + sample[1] -= marginal_cdf_row; + let r0 = self.lookup( + &self.conditional_cdf, + conditional_offset + (row + 1) * self.size.x() as u32 - 1, + conditional_size, + ¶m_weights, + ); + let r1 = self.lookup( + &self.conditional_cdf, + conditional_offset + (row + 2) * self.size.x() as u32 - 1, + conditional_size, + ¶m_weights, + ); + let delta_r = r1 - r0; + let is_const = delta_r.abs() < 1e-4 * (r0 + r1).abs(); + sample[1] /= if is_const { r0 + r1 } else { delta_r }; + if !is_const { + sample[1] = r0 - safe_sqrt(r0 * r0 - 2.0 * sample.y() * delta_r); + } else { + sample[1] *= 2.0; + } + sample[0] *= (1.0 - sample.y()) * r0 + sample.y() * r1; + let conditional_row_offset = conditional_offset + row * self.size.x() as u32; + let fetch_conditional = |idx: u32| { + let v0 = self.lookup( + &self.conditional_cdf, + conditional_row_offset + idx, + conditional_size, + ¶m_weights, + ); + let v1 = self.lookup( + &self.conditional_cdf, + conditional_row_offset + idx + self.size.x() as u32, + conditional_size, + ¶m_weights, + ); + (1.0 - sample.y()) * v0 + sample.y() * v1 + }; + let col = Self::find_interval(self.size.x() as u32, |idx| { + fetch_conditional(idx) <= sample.x() + }); + sample[0] -= fetch_conditional(col); + let offset = conditional_row_offset + col; + let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights); + let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights); + let v01 = self.lookup( + &self.data, + offset + self.size.x() as u32, + slice_size, + ¶m_weights, + ); + let v11 = self.lookup( + &self.data, + offset + self.size.x() as u32 + 1, + slice_size, + ¶m_weights, + ); + let c0 = (1.0 - sample.y()) * v00 + sample.y() * v01; + let c1 = (1.0 - sample.y()) * v10 + sample.y() * v11; + let delta_c = c1 - c0; + let is_const = delta_c.abs() < 1e-4 * (c0 + c1).abs(); + sample[0] /= if is_const { c0 + c1 } else { delta_c }; + if !is_const { + sample[0] = c0 - safe_sqrt(c0 * c0 - 2.0 * sample.x() * delta_c); + } else { + sample[0] *= 2.0; + } + let pdf = (1.0 - sample.x()) * c0 + sample.x() * c1; + let offset_point = Point2f::new(col as Float, row as Float); + + PLSample { + p: Point2f::new( + sample.x() * self.inv_patch_size.x() + offset_point.x() * self.inv_patch_size.x(), + sample.y() * self.inv_patch_size.y() + offset_point.y() * self.inv_patch_size.y(), + ), + pdf: pdf * self.inv_patch_size.x() * self.inv_patch_size.y(), + } + } + + pub fn invert(&self, mut p: Point2f, params: [Float; N]) -> PLSample { + let (slice_offset, param_weights) = self.get_slice_info(params); + + p[0] *= self.inv_patch_size.x(); + p[1] *= self.inv_patch_size.y(); + let col = (p.x() as i32).min(self.size.x() - 2); + let row = (p.y() as i32).min(self.size.y() - 2); + let frac = Point2f::new(p.x() - col as Float, p.y() - row as Float); + let slice_size = (self.size.x() * self.size.y()) as u32; + let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32; + let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights); + let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights); + let v01 = self.lookup( + &self.data, + offset + self.size.x() as u32, + slice_size, + ¶m_weights, + ); + let v11 = self.lookup( + &self.data, + offset + self.size.x() as u32 + 1, + slice_size, + ¶m_weights, + ); + let w1 = frac; + let w0 = Point2f::new(1.0 - w1.x(), 1.0 - w1.y()); + let c0 = w0.y() * v00 + w1.y() * v01; + let c1 = w0.y() * v10 + w1.y() * v11; + let pdf = w0.x() * c0 + w1.x() * c1; + let mut u = Point2f::new(0.0, 0.0); + u[0] = w1.x() * (c0 + 0.5 * w1.x() * (c1 - c0)); + let conditional_row_offset = slice_offset * slice_size + (row * self.size.x()) as u32; + let v0 = self.lookup( + &self.conditional_cdf, + conditional_row_offset + col as u32, + slice_size, + ¶m_weights, + ); + let v1 = self.lookup( + &self.conditional_cdf, + conditional_row_offset + col as u32 + self.size.x() as u32, + slice_size, + ¶m_weights, + ); + u[0] += (1.0 - u.y()) * v0 + u.y() * v1; + let r0 = self.lookup( + &self.conditional_cdf, + conditional_row_offset + self.size.x() as u32 - 1, + slice_size, + ¶m_weights, + ); + let r1 = self.lookup( + &self.conditional_cdf, + conditional_row_offset + self.size.x() as u32 * 2 - 1, + slice_size, + ¶m_weights, + ); + u[0] /= (1.0 - u.y()) * r0 + u.y() * r1; + u[1] = w1.y() * (r0 + 0.5 * w1.y() * (r1 - r0)); + let marginal_offset = slice_offset * self.size.y() as u32 + row as u32; + u[1] += self.lookup( + &self.marginal_cdf, + marginal_offset, + self.size.y() as u32, + ¶m_weights, + ); + + PLSample { + p: u, + pdf: pdf * (self.inv_patch_size.x() * self.inv_patch_size.y()), + } + } + + pub fn evaluate(&self, p: Point2f, params: [Float; N]) -> Float { + let (slice_offset, param_weights) = self.get_slice_info(params); + let pos = Point2f::new( + p.x() * self.inv_patch_size.x(), + p.y() * self.inv_patch_size.y(), + ); + let col = (pos.x() as i32).min(self.size.x() - 2); + let row = (pos.y() as i32).min(self.size.y() - 2); + let w1 = Point2f::new(pos.x() - col as Float, pos.y() - row as Float); + let w0 = Point2f::new(1.0 - w1.x(), 1.0 - w1.y()); + let slice_size = (self.size.x() * self.size.y()) as u32; + let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32; + let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights); + let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights); + let v01 = self.lookup( + &self.data, + offset + self.size.x() as u32, + slice_size, + ¶m_weights, + ); + let v11 = self.lookup( + &self.data, + offset + self.size.x() as u32 + 1, + slice_size, + ¶m_weights, + ); + + let pdf = w0.y() * (w0.x() * v00 + w1.x() * v10) + w1.y() * (w0.x() * v01 + w1.x() * v11); + pdf * self.inv_patch_size.x() * self.inv_patch_size.y() + } + + fn find_interval(size: u32, pred: impl Fn(u32) -> bool) -> u32 { + let mut first = 1u32; + let mut size = size - 1; + while size > 0 { + let half = size >> 1; + let middle = first + half; + if pred(middle) { + first = middle + 1; + size -= half + 1; + } else { + size = half; + } + } + first.saturating_sub(1) + } + + fn get_slice_info(&self, params: [Float; N]) -> (u32, [(Float, Float); N]) { + let mut param_weight = [(0.0, 0.0); N]; + let mut slice_offset = 0u32; + + for dim in 0..N { + if self.param_size[dim] == 1 { + param_weight[2 * dim] = (1.0, 0.0); + continue; + } + + let param_index = Self::find_interval(self.param_size[dim], |idx| { + self.param_values[dim][idx as usize] <= params[dim] + }); + + let p0 = self.param_values[dim][param_index as usize]; + let p1 = self.param_values[dim] + [(param_index as usize + 1).min(self.param_values[dim].len() - 1)]; + let w1 = if p1 != p0 { + ((params[dim] - p0) / (p1 - p0)).clamp(0.0, 1.0) + } else { + 0.0 + }; + param_weight[dim] = (1. - w1, w1); + slice_offset += self.param_strides[dim] * param_index; + } + + (slice_offset, param_weight) + } + + fn lookup( + &self, + data: &[Float], + i0: u32, + size: u32, + param_weight: &[(Float, Float); N], + ) -> Float { + let mut result: Float = 0.0; + let num_corners = 1usize << N; // 2^N + for mask in 0..num_corners { + let mut offset = 0u32; + let mut weight: Float = 1.0; + for d in 0..N { + let bit = (mask >> d) & 1; + if bit == 1 { + offset += self.param_strides[d] * size; + weight *= param_weight[d].1; + } else { + weight *= param_weight[d].0; + } + } + result += weight * data[(i0 + offset) as usize]; + } + result + } +} diff --git a/src/utils/scattering.rs b/src/utils/scattering.rs index 54b5934..33fe093 100644 --- a/src/utils/scattering.rs +++ b/src/utils/scattering.rs @@ -1,22 +1,117 @@ -use crate::utils::geometry::{Normal3f, Vector3f, Dot}; -use crate::core::pbrt::{Float, square}; +use super::math::safe_sqrt; +use super::sampling::sample_uniform_disk_polar; +use super::spectrum::{N_SPECTRUM_SAMPLES, SampledSpectrum}; +use crate::core::pbrt::{Float, PI, clamp_t, lerp}; +use crate::geometry::{ + Normal3f, Point2f, Vector2f, Vector3f, VectorLike, abs_cos_theta, cos_phi, cos2_theta, sin_phi, + tan2_theta, +}; +use crate::utils::math::square; + +use num::complex::Complex; + +#[derive(Debug, Default, Clone, Copy)] +pub struct TrowbridgeReitzDistribution { + alpha_x: Float, + alpha_y: Float, +} + +impl TrowbridgeReitzDistribution { + pub fn new(alpha_x: Float, alpha_y: Float) -> Self { + Self { alpha_x, alpha_y } + } + + pub fn d(&self, wm: Vector3f) -> Float { + let tan2_theta = tan2_theta(wm); + if tan2_theta.is_infinite() { + return 0.; + } + let cos4_theta = square(cos2_theta(wm)); + let e = + tan2_theta * (square(cos_phi(wm) / self.alpha_x) + square(sin_phi(wm) / self.alpha_y)); + 1.0 / (PI * self.alpha_x * self.alpha_y * cos4_theta * square(1. + e)) + } + + pub fn effectively_smooth(&self) -> bool { + self.alpha_x.max(self.alpha_y) < 1e-3 + } + + pub fn lambda(&self, w: Vector3f) -> Float { + let tan2_theta = tan2_theta(w); + if tan2_theta.is_infinite() { + return 0.; + } + let alpha2 = square(cos_phi(w) * self.alpha_x) + square(sin_phi(w) * self.alpha_y); + ((1. + alpha2 * tan2_theta).sqrt() - 1.) / 2. + } + + pub fn g(&self, wo: Vector3f, wi: Vector3f) -> Float { + 1. / (1. + self.lambda(wo) + self.lambda(wi)) + } + + pub fn g1(&self, w: Vector3f) -> Float { + 1. / (1. / self.lambda(w)) + } + + pub fn d_from_w(&self, w: Vector3f, wm: Vector3f) -> Float { + self.g1(w) / abs_cos_theta(w) * self.d(wm) * w.dot(wm).abs() + } + + pub fn pdf(&self, w: Vector3f, wm: Vector3f) -> Float { + self.d_from_w(w, wm) + } + + pub fn sample_wm(&self, w: Vector3f, u: Point2f) -> Vector3f { + let mut wh = Vector3f::new(self.alpha_x * w.x(), self.alpha_y * w.y(), w.z()).normalize(); + if wh.z() < 0. { + wh = -wh; + } + let t1 = if wh.z() < 0.99999 { + Vector3f::new(0., 0., 1.).cross(wh).normalize() + } else { + Vector3f::new(1., 0., 0.) + }; + let t2 = wh.cross(t1); + let mut p = sample_uniform_disk_polar(u); + let h = (1. - square(p.x())).sqrt(); + p[1] = lerp((1. + wh.z()) / 2., h, p.y()); + let pz = 0_f32.max(1. - Vector2f::from(p).norm_squared()); + let nh = p.x() * t1 + p.y() * t2 + pz * wh; + Vector3f::new( + self.alpha_x * nh.x(), + self.alpha_y * nh.y(), + nh.z().max(1e-6), + ) + .normalize() + } + + pub fn roughness_to_alpha(roughness: Float) -> Float { + roughness.sqrt() + } + + pub fn regularize(&mut self) { + if self.alpha_x < 0.3 { + self.alpha_x = clamp_t(2. * self.alpha_x, 0.1, 0.3); + } + if self.alpha_y < 0.3 { + self.alpha_y = clamp_t(2. * self.alpha_y, 0.1, 0.3); + } + } +} 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); + let mut cos_theta_i = Vector3f::from(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_i = (1.0 - square(cos_theta_i)).max(0.0_f32); let sin2_theta_t = sin2_theta_i / square(eta); // Handle total internal reflection @@ -29,3 +124,50 @@ pub fn refract(wi: Vector3f, n: Normal3f, eta_ratio: Float) -> Option<(Vector3f, let wt = -wi / eta + (cos_theta_i / eta - cos_theta_t) * Vector3f::from(n_interface); Some((wt, eta)) } + +pub fn reflect(wo: Vector3f, n: Normal3f) -> Vector3f { + -wo + Vector3f::from(2. * wo.dot(n.into()) * n) +} + +pub fn fr_dielectric(cos_theta_i: Float, eta: Float) -> Float { + let mut cos_safe = clamp_t(cos_theta_i, -1., 1.); + let mut eta_corr = eta; + if cos_theta_i < 0. { + eta_corr = 1. / eta_corr; + cos_safe = -cos_safe; + } + let sin2_theta_i = 1. - square(cos_safe); + let sin2_theta_t = sin2_theta_i / square(eta_corr); + if sin2_theta_t >= 1. { + return 1.; + } + let cos_theta_t = safe_sqrt(1. - sin2_theta_t); + let r_parl = (eta_corr * cos_safe - cos_theta_t) / (eta_corr * cos_safe + cos_theta_t); + let r_perp = (cos_safe - eta_corr * cos_theta_t) / (cos_safe + eta_corr * cos_theta_t); + + (square(r_parl) + square(r_perp)) / 2. +} + +pub fn fr_complex(cos_theta_i: Float, eta: Complex) -> Float { + let cos_corr = clamp_t(cos_theta_i, 0., 1.); + let sin2_theta_i = 1. - square(cos_corr); + let sin2_theta_t: Complex = (sin2_theta_i / square(eta)).into(); + let cos2_theta_t: Complex = (1. - sin2_theta_t).sqrt(); + + let r_parl = (eta * cos_corr - cos2_theta_t) / (eta * cos_corr + cos2_theta_t); + let r_perp = (cos_corr - eta * cos2_theta_t) / (cos_corr + eta * cos2_theta_t); + + (r_parl.norm() + r_perp.norm()) / 2. +} + +pub fn fr_complex_from_spectrum( + cos_theta_i: Float, + eta: SampledSpectrum, + k: SampledSpectrum, +) -> SampledSpectrum { + let mut result = SampledSpectrum::default(); + for i in 0..N_SPECTRUM_SAMPLES { + result[i] = fr_complex(cos_theta_i, Complex::new(eta[i], k[i])); + } + result +} diff --git a/src/utils/spectrum.rs b/src/utils/spectrum.rs index 84d2ab2..a2329e7 100644 --- a/src/utils/spectrum.rs +++ b/src/utils/spectrum.rs @@ -1,16 +1,18 @@ -use std::fmt; -use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg, Index, IndexMut}; use once_cell::sync::Lazy; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, +}; use std::sync::Arc; -use crate::core::pbrt::Float; -use crate::core::cie; -use super::color::{RGB, XYZ, RGBSigmoidPolynomial}; +use super::color::{RGB, RGBSigmoidPolynomial, XYZ}; use super::colorspace::RGBColorspace; +use crate::core::cie; +use crate::core::pbrt::Float; pub const CIE_Y_INTEGRAL: Float = 106.856895; pub const N_SPECTRUM_SAMPLES: usize = 1200; -pub const LAMBDA_MIN: i32 = 360; +pub const LAMBDA_MIN: i32 = 360; pub const LAMBDA_MAX: i32 = 830; #[derive(Debug, Copy, Clone)] @@ -19,7 +21,11 @@ pub struct SampledSpectrum { } impl Default for SampledSpectrum { - fn default() -> Self { Self { values: [0.0; N_SPECTRUM_SAMPLES] } } + fn default() -> Self { + Self { + values: [0.0; N_SPECTRUM_SAMPLES], + } + } } impl SampledSpectrum { @@ -37,6 +43,10 @@ impl SampledSpectrum { s } + pub fn is_black(&self) -> bool { + self.values.iter().all(|&sample| sample == 0.0) + } + pub fn has_nans(&self) -> bool { self.values.iter().any(|&v| v.is_nan()) } @@ -46,7 +56,9 @@ impl SampledSpectrum { } pub fn max_component_value(&self) -> Float { - self.values.iter().fold(Float::NEG_INFINITY, |a, &b| a.max(b)) + self.values + .iter() + .fold(Float::NEG_INFINITY, |a, &b| a.max(b)) } pub fn average(&self) -> Float { @@ -55,8 +67,12 @@ impl SampledSpectrum { pub fn safe_div(&self, rhs: SampledSpectrum) -> Self { let mut r = SampledSpectrum::new(0.0); - for i in 0..N_SPECTRUM_SAMPLES { - r.values[i] = if rhs[i] != 0.0 {self.values[i] / rhs.values[i]} else { 0.0 } + for i in 0..N_SPECTRUM_SAMPLES { + r.values[i] = if rhs[i] != 0.0 { + self.values[i] / rhs.values[i] + } else { + 0.0 + } } r } @@ -70,7 +86,7 @@ impl SampledSpectrum { (*self * x).safe_div(pdf).average(), (*self * y).safe_div(pdf).average(), (*self * z).safe_div(pdf).average(), - ) / CIE_Y_INTEGRAL + ) / CIE_Y_INTEGRAL } pub fn to_rgb(&self, lambda: &SampledWavelengths, c: &RGBColorspace) -> RGB { @@ -79,14 +95,17 @@ impl SampledSpectrum { } } - impl Index for SampledSpectrum { type Output = Float; - fn index(&self, i: usize) -> &Self::Output { &self.values[i] } + fn index(&self, i: usize) -> &Self::Output { + &self.values[i] + } } impl IndexMut for SampledSpectrum { - fn index_mut(&mut self, i: usize) -> &mut Self::Output { &mut self.values[i] } + fn index_mut(&mut self, i: usize) -> &mut Self::Output { + &mut self.values[i] + } } impl Add for SampledSpectrum { @@ -248,7 +267,7 @@ impl SampledWavelengths { pub fn secondary_terminated(&self) -> bool { for i in 1..N_SPECTRUM_SAMPLES { if self.pdf[i] != 0.0 { - return false + return false; } } true @@ -287,7 +306,9 @@ impl SampledWavelengths { } pub fn visible_wavelengths_pdf(lambda: Float) -> Float { - if lambda < 360.0 || lambda > 830.0 { return 0.0; } + if lambda < 360.0 || lambda > 830.0 { + return 0.0; + } 0.0039398042 / (Float::cosh(0.0072 * (lambda - 538.0))).sqrt() } @@ -297,7 +318,9 @@ impl SampledWavelengths { for i in 0..N_SPECTRUM_SAMPLES { let mut up = u + i as Float / N_SPECTRUM_SAMPLES as Float; - if up > 1.0 { up -= 1.0; } + if up > 1.0 { + up -= 1.0; + } lambda[i] = Self::sample_visible_wavelengths(up); pdf[i] = Self::visible_wavelengths_pdf(lambda[i]); } @@ -307,11 +330,15 @@ impl SampledWavelengths { impl Index for SampledWavelengths { type Output = Float; - fn index(&self, i: usize) -> &Self::Output { &self.lambda[i] } + fn index(&self, i: usize) -> &Self::Output { + &self.lambda[i] + } } impl IndexMut for SampledWavelengths { - fn index_mut(&mut self, i: usize) -> &mut Self::Output { &mut self.lambda[i] } + fn index_mut(&mut self, i: usize) -> &mut Self::Output { + &mut self.lambda[i] + } } #[derive(Debug, Clone)] @@ -353,10 +380,12 @@ impl Spectrum { } pub fn to_xyz(&self) -> XYZ { - XYZ::new(inner_product(&spectra::X, self), + XYZ::new( + inner_product(&spectra::X, self), inner_product(&spectra::Y, self), - inner_product(&spectra::Z, self))/CIE_Y_INTEGRAL - } + inner_product(&spectra::Z, self), + ) / CIE_Y_INTEGRAL + } } pub fn inner_product(f: &Spectrum, g: &Spectrum) -> Float { @@ -367,20 +396,23 @@ pub fn inner_product(f: &Spectrum, g: &Spectrum) -> Float { integral } - #[derive(Debug, Clone, Copy)] pub struct ConstantSpectrum { c: Float, } impl ConstantSpectrum { - pub fn new(c: Float) -> Self { Self { c } } + pub fn new(c: Float) -> Self { + Self { c } + } pub fn sample_at(&self, _lambda: Float) -> Float { self.c } - fn max_value(&self) -> Float { self.c } + fn max_value(&self) -> Float { + self.c + } } #[derive(Debug, Clone, Copy)] @@ -390,7 +422,9 @@ pub struct RGBAlbedoSpectrum { impl RGBAlbedoSpectrum { pub fn new(cs: &RGBColorspace, rgb: RGB) -> Self { - Self { rsp: cs.to_rgb_coeffs(rgb) } + Self { + rsp: cs.to_rgb_coeffs(rgb), + } } pub fn sample_at(&self, lambda: Float) -> Float { @@ -420,8 +454,15 @@ 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 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) } + 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) @@ -442,7 +483,9 @@ pub struct RGBIlluminantSpectrum { impl RGBIlluminantSpectrum { pub fn sample_at(&self, lambda: Float) -> Float { match &self.illuminant { - Some(illuminant) => self.scale * self.rsp.evaluate(lambda) * illuminant.sample_at(lambda), + Some(illuminant) => { + self.scale * self.rsp.evaluate(lambda) * illuminant.sample_at(lambda) + } None => 0.0, } } @@ -455,7 +498,6 @@ impl RGBIlluminantSpectrum { } } - #[derive(Debug, Clone)] pub struct DenselySampledSpectrum { lambda_min: i32, @@ -466,12 +508,18 @@ pub struct DenselySampledSpectrum { impl DenselySampledSpectrum { pub fn new(lambda_min: i32, lambda_max: i32) -> Self { let n_values = (lambda_max - lambda_min + 1).max(0) as usize; - Self { lambda_min, lambda_max, values: vec![0.0; n_values] } + Self { + lambda_min, + lambda_max, + values: vec![0.0; n_values], + } } pub fn from_spectrum(spec: &Spectrum, lambda_min: i32, lambda_max: i32) -> Self { let mut s = Self::new(lambda_min, lambda_max); - if s.values.is_empty() { return s; } + if s.values.is_empty() { + return s; + } for lambda in lambda_min..=lambda_max { let index = (lambda - lambda_min) as usize; s.values[index] = spec.sample_at(lambda as Float); @@ -479,9 +527,14 @@ impl DenselySampledSpectrum { s } - pub fn from_function(f: F, lambda_min: i32, lambda_max: i32) -> Self where F: Fn(Float) -> Float { + pub fn from_function(f: F, lambda_min: i32, lambda_max: i32) -> Self + where + F: Fn(Float) -> Float, + { let mut s = Self::new(lambda_min, lambda_max); - if s.values.is_empty() { return s; } + if s.values.is_empty() { + return s; + } for lambda in lambda_min..=lambda_max { let index = (lambda - lambda_min) as usize; s.values[index] = f(lambda as Float); @@ -498,8 +551,48 @@ impl DenselySampledSpectrum { } } + pub fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum { + let mut s = SampledSpectrum::default(); + + for i in 0..N_SPECTRUM_SAMPLES { + let offset = lambda[i].round() as usize - LAMBDA_MIN as usize; + if offset >= self.values.len() { + s[i] = 0.; + } else { + s[i] = self.values[offset]; + } + } + s + } + pub fn max_value(&self) -> Float { - self.values.iter().fold(Float::MIN, |a,b| a.max(*b)) + self.values.iter().fold(Float::MIN, |a, b| a.max(*b)) + } + + pub fn min_component_value(&self) -> Float { + self.values.iter().fold(Float::INFINITY, |a, &b| a.min(b)) + } + + pub fn max_component_value(&self) -> Float { + self.values + .iter() + .fold(Float::NEG_INFINITY, |a, &b| a.max(b)) + } + + pub fn average(&self) -> Float { + self.values.iter().sum::() / (N_SPECTRUM_SAMPLES as Float) + } + + pub fn safe_div(&self, rhs: SampledSpectrum) -> Self { + let mut r = Self::new(1, 1); + for i in 0..N_SPECTRUM_SAMPLES { + r.values[i] = if rhs[i] != 0.0 { + self.values[i] / rhs.values[i] + } else { + 0.0 + } + } + r } } @@ -510,7 +603,10 @@ pub struct PiecewiseLinearSpectrum { impl PiecewiseLinearSpectrum { pub fn from_interleaved(data: &[Float]) -> Self { - assert!(data.len() % 2 == 0, "Interleaved data must have an even number of elements"); + assert!( + data.len() % 2 == 0, + "Interleaved data must have an even number of elements" + ); let mut samples = Vec::new(); for pair in data.chunks(2) { samples.push((pair[0], pair[1])); @@ -535,7 +631,7 @@ impl PiecewiseLinearSpectrum { let i = self.samples.partition_point(|s| s.0 < lambda); let s1 = self.samples[i - 1]; let s2 = self.samples[i]; - + let t = (lambda - s1.0) / (s2.0 - s1.0); (1.0 - t) * s1.1 + t * s2.1 } @@ -561,17 +657,23 @@ impl BlackbodySpectrum { normalization_factor: if max_val > 0.0 { 1.0 / max_val } else { 0.0 }, } } - + fn planck_law(lambda_nm: Float, temp: Float) -> Float { - if temp <= 0.0 { return 0.0; } + if temp <= 0.0 { + return 0.0; + } let lambda_m = lambda_nm * 1e-9; // Convert nm to meters let c1 = 2.0 * Self::H * Self::C * Self::C; let c2 = (Self::H * Self::C) / Self::KB; - + let numerator = c1 / lambda_m.powi(5); let denominator = (c2 / (lambda_m * temp)).exp() - 1.0; - - if denominator.is_infinite() { 0.0 } else { numerator / denominator } + + if denominator.is_infinite() { + 0.0 + } else { + numerator / denominator + } } fn sample_at(&self, lambda: Float) -> Float { @@ -580,7 +682,9 @@ impl BlackbodySpectrum { } #[derive(Debug, Clone, Copy)] -pub struct RGBSpectrum { pub c: [Float; 3] } +pub struct RGBSpectrum { + pub c: [Float; 3], +} #[derive(Debug, Clone, Copy)] pub struct RGBUnboundedSpectrum(pub RGBSpectrum); @@ -614,5 +718,4 @@ pub mod spectra { ); Spectrum::DenselySampled(dss) }); - - } +} diff --git a/src/utils/splines.rs b/src/utils/splines.rs new file mode 100644 index 0000000..fda9468 --- /dev/null +++ b/src/utils/splines.rs @@ -0,0 +1,76 @@ +use crate::core::pbrt::{Float, lerp}; +use crate::geometry::{Bounds3f, Lerp, Point3f, Vector3f, VectorLike}; +use num_traits::Num; + +fn bounds_cubic_bezier(cp: &[Point3f]) -> Bounds3f { + Bounds3f::from_points(cp[0], cp[1]).union(Bounds3f::from_points(cp[2], cp[3])) +} + +pub fn bound_cubic_bezier(cp: &[Point3f], u_min: Float, u_max: Float) -> Bounds3f { + if u_min == 0. && u_max == 1. { + return bounds_cubic_bezier(cp); + } + let cp_seg = cubic_bezier_control_points(cp, u_min, u_max); + bounds_cubic_bezier(&cp_seg) +} + +pub fn cubic_bezier_control_points(cp: &[Point3f], u_min: Float, u_max: Float) -> [Point3f; 4] { + [ + blossom_cubic_bezier(cp, u_min, u_max, u_min), + blossom_cubic_bezier(cp, u_min, u_min, u_max), + blossom_cubic_bezier(cp, u_min, u_max, u_max), + blossom_cubic_bezier(cp, u_max, u_max, u_max), + ] +} + +fn blossom_cubic_bezier(p: &[P], u0: F, u1: F, u2: F) -> P +where + F: Copy + Num, + P: Lerp, +{ + let a: [P; 3] = [ + lerp(u0, p[0], p[1]), + lerp(u0, p[1], p[2]), + lerp(u0, p[2], p[3]), + ]; + let b: [P; 2] = [lerp(u1, a[0], a[1]), lerp(u1, a[1], a[2])]; + lerp(u2, b[0], b[1]) +} + +pub fn subdivide_cubic_bezier(cp: &[Point3f]) -> [Point3f; 7] { + let v: Vec = cp.iter().map(|&p| p.into()).collect(); + let v01 = (v[0] + v[1]) / 2.0; + let v12 = (v[1] + v[2]) / 2.0; + let v23 = (v[2] + v[3]) / 2.0; + + let v012 = (v01 + v12) / 2.0; + let v123 = (v12 + v23) / 2.0; + + let v0123 = (v012 + v123) / 2.0; + [ + cp[0], + v01.into(), + v012.into(), + v0123.into(), + v123.into(), + v23.into(), + cp[3], + ] +} + +pub fn evaluate_cubic_bezier(cp: &[Point3f], u: Float) -> (Point3f, Vector3f) { + let cp1 = [ + lerp(u, cp[0], cp[1]), + lerp(u, cp[1], cp[2]), + lerp(u, cp[2], cp[3]), + ]; + let cp2 = [lerp(u, cp1[0], cp1[1]), lerp(u, cp1[1], cp1[2])]; + let deriv: Vector3f; + if (cp2[1] - cp2[0]).norm_squared() > 0. { + deriv = (cp2[1] - cp2[0]) * 3.; + } else { + deriv = cp[3] - cp[0] + } + + (lerp(u, cp2[0], cp2[1]), deriv) +} diff --git a/src/utils/transform.rs b/src/utils/transform.rs index e539ea8..318e760 100644 --- a/src/utils/transform.rs +++ b/src/utils/transform.rs @@ -1,473 +1,37 @@ -use num_traits::{Num, One, Zero, Signed, Float as NumFloat}; +use num_traits::Float as NumFloat; +use std::error::Error; +use std::fmt::{self, Display}; 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::math::{SquareMatrix, safe_acos}; use super::quaternion::Quaternion; +use crate::core::pbrt::{Float, gamma}; +use crate::geometry::{ + Bounds3f, Normal, Normal3f, Point, Point3f, Point3fi, Ray, Vector, Vector3f, Vector3fi, + VectorLike, +}; +use crate::utils::error::InversionError; -#[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 { +#[derive(Debug, 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 } +impl Transform { + pub fn new(m: SquareMatrix, m_inv: SquareMatrix) -> Self { + Self { m, 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 from_matrix(m: SquareMatrix) -> Result> { + let inv = m.inverse()?; + Ok(Self { m, m_inv: inv }) } pub fn identity() -> Self { let m: SquareMatrix = SquareMatrix::identity(); - let m_inv = m.inverse().expect("Singular matrix"); + let m_inv = m.clone(); Self { m, m_inv } } @@ -476,19 +40,31 @@ impl Transform { } pub fn inverse(&self) -> Self { - Self { m: self.m_inv, m_inv: self.m} + Self { + m: self.m_inv.clone(), + m_inv: self.m.clone(), + } } pub fn apply_inverse(&self, p: Point) -> Point { - *self * p + self.clone() * p } pub fn apply_inverse_vector(&self, v: Vector) -> Vector { - *self * v + self.clone() * v } - pub fn apply_inverse_normal(&self, v: Normal) -> Normal { - *self * v + pub fn apply_inverse_normal(&self, n: Normal) -> Normal { + self.clone() * n + } + + pub fn swaps_handedness(&self) -> bool { + let s = SquareMatrix::::new([ + [self.m[0][0], self.m[0][1], self.m[0][2]], + [self.m[1][0], self.m[1][1], self.m[1][2]], + [self.m[2][0], self.m[2][1], self.m[2][2]], + ]); + return s.determinant() < T::zero(); } } @@ -503,9 +79,9 @@ impl Transform { 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) + return Point3f::new(xp, yp, zp); } else { - return Point3f::new(xp/wp, yp/wp, zp/wp) + return Point3f::new(xp / wp, yp / wp, zp / wp); } } @@ -513,15 +89,15 @@ impl Transform { 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 xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; if wp == 1. { - return Vector3f::new(xp, yp, zp) + return Vector3f::new(xp, yp, zp); } else { - return Vector3f::new(xp/wp, yp/wp, zp/wp) + return Vector3f::new(xp / wp, yp / wp, zp / wp); } } @@ -529,23 +105,32 @@ impl Transform { 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 xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; if wp == 1. { - return Normal3f::new(xp, yp, zp) + return Normal3f::new(xp, yp, zp); } else { - return Normal3f::new(xp/wp, yp/wp, zp/wp) + return Normal3f::new(xp / wp, yp / wp, zp / wp); } } + pub fn apply_to_bounds(&self, b: Bounds3f) -> Bounds3f { + let mut new_bounds = Bounds3f::default(); + for i in 0..8 { + let corner = self.apply_to_point(b.corner(i)); + new_bounds = new_bounds.union_point(corner); + } + new_bounds + } + 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 dt = r.d.abs().dot(o.error()) / norm_squared; let offset = Vector3fi::new_from_vector(r.d * dt); o = o + offset; @@ -561,25 +146,65 @@ impl Transform { 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 xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; 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(); + 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) + return Point3fi::new_with_error(Point([xp / wp, yp / wp, zp / wp]), p_error); + } + } + + pub fn apply_to_vector_interval(&self, vi: &Vector3fi) -> Vector3fi { + let v = vi.midpoint(); + let x = v.x(); + let y = v.y(); + let z = v.z(); + + // Transform the midpoint of the vector interval + let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; + + let mut v_error = Vector3f::default(); + + // Propagate the error, ignoring the translational part of the matrix + if vi.is_exact() { + v_error[0] = gamma(3) + * ((self.m[0][0] * x).abs() + (self.m[0][1] * y).abs() + (self.m[0][2] * z).abs()); + v_error[1] = gamma(3) + * ((self.m[1][0] * x).abs() + (self.m[1][1] * y).abs() + (self.m[1][2] * z).abs()); + v_error[2] = gamma(3) + * ((self.m[2][0] * x).abs() + (self.m[2][1] * y).abs() + (self.m[2][2] * z).abs()); + } + + if wp == 1. { + return Vector3fi::new_with_error(Vector3f::new(xp, yp, zp), v_error); + } else { + return Vector3fi::new_with_error(Vector3f::new(xp / wp, yp / wp, zp / wp), v_error); } } @@ -590,19 +215,22 @@ impl Transform { 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; + quat.v[1] = (self.m[0][2] - self.m[2][0]) * s; + quat.v[2] = (self.m[1][0] - self.m[0][1]) * s; } else { - let nxt = [1, 2, 0]; + 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(); @@ -622,26 +250,21 @@ impl Transform { 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], - ], - }, + m: SquareMatrix::new([ + [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::new([ + [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.], @@ -651,18 +274,17 @@ impl Transform { ]); let m_inv = SquareMatrix::new([ - [1./x, 0., 0., 0.], - [0., 1./y, 0., 0.], - [0., 0., 1./z, 0.], + [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)) -} + 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(); @@ -687,11 +309,12 @@ impl Transform { 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()) + Transform::new(m.clone(), 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 { @@ -702,15 +325,18 @@ impl Transform { let u = refl - from; let v = refl - to; + let uu = u.dot(u); + let vv = v.dot(v); + let uv = u.dot(v); 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]; + r[i][j] = k - 2. / uu * u[i] * u[j] - 2. * vv * v[i] * v[j] + + 4. * uv / (uu * vv) * v[i] * u[j]; } } - Transform::new(&r, &r.transpose()) + Transform::new(r.clone(), r.transpose()) } pub fn rotate_x(theta: Float) -> Self { @@ -722,7 +348,10 @@ impl Transform { [0., sin_theta, cos_theta, 0.], [0., 0., 0., 1.], ]); - Self { m, m_inv: m.transpose() } + Self { + m: m.clone(), + m_inv: m.transpose(), + } } pub fn rotate_y(theta: Float) -> Self { @@ -734,7 +363,10 @@ impl Transform { [-sin_theta, 0., cos_theta, 0.], [0., 0., 0., 1.], ]); - Self { m, m_inv: m.transpose() } + Self { + m: m.clone(), + m_inv: m.transpose(), + } } pub fn rotate_z(theta: Float) -> Self { @@ -746,12 +378,15 @@ impl Transform { [0., 0., 1., 0.], [0., 0., 0., 1.], ]); - Self { m, m_inv: m.transpose() } + Self { + m: m.clone(), + 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; + let mut m = self.m.clone(); for i in 0..3 { m[i][3] = 0.; m[3][i] = m[i][3]; @@ -763,23 +398,31 @@ impl Transform { 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.; + let rit = r + .transpose() + .inverse() + .expect("Transform is not decomposable"); + let rnext = (r.clone() + rit.clone()) / 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]); + let n = (r[i][0] - rnext[i][0]).abs() + + (r[i][1] - rnext[i][1]).abs() + + (r[i][2] - rnext[i][2]).abs(); norm = norm.max(n); } r = rnext; count += 1; if count > 100 || norm < 0.0001 { - break + break; } } - let s = r.transpose().inverse().expect("Part of decomposition is singular") * m; + let s = r + .transpose() + .inverse() + .expect("Part of decomposition is singular") + * m; (t, r, s) } - } impl PartialEq for Transform { @@ -788,26 +431,43 @@ impl PartialEq for Transform { } } -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 Transform { + type Output = Transform; + + fn mul(self, rhs: Transform) -> Self::Output { + Transform { + m: self.m * rhs.m, + m_inv: rhs.m_inv * self.m_inv, + } } } -impl Mul> for Float +impl<'a, 'b, T> Mul<&'b Transform> for &'a Transform +where + T: NumFloat, { + type Output = Transform; + fn mul(self, rhs: &'b Transform) -> Self::Output { + Transform { + m: self.m.clone() * rhs.m.clone(), + m_inv: rhs.m_inv.clone() * self.m_inv.clone(), + } + } +} + +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 } + Transform { + m: rhs.m * self, + m_inv: rhs.m_inv * self, + } } } impl Mul> for Transform -where - T: NumFloat +where + T: NumFloat, { type Output = Point; fn mul(self, p: Point) -> Self::Output { @@ -819,14 +479,14 @@ where if wp == T::one() { Point([xp, yp, zp]) } else { - Point([xp/wp, yp/wp, zp/wp]) + Point([xp / wp, yp / wp, zp / wp]) } } } impl Mul> for Transform -where - T: NumFloat +where + T: NumFloat, { type Output = Vector; fn mul(self, p: Vector) -> Self::Output { @@ -838,14 +498,14 @@ where if wp == T::one() { Vector([xp, yp, zp]) } else { - Vector([xp/wp, yp/wp, zp/wp]) + Vector([xp / wp, yp / wp, zp / wp]) } } } impl Mul> for Transform -where - T: NumFloat +where + T: NumFloat, { type Output = Normal; fn mul(self, p: Normal) -> Self::Output { @@ -857,7 +517,7 @@ where if wp == T::one() { Normal([xp, yp, zp]) } else { - Normal([xp/wp, yp/wp, zp/wp]) + Normal([xp / wp, yp / wp, zp / wp]) } } } @@ -874,7 +534,7 @@ impl From for Transform { 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); @@ -893,11 +553,10 @@ impl From for Transform { // For a pure rotation, the inverse is the transpose. let m_inv_sq = m_sq.transpose(); - Transform::new(&m_sq, &m_inv_sq) + Transform::new(m_sq, m_inv_sq) } } - #[derive(Default, Debug, Copy, Clone)] pub struct DerivativeTerm { kc: Float, @@ -912,7 +571,12 @@ impl DerivativeTerm { } pub fn from_matrix(m: [Float; 4]) -> Self { - Self { kc: m[0], kx: m[1], ky: m[2], kz: m[3] } + Self { + kc: m[0], + kx: m[1], + ky: m[2], + kz: m[3], + } } } @@ -934,27 +598,53 @@ pub struct AnimatedTransform { } impl AnimatedTransform { - pub fn new(start_transform: &Transform, start_time: Float, end_transform: &Transform, end_time: Float) -> Option { + pub fn new( + start_transform: &Transform, + start_time: Float, + end_transform: &Transform, + end_time: Float, + ) -> Self { let actually_animated = start_transform != end_transform; if !actually_animated { - return None + return Self { + start_transform: start_transform.clone(), + end_transform: end_transform.clone(), + start_time, + end_time, + actually_animated: false, + t: [Vector3f::default(); 2], + r: [Quaternion::default(); 2], + s: std::array::from_fn(|_| SquareMatrix::default()), + has_rotation: false, + c1: [DerivativeTerm::default(); 3], + c2: [DerivativeTerm::default(); 3], + c3: [DerivativeTerm::default(); 3], + c4: [DerivativeTerm::default(); 3], + c5: [DerivativeTerm::default(); 3], + }; } - let (t0, r_temp, s0) = start_transform.decompose(); + let (t0, r_temp, s0) = start_transform.decompose(); let mut rm = r_temp; - let r0 = Transform::from_matrix(&rm).to_quaternion(); + let scale_transform = Transform::from_matrix(rm).expect("Scaling matrix should invertible"); + let r0 = scale_transform.to_quaternion(); let (t1, r_temp, s1) = end_transform.decompose(); rm = r_temp; - let mut r1 = Transform::from_matrix(&rm).to_quaternion(); + + let scale_transform = Transform::from_matrix(rm).expect("Scaling matrix should invertible"); + let mut r1 = scale_transform.to_quaternion(); + if r0.dot(r1) < 0. { - r1 -= r1; + 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 (c1, c2, c3, c4, c5) = 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]; @@ -999,574 +689,1124 @@ impl AnimatedTransform { 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)); + (-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)); + -(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); + -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)); + -(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); + 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); + -(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)); + 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); + 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)); + -(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); + -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); + 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); + 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); + -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)); + 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 + 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, + ); + (c1, c2, c3, c4, c5) + } else { + ( + std::array::from_fn(|_| DerivativeTerm::default()), + std::array::from_fn(|_| DerivativeTerm::default()), + std::array::from_fn(|_| DerivativeTerm::default()), + std::array::from_fn(|_| DerivativeTerm::default()), + std::array::from_fn(|_| DerivativeTerm::default()), + ) + }; + return 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, + }; } 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.start_transform.apply_to_point(p); + } 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 { + pub fn apply_vector(&self, v: 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); + return self.start_transform.apply_to_vector(v); + } else if time >= self.end_time { + return self.end_transform.apply_to_vector(v); } let t = self.interpolate(time); - t.apply_to_vector(p) + t.apply_to_vector(v) } 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 { + } 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 } + 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]; @@ -1574,88 +1814,74 @@ impl AnimatedTransform { let rotate = Quaternion::slerp(dt, self.r[0], self.r[1]); let scale = (1.0 - dt) * self.s[0] + dt * self.s[1]; + let scale_transform = + Transform::from_matrix(scale).expect("Scale matrix is not inversible"); - // Return interpolated matrix as product of interpolated components - return Transform::translate(trans) * Transform::from(rotate) * Transform::from_matrix(&scale); + return Transform::translate(trans) * Transform::from(rotate) * scale_transform; } 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) + 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) + 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) + return self.interpolate(time).apply_inverse_normal(n); } } -#[cfg(test)] -mod tests { - use super::*; +pub fn look_at( + pos: Point3f, + look: Point3f, + up: Point3f, +) -> Result, InversionError> { + let mut world_from_camera: SquareMatrix = SquareMatrix::default(); + // Initialize fourth column of viewing matrix + world_from_camera[0][3] = pos.x(); + world_from_camera[1][3] = pos.y(); + world_from_camera[2][3] = pos.z(); + world_from_camera[3][3] = 1.; - 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 - ); - } - } + // Initialize first three columns of viewing matrix + let dir = (look - pos).normalize(); + if Vector3f::from(up).normalize().cross(dir).norm() == 0. { + panic!( + "LookAt: \"up\" vector ({}, {}, {}) and viewing direction ({}, {}, {}) passed to LookAt are pointing in the same direction.", + up.x(), + up.y(), + up.z(), + dir.x(), + dir.y(), + dir.z() + ); } + let right = Vector3f::from(up).normalize().cross(dir).normalize(); + let new_up = dir.cross(right); + world_from_camera[0][0] = right.x(); + world_from_camera[1][0] = right.y(); + world_from_camera[2][0] = right.z(); + world_from_camera[3][0] = 0.; + world_from_camera[0][1] = new_up.x(); + world_from_camera[1][1] = new_up.y(); + world_from_camera[2][1] = new_up.z(); + world_from_camera[3][1] = 0.; + world_from_camera[0][2] = dir.x(); + world_from_camera[1][2] = dir.y(); + world_from_camera[2][2] = dir.z(); + world_from_camera[3][2] = 0.; - #[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); - } + let camera_from_world = world_from_camera.inverse()?; + Ok(Transform::new(camera_from_world, world_from_camera)) }