pbrt/src/utils/color.rs

998 lines
22 KiB
Rust

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 crate::geometry::Point2f;
use crate::spectra::Spectrum;
use crate::utils::math::SquareMatrix;
use once_cell::sync::Lazy;
use enum_dispatch::enum_dispatch;
pub trait Triplet {
fn from_triplet(c1: Float, c2: Float, c3: Float) -> Self;
}
#[derive(Debug, Clone)]
pub struct XYZ {
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 {
pub fn new(x: Float, y: Float, z: Float) -> Self {
Self { x, y, z }
}
pub fn x(&self) -> Float {
self.x
}
pub fn y(&self) -> Float {
self.y
}
pub fn z(&self) -> Float {
self.z
}
pub fn average(&self) -> Float {
(self.x + self.y + self.z) / 3.0
}
pub fn xy(&self) -> Point2f {
let sum = self.x + self.y + self.z;
Point2f::new(self.x / sum, self.y / sum)
}
pub fn from_xyy(xy: Point2f, y: Option<Float>) -> Self {
let scale: Float;
if let Some(y_val) = y {
scale = y_val;
} else {
scale = 1.;
}
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(),
)
}
}
impl Index<usize> for XYZ {
type Output = Float;
fn index(&self, index: usize) -> &Self::Output {
debug_assert!(index < 3);
match index {
0 => &self.x,
1 => &self.y,
_ => &self.z,
}
}
}
impl IndexMut<usize> for XYZ {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
debug_assert!(index < 3);
match index {
0 => &mut self.x,
1 => &mut self.y,
_ => &mut self.z,
}
}
}
impl Neg for XYZ {
type Output = Self;
fn neg(self) -> Self {
Self {
x: -self.x,
y: -self.y,
z: -self.z,
}
}
}
impl Add for XYZ {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl AddAssign for XYZ {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl Sub for XYZ {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl SubAssign for XYZ {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl Sub<XYZ> for Float {
type Output = XYZ;
fn sub(self, rhs: XYZ) -> XYZ {
XYZ::new(self - rhs.x, self - rhs.y, self - rhs.z)
}
}
impl Mul for XYZ {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
z: self.z * rhs.z,
}
}
}
impl MulAssign for XYZ {
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
self.z *= rhs.z;
}
}
impl Mul<Float> for XYZ {
type Output = Self;
fn mul(self, rhs: Float) -> Self {
Self {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
}
}
}
impl MulAssign<Float> for XYZ {
fn mul_assign(&mut self, rhs: Float) {
self.x *= rhs;
self.y *= rhs;
self.z *= rhs;
}
}
impl Mul<XYZ> for Float {
type Output = XYZ;
fn mul(self, rhs: XYZ) -> XYZ {
rhs * self
}
}
impl Div for XYZ {
type Output = Self;
fn div(self, rhs: Self) -> Self {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
z: self.z / rhs.z,
}
}
}
impl DivAssign for XYZ {
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
self.z /= rhs.z;
}
}
impl Div<Float> for XYZ {
type Output = Self;
fn div(self, rhs: Float) -> Self {
debug_assert_ne!(rhs, 0.0, "Division by zero.");
let inv = 1.0 / rhs;
self * inv
}
}
impl DivAssign<Float> for XYZ {
fn div_assign(&mut self, rhs: Float) {
debug_assert_ne!(rhs, 0.0, "Division by zero.");
let inv = 1.0 / rhs;
*self *= inv;
}
}
impl fmt::Display for XYZ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[ {}, {}, {} ]", self.x, self.y, self.z)
}
}
#[derive(Debug, Copy, Clone)]
pub struct RGB {
pub r: Float,
pub g: Float,
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 {
Self { r, g, b }
}
pub fn average(&self) -> Float {
(self.r + self.g + self.b) / 3.0
}
pub fn max(&self) -> Float {
self.r.max(self.g).max(self.b)
}
}
impl Index<usize> for RGB {
type Output = Float;
fn index(&self, index: usize) -> &Self::Output {
debug_assert!(index < 3);
match index {
0 => &self.r,
1 => &self.g,
_ => &self.b,
}
}
}
impl IndexMut<usize> for RGB {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
debug_assert!(index < 3);
match index {
0 => &mut self.r,
1 => &mut self.g,
_ => &mut self.b,
}
}
}
impl Neg for RGB {
type Output = Self;
fn neg(self) -> Self {
Self {
r: -self.r,
g: -self.g,
b: -self.b,
}
}
}
impl Add for RGB {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self {
r: self.r + rhs.r,
g: self.g + rhs.g,
b: self.b + rhs.b,
}
}
}
impl AddAssign for RGB {
fn add_assign(&mut self, rhs: Self) {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
}
}
impl Sub for RGB {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self {
r: self.r - rhs.r,
g: self.g - rhs.g,
b: self.b - rhs.b,
}
}
}
impl SubAssign for RGB {
fn sub_assign(&mut self, rhs: Self) {
self.r -= rhs.r;
self.g -= rhs.g;
self.b -= rhs.b;
}
}
impl Sub<RGB> for Float {
type Output = RGB;
fn sub(self, rhs: RGB) -> RGB {
RGB::new(self - rhs.r, self - rhs.g, self - rhs.b)
}
}
impl Mul for RGB {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self {
r: self.r * rhs.r,
g: self.g * rhs.g,
b: self.b * rhs.b,
}
}
}
impl MulAssign for RGB {
fn mul_assign(&mut self, rhs: Self) {
self.r *= rhs.r;
self.g *= rhs.g;
self.b *= rhs.b;
}
}
// Scalar multiplication (ryz * float)
impl Mul<Float> for RGB {
type Output = Self;
fn mul(self, rhs: Float) -> Self {
Self {
r: self.r * rhs,
g: self.g * rhs,
b: self.b * rhs,
}
}
}
impl MulAssign<Float> for RGB {
fn mul_assign(&mut self, rhs: Float) {
self.r *= rhs;
self.g *= rhs;
self.b *= rhs;
}
}
impl Mul<RGB> for Float {
type Output = RGB;
fn mul(self, rhs: RGB) -> RGB {
rhs * self
}
}
impl Div for RGB {
type Output = Self;
fn div(self, rhs: Self) -> Self {
Self {
r: self.r / rhs.r,
g: self.g / rhs.g,
b: self.b / rhs.b,
}
}
}
impl DivAssign for RGB {
fn div_assign(&mut self, rhs: Self) {
self.r /= rhs.r;
self.g /= rhs.g;
self.b /= rhs.b;
}
}
impl Div<Float> for RGB {
type Output = Self;
fn div(self, rhs: Float) -> Self {
debug_assert_ne!(rhs, 0.0, "Division by zero.");
let inv = 1.0 / rhs;
self * inv
}
}
impl DivAssign<Float> for RGB {
fn div_assign(&mut self, rhs: Float) {
debug_assert_ne!(rhs, 0.0, "Division by zero.");
let inv = 1.0 / rhs;
*self *= inv;
}
}
impl fmt::Display for RGB {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[ {}, {}, {} ]", self.r, self.g, self.b)
}
}
impl Mul<XYZ> for SquareMatrix<Float, 3> {
type Output = RGB;
fn mul(self, v: XYZ) -> RGB {
let r = self[0][0] * v.x + self[0][1] * v.y + self[0][2] * v.z;
let g = self[1][0] * v.x + self[1][1] * v.y + self[1][2] * v.z;
let b = self[2][0] * v.x + self[2][1] * v.y + self[2][2] * v.z;
RGB::new(r, g, b)
}
}
impl Mul<RGB> for SquareMatrix<Float, 3> {
type Output = XYZ;
fn mul(self, v: RGB) -> XYZ {
let x = self[0][0] * v.r + self[0][1] * v.g + self[0][2] * v.b;
let y = self[1][0] * v.r + self[1][1] * v.g + self[1][2] * v.b;
let z = self[2][0] * v.r + self[2][1] * v.g + self[2][2] * v.b;
XYZ::new(x, y, z)
}
}
pub trait MatrixMulColor {
fn mul_rgb(&self, v: RGB) -> RGB;
fn mul_xyz(&self, v: XYZ) -> XYZ;
}
impl MatrixMulColor for SquareMatrix<Float, 3> {
fn mul_rgb(&self, v: RGB) -> RGB {
let m = self;
RGB::new(
m[0][0] * v.r + m[0][1] * v.g + m[0][2] * v.b,
m[1][0] * v.r + m[1][1] * v.g + m[1][2] * v.b,
m[2][0] * v.r + m[2][1] * v.g + m[2][2] * v.b,
)
}
fn mul_xyz(&self, v: XYZ) -> XYZ {
let m = self;
XYZ::new(
m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z,
m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z,
m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z,
)
}
}
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,
}
impl RGBToSpectrumTable {
pub fn srgb() -> Self {
// use crate::core::constants::{RGB_TO_SPECTRUM_Z_NODES, RGB_TO_SPECTRUM_COEFFS};
// Self::new(&RGB_TO_SPECTRUM_Z_NODES, &RGB_TO_SPECTRUM_COEFFS)
todo!("Link the static constant arrays for sRGB coefficients here")
}
}
#[derive(Debug, Default, Copy, Clone)]
pub struct RGBSigmoidPolynomial {
c0: Float,
c1: Float,
c2: Float,
}
impl RGBSigmoidPolynomial {
pub fn new(c0: Float, c1: Float, c2: Float) -> Self {
Self { c0, c1, c2 }
}
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")
}
};
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 (360.0..830.0).contains(&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 }
}
0.5 + x / (2.0 * (1.0 + (x * x)).sqrt())
}
}
impl RGBToSpectrumTable {
pub fn new(z_nodes: &'static [f32], coeffs: &'static CoefficientArray) -> Self {
Self { z_nodes, coeffs }
}
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(),
);
}
let maxc;
if rgb[0] > rgb[1] {
if rgb[0] > rgb[2] {
maxc = 0;
} else {
maxc = 2;
}
} else if rgb[1] > rgb[2] {
maxc = 1;
} else {
maxc = 2;
}
let z = rgb[maxc];
let x = rgb[(maxc + 1) % 3] * (RES - 1) as Float / z;
let y = rgb[(maxc + 2) % 3] * (RES - 1) as Float / z;
let xi = x.min(RES as Float - 2.0);
let yi = y.min(RES as Float - 2.0);
let zi = crate::core::pbrt::find_interval(RES, |i: usize| self.z_nodes[i] < z);
let dx = (x - xi) as usize;
let dy = (y - yi) as usize;
let dz = (z - self.z_nodes[zi]) / (self.z_nodes[zi + 1] - self.z_nodes[zi]);
let mut c = [0.0; 3];
#[allow(clippy::needless_range_loop)]
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),
),
);
}
RGBSigmoidPolynomial {
c0: c[0],
c1: c[1],
c2: c[2],
}
}
}
const LMS_FROM_XYZ: SquareMatrix<Float, 3> = 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<Float, 3> = 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<Float, 3> {
// 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::<Float, 3>::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
}
#[enum_dispatch]
pub trait ColorEncodingTrait: '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::<Self>()
}
}
#[enum_dispatch(ColorEncodingTrait)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum ColorEncoding {
Linear(LinearEncoding),
SRGB(SRGBEncoding),
}
impl fmt::Display for ColorEncoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Encoding")
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct LinearEncoding;
impl ColorEncodingTrait 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, Clone, Copy, Hash, PartialEq, Eq)]
pub struct SRGBEncoding;
impl ColorEncodingTrait 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 const LINEAR: ColorEncoding = ColorEncoding::Linear(LinearEncoding);
pub const SRGB: ColorEncoding = ColorEncoding::SRGB(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,
];