Expand description
Axis-aligned bounding box.
+Structs§
- Axis-aligned bounding box Defined by two opposing corners, each of which are a Vec3.
diff --git a/.lock b/.lock new file mode 100644 index 00000000..e69de29b diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/clovers/aabb/index.html b/clovers/aabb/index.html new file mode 100644 index 00000000..b943fffe --- /dev/null +++ b/clovers/aabb/index.html @@ -0,0 +1,3 @@ +
pub struct AABB {
+ pub x: Interval,
+ pub y: Interval,
+ pub z: Interval,
+}
Axis-aligned bounding box Defined by two opposing corners, each of which are a Vec3.
+This is useful for creating bounding volume hierarchies, which is an optimization for reducing the time spent on calculating ray-object intersections.
+x: Interval
The bounding interval on the X axis
+y: Interval
The bounding interval on the Y axis
+z: Interval
The bounding interval on the Z axis
+Creates a new axis-aligned bounding box from three intervals
+Creates a new axis-aligned bounding box from two coordinates. Treats the two points a and b as extrema for the bounding box, so we don’t require a particular minimum/maximum coordinate order.
+Given a Ray, returns whether the ray hits the bounding box or not. Current default method, based on “An Optimized AABB Hit Method”
+Given a Ray, returns whether the ray hits the bounding box or not. Old method from a GitHub issue. Exists mostly for testing purposes.
+Given a Ray, returns whether the ray hits the bounding box or not. Newer method from a GitHub issue. Exists mostly for testing purposes.
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct BVHNode<'scene> {
+ pub left: Box<Hitable<'scene>>,
+ pub right: Box<Hitable<'scene>>,
+ pub bounding_box: AABB,
+}
Bounding Volume Hierarchy Node.
+A node in a tree structure defining a hierarchy of objects in a scene: a node knows its bounding box, and has two children which are also BVHNode
s. This is used for accelerating the ray-object intersection calculation in the ray tracer. See Bounding Volume hierarchies
left: Box<Hitable<'scene>>
Left child of the BVHNode
right: Box<Hitable<'scene>>
Right child of the BVHNode
bounding_box: AABB
Bounding box containing both of the child nodes
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreCamera. Used for creating Rays towards the scene, with directions defined by the camera properties.
+aspect_ratio
) are derived from other info (such as width, height)pub struct Camera {
+ pub lower_left_corner: Position,
+ pub horizontal: Vec3,
+ pub vertical: Vec3,
+ pub origin: Position,
+ pub lens_radius: Float,
+ pub time_0: Float,
+ pub time_1: Float,
+ pub u: Direction,
+ pub v: Direction,
+ pub w: Direction,
+}
The main Camera object used in the ray tracing.
+lower_left_corner: Position
Coordinate of the lower left corner of the camera.
+horizontal: Vec3
Defines the horizontal axis for the camera.
+vertical: Vec3
Defines the vertical axis for the camera.
+origin: Position
Defines the origin of the camera.
+lens_radius: Float
Defines the lens radius for the camera. TODO: understand and explain better
+time_0: Float
Defines the earliest starting time for the camera, used when generating Rays.
+time_1: Float
Defines the latest ending time for the camera, used when generating Rays.
+u: Direction
U
+v: Direction
V
+w: Direction
W
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CameraInit {
+ pub look_from: Position,
+ pub look_at: Position,
+ pub up: Vec3,
+ pub vertical_fov: Float,
+ pub aperture: Float,
+ pub focus_distance: Float,
+}
Represents the fields that can be described in a Scene file. Some other fields the main Camera struct requires (such as aspect_ratio
) are derived from other info (such as width, height)
look_from: Position
Describes where the camera is
+look_at: Position
Describes where the camera is looking at
+up: Vec3
Describes the subjective “up” direction for the camera to define the orientation
+vertical_fov: Float
Describes the vertical field of view for the camera
+aperture: Float
Describes the size of the aperture of the camera.
+focus_distance: Float
Describes the distance at which the camera has been focused to
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum ColorInit {
+ Color([Float; 3]),
+ TypedColor(TypedColorInit),
+}
Initialization structure for a color. Contains either an untyped, legacy variant (assumed Srgb) or one of the new type-safe, tagged versions.
+Legacy color, assume Srgb given as array of three floats in range 0..1 for up to unity gain, >1 for positive gain (illuminating) colors
+Typesafe color initialization structure
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum TypedColorInit {
+ Hex(String),
+ LinSrgb(LinSrgb),
+ Srgb(Srgb),
+ XyzE(Xyz<E>),
+ XyzD65(Xyz<D65>),
+ Oklch(Oklch),
+}
Type safe initialization structure for a color. Can be specified in a variety of color spaces.
+Hex “web color” Srgb
+Linear Srgb
+Non-linear Srgb
+XYZ, E illuminant
+XYZ, D65 illuminant
+Oklch
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreInitialization structures for colors. This exists for deserialization purposes.
+pub const EPSILON_CONSTANT_MEDIUM: Float = 0.000_1;
Internal const: epsilon used in the hit calculation of a ConstantMedium
.
pub const EPSILON_RECT_THICKNESS: Float = 0.000_1;
Internal const: epsilon used for having a finitely-sized thickness for the bounding box of an infinitely-thin rectangle. Shouldn’t be too small.
+pub const EPSILON_SHADOW_ACNE: Float = 0.001;
Internal const: epsilon used for avoiding “shadow acne”. This is mostly used for the initial minimum distance for ray hits after reflecting or scattering from a surface.
+pub enum Hitable<'scene> {
+Show 13 variants
Boxy(Boxy<'scene>),
+ BVHNode(BVHNode<'scene>),
+ ConstantMedium(ConstantMedium<'scene>),
+ MovingSphere(MovingSphere<'scene>),
+ Quad(Quad<'scene>),
+ RotateY(RotateY<'scene>),
+ Sphere(Sphere<'scene>),
+ STL(STL<'scene>),
+ GLTF(GLTF<'scene>),
+ Translate(Translate<'scene>),
+ Triangle(Triangle<'scene>),
+ Empty(Empty),
+ GLTFTriangle(GLTFTriangle<'scene>),
+}
An abstraction for things that can be hit by Rays.
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreAn abstraction for things that can be hit by Rays.
+(front_face, normal)
. Used in lieu of set_face_normal
in the Ray Tracing for the Rest Of Your Life book.pub struct Empty {}
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct HitRecord<'a> {
+ pub distance: Float,
+ pub position: Position,
+ pub normal: Direction,
+ pub u: Float,
+ pub v: Float,
+ pub material: &'a dyn MaterialTrait,
+ pub front_face: bool,
+}
Represents a ray-object intersection, with plenty of data about the intersection.
+distance: Float
Distance from the ray origin to the hitpoint
+position: Position
3D coordinate of the hitpoint
+normal: Direction
Surface normal from the hitpoint
+u: Float
U surface coordinate of the hitpoint
+v: Float
V surface coordinate of the hitpoint
+material: &'a dyn MaterialTrait
Reference to the material at the hitpoint
+front_face: bool
Is the hitpoint at the front of the surface
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait HitableTrait {
+ // Required methods
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng
+ ) -> Option<HitRecord<'_>>;
+ fn bounding_box(&self, t0: Float, t1: Float) -> Option<&AABB>;
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng
+ ) -> Float;
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position;
+}
Note: This library is experimental & heavily work in progress. Everything can change at a moment’s notice. It is probably not a good idea to use this library for anything other than experimentation for now!
+This project uses GitHub for development and issue tracking. Link to the repository.
+WebAssembly
, etcThere are a few core stages of using clovers.
+First, you will need a Scene. You can create a scene manually or utilize serde to deserialize from a file. Currently, the example binary uses a JSON format.
+clovers is not opinionated on how you want to render your scene. In a usual scenario, you probably want to have some form of a pixel buffer, with knowledge of the x
and y
coordinates of your buffer.
Ray
s and seeing what they hitRay
has an origin and a directionObject
has a hit()
method that takes a Ray and returns an Option<HitRecord
>HitRecord
), use its details to colorize your pixelscatter()
and cast a new Ray
?You most likely want to repeat this process multiple times for each of your pixels: generating multiple samples per pixel results in a higher quality image.
+TODO: maybe add some post processing utilities?
+At the end, use your pixel buffer - save to an image file, draw a frame in a GUI window, etc.
+T
.Vec<T>
, short for ‘vector’.ConstantMedium
.pub struct Interval {
+ pub min: Float,
+ pub max: Float,
+}
An interval structure.
+min: Float
Smallest value of the interval. Must be kept in order
+max: Float
Largest value of the interval. Must be kept in order
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ConeLight { /* private fields */ }
A cone light material. The material emits light if the incoming ray is within a certain amount of degrees from the surface normal.
+Scatter method for the ConeLight
material. Always returns None
, as diffuse light does not scatter.
Scattering probability density function for the ConeLight
material. Always returns 0, as diffuse light does not scatter.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreA dielectric material. This resembles glass and other transparent and reflective materials.
+pub struct Dielectric {
+ pub refractive_index: Float,
+ pub color: Xyz<E>,
+}
A dielectric material. This resembles glass and other transparent and reflective materials.
+refractive_index: Float
Refractive index of the material. Used for calculating the new direction of a ray when entering the material at an angle. Follows Snell’s law of refraction. Default value: 1.5, based on typical window glass.
+color: Xyz<E>
Color of the material. Used for colorizing the rays. Default value: [(1.0, 1.0, 1.0)
], producing a fully transparent, clear glass.
source
. Read moreScatter method for the Dielectric material. Given a ray
and a hit_record
, evaluate a ScatterRecord
based on possible reflection or refraction.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreA diffuse light material.
+pub struct DiffuseLight { /* private fields */ }
A diffuse light material. On this material, rays never scatter - the material always emits a color based on its texture.
+source
. Read moreCreates a new DiffuseLight
with white light at intensity 100.0
Scatter method for the DiffuseLight
material. Always returns None
, as diffuse light does not scatter.
Scattering probability density function for the DiffuseLight
material. Always returns 0, as diffuse light does not scatter.
Emission function for DiffuseLight
. If the given HitRecord
has been hit on the front_face
, emit a color based on the texture and surface coordinates. Otherwise, emit pure black.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreDispersive material. +Based on Cauchy’s equation
+pub struct Dispersive {
+ pub cauchy_a: Float,
+ pub cauchy_b: Float,
+}
A dispersive glass material.
+cauchy_a: Float
Cauchy coefficient A of the material
+cauchy_b: Float
Cauchy coefficient B of the material
+Creates a new Dispersive material with the given Cauchy equation constants.
+Calculates the refractive index of the material for the given wavelength
+source
. Read moreScatterRecord
.parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Material {
+ Dielectric(Dielectric),
+ Dispersive(Dispersive),
+ Lambertian(Lambertian),
+ ConeLight(ConeLight),
+ DiffuseLight(DiffuseLight),
+ Metal(Metal),
+ Isotropic(Isotropic),
+}
A material enum. TODO: for ideal clean abstraction, this should be a trait. However, that comes with some additional considerations, including e.g. performance.
+Dielectric material
+Dispersive material
+Lambertian material
+ConeLight material
+DiffuseLight material
+Metal material
+Isotropic material
+Given a ray and a hitrecord, return the possible ScatterRecord
.
TODO: explain
+Returns the emissivity of the material at the given position. Defaults to black as most materials don’t emit - override when needed.
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum MaterialInit {
+ Shared(String),
+ Owned(Material),
+}
Initialization structure for a Material
. Either contains a Material
by itself, or a String name
to be found in a shared material list.
source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum MaterialType {
+ Diffuse,
+ Specular,
+}
Enum for the types of materials: Diffuse and Specular (i.e., matte and shiny)
+A matte material that does not reflect rays
+A shiny material that reflects some rays
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct GLTFMaterial<'scene> { /* private fields */ }
GLTF Material wrapper type
+source
. Read moreScatterRecord
.parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreMaterials enable different behaviors of light on objects.
+pub use cone_light::*;
pub use dielectric::*;
pub use diffuse_light::*;
pub use dispersive::*;
pub use isotropic::*;
pub use lambertian::*;
pub use metal::*;
Material
that can be referred to by name for reuse across multiple Object
sMaterial
. Either contains a Material
by itself, or a String name
to be found in a shared material list.scatter
, scattering_pdf
, and emit
.pub struct Isotropic { /* private fields */ }
Isotropic material. Used in ConstantMedium
. TODO: understand this!
Returns a ScatterRecord
based on the HitRecord
coordinates and the given Texture, or None if the ray did not hit the material.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreLambertian material. This is the default material with a smooth, matte surface.
+pub struct Lambertian { /* private fields */ }
Lambertian material. This is the default material with a smooth, matte surface.
+source
. Read moreReturns None, if ray is absorbed. Otherwise, returns a ray, albedo of what was hit, and (?) a value used for probability density function based sampling
+Returns the scattering probability density function for the Lambertian material. TODO: explain the math
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Metal { /* private fields */ }
A metal material. The amount of reflection can be adjusted with the fuzz
parameter.
Scatter function for the Metal material. Metal always reflects, and a specular ray is calculated with some randomness adjusted by the fuzz
factor. This means the metal can be made more shiny or more matte. The returned ScatterRecord
will have a probability density function of ZeroPDF
and material type of MaterialType::Specular
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ScatterRecord<'ray> {
+ pub material_type: MaterialType,
+ pub specular_ray: Option<Ray>,
+ pub attenuation: Xyz<E>,
+ pub pdf_ptr: PDF<'ray>,
+}
material_type: MaterialType
The material type that was scattered on
+specular_ray: Option<Ray>
Direction of a generated specular ray
+attenuation: Xyz<E>
Current color to take into account when following the scattered ray for futher iterations
+pdf_ptr: PDF<'ray>
Probability density function to use with the ScatterRecord
.
source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SharedMaterial {
+ pub name: String,
+ pub material: Material,
+}
A Material
that can be referred to by name for reuse across multiple Object
s
name: String
Name of the shared material
+material: Material
The shared material itself
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait MaterialTrait: Debug {
+ // Required methods
+ fn scatter(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord<'_>,
+ rng: &mut SmallRng
+ ) -> Option<ScatterRecord<'_>>;
+ fn scattering_pdf(
+ &self,
+ hit_record: &HitRecord<'_>,
+ scattered: &Ray,
+ rng: &mut SmallRng
+ ) -> Option<Float>;
+
+ // Provided method
+ fn emit(
+ &self,
+ _ray: &Ray,
+ _hit_record: &HitRecord<'_>,
+ _u: Float,
+ _v: Float,
+ _position: Position
+ ) -> Xyz<E> { ... }
+}
Trait for materials. Requires three function implementations: scatter
, scattering_pdf
, and emit
.
pub struct Boxy<'scene> {
+ pub material: &'scene Material,
+ pub aabb: AABB,
+ /* private fields */
+}
material: &'scene Material
The material of the box
+aabb: AABB
Axis-aligned bounding box
+Returns the axis-aligned bounding box AABB of the object.
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct BoxyInit {
+ pub priority: bool,
+ pub corner_0: Position,
+ pub corner_1: Position,
+ pub material: MaterialInit,
+}
priority: bool
Used for multiple importance sampling
+corner_0: Position
First corner for the box
+corner_1: Position
Second, opposing corner for the box
+material: MaterialInit
Material used for the box
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreConstantMedium
object. This should probably be a Material at some point, but this will do for now. This is essentially a fog with a known size, shape and density.
ConstantMedium
object. This should probably be a Material at some point, but this will do for now. This is essentially a fog with a known size, shape and density.ConstantMediumInit
structure describes the necessary data for constructing a ConstantMedium
. Used with serde when importing SceneFiles
.pub struct ConstantMedium<'scene> { /* private fields */ }
ConstantMedium
object. This should probably be a Material at some point, but this will do for now. This is essentially a fog with a known size, shape and density.
source
. Read moreHit function for the ConstantMedium
object. Returns a HitRecord
if hit. TODO: explain the math for the fog
Returns the axis-aligned bounding box AABB of the defining boundary
object for the fog.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ConstantMediumInit {
+ pub priority: bool,
+ pub boundary: Box<Object>,
+ pub density: Float,
+ pub texture: Texture,
+}
ConstantMediumInit
structure describes the necessary data for constructing a ConstantMedium
. Used with serde when importing SceneFiles
.
priority: bool
Used for multiple importance sampling
+boundary: Box<Object>
The boundary object for the constant medium. This determines the size and shape of the fog object.
+density: Float
Density of the fog. TODO: example good value range?
+texture: Texture
Texture used for the colorization of the fog.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Object {
+ Boxy(BoxyInit),
+ ConstantMedium(ConstantMediumInit),
+ MovingSphere(MovingSphereInit),
+ ObjectList(ObjectList),
+ Quad(QuadInit),
+ RotateY(RotateInit),
+ Sphere(SphereInit),
+ STL(STLInit),
+ GLTF(GLTFInit),
+ Translate(TranslateInit),
+ Triangle(TriangleInit),
+}
An object enum. TODO: for ideal clean abstraction, this should be a trait. However, that comes with some additional considerations, including e.g. performance.
+Boxy object initializer
+ConstantMedium
object initializer
MovingSphere
object initializer
ObjectList
object initializer
Quad object initializer
+RotateY
object initializer
Sphere object initializer
+STL object initializer
+GLTF object initializer
+Translate object initializer
+Triangle object initializer
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn object_to_hitable(
+ obj: Object,
+ materials: &[SharedMaterial]
+) -> Hitable<'_>
Initializes an Object
into a Hitable
.
pub struct GLTF<'scene> {
+ pub bvhnode: BVHNode<'scene>,
+ pub aabb: AABB,
+}
Internal GLTF object representation after initialization.
+bvhnode: BVHNode<'scene>
Bounding Volume Hierarchy tree for the object
+aabb: AABB
Axis-aligned bounding box of the object
+Hit method for the GLTF object
+Return the axis-aligned bounding box for the object
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct GLTFInit {
+ pub priority: bool,
+ pub path: String,
+}
GLTF initialization structure
+priority: bool
Used for multiple importance sampling
+path: String
Path of the .gltf file
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct GLTFTriangle<'scene> {
+ pub aabb: AABB,
+ pub material: &'scene GLTFMaterial<'scene>,
+ /* private fields */
+}
Internal GLTF object representation after initialization.
+aabb: AABB
Axis-aligned bounding box of the object
+material: &'scene GLTFMaterial<'scene>
Material of the object
+Initialize a new GLTF object
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreVarious literal objects and meta-object utilities for creating content in Scenes.
+pub use self::gltf::*;
pub use boxy::*;
pub use constant_medium::*;
pub use moving_sphere::*;
pub use quad::*;
pub use rotate::*;
pub use sphere::*;
pub use stl::*;
pub use translate::*;
pub use triangle::*;
ConstantMedium
object. This should probably be a Material at some point, but this will do for now. This is essentially a fog with a known size, shape and density.hit_ab
method.Object
into a Hitable
.A moving sphere object.
+radius
, two center points center_0
center_1
, two times time_0
time_1
, and a Material. Any Rays hitting the object will also have an internal time
value, which will be used for determining the interpolated position of the sphere at that time. With lots of rays hitting every pixel but at randomized times, we get temporal multiplexing and an approximation of perceived motion blur.pub struct MovingSphere<'scene> {
+ pub center_0: Position,
+ pub center_1: Position,
+ pub time_0: Float,
+ pub time_1: Float,
+ pub radius: Float,
+ pub material: &'scene Material,
+ pub aabb: AABB,
+}
A moving sphere object. This is represented by one radius
, two center points center_0
center_1
, two times time_0
time_1
, and a Material. Any Rays hitting the object will also have an internal time
value, which will be used for determining the interpolated position of the sphere at that time. With lots of rays hitting every pixel but at randomized times, we get temporal multiplexing and an approximation of perceived motion blur.
center_0: Position
Center point of the sphere at time_0
center_1: Position
Center point of the sphere at time_1
time_0: Float
Time 0
+time_1: Float
Time 1
+radius: Float
Radius of the sphere
+material: &'scene Material
Material of the sphere
+aabb: AABB
Axis-aligned bounding box
+Creates a new MovingSphere
object. See the struct documentation for more information: MovingSphere
.
source
. Read moreHit method for the MovingSphere
object. First gets the interpolated center position at the given time, then follows the implementation of Sphere object’s hit method.
Returns the axis-aligned bounding box of the MovingSphere
object. This is the maximum possible bounding box of the entire span of the movement of the sphere, calculated from the two center positions and the radius.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct MovingSphereInit {
+ pub priority: bool,
+ pub center_0: Position,
+ pub center_1: Position,
+ pub radius: Float,
+ pub material: MaterialInit,
+}
priority: bool
Used for multiple importance sampling
+center_0: Position
Center point of the sphere at time_0
center_1: Position
Center point of the sphere at time_1
radius: Float
Radius of the sphere.
+material: MaterialInit
Material of the sphere.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Quad<'scene> {
+ pub q: Position,
+ pub u: Vec3,
+ pub v: Vec3,
+ pub material: &'scene Material,
+ pub area: Float,
+ pub normal: Direction,
+ pub d: Float,
+ pub w: Vec3,
+ pub aabb: AABB,
+}
Quadrilateral shape. This can be an arbitrary parallelogram, not just a rectangle.
+q: Position
Corner point
+u: Vec3
Vector describing the u side
+v: Vec3
Vector describing the v side
+material: &'scene Material
Material of the surface
+area: Float
Area of the surface
+normal: Direction
Normal vector of the surface
+d: Float
What is this? // TODO: understand, explain
+w: Vec3
What is this? // TODO: understand, explain
+aabb: AABB
Bounding box of the surface
+Hit method for the quad rectangle
+Returns the bounding box of the quad
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct QuadInit {
+ pub priority: bool,
+ pub q: Position,
+ pub u: Vec3,
+ pub v: Vec3,
+ pub material: MaterialInit,
+}
Initialization structure for a Quad object.
+priority: bool
Used for multiple importance sampling
+q: Position
Corner point
+u: Vec3
Vector describing the u side
+v: Vec3
Vector describing the v side
+material: MaterialInit
Material of the surface
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct RotateInit {
+ pub priority: bool,
+ pub object: Box<Object>,
+ pub angle: Float,
+}
priority: bool
Used for multiple importance sampling
+object: Box<Object>
The encased Object to rotate
+angle: Float
Angle to rotate the object, in degrees
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct RotateY<'scene> { /* private fields */ }
RotateY
object. It wraps the given Object and has adjusted hit()
and bounding_box()
methods based on the angle
given.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Sphere<'scene> { /* private fields */ }
A sphere object.
+Returns the axis-aligned bounding box AABB for the sphere.
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SphereInit {
+ pub priority: bool,
+ pub center: Position,
+ pub radius: Float,
+ pub material: MaterialInit,
+}
priority: bool
Used for multiple importance sampling
+center: Position
Center of the sphere.
+radius: Float
Radius of the sphere.
+material: MaterialInit
Material of the sphere.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn initialize_stl<'scene>(
+ stl_init: STLInit,
+ materials: &'scene [SharedMaterial]
+) -> STL<'scene>
Initializes an STL
+pub struct STL<'scene> {
+ pub bvhnode: BVHNode<'scene>,
+ pub material: &'scene Material,
+ pub aabb: AABB,
+}
Internal STL object representation after initialization. Contains the material for all triangles in it to avoid having n copies.
+bvhnode: BVHNode<'scene>
Bounding Volume Hierarchy tree for the object
+material: &'scene Material
Material for the object
+aabb: AABB
Axis-aligned bounding box of the object
+Hit method for the STL object
+Return the axis-aligned bounding box for the object
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct STLInit {
+ pub priority: bool,
+ pub path: String,
+ pub material: MaterialInit,
+ pub scale: Float,
+ pub center: Position,
+ pub rotation: Vec3,
+}
STL structure. This gets converted into an internal representation using Triangles
+priority: bool
Used for multiple importance sampling
+path: String
Path of the .stl file
+material: MaterialInit
Material to use for the .stl object
+scale: Float
Scaling factor for the object
+center: Position
Location of the object in the rendered scene
+rotation: Vec3
Rotation of the object. Described as three angles, roll
, pitch
, yaw
, applied in that order.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ObjectList {
+ pub priority: bool,
+ pub objects: Vec<Object>,
+}
A list of objects. Allows multiple objects to be used e.g. in a Rotate or Translate object as the target.
+priority: bool
Priority
+objects: Vec<Object>
The encased Object list
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Translate<'scene> { /* private fields */ }
Translate object. It wraps the given Object and has adjusted hit()
and bounding_box()
methods based on the offset
given.
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TranslateInit {
+ pub priority: bool,
+ pub object: Box<Object>,
+ pub offset: Vec3,
+}
priority: bool
Used for multiple importance sampling
+object: Box<Object>
The encased Object to translate i.e. move
+offset: Vec3
The vector describing the movement of the object
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Triangle<'scene> {
+ pub q: Position,
+ pub u: Vec3,
+ pub v: Vec3,
+ pub material: &'scene Material,
+ pub area: Float,
+ pub normal: Direction,
+ pub d: Float,
+ pub w: Vec3,
+ pub aabb: AABB,
+}
Triangle shape. Heavily based on Quad and may contain inaccuracies
+q: Position
Corner point
+u: Vec3
Vector describing the u side
+v: Vec3
Vector describing the v side
+material: &'scene Material
Material of the surface
+area: Float
Area of the surface
+normal: Direction
Normal vector of the surface
+d: Float
What is this? // TODO: understand, explain
+w: Vec3
What is this? // TODO: understand, explain
+aabb: AABB
Bounding box of the surface
+Hit method for the triangle
+Returns the bounding box of the triangle
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TriangleInit {
+ pub priority: bool,
+ pub q: Position,
+ pub u: Vec3,
+ pub v: Vec3,
+ pub material: MaterialInit,
+}
Initialization structure for a triangle primitive
+priority: bool
Used for multiple importance sampling
+q: Position
Corner point
+u: Vec3
Vector describing the u side
+v: Vec3
Vector describing the v side
+material: MaterialInit
Material of the surface
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ONB {
+ pub u: Direction,
+ pub v: Direction,
+ pub w: Direction,
+}
An orthonormal basis structure.
+u: Direction
U
+v: Direction
V
+w: Direction
W
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum PDF<'scene> {
+ CosinePDF(CosinePDF),
+ SpherePDF(SpherePDF),
+ HitablePDF(HitablePDF<'scene>),
+ MixturePDF(MixturePDF<'scene>),
+ ZeroPDF(ZeroPDF),
+}
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CosinePDF { /* private fields */ }
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct HitablePDF<'scene> { /* private fields */ }
source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct MixturePDF<'scene> { /* private fields */ }
source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SpherePDF {}
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ZeroPDF {}
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait PDFTrait {
+ // Required methods
+ fn value(
+ &self,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng
+ ) -> Float;
+ fn generate(&self, rng: &mut SmallRng) -> Position;
+}
Various internal helper functions for getting specific kinds of random values.
+pub struct Ray {
+ pub origin: Position,
+ pub direction: Direction,
+ pub time: Float,
+ pub wavelength: Wavelength,
+}
A Ray has an origin and a direction, as well as an instant in time it exists in. Motion blur is achieved by creating multiple rays with slightly different times.
+origin: Position
The origin of the ray.
+direction: Direction
The direction of the ray.
+time: Float
The time instant at which the ray exists.
+wavelength: Wavelength
Wavelength of the ray
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Scene<'scene> {
+ pub hitables: BVHNode<'scene>,
+ pub camera: Camera,
+ pub background_color: Srgb,
+ pub priority_hitables: Hitable<'scene>,
+}
A representation of the scene that is being rendered.
+hitables: BVHNode<'scene>
Bounding-volume hierarchy of Hitable objects in the scene. This could, as currently written, be any Hitable - in practice, we place the root of the BVHNode
tree here.
camera: Camera
The camera object used for rendering the scene.
+background_color: Srgb
The background color to use when the rays do not hit anything in the scene.
+priority_hitables: Hitable<'scene>
A BVHNode
tree of prioritized objects - e.g. glass items or lights - that affect the biased sampling of the scene. Wrapped into a Hitable for convenience reasons (see various PDF functions).
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SceneFile { /* private fields */ }
A serialized representation of a Scene.
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn spectrum_xyz_to_p(lambda: Wavelength, xyz: Xyz<E>) -> Float
Evaluate the spectrum at the given wavelength for the given XYZ color
+Utilities for Physically Meaningful Rendering using Tristimulus Colours
+spectra_xyz_5nm_380_780_0.97.h
in the supplemental material from Physically Meaningful Rendering using Tristimulus Coloursspectrum_grid.h
in the supplemental material from Physically Meaningful Rendering using Tristimulus Colourspub const cmf_wavelength: [f64; 81];
pub const equal_energy_reflectance: f64 = 0.009358239977091027;
pub const spectrum_bin_size: f64 = 5.0;
pub const spectrum_data_points: [spectrum_data_point_t; 186];
pub const spectrum_grid: [spectrum_grid_cell_t; 168];
pub const spectrum_grid_height: usize = 14;
pub const spectrum_grid_height_f: f64 = 14.0;
pub const spectrum_grid_width: usize = 12;
pub const spectrum_grid_width_f: f64 = 12.0;
pub const spectrum_num_samples: usize = 81;
pub const spectrum_num_samples_f: f64 = 81.0;
pub const spectrum_sample_max: f64 = 780.0;
pub const spectrum_sample_min: f64 = 380.0;
pub fn spectrum_uv_to_xy(uv: [f64; 2], xy: &mut [f64; 2])
pub fn spectrum_xy_to_uv(xy: [f64; 2], uv: &mut [f64; 2])
pub fn xyz_from_spectrum(spectrum: &[f64; 81], xyz: &mut [f64; 3])
Hand-converted from spectra_xyz_5nm_380_780_0.97.h
in the supplemental material from Physically Meaningful Rendering using Tristimulus Colours
pub struct spectrum_data_point_t {
+ pub xystar: [f64; 2],
+ pub uv: [f64; 2],
+ pub spectrum: [f64; 81],
+}
xystar: [f64; 2]
§uv: [f64; 2]
§spectrum: [f64; 81]
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct spectrum_grid_cell_t {
+ pub inside: usize,
+ pub num_points: usize,
+ pub idx: [isize; 6],
+}
inside: usize
§num_points: usize
§idx: [isize; 6]
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreHand-converted from spectrum_grid.h
in the supplemental material from Physically Meaningful Rendering using Tristimulus Colours
pub struct Box<T, A = Global>(/* private fields */)
+where
+ A: Allocator,
+ T: ?Sized;
A pointer type that uniquely owns a heap allocation of type T
.
See the module-level documentation for more.
+Allocates memory on the heap and then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
let five = Box::new(5);
new_uninit
)Constructs a new box with uninitialized contents.
+#![feature(new_uninit)]
+
+let mut five = Box::<u32>::new_uninit();
+
+let five = unsafe {
+ // Deferred initialization:
+ five.as_mut_ptr().write(5);
+
+ five.assume_init()
+};
+
+assert_eq!(*five, 5)
new_uninit
)Constructs a new Box
with uninitialized contents, with the memory
+being filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(new_uninit)]
+
+let zero = Box::<u32>::new_zeroed();
+let zero = unsafe { zero.assume_init() };
+
+assert_eq!(*zero, 0)
Constructs a new Pin<Box<T>>
. If T
does not implement Unpin
, then
+x
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin(x)
+does the same as Box::into_pin(Box::new(x))
. Consider using
+into_pin
if you already have a Box<T>
, or if you want to
+construct a (pinned) Box
in a different way than with Box::new
.
allocator_api
)Allocates memory on the heap then places x
into it,
+returning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]
+
+let five = Box::try_new(5)?;
allocator_api
)Constructs a new box with uninitialized contents on the heap, +returning an error if the allocation fails
+#![feature(allocator_api, new_uninit)]
+
+let mut five = Box::<u32>::try_new_uninit()?;
+
+let five = unsafe {
+ // Deferred initialization:
+ five.as_mut_ptr().write(5);
+
+ five.assume_init()
+};
+
+assert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory
+being filled with 0
bytes on the heap
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(allocator_api, new_uninit)]
+
+let zero = Box::<u32>::try_new_zeroed()?;
+let zero = unsafe { zero.assume_init() };
+
+assert_eq!(*zero, 0);
allocator_api
)Allocates memory in the given allocator then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let five = Box::new_in(5, System);
allocator_api
)Allocates memory in the given allocator then places x
into it,
+returning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let five = Box::try_new_in(5, System)?;
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator.
+#![feature(allocator_api, new_uninit)]
+
+use std::alloc::System;
+
+let mut five = Box::<u32, _>::new_uninit_in(System);
+
+let five = unsafe {
+ // Deferred initialization:
+ five.as_mut_ptr().write(5);
+
+ five.assume_init()
+};
+
+assert_eq!(*five, 5)
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator, +returning an error if the allocation fails
+#![feature(allocator_api, new_uninit)]
+
+use std::alloc::System;
+
+let mut five = Box::<u32, _>::try_new_uninit_in(System)?;
+
+let five = unsafe {
+ // Deferred initialization:
+ five.as_mut_ptr().write(5);
+
+ five.assume_init()
+};
+
+assert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory
+being filled with 0
bytes in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(allocator_api, new_uninit)]
+
+use std::alloc::System;
+
+let zero = Box::<u32, _>::new_zeroed_in(System);
+let zero = unsafe { zero.assume_init() };
+
+assert_eq!(*zero, 0)
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory
+being filled with 0
bytes in the provided allocator,
+returning an error if the allocation fails,
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(allocator_api, new_uninit)]
+
+use std::alloc::System;
+
+let zero = Box::<u32, _>::try_new_zeroed_in(System)?;
+let zero = unsafe { zero.assume_init() };
+
+assert_eq!(*zero, 0);
allocator_api
)Constructs a new Pin<Box<T, A>>
. If T
does not implement Unpin
, then
+x
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin_in(x, alloc)
+does the same as Box::into_pin(Box::new_in(x, alloc))
. Consider using
+into_pin
if you already have a Box<T, A>
, or if you want to
+construct a (pinned) Box
in a different way than with Box::new_in
.
box_into_boxed_slice
)Converts a Box<T>
into a Box<[T]>
This conversion does not allocate on the heap and happens in place.
+box_into_inner
)Consumes the Box
, returning the wrapped value.
#![feature(box_into_inner)]
+
+let c = Box::new(5);
+
+assert_eq!(Box::into_inner(c), 5);
new_uninit
)Constructs a new boxed slice with uninitialized contents.
+#![feature(new_uninit)]
+
+let mut values = Box::<[u32]>::new_uninit_slice(3);
+
+let values = unsafe {
+ // Deferred initialization:
+ values[0].as_mut_ptr().write(1);
+ values[1].as_mut_ptr().write(2);
+ values[2].as_mut_ptr().write(3);
+
+ values.assume_init()
+};
+
+assert_eq!(*values, [1, 2, 3])
new_uninit
)Constructs a new boxed slice with uninitialized contents, with the memory
+being filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(new_uninit)]
+
+let values = Box::<[u32]>::new_zeroed_slice(3);
+let values = unsafe { values.assume_init() };
+
+assert_eq!(*values, [0, 0, 0])
allocator_api
)Constructs a new boxed slice with uninitialized contents. Returns an error if +the allocation fails
+#![feature(allocator_api, new_uninit)]
+
+let mut values = Box::<[u32]>::try_new_uninit_slice(3)?;
+let values = unsafe {
+ // Deferred initialization:
+ values[0].as_mut_ptr().write(1);
+ values[1].as_mut_ptr().write(2);
+ values[2].as_mut_ptr().write(3);
+ values.assume_init()
+};
+
+assert_eq!(*values, [1, 2, 3]);
allocator_api
)Constructs a new boxed slice with uninitialized contents, with the memory
+being filled with 0
bytes. Returns an error if the allocation fails
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(allocator_api, new_uninit)]
+
+let values = Box::<[u32]>::try_new_zeroed_slice(3)?;
+let values = unsafe { values.assume_init() };
+
+assert_eq!(*values, [0, 0, 0]);
allocator_api
)Constructs a new boxed slice with uninitialized contents in the provided allocator.
+#![feature(allocator_api, new_uninit)]
+
+use std::alloc::System;
+
+let mut values = Box::<[u32], _>::new_uninit_slice_in(3, System);
+
+let values = unsafe {
+ // Deferred initialization:
+ values[0].as_mut_ptr().write(1);
+ values[1].as_mut_ptr().write(2);
+ values[2].as_mut_ptr().write(3);
+
+ values.assume_init()
+};
+
+assert_eq!(*values, [1, 2, 3])
allocator_api
)Constructs a new boxed slice with uninitialized contents in the provided allocator,
+with the memory being filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage
+of this method.
#![feature(allocator_api, new_uninit)]
+
+use std::alloc::System;
+
+let values = Box::<[u32], _>::new_zeroed_slice_in(3, System);
+let values = unsafe { values.assume_init() };
+
+assert_eq!(*values, [0, 0, 0])
new_uninit
)Converts to Box<T, A>
.
As with MaybeUninit::assume_init
,
+it is up to the caller to guarantee that the value
+really is in an initialized state.
+Calling this when the content is not yet fully initialized
+causes immediate undefined behavior.
#![feature(new_uninit)]
+
+let mut five = Box::<u32>::new_uninit();
+
+let five: Box<u32> = unsafe {
+ // Deferred initialization:
+ five.as_mut_ptr().write(5);
+
+ five.assume_init()
+};
+
+assert_eq!(*five, 5)
new_uninit
)Writes the value and converts to Box<T, A>
.
This method converts the box similarly to Box::assume_init
but
+writes value
into it before conversion thus guaranteeing safety.
+In some scenarios use of this method may improve performance because
+the compiler may be able to optimize copying from stack.
#![feature(new_uninit)]
+
+let big_box = Box::<[usize; 1024]>::new_uninit();
+
+let mut array = [0; 1024];
+for (i, place) in array.iter_mut().enumerate() {
+ *place = i;
+}
+
+// The optimizer may be able to elide this copy, so previous code writes
+// to heap directly.
+let big_box = Box::write(big_box, array);
+
+for (i, x) in big_box.iter().enumerate() {
+ assert_eq!(*x, i);
+}
new_uninit
)Converts to Box<[T], A>
.
As with MaybeUninit::assume_init
,
+it is up to the caller to guarantee that the values
+really are in an initialized state.
+Calling this when the content is not yet fully initialized
+causes immediate undefined behavior.
#![feature(new_uninit)]
+
+let mut values = Box::<[u32]>::new_uninit_slice(3);
+
+let values = unsafe {
+ // Deferred initialization:
+ values[0].as_mut_ptr().write(1);
+ values[1].as_mut_ptr().write(2);
+ values[2].as_mut_ptr().write(3);
+
+ values.assume_init()
+};
+
+assert_eq!(*values, [1, 2, 3])
Constructs a box from a raw pointer.
+After calling this function, the raw pointer is owned by the
+resulting Box
. Specifically, the Box
destructor will call
+the destructor of T
and free the allocated memory. For this
+to be safe, the memory must have been allocated in accordance
+with the memory layout used by Box
.
This function is unsafe because improper use may lead to +memory problems. For example, a double-free may occur if the +function is called twice on the same raw pointer.
+The safety conditions are described in the memory layout section.
+Recreate a Box
which was previously converted to a raw pointer
+using Box::into_raw
:
let x = Box::new(5);
+let ptr = Box::into_raw(x);
+let x = unsafe { Box::from_raw(ptr) };
Manually create a Box
from scratch by using the global allocator:
use std::alloc::{alloc, Layout};
+
+unsafe {
+ let ptr = alloc(Layout::new::<i32>()) as *mut i32;
+ // In general .write is required to avoid attempting to destruct
+ // the (uninitialized) previous contents of `ptr`, though for this
+ // simple example `*ptr = 5` would have worked as well.
+ ptr.write(5);
+ let x = Box::from_raw(ptr);
+}
allocator_api
)Constructs a box from a raw pointer in the given allocator.
+After calling this function, the raw pointer is owned by the
+resulting Box
. Specifically, the Box
destructor will call
+the destructor of T
and free the allocated memory. For this
+to be safe, the memory must have been allocated in accordance
+with the memory layout used by Box
.
This function is unsafe because improper use may lead to +memory problems. For example, a double-free may occur if the +function is called twice on the same raw pointer.
+Recreate a Box
which was previously converted to a raw pointer
+using Box::into_raw_with_allocator
:
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let x = Box::new_in(5, System);
+let (ptr, alloc) = Box::into_raw_with_allocator(x);
+let x = unsafe { Box::from_raw_in(ptr, alloc) };
Manually create a Box
from scratch by using the system allocator:
#![feature(allocator_api, slice_ptr_get)]
+
+use std::alloc::{Allocator, Layout, System};
+
+unsafe {
+ let ptr = System.allocate(Layout::new::<i32>())?.as_mut_ptr() as *mut i32;
+ // In general .write is required to avoid attempting to destruct
+ // the (uninitialized) previous contents of `ptr`, though for this
+ // simple example `*ptr = 5` would have worked as well.
+ ptr.write(5);
+ let x = Box::from_raw_in(ptr, System);
+}
Consumes the Box
, returning a wrapped raw pointer.
The pointer will be properly aligned and non-null.
+After calling this function, the caller is responsible for the
+memory previously managed by the Box
. In particular, the
+caller should properly destroy T
and release the memory, taking
+into account the memory layout used by Box
. The easiest way to
+do this is to convert the raw pointer back into a Box
with the
+Box::from_raw
function, allowing the Box
destructor to perform
+the cleanup.
Note: this is an associated function, which means that you have
+to call it as Box::into_raw(b)
instead of b.into_raw()
. This
+is so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw
+for automatic cleanup:
let x = Box::new(String::from("Hello"));
+let ptr = Box::into_raw(x);
+let x = unsafe { Box::from_raw(ptr) };
Manual cleanup by explicitly running the destructor and deallocating +the memory:
+ +use std::alloc::{dealloc, Layout};
+use std::ptr;
+
+let x = Box::new(String::from("Hello"));
+let ptr = Box::into_raw(x);
+unsafe {
+ ptr::drop_in_place(ptr);
+ dealloc(ptr as *mut u8, Layout::new::<String>());
+}
Note: This is equivalent to the following:
+ +let x = Box::new(String::from("Hello"));
+let ptr = Box::into_raw(x);
+unsafe {
+ drop(Box::from_raw(ptr));
+}
allocator_api
)Consumes the Box
, returning a wrapped raw pointer and the allocator.
The pointer will be properly aligned and non-null.
+After calling this function, the caller is responsible for the
+memory previously managed by the Box
. In particular, the
+caller should properly destroy T
and release the memory, taking
+into account the memory layout used by Box
. The easiest way to
+do this is to convert the raw pointer back into a Box
with the
+Box::from_raw_in
function, allowing the Box
destructor to perform
+the cleanup.
Note: this is an associated function, which means that you have
+to call it as Box::into_raw_with_allocator(b)
instead of b.into_raw_with_allocator()
. This
+is so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw_in
+for automatic cleanup:
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let x = Box::new_in(String::from("Hello"), System);
+let (ptr, alloc) = Box::into_raw_with_allocator(x);
+let x = unsafe { Box::from_raw_in(ptr, alloc) };
Manual cleanup by explicitly running the destructor and deallocating +the memory:
+ +#![feature(allocator_api)]
+
+use std::alloc::{Allocator, Layout, System};
+use std::ptr::{self, NonNull};
+
+let x = Box::new_in(String::from("Hello"), System);
+let (ptr, alloc) = Box::into_raw_with_allocator(x);
+unsafe {
+ ptr::drop_in_place(ptr);
+ let non_null = NonNull::new_unchecked(ptr);
+ alloc.deallocate(non_null.cast(), Layout::new::<String>());
+}
allocator_api
)Returns a reference to the underlying allocator.
+Note: this is an associated function, which means that you have
+to call it as Box::allocator(&b)
instead of b.allocator()
. This
+is so that there is no conflict with a method on the inner type.
Consumes and leaks the Box
, returning a mutable reference,
+&'a mut T
. Note that the type T
must outlive the chosen lifetime
+'a
. If the type has only static references, or none at all, then this
+may be chosen to be 'static
.
This function is mainly useful for data that lives for the remainder of
+the program’s life. Dropping the returned reference will cause a memory
+leak. If this is not acceptable, the reference should first be wrapped
+with the Box::from_raw
function producing a Box
. This Box
can
+then be dropped which will properly destroy T
and release the
+allocated memory.
Note: this is an associated function, which means that you have
+to call it as Box::leak(b)
instead of b.leak()
. This
+is so that there is no conflict with a method on the inner type.
Simple usage:
+ +let x = Box::new(41);
+let static_ref: &'static mut usize = Box::leak(x);
+*static_ref += 1;
+assert_eq!(*static_ref, 42);
Unsized data:
+ +let x = vec![1, 2, 3].into_boxed_slice();
+let static_ref = Box::leak(x);
+static_ref[0] = 4;
+assert_eq!(*static_ref, [4, 2, 3]);
Converts a Box<T>
into a Pin<Box<T>>
. If T
does not implement Unpin
, then
+*boxed
will be pinned in memory and unable to be moved.
This conversion does not allocate on the heap and happens in place.
+This is also available via From
.
Constructing and pinning a Box
with Box::into_pin(Box::new(x))
+can also be written more concisely using Box::pin(x)
.
+This into_pin
method is useful if you already have a Box<T>
, or you are
+constructing a (pinned) Box
in a different way than with Box::new
.
It’s not recommended that crates add an impl like From<Box<T>> for Pin<T>
,
+as it’ll introduce an ambiguity when calling Pin::from
.
+A demonstration of such a poor impl is shown below.
struct Foo; // A type defined in this crate.
+impl From<Box<()>> for Pin<Foo> {
+ fn from(_: Box<()>) -> Pin<Foo> {
+ Pin::new(Foo)
+ }
+}
+
+let foo = Box::new(());
+let bar = Pin::from(foo);
Attempt to downcast the box to a concrete type.
+use std::any::Any;
+
+fn print_if_string(value: Box<dyn Any>) {
+ if let Ok(string) = value.downcast::<String>() {
+ println!("String ({}): {}", string.len(), string);
+ }
+}
+
+let my_string = "Hello World".to_string();
+print_if_string(Box::new(my_string));
+print_if_string(Box::new(0i8));
downcast_unchecked
)Downcasts the box to a concrete type.
+For a safe alternative see downcast
.
#![feature(downcast_unchecked)]
+
+use std::any::Any;
+
+let x: Box<dyn Any> = Box::new(1_usize);
+
+unsafe {
+ assert_eq!(*x.downcast_unchecked::<usize>(), 1);
+}
The contained value must be of type T
. Calling this method
+with the incorrect type is undefined behavior.
Attempt to downcast the box to a concrete type.
+use std::any::Any;
+
+fn print_if_string(value: Box<dyn Any + Send>) {
+ if let Ok(string) = value.downcast::<String>() {
+ println!("String ({}): {}", string.len(), string);
+ }
+}
+
+let my_string = "Hello World".to_string();
+print_if_string(Box::new(my_string));
+print_if_string(Box::new(0i8));
downcast_unchecked
)Downcasts the box to a concrete type.
+For a safe alternative see downcast
.
#![feature(downcast_unchecked)]
+
+use std::any::Any;
+
+let x: Box<dyn Any + Send> = Box::new(1_usize);
+
+unsafe {
+ assert_eq!(*x.downcast_unchecked::<usize>(), 1);
+}
The contained value must be of type T
. Calling this method
+with the incorrect type is undefined behavior.
Attempt to downcast the box to a concrete type.
+use std::any::Any;
+
+fn print_if_string(value: Box<dyn Any + Send + Sync>) {
+ if let Ok(string) = value.downcast::<String>() {
+ println!("String ({}): {}", string.len(), string);
+ }
+}
+
+let my_string = "Hello World".to_string();
+print_if_string(Box::new(my_string));
+print_if_string(Box::new(0i8));
downcast_unchecked
)Downcasts the box to a concrete type.
+For a safe alternative see downcast
.
#![feature(downcast_unchecked)]
+
+use std::any::Any;
+
+let x: Box<dyn Any + Send + Sync> = Box::new(1_usize);
+
+unsafe {
+ assert_eq!(*x.downcast_unchecked::<usize>(), 1);
+}
The contained value must be of type T
. Calling this method
+with the incorrect type is undefined behavior.
async_fn_traits
)AsyncFn::async_call
.async_fn_traits
)AsyncFn
, returning a future which may borrow from the called closure.async_fn_traits
)AsyncFnMut::async_call_mut
.async_fn_traits
)AsyncFnMut
, returning a future which may borrow from the called closure.async_fn_traits
)async_fn_traits
)AsyncFnOnce::async_call_once
.async_fn_traits
)AsyncFnOnce
, returning a future which may move out of the called closure.async_iterator
)async_iterator
)None
if the async iterator is exhausted. Read moreamt
bytes have been consumed from the buffer,
+so they should no longer be returned in calls to read
. Read more0xA
byte) is reached, and append
+them to the provided String
buffer. Read morebuf_read_has_data_left
)Read
has any data left to be read. Read morebufread_skip_until
)byte
or EOF is reached. Read moreReturns a new box with a clone()
of this box’s contents.
let x = Box::new(5);
+let y = x.clone();
+
+// The value is the same
+assert_eq!(x, y);
+
+// But they are unique objects
+assert_ne!(&*x as *const i32, &*y as *const i32);
Copies source
’s contents into self
without creating a new allocation.
let x = Box::new(5);
+let mut y = Box::new(10);
+let yp: *const i32 = &*y;
+
+y.clone_from(&x);
+
+// The value is the same
+assert_eq!(x, y);
+
+// And no allocation occurred
+assert_eq!(yp, &*y);
coroutine_trait
)n
th element from the end of the iterator. Read moreiter_advance_by
)n
elements. Read moreIterator::try_fold()
: it takes
+elements starting from the back of the iterator. Read moreConverts a &[T]
into a Box<[T]>
This conversion allocates on the heap
+and performs a copy of slice
and its contents.
// create a &[u8] which will be used to create a Box<[u8]>
+let slice: &[u8] = &[104, 101, 108, 108, 111];
+let boxed_slice: Box<[u8]> = Box::from(slice);
+
+println!("{boxed_slice:?}");
Converts a str
into a box of dyn Error
+ Send
+ Sync
.
use std::error::Error;
+use std::mem;
+
+let a_str_error = "a str error";
+let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_str_error);
+assert!(
+ mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
Converts a Box<str>
into a Box<[u8]>
This conversion does not allocate on the heap and happens in place.
+// create a Box<str> which will be used to create a Box<[u8]>
+let boxed: Box<str> = Box::from("hello");
+let boxed_str: Box<[u8]> = Box::from(boxed);
+
+// create a &[u8] which will be used to create a Box<[u8]>
+let slice: &[u8] = &[104, 101, 108, 108, 111];
+let boxed_slice = Box::from(slice);
+
+assert_eq!(boxed_slice, boxed_str);
Converts a Cow<'_, str>
into a Box<str>
When cow
is the Cow::Borrowed
variant, this
+conversion allocates on the heap and copies the
+underlying str
. Otherwise, it will try to reuse the owned
+String
’s allocation.
use std::borrow::Cow;
+
+let unboxed = Cow::Borrowed("hello");
+let boxed: Box<str> = Box::from(unboxed);
+println!("{boxed}");
let unboxed = Cow::Owned("hello".to_string());
+let boxed: Box<str> = Box::from(unboxed);
+println!("{boxed}");
Converts a Cow
into a box of dyn Error
.
use std::error::Error;
+use std::mem;
+use std::borrow::Cow;
+
+let a_cow_str_error = Cow::from("a str error");
+let a_boxed_error = Box::<dyn Error>::from(a_cow_str_error);
+assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
Converts a Cow
into a box of dyn Error
+ Send
+ Sync
.
use std::error::Error;
+use std::mem;
+use std::borrow::Cow;
+
+let a_cow_str_error = Cow::from("a str error");
+let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_cow_str_error);
+assert!(
+ mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
Converts a type of Error
into a box of dyn Error
.
use std::error::Error;
+use std::fmt;
+use std::mem;
+
+#[derive(Debug)]
+struct AnError;
+
+impl fmt::Display for AnError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "An error")
+ }
+}
+
+impl Error for AnError {}
+
+let an_error = AnError;
+assert!(0 == mem::size_of_val(&an_error));
+let a_boxed_error = Box::<dyn Error>::from(an_error);
+assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
Converts a type of Error
+ Send
+ Sync
into a box of
+dyn Error
+ Send
+ Sync
.
use std::error::Error;
+use std::fmt;
+use std::mem;
+
+#[derive(Debug)]
+struct AnError;
+
+impl fmt::Display for AnError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "An error")
+ }
+}
+
+impl Error for AnError {}
+
+unsafe impl Send for AnError {}
+
+unsafe impl Sync for AnError {}
+
+let an_error = AnError;
+assert!(0 == mem::size_of_val(&an_error));
+let a_boxed_error = Box::<dyn Error + Send + Sync>::from(an_error);
+assert!(
+ mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
Converts a String
into a box of dyn Error
+ Send
+ Sync
.
use std::error::Error;
+use std::mem;
+
+let a_string_error = "a string error".to_string();
+let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_string_error);
+assert!(
+ mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
Convert a vector into a boxed slice.
+Before doing the conversion, this method discards excess capacity like Vec::shrink_to_fit
.
assert_eq!(Box::from(vec![1, 2, 3]), vec![1, 2, 3].into_boxed_slice());
Any excess capacity is removed:
+ +let mut vec = Vec::with_capacity(10);
+vec.extend([1, 2, 3]);
+
+assert_eq!(Box::from(vec), vec![1, 2, 3].into_boxed_slice());
Convert all colors in place, without reallocating.
+ +use palette::{convert::FromColor, SaturateAssign, Srgb, Lch};
+
+let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)].into_boxed_slice();
+let mut lch = Box::<[Lch]>::from_color(srgb);
+
+lch.saturate_assign(0.1);
+
+let srgb = Box::<[Srgb]>::from_color(lch);
Convert all colors in place, without reallocating.
+ +use palette::{convert::FromColorUnclamped, SaturateAssign, Srgb, Lch};
+
+let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)].into_boxed_slice();
+let mut lch = Box::<[Lch]>::from_color_unclamped(srgb);
+
+lch.saturate_assign(0.1);
+
+let srgb = Box::<[Srgb]>::from_color_unclamped(lch);
u128
into this hasher.usize
into this hasher.i128
into this hasher.isize
into this hasher.hasher_prefixfree_extras
)Ok(None)
if the image does not have one. Read moren
th element of the iterator. Read moreiter_next_chunk
)N
values. Read moreiter_advance_by
)n
elements. Read moreiter_intersperse
)separator
+between adjacent items of the original iterator. Read moren
elements. Read moren
elements, or fewer
+if the underlying iterator ends sooner. Read moreiter_map_windows
)f
for each contiguous window of size N
over
+self
and returns an iterator over the outputs of f
. Like slice::windows()
,
+the windows during mapping overlap as well. Read moreiter_collect_into
)iter_is_partitioned
)true
precede all those that return false
. Read moreiterator_try_reduce
)try_find
)iter_array_chunks
)N
elements of the iterator at a time. Read moreiter_order_by
)Iterator
with those
+of another with respect to the specified comparison function. Read morePartialOrd
elements of
+this Iterator
with those of another. The comparison works like short-circuit
+evaluation, returning a result without comparing the remaining elements.
+As soon as an order can be determined, the evaluation stops and a result is returned. Read moreiter_order_by
)Iterator
with those
+of another with respect to the specified comparison function. Read moreiter_order_by
)Iterator
are lexicographically
+less than those of another. Read moreIterator
are lexicographically
+less or equal to those of another. Read moreIterator
are lexicographically
+greater than those of another. Read moreIterator
are lexicographically
+greater than or equal to those of another. Read moreis_sorted
)is_sorted
)self
and other
) and is used by the <=
+operator. Read moreread_buf
)read
, except that it reads into a slice of buffers. Read morecan_vector
)buf
. Read morebuf
. Read morebuf
. Read moreread_buf
)cursor
. Read moreRead
. Read moredest
with random data. Read moreSubscriber
will
+enable, or None
, if the subscriber does not implement level-based
+filtering or chooses not to implement this method. Read moreEvent
] should be recorded. Read moreSubscriber::try_close
insteadself
is the same type as the provided TypeId
, returns an untyped
+*const
pointer to that type. Otherwise, returns None
. Read moreDispatch
]. Read moretry_components_as
fails to cast.try_components_as_mut
fails to cast.Attempts to convert a Box<[T]>
into a Box<[T; N]>
.
The conversion occurs in-place and does not require a +new memory allocation.
+Returns the old Box<[T]>
in the Err
variant if
+boxed_slice.len()
does not equal N
.
Attempts to convert a Vec<T>
into a Box<[T; N]>
.
Like Vec::into_boxed_slice
, this is in-place if vec.capacity() == N
,
+but will require a reallocation otherwise.
Returns the original Vec<T>
in the Err
variant if
+boxed_slice.len()
does not equal N
.
This can be used with vec!
to create an array on the heap:
let state: Box<[f32; 100]> = vec![1.0; 100].try_into().unwrap();
+assert_eq!(state.len(), 100);
try_from_components
fails to cast.try_from_components
fails to cast.can_vector
)parameters
when converting.self
into C
, using the provided parameters.RngCore
trait object.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.async_iterator
)async_iterator
)async_iterator
)self
into an async iteratorparameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.pattern
)pattern
)self
and the haystack
to search in.pattern
)pattern
)pattern
)pattern
)pattern
)read_f32_into
insteadp
of being true. Read morenumerator/denominator
of being
+true. I.e. gen_ratio(2, 3)
has chance of 2 in 3, or about 67%, of
+returning true. If numerator == denominator
, then the returned value
+is guaranteed to be true
. If numerator == 0
, then the returned
+value is guaranteed to be false
. Read moreself
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read morefmt::Debug
.Error
.Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct RenderOpts {
+ pub width: u32,
+ pub height: u32,
+ pub samples: u32,
+ pub max_depth: u32,
+ pub quiet: bool,
+ pub normalmap: bool,
+}
Rendering options struct
+width: u32
Width of the render in pixels
+height: u32
Height of the render in pixels
+samples: u32
Samples per pixel to render for multisampling. Higher number implies higher quality.
+max_depth: u32
Maximum ray bounce depth. Higher number implies higher quality.
+quiet: bool
Optionally, suppress CLI output
+normalmap: bool
Experimental render mode: return a normal map only instead of doing a full path trace render.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Vec<T, A = Global>where
+ A: Allocator,{ /* private fields */ }
A contiguous growable array type, written as Vec<T>
, short for ‘vector’.
let mut vec = Vec::new();
+vec.push(1);
+vec.push(2);
+
+assert_eq!(vec.len(), 2);
+assert_eq!(vec[0], 1);
+
+assert_eq!(vec.pop(), Some(2));
+assert_eq!(vec.len(), 1);
+
+vec[0] = 7;
+assert_eq!(vec[0], 7);
+
+vec.extend([1, 2, 3]);
+
+for x in &vec {
+ println!("{x}");
+}
+assert_eq!(vec, [7, 1, 2, 3]);
The vec!
macro is provided for convenient initialization:
let mut vec1 = vec![1, 2, 3];
+vec1.push(4);
+let vec2 = Vec::from([1, 2, 3, 4]);
+assert_eq!(vec1, vec2);
It can also initialize each element of a Vec<T>
with a given value.
+This may be more efficient than performing allocation and initialization
+in separate steps, especially when initializing a vector of zeros:
let vec = vec![0; 5];
+assert_eq!(vec, [0, 0, 0, 0, 0]);
+
+// The following is equivalent, but potentially slower:
+let mut vec = Vec::with_capacity(5);
+vec.resize(5, 0);
+assert_eq!(vec, [0, 0, 0, 0, 0]);
For more information, see +Capacity and Reallocation.
+Use a Vec<T>
as an efficient stack:
let mut stack = Vec::new();
+
+stack.push(1);
+stack.push(2);
+stack.push(3);
+
+while let Some(top) = stack.pop() {
+ // Prints 3, 2, 1
+ println!("{top}");
+}
The Vec
type allows access to values by index, because it implements the
+Index
trait. An example will be more explicit:
let v = vec![0, 2, 4, 6];
+println!("{}", v[1]); // it will display '2'
However be careful: if you try to access an index which isn’t in the Vec
,
+your software will panic! You cannot do this:
let v = vec![0, 2, 4, 6];
+println!("{}", v[6]); // it will panic!
Use get
and get_mut
if you want to check whether the index is in
+the Vec
.
A Vec
can be mutable. On the other hand, slices are read-only objects.
+To get a slice, use &
. Example:
fn read_slice(slice: &[usize]) {
+ // ...
+}
+
+let v = vec![0, 1];
+read_slice(&v);
+
+// ... and that's all!
+// you can also do it like this:
+let u: &[usize] = &v;
+// or like this:
+let u: &[_] = &v;
In Rust, it’s more common to pass slices as arguments rather than vectors
+when you just want to provide read access. The same goes for String
and
+&str
.
The capacity of a vector is the amount of space allocated for any future +elements that will be added onto the vector. This is not to be confused with +the length of a vector, which specifies the number of actual elements +within the vector. If a vector’s length exceeds its capacity, its capacity +will automatically be increased, but its elements will have to be +reallocated.
+For example, a vector with capacity 10 and length 0 would be an empty vector
+with space for 10 more elements. Pushing 10 or fewer elements onto the
+vector will not change its capacity or cause reallocation to occur. However,
+if the vector’s length is increased to 11, it will have to reallocate, which
+can be slow. For this reason, it is recommended to use Vec::with_capacity
+whenever possible to specify how big the vector is expected to get.
Due to its incredibly fundamental nature, Vec
makes a lot of guarantees
+about its design. This ensures that it’s as low-overhead as possible in
+the general case, and can be correctly manipulated in primitive ways
+by unsafe code. Note that these guarantees refer to an unqualified Vec<T>
.
+If additional type parameters are added (e.g., to support custom allocators),
+overriding their defaults may change the behavior.
Most fundamentally, Vec
is and always will be a (pointer, capacity, length)
+triplet. No more, no less. The order of these fields is completely
+unspecified, and you should use the appropriate methods to modify these.
+The pointer will never be null, so this type is null-pointer-optimized.
However, the pointer might not actually point to allocated memory. In particular,
+if you construct a Vec
with capacity 0 via Vec::new
, vec![]
,
+Vec::with_capacity(0)
, or by calling shrink_to_fit
+on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized
+types inside a Vec
, it will not allocate space for them. Note that in this case
+the Vec
might not report a capacity
of 0. Vec
will allocate if and only
+if mem::size_of::<T>() * capacity() > 0
. In general, Vec
’s allocation
+details are very subtle — if you intend to allocate memory using a Vec
+and use it for something else (either to pass to unsafe code, or to build your
+own memory-backed collection), be sure to deallocate this memory by using
+from_raw_parts
to recover the Vec
and then dropping it.
If a Vec
has allocated memory, then the memory it points to is on the heap
+(as defined by the allocator Rust is configured to use by default), and its
+pointer points to len
initialized, contiguous elements in order (what
+you would see if you coerced it to a slice), followed by capacity - len
+logically uninitialized, contiguous elements.
A vector containing the elements 'a'
and 'b'
with capacity 4 can be
+visualized as below. The top part is the Vec
struct, it contains a
+pointer to the head of the allocation in the heap, length and capacity.
+The bottom part is the allocation on the heap, a contiguous memory block.
ptr len capacity
+ +--------+--------+--------+
+ | 0x0123 | 2 | 4 |
+ +--------+--------+--------+
+ |
+ v
+Heap +--------+--------+--------+--------+
+ | 'a' | 'b' | uninit | uninit |
+ +--------+--------+--------+--------+
+
MaybeUninit
.Vec
makes no guarantees about its memory
+layout (including the order of fields).Vec
will never perform a “small optimization” where elements are actually
+stored on the stack for two reasons:
It would make it more difficult for unsafe code to correctly manipulate
+a Vec
. The contents of a Vec
wouldn’t have a stable address if it were
+only moved, and it would be more difficult to determine if a Vec
had
+actually allocated memory.
It would penalize the general case, incurring an additional branch +on every access.
+Vec
will never automatically shrink itself, even if completely empty. This
+ensures no unnecessary allocations or deallocations occur. Emptying a Vec
+and then filling it back up to the same len
should incur no calls to
+the allocator. If you wish to free up unused memory, use
+shrink_to_fit
or shrink_to
.
push
and insert
will never (re)allocate if the reported capacity is
+sufficient. push
and insert
will (re)allocate if
+len == capacity
. That is, the reported capacity is completely
+accurate, and can be relied on. It can even be used to manually free the memory
+allocated by a Vec
if desired. Bulk insertion methods may reallocate, even
+when not necessary.
Vec
does not guarantee any particular growth strategy when reallocating
+when full, nor when reserve
is called. The current strategy is basic
+and it may prove desirable to use a non-constant growth factor. Whatever
+strategy is used will of course guarantee O(1) amortized push
.
vec![x; n]
, vec![a, b, c, d]
, and
+Vec::with_capacity(n)
, will all produce a Vec
+with at least the requested capacity. If len == capacity
,
+(as is the case for the vec!
macro), then a Vec<T>
can be converted to
+and from a Box<[T]>
without reallocating or moving the elements.
Vec
will not specifically overwrite any data that is removed from it,
+but also won’t specifically preserve it. Its uninitialized memory is
+scratch space that it may use however it wants. It will generally just do
+whatever is most efficient or otherwise easy to implement. Do not rely on
+removed data to be erased for security purposes. Even if you drop a Vec
, its
+buffer may simply be reused by another allocation. Even if you zero a Vec
’s memory
+first, that might not actually happen because the optimizer does not consider
+this a side-effect that must be preserved. There is one case which we will
+not break, however: using unsafe
code to write to the excess capacity,
+and then increasing the length to match, is always valid.
Currently, Vec
does not guarantee the order in which elements are dropped.
+The order has changed in the past and may change again.
Constructs a new, empty Vec<T>
.
The vector will not allocate until elements are pushed onto it.
+let mut vec: Vec<i32> = Vec::new();
Constructs a new, empty Vec<T>
with at least the specified capacity.
The vector will be able to hold at least capacity
elements without
+reallocating. This method is allowed to allocate for more elements than
+capacity
. If capacity
is 0, the vector will not allocate.
It is important to note that although the returned vector has the +minimum capacity specified, the vector will have a zero length. For +an explanation of the difference between length and capacity, see +Capacity and reallocation.
+If it is important to know the exact allocated capacity of a Vec
,
+always use the capacity
method after construction.
For Vec<T>
where T
is a zero-sized type, there will be no allocation
+and the capacity will always be usize::MAX
.
Panics if the new capacity exceeds isize::MAX
bytes.
let mut vec = Vec::with_capacity(10);
+
+// The vector contains no items, even though it has capacity for more
+assert_eq!(vec.len(), 0);
+assert!(vec.capacity() >= 10);
+
+// These are all done without reallocating...
+for i in 0..10 {
+ vec.push(i);
+}
+assert_eq!(vec.len(), 10);
+assert!(vec.capacity() >= 10);
+
+// ...but this may make the vector reallocate
+vec.push(11);
+assert_eq!(vec.len(), 11);
+assert!(vec.capacity() >= 11);
+
+// A vector of a zero-sized type will always over-allocate, since no
+// allocation is necessary
+let vec_units = Vec::<()>::with_capacity(10);
+assert_eq!(vec_units.capacity(), usize::MAX);
try_with_capacity
)Constructs a new, empty Vec<T>
with at least the specified capacity.
The vector will be able to hold at least capacity
elements without
+reallocating. This method is allowed to allocate for more elements than
+capacity
. If capacity
is 0, the vector will not allocate.
Returns an error if the capacity exceeds isize::MAX
bytes,
+or if the allocator reports allocation failure.
Creates a Vec<T>
directly from a pointer, a length, and a capacity.
This is highly unsafe, due to the number of invariants that aren’t +checked:
+ptr
must have been allocated using the global allocator, such as via
+the alloc::alloc
function.T
needs to have the same alignment as what ptr
was allocated with.
+(T
having a less strict alignment is not sufficient, the alignment really
+needs to be equal to satisfy the dealloc
requirement that memory must be
+allocated and deallocated with the same layout.)T
times the capacity
(ie. the allocated size in bytes) needs
+to be the same size as the pointer was allocated with. (Because similar to
+alignment, dealloc
must be called with the same layout size
.)length
needs to be less than or equal to capacity
.length
values must be properly initialized values of type T
.capacity
needs to be the capacity that the pointer was allocated with.isize::MAX
.
+See the safety documentation of pointer::offset
.These requirements are always upheld by any ptr
that has been allocated
+via Vec<T>
. Other allocation sources are allowed if the invariants are
+upheld.
Violating these may cause problems like corrupting the allocator’s
+internal data structures. For example it is normally not safe
+to build a Vec<u8>
from a pointer to a C char
array with length
+size_t
, doing so is only safe if the array was initially allocated by
+a Vec
or String
.
+It’s also not safe to build one from a Vec<u16>
and its length, because
+the allocator cares about the alignment, and these two types have different
+alignments. The buffer was allocated with alignment 2 (for u16
), but after
+turning it into a Vec<u8>
it’ll be deallocated with alignment 1. To avoid
+these issues, it is often preferable to do casting/transmuting using
+slice::from_raw_parts
instead.
The ownership of ptr
is effectively transferred to the
+Vec<T>
which may then deallocate, reallocate or change the
+contents of memory pointed to by the pointer at will. Ensure
+that nothing else uses the pointer after calling this
+function.
use std::ptr;
+use std::mem;
+
+let v = vec![1, 2, 3];
+
+// Prevent running `v`'s destructor so we are in complete control
+// of the allocation.
+let mut v = mem::ManuallyDrop::new(v);
+
+// Pull out the various important pieces of information about `v`
+let p = v.as_mut_ptr();
+let len = v.len();
+let cap = v.capacity();
+
+unsafe {
+ // Overwrite memory with 4, 5, 6
+ for i in 0..len {
+ ptr::write(p.add(i), 4 + i);
+ }
+
+ // Put everything back together into a Vec
+ let rebuilt = Vec::from_raw_parts(p, len, cap);
+ assert_eq!(rebuilt, [4, 5, 6]);
+}
Using memory that was allocated elsewhere:
+ +use std::alloc::{alloc, Layout};
+
+fn main() {
+ let layout = Layout::array::<u32>(16).expect("overflow cannot happen");
+
+ let vec = unsafe {
+ let mem = alloc(layout).cast::<u32>();
+ if mem.is_null() {
+ return;
+ }
+
+ mem.write(1_000_000);
+
+ Vec::from_raw_parts(mem, 1, 16)
+ };
+
+ assert_eq!(vec, &[1_000_000]);
+ assert_eq!(vec.capacity(), 16);
+}
allocator_api
)Constructs a new, empty Vec<T, A>
.
The vector will not allocate until elements are pushed onto it.
+#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let mut vec: Vec<i32, _> = Vec::new_in(System);
allocator_api
)Constructs a new, empty Vec<T, A>
with at least the specified capacity
+with the provided allocator.
The vector will be able to hold at least capacity
elements without
+reallocating. This method is allowed to allocate for more elements than
+capacity
. If capacity
is 0, the vector will not allocate.
It is important to note that although the returned vector has the +minimum capacity specified, the vector will have a zero length. For +an explanation of the difference between length and capacity, see +Capacity and reallocation.
+If it is important to know the exact allocated capacity of a Vec
,
+always use the capacity
method after construction.
For Vec<T, A>
where T
is a zero-sized type, there will be no allocation
+and the capacity will always be usize::MAX
.
Panics if the new capacity exceeds isize::MAX
bytes.
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let mut vec = Vec::with_capacity_in(10, System);
+
+// The vector contains no items, even though it has capacity for more
+assert_eq!(vec.len(), 0);
+assert!(vec.capacity() >= 10);
+
+// These are all done without reallocating...
+for i in 0..10 {
+ vec.push(i);
+}
+assert_eq!(vec.len(), 10);
+assert!(vec.capacity() >= 10);
+
+// ...but this may make the vector reallocate
+vec.push(11);
+assert_eq!(vec.len(), 11);
+assert!(vec.capacity() >= 11);
+
+// A vector of a zero-sized type will always over-allocate, since no
+// allocation is necessary
+let vec_units = Vec::<(), System>::with_capacity_in(10, System);
+assert_eq!(vec_units.capacity(), usize::MAX);
allocator_api
)Constructs a new, empty Vec<T, A>
with at least the specified capacity
+with the provided allocator.
The vector will be able to hold at least capacity
elements without
+reallocating. This method is allowed to allocate for more elements than
+capacity
. If capacity
is 0, the vector will not allocate.
Returns an error if the capacity exceeds isize::MAX
bytes,
+or if the allocator reports allocation failure.
allocator_api
)Creates a Vec<T, A>
directly from a pointer, a length, a capacity,
+and an allocator.
This is highly unsafe, due to the number of invariants that aren’t +checked:
+ptr
must be currently allocated via the given allocator alloc
.T
needs to have the same alignment as what ptr
was allocated with.
+(T
having a less strict alignment is not sufficient, the alignment really
+needs to be equal to satisfy the dealloc
requirement that memory must be
+allocated and deallocated with the same layout.)T
times the capacity
(ie. the allocated size in bytes) needs
+to be the same size as the pointer was allocated with. (Because similar to
+alignment, dealloc
must be called with the same layout size
.)length
needs to be less than or equal to capacity
.length
values must be properly initialized values of type T
.capacity
needs to fit the layout size that the pointer was allocated with.isize::MAX
.
+See the safety documentation of pointer::offset
.These requirements are always upheld by any ptr
that has been allocated
+via Vec<T, A>
. Other allocation sources are allowed if the invariants are
+upheld.
Violating these may cause problems like corrupting the allocator’s
+internal data structures. For example it is not safe
+to build a Vec<u8>
from a pointer to a C char
array with length size_t
.
+It’s also not safe to build one from a Vec<u16>
and its length, because
+the allocator cares about the alignment, and these two types have different
+alignments. The buffer was allocated with alignment 2 (for u16
), but after
+turning it into a Vec<u8>
it’ll be deallocated with alignment 1.
The ownership of ptr
is effectively transferred to the
+Vec<T>
which may then deallocate, reallocate or change the
+contents of memory pointed to by the pointer at will. Ensure
+that nothing else uses the pointer after calling this
+function.
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+use std::ptr;
+use std::mem;
+
+let mut v = Vec::with_capacity_in(3, System);
+v.push(1);
+v.push(2);
+v.push(3);
+
+// Prevent running `v`'s destructor so we are in complete control
+// of the allocation.
+let mut v = mem::ManuallyDrop::new(v);
+
+// Pull out the various important pieces of information about `v`
+let p = v.as_mut_ptr();
+let len = v.len();
+let cap = v.capacity();
+let alloc = v.allocator();
+
+unsafe {
+ // Overwrite memory with 4, 5, 6
+ for i in 0..len {
+ ptr::write(p.add(i), 4 + i);
+ }
+
+ // Put everything back together into a Vec
+ let rebuilt = Vec::from_raw_parts_in(p, len, cap, alloc.clone());
+ assert_eq!(rebuilt, [4, 5, 6]);
+}
Using memory that was allocated elsewhere:
+ +#![feature(allocator_api)]
+
+use std::alloc::{AllocError, Allocator, Global, Layout};
+
+fn main() {
+ let layout = Layout::array::<u32>(16).expect("overflow cannot happen");
+
+ let vec = unsafe {
+ let mem = match Global.allocate(layout) {
+ Ok(mem) => mem.cast::<u32>().as_ptr(),
+ Err(AllocError) => return,
+ };
+
+ mem.write(1_000_000);
+
+ Vec::from_raw_parts_in(mem, 1, 16, Global)
+ };
+
+ assert_eq!(vec, &[1_000_000]);
+ assert_eq!(vec.capacity(), 16);
+}
vec_into_raw_parts
)Decomposes a Vec<T>
into its raw components: (pointer, length, capacity)
.
Returns the raw pointer to the underlying data, the length of
+the vector (in elements), and the allocated capacity of the
+data (in elements). These are the same arguments in the same
+order as the arguments to from_raw_parts
.
After calling this function, the caller is responsible for the
+memory previously managed by the Vec
. The only way to do
+this is to convert the raw pointer, length, and capacity back
+into a Vec
with the from_raw_parts
function, allowing
+the destructor to perform the cleanup.
#![feature(vec_into_raw_parts)]
+let v: Vec<i32> = vec![-1, 0, 1];
+
+let (ptr, len, cap) = v.into_raw_parts();
+
+let rebuilt = unsafe {
+ // We can now make changes to the components, such as
+ // transmuting the raw pointer to a compatible type.
+ let ptr = ptr as *mut u32;
+
+ Vec::from_raw_parts(ptr, len, cap)
+};
+assert_eq!(rebuilt, [4294967295, 0, 1]);
allocator_api
)Decomposes a Vec<T>
into its raw components: (pointer, length, capacity, allocator)
.
Returns the raw pointer to the underlying data, the length of the vector (in elements),
+the allocated capacity of the data (in elements), and the allocator. These are the same
+arguments in the same order as the arguments to from_raw_parts_in
.
After calling this function, the caller is responsible for the
+memory previously managed by the Vec
. The only way to do
+this is to convert the raw pointer, length, and capacity back
+into a Vec
with the from_raw_parts_in
function, allowing
+the destructor to perform the cleanup.
#![feature(allocator_api, vec_into_raw_parts)]
+
+use std::alloc::System;
+
+let mut v: Vec<i32, System> = Vec::new_in(System);
+v.push(-1);
+v.push(0);
+v.push(1);
+
+let (ptr, len, cap, alloc) = v.into_raw_parts_with_alloc();
+
+let rebuilt = unsafe {
+ // We can now make changes to the components, such as
+ // transmuting the raw pointer to a compatible type.
+ let ptr = ptr as *mut u32;
+
+ Vec::from_raw_parts_in(ptr, len, cap, alloc)
+};
+assert_eq!(rebuilt, [4294967295, 0, 1]);
Returns the total number of elements the vector can hold without +reallocating.
+let mut vec: Vec<i32> = Vec::with_capacity(10);
+vec.push(42);
+assert!(vec.capacity() >= 10);
Reserves capacity for at least additional
more elements to be inserted
+in the given Vec<T>
. The collection may reserve more space to
+speculatively avoid frequent reallocations. After calling reserve
,
+capacity will be greater than or equal to self.len() + additional
.
+Does nothing if capacity is already sufficient.
Panics if the new capacity exceeds isize::MAX
bytes.
let mut vec = vec![1];
+vec.reserve(10);
+assert!(vec.capacity() >= 11);
Reserves the minimum capacity for at least additional
more elements to
+be inserted in the given Vec<T>
. Unlike reserve
, this will not
+deliberately over-allocate to speculatively avoid frequent allocations.
+After calling reserve_exact
, capacity will be greater than or equal to
+self.len() + additional
. Does nothing if the capacity is already
+sufficient.
Note that the allocator may give the collection more space than it
+requests. Therefore, capacity can not be relied upon to be precisely
+minimal. Prefer reserve
if future insertions are expected.
Panics if the new capacity exceeds isize::MAX
bytes.
let mut vec = vec![1];
+vec.reserve_exact(10);
+assert!(vec.capacity() >= 11);
Tries to reserve capacity for at least additional
more elements to be inserted
+in the given Vec<T>
. The collection may reserve more space to speculatively avoid
+frequent reallocations. After calling try_reserve
, capacity will be
+greater than or equal to self.len() + additional
if it returns
+Ok(())
. Does nothing if capacity is already sufficient. This method
+preserves the contents even if an error occurs.
If the capacity overflows, or the allocator reports a failure, then an error +is returned.
+use std::collections::TryReserveError;
+
+fn process_data(data: &[u32]) -> Result<Vec<u32>, TryReserveError> {
+ let mut output = Vec::new();
+
+ // Pre-reserve the memory, exiting if we can't
+ output.try_reserve(data.len())?;
+
+ // Now we know this can't OOM in the middle of our complex work
+ output.extend(data.iter().map(|&val| {
+ val * 2 + 5 // very complicated
+ }));
+
+ Ok(output)
+}
Tries to reserve the minimum capacity for at least additional
+elements to be inserted in the given Vec<T>
. Unlike try_reserve
,
+this will not deliberately over-allocate to speculatively avoid frequent
+allocations. After calling try_reserve_exact
, capacity will be greater
+than or equal to self.len() + additional
if it returns Ok(())
.
+Does nothing if the capacity is already sufficient.
Note that the allocator may give the collection more space than it
+requests. Therefore, capacity can not be relied upon to be precisely
+minimal. Prefer try_reserve
if future insertions are expected.
If the capacity overflows, or the allocator reports a failure, then an error +is returned.
+use std::collections::TryReserveError;
+
+fn process_data(data: &[u32]) -> Result<Vec<u32>, TryReserveError> {
+ let mut output = Vec::new();
+
+ // Pre-reserve the memory, exiting if we can't
+ output.try_reserve_exact(data.len())?;
+
+ // Now we know this can't OOM in the middle of our complex work
+ output.extend(data.iter().map(|&val| {
+ val * 2 + 5 // very complicated
+ }));
+
+ Ok(output)
+}
Shrinks the capacity of the vector as much as possible.
+The behavior of this method depends on the allocator, which may either shrink the vector
+in-place or reallocate. The resulting vector might still have some excess capacity, just as
+is the case for with_capacity
. See Allocator::shrink
for more details.
let mut vec = Vec::with_capacity(10);
+vec.extend([1, 2, 3]);
+assert!(vec.capacity() >= 10);
+vec.shrink_to_fit();
+assert!(vec.capacity() >= 3);
Shrinks the capacity of the vector with a lower bound.
+The capacity will remain at least as large as both the length +and the supplied value.
+If the current capacity is less than the lower limit, this is a no-op.
+let mut vec = Vec::with_capacity(10);
+vec.extend([1, 2, 3]);
+assert!(vec.capacity() >= 10);
+vec.shrink_to(4);
+assert!(vec.capacity() >= 4);
+vec.shrink_to(0);
+assert!(vec.capacity() >= 3);
Converts the vector into Box<[T]>
.
Before doing the conversion, this method discards excess capacity like shrink_to_fit
.
let v = vec![1, 2, 3];
+
+let slice = v.into_boxed_slice();
Any excess capacity is removed:
+ +let mut vec = Vec::with_capacity(10);
+vec.extend([1, 2, 3]);
+
+assert!(vec.capacity() >= 10);
+let slice = vec.into_boxed_slice();
+assert_eq!(slice.into_vec().capacity(), 3);
Shortens the vector, keeping the first len
elements and dropping
+the rest.
If len
is greater or equal to the vector’s current length, this has
+no effect.
The drain
method can emulate truncate
, but causes the excess
+elements to be returned instead of dropped.
Note that this method has no effect on the allocated capacity +of the vector.
+Truncating a five element vector to two elements:
+ +let mut vec = vec![1, 2, 3, 4, 5];
+vec.truncate(2);
+assert_eq!(vec, [1, 2]);
No truncation occurs when len
is greater than the vector’s current
+length:
let mut vec = vec![1, 2, 3];
+vec.truncate(8);
+assert_eq!(vec, [1, 2, 3]);
Truncating when len == 0
is equivalent to calling the clear
+method.
let mut vec = vec![1, 2, 3];
+vec.truncate(0);
+assert_eq!(vec, []);
Extracts a slice containing the entire vector.
+Equivalent to &s[..]
.
use std::io::{self, Write};
+let buffer = vec![1, 2, 3, 5, 8];
+io::sink().write(buffer.as_slice()).unwrap();
Extracts a mutable slice of the entire vector.
+Equivalent to &mut s[..]
.
use std::io::{self, Read};
+let mut buffer = vec![0; 3];
+io::repeat(0b101).read_exact(buffer.as_mut_slice()).unwrap();
Returns a raw pointer to the vector’s buffer, or a dangling raw pointer +valid for zero sized reads if the vector didn’t allocate.
+The caller must ensure that the vector outlives the pointer this +function returns, or else it will end up pointing to garbage. +Modifying the vector may cause its buffer to be reallocated, +which would also make any pointers to it invalid.
+The caller must also ensure that the memory the pointer (non-transitively) points to
+is never written to (except inside an UnsafeCell
) using this pointer or any pointer
+derived from it. If you need to mutate the contents of the slice, use as_mut_ptr
.
This method guarantees that for the purpose of the aliasing model, this method
+does not materialize a reference to the underlying slice, and thus the returned pointer
+will remain valid when mixed with other calls to as_ptr
and as_mut_ptr
.
+Note that calling other methods that materialize mutable references to the slice,
+or mutable references to specific elements you are planning on accessing through this pointer,
+as well as writing to those elements, may still invalidate this pointer.
+See the second example below for how this guarantee can be used.
let x = vec![1, 2, 4];
+let x_ptr = x.as_ptr();
+
+unsafe {
+ for i in 0..x.len() {
+ assert_eq!(*x_ptr.add(i), 1 << i);
+ }
+}
Due to the aliasing guarantee, the following code is legal:
+ +unsafe {
+ let mut v = vec![0, 1, 2];
+ let ptr1 = v.as_ptr();
+ let _ = ptr1.read();
+ let ptr2 = v.as_mut_ptr().offset(2);
+ ptr2.write(2);
+ // Notably, the write to `ptr2` did *not* invalidate `ptr1`
+ // because it mutated a different element:
+ let _ = ptr1.read();
+}
Returns an unsafe mutable pointer to the vector’s buffer, or a dangling +raw pointer valid for zero sized reads if the vector didn’t allocate.
+The caller must ensure that the vector outlives the pointer this +function returns, or else it will end up pointing to garbage. +Modifying the vector may cause its buffer to be reallocated, +which would also make any pointers to it invalid.
+This method guarantees that for the purpose of the aliasing model, this method
+does not materialize a reference to the underlying slice, and thus the returned pointer
+will remain valid when mixed with other calls to as_ptr
and as_mut_ptr
.
+Note that calling other methods that materialize references to the slice,
+or references to specific elements you are planning on accessing through this pointer,
+may still invalidate this pointer.
+See the second example below for how this guarantee can be used.
// Allocate vector big enough for 4 elements.
+let size = 4;
+let mut x: Vec<i32> = Vec::with_capacity(size);
+let x_ptr = x.as_mut_ptr();
+
+// Initialize elements via raw pointer writes, then set length.
+unsafe {
+ for i in 0..size {
+ *x_ptr.add(i) = i as i32;
+ }
+ x.set_len(size);
+}
+assert_eq!(&*x, &[0, 1, 2, 3]);
Due to the aliasing guarantee, the following code is legal:
+ +unsafe {
+ let mut v = vec![0];
+ let ptr1 = v.as_mut_ptr();
+ ptr1.write(1);
+ let ptr2 = v.as_mut_ptr();
+ ptr2.write(2);
+ // Notably, the write to `ptr2` did *not* invalidate `ptr1`:
+ ptr1.write(3);
+}
allocator_api
)Returns a reference to the underlying allocator.
+Forces the length of the vector to new_len
.
This is a low-level operation that maintains none of the normal
+invariants of the type. Normally changing the length of a vector
+is done using one of the safe operations instead, such as
+truncate
, resize
, extend
, or clear
.
new_len
must be less than or equal to capacity()
.old_len..new_len
must be initialized.This method can be useful for situations in which the vector +is serving as a buffer for other code, particularly over FFI:
+ +pub fn get_dictionary(&self) -> Option<Vec<u8>> {
+ // Per the FFI method's docs, "32768 bytes is always enough".
+ let mut dict = Vec::with_capacity(32_768);
+ let mut dict_length = 0;
+ // SAFETY: When `deflateGetDictionary` returns `Z_OK`, it holds that:
+ // 1. `dict_length` elements were initialized.
+ // 2. `dict_length` <= the capacity (32_768)
+ // which makes `set_len` safe to call.
+ unsafe {
+ // Make the FFI call...
+ let r = deflateGetDictionary(self.strm, dict.as_mut_ptr(), &mut dict_length);
+ if r == Z_OK {
+ // ...and update the length to what was initialized.
+ dict.set_len(dict_length);
+ Some(dict)
+ } else {
+ None
+ }
+ }
+}
While the following example is sound, there is a memory leak since
+the inner vectors were not freed prior to the set_len
call:
let mut vec = vec![vec![1, 0, 0],
+ vec![0, 1, 0],
+ vec![0, 0, 1]];
+// SAFETY:
+// 1. `old_len..0` is empty so no elements need to be initialized.
+// 2. `0 <= capacity` always holds whatever `capacity` is.
+unsafe {
+ vec.set_len(0);
+}
Normally, here, one would use clear
instead to correctly drop
+the contents and thus not leak memory.
Removes an element from the vector and returns it.
+The removed element is replaced by the last element of the vector.
+This does not preserve ordering, but is O(1).
+If you need to preserve the element order, use remove
instead.
Panics if index
is out of bounds.
let mut v = vec!["foo", "bar", "baz", "qux"];
+
+assert_eq!(v.swap_remove(1), "bar");
+assert_eq!(v, ["foo", "qux", "baz"]);
+
+assert_eq!(v.swap_remove(0), "foo");
+assert_eq!(v, ["baz", "qux"]);
Inserts an element at position index
within the vector, shifting all
+elements after it to the right.
Panics if index > len
.
let mut vec = vec![1, 2, 3];
+vec.insert(1, 4);
+assert_eq!(vec, [1, 4, 2, 3]);
+vec.insert(4, 5);
+assert_eq!(vec, [1, 4, 2, 3, 5]);
Takes O(Vec::len
) time. All items after the insertion index must be
+shifted to the right. In the worst case, all elements are shifted when
+the insertion index is 0.
Removes and returns the element at position index
within the vector,
+shifting all elements after it to the left.
Note: Because this shifts over the remaining elements, it has a
+worst-case performance of O(n). If you don’t need the order of elements
+to be preserved, use swap_remove
instead. If you’d like to remove
+elements from the beginning of the Vec
, consider using
+VecDeque::pop_front
instead.
Panics if index
is out of bounds.
let mut v = vec![1, 2, 3];
+assert_eq!(v.remove(1), 2);
+assert_eq!(v, [1, 3]);
Retains only the elements specified by the predicate.
+In other words, remove all elements e
for which f(&e)
returns false
.
+This method operates in place, visiting each element exactly once in the
+original order, and preserves the order of the retained elements.
let mut vec = vec![1, 2, 3, 4];
+vec.retain(|&x| x % 2 == 0);
+assert_eq!(vec, [2, 4]);
Because the elements are visited exactly once in the original order, +external state may be used to decide which elements to keep.
+ +let mut vec = vec![1, 2, 3, 4, 5];
+let keep = [false, true, true, false, true];
+let mut iter = keep.iter();
+vec.retain(|_| *iter.next().unwrap());
+assert_eq!(vec, [2, 3, 5]);
Retains only the elements specified by the predicate, passing a mutable reference to it.
+In other words, remove all elements e
such that f(&mut e)
returns false
.
+This method operates in place, visiting each element exactly once in the
+original order, and preserves the order of the retained elements.
let mut vec = vec![1, 2, 3, 4];
+vec.retain_mut(|x| if *x <= 3 {
+ *x += 1;
+ true
+} else {
+ false
+});
+assert_eq!(vec, [2, 3, 4]);
Removes all but the first of consecutive elements in the vector that resolve to the same +key.
+If the vector is sorted, this removes all duplicates.
+let mut vec = vec![10, 20, 21, 30, 20];
+
+vec.dedup_by_key(|i| *i / 10);
+
+assert_eq!(vec, [10, 20, 30, 20]);
Removes all but the first of consecutive elements in the vector satisfying a given equality +relation.
+The same_bucket
function is passed references to two elements from the vector and
+must determine if the elements compare equal. The elements are passed in opposite order
+from their order in the slice, so if same_bucket(a, b)
returns true
, a
is removed.
If the vector is sorted, this removes all duplicates.
+let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"];
+
+vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+
+assert_eq!(vec, ["foo", "bar", "baz", "bar"]);
Appends an element to the back of a collection.
+Panics if the new capacity exceeds isize::MAX
bytes.
let mut vec = vec![1, 2];
+vec.push(3);
+assert_eq!(vec, [1, 2, 3]);
Takes amortized O(1) time. If the vector’s length would exceed its +capacity after the push, O(capacity) time is taken to copy the +vector’s elements to a larger allocation. This expensive operation is +offset by the capacity O(1) insertions it allows.
+vec_push_within_capacity
)Appends an element if there is sufficient spare capacity, otherwise an error is returned +with the element.
+Unlike push
this method will not reallocate when there’s insufficient capacity.
+The caller should use reserve
or try_reserve
to ensure that there is enough capacity.
A manual, panic-free alternative to FromIterator
:
#![feature(vec_push_within_capacity)]
+
+use std::collections::TryReserveError;
+fn from_iter_fallible<T>(iter: impl Iterator<Item=T>) -> Result<Vec<T>, TryReserveError> {
+ let mut vec = Vec::new();
+ for value in iter {
+ if let Err(value) = vec.push_within_capacity(value) {
+ vec.try_reserve(1)?;
+ // this cannot fail, the previous line either returned or added at least 1 free slot
+ let _ = vec.push_within_capacity(value);
+ }
+ }
+ Ok(vec)
+}
+assert_eq!(from_iter_fallible(0..100), Ok(Vec::from_iter(0..100)));
Takes O(1) time.
+Removes the last element from a vector and returns it, or None
if it
+is empty.
If you’d like to pop the first element, consider using
+VecDeque::pop_front
instead.
let mut vec = vec![1, 2, 3];
+assert_eq!(vec.pop(), Some(3));
+assert_eq!(vec, [1, 2]);
Takes O(1) time.
+Removes the specified range from the vector in bulk, returning all +removed elements as an iterator. If the iterator is dropped before +being fully consumed, it drops the remaining removed elements.
+The returned iterator keeps a mutable borrow on the vector to optimize +its implementation.
+Panics if the starting point is greater than the end point or if +the end point is greater than the length of the vector.
+If the returned iterator goes out of scope without being dropped (due to
+mem::forget
, for example), the vector may have lost and leaked
+elements arbitrarily, including elements outside the range.
let mut v = vec![1, 2, 3];
+let u: Vec<_> = v.drain(1..).collect();
+assert_eq!(v, &[1]);
+assert_eq!(u, &[2, 3]);
+
+// A full range clears the vector, like `clear()` does
+v.drain(..);
+assert_eq!(v, &[]);
Clears the vector, removing all values.
+Note that this method has no effect on the allocated capacity +of the vector.
+let mut v = vec![1, 2, 3];
+
+v.clear();
+
+assert!(v.is_empty());
Returns the number of elements in the vector, also referred to +as its ‘length’.
+let a = vec![1, 2, 3];
+assert_eq!(a.len(), 3);
Returns true
if the vector contains no elements.
let mut v = Vec::new();
+assert!(v.is_empty());
+
+v.push(1);
+assert!(!v.is_empty());
Splits the collection into two at the given index.
+Returns a newly allocated vector containing the elements in the range
+[at, len)
. After the call, the original vector will be left containing
+the elements [0, at)
with its previous capacity unchanged.
mem::take
or mem::replace
.Vec::truncate
.Vec::drain
.Panics if at > len
.
let mut vec = vec![1, 2, 3];
+let vec2 = vec.split_off(1);
+assert_eq!(vec, [1]);
+assert_eq!(vec2, [2, 3]);
Resizes the Vec
in-place so that len
is equal to new_len
.
If new_len
is greater than len
, the Vec
is extended by the
+difference, with each additional slot filled with the result of
+calling the closure f
. The return values from f
will end up
+in the Vec
in the order they have been generated.
If new_len
is less than len
, the Vec
is simply truncated.
This method uses a closure to create new values on every push. If
+you’d rather Clone
a given value, use Vec::resize
. If you
+want to use the Default
trait to generate values, you can
+pass Default::default
as the second argument.
let mut vec = vec![1, 2, 3];
+vec.resize_with(5, Default::default);
+assert_eq!(vec, [1, 2, 3, 0, 0]);
+
+let mut vec = vec![];
+let mut p = 1;
+vec.resize_with(4, || { p *= 2; p });
+assert_eq!(vec, [2, 4, 8, 16]);
Consumes and leaks the Vec
, returning a mutable reference to the contents,
+&'a mut [T]
. Note that the type T
must outlive the chosen lifetime
+'a
. If the type has only static references, or none at all, then this
+may be chosen to be 'static
.
As of Rust 1.57, this method does not reallocate or shrink the Vec
,
+so the leaked allocation may include unused capacity that is not part
+of the returned slice.
This function is mainly useful for data that lives for the remainder of +the program’s life. Dropping the returned reference will cause a memory +leak.
+Simple usage:
+ +let x = vec![1, 2, 3];
+let static_ref: &'static mut [usize] = x.leak();
+static_ref[0] += 1;
+assert_eq!(static_ref, &[2, 2, 3]);
Returns the remaining spare capacity of the vector as a slice of
+MaybeUninit<T>
.
The returned slice can be used to fill the vector with data (e.g. by
+reading from a file) before marking the data as initialized using the
+set_len
method.
// Allocate vector big enough for 10 elements.
+let mut v = Vec::with_capacity(10);
+
+// Fill in the first 3 elements.
+let uninit = v.spare_capacity_mut();
+uninit[0].write(0);
+uninit[1].write(1);
+uninit[2].write(2);
+
+// Mark the first 3 elements of the vector as being initialized.
+unsafe {
+ v.set_len(3);
+}
+
+assert_eq!(&v, &[0, 1, 2]);
vec_split_at_spare
)Returns vector content as a slice of T
, along with the remaining spare
+capacity of the vector as a slice of MaybeUninit<T>
.
The returned spare capacity slice can be used to fill the vector with data
+(e.g. by reading from a file) before marking the data as initialized using
+the set_len
method.
Note that this is a low-level API, which should be used with care for
+optimization purposes. If you need to append data to a Vec
+you can use push
, extend
, extend_from_slice
,
+extend_from_within
, insert
, append
, resize
or
+resize_with
, depending on your exact needs.
#![feature(vec_split_at_spare)]
+
+let mut v = vec![1, 1, 2];
+
+// Reserve additional space big enough for 10 elements.
+v.reserve(10);
+
+let (init, uninit) = v.split_at_spare_mut();
+let sum = init.iter().copied().sum::<u32>();
+
+// Fill in the next 4 elements.
+uninit[0].write(sum);
+uninit[1].write(sum * 2);
+uninit[2].write(sum * 3);
+uninit[3].write(sum * 4);
+
+// Mark the 4 elements of the vector as being initialized.
+unsafe {
+ let len = v.len();
+ v.set_len(len + 4);
+}
+
+assert_eq!(&v, &[1, 1, 2, 4, 8, 12, 16]);
Resizes the Vec
in-place so that len
is equal to new_len
.
If new_len
is greater than len
, the Vec
is extended by the
+difference, with each additional slot filled with value
.
+If new_len
is less than len
, the Vec
is simply truncated.
This method requires T
to implement Clone
,
+in order to be able to clone the passed value.
+If you need more flexibility (or want to rely on Default
instead of
+Clone
), use Vec::resize_with
.
+If you only need to resize to a smaller size, use Vec::truncate
.
let mut vec = vec!["hello"];
+vec.resize(3, "world");
+assert_eq!(vec, ["hello", "world", "world"]);
+
+let mut vec = vec![1, 2, 3, 4];
+vec.resize(2, 0);
+assert_eq!(vec, [1, 2]);
Clones and appends all elements in a slice to the Vec
.
Iterates over the slice other
, clones each element, and then appends
+it to this Vec
. The other
slice is traversed in-order.
Note that this function is same as extend
except that it is
+specialized to work with slices instead. If and when Rust gets
+specialization this function will likely be deprecated (but still
+available).
let mut vec = vec![1];
+vec.extend_from_slice(&[2, 3, 4]);
+assert_eq!(vec, [1, 2, 3, 4]);
Copies elements from src
range to the end of the vector.
Panics if the starting point is greater than the end point or if +the end point is greater than the length of the vector.
+let mut vec = vec![0, 1, 2, 3, 4];
+
+vec.extend_from_within(2..);
+assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]);
+
+vec.extend_from_within(..2);
+assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]);
+
+vec.extend_from_within(4..8);
+assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]);
slice_flatten
)Takes a Vec<[T; N]>
and flattens it into a Vec<T>
.
Panics if the length of the resulting vector would overflow a usize
.
This is only possible when flattening a vector of arrays of zero-sized
+types, and thus tends to be irrelevant in practice. If
+size_of::<T>() > 0
, this will never panic.
#![feature(slice_flatten)]
+
+let mut vec = vec![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
+assert_eq!(vec.pop(), Some([7, 8, 9]));
+
+let mut flattened = vec.into_flattened();
+assert_eq!(flattened.pop(), Some(6));
Creates a splicing iterator that replaces the specified range in the vector
+with the given replace_with
iterator and yields the removed items.
+replace_with
does not need to be the same length as range
.
range
is removed even if the iterator is not consumed until the end.
It is unspecified how many elements are removed from the vector
+if the Splice
value is leaked.
The input iterator replace_with
is only consumed when the Splice
value is dropped.
This is optimal if:
+range
) is empty,replace_with
yields fewer or equal elements than range
’s lengthsize_hint()
is exact.Otherwise, a temporary vector is allocated and the tail is moved twice.
+Panics if the starting point is greater than the end point or if +the end point is greater than the length of the vector.
+let mut v = vec![1, 2, 3, 4];
+let new = [7, 8, 9];
+let u: Vec<_> = v.splice(1..3, new).collect();
+assert_eq!(v, &[1, 7, 8, 9, 4]);
+assert_eq!(u, &[2, 3]);
extract_if
)Creates an iterator which uses a closure to determine if an element should be removed.
+If the closure returns true, then the element is removed and yielded. +If the closure returns false, the element will remain in the vector and will not be yielded +by the iterator.
+If the returned ExtractIf
is not exhausted, e.g. because it is dropped without iterating
+or the iteration short-circuits, then the remaining elements will be retained.
+Use retain
with a negated predicate if you do not need the returned iterator.
Using this method is equivalent to the following code:
+ +let mut i = 0;
+while i < vec.len() {
+ if some_predicate(&mut vec[i]) {
+ let val = vec.remove(i);
+ // your code here
+ } else {
+ i += 1;
+ }
+}
+
But extract_if
is easier to use. extract_if
is also more efficient,
+because it can backshift the elements of the array in bulk.
Note that extract_if
also lets you mutate every element in the filter closure,
+regardless of whether you choose to keep or remove it.
Splitting an array into evens and odds, reusing the original allocation:
+ +#![feature(extract_if)]
+let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15];
+
+let evens = numbers.extract_if(|x| *x % 2 == 0).collect::<Vec<_>>();
+let odds = numbers;
+
+assert_eq!(evens, vec![2, 4, 6, 8, 14]);
+assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]);
sort_floats
)Sorts the slice of floats.
+This sort is in-place (i.e. does not allocate), O(n * log(n)) worst-case, and uses
+the ordering defined by f32::total_cmp
.
This uses the same sorting algorithm as sort_unstable_by
.
#![feature(sort_floats)]
+let mut v = [2.6, -5e-8, f32::NAN, 8.29, f32::INFINITY, -1.0, 0.0, -f32::INFINITY, -0.0];
+
+v.sort_floats();
+let sorted = [-f32::INFINITY, -1.0, -5e-8, -0.0, 0.0, 2.6, 8.29, f32::INFINITY, f32::NAN];
+assert_eq!(&v[..8], &sorted[..8]);
+assert!(v[8].is_nan());
Checks if all bytes in this slice are within the ASCII range.
+ascii_char
)If this slice is_ascii
, returns it as a slice of
+ASCII characters, otherwise returns None
.
ascii_char
)Converts this slice of bytes into a slice of ASCII characters, +without checking whether they’re valid.
+Every byte in the slice must be in 0..=127
, or else this is UB.
Checks that two slices are an ASCII case-insensitive match.
+Same as to_ascii_lowercase(a) == to_ascii_lowercase(b)
,
+but without allocating and copying temporaries.
Converts this slice to its ASCII upper case equivalent in-place.
+ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.
+To return a new uppercased value without modifying the existing one, use
+to_ascii_uppercase
.
Converts this slice to its ASCII lower case equivalent in-place.
+ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.
+To return a new lowercased value without modifying the existing one, use
+to_ascii_lowercase
.
Returns an iterator that produces an escaped version of this slice, +treating it as an ASCII string.
+
+let s = b"0\t\r\n'\"\\\x9d";
+let escaped = s.escape_ascii().to_string();
+assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
byte_slice_trim_ascii
)Returns a byte slice with leading ASCII whitespace bytes removed.
+‘Whitespace’ refers to the definition used by
+u8::is_ascii_whitespace
.
#![feature(byte_slice_trim_ascii)]
+
+assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n");
+assert_eq!(b" ".trim_ascii_start(), b"");
+assert_eq!(b"".trim_ascii_start(), b"");
byte_slice_trim_ascii
)Returns a byte slice with trailing ASCII whitespace bytes removed.
+‘Whitespace’ refers to the definition used by
+u8::is_ascii_whitespace
.
#![feature(byte_slice_trim_ascii)]
+
+assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world");
+assert_eq!(b" ".trim_ascii_end(), b"");
+assert_eq!(b"".trim_ascii_end(), b"");
byte_slice_trim_ascii
)Returns a byte slice with leading and trailing ASCII whitespace bytes +removed.
+‘Whitespace’ refers to the definition used by
+u8::is_ascii_whitespace
.
#![feature(byte_slice_trim_ascii)]
+
+assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world");
+assert_eq!(b" ".trim_ascii(), b"");
+assert_eq!(b"".trim_ascii(), b"");
ascii_char
)Views this slice of ASCII characters as a UTF-8 str
.
ascii_char
)Views this slice of ASCII characters as a slice of u8
bytes.
sort_floats
)Sorts the slice of floats.
+This sort is in-place (i.e. does not allocate), O(n * log(n)) worst-case, and uses
+the ordering defined by f64::total_cmp
.
This uses the same sorting algorithm as sort_unstable_by
.
#![feature(sort_floats)]
+let mut v = [2.6, -5e-8, f64::NAN, 8.29, f64::INFINITY, -1.0, 0.0, -f64::INFINITY, -0.0];
+
+v.sort_floats();
+let sorted = [-f64::INFINITY, -1.0, -5e-8, -0.0, 0.0, 2.6, 8.29, f64::INFINITY, f64::NAN];
+assert_eq!(&v[..8], &sorted[..8]);
+assert!(v[8].is_nan());
Returns the number of elements in the slice.
+let a = [1, 2, 3];
+assert_eq!(a.len(), 3);
Returns true
if the slice has a length of 0.
let a = [1, 2, 3];
+assert!(!a.is_empty());
+
+let b: &[i32] = &[];
+assert!(b.is_empty());
Returns the first element of the slice, or None
if it is empty.
let v = [10, 40, 30];
+assert_eq!(Some(&10), v.first());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.first());
Returns a mutable pointer to the first element of the slice, or None
if it is empty.
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_mut() {
+ *first = 5;
+}
+assert_eq!(x, &[5, 1, 2]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.first_mut());
Returns the first and all the rest of the elements of the slice, or None
if it is empty.
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first() {
+ assert_eq!(first, &0);
+ assert_eq!(elements, &[1, 2]);
+}
Returns the first and all the rest of the elements of the slice, or None
if it is empty.
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_mut() {
+ *first = 3;
+ elements[0] = 4;
+ elements[1] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
Returns the last and all the rest of the elements of the slice, or None
if it is empty.
let x = &[0, 1, 2];
+
+if let Some((last, elements)) = x.split_last() {
+ assert_eq!(last, &2);
+ assert_eq!(elements, &[0, 1]);
+}
Returns the last and all the rest of the elements of the slice, or None
if it is empty.
let x = &mut [0, 1, 2];
+
+if let Some((last, elements)) = x.split_last_mut() {
+ *last = 3;
+ elements[0] = 4;
+ elements[1] = 5;
+}
+assert_eq!(x, &[4, 5, 3]);
Returns the last element of the slice, or None
if it is empty.
let v = [10, 40, 30];
+assert_eq!(Some(&30), v.last());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.last());
Returns a mutable reference to the last item in the slice, or None
if it is empty.
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_mut() {
+ *last = 10;
+}
+assert_eq!(x, &[0, 1, 10]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.last_mut());
Return an array reference to the first N
items in the slice.
If the slice is not at least N
in length, this will return None
.
let u = [10, 40, 30];
+assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.first_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.first_chunk::<0>());
Return a mutable array reference to the first N
items in the slice.
If the slice is not at least N
in length, this will return None
.
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_chunk_mut::<2>() {
+ first[0] = 5;
+ first[1] = 4;
+}
+assert_eq!(x, &[5, 4, 2]);
+
+assert_eq!(None, x.first_chunk_mut::<4>());
Return an array reference to the first N
items in the slice and the remaining slice.
If the slice is not at least N
in length, this will return None
.
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk::<2>() {
+ assert_eq!(first, &[0, 1]);
+ assert_eq!(elements, &[2]);
+}
+
+assert_eq!(None, x.split_first_chunk::<4>());
Return a mutable array reference to the first N
items in the slice and the remaining
+slice.
If the slice is not at least N
in length, this will return None
.
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
+ first[0] = 3;
+ first[1] = 4;
+ elements[0] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
+
+assert_eq!(None, x.split_first_chunk_mut::<4>());
Return an array reference to the last N
items in the slice and the remaining slice.
If the slice is not at least N
in length, this will return None
.
let x = &[0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk::<2>() {
+ assert_eq!(elements, &[0]);
+ assert_eq!(last, &[1, 2]);
+}
+
+assert_eq!(None, x.split_last_chunk::<4>());
Return a mutable array reference to the last N
items in the slice and the remaining
+slice.
If the slice is not at least N
in length, this will return None
.
let x = &mut [0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
+ last[0] = 3;
+ last[1] = 4;
+ elements[0] = 5;
+}
+assert_eq!(x, &[5, 3, 4]);
+
+assert_eq!(None, x.split_last_chunk_mut::<4>());
Return an array reference to the last N
items in the slice.
If the slice is not at least N
in length, this will return None
.
let u = [10, 40, 30];
+assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.last_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.last_chunk::<0>());
Return a mutable array reference to the last N
items in the slice.
If the slice is not at least N
in length, this will return None
.
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_chunk_mut::<2>() {
+ last[0] = 10;
+ last[1] = 20;
+}
+assert_eq!(x, &[0, 10, 20]);
+
+assert_eq!(None, x.last_chunk_mut::<4>());
Returns a reference to an element or subslice depending on the type of +index.
+None
if out of bounds.None
if out of bounds.let v = [10, 40, 30];
+assert_eq!(Some(&40), v.get(1));
+assert_eq!(Some(&[10, 40][..]), v.get(0..2));
+assert_eq!(None, v.get(3));
+assert_eq!(None, v.get(0..4));
Returns a reference to an element or subslice, without doing bounds +checking.
+For a safe alternative see get
.
Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.
+You can think of this like .get(index).unwrap_unchecked()
. It’s UB
+to call .get_unchecked(len)
, even if you immediately convert to a
+pointer. And it’s UB to call .get_unchecked(..len + 1)
,
+.get_unchecked(..=len)
, or similar.
let x = &[1, 2, 4];
+
+unsafe {
+ assert_eq!(x.get_unchecked(1), &2);
+}
Returns a mutable reference to an element or subslice, without doing +bounds checking.
+For a safe alternative see get_mut
.
Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.
+You can think of this like .get_mut(index).unwrap_unchecked()
. It’s
+UB to call .get_unchecked_mut(len)
, even if you immediately convert
+to a pointer. And it’s UB to call .get_unchecked_mut(..len + 1)
,
+.get_unchecked_mut(..=len)
, or similar.
let x = &mut [1, 2, 4];
+
+unsafe {
+ let elem = x.get_unchecked_mut(1);
+ *elem = 13;
+}
+assert_eq!(x, &[1, 13, 4]);
Returns a raw pointer to the slice’s buffer.
+The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up pointing to garbage.
+The caller must also ensure that the memory the pointer (non-transitively) points to
+is never written to (except inside an UnsafeCell
) using this pointer or any pointer
+derived from it. If you need to mutate the contents of the slice, use as_mut_ptr
.
Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.
+let x = &[1, 2, 4];
+let x_ptr = x.as_ptr();
+
+unsafe {
+ for i in 0..x.len() {
+ assert_eq!(x.get_unchecked(i), &*x_ptr.add(i));
+ }
+}
Returns an unsafe mutable pointer to the slice’s buffer.
+The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up pointing to garbage.
+Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.
+let x = &mut [1, 2, 4];
+let x_ptr = x.as_mut_ptr();
+
+unsafe {
+ for i in 0..x.len() {
+ *x_ptr.add(i) += 2;
+ }
+}
+assert_eq!(x, &[3, 4, 6]);
Returns the two raw pointers spanning the slice.
+The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.
+See as_ptr
for warnings on using these pointers. The end pointer
+requires extra caution, as it does not point to a valid element in the
+slice.
This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.
+It can also be useful to check if a pointer to an element refers to an +element of this slice:
+ +let a = [1, 2, 3];
+let x = &a[1] as *const _;
+let y = &5 as *const _;
+
+assert!(a.as_ptr_range().contains(&x));
+assert!(!a.as_ptr_range().contains(&y));
Returns the two unsafe mutable pointers spanning the slice.
+The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.
+See as_mut_ptr
for warnings on using these pointers. The end
+pointer requires extra caution, as it does not point to a valid element
+in the slice.
This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.
+Swaps two elements in the slice.
+If a
equals to b
, it’s guaranteed that elements won’t change value.
Panics if a
or b
are out of bounds.
let mut v = ["a", "b", "c", "d", "e"];
+v.swap(2, 4);
+assert!(v == ["a", "b", "e", "d", "c"]);
slice_swap_unchecked
)Swaps two elements in the slice, without doing bounds checking.
+For a safe alternative see swap
.
Calling this method with an out-of-bounds index is undefined behavior.
+The caller has to ensure that a < self.len()
and b < self.len()
.
#![feature(slice_swap_unchecked)]
+
+let mut v = ["a", "b", "c", "d"];
+// SAFETY: we know that 1 and 3 are both indices of the slice
+unsafe { v.swap_unchecked(1, 3) };
+assert!(v == ["a", "d", "c", "b"]);
Reverses the order of elements in the slice, in place.
+let mut v = [1, 2, 3];
+v.reverse();
+assert!(v == [3, 2, 1]);
Returns an iterator over the slice.
+The iterator yields all items from start to end.
+let x = &[1, 2, 4];
+let mut iterator = x.iter();
+
+assert_eq!(iterator.next(), Some(&1));
+assert_eq!(iterator.next(), Some(&2));
+assert_eq!(iterator.next(), Some(&4));
+assert_eq!(iterator.next(), None);
Returns an iterator that allows modifying each value.
+The iterator yields all items from start to end.
+let x = &mut [1, 2, 4];
+for elem in x.iter_mut() {
+ *elem += 2;
+}
+assert_eq!(x, &[3, 4, 6]);
Returns an iterator over all contiguous windows of length
+size
. The windows overlap. If the slice is shorter than
+size
, the iterator returns no values.
Panics if size
is 0.
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.windows(3);
+assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']);
+assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']);
+assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']);
+assert!(iter.next().is_none());
If the slice is shorter than size
:
let slice = ['f', 'o', 'o'];
+let mut iter = slice.windows(4);
+assert!(iter.next().is_none());
There’s no windows_mut
, as that existing would let safe code violate the
+“only one &mut
at a time to the same thing” rule. However, you can sometimes
+use Cell::as_slice_of_cells
in
+conjunction with windows
to accomplish something similar:
use std::cell::Cell;
+
+let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+let slice = &mut array[..];
+let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+for w in slice_of_cells.windows(3) {
+ Cell::swap(&w[0], &w[2]);
+}
+assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
Returns an iterator over chunk_size
elements of the slice at a time, starting at the
+beginning of the slice.
The chunks are slices and do not overlap. If chunk_size
does not divide the length of the
+slice, then the last chunk will not have length chunk_size
.
See chunks_exact
for a variant of this iterator that returns chunks of always exactly
+chunk_size
elements, and rchunks
for the same iterator but starting at the end of the
+slice.
Panics if chunk_size
is 0.
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert_eq!(iter.next().unwrap(), &['m']);
+assert!(iter.next().is_none());
Returns an iterator over chunk_size
elements of the slice at a time, starting at the
+beginning of the slice.
The chunks are mutable slices, and do not overlap. If chunk_size
does not divide the
+length of the slice, then the last chunk will not have length chunk_size
.
See chunks_exact_mut
for a variant of this iterator that returns chunks of always
+exactly chunk_size
elements, and rchunks_mut
for the same iterator but starting at
+the end of the slice.
Panics if chunk_size
is 0.
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_mut(2) {
+ for elem in chunk.iter_mut() {
+ *elem += count;
+ }
+ count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 3]);
Returns an iterator over chunk_size
elements of the slice at a time, starting at the
+beginning of the slice.
The chunks are slices and do not overlap. If chunk_size
does not divide the length of the
+slice, then the last up to chunk_size-1
elements will be omitted and can be retrieved
+from the remainder
function of the iterator.
Due to each chunk having exactly chunk_size
elements, the compiler can often optimize the
+resulting code better than in the case of chunks
.
See chunks
for a variant of this iterator that also returns the remainder as a smaller
+chunk, and rchunks_exact
for the same iterator but starting at the end of the slice.
Panics if chunk_size
is 0.
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['m']);
Returns an iterator over chunk_size
elements of the slice at a time, starting at the
+beginning of the slice.
The chunks are mutable slices, and do not overlap. If chunk_size
does not divide the
+length of the slice, then the last up to chunk_size-1
elements will be omitted and can be
+retrieved from the into_remainder
function of the iterator.
Due to each chunk having exactly chunk_size
elements, the compiler can often optimize the
+resulting code better than in the case of chunks_mut
.
See chunks_mut
for a variant of this iterator that also returns the remainder as a
+smaller chunk, and rchunks_exact_mut
for the same iterator but starting at the end of
+the slice.
Panics if chunk_size
is 0.
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_exact_mut(2) {
+ for elem in chunk.iter_mut() {
+ *elem += count;
+ }
+ count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 0]);
slice_as_chunks
)Splits the slice into a slice of N
-element arrays,
+assuming that there’s no remainder.
This may only be called when
+N
-element chunks (aka self.len() % N == 0
).N != 0
.#![feature(slice_as_chunks)]
+let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &[[char; 1]] =
+ // SAFETY: 1-element chunks never have remainder
+ unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &[[char; 3]] =
+ // SAFETY: The slice length (6) is a multiple of 3
+ unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
slice_as_chunks
)Splits the slice into a slice of N
-element arrays,
+starting at the beginning of the slice,
+and a remainder slice with length strictly less than N
.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(slice_as_chunks)]
+let slice = ['l', 'o', 'r', 'e', 'm'];
+let (chunks, remainder) = slice.as_chunks();
+assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
+assert_eq!(remainder, &['m']);
If you expect the slice to be an exact multiple, you can combine
+let
-else
with an empty slice pattern:
#![feature(slice_as_chunks)]
+let slice = ['R', 'u', 's', 't'];
+let (chunks, []) = slice.as_chunks::<2>() else {
+ panic!("slice didn't have even length")
+};
+assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
slice_as_chunks
)Splits the slice into a slice of N
-element arrays,
+starting at the end of the slice,
+and a remainder slice with length strictly less than N
.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(slice_as_chunks)]
+let slice = ['l', 'o', 'r', 'e', 'm'];
+let (remainder, chunks) = slice.as_rchunks();
+assert_eq!(remainder, &['l']);
+assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
array_chunks
)Returns an iterator over N
elements of the slice at a time, starting at the
+beginning of the slice.
The chunks are array references and do not overlap. If N
does not divide the
+length of the slice, then the last up to N-1
elements will be omitted and can be
+retrieved from the remainder
function of the iterator.
This method is the const generic equivalent of chunks_exact
.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(array_chunks)]
+let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.array_chunks();
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['m']);
slice_as_chunks
)Splits the slice into a slice of N
-element arrays,
+assuming that there’s no remainder.
This may only be called when
+N
-element chunks (aka self.len() % N == 0
).N != 0
.#![feature(slice_as_chunks)]
+let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &mut [[char; 1]] =
+ // SAFETY: 1-element chunks never have remainder
+ unsafe { slice.as_chunks_unchecked_mut() };
+chunks[0] = ['L'];
+assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &mut [[char; 3]] =
+ // SAFETY: The slice length (6) is a multiple of 3
+ unsafe { slice.as_chunks_unchecked_mut() };
+chunks[1] = ['a', 'x', '?'];
+assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
slice_as_chunks
)Splits the slice into a slice of N
-element arrays,
+starting at the beginning of the slice,
+and a remainder slice with length strictly less than N
.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(slice_as_chunks)]
+let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (chunks, remainder) = v.as_chunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+ *chunk = [count; 2];
+ count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 9]);
slice_as_chunks
)Splits the slice into a slice of N
-element arrays,
+starting at the end of the slice,
+and a remainder slice with length strictly less than N
.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(slice_as_chunks)]
+let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (remainder, chunks) = v.as_rchunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+ *chunk = [count; 2];
+ count += 1;
+}
+assert_eq!(v, &[9, 1, 1, 2, 2]);
array_chunks
)Returns an iterator over N
elements of the slice at a time, starting at the
+beginning of the slice.
The chunks are mutable array references and do not overlap. If N
does not divide
+the length of the slice, then the last up to N-1
elements will be omitted and
+can be retrieved from the into_remainder
function of the iterator.
This method is the const generic equivalent of chunks_exact_mut
.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(array_chunks)]
+let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.array_chunks_mut() {
+ *chunk = [count; 2];
+ count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 0]);
array_windows
)Returns an iterator over overlapping windows of N
elements of a slice,
+starting at the beginning of the slice.
This is the const generic equivalent of windows
.
If N
is greater than the size of the slice, it will return no windows.
Panics if N
is 0. This check will most probably get changed to a compile time
+error before this method gets stabilized.
#![feature(array_windows)]
+let slice = [0, 1, 2, 3];
+let mut iter = slice.array_windows();
+assert_eq!(iter.next().unwrap(), &[0, 1]);
+assert_eq!(iter.next().unwrap(), &[1, 2]);
+assert_eq!(iter.next().unwrap(), &[2, 3]);
+assert!(iter.next().is_none());
Returns an iterator over chunk_size
elements of the slice at a time, starting at the end
+of the slice.
The chunks are slices and do not overlap. If chunk_size
does not divide the length of the
+slice, then the last chunk will not have length chunk_size
.
See rchunks_exact
for a variant of this iterator that returns chunks of always exactly
+chunk_size
elements, and chunks
for the same iterator but starting at the beginning
+of the slice.
Panics if chunk_size
is 0.
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert_eq!(iter.next().unwrap(), &['l']);
+assert!(iter.next().is_none());
Returns an iterator over chunk_size
elements of the slice at a time, starting at the end
+of the slice.
The chunks are mutable slices, and do not overlap. If chunk_size
does not divide the
+length of the slice, then the last chunk will not have length chunk_size
.
See rchunks_exact_mut
for a variant of this iterator that returns chunks of always
+exactly chunk_size
elements, and chunks_mut
for the same iterator but starting at the
+beginning of the slice.
Panics if chunk_size
is 0.
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_mut(2) {
+ for elem in chunk.iter_mut() {
+ *elem += count;
+ }
+ count += 1;
+}
+assert_eq!(v, &[3, 2, 2, 1, 1]);
Returns an iterator over chunk_size
elements of the slice at a time, starting at the
+end of the slice.
The chunks are slices and do not overlap. If chunk_size
does not divide the length of the
+slice, then the last up to chunk_size-1
elements will be omitted and can be retrieved
+from the remainder
function of the iterator.
Due to each chunk having exactly chunk_size
elements, the compiler can often optimize the
+resulting code better than in the case of rchunks
.
See rchunks
for a variant of this iterator that also returns the remainder as a smaller
+chunk, and chunks_exact
for the same iterator but starting at the beginning of the
+slice.
Panics if chunk_size
is 0.
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['l']);
Returns an iterator over chunk_size
elements of the slice at a time, starting at the end
+of the slice.
The chunks are mutable slices, and do not overlap. If chunk_size
does not divide the
+length of the slice, then the last up to chunk_size-1
elements will be omitted and can be
+retrieved from the into_remainder
function of the iterator.
Due to each chunk having exactly chunk_size
elements, the compiler can often optimize the
+resulting code better than in the case of chunks_mut
.
See rchunks_mut
for a variant of this iterator that also returns the remainder as a
+smaller chunk, and chunks_exact_mut
for the same iterator but starting at the beginning
+of the slice.
Panics if chunk_size
is 0.
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_exact_mut(2) {
+ for elem in chunk.iter_mut() {
+ *elem += count;
+ }
+ count += 1;
+}
+assert_eq!(v, &[0, 2, 2, 1, 1]);
Returns an iterator over the slice producing non-overlapping runs +of elements using the predicate to separate them.
+The predicate is called for every pair of consecutive elements,
+meaning that it is called on slice[0]
and slice[1]
,
+followed by slice[1]
and slice[2]
, and so on.
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&[3, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+assert_eq!(iter.next(), None);
This method can be used to extract the sorted subslices:
+ +let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
+assert_eq!(iter.next(), None);
Returns an iterator over the slice producing non-overlapping mutable +runs of elements using the predicate to separate them.
+The predicate is called for every pair of consecutive elements,
+meaning that it is called on slice[0]
and slice[1]
,
+followed by slice[1]
and slice[2]
, and so on.
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by_mut(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&mut [3, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
+assert_eq!(iter.next(), None);
This method can be used to extract the sorted subslices:
+ +let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by_mut(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
+assert_eq!(iter.next(), None);
Divides one slice into two at an index.
+The first will contain all indices from [0, mid)
(excluding
+the index mid
itself) and the second will contain all
+indices from [mid, len)
(excluding the index len
itself).
Panics if mid > len
. For a non-panicking alternative see
+split_at_checked
.
let v = [1, 2, 3, 4, 5, 6];
+
+{
+ let (left, right) = v.split_at(0);
+ assert_eq!(left, []);
+ assert_eq!(right, [1, 2, 3, 4, 5, 6]);
+}
+
+{
+ let (left, right) = v.split_at(2);
+ assert_eq!(left, [1, 2]);
+ assert_eq!(right, [3, 4, 5, 6]);
+}
+
+{
+ let (left, right) = v.split_at(6);
+ assert_eq!(left, [1, 2, 3, 4, 5, 6]);
+ assert_eq!(right, []);
+}
Divides one mutable slice into two at an index.
+The first will contain all indices from [0, mid)
(excluding
+the index mid
itself) and the second will contain all
+indices from [mid, len)
(excluding the index len
itself).
Panics if mid > len
. For a non-panicking alternative see
+split_at_mut_checked
.
let mut v = [1, 0, 3, 0, 5, 6];
+let (left, right) = v.split_at_mut(2);
+assert_eq!(left, [1, 0]);
+assert_eq!(right, [3, 0, 5, 6]);
+left[1] = 2;
+right[1] = 4;
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
slice_split_at_unchecked
)Divides one slice into two at an index, without doing bounds checking.
+The first will contain all indices from [0, mid)
(excluding
+the index mid
itself) and the second will contain all
+indices from [mid, len)
(excluding the index len
itself).
For a safe alternative see split_at
.
Calling this method with an out-of-bounds index is undefined behavior
+even if the resulting reference is not used. The caller has to ensure that
+0 <= mid <= self.len()
.
#![feature(slice_split_at_unchecked)]
+
+let v = [1, 2, 3, 4, 5, 6];
+
+unsafe {
+ let (left, right) = v.split_at_unchecked(0);
+ assert_eq!(left, []);
+ assert_eq!(right, [1, 2, 3, 4, 5, 6]);
+}
+
+unsafe {
+ let (left, right) = v.split_at_unchecked(2);
+ assert_eq!(left, [1, 2]);
+ assert_eq!(right, [3, 4, 5, 6]);
+}
+
+unsafe {
+ let (left, right) = v.split_at_unchecked(6);
+ assert_eq!(left, [1, 2, 3, 4, 5, 6]);
+ assert_eq!(right, []);
+}
slice_split_at_unchecked
)Divides one mutable slice into two at an index, without doing bounds checking.
+The first will contain all indices from [0, mid)
(excluding
+the index mid
itself) and the second will contain all
+indices from [mid, len)
(excluding the index len
itself).
For a safe alternative see split_at_mut
.
Calling this method with an out-of-bounds index is undefined behavior
+even if the resulting reference is not used. The caller has to ensure that
+0 <= mid <= self.len()
.
#![feature(slice_split_at_unchecked)]
+
+let mut v = [1, 0, 3, 0, 5, 6];
+// scoped to restrict the lifetime of the borrows
+unsafe {
+ let (left, right) = v.split_at_mut_unchecked(2);
+ assert_eq!(left, [1, 0]);
+ assert_eq!(right, [3, 0, 5, 6]);
+ left[1] = 2;
+ right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
split_at_checked
)Divides one slice into two at an index, returning None
if the slice is
+too short.
If mid ≤ len
returns a pair of slices where the first will contain all
+indices from [0, mid)
(excluding the index mid
itself) and the
+second will contain all indices from [mid, len)
(excluding the index
+len
itself).
Otherwise, if mid > len
, returns None
.
#![feature(split_at_checked)]
+
+let v = [1, -2, 3, -4, 5, -6];
+
+{
+ let (left, right) = v.split_at_checked(0).unwrap();
+ assert_eq!(left, []);
+ assert_eq!(right, [1, -2, 3, -4, 5, -6]);
+}
+
+{
+ let (left, right) = v.split_at_checked(2).unwrap();
+ assert_eq!(left, [1, -2]);
+ assert_eq!(right, [3, -4, 5, -6]);
+}
+
+{
+ let (left, right) = v.split_at_checked(6).unwrap();
+ assert_eq!(left, [1, -2, 3, -4, 5, -6]);
+ assert_eq!(right, []);
+}
+
+assert_eq!(None, v.split_at_checked(7));
split_at_checked
)Divides one mutable slice into two at an index, returning None
if the
+slice is too short.
If mid ≤ len
returns a pair of slices where the first will contain all
+indices from [0, mid)
(excluding the index mid
itself) and the
+second will contain all indices from [mid, len)
(excluding the index
+len
itself).
Otherwise, if mid > len
, returns None
.
#![feature(split_at_checked)]
+
+let mut v = [1, 0, 3, 0, 5, 6];
+
+if let Some((left, right)) = v.split_at_mut_checked(2) {
+ assert_eq!(left, [1, 0]);
+ assert_eq!(right, [3, 0, 5, 6]);
+ left[1] = 2;
+ right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
+
+assert_eq!(None, v.split_at_mut_checked(7));
Returns an iterator over subslices separated by elements that match
+pred
. The matched element is not contained in the subslices.
let slice = [10, 40, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
If the first element is matched, an empty slice will be the first item +returned by the iterator. Similarly, if the last element in the slice +is matched, an empty slice will be the last item returned by the +iterator:
+ +let slice = [10, 40, 33];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert!(iter.next().is_none());
If two matched elements are directly adjacent, an empty slice will be +present between them:
+ +let slice = [10, 6, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
Returns an iterator over mutable subslices separated by elements that
+match pred
. The matched element is not contained in the subslices.
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_mut(|num| *num % 3 == 0) {
+ group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 1]);
Returns an iterator over subslices separated by elements that match
+pred
. The matched element is contained in the end of the previous
+subslice as a terminator.
let slice = [10, 40, 33, 20];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
If the last element of the slice is matched, +that element will be considered the terminator of the preceding slice. +That slice will be the last item returned by the iterator.
+ +let slice = [3, 10, 40, 33];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[3]);
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert!(iter.next().is_none());
Returns an iterator over mutable subslices separated by elements that
+match pred
. The matched element is contained in the previous
+subslice as a terminator.
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_inclusive_mut(|num| *num % 3 == 0) {
+ let terminator_idx = group.len()-1;
+ group[terminator_idx] = 1;
+}
+assert_eq!(v, [10, 40, 1, 20, 1, 1]);
Returns an iterator over subslices separated by elements that match
+pred
, starting at the end of the slice and working backwards.
+The matched element is not contained in the subslices.
let slice = [11, 22, 33, 0, 44, 55];
+let mut iter = slice.rsplit(|num| *num == 0);
+
+assert_eq!(iter.next().unwrap(), &[44, 55]);
+assert_eq!(iter.next().unwrap(), &[11, 22, 33]);
+assert_eq!(iter.next(), None);
As with split()
, if the first or last element is matched, an empty
+slice will be the first (or last) item returned by the iterator.
let v = &[0, 1, 1, 2, 3, 5, 8];
+let mut it = v.rsplit(|n| *n % 2 == 0);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next().unwrap(), &[3, 5]);
+assert_eq!(it.next().unwrap(), &[1, 1]);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next(), None);
Returns an iterator over mutable subslices separated by elements that
+match pred
, starting at the end of the slice and working
+backwards. The matched element is not contained in the subslices.
let mut v = [100, 400, 300, 200, 600, 500];
+
+let mut count = 0;
+for group in v.rsplit_mut(|num| *num % 3 == 0) {
+ count += 1;
+ group[0] = count;
+}
+assert_eq!(v, [3, 400, 300, 2, 600, 1]);
Returns an iterator over subslices separated by elements that match
+pred
, limited to returning at most n
items. The matched element is
+not contained in the subslices.
The last element returned, if any, will contain the remainder of the +slice.
+Print the slice split once by numbers divisible by 3 (i.e., [10, 40]
,
+[20, 60, 50]
):
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn(2, |num| *num % 3 == 0) {
+ println!("{group:?}");
+}
Returns an iterator over mutable subslices separated by elements that match
+pred
, limited to returning at most n
items. The matched element is
+not contained in the subslices.
The last element returned, if any, will contain the remainder of the +slice.
+let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn_mut(2, |num| *num % 3 == 0) {
+ group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 50]);
Returns an iterator over subslices separated by elements that match
+pred
limited to returning at most n
items. This starts at the end of
+the slice and works backwards. The matched element is not contained in
+the subslices.
The last element returned, if any, will contain the remainder of the +slice.
+Print the slice split once, starting from the end, by numbers divisible
+by 3 (i.e., [50]
, [10, 40, 30, 20]
):
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.rsplitn(2, |num| *num % 3 == 0) {
+ println!("{group:?}");
+}
Returns an iterator over subslices separated by elements that match
+pred
limited to returning at most n
items. This starts at the end of
+the slice and works backwards. The matched element is not contained in
+the subslices.
The last element returned, if any, will contain the remainder of the +slice.
+let mut s = [10, 40, 30, 20, 60, 50];
+
+for group in s.rsplitn_mut(2, |num| *num % 3 == 0) {
+ group[0] = 1;
+}
+assert_eq!(s, [1, 40, 30, 20, 60, 1]);
slice_split_once
)Splits the slice on the first element that matches the specified +predicate.
+If any matching elements are present in the slice, returns the prefix
+before the match and suffix after. The matching element itself is not
+included. If no elements match, returns None
.
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.split_once(|&x| x == 2), Some((
+ &[1][..],
+ &[3, 2, 4][..]
+)));
+assert_eq!(s.split_once(|&x| x == 0), None);
slice_split_once
)Splits the slice on the last element that matches the specified +predicate.
+If any matching elements are present in the slice, returns the prefix
+before the match and suffix after. The matching element itself is not
+included. If no elements match, returns None
.
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.rsplit_once(|&x| x == 2), Some((
+ &[1, 2, 3][..],
+ &[4][..]
+)));
+assert_eq!(s.rsplit_once(|&x| x == 0), None);
Returns true
if the slice contains an element with the given value.
This operation is O(n).
+Note that if you have a sorted slice, binary_search
may be faster.
let v = [10, 40, 30];
+assert!(v.contains(&30));
+assert!(!v.contains(&50));
If you do not have a &T
, but some other value that you can compare
+with one (for example, String
implements PartialEq<str>
), you can
+use iter().any
:
let v = [String::from("hello"), String::from("world")]; // slice of `String`
+assert!(v.iter().any(|e| e == "hello")); // search with `&str`
+assert!(!v.iter().any(|e| e == "hi"));
Returns true
if needle
is a prefix of the slice or equal to the slice.
let v = [10, 40, 30];
+assert!(v.starts_with(&[10]));
+assert!(v.starts_with(&[10, 40]));
+assert!(v.starts_with(&v));
+assert!(!v.starts_with(&[50]));
+assert!(!v.starts_with(&[10, 50]));
Always returns true
if needle
is an empty slice:
let v = &[10, 40, 30];
+assert!(v.starts_with(&[]));
+let v: &[u8] = &[];
+assert!(v.starts_with(&[]));
Returns true
if needle
is a suffix of the slice or equal to the slice.
let v = [10, 40, 30];
+assert!(v.ends_with(&[30]));
+assert!(v.ends_with(&[40, 30]));
+assert!(v.ends_with(&v));
+assert!(!v.ends_with(&[50]));
+assert!(!v.ends_with(&[50, 30]));
Always returns true
if needle
is an empty slice:
let v = &[10, 40, 30];
+assert!(v.ends_with(&[]));
+let v: &[u8] = &[];
+assert!(v.ends_with(&[]));
Returns a subslice with the prefix removed.
+If the slice starts with prefix
, returns the subslice after the prefix, wrapped in Some
.
+If prefix
is empty, simply returns the original slice. If prefix
is equal to the
+original slice, returns an empty slice.
If the slice does not start with prefix
, returns None
.
let v = &[10, 40, 30];
+assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..]));
+assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..]));
+assert_eq!(v.strip_prefix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_prefix(&[50]), None);
+assert_eq!(v.strip_prefix(&[10, 50]), None);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".strip_prefix(prefix.as_bytes()),
+ Some(b"llo".as_ref()));
Returns a subslice with the suffix removed.
+If the slice ends with suffix
, returns the subslice before the suffix, wrapped in Some
.
+If suffix
is empty, simply returns the original slice. If suffix
is equal to the
+original slice, returns an empty slice.
If the slice does not end with suffix
, returns None
.
let v = &[10, 40, 30];
+assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..]));
+assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..]));
+assert_eq!(v.strip_suffix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_suffix(&[50]), None);
+assert_eq!(v.strip_suffix(&[50, 30]), None);
Binary searches this slice for a given element. +If the slice is not sorted, the returned result is unspecified and +meaningless.
+If the value is found then Result::Ok
is returned, containing the
+index of the matching element. If there are multiple matches, then any
+one of the matches could be returned. The index is chosen
+deterministically, but is subject to change in future versions of Rust.
+If the value is not found then Result::Err
is returned, containing
+the index where a matching element could be inserted while maintaining
+sorted order.
See also binary_search_by
, binary_search_by_key
, and partition_point
.
Looks up a series of four elements. The first is found, with a
+uniquely determined position; the second and third are not
+found; the fourth could match any position in [1, 4]
.
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+assert_eq!(s.binary_search(&13), Ok(9));
+assert_eq!(s.binary_search(&4), Err(7));
+assert_eq!(s.binary_search(&100), Err(13));
+let r = s.binary_search(&1);
+assert!(match r { Ok(1..=4) => true, _ => false, });
If you want to find that whole range of matching items, rather than
+an arbitrary matching one, that can be done using partition_point
:
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let low = s.partition_point(|x| x < &1);
+assert_eq!(low, 1);
+let high = s.partition_point(|x| x <= &1);
+assert_eq!(high, 5);
+let r = s.binary_search(&1);
+assert!((low..high).contains(&r.unwrap()));
+
+assert!(s[..low].iter().all(|&x| x < 1));
+assert!(s[low..high].iter().all(|&x| x == 1));
+assert!(s[high..].iter().all(|&x| x > 1));
+
+// For something not found, the "range" of equal items is empty
+assert_eq!(s.partition_point(|x| x < &11), 9);
+assert_eq!(s.partition_point(|x| x <= &11), 9);
+assert_eq!(s.binary_search(&11), Err(9));
If you want to insert an item to a sorted vector, while maintaining
+sort order, consider using partition_point
:
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x < num);
+// The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);`
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
Binary searches this slice with a comparator function.
+The comparator function should return an order code that indicates
+whether its argument is Less
, Equal
or Greater
the desired
+target.
+If the slice is not sorted or if the comparator function does not
+implement an order consistent with the sort order of the underlying
+slice, the returned result is unspecified and meaningless.
If the value is found then Result::Ok
is returned, containing the
+index of the matching element. If there are multiple matches, then any
+one of the matches could be returned. The index is chosen
+deterministically, but is subject to change in future versions of Rust.
+If the value is not found then Result::Err
is returned, containing
+the index where a matching element could be inserted while maintaining
+sorted order.
See also binary_search
, binary_search_by_key
, and partition_point
.
Looks up a series of four elements. The first is found, with a
+uniquely determined position; the second and third are not
+found; the fourth could match any position in [1, 4]
.
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let seek = 13;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9));
+let seek = 4;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7));
+let seek = 100;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13));
+let seek = 1;
+let r = s.binary_search_by(|probe| probe.cmp(&seek));
+assert!(match r { Ok(1..=4) => true, _ => false, });
Binary searches this slice with a key extraction function.
+Assumes that the slice is sorted by the key, for instance with
+sort_by_key
using the same key extraction function.
+If the slice is not sorted by the key, the returned result is
+unspecified and meaningless.
If the value is found then Result::Ok
is returned, containing the
+index of the matching element. If there are multiple matches, then any
+one of the matches could be returned. The index is chosen
+deterministically, but is subject to change in future versions of Rust.
+If the value is not found then Result::Err
is returned, containing
+the index where a matching element could be inserted while maintaining
+sorted order.
See also binary_search
, binary_search_by
, and partition_point
.
Looks up a series of four elements in a slice of pairs sorted by
+their second elements. The first is found, with a uniquely
+determined position; the second and third are not found; the
+fourth could match any position in [1, 4]
.
let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
+ (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
+ (1, 21), (2, 34), (4, 55)];
+
+assert_eq!(s.binary_search_by_key(&13, |&(a, b)| b), Ok(9));
+assert_eq!(s.binary_search_by_key(&4, |&(a, b)| b), Err(7));
+assert_eq!(s.binary_search_by_key(&100, |&(a, b)| b), Err(13));
+let r = s.binary_search_by_key(&1, |&(a, b)| b);
+assert!(match r { Ok(1..=4) => true, _ => false, });
Sorts the slice, but might not preserve the order of equal elements.
+This sort is unstable (i.e., may reorder equal elements), in-place +(i.e., does not allocate), and O(n * log(n)) worst-case.
+The current algorithm is based on pattern-defeating quicksort by Orson Peters, +which combines the fast average case of randomized quicksort with the fast worst case of +heapsort, while achieving linear time on slices with certain patterns. It uses some +randomization to avoid degenerate cases, but with a fixed seed to always provide +deterministic behavior.
+It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice consists of several concatenated sorted sequences.
+let mut v = [-5, 4, 1, -3, 2];
+
+v.sort_unstable();
+assert!(v == [-5, -3, 1, 2, 4]);
Sorts the slice with a comparator function, but might not preserve the order of equal +elements.
+This sort is unstable (i.e., may reorder equal elements), in-place +(i.e., does not allocate), and O(n * log(n)) worst-case.
+The comparator function must define a total ordering for the elements in the slice. If
+the ordering is not total, the order of the elements is unspecified. An order is a
+total order if it is (for all a
, b
and c
):
a < b
, a == b
or a > b
is true, anda < b
and b < c
implies a < c
. The same must hold for both ==
and >
.For example, while f64
doesn’t implement Ord
because NaN != NaN
, we can use
+partial_cmp
as our sort function when we know the slice doesn’t contain a NaN
.
let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0];
+floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
+assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]);
The current algorithm is based on pattern-defeating quicksort by Orson Peters, +which combines the fast average case of randomized quicksort with the fast worst case of +heapsort, while achieving linear time on slices with certain patterns. It uses some +randomization to avoid degenerate cases, but with a fixed seed to always provide +deterministic behavior.
+It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice consists of several concatenated sorted sequences.
+let mut v = [5, 4, 1, 3, 2];
+v.sort_unstable_by(|a, b| a.cmp(b));
+assert!(v == [1, 2, 3, 4, 5]);
+
+// reverse sorting
+v.sort_unstable_by(|a, b| b.cmp(a));
+assert!(v == [5, 4, 3, 2, 1]);
Sorts the slice with a key extraction function, but might not preserve the order of equal +elements.
+This sort is unstable (i.e., may reorder equal elements), in-place +(i.e., does not allocate), and O(m * n * log(n)) worst-case, where the key function is +O(m).
+The current algorithm is based on pattern-defeating quicksort by Orson Peters, +which combines the fast average case of randomized quicksort with the fast worst case of +heapsort, while achieving linear time on slices with certain patterns. It uses some +randomization to avoid degenerate cases, but with a fixed seed to always provide +deterministic behavior.
+Due to its key calling strategy, sort_unstable_by_key
+is likely to be slower than sort_by_cached_key
in
+cases where the key function is expensive.
let mut v = [-5i32, 4, 1, -3, 2];
+
+v.sort_unstable_by_key(|k| k.abs());
+assert!(v == [1, 2, -3, 4, -5]);
Reorder the slice such that the element at index
after the reordering is at its final sorted position.
This reordering has the additional property that any value at position i < index
will be
+less than or equal to any value at a position j > index
. Additionally, this reordering is
+unstable (i.e. any number of equal elements may end up at position index
), in-place
+(i.e. does not allocate), and runs in O(n) time.
+This function is also known as “kth element” in other libraries.
It returns a triplet of the following from the reordered slice:
+the subslice prior to index
, the element at index
, and the subslice after index
;
+accordingly, the values in those two subslices will respectively all be less-than-or-equal-to
+and greater-than-or-equal-to the value of the element at index
.
The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also
+the basis for sort_unstable
. The fallback algorithm is Median of Medians using Tukey’s Ninther for
+pivot selection, which guarantees linear runtime for all inputs.
Panics when index >= len()
, meaning it always panics on empty slices.
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items less than or equal to the median, the median, and greater than or equal to
+// the median.
+let (lesser, median, greater) = v.select_nth_unstable(2);
+
+assert!(lesser == [-3, -5] || lesser == [-5, -3]);
+assert_eq!(median, &mut 1);
+assert!(greater == [4, 2] || greater == [2, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [-3, -5, 1, 2, 4] ||
+ v == [-5, -3, 1, 2, 4] ||
+ v == [-3, -5, 1, 4, 2] ||
+ v == [-5, -3, 1, 4, 2]);
Reorder the slice with a comparator function such that the element at index
after the reordering is at
+its final sorted position.
This reordering has the additional property that any value at position i < index
will be
+less than or equal to any value at a position j > index
using the comparator function.
+Additionally, this reordering is unstable (i.e. any number of equal elements may end up at
+position index
), in-place (i.e. does not allocate), and runs in O(n) time.
+This function is also known as “kth element” in other libraries.
It returns a triplet of the following from
+the slice reordered according to the provided comparator function: the subslice prior to
+index
, the element at index
, and the subslice after index
; accordingly, the values in
+those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to
+the value of the element at index
.
The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also
+the basis for sort_unstable
. The fallback algorithm is Median of Medians using Tukey’s Ninther for
+pivot selection, which guarantees linear runtime for all inputs.
Panics when index >= len()
, meaning it always panics on empty slices.
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items less than or equal to the median, the median, and greater than or equal to
+// the median as if the slice were sorted in descending order.
+let (lesser, median, greater) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
+
+assert!(lesser == [4, 2] || lesser == [2, 4]);
+assert_eq!(median, &mut 1);
+assert!(greater == [-3, -5] || greater == [-5, -3]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [2, 4, 1, -5, -3] ||
+ v == [2, 4, 1, -3, -5] ||
+ v == [4, 2, 1, -5, -3] ||
+ v == [4, 2, 1, -3, -5]);
Reorder the slice with a key extraction function such that the element at index
after the reordering is
+at its final sorted position.
This reordering has the additional property that any value at position i < index
will be
+less than or equal to any value at a position j > index
using the key extraction function.
+Additionally, this reordering is unstable (i.e. any number of equal elements may end up at
+position index
), in-place (i.e. does not allocate), and runs in O(n) time.
+This function is also known as “kth element” in other libraries.
It returns a triplet of the following from
+the slice reordered according to the provided key extraction function: the subslice prior to
+index
, the element at index
, and the subslice after index
; accordingly, the values in
+those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to
+the value of the element at index
.
The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also
+the basis for sort_unstable
. The fallback algorithm is Median of Medians using Tukey’s Ninther for
+pivot selection, which guarantees linear runtime for all inputs.
Panics when index >= len()
, meaning it always panics on empty slices.
let mut v = [-5i32, 4, 1, -3, 2];
+
+// Find the items less than or equal to the median, the median, and greater than or equal to
+// the median as if the slice were sorted according to absolute value.
+let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
+
+assert!(lesser == [1, 2] || lesser == [2, 1]);
+assert_eq!(median, &mut -3);
+assert!(greater == [4, -5] || greater == [-5, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [1, 2, -3, 4, -5] ||
+ v == [1, 2, -3, -5, 4] ||
+ v == [2, 1, -3, 4, -5] ||
+ v == [2, 1, -3, -5, 4]);
slice_partition_dedup
)Moves all consecutive repeated elements to the end of the slice according to the
+PartialEq
trait implementation.
Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.
+If the slice is sorted, the first returned slice contains no duplicates.
+#![feature(slice_partition_dedup)]
+
+let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
+
+let (dedup, duplicates) = slice.partition_dedup();
+
+assert_eq!(dedup, [1, 2, 3, 2, 1]);
+assert_eq!(duplicates, [2, 3, 1]);
slice_partition_dedup
)Moves all but the first of consecutive elements to the end of the slice satisfying +a given equality relation.
+Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.
+The same_bucket
function is passed references to two elements from the slice and
+must determine if the elements compare equal. The elements are passed in opposite order
+from their order in the slice, so if same_bucket(a, b)
returns true
, a
is moved
+at the end of the slice.
If the slice is sorted, the first returned slice contains no duplicates.
+#![feature(slice_partition_dedup)]
+
+let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
+
+let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+
+assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
+assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
slice_partition_dedup
)Moves all but the first of consecutive elements to the end of the slice that resolve +to the same key.
+Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.
+If the slice is sorted, the first returned slice contains no duplicates.
+#![feature(slice_partition_dedup)]
+
+let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
+
+let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
+
+assert_eq!(dedup, [10, 20, 30, 20, 11]);
+assert_eq!(duplicates, [21, 30, 13]);
Rotates the slice in-place such that the first mid
elements of the
+slice move to the end while the last self.len() - mid
elements move to
+the front. After calling rotate_left
, the element previously at index
+mid
will become the first element in the slice.
This function will panic if mid
is greater than the length of the
+slice. Note that mid == self.len()
does not panic and is a no-op
+rotation.
Takes linear (in self.len()
) time.
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_left(2);
+assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']);
Rotating a subslice:
+ +let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_left(1);
+assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']);
Rotates the slice in-place such that the first self.len() - k
+elements of the slice move to the end while the last k
elements move
+to the front. After calling rotate_right
, the element previously at
+index self.len() - k
will become the first element in the slice.
This function will panic if k
is greater than the length of the
+slice. Note that k == self.len()
does not panic and is a no-op
+rotation.
Takes linear (in self.len()
) time.
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_right(2);
+assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
Rotating a subslice:
+ +let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_right(1);
+assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']);
Fills self
with elements by cloning value
.
let mut buf = vec![0; 10];
+buf.fill(1);
+assert_eq!(buf, vec![1; 10]);
Fills self
with elements returned by calling a closure repeatedly.
This method uses a closure to create new values. If you’d rather
+Clone
a given value, use fill
. If you want to use the Default
+trait to generate values, you can pass Default::default
as the
+argument.
let mut buf = vec![1; 10];
+buf.fill_with(Default::default);
+assert_eq!(buf, vec![0; 10]);
Copies the elements from src
into self
.
The length of src
must be the same as self
.
This function will panic if the two slices have different lengths.
+Cloning two elements from a slice into another:
+ +let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.clone_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
Rust enforces that there can only be one mutable reference with no
+immutable references to a particular piece of data in a particular
+scope. Because of this, attempting to use clone_from_slice
on a
+single slice will result in a compile failure:
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].clone_from_slice(&slice[3..]); // compile fail!
To work around this, we can use split_at_mut
to create two distinct
+sub-slices from a slice:
let mut slice = [1, 2, 3, 4, 5];
+
+{
+ let (left, right) = slice.split_at_mut(2);
+ left.clone_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
Copies all elements from src
into self
, using a memcpy.
The length of src
must be the same as self
.
If T
does not implement Copy
, use clone_from_slice
.
This function will panic if the two slices have different lengths.
+Copying two elements from a slice into another:
+ +let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.copy_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
Rust enforces that there can only be one mutable reference with no
+immutable references to a particular piece of data in a particular
+scope. Because of this, attempting to use copy_from_slice
on a
+single slice will result in a compile failure:
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].copy_from_slice(&slice[3..]); // compile fail!
To work around this, we can use split_at_mut
to create two distinct
+sub-slices from a slice:
let mut slice = [1, 2, 3, 4, 5];
+
+{
+ let (left, right) = slice.split_at_mut(2);
+ left.copy_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
Copies elements from one part of the slice to another part of itself, +using a memmove.
+src
is the range within self
to copy from. dest
is the starting
+index of the range within self
to copy to, which will have the same
+length as src
. The two ranges may overlap. The ends of the two ranges
+must be less than or equal to self.len()
.
This function will panic if either range exceeds the end of the slice,
+or if the end of src
is before the start.
Copying four bytes within a slice:
+ +let mut bytes = *b"Hello, World!";
+
+bytes.copy_within(1..5, 8);
+
+assert_eq!(&bytes, b"Hello, Wello!");
Swaps all elements in self
with those in other
.
The length of other
must be the same as self
.
This function will panic if the two slices have different lengths.
+Swapping two elements across slices:
+ +let mut slice1 = [0, 0];
+let mut slice2 = [1, 2, 3, 4];
+
+slice1.swap_with_slice(&mut slice2[2..]);
+
+assert_eq!(slice1, [3, 4]);
+assert_eq!(slice2, [1, 2, 0, 0]);
Rust enforces that there can only be one mutable reference to a
+particular piece of data in a particular scope. Because of this,
+attempting to use swap_with_slice
on a single slice will result in
+a compile failure:
let mut slice = [1, 2, 3, 4, 5];
+slice[..2].swap_with_slice(&mut slice[3..]); // compile fail!
To work around this, we can use split_at_mut
to create two distinct
+mutable sub-slices from a slice:
let mut slice = [1, 2, 3, 4, 5];
+
+{
+ let (left, right) = slice.split_at_mut(2);
+ left.swap_with_slice(&mut right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 1, 2]);
Transmute the slice to a slice of another type, ensuring alignment of the types is +maintained.
+This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.
+This method has no purpose when either input element T
or output element U
are
+zero-sized and will return the original slice without splitting anything.
This method is essentially a transmute
with respect to the elements in the returned
+middle slice, so all the usual caveats pertaining to transmute::<T, U>
also apply here.
Basic usage:
+ +unsafe {
+ let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+ let (prefix, shorts, suffix) = bytes.align_to::<u16>();
+ // less_efficient_algorithm_for_bytes(prefix);
+ // more_efficient_algorithm_for_aligned_shorts(shorts);
+ // less_efficient_algorithm_for_bytes(suffix);
+}
Transmute the mutable slice to a mutable slice of another type, ensuring alignment of the +types is maintained.
+This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.
+This method has no purpose when either input element T
or output element U
are
+zero-sized and will return the original slice without splitting anything.
This method is essentially a transmute
with respect to the elements in the returned
+middle slice, so all the usual caveats pertaining to transmute::<T, U>
also apply here.
Basic usage:
+ +unsafe {
+ let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+ let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>();
+ // less_efficient_algorithm_for_bytes(prefix);
+ // more_efficient_algorithm_for_aligned_shorts(shorts);
+ // less_efficient_algorithm_for_bytes(suffix);
+}
portable_simd
)Split a slice into a prefix, a middle of aligned SIMD types, and a suffix.
+This is a safe wrapper around slice::align_to
, so has the same weak
+postconditions as that method. You’re only assured that
+self.len() == prefix.len() + middle.len() * LANES + suffix.len()
.
Notably, all of the following are possible:
+prefix.len() >= LANES
.middle.is_empty()
despite self.len() >= 3 * LANES
.suffix.len() >= LANES
.That said, this is a safe method, so if you’re only writing safe code, +then this can at most cause incorrect logic, not unsoundness.
+This will panic if the size of the SIMD type is different from
+LANES
times that of the scalar.
At the time of writing, the trait restrictions on Simd<T, LANES>
keeps
+that from ever happening, as only power-of-two numbers of lanes are
+supported. It’s possible that, in the future, those restrictions might
+be lifted in a way that would make it possible to see panics from this
+method for something like LANES == 3
.
#![feature(portable_simd)]
+use core::simd::prelude::*;
+
+let short = &[1, 2, 3];
+let (prefix, middle, suffix) = short.as_simd::<4>();
+assert_eq!(middle, []); // Not enough elements for anything in the middle
+
+// They might be split in any possible way between prefix and suffix
+let it = prefix.iter().chain(suffix).copied();
+assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
+
+fn basic_simd_sum(x: &[f32]) -> f32 {
+ use std::ops::Add;
+ let (prefix, middle, suffix) = x.as_simd();
+ let sums = f32x4::from_array([
+ prefix.iter().copied().sum(),
+ 0.0,
+ 0.0,
+ suffix.iter().copied().sum(),
+ ]);
+ let sums = middle.iter().copied().fold(sums, f32x4::add);
+ sums.reduce_sum()
+}
+
+let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
+assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
portable_simd
)Split a mutable slice into a mutable prefix, a middle of aligned SIMD types, +and a mutable suffix.
+This is a safe wrapper around slice::align_to_mut
, so has the same weak
+postconditions as that method. You’re only assured that
+self.len() == prefix.len() + middle.len() * LANES + suffix.len()
.
Notably, all of the following are possible:
+prefix.len() >= LANES
.middle.is_empty()
despite self.len() >= 3 * LANES
.suffix.len() >= LANES
.That said, this is a safe method, so if you’re only writing safe code, +then this can at most cause incorrect logic, not unsoundness.
+This is the mutable version of slice::as_simd
; see that for examples.
This will panic if the size of the SIMD type is different from
+LANES
times that of the scalar.
At the time of writing, the trait restrictions on Simd<T, LANES>
keeps
+that from ever happening, as only power-of-two numbers of lanes are
+supported. It’s possible that, in the future, those restrictions might
+be lifted in a way that would make it possible to see panics from this
+method for something like LANES == 3
.
is_sorted
)Checks if the elements of this slice are sorted.
+That is, for each element a
and its following element b
, a <= b
must hold. If the
+slice yields exactly zero or one element, true
is returned.
Note that if Self::Item
is only PartialOrd
, but not Ord
, the above definition
+implies that this function returns false
if any two consecutive items are not
+comparable.
#![feature(is_sorted)]
+let empty: [i32; 0] = [];
+
+assert!([1, 2, 2, 9].is_sorted());
+assert!(![1, 3, 2, 4].is_sorted());
+assert!([0].is_sorted());
+assert!(empty.is_sorted());
+assert!(![0.0, 1.0, f32::NAN].is_sorted());
is_sorted
)Checks if the elements of this slice are sorted using the given comparator function.
+Instead of using PartialOrd::partial_cmp
, this function uses the given compare
+function to determine whether two elements are to be considered in sorted order.
#![feature(is_sorted)]
+
+assert!([1, 2, 2, 9].is_sorted_by(|a, b| a <= b));
+assert!(![1, 2, 2, 9].is_sorted_by(|a, b| a < b));
+
+assert!([0].is_sorted_by(|a, b| true));
+assert!([0].is_sorted_by(|a, b| false));
+
+let empty: [i32; 0] = [];
+assert!(empty.is_sorted_by(|a, b| false));
+assert!(empty.is_sorted_by(|a, b| true));
is_sorted
)Checks if the elements of this slice are sorted using the given key extraction function.
+Instead of comparing the slice’s elements directly, this function compares the keys of the
+elements, as determined by f
. Apart from that, it’s equivalent to is_sorted
; see its
+documentation for more information.
#![feature(is_sorted)]
+
+assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
+assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
Returns the index of the partition point according to the given predicate +(the index of the first element of the second partition).
+The slice is assumed to be partitioned according to the given predicate.
+This means that all elements for which the predicate returns true are at the start of the slice
+and all elements for which the predicate returns false are at the end.
+For example, [7, 15, 3, 5, 4, 12, 6]
is partitioned under the predicate x % 2 != 0
+(all odd numbers are at the start, all even at the end).
If this slice is not partitioned, the returned result is unspecified and meaningless, +as this method performs a kind of binary search.
+See also binary_search
, binary_search_by
, and binary_search_by_key
.
let v = [1, 2, 3, 3, 5, 6, 7];
+let i = v.partition_point(|&x| x < 5);
+
+assert_eq!(i, 4);
+assert!(v[..i].iter().all(|&x| x < 5));
+assert!(v[i..].iter().all(|&x| !(x < 5)));
If all elements of the slice match the predicate, including if the slice +is empty, then the length of the slice will be returned:
+ +let a = [2, 4, 8];
+assert_eq!(a.partition_point(|x| x < &100), a.len());
+let a: [i32; 0] = [];
+assert_eq!(a.partition_point(|x| x < &100), 0);
If you want to insert an item to a sorted vector, while maintaining +sort order:
+ +let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x < num);
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
slice_take
)Removes the subslice corresponding to the given range +and returns a reference to it.
+Returns None
and does not modify the slice if the given
+range is out of bounds.
Note that this method only accepts one-sided ranges such as
+2..
or ..6
, but not 2..6
.
Taking the first three elements of a slice:
+ +#![feature(slice_take)]
+
+let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut first_three = slice.take(..3).unwrap();
+
+assert_eq!(slice, &['d']);
+assert_eq!(first_three, &['a', 'b', 'c']);
Taking the last two elements of a slice:
+ +#![feature(slice_take)]
+
+let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut tail = slice.take(2..).unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(tail, &['c', 'd']);
Getting None
when range
is out of bounds:
#![feature(slice_take)]
+
+let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.take(5..));
+assert_eq!(None, slice.take(..5));
+assert_eq!(None, slice.take(..=4));
+let expected: &[char] = &['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.take(..4));
slice_take
)Removes the subslice corresponding to the given range +and returns a mutable reference to it.
+Returns None
and does not modify the slice if the given
+range is out of bounds.
Note that this method only accepts one-sided ranges such as
+2..
or ..6
, but not 2..6
.
Taking the first three elements of a slice:
+ +#![feature(slice_take)]
+
+let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut first_three = slice.take_mut(..3).unwrap();
+
+assert_eq!(slice, &mut ['d']);
+assert_eq!(first_three, &mut ['a', 'b', 'c']);
Taking the last two elements of a slice:
+ +#![feature(slice_take)]
+
+let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut tail = slice.take_mut(2..).unwrap();
+
+assert_eq!(slice, &mut ['a', 'b']);
+assert_eq!(tail, &mut ['c', 'd']);
Getting None
when range
is out of bounds:
#![feature(slice_take)]
+
+let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.take_mut(5..));
+assert_eq!(None, slice.take_mut(..5));
+assert_eq!(None, slice.take_mut(..=4));
+let expected: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.take_mut(..4));
slice_take
)Removes the first element of the slice and returns a reference +to it.
+Returns None
if the slice is empty.
#![feature(slice_take)]
+
+let mut slice: &[_] = &['a', 'b', 'c'];
+let first = slice.take_first().unwrap();
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'a');
slice_take
)Removes the first element of the slice and returns a mutable +reference to it.
+Returns None
if the slice is empty.
#![feature(slice_take)]
+
+let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let first = slice.take_first_mut().unwrap();
+*first = 'd';
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'d');
slice_take
)Removes the last element of the slice and returns a reference +to it.
+Returns None
if the slice is empty.
#![feature(slice_take)]
+
+let mut slice: &[_] = &['a', 'b', 'c'];
+let last = slice.take_last().unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'c');
slice_take
)Removes the last element of the slice and returns a mutable +reference to it.
+Returns None
if the slice is empty.
#![feature(slice_take)]
+
+let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let last = slice.take_last_mut().unwrap();
+*last = 'd';
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'d');
get_many_mut
)Returns mutable references to many indices at once, without doing any checks.
+For a safe alternative see get_many_mut
.
Calling this method with overlapping or out-of-bounds indices is undefined behavior +even if the resulting references are not used.
+#![feature(get_many_mut)]
+
+let x = &mut [1, 2, 4];
+
+unsafe {
+ let [a, b] = x.get_many_unchecked_mut([0, 2]);
+ *a *= 10;
+ *b *= 100;
+}
+assert_eq!(x, &[10, 2, 400]);
get_many_mut
)Returns mutable references to many indices at once.
+Returns an error if any index is out-of-bounds, or if the same index was +passed more than once.
+#![feature(get_many_mut)]
+
+let v = &mut [1, 2, 3];
+if let Ok([a, b]) = v.get_many_mut([0, 2]) {
+ *a = 413;
+ *b = 612;
+}
+assert_eq!(v, &[413, 2, 612]);
slice_flatten
)Takes a &[[T; N]]
, and flattens it to a &[T]
.
This panics if the length of the resulting slice would overflow a usize
.
This is only possible when flattening a slice of arrays of zero-sized
+types, and thus tends to be irrelevant in practice. If
+size_of::<T>() > 0
, this will never panic.
#![feature(slice_flatten)]
+
+assert_eq!([[1, 2, 3], [4, 5, 6]].flatten(), &[1, 2, 3, 4, 5, 6]);
+
+assert_eq!(
+ [[1, 2, 3], [4, 5, 6]].flatten(),
+ [[1, 2], [3, 4], [5, 6]].flatten(),
+);
+
+let slice_of_empty_arrays: &[[i32; 0]] = &[[], [], [], [], []];
+assert!(slice_of_empty_arrays.flatten().is_empty());
+
+let empty_slice_of_arrays: &[[u32; 10]] = &[];
+assert!(empty_slice_of_arrays.flatten().is_empty());
slice_flatten
)Takes a &mut [[T; N]]
, and flattens it to a &mut [T]
.
This panics if the length of the resulting slice would overflow a usize
.
This is only possible when flattening a slice of arrays of zero-sized
+types, and thus tends to be irrelevant in practice. If
+size_of::<T>() > 0
, this will never panic.
#![feature(slice_flatten)]
+
+fn add_5_to_all(slice: &mut [i32]) {
+ for i in slice {
+ *i += 5;
+ }
+}
+
+let mut array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
+add_5_to_all(array.flatten_mut());
+assert_eq!(array, [[6, 7, 8], [9, 10, 11], [12, 13, 14]]);
Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII upper case equivalent.
+ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.
+To uppercase the value in-place, use make_ascii_uppercase
.
Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII lower case equivalent.
+ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.
+To lowercase the value in-place, use make_ascii_lowercase
.
Sorts the slice.
+This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) worst-case.
+When applicable, unstable sorting is preferred because it is generally faster than stable
+sorting and it doesn’t allocate auxiliary memory.
+See sort_unstable
.
The current algorithm is an adaptive, iterative merge sort inspired by +timsort. +It is designed to be very fast in cases where the slice is nearly sorted, or consists of +two or more sorted sequences concatenated one after another.
+Also, it allocates temporary storage half the size of self
, but for short slices a
+non-allocating insertion sort is used instead.
let mut v = [-5, 4, 1, -3, 2];
+
+v.sort();
+assert!(v == [-5, -3, 1, 2, 4]);
Sorts the slice with a comparator function.
+This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) worst-case.
+The comparator function must define a total ordering for the elements in the slice. If
+the ordering is not total, the order of the elements is unspecified. An order is a
+total order if it is (for all a
, b
and c
):
a < b
, a == b
or a > b
is true, anda < b
and b < c
implies a < c
. The same must hold for both ==
and >
.For example, while f64
doesn’t implement Ord
because NaN != NaN
, we can use
+partial_cmp
as our sort function when we know the slice doesn’t contain a NaN
.
let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0];
+floats.sort_by(|a, b| a.partial_cmp(b).unwrap());
+assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]);
When applicable, unstable sorting is preferred because it is generally faster than stable
+sorting and it doesn’t allocate auxiliary memory.
+See sort_unstable_by
.
The current algorithm is an adaptive, iterative merge sort inspired by +timsort. +It is designed to be very fast in cases where the slice is nearly sorted, or consists of +two or more sorted sequences concatenated one after another.
+Also, it allocates temporary storage half the size of self
, but for short slices a
+non-allocating insertion sort is used instead.
let mut v = [5, 4, 1, 3, 2];
+v.sort_by(|a, b| a.cmp(b));
+assert!(v == [1, 2, 3, 4, 5]);
+
+// reverse sorting
+v.sort_by(|a, b| b.cmp(a));
+assert!(v == [5, 4, 3, 2, 1]);
Sorts the slice with a key extraction function.
+This sort is stable (i.e., does not reorder equal elements) and O(m * n * log(n)) +worst-case, where the key function is O(m).
+For expensive key functions (e.g. functions that are not simple property accesses or
+basic operations), sort_by_cached_key
is likely to be
+significantly faster, as it does not recompute element keys.
When applicable, unstable sorting is preferred because it is generally faster than stable
+sorting and it doesn’t allocate auxiliary memory.
+See sort_unstable_by_key
.
The current algorithm is an adaptive, iterative merge sort inspired by +timsort. +It is designed to be very fast in cases where the slice is nearly sorted, or consists of +two or more sorted sequences concatenated one after another.
+Also, it allocates temporary storage half the size of self
, but for short slices a
+non-allocating insertion sort is used instead.
let mut v = [-5i32, 4, 1, -3, 2];
+
+v.sort_by_key(|k| k.abs());
+assert!(v == [1, 2, -3, 4, -5]);
Sorts the slice with a key extraction function.
+During sorting, the key function is called at most once per element, by using +temporary storage to remember the results of key evaluation. +The order of calls to the key function is unspecified and may change in future versions +of the standard library.
+This sort is stable (i.e., does not reorder equal elements) and O(m * n + n * log(n)) +worst-case, where the key function is O(m).
+For simple key functions (e.g., functions that are property accesses or
+basic operations), sort_by_key
is likely to be
+faster.
The current algorithm is based on pattern-defeating quicksort by Orson Peters, +which combines the fast average case of randomized quicksort with the fast worst case of +heapsort, while achieving linear time on slices with certain patterns. It uses some +randomization to avoid degenerate cases, but with a fixed seed to always provide +deterministic behavior.
+In the worst case, the algorithm allocates temporary storage in a Vec<(K, usize)>
the
+length of the slice.
let mut v = [-5i32, 4, 32, -3, 2];
+
+v.sort_by_cached_key(|k| k.to_string());
+assert!(v == [-3, -5, 2, 32, 4]);
Copies self
into a new Vec
.
let s = [10, 40, 30];
+let x = s.to_vec();
+// Here, `s` and `x` can be modified independently.
allocator_api
)Copies self
into a new Vec
with an allocator.
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let s = [10, 40, 30];
+let x = s.to_vec_in(System);
+// Here, `s` and `x` can be modified independently.
Flattens a slice of T
into a single value Self::Output
.
assert_eq!(["hello", "world"].concat(), "helloworld");
+assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
Flattens a slice of T
into a single value Self::Output
, placing a
+given separator between each.
assert_eq!(["hello", "world"].join(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]);
+assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]);
Flattens a slice of T
into a single value Self::Output
, placing a
+given separator between each.
assert_eq!(["hello", "world"].connect(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]);
Extend implementation that copies elements out of references before pushing them onto the Vec.
+This implementation is specialized for slice iterators, where it uses copy_from_slice
to
+append the entire slice at once.
extend_one
)extend_one
)extend_one
)extend_one
)Converts a BinaryHeap<T>
into a Vec<T>
.
This conversion requires no data movement or allocation, and has +constant time complexity.
+Convert a clone-on-write slice into a vector.
+If s
already owns a Vec<T>
, it will be returned directly.
+If s
is borrowing a slice, a new Vec<T>
will be allocated and
+filled by cloning s
’s items into it.
let o: Cow<'_, [i32]> = Cow::Owned(vec![1, 2, 3]);
+let b: Cow<'_, [i32]> = Cow::Borrowed(&[1, 2, 3]);
+assert_eq!(Vec::from(o), Vec::from(b));
Convert a vector into a boxed slice.
+Before doing the conversion, this method discards excess capacity like Vec::shrink_to_fit
.
assert_eq!(Box::from(vec![1, 2, 3]), vec![1, 2, 3].into_boxed_slice());
Any excess capacity is removed:
+ +let mut vec = Vec::with_capacity(10);
+vec.extend([1, 2, 3]);
+
+assert_eq!(Box::from(vec), vec![1, 2, 3].into_boxed_slice());
Turn a VecDeque<T>
into a Vec<T>
.
This never needs to re-allocate, but does need to do O(n) data movement if +the circular buffer doesn’t happen to be at the beginning of the allocation.
+use std::collections::VecDeque;
+
+// This one is *O*(1).
+let deque: VecDeque<_> = (1..5).collect();
+let ptr = deque.as_slices().0.as_ptr();
+let vec = Vec::from(deque);
+assert_eq!(vec, [1, 2, 3, 4]);
+assert_eq!(vec.as_ptr(), ptr);
+
+// This one needs data rearranging.
+let mut deque: VecDeque<_> = (1..5).collect();
+deque.push_front(9);
+deque.push_front(8);
+let ptr = deque.as_slices().1.as_ptr();
+let vec = Vec::from(deque);
+assert_eq!(vec, [8, 9, 1, 2, 3, 4]);
+assert_eq!(vec.as_ptr(), ptr);
Convert all colors in place, without reallocating.
+ +use palette::{convert::FromColor, SaturateAssign, Srgb, Lch};
+
+let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)];
+let mut lch = Vec::<Lch>::from_color(srgb);
+
+lch.saturate_assign(0.1);
+
+let srgb = Vec::<Srgb>::from_color(lch);
Convert all colors in place, without reallocating.
+ +use palette::{convert::FromColorUnclamped, SaturateAssign, Srgb, Lch};
+
+let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)];
+let mut lch = Vec::<Lch>::from_color_unclamped(srgb);
+
+lch.saturate_assign(0.1);
+
+let srgb = Vec::<Srgb>::from_color_unclamped(lch);
Collects an iterator into a Vec, commonly called via Iterator::collect()
In general Vec
does not guarantee any particular growth or allocation strategy.
+That also applies to this trait impl.
Note: This section covers implementation details and is therefore exempt from +stability guarantees.
+Vec may use any or none of the following strategies, +depending on the supplied iterator:
+Iterator::size_hint()
+pushing
one item at a timeThe last case warrants some attention. It is an optimization that in many cases reduces peak memory
+consumption and improves cache locality. But when big, short-lived allocations are created,
+only a small fraction of their items get collected, no further use is made of the spare capacity
+and the resulting Vec
is moved into a longer-lived structure, then this can lead to the large
+allocations having their lifetimes unnecessarily extended which can result in increased memory
+footprint.
In cases where this is an issue, the excess capacity can be discarded with Vec::shrink_to()
,
+Vec::shrink_to_fit()
or by collecting into Box<[T]>
instead, which additionally reduces
+the size of the long-lived struct.
static LONG_LIVED: Mutex<Vec<Vec<u16>>> = Mutex::new(Vec::new());
+
+for i in 0..10 {
+ let big_temporary: Vec<u16> = (0..1024).collect();
+ // discard most items
+ let mut result: Vec<_> = big_temporary.into_iter().filter(|i| i % 100 == 0).collect();
+ // without this a lot of unused capacity might be moved into the global
+ result.shrink_to_fit();
+ LONG_LIVED.lock().unwrap().push(result);
+}
The hash of a vector is the same as that of the corresponding slice,
+as required by the core::borrow::Borrow
implementation.
use std::hash::BuildHasher;
+
+let b = std::hash::RandomState::new();
+let v: Vec<u8> = vec![0xa8, 0x3c, 0x09];
+let s: &[u8] = &[0xa8, 0x3c, 0x09];
+assert_eq!(b.hash_one(v), b.hash_one(s));
Creates a consuming iterator, that is, one that moves each value out of +the vector (from start to end). The vector cannot be used after calling +this.
+let v = vec!["a".to_string(), "b".to_string()];
+let mut v_iter = v.into_iter();
+
+let first_element: Option<String> = v_iter.next();
+
+assert_eq!(first_element, Some("a".to_string()));
+assert_eq!(v_iter.next(), Some("b".to_string()));
+assert_eq!(v_iter.next(), None);
Implements ordering of vectors, lexicographically.
+Implements comparison of vectors, lexicographically.
+try_components_as
fails to cast.try_components_as_mut
fails to cast.Attempts to convert a Vec<T>
into a Box<[T; N]>
.
Like Vec::into_boxed_slice
, this is in-place if vec.capacity() == N
,
+but will require a reallocation otherwise.
Returns the original Vec<T>
in the Err
variant if
+boxed_slice.len()
does not equal N
.
This can be used with vec!
to create an array on the heap:
let state: Box<[f32; 100]> = vec![1.0; 100].try_into().unwrap();
+assert_eq!(state.len(), 100);
try_from_components
fails to cast.try_from_components
fails to cast.try_from_components
fails to cast.Write is implemented for Vec<u8>
by appending to the vector.
+The vector will grow as needed.
can_vector
)write_all_vectored
)parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Texture {
+ SolidColor(SolidColor),
+ SpatialChecker(SpatialChecker),
+ SurfaceChecker(SurfaceChecker),
+}
A texture enum.
+SolidColor texture
+SpatialChecker texture
+SurfaceChecker texture
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreTextures enable different surface textures for colorizing objects in various ways.
+pub use solid_color::*;
pub use spatial_checker::*;
pub use surface_checker::*;
pub struct SolidColor {
+ pub color: Xyz<E>,
+}
A solid color texture. Simplest possible Texture: returns a solid color at any surface coordinate or spatial position.
+color: Xyz<E>
The color of the Texture.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SolidColorInit {
+ pub color: ColorInit,
+}
Initialization structure for a solid color texture.
+color: ColorInit
Initialization struct for the color.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreCheckered texture based on the world coordinates.
+pub struct SpatialChecker {
+ pub even: Xyz<E>,
+ pub odd: Xyz<E>,
+ pub density: Float,
+}
A standard checkered texture based on spatial 3D texturing.
+even: Xyz<E>
Uniform color for the even-numbered checkers of the texture.
+odd: Xyz<E>
Uniform color for the odd-numbered checkers of the texture.
+density: Float
Controls the density of the checkered pattern. Default value is 1.0, which corresponds to filling a 1.0 unit cube in the coordinate system with one color of the pattern. Even values preferred - odd values may create a visually thicker stripe due to two stripes with same color being next to each other.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SpatialCheckerInit {
+ pub even: ColorInit,
+ pub odd: ColorInit,
+ pub density: Float,
+}
A standard checkered texture based on spatial 3D texturing.
+even: ColorInit
Uniform color for the even-numbered checkers of the texture.
+odd: ColorInit
Uniform color for the odd-numbered checkers of the texture.
+density: Float
Controls the density of the checkered pattern. Default value is 1.0, which corresponds to filling a 1.0 unit cube in the coordinate system with one color of the pattern. Even values preferred - odd values may create a visually thicker stripe due to two stripes with same color being next to each other.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreCheckered texture based on the surface coordinates of an object.
+pub struct SurfaceChecker { /* private fields */ }
A standard checkered texture based on 2D surface UV coordinates.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SurfaceCheckerInit {
+ pub even: ColorInit,
+ pub odd: ColorInit,
+ pub density: Float,
+}
A standard checkered texture based on 2D surface UV coordinates.
+even: ColorInit
Uniform color for the even-numbered checkers of the texture.
+odd: ColorInit
Uniform color for the odd-numbered checkers of the texture.
+density: Float
Controls the density of the checkered pattern. Default value is 10, which corresponds to using 10 tiles over the width of the object. On spheres, this means 10 tiles around the sphere.
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type Position = Vec3;
Internal type alias: a nalgebra Vector3
+struct Position {
+ pub data: ArrayStorage<f32, 3, 1>,
+ /* private fields */
+}
data: ArrayStorage<f32, 3, 1>
The data storage that contains all the matrix components. Disappointed?
+Well, if you came here to see how you can access the matrix components,
+you may be in luck: you can access the individual components of all vectors with compile-time
+dimensions <= 6 using field notation like this:
+vec.x
, vec.y
, vec.z
, vec.w
, vec.a
, vec.b
. Reference and assignation work too:
let mut vec = Vector3::new(1.0, 2.0, 3.0);
+vec.x = 10.0;
+vec.y += 30.0;
+assert_eq!(vec.x, 10.0);
+assert_eq!(vec.y + 100.0, 132.0);
Similarly, for matrices with compile-time dimensions <= 6, you can use field notation
+like this: mat.m11
, mat.m42
, etc. The first digit identifies the row to address
+and the second digit identifies the column to address. So mat.m13
identifies the component
+at the first row and third column (note that the count of rows and columns start at 1 instead
+of 0 here. This is so we match the mathematical notation).
For all matrices and vectors, independently from their size, individual components can
+be accessed and modified using indexing: vec[20]
, mat[(20, 19)]
. Here the indexing
+starts at 0 as you would expect.
pub type Vec2 = Vector2<Float>;
struct Vec2 {
+ pub data: ArrayStorage<f32, 2, 1>,
+ /* private fields */
+}
data: ArrayStorage<f32, 2, 1>
The data storage that contains all the matrix components. Disappointed?
+Well, if you came here to see how you can access the matrix components,
+you may be in luck: you can access the individual components of all vectors with compile-time
+dimensions <= 6 using field notation like this:
+vec.x
, vec.y
, vec.z
, vec.w
, vec.a
, vec.b
. Reference and assignation work too:
let mut vec = Vector3::new(1.0, 2.0, 3.0);
+vec.x = 10.0;
+vec.y += 30.0;
+assert_eq!(vec.x, 10.0);
+assert_eq!(vec.y + 100.0, 132.0);
Similarly, for matrices with compile-time dimensions <= 6, you can use field notation
+like this: mat.m11
, mat.m42
, etc. The first digit identifies the row to address
+and the second digit identifies the column to address. So mat.m13
identifies the component
+at the first row and third column (note that the count of rows and columns start at 1 instead
+of 0 here. This is so we match the mathematical notation).
For all matrices and vectors, independently from their size, individual components can
+be accessed and modified using indexing: vec[20]
, mat[(20, 19)]
. Here the indexing
+starts at 0 as you would expect.
pub type Vec3 = Vector3<Float>;
struct Vec3 {
+ pub data: ArrayStorage<f32, 3, 1>,
+ /* private fields */
+}
data: ArrayStorage<f32, 3, 1>
The data storage that contains all the matrix components. Disappointed?
+Well, if you came here to see how you can access the matrix components,
+you may be in luck: you can access the individual components of all vectors with compile-time
+dimensions <= 6 using field notation like this:
+vec.x
, vec.y
, vec.z
, vec.w
, vec.a
, vec.b
. Reference and assignation work too:
let mut vec = Vector3::new(1.0, 2.0, 3.0);
+vec.x = 10.0;
+vec.y += 30.0;
+assert_eq!(vec.x, 10.0);
+assert_eq!(vec.y + 100.0, 132.0);
Similarly, for matrices with compile-time dimensions <= 6, you can use field notation
+like this: mat.m11
, mat.m42
, etc. The first digit identifies the row to address
+and the second digit identifies the column to address. So mat.m13
identifies the component
+at the first row and third column (note that the count of rows and columns start at 1 instead
+of 0 here. This is so we match the mathematical notation).
For all matrices and vectors, independently from their size, individual components can
+be accessed and modified using indexing: vec[20]
, mat[(20, 19)]
. Here the indexing
+starts at 0 as you would expect.
pub type Vec4 = Vector4<Float>;
struct Vec4 {
+ pub data: ArrayStorage<f32, 4, 1>,
+ /* private fields */
+}
data: ArrayStorage<f32, 4, 1>
The data storage that contains all the matrix components. Disappointed?
+Well, if you came here to see how you can access the matrix components,
+you may be in luck: you can access the individual components of all vectors with compile-time
+dimensions <= 6 using field notation like this:
+vec.x
, vec.y
, vec.z
, vec.w
, vec.a
, vec.b
. Reference and assignation work too:
let mut vec = Vector3::new(1.0, 2.0, 3.0);
+vec.x = 10.0;
+vec.y += 30.0;
+assert_eq!(vec.x, 10.0);
+assert_eq!(vec.y + 100.0, 132.0);
Similarly, for matrices with compile-time dimensions <= 6, you can use field notation
+like this: mat.m11
, mat.m42
, etc. The first digit identifies the row to address
+and the second digit identifies the column to address. So mat.m13
identifies the component
+at the first row and third column (note that the count of rows and columns start at 1 instead
+of 0 here. This is so we match the mathematical notation).
For all matrices and vectors, independently from their size, individual components can
+be accessed and modified using indexing: vec[20]
, mat[(20, 19)]
. Here the indexing
+starts at 0 as you would expect.
pub const MAX_WAVELENGTH: Wavelength = 780;
The upper bound for the wavelenghts, exclusive
+pub const MIN_WAVELENGTH: Wavelength = 380;
The lower bound for the wavelengths, inclusive
+pub const SPECTRUM: Range<Wavelength>;
The range of wavelenghts used, inclusive low, exclusive high
+pub const SPECTRUM_SIZE: usize = _; // 400usize
The length of the wavelength spectrum used
+pub const WAVELENGTH_PROBABILITY: Float = _; // 0.00249999994f32
The probability of picking a specific wavelength
+pub const WAVE_SAMPLE_COUNT: usize = 4;
The count of wavelenghts used per ray in Hero Wavelength Sampling
+pub fn random_wavelength(rng: &mut SmallRng) -> Wavelength
Return a random wavelength, sampled uniformly from the visible spectrum.
+pub fn rotate_wavelength(hero: Wavelength) -> [Wavelength; 4]
Given a hero wavelength, create additional equidistant wavelengths in the visible spectrum. Returns an array of wavelengths, with the original hero wavelength as the first one.
+pub fn sample_wavelength(sample: Float) -> Wavelength
Given a sample seed from a sampler, return the approximate wavelenght.
+pub fn wavelength_into_xyz(lambda: Wavelength) -> Xyz<E>
Helper function adapted from https://en.wikipedia.org/wiki/CIE_1931_color_space#Analytical_approximation
+The fundamental building blocks of spectral rendering.
+pub type Wavelength = usize;
Wavelength in nanometers
+Command Line Interface for the clovers
raytracing renderer.
clovers
raytracing renderer.pub struct Opts {
+ pub(crate) input: String,
+ pub(crate) output: Option<String>,
+ pub(crate) width: u32,
+ pub(crate) height: u32,
+ pub(crate) samples: u32,
+ pub(crate) max_depth: u32,
+ pub(crate) quiet: bool,
+ pub(crate) gpu: bool,
+ pub(crate) debug: bool,
+ pub(crate) normalmap: bool,
+ pub(crate) sampler: Sampler,
+}
Command line parameters for the clovers
raytracing renderer.
input: String
Input filename / location
+output: Option<String>
Output filename / location. Default: renders/unix_timestamp.png
+width: u32
Width of the image in pixels. Default: 1024
+height: u32
Height of the image in pixels. Default: 1024
+samples: u32
Number of samples to generate per each pixel. Default: 64
+max_depth: u32
Maximum evaluated bounce depth for each ray. Default: 64
+quiet: bool
Suppress most of the text output
+gpu: bool
Use the GPU draw process instead of CPU
+debug: bool
Enable some debug logging
+normalmap: bool
Render a normal map only. Experimental feature.
+sampler: Sampler
Sampler to use for rendering. Experimental feature.
+ArgGroup::id
][crate::ArgGroup::id] for this set of argumentsArgMatches
to self
.ArgMatches
to self
.parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into a Left
variant of Either<Self, Self>
+if into_left
is true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into a Left
variant of Either<Self, Self>
+if into_left(&self)
returns true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn colorize(
+ ray: &Ray,
+ scene: &Scene<'_>,
+ depth: u32,
+ max_depth: u32,
+ rng: &mut SmallRng,
+ sampler: &dyn SamplerTrait<'_>
+) -> Xyz<E>
The main coloring function. Sends a [Ray
] to the [Scene
], sees if it hits anything, and eventually returns a color. Taking into account the Material that is hit, the method recurses with various adjustments, with a new [Ray
] started from the location that was hit.
An opinionated colorize method. Given a [Ray] and a [Scene], evaluates the ray’s path and returns a color.
+Ray
] to the [Scene
], sees if it hits anything, and eventually returns a color. Taking into account the Material that is hit, the method recurses with various adjustments, with a new [Ray
] started from the location that was hit.An opinionated method for drawing a scene using the CPU for rendering.
+Vec<Srgb>
as a pixelbuffer.Runtime functions of the clovers
renderer.
pub fn normal_map(ray: &Ray, scene: &Scene<'_>, rng: &mut SmallRng) -> LinSrgb
Rendering function for getting a normal map in tangent space. Sends a [Ray] to the [Scene], sees what it hits, gets the normal at that point, and returns a color based on the normal mapping colorization. Wikipedia: Normal mapping.
+pub fn normal_to_color(normal: Direction) -> LinSrgb
Given a surface normal, return a color based on normal mapping colorization.
+Alternative rendering method. Only returns a normalmap of the image, colorized using standard conventions.
+pub fn blue_sample_spp1(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp128(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp16(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp2(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp256(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp32(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp4(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp64(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
pub fn blue_sample_spp8(
+ pixel_i: i32,
+ pixel_j: i32,
+ sample_index: i32,
+ sample_dimension: SamplerDimension
+) -> Float
A sampler based on blue noise. Works especially well at low samples-per-pixel counts.
+Utilizes library code from https://github.com/Jasper-Bekkers/blue-noise-sampler.
+pub struct BlueSampler { /* private fields */ }
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into a Left
variant of Either<Self, Self>
+if into_left
is true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into a Left
variant of Either<Self, Self>
+if into_left(&self)
returns true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Sampler {
+ Blue,
+ Random,
+}
Enum of the supported samplers.
+Blue noise based sampler, see BlueSampler
+Random number generator based sampler, see RandomSampler
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into a Left
variant of Either<Self, Self>
+if into_left
is true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into a Left
variant of Either<Self, Self>
+if into_left(&self)
returns true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum SamplerDimension {
+ PixelOffsetX,
+ PixelOffsetY,
+ LensOffsetX,
+ LensOffsetY,
+ Time,
+ Wavelength,
+}
Various sampling dimensions used by the samplers
+source
. Read moreparameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into a Left
variant of Either<Self, Self>
+if into_left
is true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into a Left
variant of Either<Self, Self>
+if into_left(&self)
returns true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreSampler architecture for the renderer, based on the sampling infrastructure described in the book Physically Based Rendering, chapter 8.3 Sampling Interface
+A sampler based on a random number generator. This is the default sampler used in this renderer. It works especially well at high samples-per-pixel counts.
+pub struct RandomSampler<'scene> { /* private fields */ }
parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into a Left
variant of Either<Self, Self>
+if into_left
is true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into a Left
variant of Either<Self, Self>
+if into_left(&self)
returns true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Randomness {
+ pub pixel_offset: Vec2,
+ pub lens_offset: Vec2,
+ pub time: Float,
+ pub wavelength: Wavelength,
+}
A collection of random values to be used for each sample. Returned as a struct to ensure the correct sampling order for the underlying source of randomness.
+pixel_offset: Vec2
Intra-pixel (x,y)
offset, both in range [0..1]
. Used for antialiasing.
lens_offset: Vec2
The (x,y)
offset used in the lens equations for aperture / depth-of-field simulation. The coordinates are within the range [-0.5..0.5]
and within a unit disk.
time: Float
The time of the ray, in range [0..1]
wavelength: Wavelength
Wavelength of the ray
+parameters
when converting.self
into C
, using the provided parameters.angle
.other
into Self
, while performing the appropriate scaling,
+rounding and clamping.T
.parameters
when converting.self
into C
, using the provided parameters.self
into a Left
variant of Either<Self, Self>
+if into_left
is true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into a Left
variant of Either<Self, Self>
+if into_left(&self)
returns true
.
+Converts self
into a Right
variant of Either<Self, Self>
+otherwise. Read moreself
into T
, while performing the appropriate scaling,
+rounding and clamping.self
from the equivalent element of its
+superset. Read moreself
is actually part of its subset T
(and can be converted to it).self.to_subset
but without any property checks. Always succeeds.self
to the equivalent element of its superset.try_into_colors
fails to cast.OutOfBounds
error is returned which contains
+the unclamped color. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait SamplerTrait<'scene> {
+ // Required methods
+ fn sample(&mut self, i: i32, j: i32, index: i32) -> Randomness;
+ fn sample_dimension(
+ &mut self,
+ i: i32,
+ j: i32,
+ index: i32,
+ dimension: SamplerDimension
+ ) -> Float;
+}
Manually request a sample from the specific dimension
+Vec<T>
, short …","Internal type alias: a nalgebra Vector2 which is a vector …","Internal type alias: a nalgebra Vector3 which is a vector …","Internal type alias: a nalgebra Vector4 which is a vector …","Axis-aligned bounding box.","","","","Returns a reference to the underlying allocator.","Returns a reference to the underlying allocator.","Moves all the elements of other
into self
, leaving other
…","","","","","","","","","","","","","","","","","","","","","","","","Returns an unsafe mutable pointer to the vector’s …","Extracts a mutable slice of the entire vector.","Returns a raw pointer to the vector’s buffer, or a …","","","","","","Extracts a slice containing the entire vector.","","","","","Converts to Box<T, A>
.","Converts to Box<[T], A>
.","","","","","","","","","","","","","","Bounding Volume Hierarchy Node.","","","","","","","Camera. Used for creating Rays towards the scene, with …","Returns the total number of elements the vector can hold …","","Clears the vector, removing all values.","","","","","","Returns a new box with a clone()
of this box’s contents.","","","","","Copies source
’s contents into self
without creating a …","","","","","","","","","Initialization structures for colors. This exists for …","","","","","","The data storage that contains all the matrix components. …","The data storage that contains all the matrix components. …","The data storage that contains all the matrix components. …","The data storage that contains all the matrix components. …","Removes consecutive repeated elements in the vector …","Removes all but the first of consecutive elements in the …","Removes all but the first of consecutive elements in the …","","","Creates a Box<T>
, with the Default
value for T.","","","","Creates an empty Vec<T>
.","","","","","","","","","","","","","","","","Attempt to downcast the box to a concrete type.","Attempt to downcast the box to a concrete type.","Attempt to downcast the box to a concrete type.","","Downcasts the box to a concrete type.","Downcasts the box to a concrete type.","Downcasts the box to a concrete type.","Removes the specified range from the vector in bulk, …","","","","","","","","","","","","","","","","","","Clones and appends all elements in a slice to the Vec
.","Copies elements from src
range to the end of the vector.","","","","","Creates an iterator which uses a closure to determine if …","","","","","","","","","","","","Converts the given String
to a boxed str
slice that is …","","","","","","","","","","","","","","","","","","","","Creates a boxed Path
from a reference.","Converts an OsString
into a Box<OsStr> without copying or …","Creates a boxed Path
from a clone-on-write pointer.","","Converts a Cow
into a box of dyn Error
+ Send
+ Sync
.","Converts a Cow<'a, OsStr>
into a Box<OsStr>, by copying …","Converts a PathBuf
into a Box<Path>.","Copies the string into a newly allocated Box<OsStr>.","","","","","Converts a Box<str>
into a Box<[u8]>
","Converts a &str
into a Box<str>
","Converts a str
into a box of dyn Error
+ Send
+ Sync
.","","","Converts a type of Error
+ Send
+ Sync
into a box of dyn …","Converts a type of Error
into a box of dyn Error
.","","","","","Converts a Cow
into a box of dyn Error
.","","","Convert a vector into a boxed slice.","","","Converts a &[T]
into a Box<[T]>
","Converts a Cow<'_, str>
into a Box<str>
","","","Converts a &CStr
into a Box<CStr>
, by copying the contents …","Converts a Cow<'a, CStr>
into a Box<CStr>
, by copying the …","Converts a str
into a box of dyn Error
.","","Converts a String
into a box of dyn Error
.","","","","Converts a Cow<'_, [T]>
into a Box<[T]>
","Converts a T
into a Box<T>
","Converts a [T; N]
into a Box<[T]>
","Converts a CString
into a Box<CStr> without copying or …","Converts a String
into a box of dyn Error
+ Send
+ Sync
.","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","Returns the argument unchanged.","Allocate a Vec<u8>
and fill it with a UTF-8 string.","Returns the argument unchanged.","","Allocate a Vec<T>
and fill it by cloning s
’s items.","Converts a CString
into a Vec<u8>.","Allocate a Vec<T>
and move s
’s items into it.","Allocate a Vec<T>
and fill it by cloning s
’s items.","Allocate a Vec<T>
and fill it by cloning s
’s items.","Converts the given String
to a vector Vec
that holds …","Turn a VecDeque<T>
into a Vec<T>
.","Allocate a Vec<T>
and fill it by cloning s
’s items.","Convert a clone-on-write slice into a vector.","Convert a boxed slice into a vector by transferring …","Converts a BinaryHeap<T>
into a Vec<T>
.","","","","","","","Convert all colors in place, without reallocating.","Convert all colors in place, without reallocating.","Convert all colors in place, without reallocating.","Convert all colors in place, without reallocating.","","","Constructs a box from a raw pointer.","Constructs a box from a raw pointer in the given allocator.","Creates a Vec<T>
directly from a pointer, a length, and a …","Creates a Vec<T, A>
directly from a pointer, a length, a …","","","","","","","","","","","","","","","","","","","Height of the render in pixels","An abstraction for things that can be hit by Rays.","","","","Inserts an element at position index
within the vector, …","Interval helper adapted from the book","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","Converts a Box<T>
into a Box<[T]>
","Converts the vector into Box<[T]>
.","","","","","","","","","","","","","","","","","Takes a Vec<[T; N]>
and flattens it into a Vec<T>
.","","Consumes the Box
, returning the wrapped value.","","","","Creates a consuming iterator, that is, one that moves each …","Converts a Box<T>
into a Pin<Box<T>>
. If T
does not …","Consumes the Box
, returning a wrapped raw pointer.","Decomposes a Vec<T>
into its raw components: …","Decomposes a Vec<T>
into its raw components: …","Consumes the Box
, returning a wrapped raw pointer and the …","","","","","","","","","","","","","Returns true
if the vector contains no elements.","","","","","","","","","","","Consumes and leaks the Box
, returning a mutable reference, …","Consumes and leaks the Vec
, returning a mutable reference …","","Returns the number of elements in the vector, also …","","Materials enable different behaviors of light on objects.","Maximum ray bounce depth. Higher number implies higher …","","","","","","","","","Allocates memory on the heap and then places x
into it.","Constructs a new, empty Vec<T>
.","Allocates memory in the given allocator then places x
into …","Constructs a new, empty Vec<T, A>
.","","Constructs a new box with uninitialized contents.","Constructs a new box with uninitialized contents in the …","Constructs a new boxed slice with uninitialized contents.","Constructs a new boxed slice with uninitialized contents …","Constructs a new Box
with uninitialized contents, with the …","Constructs a new Box
with uninitialized contents, with the …","Constructs a new boxed slice with uninitialized contents, …","Constructs a new boxed slice with uninitialized contents …","","","","","Experimental render mode: return a normal map only instead …","","","Various literal objects and meta-object utilities for …","Orthonormal bases","","","","Probability density functions","Constructs a new Pin<Box<T>>
. If T
does not implement Unpin
…","Constructs a new Pin<Box<T, A>>
. If T
does not implement …","","","Removes the last element from a vector and returns it, or …","","Appends an element to the back of a collection.","Appends an element if there is sufficient spare capacity, …","Optionally, suppress CLI output","Various internal helper functions for getting specific …","The very core of the ray tracing rendering itself: the Ray","","","","","","","","","","","","","","","","Removes and returns the element at position index
within …","Reserves capacity for at least additional
more elements to …","Reserves the minimum capacity for at least additional
more …","Resizes the Vec
in-place so that len
is equal to new_len
.","Resizes the Vec
in-place so that len
is equal to new_len
.","","Retains only the elements specified by the predicate.","Retains only the elements specified by the predicate, …","Samples per pixel to render for multisampling. Higher …","A collection of objects, camera, and other things …","","","","","Forces the length of the vector to new_len
.","","Shrinks the capacity of the vector with a lower bound.","Shrinks the capacity of the vector as much as possible.","","","","Returns the remaining spare capacity of the vector as a …","Utilities for Physically Meaningful Rendering using …","Creates a splicing iterator that replaces the specified …","Returns vector content as a slice of T
, along with the …","Splits the collection into two at the given index.","","","","Removes an element from the vector and returns it.","Textures enable different surface textures for colorizing …","","","","","","","","","","","","Shortens the vector, keeping the first len
elements and …","","","","","","","","","","","Attempts to convert a Box<[T]>
into a Box<[T; N]>
.","Attempts to convert a Vec<T>
into a Box<[T; N]>
.","","","","","","","","","","","Allocates memory on the heap then places x
into it, …","Allocates memory in the given allocator then places x
into …","Constructs a new box with uninitialized contents on the …","Constructs a new box with uninitialized contents in the …","Constructs a new boxed slice with uninitialized contents. …","Constructs a new Box
with uninitialized contents, with the …","Constructs a new Box
with uninitialized contents, with the …","Constructs a new boxed slice with uninitialized contents, …","Tries to reserve capacity for at least additional
more …","Tries to reserve the minimum capacity for at least …","Constructs a new, empty Vec<T>
with at least the specified …","Constructs a new, empty Vec<T, A>
with at least the …","","","","","","","","","","","","","","","","","","","The fundamental building blocks of spectral rendering.","Width of the render in pixels","Constructs a new, empty Vec<T>
with at least the specified …","Constructs a new, empty Vec<T, A>
with at least the …","Writes the value and converts to Box<T, A>
.","","","","","","","","","","","","","","","","","","","","","","","Axis-aligned bounding box Defined by two opposing corners, …","","","","","Returns the interval of the given axis.","","","","","","","","","","Returns the argument unchanged.","","","","Given a Ray, returns whether the ray hits the bounding box …","Given a Ray, returns whether the ray hits the bounding box …","Given a Ray, returns whether the ray hits the bounding box …","Calls U::from(self)
.","","","","","","","Creates a new axis-aligned bounding box from three …","Creates a new axis-aligned bounding box from two …","Make sure we don’t have a zero-thickness AABB, padding …","","Given two axis-aligned bounding boxes, return a new AABB …","","","","","","","","","","","","The bounding interval on the X axis","The bounding interval on the Y axis","The bounding interval on the Z axis","Bounding Volume Hierarchy Node.","","","","","","Returns the axis-aligned bounding box AABB of the objects …","Bounding box containing both of the child nodes","","","","","Returns the count of the nodes in the tree","","Returns the argument unchanged.","","Create a new BVHNode
tree from a given list of Objects","","","The main hit
function for a BVHNode
. Given a Ray, and an …","Calls U::from(self)
.","","","","","","","Left child of the BVHNode
","Returns a probability density function value based on the …","Returns a random point on the surface of one of the …","Right child of the BVHNode
","","","","","","","","","","","","The main Camera object used in the ray tracing.","Represents the fields that can be described in a Scene …","","","Describes the size of the aperture of the camera.","","","","","","","","","","","","","","","","","","","","","Describes the distance at which the camera has been …","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Generates a new Ray from the camera","Defines the horizontal axis for the camera.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Defines the lens radius for the camera. TODO: understand …","Describes where the camera is looking at","Describes where the camera is","Coordinate of the lower left corner of the camera.","Creates a new Camera with the given parameters.","Defines the origin of the camera.","","","Defines the earliest starting time for the camera, used …","Defines the latest ending time for the camera, used when …","","","","","","","","","","","","","","","","","U","","","","","Describes the subjective “up” direction for the camera …","V","Defines the vertical axis for the camera.","Describes the vertical field of view for the camera","","","W","Legacy color, assume Srgb given as array of three floats …","Initialization structure for a color. Contains either an …","Hex “web color” Srgb","Linear Srgb","Oklch","Non-linear Srgb","Typesafe color initialization structure","Type safe initialization structure for a color. Can be …","XYZ, D65 illuminant","XYZ, E illuminant","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Represents a ray-object intersection, with plenty of data …","An abstraction for things that can be hit by Rays.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Distance from the ray origin to the hitpoint","","","","Returns the argument unchanged.","","Returns the argument unchanged.","","","","","","","","","","","","","Returns the argument unchanged.","","","","","","","","","","Is the hitpoint at the front of the surface","Returns a tuple of (front_face, normal)
. Used in lieu of …","","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","Reference to the material at the hitpoint","Surface normal from the hitpoint","","","","3D coordinate of the hitpoint","","","","Helper function for getting normals pointing at the …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","U surface coordinate of the hitpoint","","","","","","","V surface coordinate of the hitpoint","","","","An interval structure.","","","","","","","","","","","","","Returns an interval expanded with delta at both ends","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Largest value of the interval. Must be kept in order","Smallest value of the interval. Must be kept in order ","Constructs a new interval","Constructs a new interval from two intervals","","Returns the size of the interval","","","","","","","","","","","","ConeLight material","Dielectric material","A matte material that does not reflect rays","DiffuseLight material","Dispersive material","Isotropic material","Lambertian material","A material enum. TODO: for ideal clean abstraction, this …","Initialization structure for a Material
. Either contains a …","Trait for materials. Requires three function …","Enum for the types of materials: Diffuse and Specular …","Metal material","Owned material structure","A record of an scattering event of a Ray on a Material.","Name of the shared material","A Material
that can be referred to by name for reuse …","A shiny material that reflects some rays","","","","","","","","","","","","","","","","Current color to take into account when following the …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A cone light material.","","","","","","","A dielectric material. This resembles glass and other …","A diffuse light material.","Dispersive material. Based on Cauchy’s equation","Returns the emissivity of the material at the given …","Returns the emissivity of the material at the given …","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","","","","","","","","","Wrapper for GLTF materials.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Isotropic material.","Lambertian material. This is the default material with a …","The shared material itself","The material type that was scattered on","A metal material.","Name of the shared material","Probability density function to use with the ScatterRecord
.","Given a ray and a hitrecord, return the possible …","Given a ray and a hitrecord, return the possible …","TODO: explain","TODO: explain","","","","Direction of a generated specular ray","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A cone light material. The material emits light if the …","","","","","","","","","","Creates a new ConeLight
with white light at intensity 100.0
…","","Emission function for ConeLight
. If the given HitRecord
…","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new ConeLight
material with the given Texture.","Scatter method for the ConeLight
material. Always returns …","Scattering probability density function for the ConeLight
…","","","","","","","","","","","","","A dielectric material. This resembles glass and other …","","","","","","","","","Color of the material. Used for colorizing the rays. …","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new Dielectric material with the given …","Refractive index of the material. Used for calculating the …","Scatter method for the Dielectric material. Given a ray
…","Scattering probability density function for Dielectric …","","","","","","","","","","","","","A diffuse light material. On this material, rays never …","","","","","","","","","","Creates a new DiffuseLight
with white light at intensity …","","Emission function for DiffuseLight
. If the given HitRecord
…","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new DiffuseLight
material with the given Texture.","Scatter method for the DiffuseLight
material. Always …","Scattering probability density function for the …","","","","","","","","","","","","","A dispersive glass material.","","","","","","","Cauchy coefficient A of the material","Cauchy coefficient B of the material","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new Dispersive material with the given Cauchy …","Calculates the refractive index of the material for the …","","","","","","","","","","","","","","","GLTF Material wrapper type","","","","","","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Initialize a new GLTF material wrapper","","","","","","","","","","","","","","Isotropic material. Used in ConstantMedium
. TODO: …","","","","","","","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new Isotropic material with an albedo of the …","Returns a ScatterRecord
based on the HitRecord
coordinates …","Returns the scattering probability density function for …","","","","","","","","","","","","","Lambertian material. This is the default material with a …","","","","","","","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new instance of the Lambertian material with an …","Returns None, if ray is absorbed. Otherwise, returns a …","Returns the scattering probability density function for …","","","","","","","","","","","","","A metal material. The amount of reflection can be adjusted …","","","","","","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Creates a new Metal material with the albedo of the given …","Scatter function for the Metal material. Metal always …","Scattering probability density function for Metal. Always …","","","","","","","","","","","","","Boxy object initializer","ConstantMedium
object initializer","GLTF object initializer","MovingSphere
object initializer","An object enum. TODO: for ideal clean abstraction, this …","A list of objects. Allows multiple objects to be used e.g. …","ObjectList
object initializer","Quad object initializer","RotateY
object initializer","STL object initializer","Sphere object initializer","Translate object initializer","Triangle object initializer","","","","","","","","","","","A box or a cuboid object: a parallelepiped with six …","","","","","","","","","ConstantMedium
object. This should probably be a Material …","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","GLTF format support for the renderer","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","A moving sphere object.","Initializes an Object
into a Hitable
.","The encased Object list","Priority","A quadrilateral object.","Utility object for rotating another object.","","","A sphere object.","STL utilities","","","","","","","Utility object for translating i.e. moving another object.","A triangle object. Almost exact copy of Quad, with an …","","","","","","","","","","","","","","","","","A box or a cuboid object: a parallelepiped with six …","BoxyInit
structure describes the necessary data for …","Axis-aligned bounding box","","","","","","","","","","","Returns the axis-aligned bounding box AABB of the object.","","","","","","","","","First corner for the box","Second, opposing corner for the box","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","The main hit
function for a Boxy. Given a Ray, and an …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Material used for the box","The material of the box","Initializes a new instance of a box, given two opposing …","Returns a probability density function value? // TODO: …","Used for multiple importance sampling","Returns a random point on the box","","","","","","","","","","","","","","","","","","","","","","","","ConstantMedium
object. This should probably be a Material …","ConstantMediumInit
structure describes the necessary data …","","","","","","","","","","","The boundary object for the constant medium. This …","Returns the axis-aligned bounding box AABB of the defining …","","","","","","","","","Density of the fog. TODO: example good value range?","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Hit function for the ConstantMedium
object. Returns a …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Creates a new ConstantMedium
with a known size, shape and …","Returns a probability density function value based on the …","Used for multiple importance sampling","Returns a random point on the surface of the boundary of …","","Texture used for the colorization of the fog.","","","","","","","","","","","","","","","","","","","","","","","Internal GLTF object representation after initialization.","GLTF initialization structure","Internal GLTF object representation after initialization.","Axis-aligned bounding box of the object","Axis-aligned bounding box of the object","","","","","","","","","","","","","","","","Return the axis-aligned bounding box for the object","","Bounding Volume Hierarchy tree for the object","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","","","Hit method for the GLTF object","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","Material of the object","Create a new STL object with the given initialization …","Initialize a new GLTF object","Path of the .gltf file","Returns a probability density function value based on the …","","Used for multiple importance sampling","Returns a random point on the surface of the object","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A moving sphere object. This is represented by one radius
, …","SphereInit
structure describes the necessary data for …","Axis-aligned bounding box","","","","","","","","","","","Returns the axis-aligned bounding box of the MovingSphere
…","","","Returns the interpolated center of the moving sphere at …","Center point of the sphere at time_0
","Center point of the sphere at time_0
","Center point of the sphere at time_1
","Center point of the sphere at time_1
","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Returns the U,V surface coordinates of a hitpoint","Hit method for the MovingSphere
object. First gets the …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Material of the sphere.","Material of the sphere","Creates a new MovingSphere
object. See the struct …","","Used for multiple importance sampling","Radius of the sphere.","Radius of the sphere","","","Time 0","Time 1","","","","","","","","","","","","","","","","","","","","","","","Quadrilateral shape. This can be an arbitrary …","Initialization structure for a Quad object.","Bounding box of the surface","","","Area of the surface","","","","","","","","","Returns the bounding box of the quad","","","","","","","","","What is this? // TODO: understand, explain","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Hit method for the quad rectangle","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Material of the surface","Material of the surface","Creates a new quad","Normal vector of the surface","Returns a probability density function value? // TODO: …","Used for multiple importance sampling","Corner point","Corner point","Returns a random point on the quadrilateral surface","","","","","","","","","","","","","","","","","","Vector describing the u side","Vector describing the u side","","","","","Vector describing the v side","Vector describing the v side","","","What is this? // TODO: understand, explain","RotateInit
structure describes the necessary data for …","RotateY
object. It wraps the given Object and has adjusted …","","","Angle to rotate the object, in degrees","","","","","","","","","Bounding box method for the RotateY
object. Finds the …","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Hit method for the RotateY
object. Finds the …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Creates a new RotateY
object. It wraps the given Object …","The encased Object to rotate","","Used for multiple importance sampling","","","","","","","","","","","","","","","","","","","","","","","","","A sphere object.","SphereInit
structure describes the necessary data for …","","","","","","","","","","","Returns the axis-aligned bounding box AABB for the sphere.","","","Center of the sphere.","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Returns the U,V surface coordinates of a hitpoint","Hit method for the Sphere object. Returns a HitRecord
if …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Material of the sphere.","Creates a new Sphere
object with the given center, radius …","Returns the probability density function for the sphere? …","Used for multiple importance sampling","Radius of the sphere.","Utility function from Ray Tracing: The Rest of Your Life. …","","","","","","","","","","","","","","","","","","","","","","","","Internal STL object representation after initialization. …","STL structure. This gets converted into an internal …","Axis-aligned bounding box of the object","","","","","","","","","","","Return the axis-aligned bounding box for the object","Bounding Volume Hierarchy tree for the object","","","Location of the object in the rendered scene","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Hit method for the STL object","Initializes an STL","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Material for the object","Material to use for the .stl object","Path of the .stl file","Returns a probability density function value based on the …","Used for multiple importance sampling","Returns a random point on the ssurface of the object","Rotation of the object. Described as three angles, roll
, …","Scaling factor for the object","","","","","","","","","","","","","","","","","","","","","","","","Translate object. It wraps the given Object and has …","TranslateInit
structure describes the necessary data for …","","","","","","","","","","","Bounding box method for the Translate object. Finds the …","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Hit method for the Translate object. Finds the …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Creates a new Translate
object. It wraps the given Object …","The encased Object to translate i.e. move","The vector describing the movement of the object","Returns a probability density function value based on the …","Used for multiple importance sampling","Returns a random point on the surface of the moved object","","","","","","","","","","","","","","","","","","","","","","","","Triangle shape. Heavily based on Quad and may contain …","Initialization structure for a triangle primitive","Bounding box of the surface","","","Area of the surface","","","","","","","","","Returns the bounding box of the triangle","","","","","","","","","What is this? // TODO: understand, explain","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","Creates a new triangle from three Cartesian space …","","","","","Hit method for the triangle","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Material of the surface","Material of the surface","Creates a new triangle from a coordinate point and two …","Normal vector of the surface","Returns a probability density function value? // TODO: …","Used for multiple importance sampling","Corner point","Corner point","Returns a random point on the triangle surface","","","","","","","","","","","","","","","","","","Vector describing the u side","Vector describing the u side","","","","","Vector describing the v side","Vector describing the v side","","","What is this? // TODO: understand, explain","An orthonormal basis structure.","","","","","","Builds a new ONB structure given a normal vector.","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","Returns the ONB-projected version of the provided vector?","","","","","","","","","U","","","V","","W","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","","","","","","","","","","","","","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Internal helper.","Internal helper.","Internal helper.","Internal helper.","A Ray has an origin and a direction, as well as an instant …","","","","","","","","","","The direction of the ray.","","Evaluates the position (coordinate) at which the ray is at …","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","","","","","","The origin of the ray.","The time instant at which the ray exists.","","","","","","","","","","","","Wavelength of the ray","A representation of the scene that is being rendered.","A serialized representation of a Scene.","","","","","","","The background color to use when the rays do not hit …","","","","","","","The camera object used for rendering the scene.","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Bounding-volume hierarchy of Hitable objects in the scene. …","Initializes a new Scene instance by parsing the contents …","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Creates a new Scene with the given parameters.","A BVHNode
tree of prioritized objects - e.g. glass items …","","","","","","","","","","","","","","","","","","","","","","","Hand-converted from spectra_xyz_5nm_380_780_0.97.h
in the …","Hand-converted from spectrum_grid.h
in the supplemental …","Evaluate the spectrum at the given wavelength for the …","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","SolidColor texture","SpatialChecker texture","SurfaceChecker texture","A texture enum.","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","","Calls U::from(self)
.","","","","","","","","A solid color texture.","Checkered texture based on the world coordinates.","Checkered texture based on the surface coordinates of an …","","","","","","","","","","","","","","","A solid color texture. Simplest possible Texture: returns …","Initialization structure for a solid color texture.","","","","","","","","","","","","","","","","","Initialization struct for the color.","The color of the Texture.","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Creates a new solid color texture with the specified color.","","","","","","","","","","","","","","","","","","","","","","","","","A standard checkered texture based on spatial 3D texturing.","A standard checkered texture based on spatial 3D texturing.","","","","","","","","","","","","","","","","","","","Controls the density of the checkered pattern. Default …","Controls the density of the checkered pattern. Default …","","","Uniform color for the even-numbered checkers of the …","Uniform color for the even-numbered checkers of the …","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Create a new SpatialChecker
object with the specified …","Uniform color for the odd-numbered checkers of the texture.","Uniform color for the odd-numbered checkers of the texture.","","","","","","","","","","","","","","","","","","","","","","","","","A standard checkered texture based on 2D surface UV …","A standard checkered texture based on 2D surface UV …","","","","","","","","","","","","","","","","","","","Controls the density of the checkered pattern. Default …","","","Uniform color for the even-numbered checkers of the …","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","Create a new SurfaceChecker
object with the specified …","Uniform color for the odd-numbered checkers of the texture.","","","","","","","","","","","","","","","","","","","","","","","","","The upper bound for the wavelenghts, exclusive","The lower bound for the wavelengths, inclusive","The range of wavelenghts used, inclusive low, exclusive …","The length of the wavelength spectrum used","The probability of picking a specific wavelength","The count of wavelenghts used per ray in Hero Wavelength …","Wavelength in nanometers","Return a random wavelength, sampled uniformly from the …","Given a hero wavelength, create additional equidistant …","Given a sample seed from a sampler, return the approximate …","Helper function adapted from …"],"i":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,36,5,2,5,5,2,2,5,2,5,2,36,5,2,36,5,2,5,2,5,2,5,2,5,2,2,5,5,5,5,5,2,2,5,5,2,5,2,5,2,5,2,2,2,2,2,2,2,36,5,5,2,2,36,5,5,0,2,2,2,2,36,5,0,5,2,5,2,2,2,2,2,2,2,36,5,2,2,5,2,36,5,2,2,5,2,0,2,36,5,2,2,185,173,252,177,5,5,5,2,2,2,2,2,2,5,2,5,2,5,2,2,2,2,2,2,2,2,36,5,2,2,2,2,2,2,2,2,5,2,5,2,2,2,2,5,5,5,5,5,5,2,2,2,5,5,5,5,5,5,5,5,5,2,2,2,2,5,2,2,2,36,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,36,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,2,36,5,2,5,2,5,2,5,2,5,2,2,5,5,2,36,5,2,36,5,2,5,2,5,5,5,5,5,5,2,2,5,36,0,2,5,5,5,0,2,36,5,2,36,5,2,2,2,5,5,5,2,2,5,2,36,5,2,36,5,2,36,5,2,2,2,5,5,5,5,5,2,2,2,5,5,5,2,2,5,5,2,2,2,36,5,2,2,2,5,5,5,2,2,5,2,36,5,2,2,2,2,5,2,2,2,5,2,5,2,0,36,2,2,5,5,5,5,5,5,2,5,2,5,2,2,2,2,2,2,2,2,2,2,2,2,2,36,2,2,0,0,2,2,5,0,2,2,2,2,5,2,5,5,36,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,2,5,5,36,0,2,2,36,5,5,2,5,5,2,2,2,5,0,5,5,5,2,2,2,5,0,2,36,5,2,2,36,5,2,36,5,2,5,2,2,5,2,5,2,36,5,2,2,2,2,36,5,2,5,2,36,5,2,36,5,2,2,2,2,2,2,2,2,5,5,5,5,2,36,5,2,5,2,5,2,36,5,2,36,5,2,5,2,36,5,0,36,5,5,2,2,2,5,2,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,5,0,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,0,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,0,0,183,184,184,183,184,183,184,183,184,183,184,183,184,183,184,183,184,183,184,183,184,183,184,184,183,184,183,184,183,184,183,184,183,183,183,184,183,184,183,184,183,184,183,184,183,184,183,184,183,184,184,183,183,183,183,184,183,183,183,184,183,184,183,184,183,184,183,184,183,184,183,184,183,184,183,183,184,183,184,184,183,183,184,183,184,183,187,0,186,186,186,186,187,0,186,186,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,186,187,115,115,115,0,115,115,115,0,0,0,115,115,115,115,115,115,115,180,115,189,180,115,189,180,115,189,180,115,189,180,115,189,188,115,189,180,115,189,115,189,115,189,180,115,189,180,180,115,189,180,115,115,115,115,115,115,115,115,115,115,115,115,115,115,189,180,115,189,180,115,189,180,115,189,180,0,188,115,189,180,115,189,180,115,189,180,115,189,180,115,189,180,115,189,180,115,189,180,115,189,180,180,188,115,189,180,188,115,189,180,115,189,180,115,189,180,115,189,180,115,189,180,115,189,180,115,115,115,115,115,115,115,115,115,115,115,115,115,115,189,180,115,189,180,115,189,180,180,115,189,180,115,189,180,180,115,189,0,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,203,203,204,203,203,203,203,0,0,0,0,203,201,0,201,0,204,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,0,201,202,203,201,202,203,0,0,0,206,203,201,202,203,204,205,201,202,203,203,203,203,203,203,203,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,0,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,0,0,202,205,0,202,205,206,203,206,203,201,202,203,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,203,203,203,203,203,203,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,201,202,203,204,205,0,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,0,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,0,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,0,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,214,0,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,0,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,221,221,221,221,0,0,221,221,221,221,221,221,221,220,221,220,221,220,221,220,221,220,221,0,220,221,220,221,220,221,220,221,0,220,221,220,221,220,221,220,221,220,221,220,221,0,220,221,220,221,220,221,220,221,220,221,220,221,220,221,0,0,220,220,0,0,220,221,0,0,220,221,220,221,220,221,0,0,220,221,220,221,220,221,220,221,220,221,220,221,220,221,220,221,0,0,199,222,199,222,199,222,199,222,199,222,199,199,222,199,222,199,222,199,222,199,222,222,222,222,199,222,199,222,199,222,199,222,199,199,222,199,222,199,222,199,222,199,222,199,222,199,222,199,222,199,199,199,222,199,222,222,199,222,199,222,199,222,199,222,199,222,199,222,199,222,199,222,199,222,199,222,199,0,0,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,223,223,193,223,193,223,193,223,193,223,193,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,193,193,223,193,223,223,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,223,193,0,0,0,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,190,198,190,114,190,198,114,190,198,114,190,198,114,190,198,114,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,198,190,198,114,190,198,114,190,198,114,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,114,190,198,0,0,200,225,200,225,200,225,200,225,200,225,200,200,225,200,200,225,200,225,200,225,200,225,200,225,200,225,225,200,225,200,225,200,225,200,225,200,200,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,200,200,225,225,200,200,225,200,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,225,200,0,0,197,226,197,197,226,197,226,197,226,197,226,197,197,226,197,226,197,226,197,226,197,197,226,226,197,226,197,226,197,226,197,226,197,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,197,197,197,226,226,197,197,226,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,226,197,197,0,0,227,194,227,227,194,227,194,227,194,227,194,194,227,194,227,194,227,194,227,194,227,227,194,227,194,227,194,227,194,227,194,194,227,194,227,194,227,194,227,194,227,194,227,194,227,194,194,227,194,227,194,227,227,194,227,194,227,194,227,194,227,194,227,194,227,194,227,194,227,194,227,194,227,194,0,0,228,192,228,192,228,192,228,192,228,192,192,228,192,228,228,192,228,192,228,192,228,228,192,228,192,228,192,228,192,228,192,192,192,228,192,228,192,228,192,228,192,228,192,228,192,228,192,228,192,192,228,228,192,228,228,192,228,192,228,192,228,192,228,192,228,192,228,192,228,192,228,192,228,192,228,192,0,0,195,195,229,195,229,195,229,195,229,195,229,195,195,195,229,229,195,229,195,229,195,229,229,195,229,195,229,195,229,195,229,195,229,195,0,195,229,195,229,195,229,195,229,195,229,195,229,195,229,195,229,229,195,229,195,229,229,229,195,229,195,229,195,229,195,229,195,229,195,229,195,229,195,229,195,229,195,229,195,229,0,0,230,191,230,191,230,191,230,191,230,191,191,230,191,230,191,230,191,230,191,230,230,191,230,191,230,191,230,191,230,191,191,230,191,230,191,230,191,230,191,230,191,230,191,230,191,191,230,230,191,230,191,230,230,191,230,191,230,191,230,191,230,191,230,191,230,191,230,191,230,191,230,191,230,191,0,0,196,231,196,196,231,196,231,196,231,196,231,196,196,231,196,231,196,231,196,231,196,196,231,231,196,231,196,231,196,196,231,196,231,196,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,196,196,196,231,231,196,196,231,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,231,196,196,0,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,0,233,0,233,0,233,0,0,0,233,0,233,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,237,238,233,234,235,236,237,238,233,233,233,233,233,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,239,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,233,233,233,233,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,233,234,235,236,237,238,239,233,234,235,236,237,238,233,234,235,236,237,238,0,0,0,0,0,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,0,0,241,240,241,240,241,240,241,241,240,241,240,241,240,241,240,240,241,240,240,241,240,241,240,241,240,241,240,241,240,241,0,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,241,240,240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,0,0,0,243,244,243,244,243,244,243,244,243,244,243,244,0,243,244,0,243,244,243,244,243,244,243,244,243,244,243,243,243,244,243,244,243,244,243,244,243,244,243,244,243,244,243,244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,243,244,243,244,243,244,243,244,243,244,243,244,243,244,243,244,243,244,244,243,244,244,0,215,215,215,0,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,215,0,0,0,215,215,215,215,215,215,215,215,215,215,215,215,215,215,0,0,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,248,249,248,249,248,249,248,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,249,248,0,0,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,250,246,0,0,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,251,247,251,251,247,251,247,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,247,251,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,251,247,0,0,0,0,0,0,0,0,0,0,0],"f":"``````````````{{cg}i{}{}{{b{e}}}{}}00{{{d{ce}}}efh}{{{j{ce}}}e{}h}{{{j{ce}}{j{ce}}}l{}h}{{c{n{e}}{n{e}}}{{n{e}}}{}{}}{{{d{{Ab{{A`{c}}}}}}}{{Ab{e}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{j{{A`{c}}}}}{{Ab{e}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}10{ce{}{}}00000{{{d{{Ab{e}}}}}{{Ab{{A`{c}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{j{e}}}{{Ab{{A`{c}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}10{{{d{{Ab{e}}}}}{{Ab{c}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{j{e}}}{{Ab{c}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}10{{{d{c}}}Ah{Ajf}}{{{d{ce}}}cfh}{{{j{ce}}}{{Ab{c}}}{}h}{{{j{ce}}}{{j{ce}}}{}h}{{{j{ce}}}{}{}h}20{{{d{c}}}AlAn}432{cB`{}}4{{{d{{Ab{c}}}}}AbBb}{{{j{c}}}AbBb}10{{{d{{Bd{c}}e}}}{{d{ce}}}{}h}{{{d{{Ab{{Bd{c}}}}e}}}{{d{{Ab{c}}e}}}{}h}{{{d{eg}}c}{}Bf{{Bh{c}}f}h}{{{d{eg}}c}{}Bf{{Bj{c}}f}h}{{{d{eg}}c}{}Bf{{Bl{c}}f}h}<{ce{}{}}00<0=00<`{{{d{eg}}c}{}Bf{{Bn{c}}f}h}{{{d{eg}}c}{}Bf{{C`{c}}f}h}{{{d{eg}}c}{}Bf{{Cb{c}}f}h}{{c{Cd{e}}}g{}{}{}}00`{{{j{ce}}}Cf{}h}{{{d{c}}}{{Cj{Ch}}}Ch}{{{j{ce}}}l{}h}{{{d{Cl}}}{{d{Cl}}}}{{{d{Cn}}}{{d{Cn}}}}{{{d{D`}}}{{d{D`}}}}{{{d{Db}}}{{d{Db}}}}{{{d{Dd}}}{{d{Dd}}}}{{{d{ce}}}{{d{ce}}}Df{hDf}}{{{d{{Ab{c}}e}}}{{d{{Ab{c}}e}}}Df{hDf}}{DhDh}{{{j{ce}}}{{j{ce}}}Df{hDf}}{{{d{{Ab{c}}e}}{d{{Ab{c}}e}}}lDf{hDf}}{{{d{ce}}{d{ce}}}lDf{hDf}}{{{j{ce}}{j{ce}}}lDf{hDf}}{{ce}l{}{}}00{{{d{c}}Dj}Dj{Dlf}}{{{d{ce}}{d{ce}}}Dn{E`f}h}{{{j{ce}}{j{ce}}}DnE`h}{{{d{c}}}Eb{Edf}}`{ce{}{}}00{{{d{c}}Cf}l{Eff}}{{{d{c}}}Eh{Dlf}}````{{{j{ce}}}lEjh}{{{j{ce}}g}l{}h{{C`{cc}{{El{En}}}}}}{{{j{ce}}i}l{}hEj{{C`{c}{{El{g}}}}}}{{}{{d{Db}}}}{{}{{d{Cl}}}}{{}{{d{c}}}F`}{{}{{d{D`}}}}{{}{{d{{Ab{c}}}}}{}}{{}{{d{Cn}}}}{{}{{j{c}}}{}}{{{d{ce}}}cfh}{{{j{ce}}}{{Ab{c}}}{}h}10{{{d{c}}}CnCh}{c{{Fb{{d{Db}}}}}Fd}{c{{Fb{{d{e}}}}}FdFf}{c{{Fb{{d{{Ab{e}}}}}}}FdFf}{c{{Fb{{d{Dd}}}}}Fd}{c{{Fb{{d{D`}}}}}Fd}{c{{Fb{{d{Cl}}}}}Fd}{c{{Fb{{d{Cn}}}}}Fd}{c{{Fb{Dh}}}Fd}{c{{Fb{{j{e}}}}}FdFf}{{{d{c}}}{{Fj{FhFh}}}{Edf}}{{{d{Flc}}}{{Fb{{d{ec}}{d{Flc}}}}}hFl}00{{{d{c}}Fn}{{Cj{l}}}{Dlf}}{{{d{Flc}}}{{d{ec}}}hFl}00{{{j{ce}}g}{{G`{ce}}}{}h{{Gb{Cf}}}}{{{d{ce}}}lfh}{{{j{ce}}}l{}h}{{{d{c}}Dj}l{Dlf}}{{{d{c}}Gd}En{Dlf}}1{{{d{ce}}{d{ce}}}En{Ejf}h}{{{j{eg}}{A`{c}}}En{}{{Ej{c}}}h}{{{j{eg}}{Ab{c}}}En{}{{Ej{c}}}h}{{{j{eg}}{j{ci}}}En{}{{Ej{c}}}hh}121{{{d{c}}Gf}l{Dlf}}{{{d{c}}Gf}En{Dlf}}7{{{j{ce}}g}l{}h{{Gj{}{{Gh{c}}}}}}{{{j{ce}}g}lGlh{{Gj{}{{Gh{c}}}}}}{{{j{ce}}{Ab{c}}}lDfh}{{{j{ce}}g}lDfh{{Gb{Cf}}}}{{{j{ce}}c}l{}h}{{{j{ce}}c}lGlh}{{{j{ce}}Cf}lGlh}{{{j{ce}}Cf}l{}h}{{{j{ce}}g}{{Gn{cge}}}{}h{{C`{c}{{El{En}}}}}}{{{d{c}}}{{Fb{{Ab{H`}}Hb}}}{Eff}}{{{d{c}}{Ab{H`}}}l{B`f}}{{{d{ce}}}Hd{Hff}h}{{{d{c}}}{{Fb{lHb}}}{Hhf}}{{{j{H`c}}}{{Fb{lHb}}}h}{{{d{ce}}Hj}{{Fb{lHl}}}{Hnf}h}{{{d{ce}}Hj}{{Fb{lHl}}}{I`f}h}{{{d{ce}}Hj}{{Fb{lHl}}}fh}{{DhHj}Ib}{{{j{ce}}Hj}{{Fb{lHl}}}Hnh}{{{d{{A`{c}}}}}{{d{{Id{c}}}}}{}}{If{{d{Cn}}}}{{{d{{Ih{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{Ij{c}}}}}{{d{{A`{c}}}}}{}}{{{d{Db}}}{{d{Cn}}}}{{{d{{A`{c}}}}}{{d{{Il{ec}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{In{ec}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{J`{ec}}}}}{}{}}{{{d{{Jb{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Ih{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Jd{ec}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Jf{c}}}}}{}}{{{d{{J`{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{Jh{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{Jf{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{Jj{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Jj{ec}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Jl{ec}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Jn{c}}}}}{}}{{{d{{K`{c{A`{e}}}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{Kb{c}}}}}{{d{{A`{c}}}}}{}}{Dd{{d{Dd}}}}{Kd{{d{Cl}}}}{{{Kf{Dd}}}{{d{Dd}}}}{{{d{{n{c}}}}}{{d{A`}}}Kh}{{{Kf{Cn}}}{{d{Ch}}}}{{{Kf{Cl}}}{{d{Cl}}}}{Kj{{d{Dd}}}}{Cl{{d{Cl}}}}{{{d{{Jd{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{Kl{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{Kn{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Kn{ec}}}}}{}{}}{{{d{Cnc}}}{{d{{Ab{H`}}c}}}h}{Cn{{d{Cn}}}}{Cn{{d{Ch}}}}{{{d{{L`{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Lb{c}}}}}{}}{c{{d{Ch}}}{ChLdLf}}{c{{d{Ch}}}Ch}{{{d{{Id{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{Lh{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{Lj{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Ll{ec}}}}}{}{}}{{{Kf{Cn}}}{{d{Ch}}}}{{{d{{A`{c}}}}}{{d{{Jb{ec}}}}}{}{}}{{{d{{Ln{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{j{ce}}}{{d{{Ab{c}}e}}}{}h}{{{d{A`}}}{{d{{n{c}}}}}Kh}{{{d{{A`{c}}}}}{{d{{M`{ec}}}}}{}{}}{{{Ab{c}}}{{d{{Ab{c}}}}}Df}{{{Kf{Cn}}}{{d{Cn}}}}{{{d{{Ll{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{M`{ce}}}}}{{d{{A`{e}}}}}{}{}}{D`{{d{D`}}}}{{{Kf{D`}}}{{d{D`}}}}{Cn{{d{Ch}}}}{{{d{{A`{c}}}}}{{d{{Mb{c}}}}}{}}{If{{d{Ch}}}}{{{d{{A`{c}}}}}{{d{{Jh{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Kb{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{L`{c}}}}}{}}{{{Kf{{Ab{c}}}}}{{d{{Ab{c}}}}}Df}{c{{d{c}}}{}}{{{A`{c}}}{{d{{Ab{c}}}}}{}}{Md{{d{D`}}}}7{{{d{{Mb{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Mf{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Kl{ec}}}}}{}{}}{{{d{{Il{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Ln{ec}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Lh{ec}}}}}{}{}}{{{d{{In{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{A`{c}}}}}{{d{{Ij{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{K`{e{A`{c}}}}}}}{}{}}{{{d{{Jl{ce}}}}}{{d{{A`{e}}}}}{}{}}{{{d{{Mf{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{Lb{c}}}}}{{d{{A`{c}}}}}{}}{Mhc{}}{{{d{{A`{c}}}}}{{d{{Mj{c}}}}}{}}{cc{}}{{{d{{Mj{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{Jn{c}}}}}{{d{{A`{c}}}}}{}}{{{d{{A`{c}}}}}{{d{{Lj{c}}}}}{}}3{Cn{{j{H`}}}}4{{{Ml{ceg}}}{{j{c}}}{}MnMn}{{{A`{c}}}{{j{c}}}Df}{Md{{j{H`}}}}{{{A`{c}}}{{j{c}}}{}}{{{Ab{c}}}{{j{c}}}Df}0{If{{j{H`}}}}{{{N`{ce}}}{{j{ce}}}{}h}5{{{Kf{{Ab{c}}}}}{{j{c}}}{}}{{{d{{Ab{c}}e}}}{{j{ce}}}{}h}{{{Nb{ce}}}{{j{ce}}}{}h}{Nd{{j{Nf}}}}???{{{d{{Ab{{A`{c}}}}}}}{{d{{Ab{e}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{j{{A`{c}}}}}{{j{e}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{d{{Ab{c}}}}}{{d{{Ab{e}}}}}Af{{Af{}{{Ad{}}}}{Nh{c}}}}{{{j{c}}}{{j{e}}}Af{{Af{}{{Ad{}}}}{Nh{c}}}}{{{d{{Ab{c}}}}}{{d{{Ab{e}}}}}Af{{Af{}{{Ad{}}}}{Nj{c}}}}{{{j{c}}}{{j{e}}}Af{{Af{}{{Ad{}}}}{Nj{c}}}}{e{{d{{Ab{c}}}}}{}{{Gj{}{{Gh{c}}}}}}{e{{j{c}}}{}{{Gj{}{{Gh{c}}}}}}{{}{{d{c}}}f}{c{{d{ec}}}hf}{{CfCf}{{j{c}}}{}}{{CfCfc}{{j{ec}}}h{}}{ce{}{}}00000{{{d{Ab}}}{{d{{Ab{c}}}}}Bb}{j{{j{c}}}Bb}{{{d{ce}}{d{ce}}}En{Nlf}h}{{{j{H`}}Cf}{{Cj{H`}}}}0{{{j{H`}}}Cf}0{{{j{H`}}{Nn{Cf}}}{{Cj{{Ab{H`}}}}}}03{{{d{ce}}g}l{O`f}hHf}{{{j{ce}}g}lO`hHf}``{{{d{c}}}{{Fb{{Cj{{j{H`}}}}Ob}}}{Edf}}{{{j{ce}}g}{}{}h{{Od{{Ab{c}}}}}}0{{{j{ce}}Cfc}l{}h}`;;;;;;{{{d{{Ab{e}}}}}{{d{{Ab{{A`{c}}}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{d{{Ab{e}}}}}{{Ab{{A`{c}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}0{{{j{e}}}{{j{{A`{c}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{j{e}}}{{Ab{{A`{c}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}0{c{}{}}{{{d{ce}}}{{d{{Ab{c}}e}}}{}h}{{{j{ce}}}{{d{{Ab{c}}e}}}{}h}{{c{Cd{e}}}g{}{}{}}00{ce{}{}}00000{{{d{{Ab{c}}}}}{{d{{Ab{e}}}}}Af{}}{{{d{{Ab{c}}}}}{{Ab{e}}}Af{}}0{{{j{c}}}{{Ab{e}}}Af{}}0{{{j{c}}}{{j{e}}}Af{}}{{{j{e}}}{}Of{{Oh{c}}}}{{{j{{A`{c}}e}}}{{j{ce}}}{}h}:{{{d{ce}}}c{}h}7{{{j{ce}}}{}{}h}00{{{d{ce}}}{{Oj{{d{ce}}}}}fh}{{{d{ce}}}{}fh}{{{j{ce}}}{{Fj{CfCf}}}{}h}{{{j{ce}}}{{Fj{CfCfe}}}{}h}{{{d{ce}}}{{Fj{e}}}fh}{{cCn}{{Ol{e}}}{}{}}>>>{{{d{{Ab{c}}}}}AbBb}{{{d{{Ab{c}}}}}{{d{Ab}}}Bb}1{{{j{c}}}AbBb}0{{{j{c}}}jBb}{{cCn}En{}}{{{d{ce}}}En{Onf}h}{{{j{ce}}}En{}h}{cEn{}}003{{{d{c}}}En{A`f}}4{{{d{c}}}En{Hhf}}{{{j{H`c}}}Enh}{{{d{ce}}}Cj{Abf}h}{{{d{ce}}{d{ce}}}En{Nlf}h}{{{d{ce}}}cfh}{{{j{ce}}}{{Ab{c}}}{}h}{{{d{ce}}}Cf{Onf}h}{{{j{ce}}}Cf{}h}4``{{{d{c}}}{{Cj{Ad}}}{Dlf}}{{{d{ce}}{d{ce}}}En{Ejf}h}{{{j{eg}}{Ab{c}}}En{}{{Ej{c}}}h}{{{j{eg}}{j{ci}}}En{}{{Ej{c}}}hh}1{{{j{eg}}{A`{c}}}En{}{{Ej{c}}}h}20{c{{d{c}}}{}}{{}{{j{c}}}{}}{{ce}{{d{ce}}}{}{hh}}{c{{j{ec}}}h{}}{{{d{c}}Af}Dj{Dlf}}{{}{{d{{Bd{c}}}}}{}}{c{{d{{Bd{e}}c}}}{hh}{}}{Cf{{d{{Ab{{Bd{c}}}}}}}{}}{{Cfc}{{d{{Ab{{Bd{e}}}}c}}}h{}}3210{{{d{ce}}}Cj{Abf}h}{{{d{ce}}}Cj{Ahf}h}{{{d{c}}}Fh{B`f}}{{{d{c}}}Hd{B`f}}`{{{d{ce}}Cf}Cj{Abf}h}{{{d{ce}}Cf}Cj{Ahf}h}``{{{d{c}}}Aj{Edf}}{{{d{ce}}{d{ce}}}{{Cj{Dn}}}{Nlf}h}{{{j{ce}}{j{cg}}}{{Cj{Dn}}}Nlhh}`{c{{Oj{{d{c}}}}}{}}{{ce}{{Oj{{d{ce}}}}}{}{hh}}{{{Oj{{d{ce}}}}Al}An{AA`AAbf}h}{{{Oj{{d{c}}}}Al}{{An{Cj}}}{AAdAAbf}}{{{j{ce}}}{{Cj{c}}}{}h}{{{d{c}}AAf}lCh}{{{j{ce}}c}l{}h}{{{j{ce}}c}{{Fb{lc}}}{}h}```{{{d{c}}{Ab{H`}}}{{Fb{CfHb}}}{A`f}}{{{d{c}}AAh}{{Fb{lHb}}}{A`f}}{{{d{c}}{Ab{H`}}}{{Fb{lHb}}}{A`f}}{{{d{c}}{Ab{H`}}}{{Fb{lOb}}}{Edf}}{{{d{{d{c}}}}{Ab{H`}}}{{Fb{lOb}}}{Edf}}{{{d{c}}If}{{Fb{CfHb}}}{Eff}}{{{d{c}}{j{H`}}}{{Fb{CfHb}}}{A`f}}{{{d{c}}If}{{Fb{CfHb}}}{A`f}}{{{d{c}}H`{j{H`}}}{{Fb{CfHb}}}{Eff}}{{{d{c}}{Ab{AAj}}}{{Fb{CfHb}}}{A`f}}{{{d{c}}DjAAl}l{Dlf}}{{{d{c}}AAnAB`}l{ABbf}}{{cAAnHn}l{}}{{{d{c}}DjDj}l{Dlf}}{{{d{c}}Gd}ABd{Dlf}}{{{j{ce}}Cf}c{}h}{{{j{ce}}Cf}l{}h}0{{{j{ce}}Cfc}lDfh}{{{j{ce}}Cfg}l{}h{{C`{}{{El{c}}}}}}{{{Oj{{d{eg}}}}c}ABf{}{{ABh{c}}AAbf}h}{{{j{ce}}g}l{}h{{C`{c}{{El{En}}}}}}0``{{{d{c}}ABj}{{Fb{HdHb}}}{ABlf}}{{{d{c}}e}Fb{ABnf}AC`}{{Dhc}FbAC`}{{{j{c}}e}FbABnAC`}8{{{d{c}}ACb}{{Fb{lOb}}}{Edf}}9{{{j{ce}}}l{}h}{{{d{ce}}}{{Fj{Cf{Cj{Cf}}}}}{Abf}h}{{{d{c}}}{{Fj{Cf{Cj{Cf}}}}}{AAdAAbf}}{{{d{c}}}{{Cj{Ch}}}Ch}{{{j{ce}}}{{Ab{{Bd{c}}}}}{}h}`{{{j{ce}}gi}{{ACd{e}}}{}h{{Gb{Cf}}}{{Gj{}{{Gh{c}}}}}}{{{j{ce}}}{{Fj{{Ab{c}}{Ab{{Bd{c}}}}}}}{}h}{{{j{ce}}Cf}{{j{ce}}}{}{Dfh}}{{{d{c}}}{{Fb{HdHb}}}{ABlf}}{{cCn}{{Cj{Cn}}}{}}0{{{j{ce}}Cf}c{}h}`{ce{}{}}00{cIf{}}{c{{Cj{e}}}{}{}}00222{{{d{c}}}Hd{Edf}}{{{j{ce}}Cf}l{}h}{{{d{c}}Dj}En{Dlf}}{{{d{{Ab{c}}}}}{{Fb{{Ab{e}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}{{{j{c}}}{{Fb{{Ab{e}}}}}{}{{Af{}{{Ad{{A`{c}}}}}}}}10{c{{Fb{e}}}{}{}}00{{{d{c}}{Ab{H`}}}{{Fb{lACf}}}{B`f}}1{{{d{{Ab{c}}}}}{{Fb{{d{{A`{c}}}}}}}{}}{{{j{c}}}{{Fb{{d{{A`{c}}}}}}}{}}33{{{d{{Ab{c}}}}}{{Fb{{d{{Ab{e}}}}}}}{}Af}{{{j{c}}}{{Fb{{j{e}}}}}{}Af}555{c{{Fb{e{ACh{e}}}}}{}{}}00{c{{Fb{{d{c}}ACj}}}{}}{{ce}{{Fb{{d{ce}}ACj}}}{}{hh}}{{}{{Fb{{d{{Bd{c}}}}ACj}}}{}}{c{{Fb{{d{{Bd{e}}c}}ACj}}}{hh}{}}{Cf{{Fb{{d{{Ab{{Bd{c}}}}}}ACj}}}{}}210{{{j{ce}}Cf}{{Fb{lACl}}}{}h}0{Cf{{Fb{{j{c}}ACl}}}{}}{{Cfc}{{Fb{{j{ec}}ACl}}}h{}}{cFn{}}00{{{d{Ab}}}{{Ab{c}}}Bb}{j{{Ab{c}}}Bb}10{ce{}{}}00000{{{d{Db}}ACnce}l{{Bn{}{{El{AD`}}}}}{{C`{BnADb}}}}{{{j{c}}ACneg}lADd{{Bn{}{{El{AD`}}}}}{{C`{BnADb}}}}222``{Cf{{j{c}}}{}}{{Cfc}{{j{ec}}}h{}}{{{d{{Bd{c}}e}}c}{{d{ce}}}{}h}{{{d{c}}{Ab{H`}}}{{Fb{CfHb}}}{Hhf}}{{{d{ce}}{Ab{H`}}}l{Hff}h}{{{j{H`c}}{Ab{H`}}}{{Fb{CfHb}}}h}{{{d{c}}{Ab{H`}}}{{Fb{lHb}}}{Hhf}}{{{j{H`c}}{Ab{H`}}}{{Fb{lHb}}}h}{{{d{c}}ADf}{{Fb{lHb}}}{Hhf}}{{{d{ce}}ADh}l{Hff}h}{{{d{ce}}ADj}l{Hff}h}{{{d{ce}}Al}l{Hff}h}{{{d{ce}}ADl}l{Hff}h}{{{d{ce}}ADn}l{Hff}h}{{{d{ce}}AE`}l{Hff}h}{{{d{ce}}Cf}l{Hff}h}{{{d{ce}}Cn}l{Hff}h}{{{d{ce}}AEb}l{Hff}h}{{{d{ce}}AEd}l{Hff}h}{{{d{ce}}Fh}l{Hff}h}{{{d{ce}}Hd}l{Hff}h}{{{d{ce}}H`}l{Hff}h}6{{{d{c}}{Ab{AEf}}}{{Fb{CfHb}}}{Hhf}}{{{j{H`c}}{Ab{AEf}}}{{Fb{CfHb}}}h}`{{cg}i{}{}{{b{e}}}{}}{{AEhAEj}c{}}{ce{}{}}0{{AEhCf}AEl}11{{c{Cd{e}}}g{}{}{}}{AEhAEh}{{ce}l{}{}}4{c{{Fb{AEh}}}Fd}{{AEhAEh}En}{{AEhHj}Ib}{cc{}}088{{AEhAEnAF`AF`}En}00997999{cEn{}}{{AElAElAEl}AEh}{{AFbAFb}AEh}{AEhl}{{AEhc}FbAC`}{{AEhAEh}AEh}?{c{{Cj{e}}}{}{}}{ce{}{}}{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}333````{{cg}i{}{}{{b{e}}}{}}4444{{AFdAF`AF`}{{Cj{AEh}}}}`{{c{Cd{e}}}g{}{}{}}{AFdAFd}{{ce}l{}{}}8{AFdCf}{{AFdHj}Ib}{cc{}}0{{{j{Nf}}AF`AF`}AFd}<<{{AFdAEnAF`AF`AFf}{{Cj{AFh}}}}==7==={cEn{}}`{{AFdAFbAFjAFlAF`AFf}AF`}{{AFdAFbAFf}AFb}`{ce{}{}}{c{{Cj{e}}}{}{}}1{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}444``{{cg}i{}{}{{b{e}}}{}}0`55555555{{c{Cd{e}}}g{}{}{}}0{AFnAFn}{AG`AG`}{{ce}l{}{}}099{c{{Fb{AFn}}}Fd}{c{{Fb{AG`}}}Fd}{{AFnHj}Ib}{{AG`Hj}Ib}`{cc{}}000>>>>{{AFnAGbAGbAF`AFl}AEn}`????99??????{cEn{}}0````{{AFbAFbAEjAF`AF`AF`AF`AF`AF`}AFn}`{{AFnc}FbAC`}{{AG`c}FbAC`}``{ce{}{}}0{c{{Cj{e}}}{}{}}011{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0`4444````44```````````{{cg}i{}{}{{b{e}}}{}}055555555{{c{Cd{e}}}g{}{}{}}0{AGdAGd}{AGfAGf}{{ce}l{}{}}099{c{{Fb{AGd}}}Fd}{c{{Fb{AGf}}}Fd}{{AGdHj}Ib}{{AGfHj}Ib}{cc{}}000>>>>>>>>88>>>>>>{cEn{}}0{{AGdc}FbAC`}{{AGfc}FbAC`}{ce{}{}}0{c{{Cj{e}}}{}{}}011{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0444444`````````````````{{cg}i{}{}{{b{e}}}{}}00555555555555{{AGhAF`AF`}{{Cj{AEh}}}}{{NfAF`AF`}{{Cj{AEh}}}}{{AGjAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}00{NfNf}{AGjAGj}{{ce}l{}{}}0<<<`{{AFhHj}Ib}{{NfHj}Ib}{{AGjHj}Ib}{cc{}}{AGlNf}1{AGnNf}{AH`Nf}{AHbNf}{AHdNf}{AHfNf}{AGjNf}{AFdNf}{AHhNf}{AHjNf}{AHlNf}{AHnNf}{AI`Nf}===={ce{}{}}00000`{{AEnAFj}{{Fj{EnAFj}}}}{{AGhAEnAF`AF`AFf}{{Cj{AFh}}}}{{NfAEnAF`AF`AFf}{{Cj{AFh}}}}{{AGjAEnAF`AF`AFf}{{Cj{AFh}}}}444444{{c{Cd{e}}}g{}{}{}}00555555555{cEn{}}00``{{AGhAFbAFjAFlAF`AFf}AF`}{{NfAFbAFjAFlAF`AFf}AF`}{{AGjAFbAFjAFlAF`AFf}AF`}`{{AGhAFbAFf}AFb}{{NfAFbAFf}AFb}{{AGjAFbAFf}AFb}{{AFhAEnAFj}l}=={c{{Cj{e}}}{}{}}00>>>{c{{Fb{e}}}{}{}}0000000{Nf{{Fb{AHfc}}}{}}{Nf{{Fb{AI`c}}}{}}{Nf{{Fb{AHnc}}}{}}{Nf{{Fb{AFdc}}}{}}{Nf{{Fb{AHdc}}}{}}{Nf{{Fb{AGjc}}}{}}{Nf{{Fb{AH`c}}}{}}{Nf{{Fb{AHlc}}}{}}{Nf{{Fb{AGlc}}}{}}{Nf{{Fb{AHhc}}}{}}{Nf{{Fb{AHjc}}}{}}{Nf{{Fb{AHbc}}}{}}{Nf{{Fb{AGnc}}}{}}={c{{Fb{e{ACh{e}}}}}{}{}}00{cFn{}}00`{ce{}{}}00000`000`{{cg}i{}{}{{b{e}}}{}}{{AElAF`}c{}}2222{{c{Cd{e}}}g{}{}{}}{AElAEl}{{ce}l{}{}}5{c{{Fb{AEl}}}Fd}{{AElAEl}En}{{AElAF`}AEl}{{AElHj}Ib}{cc{}}0::::7:::{cEn{}}``{{AF`AF`}AEl}{{AElAEl}AEl}{{AElc}FbAC`}{AElAF`}?{c{{Cj{e}}}{}{}}{ce{}{}}{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}333`````````````````{{cg}i{}{}{{b{e}}}{}}00004444444444`4444444444{{c{Cd{e}}}g{}{}{}}0000{AIbAIb}{AIdAId}{AIfAIf}{AIhAIh}{AIjAIj}{{ce}l{}{}}0000;;;;;`{{}AIb}{{}AId}{{}AIf}{c{{Fb{AIb}}}Fd}{c{{Fb{AId}}}Fd}{c{{Fb{AIf}}}Fd}```{{AIlAEnAFhAF`AF`AFb}{{Ll{AIn}}}}{{AIfAEnAFhAF`AF`AFb}{{Ll{AIn}}}}{{AIbHj}Ib}{{AIdHj}Ib}{{AIfHj}Ib}{{AIhHj}Ib}{{AIjHj}Ib}{cc{}}00{AJ`AIf}{AJbAIf}{AJdAIf}{AJfAIf}{AJhAIf}{AJjAIf}{AJlAIf}7777777{ce{}{}}000000000`0000000000{{c{Cd{e}}}g{}{}{}}0000111111111111111{cEn{}}0000```````{{AIlAEnAFhAFf}{{Cj{AIj}}}}{{AIfAEnAFhAFf}{{Cj{AIj}}}}{{AIlAFhAEnAFf}{{Cj{AF`}}}}{{AIfAFhAEnAFf}{{Cj{AF`}}}}{{AIbc}FbAC`}{{AIdc}FbAC`}{{AIfc}FbAC`}`99999{c{{Cj{e}}}{}{}}0000:::::{c{{Fb{e}}}{}{}}000000000000{AIf{{Fb{AJjc}}}{}}{AIf{{Fb{AJhc}}}{}}{AIf{{Fb{AJbc}}}{}}{AIf{{Fb{AJdc}}}{}}{AIf{{Fb{AJfc}}}{}}{AIf{{Fb{AJlc}}}{}}{AIf{{Fb{AJ`c}}}{}}77{c{{Fb{e{ACh{e}}}}}{}{}}0000{cFn{}}0000{ce{}{}}00000000000000`{{cg}i{}{}{{b{e}}}{}}1111{{c{Cd{e}}}g{}{}{}}{AJ`AJ`}{{ce}l{}{}}4{{}AJ`}{c{{Fb{AJ`}}}Fd}{{AJ`AEnAFhAF`AF`AFb}{{Ll{AIn}}}}{{AJ`Hj}Ib}{cc{}}099997999{cEn{}}{{AF`c}AJ`{{AK`{AJn}}}}{{AJ`AEnAFhAFf}{{Cj{AIj}}}}{{AJ`AFhAEnAFf}{{Cj{AF`}}}}{{AJ`c}FbAC`}>{c{{Cj{e}}}{}{}}?{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}{ce{}{}}00`{{cg}i{}{}{{b{e}}}{}}1111{{c{Cd{e}}}g{}{}{}}{AJfAJf}{{ce}l{}{}}`4{{}AJf}{c{{Fb{AJf}}}Fd}{{AJfHj}Ib}{cc{}}088886888{cEn{}}{{AF`c}AJf{{AK`{{Ll{AIn}}}}}}`{{AJfAEnAFhAFf}{{Cj{AIj}}}}{{AJfAFhAEnAFf}{{Cj{AF`}}}}{{AJfc}FbAC`}={c{{Cj{e}}}{}{}}>{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}{ce{}{}}00`{{cg}i{}{}{{b{e}}}{}}1111{{c{Cd{e}}}g{}{}{}}{AJbAJb}{{ce}l{}{}}4{{}AJb}{c{{Fb{AJb}}}Fd}{{AJbAEnAFhAF`AF`AFb}{{Ll{AIn}}}}{{AJbHj}Ib}{cc{}}099997999{cEn{}}{cAJb{{AK`{AJn}}}}{{AJbAEnAFhAFf}{{Cj{AIj}}}}{{AJbAFhAEnAFf}{{Cj{AF`}}}}{{AJbc}FbAC`}>{c{{Cj{e}}}{}{}}?{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}{ce{}{}}00`{{cg}i{}{}{{b{e}}}{}}1111{{c{Cd{e}}}g{}{}{}}``{AJlAJl}{{ce}l{}{}}4{{}AJl}{c{{Fb{AJl}}}Fd}{{AJlHj}Ib}{cc{}}088886888{cEn{}}{{AF`AF`}AJl}{{AJlAFl}AF`}{{AJlAEnAFhAFf}{{Cj{AIj}}}}{{AJlAFhAEnAFf}{{Cj{AF`}}}}{{AJlc}FbAC`}>{c{{Cj{e}}}{}{}}?{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}{ce{}{}}00`{{cg}i{}{}{{b{e}}}{}}1111{{c{Cd{e}}}g{}{}{}}{AKbAKb}{{ce}l{}{}}4{{}AKb}{{AKbHj}Ib}{cc{}}077775777{cEn{}}{{AKd{A`{{A`{AF`}}}}{Cj{{A`{{A`{AF`}}}}}}{Cj{{A`{{A`{AF`}}}}}}{Ab{AKf}}}AKb}{{AKbAEnAFhAFf}{{Cj{AIj}}}}{{AKbAFhAEnAFf}{{Cj{AF`}}}};?;>>>=<;;;`:;;;;9{AJjAJj}8<{{}AJj}{c{{Fb{AJj}}}Fd}{{AJjHj}Ib}88????=???7{cAJj{{AK`{AJn}}}}{{AJjAEnAFhAFf}{{Cj{AIj}}}}{{AJjAFhAEnAFf}{{Cj{AF`}}}}{{AJjc}FbAC`}{ce{}{}}{c{{Cj{e}}}{}{}}1{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}444`{{cg}i{}{}{{b{e}}}{}}5555{{c{Cd{e}}}g{}{}{}}{AJdAJd}{{ce}l{}{}}8{{}AJd}{c{{Fb{AJd}}}Fd}{{AJdHj}Ib}{cc{}}0<<<<6<<<{cEn{}}{cAJd{{AK`{AJn}}}}{{AJdAEnAFhAFf}{{Cj{AIj}}}}{{AJdAFhAEnAFf}{{Cj{AF`}}}}{{AJdc}FbAC`}{ce{}{}}{c{{Cj{e}}}{}{}}1{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}444`{{cg}i{}{}{{b{e}}}{}}5555{{c{Cd{e}}}g{}{}{}}{AJhAJh}{{ce}l{}{}}8{c{{Fb{AJh}}}Fd}{{AJhHj}Ib}{cc{}}0;;;;5;;;{cEn{}}{{cAF`}AJh{{AK`{AJn}}}}{{AJhAEnAFhAFf}{{Cj{AIj}}}}{{AJhAFhAEnAFf}{{Cj{AF`}}}}{{AJhc}FbAC`}{ce{}{}}{c{{Cj{e}}}{}{}}1{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}444`````````````{{cg}i{}{}{{b{e}}}{}}055555555`{{c{Cd{e}}}g{}{}{}}0{AKhAKh}{AKjAKj}{{ce}l{}{}}099`{c{{Fb{AKh}}}Fd}{c{{Fb{AKj}}}Fd}{{AKhHj}Ib}{{AKjHj}Ib}{cc{}}000>>>>`>>>>88>>>>>>{cEn{}}0`{{AKj{Ab{AId}}}Nf}````{{AKhc}FbAC`}{{AKjc}FbAC`}``{ce{}{}}0{c{{Cj{e}}}{}{}}011``{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0444444```{{cg}i{}{}{{b{e}}}{}}055555555{{AHnAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{AKlAKl}{AHnAHn}{{ce}l{}{}}0::``{c{{Fb{AKl}}}Fd}{{AKlHj}Ib}{{AHnHj}Ib}{cc{}}000>>>>{{AHnAEnAF`AF`AFf}{{Cj{AFh}}}}????88??????{cEn{}}0``{{AFbAFbAIf}AHn}{{AHnAFbAFjAFlAF`AFf}AF`}`{{AHnAFbAFf}AEj}{{AKlc}FbAC`}{ce{}{}}0{c{{Cj{e}}}{}{}}011{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0444444``{{cg}i{}{}{{b{e}}}{}}055555555`{{AHbAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{AKnAKn}{AHbAHb}{{ce}l{}{}}0::`{c{{Fb{AKn}}}Fd}{{AKnHj}Ib}{{AHbHj}Ib}{cc{}}000>>>>{{AHbAEnAF`AF`AFf}{{Cj{AFh}}}}????88??????{cEn{}}0{{{d{Nf}}AF`AJn}AHb}{{AHbAFbAFjAFlAF`AFf}AF`}`{{AHbAFbAFf}AFb}{{AKnc}FbAC`}`{ce{}{}}0{c{{Cj{e}}}{}{}}011{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0444444`````{{cg}i{}{}{{b{e}}}{}}00555555555555{{AGlAL`AL`}{{Cj{AEh}}}}{{AHlAF`AF`}{{Cj{AEh}}}}`{{c{Cd{e}}}g{}{}{}}00{NdNd}{AGlAGl}{AHlAHl}{{ce}l{}{}}00<<<{c{{Fb{Nd}}}Fd}{{NdHj}Ib}{{AGlHj}Ib}{{AHlHj}Ib}{cc{}}00000{ce{}{}}00000{{AGlAEnAL`AL`AFf}{{Cj{AFh}}}}{{AHlAEnAF`AF`AFf}{{Cj{AFh}}}}222222<<<222222222{cEn{}}00`{{NdAF`AF`}AGl}{{{A`{AEj}}AKb}AHl}`{{AGlAFbAFjAFlAF`AFf}AF`}{{AHlAFbAFjAFlAF`AFf}AF`}`{{AGlAFbAFf}AFb}{{AHlAFbAFf}AEj}{{Ndc}FbAC`}:::{c{{Cj{e}}}{}{}}00;;;{c{{Fb{e}}}{}{}}00000000{c{{Fb{e{ACh{e}}}}}{}{}}00{cFn{}}00>>>>>>>>>```{{cg}i{}{}{{b{e}}}{}}0????????{{AI`AF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{{AI`AF`}AFb}````{ALbALb}{AI`AI`}{{ce}l{}{}}0{ce{}{}}0{c{{Fb{ALb}}}Fd}{{ALbHj}Ib}{{AI`Hj}Ib}{cc{}}0004444{{AI`AFbAF`}{{Fj{AF`AF`}}}}{{AI`AEnAF`AF`AFf}{{Cj{AFh}}}}6666;;666666{cEn{}}0``{{AFbAFbAF`AF`AF`AIf}AI`}{{AI`AFbAFjAFlAF`AFf}AF`}```{{AI`AFbAFf}AFb}{{ALbc}FbAC`}``;;{c{{Cj{e}}}{}{}}0<<{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0??????```{{cg}i{}{}{{b{e}}}{}}0`{ce{}{}}0000000{{AHjAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{ALdALd}{AHjAHj}{{ce}l{}{}}055`{c{{Fb{ALd}}}Fd}{{ALdHj}Ib}{{AHjHj}Ib}{cc{}}0009999{{AHjAEnAF`AF`AFf}{{Cj{AFh}}}}::::88::::::{cEn{}}0``{{AFbAEjAEjAIf}AHj}`{{AHjAFbAFjAFlAF`AFf}AF`}```{{AHjAFbAFf}AFb}{{ALdc}FbAC`}??{c{{Cj{e}}}{}{}}0{ce{}{}}0{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0``3333``33```{{cg}i{}{}{{b{e}}}{}}0`44444444{{AHdAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{ALfALf}{AHdAHd}{{ce}l{}{}}099{c{{Fb{ALf}}}Fd}{{ALfHj}Ib}{{AHdHj}Ib}{cc{}}000===={{AHdAEnAF`AF`AFf}{{Cj{AFh}}}}>>>>88>>>>>>{cEn{}}0{{{d{Nf}}AF`}AHd}`{{AHdAFbAFjAFlAF`AFf}AF`}`{{AHdAFbAFf}AEj}{{ALfc}FbAC`}{ce{}{}}0{c{{Cj{e}}}{}{}}011{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0444444``{{cg}i{}{}{{b{e}}}{}}055555555{{AH`AF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0`{ALhALh}{AH`AH`}{{ce}l{}{}}0::{c{{Fb{ALh}}}Fd}{{ALhHj}Ib}{{AH`Hj}Ib}{cc{}}000>>>>{{AH`AFbAF`}{{Fj{AF`AF`}}}}{{AH`AEnAF`AF`AFf}{{Cj{AFh}}}}{ce{}{}}000::000000{cEn{}}0`{{AFbAF`AIf}AH`}{{AH`AFbAFjAFlAF`AFf}AF`}``{{AH`AFbAFf}AFb}{{ALhc}FbAC`}55{c{{Cj{e}}}{}{}}066{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0999999```{{cg}i{}{}{{b{e}}}{}}0::::::::{{AHfAL`AL`}{{Cj{AEh}}}}`{{c{Cd{e}}}g{}{}{}}0`{AHfAHf}{ALjALj}{{ce}l{}{}}0??{c{{Fb{ALj}}}Fd}{{AHfHj}Ib}{{ALjHj}Ib}{cc{}}000{ce{}{}}000{{AHfAEnAL`AL`AFf}{{Cj{AFh}}}}{{ALj{Ab{AId}}}AHf}2222::222222{cEn{}}0```{{AHfAFbAFjAFlAF`AFf}AF`}`{{AHfAFbAFf}AEj}``{{ALjc}FbAC`}66{c{{Cj{e}}}{}{}}077{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0::::::``{{cg}i{}{}{{b{e}}}{}}0;;;;;;;;{{AGnAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{ALlALl}{AGnAGn}{{ce}l{}{}}0{ce{}{}}0{c{{Fb{ALl}}}Fd}{{ALlHj}Ib}{{AGnHj}Ib}{cc{}}0004444{{AGnAEnAF`AF`AFf}{{Cj{AFh}}}}555599555555{cEn{}}0{{{d{Nf}}AEj}AGn}``{{AGnAFbAFjAFlAF`AFf}AF`}`{{AGnAFbAFf}AEj}{{ALlc}FbAC`}::{c{{Cj{e}}}{}{}}0;;{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0>>>>>>```{{cg}i{}{}{{b{e}}}{}}0`????????{{AHhAF`AF`}{{Cj{AEh}}}}{{c{Cd{e}}}g{}{}{}}0{ALnALn}{AHhAHh}{{ce}l{}{}}0{ce{}{}}0`{c{{Fb{ALn}}}Fd}{{ALnHj}Ib}{{AHhHj}Ib}{cc{}}000{{AEjAEjAEjAIf}AHh}5555{{AHhAEnAF`AF`AFf}{{Cj{AFh}}}}6666::666666{cEn{}}0``{{AFbAEjAEjAIf}AHh}`{{AHhAFbAFjAFlAF`AFf}AF`}```{{AHhAFbAFf}AFb}{{ALnc}FbAC`};;{c{{Cj{e}}}{}{}}0<<{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0``????``??``{{cg}i{}{}{{b{e}}}{}}{ce{}{}}000{AFjAM`}{{c{Cd{e}}}g{}{}{}}{AM`AM`}{{ce}l{}{}}4{{AM`Hj}Ib}{cc{}}066664666{cEn{}}{{AM`AFj}AFj}8=8<<<;:`88`8`````````````999999888888888888888888888888666666{AMbAMb}{AMdAMd}{AMfAMf}{AMhAMh}{AMjAMj}{AMlAMl}::::::>>>>>>{{}AMj}{{}AMl}{{AMbHj}Ib}{{AMdHj}Ib}{{AMfHj}Ib}{{AMhHj}Ib}{{AMjHj}Ib}{{AMlHj}Ib}{cc{}}{AMjAMb}{AMlAMb}{AMdAMb}{AMhAMb}{AMfAMb}55555555555{ce{}{}}00000000000{{AMnAFf}AFb}{{AMbAFf}AFb}{{AMdAFf}AFb}{{AMfAFf}AFb}{{AMhAFf}AFb}{{AMjAFf}AFb}{{AMlAFf}AFb}777777777777{{c{Cd{e}}}g{}{}{}}00000888888888888888888{cEn{}}00000{AFjAMd}{{NfAFb}AMf}{{AMbAMb}AMh}{{}AMj}{{}AMl}>>>>>>{c{{Cj{e}}}{}{}}00000??????{c{{Fb{e}}}{}{}}00000000000{AMb{{Fb{AMfc}}}{}}{AMb{{Fb{AMlc}}}{}}2{AMb{{Fb{AMdc}}}{}}{AMb{{Fb{AMhc}}}{}}{AMb{{Fb{AMjc}}}{}}55555{c{{Fb{e{ACh{e}}}}}{}{}}00000{cFn{}}00000{ce{}{}}00000000000{{AMnAFjAFlAF`AFf}AF`}{{AMbAFjAFlAF`AFf}AF`}{{AMdAFjAFlAF`AFf}AF`}{{AMfAFjAFlAF`AFf}AF`}{{AMhAFjAFlAF`AFf}AF`}{{AMjAFjAFlAF`AFf}AF`}{{AMlAFjAFlAF`AFf}AF`}777777{AFfAFj}{AFfAGb}{{AEjAFf}AFj}2`{{cg}i{}{}{{b{e}}}{}};;;;{{c{Cd{e}}}g{}{}{}}{AEnAEn}{{ce}l{}{}}>`{{AEnAEn}En}{{AEnAF`}AFb}{{AEnHj}Ib}{cc{}}0{ce{}{}}0007000{cEn{}}``1{c{{Cj{e}}}{}{}}2{c{{Fb{e}}}{}{}}00{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}555```==5555`5555<<`{AN`AN`};66{c{{Fb{AN`}}}Fd}{{ANbHj}Ib}{{AN`Hj}Ib}::::9999`{{AN`FhFh}ANb}::::{{c{Cd{e}}}g{}{}{}}0;;;;;;::{{AF`AF`AFn{j{Nf}}{j{Nf}}ANd}ANb}`{{AN`c}FbAC`}=;;==::::::9988======``{{AFl{Ll{AIn}}}AF`}{{cg}i{}{}{{b{e}}}{}}0????????44`??`{{ANfHj}Ib}{{ANhHj}Ib}{cc{}}000{ce{}{}}000``000088000000{cEn{}}0``{{{A`{ANj}}{A`{ANj}}{A`{ANj}}}l}`````````````{{{A`{ANj}}{A`{ANj}}}l}000{c{{Cj{e}}}{}{}}044{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}07777`77`4````;7777?{AJnAJn}{{ce}l{}{}}9{{}AJn}{c{{Fb{AJn}}}Fd}{{AJnHj}Ib}{ANlAJn}>{ANnAJn}{AO`AJn}{cc{}}{ce{}{}}000{{c{Cd{e}}}g{}{}{}}111{cEn{}}{{AJnc}FbAC`}```3{c{{Cj{e}}}{}{}}4{c{{Fb{e}}}{}{}}0{AJn{{Fb{ANlc}}}{}}{AJn{{Fb{ANnc}}}{}}2{AJn{{Fb{AO`c}}}{}}{c{{Fb{e{ACh{e}}}}}{}{}}{cFn{}}:::``{{cg}i{}{}{{b{e}}}{}}0;;;;;;;;::{AObAOb}{AO`AO`}{{ce}l{}{}}0``>>{{}AO`}{c{{Fb{AOb}}}Fd}{c{{Fb{AO`}}}Fd}{{AObHj}Ib}{{AO`Hj}Ib}{cc{}}0{AObAO`}11{ce{}{}}0000000{{c{Cd{e}}}g{}{}{}}0111111{cEn{}}0{cAO`{{AK`{{Ll{AIn}}}}}}{{AObc}FbAC`}{{AO`c}FbAC`}55{c{{Cj{e}}}{}{}}066{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0999999``{{cg}i{}{}{{b{e}}}{}}0::::::::99{AOdAOd}{ANlANl}{{ce}l{}{}}0==``{c{{Fb{AOd}}}Fd}{c{{Fb{ANl}}}Fd}``{{AOdHj}Ib}{{ANlHj}Ib}{cc{}}0{AOdANl}11{ce{}{}}0000000{{c{Cd{e}}}g{}{}{}}0111111{cEn{}}0{{ceAF`}ANl{{AK`{{Ll{AIn}}}}}{{AK`{{Ll{AIn}}}}}}``{{AOdc}FbAC`}{{ANlc}FbAC`}55{c{{Cj{e}}}{}{}}066{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0999999``{{cg}i{}{}{{b{e}}}{}}0::::::::99{AOfAOf}{ANnANn}{{ce}l{}{}}0==`{c{{Fb{AOf}}}Fd}{c{{Fb{ANn}}}Fd}`{{AOfHj}Ib}{{ANnHj}Ib}{cc{}}0{AOfANn}11{ce{}{}}0000000{{c{Cd{e}}}g{}{}{}}0111111{cEn{}}0{{ceAF`}ANn{{AK`{{Ll{AIn}}}}}{{AK`{{Ll{AIn}}}}}}`{{AOfc}FbAC`}{{ANnc}FbAC`}55{c{{Cj{e}}}{}{}}066{c{{Fb{e}}}{}{}}00000{c{{Fb{e{ACh{e}}}}}{}{}}0{cFn{}}0999999```````{AFfAFl}{AFl{{A`{AFl}}}}{AF`AFl}{AFl{{Ll{AIn}}}}","c":[594,595],"p":[[10,"TransformMatrix",3269],[5,"Box",0],[10,"Sized",3270],[10,"Allocator",3271],[5,"Vec",0],[1,"unit"],[5,"PreAlpha",3272],[1,"array"],[1,"slice"],[17,"Array"],[10,"ArrayCast",3273],[5,"BorrowedFd",3274],[10,"AsFd",3274],[1,"i32"],[10,"AsRawFd",3275],[10,"RngCore",3276],[10,"UintCast",3277],[20,"MaybeUninit",3278],[10,"Tuple",3270],[10,"AsyncFn",3279],[10,"AsyncFnMut",3279],[10,"AsyncFnOnce",3279],[10,"Fn",3280],[10,"FnMut",3280],[10,"FnOnce",3280],[5,"BakedParameters",3281],[1,"usize"],[10,"Error",3282],[6,"Option",3283],[5,"OsStr",3284],[1,"str"],[5,"CStr",3285],[5,"RawValue",3286],[5,"Path",3287],[10,"Clone",3288],[5,"RenderOpts",0],[5,"Id",3289],[10,"Subscriber",3290],[6,"Ordering",3291],[10,"Ord",3291],[6,"ColorType",3292],[10,"ImageDecoder",3293],[10,"BufRead",3294],[5,"Current",3289],[10,"PartialEq",3291],[17,"Output"],[1,"bool"],[10,"Default",3295],[6,"Result",3296],[10,"Deserializer",3297],[10,"Deserialize",3297],[1,"u32"],[1,"tuple"],[10,"Any",3298],[5,"TypeId",3298],[5,"Drain",3299],[10,"RangeBounds",3300],[5,"Metadata",3301],[5,"Event",3302],[17,"Item"],[10,"IntoIterator",3303],[10,"Copy",3270],[5,"ExtractIf",3304],[1,"u8"],[5,"Error",3305],[1,"u64"],[10,"Hasher",3306],[10,"Write",3294],[5,"Formatter",3307],[5,"Error",3307],[10,"Debug",3307],[10,"Display",3307],[8,"Result",3307],[5,"Okhsv",3308],[5,"String",3309],[5,"Cam16Jmh",3310],[5,"Cam16Jsh",3311],[5,"Luma",3312],[5,"Rgb",3313],[5,"Lchuv",3314],[5,"Hsl",3315],[5,"Hsluv",3316],[5,"Okhsl",3317],[5,"Cam16Qmh",3318],[5,"Lab",3319],[5,"Hwb",3320],[5,"Oklch",3321],[5,"Packed",3322],[5,"Cam16Qsh",3323],[5,"OsString",3284],[6,"Cow",3324],[10,"Premultiply",3325],[5,"PathBuf",3287],[5,"Alpha",3326],[5,"Hsv",3327],[5,"Oklab",3328],[5,"Cam16UcsJmh",3329],[10,"Send",3270],[10,"Sync",3270],[5,"Lch",3330],[5,"Cam16Jch",3331],[5,"Xyz",3332],[5,"Yxy",3333],[5,"Luv",3334],[5,"Cam16Qch",3335],[5,"CString",3336],[5,"Okhwb",3337],[1,"never"],[5,"Cam16UcsJab",3338],[5,"VecStorage",3339],[10,"Dim",3340],[5,"VecDeque",3341],[5,"BinaryHeap",3342],[5,"GLTFInit",1816],[6,"Hitable",830],[10,"FromColor",3343],[10,"FromColorUnclamped",3344],[10,"PartialOrd",3291],[5,"Range",3300],[10,"Hash",3306],[6,"ImageError",3345],[10,"SliceIndex",3346],[10,"Error",3297],[10,"IntoDeserializer",3297],[5,"Pin",3347],[5,"CharPredicateSearcher",3348],[10,"ExactSizeIterator",3349],[10,"Read",3294],[10,"Iterator",3350],[5,"LevelFilter",3301],[5,"Attributes",3289],[10,"DoubleEndedIterator",3351],[6,"ExtendedColorType",3292],[5,"Context",3352],[6,"Poll",3353],[10,"Future",3354],[10,"Unpin",3270],[10,"AsyncIterator",3355],[5,"Request",3282],[5,"BorrowedCursor",3356],[5,"IoSliceMut",3294],[5,"Record",3289],[5,"Field",3357],[10,"Visit",3357],[10,"Value",3357],[5,"Interest",3290],[6,"CoroutineState",3358],[10,"Coroutine",3358],[6,"SeekFrom",3294],[10,"Seek",3294],[10,"Serialize",3359],[10,"Serializer",3359],[5,"Limits",3360],[5,"Splice",3361],[5,"Error",3362],[5,"OutOfBounds",3363],[5,"AllocError",3271],[5,"TryReserveError",3364],[5,"Root",3365],[5,"Path",3366],[6,"Error",3367],[10,"Validate",3367],[5,"Arguments",3307],[1,"i128"],[1,"i16"],[1,"i64"],[1,"i8"],[1,"isize"],[1,"u128"],[1,"u16"],[5,"IoSlice",3294],[5,"AABB",574],[8,"Vec3",0],[5,"Interval",987],[5,"Ray",2784],[8,"Float",0],[8,"Position",0],[5,"BVHNode",622],[5,"SmallRng",3368],[5,"HitRecord",830],[8,"Direction",0],[8,"Wavelength",3258],[5,"Camera",664],[5,"CameraInit",664],[8,"Vec2",0],[6,"TypedColorInit",752],[6,"ColorInit",752],[10,"HitableTrait",830],[5,"Empty",830],[5,"GLTF",1816],[5,"Translate",2343],[5,"Sphere",2183],[5,"ConstantMedium",1739],[5,"RotateY",2107],[5,"STL",2261],[5,"Triangle",2419],[5,"Quad",2020],[5,"GLTFTriangle",1816],[5,"Boxy",1660],[5,"MovingSphere",1933],[6,"MaterialInit",1030],[5,"SharedMaterial",1030],[6,"Material",1030],[6,"MaterialType",1030],[5,"ScatterRecord",1030],[10,"MaterialTrait",1030],[5,"E",3369],[5,"ConeLight",1250],[5,"DiffuseLight",1331],[5,"Lambertian",1489],[5,"Dielectric",1290],[5,"Metal",1528],[5,"Isotropic",1450],[5,"Dispersive",1371],[6,"Texture",2982],[10,"Into",3370],[5,"GLTFMaterial",1413],[5,"Material",3371],[5,"Data",3372],[5,"ObjectList",1566],[6,"Object",1566],[5,"BoxyInit",1660],[5,"ConstantMediumInit",1739],[1,"f32"],[5,"MovingSphereInit",1933],[5,"QuadInit",2020],[5,"RotateInit",2107],[5,"SphereInit",2183],[5,"STLInit",2261],[5,"TranslateInit",2343],[5,"TriangleInit",2419],[5,"ONB",2507],[6,"PDF",2545],[5,"CosinePDF",2545],[5,"HitablePDF",2545],[5,"MixturePDF",2545],[5,"SpherePDF",2545],[5,"ZeroPDF",2545],[10,"PDFTrait",2545],[5,"SceneFile",2823],[5,"Scene",2823],[8,"Srgb",3373],[5,"spectrum_grid_cell_t",2897],[5,"spectrum_data_point_t",2897],[1,"f64"],[5,"SpatialChecker",3105],[5,"SurfaceChecker",3183],[5,"SolidColor",3030],[5,"SolidColorInit",3030],[5,"SpatialCheckerInit",3105],[5,"SurfaceCheckerInit",3183],[8,"Vec4",0]],"b":[[41,"impl-AsMut%3C%5BT%5D%3E-for-Vec%3CT,+A%3E"],[42,"impl-AsMut%3CVec%3CT,+A%3E%3E-for-Vec%3CT,+A%3E"],[48,"impl-AsRef%3C%5BT%5D%3E-for-Vec%3CT,+A%3E"],[49,"impl-AsRef%3CVec%3CT,+A%3E%3E-for-Vec%3CT,+A%3E"],[56,"impl-Box%3CMaybeUninit%3CT%3E,+A%3E"],[57,"impl-Box%3C%5BMaybeUninit%3CT%3E%5D,+A%3E"],[82,"impl-Clone-for-Box%3COsStr%3E"],[83,"impl-Clone-for-Box%3Cstr%3E"],[84,"impl-Clone-for-Box%3CCStr%3E"],[85,"impl-Clone-for-Box%3CRawValue%3E"],[86,"impl-Clone-for-Box%3CPath%3E"],[87,"impl-Clone-for-Box%3CT,+A%3E"],[88,"impl-Clone-for-Box%3C%5BT%5D,+A%3E"],[91,"impl-Clone-for-Box%3C%5BT%5D,+A%3E"],[92,"impl-Clone-for-Box%3CT,+A%3E"],[114,"impl-Default-for-Box%3CRawValue%3E"],[115,"impl-Default-for-Box%3COsStr%3E"],[116,"impl-Default-for-Box%3CT%3E"],[117,"impl-Default-for-Box%3CCStr%3E"],[118,"impl-Default-for-Box%3C%5BT%5D%3E"],[119,"impl-Default-for-Box%3Cstr%3E"],[126,"impl-Deserialize%3C\'de%3E-for-Box%3CRawValue%3E"],[127,"impl-Deserialize%3C\'de%3E-for-Box%3CT%3E"],[128,"impl-Deserialize%3C\'de%3E-for-Box%3C%5BT%5D%3E"],[129,"impl-Deserialize%3C\'de%3E-for-Box%3CPath%3E"],[130,"impl-Deserialize%3C\'de%3E-for-Box%3CCStr%3E"],[131,"impl-Deserialize%3C\'de%3E-for-Box%3COsStr%3E"],[132,"impl-Deserialize%3C\'de%3E-for-Box%3Cstr%3E"],[136,"impl-Box%3Cdyn+Any,+A%3E"],[137,"impl-Box%3Cdyn+Any+%2B+Send,+A%3E"],[138,"impl-Box%3Cdyn+Any+%2B+Send+%2B+Sync,+A%3E"],[140,"impl-Box%3Cdyn+Any+%2B+Send,+A%3E"],[141,"impl-Box%3Cdyn+Any+%2B+Send+%2B+Sync,+A%3E"],[142,"impl-Box%3Cdyn+Any,+A%3E"],[150,"impl-PartialEq%3C%5BU;+N%5D%3E-for-Vec%3CT,+A%3E"],[151,"impl-PartialEq%3C%26%5BU%5D%3E-for-Vec%3CT,+A%3E"],[152,"impl-PartialEq%3CVec%3CU,+A2%3E%3E-for-Vec%3CT,+A1%3E"],[153,"impl-PartialEq%3C%26mut+%5BU%5D%3E-for-Vec%3CT,+A%3E"],[154,"impl-PartialEq%3C%26%5BU;+N%5D%3E-for-Vec%3CT,+A%3E"],[155,"impl-PartialEq%3C%5BU%5D%3E-for-Vec%3CT,+A%3E"],[159,"impl-Extend%3CT%3E-for-Vec%3CT,+A%3E"],[160,"impl-Extend%3C%26T%3E-for-Vec%3CT,+A%3E"],[163,"impl-Extend%3CT%3E-for-Vec%3CT,+A%3E"],[164,"impl-Extend%3C%26T%3E-for-Vec%3CT,+A%3E"],[165,"impl-Extend%3C%26T%3E-for-Vec%3CT,+A%3E"],[166,"impl-Extend%3CT%3E-for-Vec%3CT,+A%3E"],[173,"impl-Debug-for-Box%3CT,+A%3E"],[174,"impl-Display-for-Box%3CT,+A%3E"],[175,"impl-Pointer-for-Box%3CT,+A%3E"],[178,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3COkhsv%3CT%3E%3E"],[179,"impl-From%3CString%3E-for-Box%3Cstr%3E"],[180,"impl-From%3CBox%3CCam16Jmh%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[181,"impl-From%3CBox%3CCam16Jsh%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[182,"impl-From%3CBox%3CRawValue%3E%3E-for-Box%3Cstr%3E"],[183,"impl-From%3CBox%3C%5BT;+1%5D%3E%3E-for-Box%3CLuma%3CS,+T%3E%3E"],[184,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CRgb%3CS,+T%3E%3E"],[185,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CLchuv%3CWp,+T%3E%3E"],[186,"impl-From%3CBox%3CHsl%3CS,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[187,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16Jmh%3CT%3E%3E"],[188,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CHsluv%3CWp,+T%3E%3E"],[189,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3COkhsl%3CT%3E%3E"],[190,"impl-From%3CBox%3CLchuv%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[191,"impl-From%3CBox%3CCam16Qmh%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[192,"impl-From%3CBox%3COkhsl%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[193,"impl-From%3CBox%3CLab%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[194,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CLab%3CWp,+T%3E%3E"],[195,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CHwb%3CS,+T%3E%3E"],[196,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3COklch%3CT%3E%3E"],[197,"impl-From%3CBox%3CPacked%3CO,+%5BT;+N%5D%3E%3E%3E-for-Box%3C%5BT;+N%5D%3E"],[198,"impl-From%3CBox%3CCam16Qsh%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[199,"impl-From%3C%26Path%3E-for-Box%3CPath%3E"],[200,"impl-From%3COsString%3E-for-Box%3COsStr%3E"],[201,"impl-From%3CCow%3C\'_,+Path%3E%3E-for-Box%3CPath%3E"],[202,"impl-From%3CBox%3CPreAlpha%3CC%3E%3E%3E-for-Box%3C%5B%3CC+as+Premultiply%3E::Scalar;+N%5D%3E"],[203,"impl-From%3CCow%3C\'b,+str%3E%3E-for-Box%3Cdyn+Error+%2B+Send+%2B+Sync%3E"],[204,"impl-From%3CCow%3C\'_,+OsStr%3E%3E-for-Box%3COsStr%3E"],[205,"impl-From%3CPathBuf%3E-for-Box%3CPath%3E"],[206,"impl-From%3C%26OsStr%3E-for-Box%3COsStr%3E"],[207,"impl-From%3CBox%3CHsluv%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[208,"impl-From%3CBox%3CAlpha%3CC,+T%3E%3E%3E-for-Box%3C%5BT;+N%5D%3E"],[209,"impl-From%3CBox%3CHsv%3CS,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[210,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CHsv%3CS,+T%3E%3E"],[211,"impl-From%3CBox%3Cstr,+A%3E%3E-for-Box%3C%5Bu8%5D,+A%3E"],[212,"impl-From%3C%26str%3E-for-Box%3Cstr%3E"],[213,"impl-From%3C%26str%3E-for-Box%3Cdyn+Error+%2B+Send+%2B+Sync%3E"],[214,"impl-From%3CBox%3COklab%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[215,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16UcsJmh%3CT%3E%3E"],[216,"impl-From%3CE%3E-for-Box%3Cdyn+Error+%2B+Send+%2B+Sync%3E"],[217,"impl-From%3CE%3E-for-Box%3Cdyn+Error%3E"],[218,"impl-From%3CBox%3COkhsv%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[219,"impl-From%3CBox%3CLch%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[220,"impl-From%3CBox%3CCam16Jch%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[221,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CXyz%3CWp,+T%3E%3E"],[222,"impl-From%3CCow%3C\'b,+str%3E%3E-for-Box%3Cdyn+Error%3E"],[223,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CHsl%3CS,+T%3E%3E"],[224,"impl-From%3CBox%3CYxy%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[225,"impl-From%3CVec%3CT,+A%3E%3E-for-Box%3C%5BT%5D,+A%3E"],[226,"impl-From%3CBox%3C%5B%3CC+as+Premultiply%3E::Scalar;+N%5D%3E%3E-for-Box%3CPreAlpha%3CC%3E%3E"],[227,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CLuv%3CWp,+T%3E%3E"],[228,"impl-From%3C%26%5BT%5D%3E-for-Box%3C%5BT%5D%3E"],[229,"impl-From%3CCow%3C\'_,+str%3E%3E-for-Box%3Cstr%3E"],[230,"impl-From%3CBox%3CXyz%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[231,"impl-From%3CBox%3CLuv%3CWp,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[232,"impl-From%3C%26CStr%3E-for-Box%3CCStr%3E"],[233,"impl-From%3CCow%3C\'_,+CStr%3E%3E-for-Box%3CCStr%3E"],[234,"impl-From%3C%26str%3E-for-Box%3Cdyn+Error%3E"],[235,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16Qch%3CT%3E%3E"],[236,"impl-From%3CString%3E-for-Box%3Cdyn+Error%3E"],[237,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16Qmh%3CT%3E%3E"],[238,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16Qsh%3CT%3E%3E"],[239,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3COklab%3CT%3E%3E"],[240,"impl-From%3CCow%3C\'_,+%5BT%5D%3E%3E-for-Box%3C%5BT%5D%3E"],[241,"impl-From%3CT%3E-for-Box%3CT%3E"],[242,"impl-From%3C%5BT;+N%5D%3E-for-Box%3C%5BT%5D%3E"],[243,"impl-From%3CCString%3E-for-Box%3CCStr%3E"],[244,"impl-From%3CString%3E-for-Box%3Cdyn+Error+%2B+Send+%2B+Sync%3E"],[245,"impl-From%3CBox%3CCam16Qch%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[246,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3COkhwb%3CT%3E%3E"],[247,"impl-From%3CBox%3C%5BT;+N%5D%3E%3E-for-Box%3CAlpha%3CC,+T%3E%3E"],[248,"impl-From%3CBox%3CLuma%3CS,+T%3E%3E%3E-for-Box%3C%5BT;+1%5D%3E"],[249,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CYxy%3CWp,+T%3E%3E"],[250,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CLch%3CWp,+T%3E%3E"],[251,"impl-From%3CBox%3CRgb%3CS,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[252,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16Jsh%3CT%3E%3E"],[253,"impl-From%3CBox%3C%5BT;+N%5D%3E%3E-for-Box%3CPacked%3CO,+%5BT;+N%5D%3E%3E"],[254,"impl-From%3CBox%3CHwb%3CS,+T%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[255,"impl-From%3CBox%3COkhwb%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[256,"impl-From%3CBox%3CCam16UcsJmh%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[258,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16UcsJab%3CT%3E%3E"],[260,"impl-From%3CBox%3CCam16UcsJab%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[261,"impl-From%3CBox%3COklch%3CT%3E%3E%3E-for-Box%3C%5BT;+3%5D%3E"],[262,"impl-From%3CBox%3C%5BT;+3%5D%3E%3E-for-Box%3CCam16Jch%3CT%3E%3E"],[264,"impl-From%3C%26str%3E-for-Vec%3Cu8%3E"],[266,"impl-From%3CVecStorage%3CT,+R,+C%3E%3E-for-Vec%3CT%3E"],[267,"impl-From%3C%26%5BT;+N%5D%3E-for-Vec%3CT%3E"],[268,"impl-From%3CCString%3E-for-Vec%3Cu8%3E"],[269,"impl-From%3C%5BT;+N%5D%3E-for-Vec%3CT%3E"],[270,"impl-From%3C%26mut+%5BT%5D%3E-for-Vec%3CT%3E"],[271,"impl-From%3C%26%5BT%5D%3E-for-Vec%3CT%3E"],[272,"impl-From%3CString%3E-for-Vec%3Cu8%3E"],[273,"impl-From%3CVecDeque%3CT,+A%3E%3E-for-Vec%3CT,+A%3E"],[274,"impl-From%3C%26mut+%5BT;+N%5D%3E-for-Vec%3CT%3E"],[275,"impl-From%3CCow%3C\'a,+%5BT%5D%3E%3E-for-Vec%3CT%3E"],[276,"impl-From%3CBox%3C%5BT%5D,+A%3E%3E-for-Vec%3CT,+A%3E"],[277,"impl-From%3CBinaryHeap%3CT,+A%3E%3E-for-Vec%3CT,+A%3E"],[278,"impl-From%3CGLTFInit%3E-for-Vec%3CHitable%3C\'scene%3E%3E"],[303,"impl-ZReaderTrait-for-%26Vec%3Cu8%3E"],[304,"impl-ZReaderTrait-for-Vec%3Cu8%3E"],[305,"impl-ZReaderTrait-for-Vec%3Cu8%3E"],[306,"impl-ZReaderTrait-for-%26Vec%3Cu8%3E"],[307,"impl-ZReaderTrait-for-%26Vec%3Cu8%3E"],[308,"impl-ZReaderTrait-for-Vec%3Cu8%3E"],[325,"impl-IntoArrays%3CBox%3C%5B%5BT;+N%5D%5D%3E%3E-for-Box%3C%5BC%5D%3E"],[326,"impl-IntoArrays%3C%26%5B%5BT;+N%5D%5D%3E-for-%26Box%3C%5BC%5D%3E"],[327,"impl-IntoArrays%3C%26mut+%5B%5BT;+N%5D%5D%3E-for-%26mut+Box%3C%5BC%5D%3E"],[328,"impl-IntoArrays%3CVec%3C%5BT;+N%5D%3E%3E-for-Vec%3CC%3E"],[329,"impl-IntoArrays%3C%26mut+%5B%5BT;+N%5D%5D%3E-for-%26mut+Vec%3CC%3E"],[330,"impl-IntoArrays%3C%26%5B%5BT;+N%5D%5D%3E-for-%26Vec%3CC%3E"],[343,"impl-IntoComponents%3CBox%3C%5BT%5D%3E%3E-for-Box%3C%5BC%5D%3E"],[344,"impl-IntoComponents%3C%26mut+%5BT%5D%3E-for-%26mut+Box%3C%5BC%5D%3E"],[345,"impl-IntoComponents%3C%26%5BT%5D%3E-for-%26Box%3C%5BC%5D%3E"],[346,"impl-IntoComponents%3C%26%5BT%5D%3E-for-%26Vec%3CC%3E"],[347,"impl-IntoComponents%3C%26mut+%5BT%5D%3E-for-%26mut+Vec%3CC%3E"],[348,"impl-IntoComponents%3CVec%3CT%3E%3E-for-Vec%3CC%3E"],[354,"impl-IntoIterator-for-%26mut+Vec%3CT,+A%3E"],[355,"impl-IntoIterator-for-%26Vec%3CT,+A%3E"],[356,"impl-IntoIterator-for-Vec%3CT,+A%3E"],[366,"impl-IntoUints%3C%26%5B%3CC+as+UintCast%3E::Uint%5D%3E-for-%26Box%3C%5BC%5D%3E"],[367,"impl-IntoUints%3CBox%3C%5B%3CC+as+UintCast%3E::Uint%5D%3E%3E-for-Box%3C%5BC%5D%3E"],[368,"impl-IntoUints%3C%26mut+%5B%3CC+as+UintCast%3E::Uint%5D%3E-for-%26mut+Box%3C%5BC%5D%3E"],[369,"impl-IntoUints%3C%26%5B%3CC+as+UintCast%3E::Uint%5D%3E-for-%26Vec%3CC%3E"],[370,"impl-IntoUints%3C%26mut+%5B%3CC+as+UintCast%3E::Uint%5D%3E-for-%26mut+Vec%3CC%3E"],[371,"impl-IntoUints%3CVec%3C%3CC+as+UintCast%3E::Uint%3E%3E-for-Vec%3CC%3E"],[394,"impl-PartialEq%3C%26mut+%5BU%5D%3E-for-Vec%3CT,+A%3E"],[395,"impl-PartialEq%3CVec%3CU,+A2%3E%3E-for-Vec%3CT,+A1%3E"],[396,"impl-PartialEq%3C%5BU%5D%3E-for-Vec%3CT,+A%3E"],[397,"impl-PartialEq%3C%5BU;+N%5D%3E-for-Vec%3CT,+A%3E"],[398,"impl-PartialEq%3C%26%5BU%5D%3E-for-Vec%3CT,+A%3E"],[399,"impl-PartialEq%3C%26%5BU;+N%5D%3E-for-Vec%3CT,+A%3E"],[447,"impl-Subscriber-for-Box%3CS%3E"],[448,"impl-Value-for-Box%3CT%3E"],[470,"impl-Iterator-for-Box%3CI,+A%3E"],[471,"impl-AsyncIterator-for-Box%3CS%3E"],[505,"impl-TryFrom%3CBox%3C%5BT%5D%3E%3E-for-Box%3C%5BT;+N%5D%3E"],[506,"impl-TryFrom%3CVec%3CT%3E%3E-for-Box%3C%5BT;+N%5D%3E"],[551,"impl-Box%3CMaybeUninit%3CT%3E,+A%3E"],[552,"impl-Write-for-Box%3CW%3E"],[553,"impl-Hasher-for-Box%3CT,+A%3E"],[880,"impl-From%3CGLTF%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[882,"impl-From%3CTranslate%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[883,"impl-From%3CSphere%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[884,"impl-From%3CConstantMedium%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[885,"impl-From%3CRotateY%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[886,"impl-From%3CSTL%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[887,"impl-From%3CEmpty%3E-for-Hitable%3C\'scene%3E"],[888,"impl-From%3CBVHNode%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[889,"impl-From%3CTriangle%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[890,"impl-From%3CQuad%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[891,"impl-From%3CGLTFTriangle%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[892,"impl-From%3CBoxy%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[893,"impl-From%3CMovingSphere%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[956,"impl-TryInto%3CSTL%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[957,"impl-TryInto%3CMovingSphere%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[958,"impl-TryInto%3CBoxy%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[959,"impl-TryInto%3CBVHNode%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[960,"impl-TryInto%3CRotateY%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[961,"impl-TryInto%3CEmpty%3E-for-Hitable%3C\'scene%3E"],[962,"impl-TryInto%3CSphere%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[963,"impl-TryInto%3CGLTFTriangle%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[964,"impl-TryInto%3CGLTF%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[965,"impl-TryInto%3CTriangle%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[966,"impl-TryInto%3CQuad%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[967,"impl-TryInto%3CConstantMedium%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[968,"impl-TryInto%3CTranslate%3C\'scene%3E%3E-for-Hitable%3C\'scene%3E"],[1113,"impl-From%3CConeLight%3E-for-Material"],[1114,"impl-From%3CDiffuseLight%3E-for-Material"],[1115,"impl-From%3CLambertian%3E-for-Material"],[1116,"impl-From%3CDielectric%3E-for-Material"],[1117,"impl-From%3CMetal%3E-for-Material"],[1118,"impl-From%3CIsotropic%3E-for-Material"],[1119,"impl-From%3CDispersive%3E-for-Material"],[1216,"impl-TryInto%3CIsotropic%3E-for-Material"],[1217,"impl-TryInto%3CMetal%3E-for-Material"],[1218,"impl-TryInto%3CDiffuseLight%3E-for-Material"],[1219,"impl-TryInto%3CLambertian%3E-for-Material"],[1220,"impl-TryInto%3CDielectric%3E-for-Material"],[1221,"impl-TryInto%3CDispersive%3E-for-Material"],[1222,"impl-TryInto%3CConeLight%3E-for-Material"],[2620,"impl-From%3CSpherePDF%3E-for-PDF%3C\'scene%3E"],[2621,"impl-From%3CZeroPDF%3E-for-PDF%3C\'scene%3E"],[2622,"impl-From%3CCosinePDF%3E-for-PDF%3C\'scene%3E"],[2623,"impl-From%3CMixturePDF%3C\'scene%3E%3E-for-PDF%3C\'scene%3E"],[2624,"impl-From%3CHitablePDF%3C\'scene%3E%3E-for-PDF%3C\'scene%3E"],[2732,"impl-TryInto%3CHitablePDF%3C\'scene%3E%3E-for-PDF%3C\'scene%3E"],[2733,"impl-TryInto%3CZeroPDF%3E-for-PDF%3C\'scene%3E"],[2735,"impl-TryInto%3CCosinePDF%3E-for-PDF%3C\'scene%3E"],[2736,"impl-TryInto%3CMixturePDF%3C\'scene%3E%3E-for-PDF%3C\'scene%3E"],[2737,"impl-TryInto%3CSpherePDF%3E-for-PDF%3C\'scene%3E"],[2998,"impl-From%3CSpatialChecker%3E-for-Texture"],[3000,"impl-From%3CSurfaceChecker%3E-for-Texture"],[3001,"impl-From%3CSolidColor%3E-for-Texture"],[3021,"impl-TryInto%3CSpatialChecker%3E-for-Texture"],[3022,"impl-TryInto%3CSurfaceChecker%3E-for-Texture"],[3024,"impl-TryInto%3CSolidColor%3E-for-Texture"]]}],\
+["clovers_cli",{"doc":"Command Line Interface for the clovers
raytracing renderer.","t":"FNNNNNNNNNNNONNNNNNNNNONONONNNNNNNOOOOOONNNNNNNNNNNNO","n":["Opts","adapt_into_using","arrays_from","arrays_into","augment_args","augment_args_for_update","borrow","borrow_mut","cam16_into_unclamped","command","command_for_update","components_from","debug","deref","deref_mut","drop","from","from_angle","from_arg_matches","from_arg_matches_mut","from_stimulus","from_subset","gpu","group_id","height","init","input","into","into_angle","into_cam16_unclamped","into_color","into_color_unclamped","into_stimulus","is_in_subset","max_depth","normalmap","output","quiet","sampler","samples","to_subset","to_subset_unchecked","try_components_into","try_from","try_into","try_into_color","type_id","uints_from","uints_into","update_from_arg_matches","update_from_arg_matches_mut","vzip","width"],"q":[[0,"clovers_cli"],[53,"palette::chromatic_adaptation"],[54,"clap_builder::builder::command"],[55,"palette::cam16::parameters"],[56,"clap_builder::parser::matches::arg_matches"],[57,"clap_builder"],[58,"core::result"],[59,"clap_builder::util::id"],[60,"core::option"],[61,"palette::convert::try_from_into_color"],[62,"core::any"]],"d":["Command line parameters for the clovers
raytracing …","","","","","","","","","","","","Enable some debug logging","","","","Returns the argument unchanged.","","","","","","Use the GPU draw process instead of CPU","","Height of the image in pixels. Default: 1024","","Input filename / location","Calls U::from(self)
.","","","","","","","Maximum evaluated bounce depth for each ray. Default: 64","Render a normal map only. Experimental feature.","Output filename / location. Default: …","Suppress most of the text output","Sampler to use for rendering. Experimental feature.","Number of samples to generate per each pixel. Default: 64","","","","","","","","","","","","","Width of the image in pixels. Default: 1024"],"i":[0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7],"f":"`{{cg}i{}{}{{b{e}}}{}}{ce{}{}}0{dd}011{{c{f{e}}}g{}{}{}}{{}d}03`{hc{}}0{hj}{cc{}}0{l{{Ab{nA`}}}}077`{{}{{Af{Ad}}}}`{{}h}`997999{cAh{}}``````{c{{Af{e}}}{}{}};{c{{Ab{e}}}{}{}}00{c{{Ab{e{Aj{e}}}}}{}{}}{cAl{}}>>{{nl}{{Ab{jA`}}}}0?`","c":[],"p":[[10,"TransformMatrix",53],[5,"Command",54],[5,"BakedParameters",55],[1,"usize"],[1,"unit"],[5,"ArgMatches",56],[5,"Opts",0],[8,"Error",57],[6,"Result",58],[5,"Id",59],[6,"Option",60],[1,"bool"],[5,"OutOfBounds",61],[5,"TypeId",62]],"b":[]}],\
+["clovers_runtime",{"doc":"Runtime functions of the clovers
renderer.","t":"CCCCHHHHPPPPPPFGGKPPNNNNNNNNNCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOCMMONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOFNNNHHHHHHHHHNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN","n":["colorize","draw_cpu","normals","sampler","colorize","draw","normal_map","normal_to_color","Blue","LensOffsetX","LensOffsetY","PixelOffsetX","PixelOffsetY","Random","Randomness","Sampler","SamplerDimension","SamplerTrait","Time","Wavelength","adapt_into_using","adapt_into_using","adapt_into_using","arrays_from","arrays_from","arrays_from","arrays_into","arrays_into","arrays_into","blue","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","cam16_into_unclamped","cam16_into_unclamped","cam16_into_unclamped","clone","clone","clone_into","clone_into","components_from","components_from","components_from","deref","deref","deref","deref_mut","deref_mut","deref_mut","drop","drop","drop","eq","fmt","fmt","from","from","from","from_angle","from_angle","from_angle","from_stimulus","from_stimulus","from_stimulus","from_subset","from_subset","from_subset","init","init","init","into","into","into","into_angle","into_angle","into_angle","into_cam16_unclamped","into_cam16_unclamped","into_cam16_unclamped","into_color","into_color","into_color","into_color_unclamped","into_color_unclamped","into_color_unclamped","into_stimulus","into_stimulus","into_stimulus","is_in_subset","is_in_subset","is_in_subset","lens_offset","pixel_offset","random","sample","sample_dimension","time","to_owned","to_owned","to_possible_value","to_string","to_subset","to_subset","to_subset","to_subset_unchecked","to_subset_unchecked","to_subset_unchecked","try_components_into","try_components_into","try_components_into","try_from","try_from","try_from","try_into","try_into","try_into","try_into_color","try_into_color","try_into_color","type_id","type_id","type_id","uints_from","uints_from","uints_from","uints_into","uints_into","uints_into","value_variants","vzip","vzip","vzip","wavelength","BlueSampler","adapt_into_using","arrays_from","arrays_into","blue_sample_spp1","blue_sample_spp128","blue_sample_spp16","blue_sample_spp2","blue_sample_spp256","blue_sample_spp32","blue_sample_spp4","blue_sample_spp64","blue_sample_spp8","borrow","borrow_mut","cam16_into_unclamped","components_from","deref","deref_mut","drop","from","from_angle","from_stimulus","from_subset","init","into","into_angle","into_cam16_unclamped","into_color","into_color_unclamped","into_stimulus","is_in_subset","new","sample","sample_dimension","to_subset","to_subset_unchecked","try_components_into","try_from","try_into","try_into_color","type_id","uints_from","uints_into","vzip","RandomSampler","adapt_into_using","arrays_from","arrays_into","borrow","borrow_mut","cam16_into_unclamped","components_from","deref","deref_mut","drop","fmt","from","from_angle","from_stimulus","from_subset","init","into","into_angle","into_cam16_unclamped","into_color","into_color_unclamped","into_stimulus","is_in_subset","new","sample","sample_dimension","to_subset","to_subset_unchecked","try_components_into","try_from","try_into","try_into_color","type_id","uints_from","uints_into","vzip"],"q":[[0,"clovers_runtime"],[4,"clovers_runtime::colorize"],[5,"clovers_runtime::draw_cpu"],[6,"clovers_runtime::normals"],[8,"clovers_runtime::sampler"],[136,"clovers_runtime::sampler::blue"],[181,"clovers_runtime::sampler::random"],[218,"clovers::ray"],[219,"clovers::scenes"],[220,"rand::rngs::small"],[221,"palette::white_point"],[222,"palette::xyz"],[223,"clovers"],[224,"palette::rgb"],[225,"alloc::vec"],[226,"palette::rgb"],[227,"palette::cam16::parameters"],[228,"core::fmt"],[229,"core::fmt"],[230,"core::option"],[231,"alloc::string"],[232,"core::result"],[233,"palette::convert::try_from_into_color"],[234,"core::any"]],"d":["An opinionated colorize method. Given a Ray and a Scene, …","An opinionated method for drawing a scene using the CPU …","Alternative rendering method. Only returns a normalmap of …","Sampler architecture for the renderer, based on the …","The main coloring function. Sends a Ray
to the Scene
, sees …","The main drawing function, returns a Vec<Srgb>
as a …","Rendering function for getting a normal map in tangent …","Given a surface normal, return a color based on normal …","Blue noise based sampler, see BlueSampler","","","","","Random number generator based sampler, see RandomSampler","A collection of random values to be used for each sample. …","Enum of the supported samplers.","Various sampling dimensions used by the samplers","","","","","","","","","","","","","A sampler based on blue noise. Works especially well at …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","","","","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","The (x,y)
offset used in the lens equations for aperture / …","Intra-pixel (x,y)
offset, both in range [0..1]
. Used for …","A sampler based on a random number generator. This is the …","","Manually request a sample from the specific dimension","The time of the ray, in range [0..1]
","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Wavelength of the ray","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","",""],"i":[0,0,0,0,0,0,0,0,9,17,17,17,17,9,0,0,0,0,17,17,24,9,17,24,9,17,24,9,17,0,24,9,17,24,9,17,24,9,17,9,17,9,17,24,9,17,24,9,17,24,9,17,24,9,17,9,9,9,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,24,0,5,5,24,9,17,9,9,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,24,9,17,9,24,9,17,24,0,33,33,33,0,0,0,0,0,0,0,0,0,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,0,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34],"f":"````{{bdffhj}{{n{l}}}}{{A`dAb}{{Ah{{Af{Ad}}}}}}{{bdh}Aj}{AlAj}````````````{{cg}i{}{}{{An{e}}}{}}00{ce{}{}}00000`000000{{c{B`{e}}}g{}{}{}}00{AbAb}{BbBb}{{ce}Bd{}{}}0444{Bfc{}}00000{BfBd}00{{AbAb}Bh}{{AbBj}Bl}0{cc{}}00000999999{{}Bf}00::::::999:::::::::{cBh{}}00```{{jBnBnBn}C`}{{jBnBnBnBb}Cb}`=={Ab{{Cf{Cd}}}}{cCh{}}{c{{Cf{e}}}{}{}}00{ce{}{}}00{c{{Cj{e}}}{}{}}00000000{c{{Cj{e{Cl{e}}}}}{}{}}00{cCn{}}00333333{{}{{D`{Ab}}}}444``{{cg}i{}{}{{An{e}}}{}}55{{BnBnBnBb}Cb}0000000066{{c{B`{e}}}g{}{}{}}7{Bfc{}}0{BfBd}{cc{}}0::{{}Bf};;4;;;{cBh{}}{A`Db}{{DbBnBnBn}C`}{{DbBnBnBnBb}Cb}{c{{Cf{e}}}{}{}}{ce{}{}}{c{{Cj{e}}}{}{}}00{c{{Cj{e{Cl{e}}}}}{}{}}{cCn{}}333`?3333=3<<;{{DdBj}Bl};;44:44>4449{hDd}{{DdBnBnBn}C`}{{DdBnBnBnBb}Cb}8766654777","c":[],"p":[[5,"Ray",218],[5,"Scene",219],[1,"u32"],[5,"SmallRng",220],[10,"SamplerTrait",8],[5,"E",221],[5,"Xyz",222],[5,"RenderOpts",223],[6,"Sampler",8],[1,"u8"],[8,"Srgb",224],[5,"Vec",225],[8,"LinSrgb",224],[8,"Direction",223],[10,"TransformMatrix",226],[5,"BakedParameters",227],[6,"SamplerDimension",8],[1,"unit"],[1,"usize"],[1,"bool"],[5,"Formatter",228],[8,"Result",228],[1,"i32"],[5,"Randomness",8],[8,"Float",223],[5,"PossibleValue",229],[6,"Option",230],[5,"String",231],[6,"Result",232],[5,"OutOfBounds",233],[5,"TypeId",234],[1,"slice"],[5,"BlueSampler",136],[5,"RandomSampler",181]],"b":[[56,"impl-Debug-for-Sampler"],[57,"impl-Display-for-Sampler"]]}]\
+]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);
diff --git a/settings.html b/settings.html
new file mode 100644
index 00000000..4cde85be
--- /dev/null
+++ b/settings.html
@@ -0,0 +1,2 @@
+1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +
//! Axis-aligned bounding box.
+
+use core::ops::Add;
+
+use crate::{interval::Interval, ray::Ray, Float, Position, Vec3, EPSILON_RECT_THICKNESS};
+
+/// Axis-aligned bounding box Defined by two opposing corners, each of which are a [Vec3].
+///
+/// This is useful for creating bounding volume hierarchies, which is an optimization for reducing the time spent on calculating ray-object intersections.
+#[derive(Clone, Debug, PartialEq)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct AABB {
+ /// The bounding interval on the X axis
+ pub x: Interval,
+ /// The bounding interval on the Y axis
+ pub y: Interval,
+ /// The bounding interval on the Z axis
+ pub z: Interval,
+}
+
+impl AABB {
+ /// Creates a new axis-aligned bounding box from three intervals
+ #[must_use]
+ pub fn new(interval_x: Interval, interval_y: Interval, interval_z: Interval) -> AABB {
+ AABB {
+ x: interval_x,
+ y: interval_y,
+ z: interval_z,
+ }
+ }
+
+ /// Creates a new axis-aligned bounding box from two coordinates. Treats the two points a and b as extrema for the bounding box, so we don't require a particular minimum/maximum coordinate order.
+ #[must_use]
+ pub fn new_from_coords(a: Position, b: Position) -> AABB {
+ AABB {
+ x: Interval::new(a[0].min(b[0]), a[0].max(b[0])),
+ y: Interval::new(a[1].min(b[1]), a[1].max(b[1])),
+ z: Interval::new(a[2].min(b[2]), a[2].max(b[2])),
+ }
+ }
+
+ #[allow(clippy::doc_link_with_quotes)]
+ /// Given a [Ray], returns whether the ray hits the bounding box or not. Current default method, based on ["An Optimized AABB Hit Method"](https://raytracing.github.io/books/RayTracingTheNextWeek.html)
+ #[must_use]
+ pub fn hit(&self, ray: &Ray, mut tmin: Float, mut tmax: Float) -> bool {
+ // TODO: Create an improved hit method with more robust handling of zeroes. See https://github.com/RayTracing/raytracing.github.io/issues/927
+ // Both methods below are susceptible for NaNs and infinities, and have subtly different edge cases.
+
+ // "My adjusted method" - possibly more zero-resistant?
+ // TODO: validate
+ for axis in 0..3 {
+ // If ray direction component is 0, invd becomes infinity.
+ // Ignore? False positive hit for aabb is probably better than false negative; the actual object can still be hit more accurately
+ let invd = 1.0 / ray.direction[axis];
+ if !invd.is_normal() {
+ continue;
+ }
+ // If the value in parenthesis ends up as zero, 0*inf can be NaN
+ let mut t0: Float = (self.axis(axis).min - ray.origin[axis]) * invd;
+ let mut t1: Float = (self.axis(axis).max - ray.origin[axis]) * invd;
+ if !t0.is_normal() || !t1.is_normal() {
+ continue;
+ }
+ if invd < 0.0 {
+ core::mem::swap(&mut t0, &mut t1);
+ }
+ tmin = if t0 > tmin { t0 } else { tmin };
+ tmax = if t1 < tmax { t1 } else { tmax };
+ if tmax <= tmin {
+ return false;
+ }
+ }
+
+ // If we have not missed on any axis, return true for the hit
+ true
+ }
+
+ /// Given a [Ray], returns whether the ray hits the bounding box or not. Old method from a GitHub issue. Exists mostly for testing purposes.
+ #[must_use]
+ #[deprecated]
+ pub fn hit_old(&self, ray: &Ray, mut tmin: Float, mut tmax: Float) -> bool {
+ // "Old method"
+ for axis in 0..3 {
+ let invd = 1.0 / ray.direction[axis];
+ let mut t0: Float = (self.axis(axis).min - ray.origin[axis]) * invd;
+ let mut t1: Float = (self.axis(axis).max - ray.origin[axis]) * invd;
+ if invd < 0.0 {
+ core::mem::swap(&mut t0, &mut t1);
+ }
+ tmin = if t0 > tmin { t0 } else { tmin };
+ tmax = if t1 < tmax { t1 } else { tmax };
+ if tmax <= tmin {
+ return false;
+ }
+ }
+ true
+ }
+
+ /// Given a [Ray], returns whether the ray hits the bounding box or not. Newer method from a GitHub issue. Exists mostly for testing purposes.
+ #[must_use]
+ #[deprecated]
+ pub fn hit_new(&self, ray: &Ray, mut tmin: Float, mut tmax: Float) -> bool {
+ // "New method"
+ for axis in 0..3 {
+ let a = (self.axis(axis).min - ray.origin[axis]) / ray.direction[axis];
+ let b = (self.axis(axis).max - ray.origin[axis]) / ray.direction[axis];
+ let t0: Float = a.min(b);
+ let t1: Float = a.max(b);
+ tmin = t0.max(tmin);
+ tmax = t1.min(tmax);
+ if tmax <= tmin {
+ return false;
+ }
+ }
+ true
+ }
+
+ /// Given two axis-aligned bounding boxes, return a new [AABB] that contains both.
+ #[must_use]
+ pub fn surrounding_box(box0: &AABB, box1: &AABB) -> AABB {
+ AABB {
+ x: Interval::new_from_intervals(box0.x, box1.x),
+ y: Interval::new_from_intervals(box0.y, box1.y),
+ z: Interval::new_from_intervals(box0.z, box1.z),
+ }
+ }
+
+ /// Make sure we don't have a zero-thickness AABB, padding if necessary.
+ pub fn pad(&mut self) {
+ // TODO: refactor
+ let delta = EPSILON_RECT_THICKNESS;
+ let new_x: Interval = if self.x.size() >= delta {
+ self.x
+ } else {
+ self.x.expand(delta)
+ };
+ let new_y: Interval = if self.y.size() >= delta {
+ self.y
+ } else {
+ self.y.expand(delta)
+ };
+ let new_z: Interval = if self.z.size() >= delta {
+ self.z
+ } else {
+ self.z.expand(delta)
+ };
+
+ *self = AABB::new(new_x, new_y, new_z);
+ }
+
+ /// Returns the interval of the given axis.
+ // TODO: this api is kind of annoying
+ #[must_use]
+ pub fn axis(&self, n: usize) -> Interval {
+ match n {
+ 0 => self.x,
+ 1 => self.y,
+ 2 => self.z,
+ _ => panic!("AABB::axis called with invalid parameter: {n:?}"),
+ }
+ }
+}
+
+impl Add<Vec3> for AABB {
+ type Output = AABB;
+
+ fn add(self, offset: Vec3) -> Self::Output {
+ AABB::new(self.x + offset.x, self.y + offset.y, self.z + offset.z)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +
//! Bounding Volume Hierarchy Node.
+
+use core::cmp::Ordering;
+
+use rand::{rngs::SmallRng, Rng};
+
+use crate::{
+ aabb::AABB,
+ hitable::{Empty, HitRecord, Hitable, HitableTrait},
+ ray::Ray,
+ wavelength::Wavelength,
+ Box, Direction, Float, Position, Vec,
+};
+
+/// Bounding Volume Hierarchy Node.
+///
+/// A node in a tree structure defining a hierarchy of objects in a scene: a node knows its bounding box, and has two children which are also `BVHNode`s. This is used for accelerating the ray-object intersection calculation in the ray tracer. See [Bounding Volume hierarchies](https://raytracing.github.io/books/RayTracingTheNextWeek.html)
+#[derive(Debug, Clone)]
+pub struct BVHNode<'scene> {
+ /// Left child of the `BVHNode`
+ pub left: Box<Hitable<'scene>>,
+ /// Right child of the `BVHNode`
+ pub right: Box<Hitable<'scene>>,
+ /// Bounding box containing both of the child nodes
+ pub bounding_box: AABB,
+}
+
+impl<'scene> BVHNode<'scene> {
+ /// Create a new `BVHNode` tree from a given list of [Object](crate::objects::Object)s
+ #[must_use]
+ pub fn from_list(mut hitables: Vec<Hitable>, time_0: Float, time_1: Float) -> BVHNode {
+ // Initialize two child nodes
+ let left: Box<Hitable>;
+ let right: Box<Hitable>;
+
+ let comparators = [box_x_compare, box_y_compare, box_z_compare];
+
+ // What is the axis with the largest span?
+ // TODO: horribly inefficient, improve!
+ let bounding: AABB =
+ vec_bounding_box(&hitables, time_0, time_1).expect("No bounding box for objects");
+ let spans = [
+ bounding.axis(0).size(),
+ bounding.axis(1).size(),
+ bounding.axis(2).size(),
+ ];
+ let largest = f32::max(f32::max(spans[0], spans[1]), spans[2]);
+ #[allow(clippy::float_cmp)] // TODO: better code for picking the largest axis...
+ let axis: usize = spans.iter().position(|&x| x == largest).unwrap();
+ let comparator = comparators[axis];
+
+ // How many objects do we have?
+ let object_span = hitables.len();
+
+ if object_span == 1 {
+ // If we only have one object, add one and an empty object.
+ // TODO: can this hack be removed?
+ left = Box::new(hitables[0].clone());
+ right = Box::new(Hitable::Empty(Empty {}));
+ let bounding_box = left.bounding_box(time_0, time_1).unwrap().clone(); // TODO: remove unwrap
+ return BVHNode {
+ left,
+ right,
+ bounding_box,
+ };
+ } else if object_span == 2 {
+ // If we are comparing two objects, perform the comparison
+ // Insert the child nodes in order
+ match comparator(&hitables[0], &hitables[1]) {
+ Ordering::Less => {
+ left = Box::new(hitables[0].clone());
+ right = Box::new(hitables[1].clone());
+ }
+ Ordering::Greater => {
+ left = Box::new(hitables[1].clone());
+ right = Box::new(hitables[0].clone());
+ }
+ Ordering::Equal => {
+ // TODO: what should happen here?
+ panic!("Equal objects in BVHNode from_list");
+ }
+ }
+ } else if object_span == 3 {
+ // Three objects: create one bare object and one BVHNode with two objects
+ hitables.sort_by(comparator);
+ left = Box::new(hitables[0].clone());
+ right = Box::new(Hitable::BVHNode(BVHNode {
+ left: Box::new(hitables[1].clone()),
+ right: Box::new(hitables[2].clone()),
+ bounding_box: AABB::surrounding_box(
+ // TODO: no unwrap?
+ hitables[1].bounding_box(time_0, time_1).unwrap(),
+ hitables[2].bounding_box(time_0, time_1).unwrap(),
+ ),
+ }));
+ } else {
+ // Otherwise, recurse
+ hitables.sort_by(comparator);
+
+ // Split the vector; divide and conquer
+ let mid = object_span / 2;
+ let hitables_right = hitables.split_off(mid);
+ left = Box::new(Hitable::BVHNode(BVHNode::from_list(
+ hitables, time_0, time_1,
+ )));
+ right = Box::new(Hitable::BVHNode(BVHNode::from_list(
+ hitables_right,
+ time_0,
+ time_1,
+ )));
+ }
+
+ let box_left = left.bounding_box(time_0, time_1);
+ let box_right = right.bounding_box(time_0, time_1);
+
+ // Generate a bounding box and BVHNode if possible
+ if let (Some(box_left), Some(box_right)) = (box_left, box_right) {
+ let bounding_box = AABB::surrounding_box(box_left, box_right);
+
+ BVHNode {
+ left,
+ right,
+ bounding_box,
+ }
+ } else {
+ panic!("No bounding box in bvh_node constructor");
+ }
+ }
+
+ #[must_use]
+ /// Returns the count of the nodes in the tree
+ pub fn count(&self) -> usize {
+ let leftsum = match &*self.left {
+ Hitable::BVHNode(b) => b.count(),
+ _ => 1,
+ };
+ let rightsum = match &*self.right {
+ Hitable::BVHNode(b) => b.count(),
+ _ => 1,
+ };
+
+ leftsum + rightsum
+ }
+}
+
+impl<'scene> HitableTrait for BVHNode<'scene> {
+ /// The main `hit` function for a [`BVHNode`]. Given a [Ray], and an interval `distance_min` and `distance_max`, returns either `None` or `Some(HitRecord)` based on whether the ray intersects with the encased objects during that interval.
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ // If we do not hit the bounding box of current node, early return None
+ if !self.bounding_box.hit(ray, distance_min, distance_max) {
+ return None;
+ }
+
+ // Otherwise we have hit the bounding box of this node, recurse to child nodes
+ let hit_left = self.left.hit(ray, distance_min, distance_max, rng);
+ let hit_right = self.right.hit(ray, distance_min, distance_max, rng);
+
+ // Did we hit neither of the child nodes, one of them, or both?
+ // Return the closest thing we hit
+ match (&hit_left, &hit_right) {
+ (None, None) => None, // In theory, this case should not be reachable
+ (None, Some(_)) => hit_right,
+ (Some(_), None) => hit_left,
+ (Some(left), Some(right)) => {
+ if left.distance < right.distance {
+ return hit_left;
+ }
+ hit_right
+ }
+ }
+ }
+
+ /// Returns the axis-aligned bounding box [AABB] of the objects within this [`BVHNode`].
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t11: Float) -> Option<&AABB> {
+ Some(&self.bounding_box)
+ }
+
+ /// Returns a probability density function value based on the children
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ match (&*self.left, &*self.right) {
+ (_, Hitable::Empty(_)) => self
+ .left
+ .pdf_value(origin, direction, wavelength, time, rng),
+ (Hitable::Empty(_), _) => self
+ .right
+ .pdf_value(origin, direction, wavelength, time, rng),
+ (_, _) => {
+ (self
+ .left
+ .pdf_value(origin, direction, wavelength, time, rng)
+ + self
+ .right
+ .pdf_value(origin, direction, wavelength, time, rng))
+ / 2.0
+ }
+ }
+ }
+
+ /// Returns a random point on the surface of one of the children
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position {
+ match (&*self.left, &*self.right) {
+ (_, Hitable::Empty(_)) => self.left.random(origin, rng),
+ (Hitable::Empty(_), _) => self.right.random(origin, rng),
+ (_, _) => {
+ if rng.gen::<bool>() {
+ self.left.random(origin, rng)
+ } else {
+ self.right.random(origin, rng)
+ }
+ }
+ }
+ }
+}
+
+fn box_compare(a: &Hitable, b: &Hitable, axis: usize) -> Ordering {
+ // TODO: proper time support?
+ let box_a: Option<&AABB> = a.bounding_box(0.0, 1.0);
+ let box_b: Option<&AABB> = b.bounding_box(0.0, 1.0);
+
+ if let (Some(box_a), Some(box_b)) = (box_a, box_b) {
+ if box_a.axis(axis).min < box_b.axis(axis).min {
+ Ordering::Less
+ } else {
+ // Default to greater, even if equal
+ Ordering::Greater
+ }
+ } else {
+ panic!("No bounding box to compare with.")
+ }
+}
+
+fn box_x_compare(a: &Hitable, b: &Hitable) -> Ordering {
+ box_compare(a, b, 0)
+}
+
+fn box_y_compare(a: &Hitable, b: &Hitable) -> Ordering {
+ box_compare(a, b, 1)
+}
+
+fn box_z_compare(a: &Hitable, b: &Hitable) -> Ordering {
+ box_compare(a, b, 2)
+}
+
+// TODO: inefficient, O(n) *and* gets called at every iteration of BVHNode creation => quadratic behavior
+#[must_use]
+fn vec_bounding_box(vec: &Vec<Hitable>, t0: Float, t1: Float) -> Option<AABB> {
+ if vec.is_empty() {
+ return None;
+ }
+
+ // Mutable AABB that we grow from zero
+ let mut output_box: Option<AABB> = None;
+
+ // Go through all the objects, and expand the AABB
+ for object in vec {
+ // Check if the object has a box
+ let Some(bounding) = object.bounding_box(t0, t1) else {
+ // No box found for the object, early return.
+ // Having even one unbounded object in a list makes the entire list unbounded!
+ return None;
+ };
+
+ // Do we have an output_box already saved?
+ match output_box {
+ // If we do, expand it & recurse
+ Some(old_box) => {
+ output_box = Some(AABB::surrounding_box(&old_box, bounding));
+ }
+ // Otherwise, set output box to be the newly-found box
+ None => {
+ output_box = Some(bounding.clone());
+ }
+ }
+ }
+
+ // Return the final combined output_box
+ output_box
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +
//! Camera. Used for creating [Rays](crate::ray::Ray) towards the scene, with directions defined by the camera properties.
+
+#![allow(clippy::too_many_arguments)] // TODO: Camera::new() has a lot of arguments.
+
+use crate::wavelength::Wavelength;
+use crate::{ray::Ray, Float, Vec3, PI};
+use crate::{Direction, Position, Vec2};
+use nalgebra::Unit;
+
+#[derive(Copy, Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// The main [Camera] object used in the ray tracing.
+pub struct Camera {
+ /// Coordinate of the lower left corner of the camera.
+ pub lower_left_corner: Position,
+ /// Defines the horizontal axis for the camera.
+ pub horizontal: Vec3,
+ /// Defines the vertical axis for the camera.
+ pub vertical: Vec3,
+ /// Defines the origin of the camera.
+ pub origin: Position,
+ /// Defines the lens radius for the camera. TODO: understand and explain better
+ pub lens_radius: Float,
+ /// Defines the earliest starting time for the camera, used when generating [Rays](Ray).
+ pub time_0: Float,
+ /// Defines the latest ending time for the camera, used when generating [Rays](Ray).
+ pub time_1: Float,
+ // TODO: clarify these odd one-letter variables
+ /// U
+ pub u: Direction,
+ /// V
+ pub v: Direction,
+ /// W
+ pub w: Direction,
+}
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// Represents the fields that can be described in a Scene file. Some other fields the main Camera struct requires (such as `aspect_ratio`) are derived from other info (such as width, height)
+pub struct CameraInit {
+ /// Describes where the camera is
+ pub look_from: Position,
+ /// Describes where the camera is looking at
+ pub look_at: Position,
+ /// Describes the subjective "up" direction for the camera to define the orientation
+ pub up: Vec3,
+ /// Describes the vertical field of view for the camera
+ pub vertical_fov: Float,
+ /// Describes the size of the aperture of the camera.
+ // TODO: does it really though
+ pub aperture: Float,
+ /// Describes the distance at which the camera has been focused to
+ pub focus_distance: Float,
+}
+
+impl Camera {
+ /// Creates a new [Camera] with the given parameters.
+ #[must_use]
+ pub fn new(
+ look_from: Position,
+ look_at: Position,
+ up: Vec3,
+ vertical_fov: Float,
+ aspect_ratio: Float,
+ aperture: Float,
+ focus_distance: Float,
+ time_0: Float,
+ time_1: Float,
+ ) -> Self {
+ let lens_radius: Float = aperture / 2.0;
+ let theta: Float = vertical_fov * PI / 180.0;
+ let half_height: Float = (theta / 2.0).tan();
+ let half_width: Float = aspect_ratio * half_height;
+ let origin: Position = look_from;
+ let w: Direction = Unit::new_normalize(look_from - look_at);
+ let u: Direction = Unit::new_normalize(up.cross(&w));
+ let v: Direction = Unit::new_normalize(w.cross(&u));
+
+ // TODO: understand this defocus
+ let lower_left_corner: Vec3 = origin
+ - half_width * focus_distance * *u
+ - half_height * focus_distance * *v
+ - focus_distance * *w;
+ let horizontal: Vec3 = 2.0 * half_width * focus_distance * *u;
+ let vertical: Vec3 = 2.0 * half_height * focus_distance * *v;
+
+ Camera {
+ lower_left_corner,
+ horizontal,
+ vertical,
+ origin,
+ lens_radius,
+ time_0,
+ time_1,
+ u,
+ v,
+ w,
+ }
+ }
+
+ /// Generates a new [Ray] from the camera
+ #[must_use]
+ pub fn get_ray(
+ self,
+ pixel_uv: Vec2, // pixel location in image uv coordinates, range 0..1
+ mut lens_offset: Vec2,
+ time: Float,
+ wavelength: Wavelength,
+ ) -> Ray {
+ let (x_offset, y_offset) = (pixel_uv.x, pixel_uv.y);
+ // TODO: add a better defocus blur / depth of field implementation
+ lens_offset.x *= &self.lens_radius;
+ lens_offset.y *= &self.lens_radius;
+ let offset: Vec3 = *self.u * lens_offset.x + *self.v * lens_offset.y;
+ let direction =
+ self.lower_left_corner + x_offset * self.horizontal + y_offset * self.vertical
+ - self.origin
+ - offset;
+ let direction = Unit::new_normalize(direction);
+ Ray {
+ origin: self.origin + offset,
+ direction,
+ time,
+ wavelength,
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +
//! Initialization structures for colors. This exists for deserialization purposes.
+
+use alloc::string::String;
+use core::str::FromStr;
+
+use palette::{
+ chromatic_adaptation::AdaptInto,
+ white_point::{D65, E},
+ LinSrgb, Oklch, Srgb, Xyz,
+};
+
+use crate::Float;
+
+/// Type safe initialization structure for a color. Can be specified in a variety of color spaces.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(rename_all = "snake_case"))]
+pub enum TypedColorInit {
+ /// Hex "web color" Srgb
+ Hex(String),
+ /// Linear Srgb
+ LinSrgb(LinSrgb),
+ /// Non-linear Srgb
+ Srgb(Srgb),
+ /// XYZ, E illuminant
+ XyzE(Xyz<E>),
+ /// XYZ, D65 illuminant
+ XyzD65(Xyz<D65>),
+ /// Oklch
+ Oklch(Oklch),
+ // TODO: add more
+}
+
+/// Initialization structure for a color. Contains either an untyped, legacy variant (assumed Srgb) or one of the new type-safe, tagged versions.
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(untagged))]
+#[derive(Clone, Debug)]
+pub enum ColorInit {
+ /// Legacy color, assume Srgb given as array of three floats in range 0..1 for up to unity gain, >1 for positive gain (illuminating) colors
+ Color([Float; 3]),
+ /// Typesafe color initialization structure
+ TypedColor(TypedColorInit),
+}
+
+impl From<ColorInit> for Xyz<E> {
+ fn from(val: ColorInit) -> Self {
+ // TODO: ensure correctness
+ match val {
+ ColorInit::Color(c) => Srgb::new(c[0], c[1], c[2]).adapt_into(),
+ ColorInit::TypedColor(s) => match s {
+ TypedColorInit::Hex(c) => Srgb::from_str(&c).unwrap().into_format().adapt_into(),
+ TypedColorInit::LinSrgb(c) => LinSrgb::new(c.red, c.green, c.blue).adapt_into(),
+ TypedColorInit::Srgb(c) => Srgb::new(c.red, c.green, c.blue).adapt_into(),
+ TypedColorInit::XyzE(c) => c,
+ TypedColorInit::XyzD65(c) => c.adapt_into(),
+ TypedColorInit::Oklch(c) => Oklch::new(c.l, c.chroma, c.hue).adapt_into(),
+ },
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +
//! An abstraction for things that can be hit by [Rays](crate::ray::Ray).
+
+#![allow(missing_docs)] // TODO: Lots of undocumented things for now
+
+#[cfg(feature = "stl")]
+use crate::objects::STL;
+#[cfg(feature = "gl_tf")]
+use crate::objects::{GLTFTriangle, GLTF};
+
+use crate::{
+ aabb::AABB,
+ bvhnode::BVHNode,
+ materials::MaterialTrait,
+ objects::{Boxy, ConstantMedium, MovingSphere, Quad, RotateY, Sphere, Translate, Triangle},
+ ray::Ray,
+ wavelength::Wavelength,
+ Direction, Float, Position,
+};
+
+use enum_dispatch::enum_dispatch;
+use rand::rngs::SmallRng;
+
+/// Represents a ray-object intersection, with plenty of data about the intersection.
+#[derive(Debug)]
+pub struct HitRecord<'a> {
+ /// Distance from the ray origin to the hitpoint
+ pub distance: Float,
+ /// 3D coordinate of the hitpoint
+ pub position: Position,
+ /// Surface normal from the hitpoint
+ pub normal: Direction,
+ /// U surface coordinate of the hitpoint
+ pub u: Float,
+ /// V surface coordinate of the hitpoint
+ pub v: Float,
+ /// Reference to the material at the hitpoint
+ pub material: &'a dyn MaterialTrait,
+ /// Is the hitpoint at the front of the surface
+ pub front_face: bool,
+}
+
+impl<'a> HitRecord<'a> {
+ /// Helper function for getting normals pointing at the correct direction. TODO: consider removal?
+ pub fn set_face_normal(&mut self, ray: &Ray, outward_normal: Direction) {
+ self.front_face = ray.direction.dot(&outward_normal) < 0.0;
+ if self.front_face {
+ self.normal = outward_normal;
+ } else {
+ self.normal = -outward_normal;
+ }
+ }
+}
+
+/// An abstraction for things that can be hit by [Rays](crate::ray::Ray).
+#[enum_dispatch(HitableTrait)]
+#[derive(Debug, Clone)]
+pub enum Hitable<'scene> {
+ Boxy(Boxy<'scene>),
+ BVHNode(BVHNode<'scene>),
+ ConstantMedium(ConstantMedium<'scene>),
+ MovingSphere(MovingSphere<'scene>),
+ Quad(Quad<'scene>),
+ RotateY(RotateY<'scene>),
+ Sphere(Sphere<'scene>),
+ #[cfg(feature = "stl")]
+ STL(STL<'scene>),
+ #[cfg(feature = "gl_tf")]
+ GLTF(GLTF<'scene>),
+ Translate(Translate<'scene>),
+ Triangle(Triangle<'scene>),
+ Empty(Empty),
+ #[cfg(feature = "gl_tf")]
+ GLTFTriangle(GLTFTriangle<'scene>),
+}
+
+// TODO: remove horrible hack
+#[derive(Debug, Clone)]
+pub struct Empty {}
+
+impl HitableTrait for Empty {
+ fn hit(
+ &self,
+ _ray: &Ray,
+ _distance_min: Float,
+ _distance_max: Float,
+ _rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ None
+ }
+
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ None
+ }
+
+ fn pdf_value(
+ &self,
+ _origin: Position,
+ _direction: Direction,
+ _wavelength: Wavelength,
+ _time: Float,
+ _rng: &mut SmallRng,
+ ) -> Float {
+ 0.0
+ }
+
+ fn random(&self, _origin: Position, _rng: &mut SmallRng) -> Position {
+ panic!("Hitable::Empty::random called!")
+ }
+}
+
+#[enum_dispatch]
+pub trait HitableTrait {
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord>;
+
+ #[must_use]
+ fn bounding_box(&self, t0: Float, t1: Float) -> Option<&AABB>;
+
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float;
+
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position;
+}
+
+/// Returns a tuple of `(front_face, normal)`. Used in lieu of `set_face_normal` in the Ray Tracing for the Rest Of Your Life book.
+#[must_use]
+pub fn get_orientation(ray: &Ray, outward_normal: Direction) -> (bool, Direction) {
+ let front_face = ray.direction.dot(&outward_normal) < 0.0;
+ let normal = if front_face {
+ outward_normal
+ } else {
+ -outward_normal
+ };
+
+ (front_face, normal)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +
//! Interval helper adapted from the book
+
+use core::ops::Add;
+
+use crate::Float;
+
+/// An interval structure.
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct Interval {
+ /// Smallest value of the interval. Must be kept in order
+ pub min: Float,
+ /// Largest value of the interval. Must be kept in order
+ pub max: Float,
+}
+
+impl Interval {
+ /// Constructs a new interval
+ #[must_use]
+ pub fn new(a: Float, b: Float) -> Self {
+ Interval {
+ min: a.min(b),
+ max: a.max(b),
+ }
+ }
+
+ /// Constructs a new interval from two intervals
+ // TODO: explanation, clarification
+ #[must_use]
+ pub fn new_from_intervals(a: Interval, b: Interval) -> Self {
+ Interval {
+ min: a.min.min(b.min),
+ max: a.max.max(b.max),
+ }
+ }
+
+ /// Returns an interval expanded with delta at both ends
+ #[must_use]
+ pub fn expand(&self, delta: Float) -> Self {
+ Interval::new(self.min - delta, self.max + delta)
+ }
+
+ /// Returns the size of the interval
+ #[must_use]
+ pub fn size(self) -> Float {
+ self.max - self.min
+ }
+}
+
+impl Add<Float> for Interval {
+ type Output = Interval;
+
+ fn add(self, offset: Float) -> Self::Output {
+ Interval::new(self.min + offset, self.max + offset)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +
//! # clovers - ray tracing in rust!
+//!
+//! **Note**: This library is experimental & heavily work in progress. Everything can change at a moment's notice. It is probably not a good idea to use this library for anything other than experimentation for now!
+//!
+//! This project uses GitHub for development and issue tracking. [Link to the repository](https://github.com/Walther/clovers).
+//!
+//! # Guiding thoughts
+//! - Keep it clean: prefer good abstractions, avoid deep integration
+//! - Platform agnostic: hopefully runnable by both CPU and GPU, on desktop and `WebAssembly`, etc
+//! - Prefer correctness: no "cheating" optimizations / approximations
+//! - Look for beautiful light <3
+//!
+//! # How it works
+//!
+//! There are a few core stages of using clovers.
+//!
+//! ## Creating and Loading a Scene
+//!
+//! First, you will need a [Scene](scenes::Scene). You can create a scene manually or utilize [serde](https://docs.serde.rs/) to deserialize from a file. Currently, the example binary uses a JSON format.
+//!
+//! - [Scenes](scenes::Scene) have [Objects](objects::Object)
+//! - [Objects](objects::Object) have a [Material](materials::Material)
+//! - [Materials](materials::Material) usually have a [Texture](textures::Texture)
+//! - Materials and Textures may have unique paramteres to adjust
+//!
+//!
+//! ## Rendering the Scene
+//!
+//! clovers is not opinionated on how you want to render your scene. In a usual scenario, you probably want to have some form of a pixel buffer, with knowledge of the `x` and `y` coordinates of your buffer.
+//!
+//! - Rendering is done by creating [`Ray`](ray::Ray)s and seeing what they hit
+//! - A [`Ray`](ray::Ray) has an origin and a direction
+//! - Every [`Object`](objects::Object) has a `hit()` method that takes a [Ray](ray::Ray) and returns an Option<[`HitRecord`](hitable::HitRecord)>
+//! - If you get None, use that information to colorize your pixel with a default color
+//! - If you get Some([`HitRecord`](hitable::HitRecord)), use its details to colorize your pixel
+//! - You most likely also want to recurse: depending on the material, maybe `scatter()` and cast a new [`Ray`](ray::Ray)?
+//!
+//! You most likely want to repeat this process multiple times for each of your pixels: generating multiple samples per pixel results in a higher quality image.
+//!
+//!
+//! ## Post processing
+//!
+//! **TODO:** maybe add some post processing utilities?
+//! - denoise support?
+//! - 3D & rendering aware effects?
+//! - etc
+//!
+//! ## Using the result
+//!
+//! At the end, use your pixel buffer - save to an image file, draw a frame in a GUI window, etc.
+
+// Lints
+#![deny(clippy::pedantic)]
+#![deny(explicit_outlives_requirements)]
+#![deny(trivial_casts)]
+#![deny(trivial_numeric_casts)]
+#![deny(unsafe_code)]
+#![deny(unused_lifetimes)]
+#![deny(unused_qualifications)]
+#![deny(missing_debug_implementations)]
+#![deny(missing_docs)]
+// TODO: temporarily allowing some in order to get a majority of clippy::pedantic enabled
+#![allow(clippy::many_single_char_names)] // Lots of places with coordinates etc
+#![allow(clippy::missing_panics_doc)] // TODO: remove panics where feasible later
+#![allow(clippy::module_name_repetitions)]
+// no_std required for gpu accelerated rendering
+#![cfg_attr(not(feature = "std"), no_std)]
+extern crate alloc;
+pub use alloc::boxed::Box;
+pub use alloc::vec::Vec;
+
+// Externals
+use nalgebra::{
+ base::{Vector2, Vector3, Vector4},
+ Unit,
+};
+
+// Internals
+pub mod aabb;
+pub mod bvhnode;
+pub mod camera;
+pub mod colorinit;
+pub mod hitable;
+pub mod interval;
+pub mod materials;
+pub mod objects;
+pub mod onb;
+pub mod pdf;
+pub mod random;
+pub mod ray;
+pub mod scenes;
+pub mod spectrum;
+pub mod textures;
+pub mod wavelength;
+
+/// Rendering options struct
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct RenderOpts {
+ /// Width of the render in pixels
+ pub width: u32,
+ /// Height of the render in pixels
+ pub height: u32,
+ /// Samples per pixel to render for multisampling. Higher number implies higher quality.
+ pub samples: u32,
+ /// Maximum ray bounce depth. Higher number implies higher quality.
+ pub max_depth: u32,
+ /// Optionally, suppress CLI output
+ pub quiet: bool,
+ /// Experimental render mode: return a normal map only instead of doing a full path trace render.
+ pub normalmap: bool,
+}
+
+// Handy aliases for internal use
+
+/// Internal type alias: this allows the crate to easily switch between float precision without modifying a lot of files.
+pub type Float = f32;
+/// Internal helper: re-exports the pi constant as our internal [Float] type. TODO: selectable at run time instead of build time?
+pub const PI: Float = core::f32::consts::PI;
+/// Internal type alias: a nalgebra [Vector2] which is a vector with two dimensions, containing two of our internal [Float] types
+pub type Vec2 = Vector2<Float>;
+/// Internal type alias: a nalgebra [Vector3] which is a vector with three dimensions, containing three of our internal [Float] types
+pub type Vec3 = Vector3<Float>;
+/// Internal type alias: a nalgebra [Vector4] which is a vector with four dimensions, containing four of our internal [Float] types
+pub type Vec4 = Vector4<Float>;
+/// Internal type alias: a nalgebra [Unit] of a [Vector3]
+pub type Direction = Unit<Vec3>;
+/// Internal type alias: a nalgebra [Vector3]
+pub type Position = Vec3;
+/// Internal const: epsilon used for avoiding "shadow acne". This is mostly used for the initial minimum distance for ray hits after reflecting or scattering from a surface.
+pub const EPSILON_SHADOW_ACNE: Float = 0.001;
+/// Internal const: epsilon used for having a finitely-sized thickness for the bounding box of an infinitely-thin rectangle. Shouldn't be too small.
+pub const EPSILON_RECT_THICKNESS: Float = 0.000_1;
+/// Internal const: epsilon used in the hit calculation of a [`ConstantMedium`](objects::constant_medium::ConstantMedium).
+// TODO: what would be an appropriate value?
+pub const EPSILON_CONSTANT_MEDIUM: Float = 0.000_1;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +
//! Materials enable different behaviors of light on objects.
+
+use alloc::string::String;
+use core::fmt::Debug;
+use nalgebra::Unit;
+
+use crate::{hitable::HitRecord, pdf::PDF, ray::Ray, Direction, Float, Position, Vec3};
+pub mod cone_light;
+pub mod dielectric;
+pub mod diffuse_light;
+pub mod dispersive;
+#[cfg(feature = "gl_tf")]
+pub mod gltf;
+pub mod isotropic;
+pub mod lambertian;
+pub mod metal;
+
+pub use cone_light::*;
+pub use dielectric::*;
+pub use diffuse_light::*;
+pub use dispersive::*;
+use enum_dispatch::enum_dispatch;
+pub use isotropic::*;
+pub use lambertian::*;
+pub use metal::*;
+use palette::{white_point::E, Xyz};
+use rand::prelude::SmallRng;
+
+/// Initialization structure for a `Material`. Either contains a `Material` by itself, or a String `name` to be found in a shared material list.
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(untagged))]
+pub enum MaterialInit {
+ /// Name of the shared material
+ Shared(String),
+ /// Owned material structure
+ Owned(Material),
+}
+
+impl Default for MaterialInit {
+ fn default() -> Self {
+ Self::Shared(String::new())
+ }
+}
+
+/// A `Material` that can be referred to by name for reuse across multiple `Object`s
+#[derive(Default, Debug, Clone)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct SharedMaterial {
+ /// Name of the shared material
+ pub name: String,
+ /// The shared material itself
+ #[cfg_attr(feature = "serde-derive", serde(flatten))]
+ pub material: Material,
+}
+
+#[enum_dispatch]
+/// Trait for materials. Requires three function implementations: `scatter`, `scattering_pdf`, and `emit`.
+pub trait MaterialTrait: Debug {
+ /// Given a ray and a hitrecord, return the possible `ScatterRecord`.
+ fn scatter(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord,
+ rng: &mut SmallRng,
+ ) -> Option<ScatterRecord>;
+
+ /// TODO: explain
+ fn scattering_pdf(
+ &self,
+ hit_record: &HitRecord,
+ scattered: &Ray,
+ rng: &mut SmallRng,
+ ) -> Option<Float>;
+
+ /// Returns the emissivity of the material at the given position. Defaults to black as most materials don't emit - override when needed.
+ fn emit(
+ &self,
+ _ray: &Ray,
+ _hit_record: &HitRecord,
+ _u: Float,
+ _v: Float,
+ _position: Position,
+ ) -> Xyz<E> {
+ Xyz::new(0.0, 0.0, 0.0)
+ }
+}
+
+#[enum_dispatch(MaterialTrait)]
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(tag = "kind"))]
+/// A material enum. TODO: for ideal clean abstraction, this should be a trait. However, that comes with some additional considerations, including e.g. performance.
+pub enum Material {
+ /// Dielectric material
+ Dielectric(Dielectric),
+ /// Dispersive material
+ Dispersive(Dispersive),
+ /// Lambertian material
+ Lambertian(Lambertian),
+ /// ConeLight material
+ ConeLight(ConeLight),
+ /// DiffuseLight material
+ DiffuseLight(DiffuseLight),
+ /// Metal material
+ Metal(Metal),
+ /// Isotropic material
+ Isotropic(Isotropic),
+}
+
+impl Default for Material {
+ fn default() -> Self {
+ Self::Lambertian(Lambertian::default())
+ }
+}
+
+#[derive(Debug, Clone)]
+/// Enum for the types of materials: Diffuse and Specular (i.e., matte and shiny)
+pub enum MaterialType {
+ /// A matte material that does not reflect rays
+ Diffuse,
+ /// A shiny material that reflects some rays
+ Specular,
+}
+
+#[derive(Debug, Clone)]
+/// A record of an scattering event of a [Ray] on a [Material].
+pub struct ScatterRecord<'ray> {
+ /// The material type that was scattered on
+ pub material_type: MaterialType,
+ /// Direction of a generated specular ray
+ pub specular_ray: Option<Ray>,
+ /// Current color to take into account when following the scattered ray for futher iterations
+ pub attenuation: Xyz<E>,
+ /// Probability density function to use with the [`ScatterRecord`].
+ // TODO: understand & explain
+ pub pdf_ptr: PDF<'ray>,
+}
+
+// TODO: are these up to date / correct?
+
+#[must_use]
+fn reflect(vector: Direction, normal: Direction) -> Direction {
+ let v: Vec3 = *vector - 2.0 * vector.dot(&normal) * *normal;
+ Unit::new_normalize(v)
+}
+
+#[must_use]
+fn refract(vector: Direction, normal: Direction, refraction_ratio: Float) -> Direction {
+ let cos_theta: Float = -vector.dot(&normal);
+ let cos_theta = cos_theta.min(1.0); // Clamp
+ let r_out_parallel: Vec3 = refraction_ratio * (*vector + cos_theta * *normal);
+ let r_out_perp: Vec3 = -(1.0 - r_out_parallel.norm_squared()).sqrt() * *normal;
+ Unit::new_normalize(r_out_parallel + r_out_perp)
+}
+
+#[must_use]
+fn schlick(cosine: Float, refractive_index: Float) -> Float {
+ let r0 = (1.0 - refractive_index) / (1.0 + refractive_index);
+ let r0 = r0 * r0;
+ r0 + (1.0 - r0) * ((1.0 - cosine).powf(5.0))
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +
//! A cone light material.
+
+use super::{MaterialTrait, ScatterRecord};
+use crate::{
+ hitable::HitRecord,
+ ray::Ray,
+ textures::{SolidColor, Texture, TextureTrait},
+ Float, Position,
+};
+use palette::{white_point::E, Xyz};
+use rand::prelude::SmallRng;
+
+/// A cone light material. The material emits light if the incoming ray is within a certain amount of degrees from the surface normal.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct ConeLight {
+ spread: Float,
+ emit: Texture,
+}
+
+impl Default for ConeLight {
+ /// Creates a new [`ConeLight`] with white light at intensity `100.0` and a spread of 10 degrees.
+ fn default() -> Self {
+ ConeLight {
+ spread: 10.0,
+ emit: Texture::SolidColor(SolidColor::new(Xyz::new(100.0, 100.0, 100.0))),
+ }
+ }
+}
+
+impl MaterialTrait for ConeLight {
+ /// Scatter method for the [`ConeLight`] material. Always returns `None`, as diffuse light does not scatter.
+ #[must_use]
+ fn scatter(
+ &self,
+ _ray: &Ray,
+ _hit_record: &HitRecord,
+ _rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ None
+ }
+
+ /// Scattering probability density function for the [`ConeLight`] material. Always returns 0, as diffuse light does not scatter.
+ #[must_use]
+ fn scattering_pdf(
+ &self,
+ _hit_record: &HitRecord,
+ _scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ None
+ }
+
+ /// Emission function for [`ConeLight`]. If the given [`HitRecord`] has been hit on the `front_face`, emit a color based on the texture and surface coordinates. Otherwise, emit pure black.
+ #[must_use]
+ fn emit(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord,
+ u: Float,
+ v: Float,
+ position: Position,
+ ) -> Xyz<E> {
+ // If we don't hit the front face, return black
+ if !hit_record.front_face {
+ return Xyz::new(0.0, 0.0, 0.0);
+ }
+
+ // We have hit the front. Calculate the angle of incidence
+ let spread_radians = self.spread.to_radians();
+ let angle = (-ray.direction.dot(&hit_record.normal)
+ / (ray.direction.magnitude() * hit_record.normal.magnitude()))
+ .acos();
+
+ let emit = self.emit.color(u, v, position);
+ if angle <= spread_radians {
+ emit
+ } else {
+ // Make sure that the front face of the lamp is tinted, even outside the main lighting angle
+ let (r, g, b) = emit.into_components();
+ let scaling_factor = r.max(g).max(b);
+ if scaling_factor > 1.0 {
+ emit / scaling_factor
+ } else {
+ emit
+ }
+ }
+ }
+}
+
+impl ConeLight {
+ /// Creates a new [`ConeLight`] material with the given [Texture].
+ #[must_use]
+ pub fn new(spread: Float, emit: impl Into<Texture>) -> Self {
+ ConeLight {
+ spread,
+ emit: emit.into(),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +
//! A dielectric material. This resembles glass and other transparent and reflective materials.
+
+use super::{reflect, refract, schlick, MaterialTrait, MaterialType, ScatterRecord};
+use crate::{
+ hitable::HitRecord,
+ pdf::{ZeroPDF, PDF},
+ ray::Ray,
+ Direction, Float,
+};
+use palette::{white_point::E, Xyz};
+use rand::rngs::SmallRng;
+use rand::Rng;
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// A dielectric material. This resembles glass and other transparent and reflective materials.
+pub struct Dielectric {
+ /// Refractive index of the material. Used for calculating the new direction of a ray when entering the material at an angle. Follows Snell's law of refraction. Default value: 1.5, based on typical window glass.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_index"))]
+ pub refractive_index: Float,
+ /// Color of the material. Used for colorizing the rays. Default value: [`(1.0, 1.0, 1.0)`], producing a fully transparent, clear glass.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_color"))]
+ pub color: Xyz<E>,
+}
+
+fn default_index() -> Float {
+ 1.5
+}
+
+fn default_color() -> Xyz<E> {
+ Xyz::new(100.0, 100.0, 100.0)
+}
+
+impl MaterialTrait for Dielectric {
+ /// Scatter method for the Dielectric material. Given a `ray` and a `hit_record`, evaluate a [`ScatterRecord`] based on possible reflection or refraction.
+ #[must_use]
+ fn scatter(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord,
+ rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ let refraction_ratio: Float = if hit_record.front_face {
+ 1.0 / self.refractive_index
+ } else {
+ self.refractive_index
+ };
+
+ let direction: Direction = ray.direction;
+ let cos_theta: Float = (-direction.dot(&hit_record.normal)).min(1.0);
+ let sin_theta: Float = (1.0 - cos_theta * cos_theta).sqrt();
+ let specular_direction: Direction = if refraction_ratio * sin_theta > 1.0 {
+ reflect(direction, hit_record.normal)
+ } else {
+ let reflect_probability: Float = schlick(cos_theta, refraction_ratio);
+ if rng.gen::<Float>() < reflect_probability {
+ reflect(direction, hit_record.normal)
+ } else {
+ // Refracted
+ refract(direction, hit_record.normal, refraction_ratio)
+ }
+ };
+ let specular_ray = Ray {
+ origin: hit_record.position,
+ direction: specular_direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ };
+
+ Some(ScatterRecord {
+ material_type: MaterialType::Specular,
+ specular_ray: Some(specular_ray),
+ attenuation: self.color,
+ pdf_ptr: PDF::ZeroPDF(ZeroPDF::new()), //TODO: ugly hack due to nullptr in original tutorial
+ })
+ }
+
+ /// Scattering probability density function for Dielectric material. NOTE: not implemented!
+ #[must_use]
+ fn scattering_pdf(
+ &self,
+ _hit_record: &HitRecord,
+ _scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ None // TODO: should a dielectric material scatter? how much?
+ }
+}
+
+impl Dielectric {
+ /// Creates a new [Dielectric] material with the given refractive index and color.
+ #[must_use]
+ pub fn new(refractive_index: Float, color: impl Into<Xyz<E>>) -> Self {
+ Dielectric {
+ refractive_index,
+ color: color.into(),
+ }
+ }
+}
+
+impl Default for Dielectric {
+ fn default() -> Self {
+ Dielectric::new(default_index(), default_color())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +
//! A diffuse light material.
+
+use super::{MaterialTrait, ScatterRecord};
+use crate::{
+ hitable::HitRecord,
+ ray::Ray,
+ textures::{SolidColor, Texture, TextureTrait},
+ Float, Position,
+};
+use palette::{white_point::E, Xyz};
+use rand::prelude::SmallRng;
+
+/// A diffuse light material. On this material, rays never scatter - the material always emits a color based on its texture.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct DiffuseLight {
+ emit: Texture,
+}
+
+impl Default for DiffuseLight {
+ /// Creates a new [`DiffuseLight`] with white light at intensity `100.0`
+ fn default() -> Self {
+ DiffuseLight {
+ emit: Texture::SolidColor(SolidColor::new(Xyz::new(100.0, 100.0, 100.0))),
+ }
+ }
+}
+
+impl MaterialTrait for DiffuseLight {
+ /// Scatter method for the [`DiffuseLight`] material. Always returns `None`, as diffuse light does not scatter.
+ #[must_use]
+ fn scatter(
+ &self,
+ _ray: &Ray,
+ _hit_record: &HitRecord,
+ _rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ None
+ }
+
+ /// Scattering probability density function for the [`DiffuseLight`] material. Always returns 0, as diffuse light does not scatter.
+ #[must_use]
+ fn scattering_pdf(
+ &self,
+ _hit_record: &HitRecord,
+ _scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ None
+ }
+
+ /// Emission function for [`DiffuseLight`]. If the given [`HitRecord`] has been hit on the `front_face`, emit a color based on the texture and surface coordinates. Otherwise, emit pure black.
+ #[must_use]
+ fn emit(
+ &self,
+ _ray: &Ray,
+ hit_record: &HitRecord,
+ u: Float,
+ v: Float,
+ position: Position,
+ ) -> Xyz<E> {
+ if hit_record.front_face {
+ self.emit.color(u, v, position)
+ } else {
+ Xyz::new(0.0, 0.0, 0.0)
+ }
+ }
+}
+
+impl DiffuseLight {
+ /// Creates a new [`DiffuseLight`] material with the given [Texture].
+ #[must_use]
+ pub fn new(emission: impl Into<Texture>) -> Self {
+ DiffuseLight {
+ emit: emission.into(),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +
//! Dispersive material.
+//! Based on [Cauchy's equation](https://en.wikipedia.org/wiki/Cauchy%27s_equation)
+
+/*
+Material A B (μm2)
+Fused silica 1.4580 0.00354
+Borosilicate glass BK7 1.5046 0.00420
+Hard crown glass K5 1.5220 0.00459
+Barium crown glass BaK4 1.5690 0.00531
+Barium flint glass BaF10 1.6700 0.00743
+Dense flint glass SF10 1.7280 0.01342
+*/
+
+// TODO: consider other options, e.g. Sellmeier https://en.wikipedia.org/wiki/Sellmeier_equation
+
+use palette::Xyz;
+use rand::{rngs::SmallRng, Rng};
+
+use crate::{
+ hitable::HitRecord,
+ pdf::{ZeroPDF, PDF},
+ ray::Ray,
+ wavelength::Wavelength,
+ Direction, Float,
+};
+
+use super::{reflect, refract, schlick, MaterialTrait, MaterialType, ScatterRecord};
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// A dispersive glass material.
+pub struct Dispersive {
+ /// Cauchy coefficient A of the material
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_a"))]
+ pub cauchy_a: Float,
+ /// Cauchy coefficient B of the material
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_b"))]
+ pub cauchy_b: Float,
+}
+
+fn default_a() -> Float {
+ 1.5046
+}
+
+fn default_b() -> Float {
+ 0.00420
+}
+
+// TODO: less precision loss?
+#[allow(clippy::cast_precision_loss)]
+impl Dispersive {
+ /// Creates a new [Dispersive] material with the given Cauchy equation constants.
+ #[must_use]
+ pub fn new(cauchy_a: Float, cauchy_b: Float) -> Self {
+ Dispersive { cauchy_a, cauchy_b }
+ }
+
+ /// Calculates the refractive index of the material for the given wavelength
+ #[must_use]
+ pub fn refractive_index(&self, wavelength: Wavelength) -> Float {
+ let wave_micros = wavelength as Float / 1000.0;
+ self.cauchy_a + (self.cauchy_b / (wave_micros * wave_micros))
+ }
+}
+
+impl Default for Dispersive {
+ fn default() -> Self {
+ Dispersive::new(default_a(), default_b())
+ }
+}
+
+impl MaterialTrait for Dispersive {
+ fn scatter(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord,
+ rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ // Calculate refractive index based on the wavelength of the incoming material
+ // TODO: colored dispersive glass?
+ let attenuation = Xyz::new(1.0, 1.0, 1.0);
+ let refractive_index = self.refractive_index(ray.wavelength);
+ let refraction_ratio: Float = if hit_record.front_face {
+ 1.0 / refractive_index
+ } else {
+ refractive_index
+ };
+
+ // Copied from Dielectric, is this correct?
+ let direction: Direction = ray.direction;
+ let cos_theta: Float = (-direction.dot(&hit_record.normal)).min(1.0);
+ let sin_theta: Float = (1.0 - cos_theta * cos_theta).sqrt();
+ let specular_direction: Direction = if refraction_ratio * sin_theta > 1.0 {
+ reflect(direction, hit_record.normal)
+ } else {
+ let reflect_probability: Float = schlick(cos_theta, refraction_ratio);
+ if rng.gen::<Float>() < reflect_probability {
+ reflect(direction, hit_record.normal)
+ } else {
+ // Refracted
+ refract(direction, hit_record.normal, refraction_ratio)
+ }
+ };
+ let specular_ray = Ray {
+ origin: hit_record.position,
+ direction: specular_direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ };
+
+ Some(ScatterRecord {
+ material_type: MaterialType::Specular,
+ specular_ray: Some(specular_ray),
+ attenuation,
+ pdf_ptr: PDF::ZeroPDF(ZeroPDF::new()), //TODO: ugly hack due to nullptr in original tutorial
+ })
+ // End copied
+ }
+
+ fn scattering_pdf(
+ &self,
+ _hit_record: &HitRecord,
+ _scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ None // TODO: should a dispersive material scatter? how much?
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +
//! Wrapper for GLTF materials.
+
+#![allow(clippy::pedantic)]
+
+#[cfg(feature = "gl_tf")]
+use gltf::{image::Data, Material};
+use nalgebra::Unit;
+use palette::{
+ chromatic_adaptation::AdaptInto, convert::IntoColorUnclamped, white_point::E, LinSrgb, Srgb,
+ Srgba, Xyz,
+};
+use rand::rngs::SmallRng;
+
+use crate::{
+ hitable::HitRecord,
+ pdf::{ZeroPDF, PDF},
+ random::random_unit_vector,
+ ray::Ray,
+ Direction, Float, Vec2, Vec3, Vec4, PI,
+};
+
+use super::{reflect, MaterialTrait, MaterialType, ScatterRecord};
+
+#[derive(Debug, Clone)]
+// #[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// GLTF Material wrapper type
+pub struct GLTFMaterial<'scene> {
+ material: &'scene Material<'scene>,
+ tex_coords: [[Float; 2]; 3],
+ images: &'scene [Data],
+ tangents: Option<[Vec3; 3]>,
+ normals: Option<[Vec3; 3]>,
+ bitangents: Option<[Vec3; 3]>,
+}
+
+impl<'scene> Default for GLTFMaterial<'scene> {
+ fn default() -> Self {
+ todo!()
+ }
+}
+
+impl<'scene> GLTFMaterial<'scene> {
+ /// Initialize a new GLTF material wrapper
+ #[must_use]
+ pub fn new(
+ material: &'scene Material,
+ tex_coords: [[Float; 2]; 3],
+ normals: Option<[[Float; 3]; 3]>,
+ tangents: Option<[[Float; 4]; 3]>,
+ images: &'scene [Data],
+ ) -> Self {
+ let normals: Option<[Vec3; 3]> = normals.map(|ns| ns.map(Vec3::from));
+ let tangents: Option<[Vec4; 3]> = tangents.map(|ns| ns.map(Vec4::from));
+ let ws: Option<[Float; 3]> = tangents.map(|ts| ts.map(|t| t[3]));
+ let tangents: Option<[Vec3; 3]> = tangents.map(|ts| ts.map(|t| Vec4::xyz(&t)));
+ // TODO: fix this horrendous mess
+ let bitangents = if let Some(normals) = normals {
+ if let Some(tangents) = tangents {
+ ws.map(|ws| {
+ [
+ normals[0].cross(&tangents[0]) * ws[0],
+ normals[1].cross(&tangents[1]) * ws[1],
+ normals[2].cross(&tangents[2]) * ws[2],
+ ]
+ })
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ Self {
+ material,
+ tex_coords,
+ normals,
+ tangents,
+ bitangents,
+ images,
+ }
+ }
+}
+
+impl<'scene> MaterialTrait for GLTFMaterial<'scene> {
+ fn scatter(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord,
+ rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ let base_color: LinSrgb = self.sample_base_color(hit_record);
+ let emissive: LinSrgb = self.sample_emissive(hit_record).into_color_unclamped();
+ let (metalness, roughness) = self.sample_metalness_roughness(hit_record);
+ let normal: Direction = self.sample_normal(hit_record);
+ let occlusion: Float = self.sample_occlusion(hit_record);
+
+ // TODO: full color model
+ let attenuation: LinSrgb = emissive + base_color * occlusion;
+ let attenuation: Xyz<E> = attenuation.adapt_into();
+
+ // TODO: better metalness model
+ if metalness > 0.0 {
+ // TODO: borrowed from metal, should this be different?
+ let reflected: Direction = reflect(ray.direction, normal);
+ let direction = *reflected + roughness * *random_unit_vector(rng);
+ let direction = Unit::new_normalize(direction);
+
+ Some(ScatterRecord {
+ specular_ray: Some(Ray {
+ origin: hit_record.position,
+ direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ }),
+ attenuation,
+ material_type: MaterialType::Specular,
+ pdf_ptr: PDF::ZeroPDF(ZeroPDF::new()),
+ })
+ } else {
+ Some(ScatterRecord {
+ specular_ray: None,
+ attenuation,
+ material_type: MaterialType::Diffuse,
+ pdf_ptr: PDF::ZeroPDF(ZeroPDF::new()),
+ })
+ }
+ }
+
+ fn scattering_pdf(
+ &self,
+ hit_record: &HitRecord,
+ scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ // TODO: what should this be for GLTF materials?
+ // Borrowed from Lambertian
+ let cosine = hit_record.normal.dot(&scattered.direction.normalize());
+ if cosine < 0.0 {
+ None
+ } else {
+ Some(cosine / PI)
+ }
+ }
+}
+
+impl<'scene> GLTFMaterial<'scene> {
+ fn sample_base_color(&self, hit_record: &HitRecord) -> LinSrgb {
+ let base_color_texture = self
+ .material
+ .pbr_metallic_roughness()
+ .base_color_texture()
+ .map(|info| &self.images[info.texture().source().index()]);
+ // TODO: proper fully correct coloring
+ let base_color = match &base_color_texture {
+ Some(texture) => {
+ let (x, y) = self.sample_texture_coords(hit_record, texture);
+ get_color_srgb(texture, x, y)
+ }
+ None => Srgb::new(1.0, 1.0, 1.0),
+ };
+ let base_color_factor: Srgba = self
+ .material
+ .pbr_metallic_roughness()
+ .base_color_factor()
+ .into();
+ let base_color_factor: Srgb = base_color_factor.into_color_unclamped();
+
+ (base_color * base_color_factor).into_color_unclamped()
+ }
+
+ fn sample_emissive(&self, hit_record: &HitRecord) -> Srgb {
+ let emissive_texture = self
+ .material
+ .emissive_texture()
+ .map(|info| &self.images[info.texture().source().index()]);
+ // TODO: proper fully correct coloring
+ let emissive = match &emissive_texture {
+ Some(texture) => {
+ let (x, y) = self.sample_texture_coords(hit_record, texture);
+ get_color_srgb(texture, x, y)
+ }
+ None => Srgb::new(1.0, 1.0, 1.0),
+ };
+ let emissive_factor: Srgb = self.material.emissive_factor().into();
+
+ (emissive * emissive_factor).into_color_unclamped()
+ }
+
+ fn sample_metalness_roughness(&self, hit_record: &HitRecord) -> (Float, Float) {
+ let metallic_roughness_texture = self
+ .material
+ .pbr_metallic_roughness()
+ .metallic_roughness_texture()
+ .map(|info| &self.images[info.texture().source().index()]);
+ let (metalness, roughness) = match &metallic_roughness_texture {
+ Some(texture) => {
+ let (x, y) = self.sample_texture_coords(hit_record, texture);
+ let sampled_color = get_color_linsrgb(texture, x, y);
+ let roughness = sampled_color.green;
+ let metalness = sampled_color.blue;
+ (metalness, roughness)
+ }
+ None => (1.0, 1.0),
+ };
+ let metalness = metalness * self.material.pbr_metallic_roughness().metallic_factor();
+ let roughness = roughness * self.material.pbr_metallic_roughness().roughness_factor();
+ (metalness, roughness)
+ }
+
+ fn sample_occlusion(&self, hit_record: &HitRecord) -> Float {
+ let occlusion_texture = self
+ .material
+ .occlusion_texture()
+ .map(|info| &self.images[info.texture().source().index()]);
+
+ match &occlusion_texture {
+ Some(texture) => {
+ let (x, y) = self.sample_texture_coords(hit_record, texture);
+ let sampled_color = get_color_linsrgb(texture, x, y);
+ // Only the red channel is taken into account
+ sampled_color.red
+ }
+ None => 1.0,
+ }
+ }
+
+ fn sample_normal(&self, hit_record: &HitRecord) -> Direction {
+ let Some(normals) = self.normals else {
+ // If we don't have normals, early return with the triangle normal
+ return hit_record.normal;
+ };
+
+ let Some(tangents) = self.tangents else {
+ // If we don't have tangents, early return with the triangle normal
+ // TODO: compute normals here or at construction time as per gltf spec
+ return hit_record.normal;
+ };
+
+ let Some(bitangents) = self.bitangents else {
+ return hit_record.normal;
+ };
+
+ let normal_texture = self
+ .material
+ .normal_texture()
+ .map(|info| &self.images[info.texture().source().index()]);
+ let texture_normal = match &normal_texture {
+ Some(texture) => {
+ let (x, y) = self.sample_texture_coords(hit_record, texture);
+ let sampled_color = get_color_linsrgb(texture, x, y);
+ // Convert from Color to Vec 0..1, scale and move to -1..1
+ let (r, g, b) = sampled_color.into_components();
+ let normal: Vec3 = Vec3::new(r, g, b) * 2.0 - Vec3::new(1.0, 1.0, 1.0);
+ normal.normalize()
+ }
+ // If we don't have a normal texture, early return with the triangle normal
+ None => return hit_record.normal,
+ };
+
+ // Barycentric coordinates and interpolation on the triangle surface
+ let normal = (hit_record.u * normals[1]
+ + hit_record.v * normals[2]
+ + (1.0 - hit_record.u - hit_record.v) * normals[0])
+ .normalize();
+ let tangent = (hit_record.u * tangents[1]
+ + hit_record.v * tangents[2]
+ + (1.0 - hit_record.u - hit_record.v) * tangents[0])
+ .normalize();
+ let bitangent = (hit_record.u * bitangents[1]
+ + hit_record.v * bitangents[2]
+ + (1.0 - hit_record.u - hit_record.v) * bitangents[0])
+ .normalize();
+
+ let matrix: nalgebra::Matrix3<Float> =
+ nalgebra::Matrix3::from_columns(&[tangent, bitangent, normal]);
+
+ // Transform the texture normal from tangent space to world space
+ Unit::new_normalize(matrix * texture_normal)
+ }
+
+ /// Find the correct texture coordinates in pixel space
+ fn sample_texture_coords(&self, hit_record: &HitRecord, image: &Data) -> (usize, usize) {
+ // Full triangle coordinates on the full texture file
+ let tex_corner0: Vec2 = Vec2::from(self.tex_coords[0]);
+ let tex_corner1: Vec2 = Vec2::from(self.tex_coords[1]);
+ let tex_corner2: Vec2 = Vec2::from(self.tex_coords[2]);
+ // Side vectors on the texture triangle
+ let tex_u: Vec2 = tex_corner1 - tex_corner0;
+ let tex_v: Vec2 = tex_corner2 - tex_corner0;
+ // Specific surface space coordinate for hit point
+ let coord: Vec2 = tex_corner0 + hit_record.u * tex_u + hit_record.v * tex_v;
+ let x = coord[0];
+ let y = coord[1];
+ // TODO: other wrapping modes, this is "repeat"
+ let x = if x < 0.0 { 1.0 + x.fract() } else { x.fract() };
+ let y = if y < 0.0 { 1.0 + y.fract() } else { y.fract() };
+ // Pixel space coordinates on the texture
+ let x = x * (image.width as Float);
+ let y = y * ((image.height - 1) as Float); // TODO: fix overflows better
+
+ // Cast
+ let x = x.floor() as usize;
+ let y = y.floor() as usize;
+ (x, y)
+ }
+}
+
+/// Given a reference to a texture and pixel space coordinates, returns the raw byte triple `(r,g,b)`
+fn sample_texture_raw(texture: &&Data, x: usize, y: usize) -> (u8, u8, u8) {
+ let index = match texture.format {
+ gltf::image::Format::R8G8B8 => 3 * (x + texture.width as usize * y),
+ gltf::image::Format::R8G8B8A8 => 4 * (x + texture.width as usize * y),
+ _ => todo!("Unsupported gltf::image::Format"),
+ };
+ let r = texture.pixels[index];
+ let g = texture.pixels[index + 1];
+ let b = texture.pixels[index + 2];
+ (r, g, b)
+}
+
+/// Given a reference to a texture and pixel space coordinates, returns the color at that pixel, sRGB with gamma.
+fn get_color_srgb(texture: &&Data, x: usize, y: usize) -> Srgb {
+ let (r, g, b) = sample_texture_raw(texture, x, y);
+ let color: Srgb<u8> = Srgb::from_components((r, g, b));
+ color.into_format()
+}
+
+/// Given a reference to a texture and pixel space coordinates, returns the color at that pixel, linear sRGB
+fn get_color_linsrgb(texture: &&Data, x: usize, y: usize) -> LinSrgb {
+ let (r, g, b) = sample_texture_raw(texture, x, y);
+ let color: LinSrgb<u8> = LinSrgb::from_components((r, g, b));
+ color.into_format()
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +
//! Isotropic material.
+
+use super::{MaterialTrait, MaterialType, ScatterRecord};
+use crate::{
+ hitable::HitRecord,
+ pdf::{SpherePDF, PDF},
+ ray::Ray,
+ textures::{Texture, TextureTrait},
+ Float, PI,
+};
+use rand::prelude::SmallRng;
+
+#[derive(Debug, Clone, Default)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// Isotropic material. Used in [`ConstantMedium`](crate::objects::constant_medium). TODO: understand this!
+pub struct Isotropic {
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ albedo: Texture,
+}
+
+impl MaterialTrait for Isotropic {
+ /// Returns a [`ScatterRecord`] based on the [`HitRecord`] coordinates and the given [Texture], or [None] if the ray did not hit the material.
+ #[must_use]
+ fn scatter(
+ &self,
+ _ray: &Ray,
+ hit_record: &HitRecord,
+ _rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ let albedo = self
+ .albedo
+ .color(hit_record.u, hit_record.v, hit_record.position);
+
+ Some(ScatterRecord {
+ material_type: MaterialType::Diffuse,
+ specular_ray: None,
+ attenuation: albedo,
+ pdf_ptr: PDF::SpherePDF(SpherePDF::new()),
+ })
+ }
+
+ /// Returns the scattering probability density function for the [Isotropic] material
+ #[must_use]
+ fn scattering_pdf(
+ &self,
+ _hit_record: &HitRecord,
+ _scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ Some(1.0 / (4.0 * PI))
+ }
+}
+
+impl Isotropic {
+ /// Creates a new [Isotropic] material with an albedo of the given [Texture].
+ #[must_use]
+ pub fn new(emission: impl Into<Texture>) -> Self {
+ Isotropic {
+ albedo: emission.into(),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +
//! Lambertian material. This is the default material with a smooth, matte surface.
+
+use super::{MaterialTrait, MaterialType, ScatterRecord};
+use crate::{
+ hitable::HitRecord,
+ pdf::{CosinePDF, PDF},
+ ray::Ray,
+ textures::{Texture, TextureTrait},
+ Float, PI,
+};
+use rand::prelude::SmallRng;
+
+#[derive(Clone, Debug, Default)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// Lambertian material. This is the default material with a smooth, matte surface.
+pub struct Lambertian {
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ albedo: Texture,
+}
+
+impl MaterialTrait for Lambertian {
+ /// Returns None, if ray is absorbed. Otherwise, returns a ray, albedo of what was hit, and (?) a value used for probability density function based sampling
+ #[must_use]
+ fn scatter(
+ &self,
+ _ray: &Ray,
+ hit_record: &HitRecord,
+ _rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ Some(ScatterRecord {
+ material_type: MaterialType::Diffuse,
+ specular_ray: None,
+ attenuation: self
+ .albedo
+ .color(hit_record.u, hit_record.v, hit_record.position),
+ pdf_ptr: PDF::CosinePDF(CosinePDF::new(hit_record.normal)),
+ })
+ }
+
+ /// Returns the scattering probability density function for the [Lambertian] material. TODO: explain the math
+ #[must_use]
+ fn scattering_pdf(
+ &self,
+ hit_record: &HitRecord,
+ scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ // TODO: explain the math
+ let cosine = hit_record.normal.dot(&scattered.direction.normalize());
+ if cosine < 0.0 {
+ None
+ } else {
+ Some(cosine / PI)
+ }
+ }
+}
+
+impl Lambertian {
+ /// Creates a new instance of the [Lambertian] material with an albedo of the given [Texture].
+ #[must_use]
+ pub fn new(albedo: impl Into<Texture>) -> Self {
+ Lambertian {
+ albedo: albedo.into(),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +
//! A metal material.
+
+use super::{reflect, MaterialTrait, MaterialType, ScatterRecord};
+use crate::{
+ hitable::HitRecord,
+ pdf::{ZeroPDF, PDF},
+ random::random_unit_vector,
+ ray::Ray,
+ textures::{Texture, TextureTrait},
+ Direction, Float,
+};
+use nalgebra::Unit;
+use rand::prelude::SmallRng;
+
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// A metal material. The amount of reflection can be adjusted with the `fuzz` parameter.
+pub struct Metal {
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ albedo: Texture,
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ fuzz: Float,
+}
+
+impl MaterialTrait for Metal {
+ /// Scatter function for the [Metal] material. Metal always reflects, and a specular ray is calculated with some randomness adjusted by the `fuzz` factor. This means the metal can be made more shiny or more matte. The returned [`ScatterRecord`] will have a probability density function of [`ZeroPDF`] and material type of [`MaterialType::Specular`]
+ #[must_use]
+ fn scatter(
+ &self,
+ ray: &Ray,
+ hit_record: &HitRecord,
+ rng: &mut SmallRng,
+ ) -> Option<ScatterRecord> {
+ let reflected: Direction = reflect(ray.direction, hit_record.normal);
+ let direction = *reflected + self.fuzz * *random_unit_vector(rng);
+ let direction = Unit::new_normalize(direction);
+ Some(ScatterRecord {
+ specular_ray: Some(Ray {
+ origin: hit_record.position,
+ direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ }),
+ attenuation: self
+ .albedo
+ .color(hit_record.u, hit_record.v, hit_record.position),
+ material_type: MaterialType::Specular,
+ pdf_ptr: PDF::ZeroPDF(ZeroPDF::new()),
+ })
+ }
+
+ /// Scattering probability density function for [Metal]. Always returns zero. TODO: why?
+ #[must_use]
+ fn scattering_pdf(
+ &self,
+ _hit_record: &HitRecord,
+ _scattered: &Ray,
+ _rng: &mut SmallRng,
+ ) -> Option<Float> {
+ None // TODO: why does metal never scatter? should it scatter if fuzzy?
+ }
+}
+
+impl Metal {
+ /// Creates a new [Metal] material with the albedo of the given [Texture] and a smoothness-roughness factor specified by `fuzz` parameter.
+ #[must_use]
+ pub fn new(albedo: impl Into<Texture>, fuzz: Float) -> Self {
+ Metal {
+ albedo: albedo.into(),
+ fuzz: fuzz.min(1.0),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +
//! Various literal objects and meta-object utilities for creating content in [Scenes](crate::scenes::Scene).
+
+use crate::{
+ bvhnode::BVHNode,
+ hitable::Hitable,
+ materials::{Material, MaterialInit, SharedMaterial},
+ Box,
+};
+
+pub mod boxy; // avoid keyword
+pub mod constant_medium;
+#[cfg(feature = "gl_tf")]
+pub mod gltf;
+pub mod moving_sphere;
+pub mod quad;
+pub mod rotate;
+pub mod sphere;
+#[cfg(feature = "stl")]
+pub mod stl;
+pub mod translate;
+pub mod triangle;
+
+#[cfg(feature = "gl_tf")]
+pub use self::gltf::*;
+use alloc::vec::Vec;
+pub use boxy::*; // avoid keyword
+pub use constant_medium::*;
+pub use moving_sphere::*;
+pub use quad::*;
+pub use rotate::*;
+pub use sphere::*;
+#[cfg(feature = "stl")]
+pub use stl::*;
+#[cfg(feature = "traces")]
+use tracing::warn;
+pub use translate::*;
+pub use triangle::*;
+
+// TODO: This is kind of an ugly hack, having to double-implement various structures to have an external representation vs internal representation. How could this be made cleaner?
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// A list of objects. Allows multiple objects to be used e.g. in a Rotate or Translate object as the target.
+pub struct ObjectList {
+ /// Priority
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// The encased [Object] list
+ pub objects: Vec<Object>,
+}
+
+#[derive(Clone, Debug)]
+/// An object enum. TODO: for ideal clean abstraction, this should be a trait. However, that comes with some additional considerations, including e.g. performance.
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(tag = "kind"))]
+pub enum Object {
+ /// Boxy object initializer
+ Boxy(BoxyInit),
+ /// `ConstantMedium` object initializer
+ ConstantMedium(ConstantMediumInit),
+ /// `MovingSphere` object initializer
+ MovingSphere(MovingSphereInit),
+ /// `ObjectList` object initializer
+ ObjectList(ObjectList),
+ /// Quad object initializer
+ Quad(QuadInit),
+ /// `RotateY` object initializer
+ RotateY(RotateInit),
+ /// Sphere object initializer
+ Sphere(SphereInit),
+ #[cfg(feature = "stl")]
+ /// STL object initializer
+ STL(STLInit),
+ #[cfg(feature = "gl_tf")]
+ /// GLTF object initializer
+ GLTF(GLTFInit),
+ /// Translate object initializer
+ Translate(TranslateInit),
+ /// Triangle object initializer
+ Triangle(TriangleInit),
+}
+
+#[must_use]
+/// Initializes an `Object` into a `Hitable`.
+pub fn object_to_hitable(obj: Object, materials: &[SharedMaterial]) -> Hitable<'_> {
+ // TODO: reduce repetition!
+
+ match obj {
+ Object::Boxy(x) => {
+ let material = initialize_material(x.material, materials);
+ Hitable::Boxy(Boxy::new(x.corner_0, x.corner_1, material))
+ }
+ Object::ConstantMedium(x) => {
+ let obj = *x.boundary;
+ let obj: Hitable = object_to_hitable(obj, materials);
+ Hitable::ConstantMedium(ConstantMedium::new(Box::new(obj), x.density, x.texture))
+ }
+ Object::MovingSphere(x) => {
+ let material = initialize_material(x.material, materials);
+ Hitable::MovingSphere(MovingSphere::new(
+ // TODO: time
+ x.center_0, x.center_1, 0.0, 1.0, x.radius, material,
+ ))
+ }
+ Object::ObjectList(x) => {
+ let objects: Vec<Hitable> = x
+ .objects
+ .iter()
+ .map(|object| -> Hitable { object_to_hitable(object.clone(), materials) })
+ .collect();
+ let bvh = BVHNode::from_list(objects, 0.0, 1.0);
+ Hitable::BVHNode(bvh)
+ }
+ Object::Quad(x) => {
+ let material = initialize_material(x.material, materials);
+ Hitable::Quad(Quad::new(x.q, x.u, x.v, material))
+ }
+ Object::RotateY(x) => {
+ let obj = *x.object;
+ let obj: Hitable = object_to_hitable(obj, materials);
+ Hitable::RotateY(RotateY::new(Box::new(obj), x.angle))
+ }
+ Object::Sphere(x) => {
+ let material = initialize_material(x.material, materials);
+ Hitable::Sphere(Sphere::new(x.center, x.radius, material))
+ }
+ #[cfg(feature = "stl")]
+ Object::STL(stl_init) => Hitable::STL(initialize_stl(stl_init, materials)),
+ #[cfg(feature = "gl_tf")]
+ Object::GLTF(x) => {
+ // TODO: time
+ Hitable::GLTF(GLTF::new(x, 0.0, 1.0))
+ }
+ Object::Translate(x) => {
+ let obj = *x.object;
+ let obj: Hitable = object_to_hitable(obj, materials);
+ Hitable::Translate(Translate::new(Box::new(obj), x.offset))
+ }
+ Object::Triangle(x) => {
+ let material = initialize_material(x.material, materials);
+ Hitable::Triangle(Triangle::new(x.q, x.u, x.v, material))
+ }
+ }
+}
+
+fn initialize_material<'scene>(
+ material_init: MaterialInit,
+ materials: &'scene [SharedMaterial],
+) -> &Material {
+ let material: &Material = match material_init {
+ MaterialInit::Owned(m) => {
+ // TODO: do not leak memory
+ let material: &'scene Material = Box::leak(Box::new(m));
+ material
+ }
+ MaterialInit::Shared(name) => {
+ // Find material by name. If the name is not found, use the default material
+ if let Some(m) = &materials.iter().find(|m| m.name == name) {
+ &m.material
+ } else {
+ #[cfg(feature = "traces")]
+ warn!(
+ "shared material `{}` not found, using default material",
+ name
+ );
+ &materials
+ .iter()
+ .find(|m| m.name.is_empty())
+ .unwrap()
+ .material
+ }
+ }
+ };
+ material
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +
//! A box or a cuboid object: a parallelepiped with six rectangular faces. Named [Boxy] to avoid clashing with [Box].
+
+use super::Quad;
+use crate::{
+ aabb::AABB,
+ hitable::{HitRecord, Hitable, HitableTrait},
+ materials::{Material, MaterialInit},
+ ray::Ray,
+ wavelength::Wavelength,
+ Box, Direction, Float, Position, Vec3,
+};
+use rand::{rngs::SmallRng, Rng};
+
+/// `BoxyInit` structure describes the necessary data for constructing a [Boxy]. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct BoxyInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// First corner for the box
+ pub corner_0: Position,
+ /// Second, opposing corner for the box
+ pub corner_1: Position,
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ /// Material used for the box
+ pub material: MaterialInit,
+}
+
+/// A box or a cuboid object: a parallelepiped with six rectangular faces. Named [Boxy] to avoid clashing with [Box].
+#[derive(Debug, Clone)]
+pub struct Boxy<'scene> {
+ sides: Box<[Hitable<'scene>; 6]>,
+ /// The material of the box
+ pub material: &'scene Material,
+ /// Axis-aligned bounding box
+ pub aabb: AABB,
+}
+
+impl<'scene> Boxy<'scene> {
+ /// Initializes a new instance of a box, given two opposing [Vec3] corners `corner_0` and `corner_1`, and a [Material] `material`.
+ #[must_use]
+ pub fn new(corner_0: Position, corner_1: Position, material: &'scene Material) -> Self {
+ // Construct the two opposite vertices with the minimum and maximum coordinates.
+ let min: Position = Position::new(
+ corner_0.x.min(corner_1.x),
+ corner_0.y.min(corner_1.y),
+ corner_0.z.min(corner_1.z),
+ );
+ let max: Position = Position::new(
+ corner_0.x.max(corner_1.x),
+ corner_0.y.max(corner_1.y),
+ corner_0.z.max(corner_1.z),
+ );
+
+ let dx: Vec3 = Vec3::new(max.x - min.x, 0.0, 0.0);
+ let dy: Vec3 = Vec3::new(0.0, max.y - min.y, 0.0);
+ let dz: Vec3 = Vec3::new(0.0, 0.0, max.z - min.z);
+
+ let sides: [Hitable; 6] = [
+ // front
+ Hitable::Quad(Quad::new(Vec3::new(min.x, min.y, max.z), dx, dy, material)),
+ // right
+ Hitable::Quad(Quad::new(Vec3::new(max.x, min.y, max.z), -dz, dy, material)),
+ // back
+ Hitable::Quad(Quad::new(Vec3::new(max.x, min.y, min.z), -dx, dy, material)),
+ // left
+ Hitable::Quad(Quad::new(Vec3::new(min.x, min.y, min.z), dz, dy, material)),
+ // top
+ Hitable::Quad(Quad::new(Vec3::new(min.x, max.y, max.z), dx, -dz, material)),
+ // bottom
+ Hitable::Quad(Quad::new(Vec3::new(min.x, min.y, min.z), dx, dz, material)),
+ ];
+ // AABB
+ let aabb = AABB::new_from_coords(corner_0, corner_1);
+
+ Boxy {
+ sides: Box::new(sides),
+ material,
+ aabb,
+ }
+ }
+}
+
+impl<'scene> HitableTrait for Boxy<'scene> {
+ /// The main `hit` function for a [Boxy]. Given a [Ray], and an interval `distance_min` and `distance_max`, returns either `None` or `Some(HitRecord)` based on whether the ray intersects with the object during that interval.
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ // start with an empty hit_record, hit all sides, return closest
+ let mut hit_record: Option<HitRecord> = None;
+ let mut closest = distance_max;
+ for hitable in &*self.sides {
+ if let Some(record) = hitable.hit(ray, distance_min, closest, rng) {
+ closest = record.distance;
+ hit_record = Some(record);
+ }
+ }
+ hit_record
+ }
+
+ /// Returns the axis-aligned bounding box [AABB] of the object.
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ /// Returns a probability density function value? // TODO: understand & explain
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ let mut sum = 0.0;
+
+ self.sides.iter().for_each(|object| {
+ sum += object.pdf_value(origin, direction, wavelength, time, rng) / 6.0;
+ });
+
+ sum
+ }
+
+ /// Returns a random point on the box
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Vec3 {
+ let index: usize = rng.gen_range(0..6);
+ self.sides[index].random(origin, rng)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +
//! `ConstantMedium` object. This should probably be a [Material] at some point, but this will do for now. This is essentially a fog with a known size, shape and density.
+
+use crate::{
+ aabb::AABB,
+ hitable::{HitRecord, Hitable, HitableTrait},
+ materials::{isotropic::Isotropic, Material},
+ random::random_unit_vector,
+ ray::Ray,
+ textures::Texture,
+ wavelength::Wavelength,
+ Box, Direction, Float, Position, EPSILON_CONSTANT_MEDIUM,
+};
+use rand::rngs::SmallRng;
+use rand::Rng;
+
+use super::Object;
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// `ConstantMediumInit` structure describes the necessary data for constructing a [`ConstantMedium`]. Used with [serde] when importing [`SceneFiles`](crate::scenes::SceneFile).
+pub struct ConstantMediumInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// The boundary object for the constant medium. This determines the size and shape of the fog object.
+ pub boundary: Box<Object>,
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_density"))]
+ /// Density of the fog. TODO: example good value range?
+ pub density: Float,
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ /// [Texture] used for the colorization of the fog.
+ pub texture: Texture,
+}
+
+#[cfg(feature = "serde-derive")]
+// TODO: does this density setting even work?
+fn default_density() -> Float {
+ 0.1
+ // 1e-9
+ // 1e9
+}
+
+#[derive(Debug, Clone)]
+/// `ConstantMedium` object. This should probably be a [Material] at some point, but this will do for now. This is essentially a fog with a known size, shape and density.
+pub struct ConstantMedium<'scene> {
+ boundary: Box<Hitable<'scene>>,
+ phase_function: Material,
+ neg_inv_density: Float,
+}
+
+impl<'scene> ConstantMedium<'scene> {
+ /// Creates a new [`ConstantMedium`] with a known size, shape and density.
+ #[must_use]
+ pub fn new(boundary: Box<Hitable<'scene>>, density: Float, texture: Texture) -> Self {
+ ConstantMedium {
+ boundary,
+ phase_function: Material::Isotropic(Isotropic::new(texture)),
+ neg_inv_density: -1.0 / density,
+ }
+ }
+}
+
+impl<'scene> HitableTrait for ConstantMedium<'scene> {
+ /// Hit function for the [`ConstantMedium`] object. Returns a [`HitRecord`] if hit. TODO: explain the math for the fog
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ // TODO: explain how the fog works.
+
+ let mut rec1 = self
+ .boundary
+ .hit(ray, Float::NEG_INFINITY, Float::INFINITY, rng)?;
+
+ let mut rec2 = self.boundary.hit(
+ ray,
+ rec1.distance + EPSILON_CONSTANT_MEDIUM,
+ Float::INFINITY,
+ rng,
+ )?;
+
+ if rec1.distance < distance_min {
+ rec1.distance = distance_min;
+ }
+ if rec2.distance > distance_max {
+ rec2.distance = distance_max;
+ }
+
+ if rec1.distance >= rec2.distance {
+ return None;
+ }
+
+ if rec1.distance < 0.0 {
+ rec1.distance = 0.0;
+ }
+
+ let ray_length: Float = ray.direction.norm();
+ let distance_inside_boundary: Float = (rec2.distance - rec1.distance) * ray_length;
+ let hit_distance: Float = self.neg_inv_density * (rng.gen::<Float>()).ln(); // TODO: verify if log_e is correct here
+
+ if hit_distance > distance_inside_boundary {
+ return None;
+ }
+
+ let distance = rec1.distance + hit_distance / ray_length;
+ let position = ray.evaluate(distance);
+
+ let normal: Direction = random_unit_vector(rng); // tutorial says: arbitrary
+ let front_face: bool = true; // tutorial says: also arbitrary
+
+ let u = rec1.u;
+ let v = rec1.v;
+
+ Some(HitRecord {
+ distance,
+ position,
+ normal,
+ u,
+ v,
+ material: &self.phase_function,
+ front_face,
+ })
+ }
+
+ /// Returns the axis-aligned bounding box [AABB] of the defining `boundary` object for the fog.
+ #[must_use]
+ fn bounding_box(&self, t0: Float, t1: Float) -> Option<&AABB> {
+ self.boundary.bounding_box(t0, t1)
+ }
+
+ /// Returns a probability density function value based on the boundary object
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ self.boundary
+ .pdf_value(origin, direction, wavelength, time, rng)
+ }
+
+ /// Returns a random point on the surface of the boundary of the fog
+ // TODO: should this return a random point inside the volume instead?
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position {
+ self.boundary.random(origin, rng)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +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 +
//! GLTF format support for the renderer
+
+use alloc::string::String;
+use alloc::vec::Vec;
+#[cfg(feature = "gl_tf")]
+use gltf::{image::Data, Mesh, Node};
+use nalgebra::Unit;
+use rand::rngs::SmallRng;
+use rand::Rng;
+#[cfg(feature = "traces")]
+use tracing::debug;
+
+use crate::{
+ aabb::AABB,
+ bvhnode::BVHNode,
+ hitable::{get_orientation, HitRecord, Hitable, HitableTrait},
+ interval::Interval,
+ materials::gltf::GLTFMaterial,
+ ray::Ray,
+ wavelength::Wavelength,
+ Direction, Float, Position, Vec3, EPSILON_RECT_THICKNESS, EPSILON_SHADOW_ACNE,
+};
+
+/// GLTF initialization structure
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct GLTFInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// Path of the .gltf file
+ pub path: String,
+}
+
+impl<'scene> From<GLTFInit> for Vec<Hitable<'scene>> {
+ fn from(gltf: GLTFInit) -> Self {
+ let mut hitables: Vec<Hitable> = Vec::new();
+
+ // Go through the objects in the gltf file
+ let (document, buffers, images) = gltf::import(gltf.path).unwrap();
+ let document: &'scene gltf::Document = Box::leak(Box::new(document));
+ let images: &'scene Vec<Data> = Box::leak(Box::new(images));
+ let materials: &'scene Vec<gltf::Material> =
+ Box::leak(Box::new(document.materials().collect()));
+
+ for scene in document.scenes() {
+ debug!("found scene");
+ for node in scene.nodes() {
+ debug!("found node");
+ parse_node(&node, &mut hitables, &buffers, materials, images);
+ }
+ }
+ debug!("hitable count: {}", &hitables.len());
+
+ hitables
+ }
+}
+
+/// Internal GLTF object representation after initialization.
+#[derive(Debug, Clone)]
+pub struct GLTF<'scene> {
+ /// Bounding Volume Hierarchy tree for the object
+ pub bvhnode: BVHNode<'scene>,
+ /// Axis-aligned bounding box of the object
+ pub aabb: AABB,
+}
+
+impl<'scene> GLTF<'scene> {
+ #[must_use]
+ /// Create a new STL object with the given initialization parameters.
+ pub fn new(gltf_init: GLTFInit, time_0: Float, time_1: Float) -> Self {
+ let triangles: Vec<Hitable> = gltf_init.into();
+ let bvhnode = BVHNode::from_list(triangles, time_0, time_1);
+ // TODO: remove unwrap
+ let aabb = bvhnode.bounding_box(time_0, time_1).unwrap().clone();
+
+ GLTF { bvhnode, aabb }
+ }
+}
+
+impl<'scene> HitableTrait for GLTF<'scene> {
+ /// Hit method for the GLTF object
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: f32,
+ distance_max: f32,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ self.bvhnode.hit(ray, distance_min, distance_max, rng)
+ }
+
+ /// Return the axis-aligned bounding box for the object
+ #[must_use]
+ fn bounding_box(&self, _t0: f32, _t1: f32) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ /// Returns a probability density function value based on the object
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ self.bvhnode
+ .pdf_value(origin, direction, wavelength, time, rng)
+ }
+
+ /// Returns a random point on the surface of the object
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position {
+ self.bvhnode.random(origin, rng)
+ }
+}
+
+fn parse_node<'scene>(
+ node: &Node,
+ objects: &mut Vec<Hitable<'scene>>,
+ buffers: &Vec<gltf::buffer::Data>,
+ materials: &'scene Vec<gltf::Material>,
+ images: &'scene Vec<Data>,
+) {
+ // Handle direct meshes
+ if let Some(mesh) = node.mesh() {
+ parse_mesh(&mesh, objects, buffers, materials, images);
+ }
+ // Handle nesting
+ for child in node.children() {
+ parse_node(&child, objects, buffers, materials, images);
+ }
+}
+
+fn parse_mesh<'scene>(
+ mesh: &Mesh,
+ objects: &mut Vec<Hitable<'scene>>,
+ buffers: &[gltf::buffer::Data],
+ materials: &'scene [gltf::Material],
+ images: &'scene [Data],
+) {
+ debug!("found mesh");
+ for primitive in mesh.primitives() {
+ debug!("found primitive");
+ match primitive.mode() {
+ gltf::mesh::Mode::Triangles => {
+ let mut trianglelist: Vec<Hitable> = Vec::new();
+
+ let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
+ let mut all_positions: Vec<Vec3> = Vec::new();
+ if let Some(iter) = reader.read_positions() {
+ for vertex_position in iter {
+ all_positions.push(vertex_position.into());
+ }
+ }
+
+ // Note that in the GLTF format the same positions can be re-used for multiple triangles, as a sort of a compression method
+ // Read the indices array in order to assemble triangles from positions
+ if let Some(accessor) = reader.read_indices() {
+ let accessor = accessor.into_u32();
+ let len = accessor.len();
+ let indices: Vec<u32> = accessor.collect();
+ let indices: Vec<usize> = indices.iter().map(|&x| x as usize).collect();
+ let mut i = 0;
+ let material_index = primitive.material().index().unwrap();
+ let material = &materials[material_index];
+
+ let coordset = match material.pbr_metallic_roughness().base_color_texture() {
+ Some(texture) => texture.tex_coord(),
+ None => 0,
+ };
+ let all_tex_coords: Vec<[Float; 2]> = reader
+ .read_tex_coords(coordset)
+ .unwrap()
+ .into_f32()
+ .collect();
+ let all_normals: Option<Vec<_>> = reader.read_normals().map(Iterator::collect);
+ let all_tangents: Option<Vec<_>> =
+ reader.read_tangents().map(Iterator::collect);
+
+ while i < len {
+ let triangle = [
+ all_positions[indices[i]],
+ all_positions[indices[i + 1]],
+ all_positions[indices[i + 2]],
+ ];
+ let tex_coords = [
+ all_tex_coords[indices[i]],
+ all_tex_coords[indices[i + 1]],
+ all_tex_coords[indices[i + 2]],
+ ];
+ let normals = all_normals.as_ref().map(|normals| {
+ [
+ normals[indices[i]],
+ normals[indices[i + 1]],
+ normals[indices[i + 2]],
+ ]
+ });
+ let tangents = all_tangents.as_ref().map(|tangents| {
+ [
+ tangents[indices[i]],
+ tangents[indices[i + 1]],
+ tangents[indices[i + 2]],
+ ]
+ });
+
+ // TODO: don't leak memory
+ let material: &'scene GLTFMaterial = Box::leak(Box::new(
+ GLTFMaterial::new(material, tex_coords, normals, tangents, images),
+ ));
+
+ let gltf_triangle = GLTFTriangle::new(triangle, material);
+ trianglelist.push(Hitable::GLTFTriangle(gltf_triangle));
+ i += 3;
+ }
+ }
+
+ let bvh: BVHNode = BVHNode::from_list(trianglelist, 0.0, 1.0);
+ objects.push(Hitable::BVHNode(bvh));
+ }
+ _ => unimplemented!(),
+ }
+ }
+}
+
+/// Internal GLTF object representation after initialization.
+#[derive(Debug, Clone)]
+pub struct GLTFTriangle<'scene> {
+ /// Axis-aligned bounding box of the object
+ pub aabb: AABB,
+ /// Material of the object
+ pub material: &'scene GLTFMaterial<'scene>,
+ q: Vec3,
+ u: Vec3,
+ v: Vec3,
+ d: Float,
+ w: Vec3,
+ area: Float,
+ normal: Direction,
+}
+
+impl<'scene> GLTFTriangle<'scene> {
+ #[must_use]
+ /// Initialize a new GLTF object
+ pub fn new(triangle: [Vec3; 3], material: &'scene GLTFMaterial<'scene>) -> Self {
+ // TODO: mostly adapted from Triangle, verify correctness!
+
+ let [a, b, c] = triangle;
+ let interval_x = Interval::new(a[0].min(b[0]).min(c[0]), a[0].max(b[0]).max(c[0]));
+ let interval_y = Interval::new(a[1].min(b[1]).min(c[1]), a[1].max(b[1]).max(c[1]));
+ let interval_z = Interval::new(a[2].min(b[2]).min(c[2]), a[2].max(b[2]).max(c[2]));
+ let mut aabb: AABB = AABB::new(interval_x, interval_y, interval_z);
+ aabb.pad();
+
+ // TODO: Check orientation and make into a corner + edge vectors triangle
+ let q = a;
+ let u = b - q;
+ let v = c - q;
+
+ let n: Vec3 = u.cross(&v);
+ let normal = Unit::new_normalize(n);
+ // TODO: what is this?
+ let d = -(normal.dot(&q));
+ // TODO: what is this?
+ let w: Vec3 = n / n.dot(&n);
+ // Compared to quad, triangle has half the area
+ let area = n.magnitude() / 2.0;
+
+ GLTFTriangle {
+ aabb,
+ material,
+ q,
+ u,
+ v,
+ d,
+ w,
+ area,
+ normal,
+ }
+ }
+}
+
+impl<'scene> HitableTrait for GLTFTriangle<'scene> {
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ _rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ // TODO: mostly adapted from Triangle, verify correctness!
+
+ let denom = self.normal.dot(&ray.direction);
+
+ // No hit if the ray is parallel to the plane.
+ if denom.abs() < EPSILON_RECT_THICKNESS {
+ return None;
+ }
+
+ // Return false if the hit point parameter t is outside the ray interval
+ let t = (-self.d - self.normal.dot(&ray.origin)) / denom;
+ if t < distance_min || t > distance_max {
+ return None;
+ }
+
+ // Determine the hit point lies within the planar shape using its plane coordinates.
+ let intersection: Vec3 = ray.evaluate(t);
+ let planar_hitpt_vector: Vec3 = intersection - self.q;
+ let alpha: Float = self.w.dot(&planar_hitpt_vector.cross(&self.v));
+ let beta: Float = self.w.dot(&self.u.cross(&planar_hitpt_vector));
+
+ // Do we hit a coordinate within the surface of the plane?
+ if !hit_ab(alpha, beta) {
+ return None;
+ }
+
+ // Ray hits the 2D shape; set the rest of the hit record and return
+
+ let (front_face, normal) = get_orientation(ray, self.normal);
+
+ Some(HitRecord {
+ distance: t,
+ position: intersection,
+ normal,
+ u: alpha,
+ v: beta,
+ material: self.material,
+ front_face,
+ })
+ }
+
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ let ray = Ray {
+ origin,
+ direction,
+ time,
+ wavelength,
+ };
+ // TODO: this is from quad and not updated!
+ match self.hit(&ray, EPSILON_SHADOW_ACNE, Float::INFINITY, rng) {
+ Some(hit_record) => {
+ let distance_squared =
+ hit_record.distance * hit_record.distance * direction.norm_squared();
+ let cosine = direction.dot(&hit_record.normal).abs() / direction.magnitude();
+
+ distance_squared / (cosine * self.area)
+ }
+ None => 0.0,
+ }
+ }
+
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Vec3 {
+ let mut a = rng.gen::<Float>();
+ let mut b = rng.gen::<Float>();
+ if a + b > 1.0 {
+ a = 1.0 - a;
+ b = 1.0 - b;
+ }
+
+ let point: Vec3 = self.q + (a * self.u) + (b * self.v);
+
+ point - origin
+ }
+}
+
+#[must_use]
+fn hit_ab(a: Float, b: Float) -> bool {
+ // Given the hit point in plane coordinates, return false if it is outside the
+ // primitive, otherwise return true.
+ // Triangle: a+b must be <=1.0
+ (0.0..=1.0).contains(&a) && (0.0..=1.0).contains(&b) && (a + b <= 1.0)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +
//! A moving sphere object.
+
+use crate::{
+ aabb::AABB,
+ hitable::{HitRecord, HitableTrait},
+ materials::{Material, MaterialInit},
+ random::random_unit_vector,
+ ray::Ray,
+ wavelength::Wavelength,
+ Direction, Float, Position, PI,
+};
+use nalgebra::Unit;
+use rand::rngs::SmallRng;
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// `SphereInit` structure describes the necessary data for constructing a [`Sphere`](super::Sphere). Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
+pub struct MovingSphereInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// Center point of the sphere at `time_0`
+ pub center_0: Position,
+ /// Center point of the sphere at `time_1`
+ pub center_1: Position,
+ /// Radius of the sphere.
+ pub radius: Float,
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ /// Material of the sphere.
+ pub material: MaterialInit,
+}
+
+#[derive(Debug, Clone)]
+/// A moving sphere object. This is represented by one `radius`, two center points `center_0` `center_1`, two times `time_0` `time_1`, and a [Material]. Any [Rays](Ray) hitting the object will also have an internal `time` value, which will be used for determining the interpolated position of the sphere at that time. With lots of rays hitting every pixel but at randomized times, we get temporal multiplexing and an approximation of perceived motion blur.
+pub struct MovingSphere<'scene> {
+ /// Center point of the sphere at `time_0`
+ pub center_0: Position,
+ /// Center point of the sphere at `time_1`
+ pub center_1: Position,
+ /// Time 0
+ pub time_0: Float,
+ /// Time 1
+ pub time_1: Float,
+ /// Radius of the sphere
+ pub radius: Float,
+ /// Material of the sphere
+ pub material: &'scene Material,
+ /// Axis-aligned bounding box
+ pub aabb: AABB,
+}
+
+impl<'scene> MovingSphere<'scene> {
+ /// Creates a new `MovingSphere` object. See the struct documentation for more information: [`MovingSphere`].
+ #[must_use]
+ pub fn new(
+ center_0: Position,
+ center_1: Position,
+ time_0: Float,
+ time_1: Float,
+ radius: Float,
+ material: &'scene Material,
+ ) -> Self {
+ let box0: AABB = AABB::new_from_coords(
+ center_0 - Position::new(radius, radius, radius),
+ center_0 + Position::new(radius, radius, radius),
+ );
+ let box1: AABB = AABB::new_from_coords(
+ center_1 - Position::new(radius, radius, radius),
+ center_1 + Position::new(radius, radius, radius),
+ );
+
+ let aabb = AABB::surrounding_box(&box0, &box1);
+
+ MovingSphere {
+ center_0,
+ center_1,
+ time_0,
+ time_1,
+ radius,
+ material,
+ aabb,
+ }
+ }
+
+ /// Returns the interpolated center of the moving sphere at the given time.
+ #[must_use]
+ pub fn center(&self, time: Float) -> Position {
+ self.center_0
+ + ((time - self.time_0) / (self.time_1 - self.time_0)) * (self.center_1 - self.center_0)
+ }
+
+ /// Returns the U,V surface coordinates of a hitpoint
+ // TODO: verify this is up to date with the sphere get_uv methods and matches a surface coordinate instead of volumetric space cordinate
+ #[must_use]
+ pub fn get_uv(&self, hit_position: Position, time: Float) -> (Float, Float) {
+ let translated: Position = (hit_position - self.center(time)) / self.radius;
+ let phi: Float = translated.z.atan2(translated.x);
+ let theta: Float = translated.y.asin();
+ let u: Float = 1.0 - (phi + PI) / (2.0 * PI);
+ let v: Float = (theta + PI / 2.0) / PI;
+ (u, v)
+ }
+}
+
+impl<'scene> HitableTrait for MovingSphere<'scene> {
+ /// Hit method for the [`MovingSphere`] object. First gets the interpolated center position at the given time, then follows the implementation of [Sphere](crate::objects::Sphere) object's hit method.
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ _rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ let oc = ray.origin - self.center(ray.time);
+ let a: Float = ray.direction.norm_squared();
+ let half_b: Float = oc.dot(&ray.direction);
+ let c: Float = oc.norm_squared() - self.radius * self.radius;
+ let discriminant = half_b * half_b - a * c;
+ if discriminant > 0.0 {
+ let root: Float = discriminant.sqrt();
+ let distance: Float = (-half_b - root) / a;
+ // First possible root
+ if distance < distance_max && distance > distance_min {
+ let position: Position = ray.evaluate(distance);
+ let outward_normal = (position - self.center(ray.time)) / self.radius;
+ let outward_normal = Unit::new_normalize(outward_normal);
+ let (u, v) = self.get_uv(position, ray.time);
+ let mut record = HitRecord {
+ distance,
+ position,
+ normal: outward_normal,
+ u,
+ v,
+ material: self.material,
+ front_face: false, // TODO: fix having to declare it before calling face_normal
+ };
+ record.set_face_normal(ray, outward_normal);
+ return Some(record);
+ }
+ // Second possible root
+ let distance: Float = (-half_b + root) / a;
+ if distance < distance_max && distance > distance_min {
+ let position: Position = ray.evaluate(distance);
+ let outward_normal = (position - self.center(ray.time)) / self.radius;
+ let outward_normal = Unit::new_normalize(outward_normal);
+ let (u, v) = self.get_uv(position, ray.time);
+ let mut record = HitRecord {
+ distance,
+ position,
+ normal: outward_normal,
+ u,
+ v,
+ material: self.material,
+ front_face: false, // TODO: fix having to declare it before calling face_normal
+ };
+ record.set_face_normal(ray, outward_normal);
+ return Some(record);
+ }
+ }
+ None
+ }
+
+ /// Returns the axis-aligned bounding box of the [`MovingSphere`] object. This is the maximum possible bounding box of the entire span of the movement of the sphere, calculated from the two center positions and the radius.
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ fn pdf_value(
+ &self,
+ _origin: Position,
+ _direction: Direction,
+ _wavelength: Wavelength,
+ _time: Float,
+ _rng: &mut SmallRng,
+ ) -> Float {
+ // TODO: fix
+ 0.0
+ }
+
+ fn random(&self, _origin: Position, rng: &mut SmallRng) -> Position {
+ // FIXME: this is incorrect! does not take into account sphere size, moving sphere position
+ *random_unit_vector(rng)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +
//! A quadrilateral object.
+// TODO: better docs
+
+use crate::hitable::HitableTrait;
+use crate::materials::MaterialInit;
+use crate::wavelength::Wavelength;
+use crate::{
+ aabb::AABB, hitable::get_orientation, hitable::HitRecord, materials::Material, ray::Ray, Float,
+ Vec3, EPSILON_RECT_THICKNESS,
+};
+use crate::{Direction, Position, EPSILON_SHADOW_ACNE};
+use nalgebra::Unit;
+use rand::rngs::SmallRng;
+use rand::Rng;
+
+/// Initialization structure for a Quad object.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct QuadInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// Corner point
+ pub q: Position,
+ /// Vector describing the u side
+ pub u: Vec3,
+ /// Vector describing the v side
+ pub v: Vec3,
+ /// Material of the surface
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub material: MaterialInit,
+}
+
+/// Quadrilateral shape. This can be an arbitrary parallelogram, not just a rectangle.
+#[derive(Clone, Debug)]
+pub struct Quad<'scene> {
+ /// Corner point
+ pub q: Position,
+ /// Vector describing the u side
+ pub u: Vec3,
+ /// Vector describing the v side
+ pub v: Vec3,
+ /// Material of the surface
+ pub material: &'scene Material,
+ /// Area of the surface
+ pub area: Float,
+ /// Normal vector of the surface
+ pub normal: Direction,
+ /// What is this? // TODO: understand, explain
+ pub d: Float,
+ /// What is this? // TODO: understand, explain
+ pub w: Vec3,
+ /// Bounding box of the surface
+ pub aabb: AABB,
+}
+
+impl<'scene> Quad<'scene> {
+ /// Creates a new quad
+ #[must_use]
+ pub fn new(q: Position, u: Vec3, v: Vec3, material: &'scene Material) -> Quad<'scene> {
+ let n: Vec3 = u.cross(&v);
+ let normal = Unit::new_normalize(n);
+ // TODO: what is this?
+ let d = -(normal.dot(&q));
+ // TODO: what is this?
+ let w: Vec3 = n / n.dot(&n);
+ let area = n.magnitude();
+ let mut aabb = AABB::new_from_coords(q, q + u + v);
+ aabb.pad();
+
+ Quad {
+ q,
+ u,
+ v,
+ material,
+ area,
+ normal,
+ d,
+ w,
+ aabb,
+ }
+ }
+}
+
+impl<'scene> HitableTrait for Quad<'scene> {
+ /// Hit method for the quad rectangle
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ _rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ let denom = self.normal.dot(&ray.direction);
+
+ // No hit if the ray is parallel to the plane.
+ if denom.abs() < EPSILON_RECT_THICKNESS {
+ return None;
+ }
+
+ // Return false if the hit point parameter t is outside the ray interval
+ let t = (-self.d - self.normal.dot(&ray.origin)) / denom;
+ if t < distance_min || t > distance_max {
+ return None;
+ }
+
+ // Determine the hit point lies within the planar shape using its plane coordinates.
+ let intersection: Position = ray.evaluate(t);
+ let planar_hitpt_vector: Position = intersection - self.q;
+ let alpha: Float = self.w.dot(&planar_hitpt_vector.cross(&self.v));
+ let beta: Float = self.w.dot(&self.u.cross(&planar_hitpt_vector));
+
+ // Do we hit a coordinate within the surface of the plane?
+ if !hit_ab(alpha, beta) {
+ return None;
+ }
+
+ // Ray hits the 2D shape; set the rest of the hit record and return
+
+ let (front_face, normal) = get_orientation(ray, self.normal);
+
+ Some(HitRecord {
+ distance: t,
+ position: intersection,
+ normal,
+ u: alpha,
+ v: beta,
+ material: self.material,
+ front_face,
+ })
+ }
+
+ /// Returns the bounding box of the quad
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ /// Returns a probability density function value? // TODO: understand & explain
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ let ray = Ray {
+ origin,
+ direction,
+ time,
+ wavelength,
+ };
+ match self.hit(&ray, EPSILON_SHADOW_ACNE, Float::INFINITY, rng) {
+ Some(hit_record) => {
+ let distance_squared =
+ hit_record.distance * hit_record.distance * direction.norm_squared();
+ let cosine = direction.dot(&hit_record.normal).abs() / direction.magnitude();
+
+ distance_squared / (cosine * self.area)
+ }
+ None => 0.0,
+ }
+ }
+
+ /// Returns a random point on the quadrilateral surface
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position {
+ let point: Position =
+ self.q + (rng.gen::<Float>() * self.u) + (rng.gen::<Float>() * self.v);
+ // TODO: why point minus origin?
+ point - origin
+ }
+}
+
+#[must_use]
+fn hit_ab(a: Float, b: Float) -> bool {
+ // Given the hit point in plane coordinates, return false if it is outside the
+ // primitive, otherwise return true.
+ (0.0..=1.0).contains(&a) && (0.0..=1.0).contains(&b)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +
//! Utility object for rotating another object.
+
+use crate::{
+ aabb::AABB,
+ hitable::{HitRecord, Hitable, HitableTrait},
+ ray::Ray,
+ wavelength::Wavelength,
+ Box, Direction, Float, Position, Vec3,
+};
+use nalgebra::Unit;
+use rand::rngs::SmallRng;
+
+use super::Object;
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// `RotateInit` structure describes the necessary data for constructing a [`RotateY`]. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
+pub struct RotateInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// The encased [Object] to rotate
+ pub object: Box<Object>,
+ /// Angle to rotate the object, in degrees
+ pub angle: Float,
+}
+
+#[derive(Debug, Clone)]
+/// `RotateY` object. It wraps the given [Object] and has adjusted `hit()` and `bounding_box()` methods based on the `angle` given.
+pub struct RotateY<'scene> {
+ object: Box<Hitable<'scene>>,
+ sin_theta: Float,
+ cos_theta: Float,
+ aabb: Option<AABB>,
+}
+
+impl<'scene> RotateY<'scene> {
+ /// Creates a new `RotateY` object. It wraps the given [Object] and has adjusted `hit()` and `bounding_box()` methods based on the `angle` given.
+ #[must_use]
+ pub fn new(object: Box<Hitable<'scene>>, angle: Float) -> Self {
+ // TODO: add proper time support
+ let time_0: Float = 0.0;
+ let time_1: Float = 1.0;
+ let radians: Float = angle.to_radians();
+ let sin_theta: Float = radians.sin();
+ let cos_theta: Float = radians.cos();
+ let bounding_box: Option<&AABB> = object.bounding_box(time_0, time_1);
+
+ // Does our object have a bounding box?
+ let Some(bbox) = bounding_box else {
+ return RotateY {
+ object,
+ sin_theta,
+ cos_theta,
+ aabb: None,
+ };
+ };
+
+ // Start with infinite bounds
+ let mut min: Vec3 = Vec3::new(Float::INFINITY, Float::INFINITY, Float::INFINITY);
+ let mut max: Vec3 = Vec3::new(
+ Float::NEG_INFINITY,
+ Float::NEG_INFINITY,
+ Float::NEG_INFINITY,
+ );
+
+ // Calculate new bounds
+ for i in [0.0, 1.0] {
+ for j in [0.0, 1.0] {
+ for k in [0.0, 1.0] {
+ let i_f: Float = i;
+ let j_f: Float = j;
+ let k_f: Float = k;
+
+ let x: Float = i_f * bbox.x.max + (1.0 - i_f) * bbox.x.min;
+ let y: Float = j_f * bbox.y.max + (1.0 - j_f) * bbox.y.min;
+ let z: Float = k_f * bbox.z.max + (1.0 - k_f) * bbox.z.min;
+
+ let new_x: Float = cos_theta * x + sin_theta * z;
+ let new_z: Float = -sin_theta * x + cos_theta * z;
+
+ let tester: Vec3 = Vec3::new(new_x, y, new_z);
+
+ for c in 0..3 {
+ min[c] = min[c].min(tester[c]);
+ max[c] = max[c].max(tester[c]);
+ }
+ }
+ }
+ }
+
+ // Return a Rotate object with the new bounding box and pre-calculated rotation utilities
+ RotateY {
+ object,
+ sin_theta,
+ cos_theta,
+ aabb: Some(AABB::new_from_coords(min, max)),
+ }
+ }
+}
+
+impl<'scene> HitableTrait for RotateY<'scene> {
+ /// Hit method for the [`RotateY`] object. Finds the rotation-adjusted [`HitRecord`] for the possible intersection of the [Ray] with the encased [Object].
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ let mut origin: Position = ray.origin;
+ let mut direction: Vec3 = *ray.direction;
+
+ origin[0] = self.cos_theta * ray.origin[0] - self.sin_theta * ray.origin[2];
+ origin[2] = self.sin_theta * ray.origin[0] + self.cos_theta * ray.origin[2];
+
+ direction[0] = self.cos_theta * ray.direction[0] - self.sin_theta * ray.direction[2];
+ direction[2] = self.sin_theta * ray.direction[0] + self.cos_theta * ray.direction[2];
+
+ let direction = Unit::new_normalize(direction);
+
+ let rotated_r: Ray = Ray {
+ origin,
+ direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ };
+
+ let Some(hit_record) = self.object.hit(&rotated_r, distance_min, distance_max, rng) else {
+ // Did not hit rotated object, early return None
+ return None;
+ };
+
+ // Determine where the intersection is
+ // TODO: understand and explain
+ let mut position: Position = hit_record.position;
+ let mut normal: Vec3 = *hit_record.normal;
+ let distance: Float = hit_record.distance;
+
+ position[0] =
+ self.cos_theta * hit_record.position[0] + self.sin_theta * hit_record.position[2];
+ position[2] =
+ -self.sin_theta * hit_record.position[0] + self.cos_theta * hit_record.position[2];
+
+ normal[0] = self.cos_theta * hit_record.normal[0] + self.sin_theta * hit_record.normal[2];
+ normal[2] = -self.sin_theta * hit_record.normal[0] + self.cos_theta * hit_record.normal[2];
+
+ let normal = Unit::new_normalize(normal);
+
+ let mut record = HitRecord {
+ distance,
+ position,
+ normal,
+ u: hit_record.u,
+ v: hit_record.v,
+ material: hit_record.material,
+ front_face: false, // TODO: fix having to declare it before calling face_normal
+ };
+ record.set_face_normal(&rotated_r, normal);
+ Some(record)
+ }
+
+ /// Bounding box method for the [`RotateY`] object. Finds the axis-aligned bounding box [AABB] for the encased [Object] after adjusting for rotation.
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ self.aabb.as_ref()
+ }
+
+ fn pdf_value(
+ &self,
+ _origin: Position,
+ _direction: Direction,
+ _wavelength: Wavelength,
+ _time: Float,
+ _rng: &mut SmallRng,
+ ) -> Float {
+ // TODO: fix
+ 0.0
+ }
+
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Vec3 {
+ // TODO: fix, take rotation into account
+ self.object.random(origin, rng)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +
//! A sphere object.
+
+use crate::{
+ aabb::AABB,
+ hitable::{HitRecord, HitableTrait},
+ materials::{Material, MaterialInit},
+ onb::ONB,
+ ray::Ray,
+ wavelength::Wavelength,
+ Direction, Float, Position, Vec3, EPSILON_SHADOW_ACNE, PI,
+};
+use nalgebra::Unit;
+use rand::{rngs::SmallRng, Rng};
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// `SphereInit` structure describes the necessary data for constructing a [Sphere]. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
+pub struct SphereInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// Center of the sphere.
+ pub center: Position,
+ /// Radius of the sphere.
+ pub radius: Float,
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ /// Material of the sphere.
+ pub material: MaterialInit,
+}
+
+#[derive(Debug, Clone)]
+/// A sphere object.
+pub struct Sphere<'scene> {
+ center: Position,
+ radius: Float,
+ material: &'scene Material,
+ aabb: AABB,
+}
+
+impl<'scene> Sphere<'scene> {
+ /// Creates a new `Sphere` object with the given center, radius and material.
+ #[must_use]
+ pub fn new(center: Position, radius: Float, material: &'scene Material) -> Self {
+ let aabb = AABB::new_from_coords(
+ center - Position::new(radius, radius, radius),
+ center + Position::new(radius, radius, radius),
+ );
+ Sphere {
+ center,
+ radius,
+ material,
+ aabb,
+ }
+ }
+
+ /// Returns the U,V surface coordinates of a hitpoint
+ #[must_use]
+ pub fn get_uv(&self, hit_position: Position, _time: Float) -> (Float, Float) {
+ let translated: Position = (hit_position - self.center) / self.radius;
+ let phi: Float = translated.z.atan2(translated.x);
+ let theta: Float = translated.y.asin();
+ let u: Float = 1.0 - (phi + PI) / (2.0 * PI);
+ let v: Float = (theta + PI / 2.0) / PI;
+ (u, v)
+ }
+}
+
+impl<'scene> HitableTrait for Sphere<'scene> {
+ /// Hit method for the [Sphere] object. Returns a [`HitRecord`] if the given [Ray] intersects with the sphere at the given distance interval.
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ _rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ let oc: Position = ray.origin - self.center;
+ let a: Float = ray.direction.norm_squared();
+ let half_b: Float = oc.dot(&ray.direction);
+ let c: Float = oc.norm_squared() - self.radius * self.radius;
+ let discriminant = half_b * half_b - a * c;
+ if discriminant > 0.0 {
+ let root: Float = discriminant.sqrt();
+
+ // First possible root
+ let distance: Float = (-half_b - root) / a;
+ if distance < distance_max && distance > distance_min {
+ let position: Position = ray.evaluate(distance);
+ let outward_normal = (position - self.center) / self.radius;
+ let outward_normal = Unit::new_normalize(outward_normal);
+ let (u, v) = self.get_uv(position, ray.time);
+ let mut record = HitRecord {
+ distance,
+ position,
+ normal: outward_normal,
+ u,
+ v,
+ material: self.material,
+ front_face: false, // TODO: fix having to declare it before calling face_normal
+ };
+ record.set_face_normal(ray, outward_normal);
+ return Some(record);
+ }
+ // Second possible root
+ let distance: Float = (-half_b + root) / a;
+ if distance < distance_max && distance > distance_min {
+ let position = ray.evaluate(distance);
+ let outward_normal = (position - self.center) / self.radius;
+ let outward_normal = Unit::new_normalize(outward_normal);
+ let (u, v) = self.get_uv(position, ray.time);
+ let mut record = HitRecord {
+ distance,
+ position,
+ normal: outward_normal,
+ u,
+ v,
+ material: self.material,
+ front_face: false, // TODO: fix having to declare it before calling face_normal
+ };
+ record.set_face_normal(ray, outward_normal);
+ return Some(record);
+ }
+ }
+ None
+ }
+
+ /// Returns the axis-aligned bounding box [AABB] for the sphere.
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ /// Returns the probability density function for the sphere? TODO: what does this do again and how
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ let ray = Ray {
+ origin,
+ direction,
+ time,
+ wavelength,
+ };
+ match self.hit(&ray, EPSILON_SHADOW_ACNE, Float::INFINITY, rng) {
+ None => 0.0,
+ Some(_hit_record) => {
+ let cos_theta_max = (1.0
+ - self.radius * self.radius / (self.center - origin).norm_squared())
+ .sqrt();
+ let solid_angle = 2.0 * PI * (1.0 - cos_theta_max);
+
+ 1.0 / solid_angle
+ }
+ }
+ }
+
+ // TODO: understand, document
+ /// Utility function from Ray Tracing: The Rest of Your Life. TODO: understand, document
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position {
+ let offset: Position = self.center - origin;
+ let distance_squared: Float = offset.norm_squared();
+ let uvw = ONB::build_from_w(Unit::new_normalize(offset));
+ let vec = random_to_sphere(self.radius, distance_squared, rng);
+ let vec = Unit::new_normalize(vec);
+ *uvw.local(vec)
+ }
+}
+
+/// Internal helper.
+fn random_to_sphere(radius: Float, distance_squared: Float, rng: &mut SmallRng) -> Vec3 {
+ let r1: Float = rng.gen();
+ let r2: Float = rng.gen();
+ let z = 1.0 + r2 * ((1.0 - radius * radius / distance_squared).sqrt() - 1.0);
+
+ let phi = 2.0 * PI * r1;
+ let x = phi.cos() * (1.0 - z * z).sqrt();
+ let y = phi.sin() * (1.0 - z * z).sqrt();
+
+ Vec3::new(x, y, z)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +
//! STL utilities
+
+use alloc::string::String;
+use nalgebra::Rotation3;
+use rand::prelude::SmallRng;
+use std::fs::OpenOptions;
+
+use crate::{
+ aabb::AABB,
+ bvhnode::BVHNode,
+ hitable::{HitRecord, Hitable, HitableTrait},
+ materials::{Material, MaterialInit, SharedMaterial},
+ objects::Triangle,
+ ray::Ray,
+ wavelength::Wavelength,
+ Direction, Float, Position, Vec3,
+};
+
+/// Internal STL object representation after initialization. Contains the material for all triangles in it to avoid having n copies.
+#[derive(Debug, Clone)]
+pub struct STL<'scene> {
+ /// Bounding Volume Hierarchy tree for the object
+ pub bvhnode: BVHNode<'scene>,
+ /// Material for the object
+ pub material: &'scene Material,
+ /// Axis-aligned bounding box of the object
+ pub aabb: AABB,
+}
+
+impl<'scene> HitableTrait for STL<'scene> {
+ /// Hit method for the STL object
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: f32,
+ distance_max: f32,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ self.bvhnode.hit(ray, distance_min, distance_max, rng)
+ }
+
+ /// Return the axis-aligned bounding box for the object
+ #[must_use]
+ fn bounding_box(&self, _t0: f32, _t1: f32) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ /// Returns a probability density function value based on the object
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ self.bvhnode
+ .pdf_value(origin, direction, wavelength, time, rng)
+ }
+
+ /// Returns a random point on the ssurface of the object
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Vec3 {
+ self.bvhnode.random(origin, rng)
+ }
+}
+
+/// STL structure. This gets converted into an internal representation using [Triangles](crate::objects::Triangle)
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct STLInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// Path of the .stl file
+ pub path: String,
+ /// Material to use for the .stl object
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub material: MaterialInit,
+ /// Scaling factor for the object
+ pub scale: Float,
+ /// Location of the object in the rendered scene
+ pub center: Position,
+ /// Rotation of the object. Described as three angles, `roll`, `pitch`, `yaw`, applied in that order.
+ pub rotation: Vec3,
+}
+
+#[must_use]
+/// Initializes an STL
+pub fn initialize_stl<'scene>(
+ stl_init: STLInit,
+ materials: &'scene [SharedMaterial],
+) -> STL<'scene> {
+ // TODO: error handling!
+ let mut file = OpenOptions::new()
+ .read(true)
+ .open(stl_init.path.clone())
+ .unwrap();
+ let mesh = stl_io::read_stl(&mut file).unwrap();
+ let triangles = mesh.vertices;
+ let mut hitable_list = Vec::new();
+ let material: &Material = match stl_init.material {
+ MaterialInit::Shared(name) => &materials.iter().find(|m| m.name == name).unwrap().material,
+ MaterialInit::Owned(m) => {
+ // TODO: do not leak memory
+ let material: &'scene Material = Box::leak(Box::new(m));
+ material
+ }
+ };
+
+ for face in mesh.faces {
+ // TODO: verify if this is the correct order / makes sense / gets correct directions and normals
+ let a = triangles[face.vertices[0]];
+ let b = triangles[face.vertices[1]];
+ let c = triangles[face.vertices[2]];
+ // TODO: better conversion between library format and own format
+ let a: Vec3 = Vec3::new(a[0], a[1], a[2]);
+ let b: Vec3 = Vec3::new(b[0], b[1], b[2]);
+ let c: Vec3 = Vec3::new(c[0], c[1], c[2]);
+ // Handle rotation
+ let rotation = Rotation3::from_euler_angles(
+ stl_init.rotation[0].to_radians(),
+ stl_init.rotation[1].to_radians(),
+ stl_init.rotation[2].to_radians(),
+ );
+ let a: Vec3 = rotation * a;
+ let b: Vec3 = rotation * b;
+ let c: Vec3 = rotation * c;
+ // Handle scaling and offset
+ let a: Vec3 = a * stl_init.scale + stl_init.center;
+ let b: Vec3 = b * stl_init.scale + stl_init.center;
+ let c: Vec3 = c * stl_init.scale + stl_init.center;
+
+ let triangle = Triangle::from_coordinates(a, b, c, material);
+ hitable_list.push(Hitable::Triangle(triangle));
+ }
+
+ // TODO: time
+ let time_0 = 0.0;
+ let time_1 = 1.0;
+
+ let bvhnode = BVHNode::from_list(hitable_list, time_0, time_1);
+ // TODO: remove unwrap
+ let aabb = bvhnode.bounding_box(time_0, time_1).unwrap().clone();
+
+ STL {
+ bvhnode,
+ material,
+ aabb,
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +
//! Utility object for translating i.e. moving another object.
+
+use crate::{
+ aabb::AABB,
+ hitable::{HitRecord, Hitable, HitableTrait},
+ ray::Ray,
+ wavelength::Wavelength,
+ Box, Direction, Float, Position, Vec3,
+};
+use rand::rngs::SmallRng;
+
+use super::Object;
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// `TranslateInit` structure describes the necessary data for constructing a [Translate] object. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
+pub struct TranslateInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// The encased [Object] to translate i.e. move
+ pub object: Box<Object>,
+ /// The vector describing the movement of the object
+ pub offset: Vec3,
+}
+
+#[derive(Debug, Clone)]
+/// Translate object. It wraps the given [Object] and has adjusted `hit()` and `bounding_box()` methods based on the `offset` given.
+pub struct Translate<'scene> {
+ object: Box<Hitable<'scene>>,
+ offset: Vec3,
+ aabb: AABB,
+}
+
+impl<'scene> Translate<'scene> {
+ /// Creates a new `Translate` object. It wraps the given [Object] and has adjusted `hit()` and `bounding_box()` methods based on the `offset` given.
+ #[must_use]
+ pub fn new(object: Box<Hitable<'scene>>, offset: Vec3) -> Self {
+ // TODO: time
+ let aabb = object.bounding_box(0.0, 1.0).unwrap().clone() + offset;
+ Translate {
+ object,
+ offset,
+ aabb,
+ }
+ }
+}
+
+impl<'scene> HitableTrait for Translate<'scene> {
+ /// Hit method for the [Translate] object. Finds the translation-adjusted [`HitRecord`] for the possible intersection of the [Ray] with the encased [Object].
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ let moved_ray = Ray {
+ origin: ray.origin - self.offset,
+ direction: ray.direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ };
+
+ match self.object.hit(&moved_ray, distance_min, distance_max, rng) {
+ // Didn't hit anything, return None
+ None => None,
+ // Hit something, adjust the position and normal
+ Some(mut hit_record) => {
+ hit_record.position += self.offset;
+ hit_record.set_face_normal(&moved_ray, hit_record.normal);
+ Some(hit_record)
+ }
+ }
+ }
+
+ /// Bounding box method for the [Translate] object. Finds the axis-aligned bounding box [AABB] for the encased [Object] after adjusting for translation.
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ Some(&self.aabb)
+ }
+
+ /// Returns a probability density function value based on the inner object
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ // TODO: is this correct?
+ self.object
+ .pdf_value(origin + self.offset, direction, wavelength, time, rng)
+ }
+
+ /// Returns a random point on the surface of the moved object
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Vec3 {
+ self.object.random(origin, rng) + self.offset
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +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 +
//! A triangle object. Almost exact copy of [Quad](crate::objects::Quad), with an adjusted `hit_ab` method.
+// TODO: better docs
+
+use crate::hitable::HitableTrait;
+use crate::interval::Interval;
+use crate::materials::MaterialInit;
+use crate::wavelength::Wavelength;
+use crate::{
+ aabb::AABB, hitable::HitRecord, materials::Material, ray::Ray, Float, Vec3,
+ EPSILON_RECT_THICKNESS,
+};
+use crate::{Direction, Position, EPSILON_SHADOW_ACNE};
+use nalgebra::Unit;
+use rand::rngs::SmallRng;
+use rand::Rng;
+
+/// Initialization structure for a triangle primitive
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct TriangleInit {
+ /// Used for multiple importance sampling
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub priority: bool,
+ /// Corner point
+ pub q: Position,
+ /// Vector describing the u side
+ pub u: Vec3,
+ /// Vector describing the v side
+ pub v: Vec3,
+ /// Material of the surface
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ pub material: MaterialInit,
+}
+
+/// Triangle shape. Heavily based on [Quad](crate::objects::Quad) and may contain inaccuracies
+#[derive(Clone, Debug)]
+pub struct Triangle<'scene> {
+ /// Corner point
+ pub q: Position,
+ /// Vector describing the u side
+ pub u: Vec3,
+ /// Vector describing the v side
+ pub v: Vec3,
+ /// Material of the surface
+ pub material: &'scene Material,
+ /// Area of the surface
+ pub area: Float,
+ /// Normal vector of the surface
+ pub normal: Direction,
+ /// What is this? // TODO: understand, explain
+ pub d: Float,
+ /// What is this? // TODO: understand, explain
+ pub w: Vec3,
+ /// Bounding box of the surface
+ pub aabb: AABB,
+}
+
+impl<'scene> Triangle<'scene> {
+ /// Creates a new triangle from a coordinate point and two side vectors relative to the point
+ #[must_use]
+ pub fn new(q: Position, u: Vec3, v: Vec3, material: &'scene Material) -> Triangle<'scene> {
+ let n: Vec3 = u.cross(&v);
+ let normal: Direction = Unit::new_normalize(n);
+ // TODO: what is this?
+ let d = -(normal.dot(&q));
+ // TODO: what is this?
+ let w: Vec3 = n / n.dot(&n);
+ // Compared to quad, triangle has half the area
+ let area = n.magnitude() / 2.0;
+ // Compute the AABB using the absolute coordinates of all corners
+ // TODO: refactor to prettier code
+ let corner1 = q;
+ let corner2 = q + u;
+ let corner3 = q + v;
+ let interval_x = Interval::new(
+ corner1[0].min(corner2[0]).min(corner3[0]),
+ corner1[0].max(corner2[0]).max(corner3[0]),
+ );
+ let interval_y = Interval::new(
+ corner1[1].min(corner2[1]).min(corner3[1]),
+ corner1[1].max(corner2[1]).max(corner3[1]),
+ );
+ let interval_z = Interval::new(
+ corner1[2].min(corner2[2]).min(corner3[2]),
+ corner1[2].max(corner2[2]).max(corner3[2]),
+ );
+ let mut aabb: AABB = AABB::new(interval_x, interval_y, interval_z);
+ aabb.pad();
+
+ Triangle {
+ q,
+ u,
+ v,
+ material,
+ area,
+ normal,
+ d,
+ w,
+ aabb,
+ }
+ }
+
+ /// Creates a new triangle from three Cartesian space coordinates
+ #[must_use]
+ pub fn from_coordinates(
+ a: Vec3,
+ b: Vec3,
+ c: Vec3,
+ material: &'scene Material,
+ ) -> Triangle<'scene> {
+ // Coordinate transform: from absolute coordinates to relative coordinates
+ let q: Position = a;
+ let u: Vec3 = b - q;
+ let v: Vec3 = c - q;
+ Triangle::new(q, u, v, material)
+ }
+}
+
+impl<'scene> HitableTrait for Triangle<'scene> {
+ /// Hit method for the triangle
+ #[must_use]
+ fn hit(
+ &self,
+ ray: &Ray,
+ distance_min: Float,
+ distance_max: Float,
+ _rng: &mut SmallRng,
+ ) -> Option<HitRecord> {
+ let denom = self.normal.dot(&ray.direction);
+
+ // No hit if the ray is parallel to the plane.
+ if denom.abs() < EPSILON_RECT_THICKNESS {
+ return None;
+ }
+
+ // Return false if the hit point parameter t is outside the ray interval
+ let t = (-self.d - self.normal.dot(&ray.origin)) / denom;
+ if t < distance_min || t > distance_max {
+ return None;
+ }
+
+ // Determine the hit point lies within the planar shape using its plane coordinates.
+ let intersection: Position = ray.evaluate(t);
+ let planar_hitpt_vector: Position = intersection - self.q;
+ let alpha: Float = self.w.dot(&planar_hitpt_vector.cross(&self.v));
+ let beta: Float = self.w.dot(&self.u.cross(&planar_hitpt_vector));
+
+ // Do we hit a coordinate within the surface of the plane?
+ if !hit_ab(alpha, beta) {
+ return None;
+ }
+
+ // Ray hits the 2D shape; set the rest of the hit record and return
+ let mut record = HitRecord {
+ distance: t,
+ position: intersection,
+ u: alpha,
+ v: beta,
+ material: self.material,
+ normal: self.normal,
+ front_face: false,
+ };
+ record.set_face_normal(ray, self.normal);
+
+ Some(record)
+ }
+
+ /// Returns the bounding box of the triangle
+ #[must_use]
+ fn bounding_box(&self, _t0: Float, _t1: Float) -> Option<&AABB> {
+ // TODO: this is from quad and not updated!
+ // although i guess a triangle's aabb is the same as the quad's aabb in worst case
+ Some(&self.aabb)
+ }
+
+ /// Returns a probability density function value? // TODO: understand & explain
+ #[must_use]
+ fn pdf_value(
+ &self,
+ origin: Position,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ let ray = Ray {
+ origin,
+ direction,
+ time,
+ wavelength,
+ };
+ // TODO: this is from quad and not updated!
+ match self.hit(&ray, EPSILON_SHADOW_ACNE, Float::INFINITY, rng) {
+ Some(hit_record) => {
+ let distance_squared =
+ hit_record.distance * hit_record.distance * direction.norm_squared();
+ let cosine = direction.dot(&hit_record.normal).abs() / direction.magnitude();
+
+ distance_squared / (cosine * self.area)
+ }
+ None => 0.0,
+ }
+ }
+
+ /// Returns a random point on the triangle surface
+ #[must_use]
+ fn random(&self, origin: Position, rng: &mut SmallRng) -> Position {
+ let mut a = rng.gen::<Float>();
+ let mut b = rng.gen::<Float>();
+ if a + b > 1.0 {
+ a = 1.0 - a;
+ b = 1.0 - b;
+ }
+
+ let point: Position = self.q + (a * self.u) + (b * self.v);
+
+ point - origin
+ }
+}
+
+#[must_use]
+fn hit_ab(a: Float, b: Float) -> bool {
+ // Given the hit point in plane coordinates, return false if it is outside the
+ // primitive, otherwise return true.
+ // Triangle: a+b must be <=1.0
+ (0.0..=1.0).contains(&a) && (0.0..=1.0).contains(&b) && (a + b <= 1.0)
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::boxed::Box;
+ use rand::SeedableRng;
+
+ use crate::interval::Interval;
+
+ use super::*;
+
+ const TIME_0: Float = 0.0;
+ const TIME_1: Float = 1.0;
+ const RAY: Ray = Ray {
+ origin: Position::new(0.0, 0.0, -1.0),
+ direction: Unit::new_unchecked(Vec3::new(0.0, 0.0, 1.0)),
+ time: TIME_0,
+ wavelength: 600,
+ };
+
+ #[test]
+ fn xy_unit_triangle() {
+ let mut rng = SmallRng::from_entropy();
+ let material = Box::default();
+
+ // Unit triangle at origin
+ let triangle = Triangle::new(
+ Vec3::new(0.0, 0.0, 0.0),
+ Vec3::new(1.0, 0.0, 0.0),
+ Vec3::new(0.0, 1.0, 0.0),
+ &material,
+ );
+
+ let aabb = triangle
+ .bounding_box(TIME_0, TIME_1)
+ .expect("No AABB for the triangle");
+
+ let expected_aabb = AABB::new(
+ Interval::new(0.0, 1.0),
+ Interval::new(0.0, 1.0),
+ Interval::new(0.0, 0.0).expand(EPSILON_RECT_THICKNESS),
+ );
+
+ assert_eq!(aabb, &expected_aabb);
+
+ let boxhit = aabb.hit(&RAY, TIME_0, TIME_1);
+ assert!(boxhit);
+
+ let hit_record = triangle
+ .hit(&RAY, Float::NEG_INFINITY, Float::INFINITY, &mut rng)
+ .expect("No hit record for triangle and ray");
+
+ assert!(hit_record.distance - 1.0 <= Float::EPSILON);
+ assert_eq!(hit_record.position, Vec3::new(0.0, 0.0, 0.0));
+ assert_eq!(
+ hit_record.normal,
+ Unit::new_normalize(Vec3::new(0.0, 0.0, -1.0))
+ );
+ assert!(!hit_record.front_face);
+ }
+
+ #[test]
+ fn yx_unit_triangle() {
+ let mut rng = SmallRng::from_entropy();
+ let material = Box::default();
+
+ // Unit triangle at origin, u and v coords swapped
+ let triangle = Triangle::new(
+ Vec3::new(0.0, 0.0, 0.0),
+ Vec3::new(0.0, 1.0, 0.0),
+ Vec3::new(1.0, 0.0, 0.0),
+ &material,
+ );
+
+ let aabb = triangle
+ .bounding_box(TIME_0, TIME_1)
+ .expect("No AABB for the triangle");
+
+ let expected_aabb = AABB::new(
+ Interval::new(0.0, 1.0),
+ Interval::new(0.0, 1.0),
+ Interval::new(0.0, 0.0).expand(EPSILON_RECT_THICKNESS),
+ );
+
+ assert_eq!(aabb, &expected_aabb);
+
+ let boxhit = aabb.hit(&RAY, TIME_0, TIME_1);
+ assert!(boxhit);
+
+ let hit_record = triangle
+ .hit(&RAY, Float::NEG_INFINITY, Float::INFINITY, &mut rng)
+ .expect("No hit record for triangle and ray");
+
+ assert!(hit_record.distance - 1.0 <= Float::EPSILON);
+ assert_eq!(hit_record.position, Position::new(0.0, 0.0, 0.0));
+ assert_eq!(
+ hit_record.normal,
+ Unit::new_normalize(Vec3::new(0.0, 0.0, -1.0))
+ );
+ assert!(hit_record.front_face);
+ }
+
+ #[test]
+ fn neg_xy_unit_triangle() {
+ let mut rng = SmallRng::from_entropy();
+ let material: Box<Material> = Box::default();
+
+ // Unit triangle at origin, u and v coords swapped
+ let triangle = Triangle::new(
+ Vec3::new(0.0, 0.0, 0.0),
+ Vec3::new(-1.0, 0.0, 0.0),
+ Vec3::new(0.0, -1.0, 0.0),
+ &material,
+ );
+
+ let aabb = triangle
+ .bounding_box(TIME_0, TIME_1)
+ .expect("No AABB for the triangle");
+
+ let expected_aabb = AABB::new(
+ Interval::new(-1.0, 0.0),
+ Interval::new(-1.0, 0.0),
+ Interval::new(0.0, 0.0).expand(EPSILON_RECT_THICKNESS),
+ );
+
+ assert_eq!(aabb, &expected_aabb);
+
+ let boxhit = aabb.hit(&RAY, TIME_0, TIME_1);
+ assert!(boxhit);
+
+ let hit_record = triangle
+ .hit(&RAY, Float::NEG_INFINITY, Float::INFINITY, &mut rng)
+ .expect("No hit record for triangle and ray");
+
+ assert!(hit_record.distance - 1.0 <= Float::EPSILON);
+ assert_eq!(hit_record.position, Position::new(0.0, 0.0, 0.0));
+ assert_eq!(
+ hit_record.normal,
+ Unit::new_normalize(Vec3::new(0.0, 0.0, -1.0))
+ );
+ assert!(!hit_record.front_face);
+ }
+
+ #[test]
+ fn neg_yx_unit_triangle() {
+ let mut rng = SmallRng::from_entropy();
+ let material: Box<Material> = Box::default();
+
+ // Unit triangle at origin, u and v coords swapped
+ let triangle = Triangle::new(
+ Vec3::new(0.0, 0.0, 0.0),
+ Vec3::new(0.0, -1.0, 0.0),
+ Vec3::new(-1.0, 0.0, 0.0),
+ &material,
+ );
+
+ let aabb = triangle
+ .bounding_box(TIME_0, TIME_1)
+ .expect("No AABB for the triangle");
+
+ let expected_aabb = AABB::new(
+ Interval::new(-1.0, 0.0),
+ Interval::new(-1.0, 0.0),
+ Interval::new(0.0, 0.0).expand(EPSILON_RECT_THICKNESS),
+ );
+
+ assert_eq!(aabb, &expected_aabb);
+
+ let boxhit = aabb.hit(&RAY, TIME_0, TIME_1);
+ assert!(boxhit);
+
+ let hit_record = triangle
+ .hit(&RAY, Float::NEG_INFINITY, Float::INFINITY, &mut rng)
+ .expect("No hit record for triangle and ray");
+
+ assert!(hit_record.distance - 1.0 <= Float::EPSILON);
+ assert_eq!(hit_record.position, Position::new(0.0, 0.0, 0.0));
+ assert_eq!(
+ hit_record.normal,
+ Unit::new_normalize(Vec3::new(0.0, 0.0, -1.0))
+ );
+ assert!(hit_record.front_face);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +
//! Orthonormal bases
+
+use nalgebra::Unit;
+
+use crate::{Direction, Vec3};
+
+#[derive(Debug, Clone)]
+/// An orthonormal basis structure.
+pub struct ONB {
+ /// U
+ pub u: Direction,
+ /// V
+ pub v: Direction,
+ /// W
+ pub w: Direction,
+}
+
+// TODO: understand, explain
+
+impl ONB {
+ /// Builds a new [ONB] structure given a normal vector.
+ #[must_use]
+ pub fn build_from_w(w: Direction) -> ONB {
+ let a: Direction = if (w.x).abs() > 0.9 {
+ Unit::new_normalize(Vec3::new(0.0, 1.0, 0.0))
+ } else {
+ Unit::new_normalize(Vec3::new(1.0, 0.0, 0.0))
+ };
+ let v = Unit::new_normalize(w.cross(&a));
+ let u = Unit::new_normalize(w.cross(&v));
+
+ ONB { u, v, w }
+ }
+
+ /// Returns the ONB-projected version of the provided vector?
+ #[must_use]
+ pub fn local(&self, vec: Direction) -> Direction {
+ let d = vec.x * *self.u + vec.y * *self.v + vec.z * *self.w;
+ Unit::new_normalize(d)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +
//! Probability density functions
+
+#![allow(missing_docs)] // TODO: Lots of undocumented things for now
+
+use crate::{
+ hitable::{Hitable, HitableTrait},
+ onb::ONB,
+ random::{random_cosine_direction, random_unit_vector},
+ wavelength::Wavelength,
+ Box, Direction, Float, Position, PI,
+};
+use enum_dispatch::enum_dispatch;
+use rand::rngs::SmallRng;
+use rand::Rng;
+
+#[enum_dispatch(PDFTrait)]
+#[derive(Debug, Clone)]
+pub enum PDF<'scene> {
+ CosinePDF(CosinePDF),
+ SpherePDF(SpherePDF),
+ HitablePDF(HitablePDF<'scene>),
+ MixturePDF(MixturePDF<'scene>),
+ ZeroPDF(ZeroPDF),
+}
+
+#[enum_dispatch]
+pub trait PDFTrait {
+ #[must_use]
+ fn value(
+ &self,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float;
+
+ #[must_use]
+ fn generate(&self, rng: &mut SmallRng) -> Position;
+}
+
+#[derive(Debug, Clone)]
+pub struct CosinePDF {
+ uvw: ONB,
+}
+
+impl CosinePDF {
+ #[must_use]
+ pub fn new(w: Direction) -> Self {
+ CosinePDF {
+ uvw: ONB::build_from_w(w),
+ }
+ }
+}
+
+impl PDFTrait for CosinePDF {
+ #[must_use]
+ fn value(
+ &self,
+ direction: Direction,
+ _wavelength: Wavelength,
+ _time: Float,
+ _rng: &mut SmallRng,
+ ) -> Float {
+ let cosine = direction.normalize().dot(&self.uvw.w);
+ if cosine <= 0.0 {
+ 0.0
+ } else {
+ cosine / PI
+ }
+ }
+
+ #[must_use]
+ fn generate(&self, rng: &mut SmallRng) -> Position {
+ *self.uvw.local(random_cosine_direction(rng))
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct HitablePDF<'scene> {
+ origin: Position,
+ hitable: &'scene Hitable<'scene>,
+}
+
+impl<'scene> HitablePDF<'scene> {
+ #[must_use]
+ pub fn new(hitable: &'scene Hitable, origin: Position) -> Self {
+ HitablePDF { origin, hitable }
+ }
+}
+
+impl<'scene> PDFTrait for HitablePDF<'scene> {
+ #[must_use]
+ fn value(
+ &self,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ self.hitable
+ .pdf_value(self.origin, direction, wavelength, time, rng)
+ }
+
+ #[must_use]
+ fn generate(&self, rng: &mut SmallRng) -> Position {
+ self.hitable.random(self.origin, rng)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct MixturePDF<'scene> {
+ // Box to prevent infinite size
+ pdf1: Box<PDF<'scene>>,
+ pdf2: Box<PDF<'scene>>,
+}
+
+impl<'scene> MixturePDF<'scene> {
+ #[must_use]
+ pub fn new(pdf1: PDF<'scene>, pdf2: PDF<'scene>) -> Self {
+ MixturePDF {
+ pdf1: Box::new(pdf1),
+ pdf2: Box::new(pdf2),
+ }
+ }
+}
+
+impl<'scene> PDFTrait for MixturePDF<'scene> {
+ #[must_use]
+ fn value(
+ &self,
+ direction: Direction,
+ wavelength: Wavelength,
+ time: Float,
+ rng: &mut SmallRng,
+ ) -> Float {
+ 0.5 * self.pdf1.value(direction, wavelength, time, rng)
+ + 0.5 * self.pdf2.value(direction, wavelength, time, rng)
+ }
+
+ #[must_use]
+ fn generate(&self, rng: &mut SmallRng) -> Position {
+ if rng.gen::<bool>() {
+ self.pdf1.generate(rng)
+ } else {
+ self.pdf2.generate(rng)
+ }
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct SpherePDF {}
+
+impl SpherePDF {
+ #[must_use]
+ pub fn new() -> Self {
+ SpherePDF {}
+ }
+}
+
+impl PDFTrait for SpherePDF {
+ #[must_use]
+ fn value(
+ &self,
+ _direction: Direction,
+ _wavelength: Wavelength,
+ _time: Float,
+ _rng: &mut SmallRng,
+ ) -> Float {
+ 1.0 / (4.0 * PI)
+ }
+
+ #[must_use]
+ fn generate(&self, rng: &mut SmallRng) -> Position {
+ // TODO: verify correctness! radius?
+ *random_unit_vector(rng)
+ }
+}
+
+// TODO: this is an ugly hack due to tutorial saying `srec.pdf_ptr = 0;` in 12.2 Handling Specular for Metal
+#[derive(Debug, Clone)]
+pub struct ZeroPDF {}
+
+impl ZeroPDF {
+ #[must_use]
+ pub fn new() -> Self {
+ ZeroPDF {}
+ }
+}
+
+impl PDFTrait for ZeroPDF {
+ #[must_use]
+ fn value(
+ &self,
+ _direction: Direction,
+ _wavelength: Wavelength,
+ _time: Float,
+ _rng: &mut SmallRng,
+ ) -> Float {
+ 0.0
+ }
+
+ #[must_use]
+ fn generate(&self, rng: &mut SmallRng) -> Position {
+ *random_unit_vector(rng)
+ }
+}
+
+impl Default for ZeroPDF {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +
//! Various internal helper functions for getting specific kinds of random values.
+
+use crate::{Direction, Float, Vec2, Vec3, PI};
+use nalgebra::Unit;
+use rand::rngs::SmallRng;
+use rand::Rng;
+use rand_distr::{Distribution, UnitDisc, UnitSphere};
+
+/// Internal helper.
+#[must_use]
+pub fn random_unit_vector(rng: &mut SmallRng) -> Direction {
+ let v = UnitSphere.sample(rng).into();
+ Unit::new_normalize(v)
+}
+
+/// Internal helper.
+#[must_use]
+pub fn random_in_unit_disk(rng: &mut SmallRng) -> Vec2 {
+ let v: [Float; 2] = UnitDisc.sample(rng);
+ Vec2::new(v[0], v[1])
+}
+
+/// Internal helper.
+#[must_use]
+pub fn random_cosine_direction(rng: &mut SmallRng) -> Direction {
+ let r1: Float = rng.gen();
+ let r2: Float = rng.gen();
+ let z = (1.0 - r2).sqrt();
+
+ let phi = 2.0 * PI * r1;
+ let x = phi.cos() * r2.sqrt();
+ let y = phi.sin() * r2.sqrt();
+
+ let v: Vec3 = Vec3::new(x, y, z);
+ Unit::new_normalize(v)
+}
+
+/// Internal helper.
+#[must_use]
+pub fn random_on_hemisphere(normal: Vec3, rng: &mut SmallRng) -> Direction {
+ let in_unit_sphere: Direction = random_unit_vector(rng);
+ if in_unit_sphere.dot(&normal) > 0.0 {
+ // In the same hemisphere as the normal
+ in_unit_sphere
+ } else {
+ -in_unit_sphere
+ }
+}
+
//! The very core of the ray tracing rendering itself: the [Ray]
+
+use crate::{wavelength::Wavelength, Direction, Float, Position};
+
+/// A Ray has an origin and a direction, as well as an instant in time it exists in. Motion blur is achieved by creating multiple rays with slightly different times.
+#[derive(Clone, Debug, PartialEq)]
+pub struct Ray {
+ /// The origin of the ray.
+ pub origin: Position,
+ /// The direction of the ray.
+ pub direction: Direction,
+ /// The time instant at which the ray exists.
+ pub time: Float,
+ /// Wavelength of the ray
+ pub wavelength: Wavelength,
+}
+
+impl Ray {
+ /// Evaluates the position (coordinate) at which the ray is at the given parameter, considering the origin and direction. Considering a default unit speed of 1 per unit time, this function can be given either a time or a distance.
+ #[must_use]
+ pub fn evaluate(&self, parameter: Float) -> Position {
+ self.origin + parameter * *self.direction
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +
//! A collection of objects, camera, and other things necessary to describe the environment you wish to render.
+
+use alloc::boxed::Box;
+
+use crate::{
+ bvhnode::BVHNode,
+ camera::{Camera, CameraInit},
+ hitable::Hitable,
+ materials::SharedMaterial,
+ objects::{object_to_hitable, Object},
+ Float, Vec,
+};
+
+use palette::Srgb;
+#[cfg(feature = "traces")]
+use tracing::info;
+
+#[derive(Debug)]
+/// A representation of the scene that is being rendered.
+pub struct Scene<'scene> {
+ /// Bounding-volume hierarchy of [Hitable] objects in the scene. This could, as currently written, be any [Hitable] - in practice, we place the root of the [`BVHNode`] tree here.
+ pub hitables: BVHNode<'scene>,
+ /// The camera object used for rendering the scene.
+ pub camera: Camera,
+ /// The background color to use when the rays do not hit anything in the scene.
+ pub background_color: Srgb, // TODO: make into Texture or something?
+ /// A [`BVHNode`] tree of prioritized objects - e.g. glass items or lights - that affect the biased sampling of the scene. Wrapped into a [Hitable] for convenience reasons (see various PDF functions).
+ pub priority_hitables: Hitable<'scene>,
+}
+
+impl<'scene> Scene<'scene> {
+ /// Creates a new [Scene] with the given parameters.
+ #[must_use]
+ pub fn new(
+ time_0: Float,
+ time_1: Float,
+ camera: Camera,
+ hitables: Vec<Hitable<'scene>>,
+ priority_hitables: Vec<Hitable<'scene>>,
+ background_color: Srgb,
+ ) -> Scene<'scene> {
+ Scene {
+ hitables: BVHNode::from_list(hitables, time_0, time_1),
+ camera,
+ background_color,
+ priority_hitables: Hitable::BVHNode(BVHNode::from_list(
+ priority_hitables,
+ time_0,
+ time_1,
+ )),
+ }
+ }
+}
+
+// TODO: better naming
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// A serialized representation of a [Scene].
+pub struct SceneFile {
+ time_0: Float,
+ time_1: Float,
+ background_color: Srgb,
+ camera: CameraInit,
+ objects: Vec<Object>,
+ #[cfg_attr(feature = "serde-derive", serde(default))]
+ materials: Vec<SharedMaterial>,
+}
+
+/// Initializes a new [Scene] instance by parsing the contents of a [`SceneFile`] structure and then using those details to construct the [Scene].
+#[must_use]
+pub fn initialize<'scene>(scene_file: SceneFile, width: u32, height: u32) -> Scene<'scene> {
+ let time_0 = scene_file.time_0;
+ let time_1 = scene_file.time_1;
+ let background_color = scene_file.background_color;
+
+ #[allow(clippy::cast_precision_loss)]
+ let camera = Camera::new(
+ scene_file.camera.look_from,
+ scene_file.camera.look_at,
+ scene_file.camera.up,
+ scene_file.camera.vertical_fov,
+ width as Float / height as Float,
+ scene_file.camera.aperture,
+ scene_file.camera.focus_distance,
+ time_0,
+ time_1,
+ );
+ let mut materials = scene_file.materials;
+ materials.push(SharedMaterial::default());
+ let materials = Box::leak(Box::new(materials));
+
+ #[cfg(feature = "traces")]
+ info!("Creating a flattened list from the objects");
+ let mut hitables: Vec<Hitable> = Vec::new();
+ let mut priority_hitables: Vec<Hitable> = Vec::new();
+
+ // TODO: this isn't the greatest ergonomics, but it gets the job done for now
+ for object in scene_file.objects {
+ if match &object {
+ Object::Boxy(i) => i.priority,
+ Object::ConstantMedium(i) => i.priority,
+ Object::MovingSphere(i) => i.priority,
+ Object::ObjectList(i) => i.priority,
+ Object::Quad(i) => i.priority,
+ Object::RotateY(i) => i.priority,
+ Object::Sphere(i) => i.priority,
+ #[cfg(feature = "stl")]
+ Object::STL(i) => i.priority,
+ #[cfg(feature = "gl_tf")]
+ Object::GLTF(i) => i.priority,
+ Object::Translate(i) => i.priority,
+ Object::Triangle(i) => i.priority,
+ } {
+ let hitable = object_to_hitable(object, materials);
+ hitables.push(hitable.clone());
+ priority_hitables.push(hitable);
+ } else {
+ let hitable = object_to_hitable(object, materials);
+ hitables.push(hitable.clone());
+ }
+ }
+
+ Scene::new(
+ time_0,
+ time_1,
+ camera,
+ hitables,
+ priority_hitables,
+ background_color,
+ )
+}
+
//! Utilities for [Physically Meaningful Rendering using Tristimulus Colours](https://doi.org/10.1111/cgf.12676)
+
+#![allow(clippy::cast_precision_loss)]
+
+use palette::{white_point::E, Xyz};
+
+use crate::{wavelength::Wavelength, Float};
+
+use self::spectra_xyz_5nm_380_780_097::equal_energy_reflectance;
+
+pub mod spectra_xyz_5nm_380_780_097;
+pub mod spectrum_grid;
+
+/// Evaluate the spectrum at the given wavelength for the given XYZ color
+#[must_use]
+pub fn spectrum_xyz_to_p(lambda: Wavelength, xyz: Xyz<E>) -> Float {
+ // Currently, the data is only built for 5nm intervals
+ // TODO: generate a file with 1nm intervals?
+ let lambda: f64 = lambda as f64;
+ let xyz: [f64; 3] = [f64::from(xyz.x), f64::from(xyz.y), f64::from(xyz.z)];
+ let p = spectrum_grid::spectrum_xyz_to_p(lambda, xyz) / equal_energy_reflectance;
+
+ #[allow(clippy::cast_possible_truncation)]
+ let p = p as Float;
+ p
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +
//! Hand-converted from `spectra_xyz_5nm_380_780_0.97.h` in the supplemental material from [Physically Meaningful Rendering using Tristimulus Colours](https://doi.org/10.1111/cgf.12676)
+
+#![allow(clippy::unreadable_literal)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_upper_case_globals)]
+#![allow(non_snake_case)]
+
+// Basic info on the spectrum grid.
+pub const spectrum_grid_width: usize = 12;
+pub const spectrum_grid_height: usize = 14;
+pub const spectrum_grid_width_f: f64 = 12.0;
+pub const spectrum_grid_height_f: f64 = 14.0;
+
+// The spectra here have these properties.
+pub const spectrum_sample_min: f64 = 380.0;
+pub const spectrum_sample_max: f64 = 780.0;
+pub const spectrum_bin_size: f64 = 5.0;
+pub const spectrum_num_samples: usize = 81;
+pub const spectrum_num_samples_f: f64 = 81.0;
+
+// xy* color space.
+const spectrum_mat_xy_to_xystar: [f64; 6] = [
+ 0.9088957044314135,
+ 0.4170234986977646,
+ -0.441973067709726,
+ -0.4170234986977646,
+ 0.9088957044314135,
+ -0.1639574019112163,
+];
+const spectrum_mat_xystar_to_xy: [f64; 6] = [
+ 0.9088957044314137,
+ -0.41702349869776467,
+ 0.33333333333333337,
+ 0.41702349869776467,
+ 0.9088957044314137,
+ 0.3333333333333333,
+];
+// uv color space.
+const spectrum_mat_xy_to_uv: [f64; 6] = [
+ 16.642783858871084,
+ 7.636114813898141,
+ -2.0929662242564078,
+ -7.382929931517094,
+ 16.090971664254482,
+ 1.0973194224208704,
+];
+const spectrum_mat_uv_to_xy: [f64; 6] = [
+ 0.04963661179157505,
+ -0.023555498979304087,
+ 0.1297356585010993,
+ 0.022774487118711398,
+ 0.05133881401140044,
+ -0.008668845424536832,
+];
+
+// apply a 3x2 matrix to a 2D color.
+pub fn spectrum_apply_3x2(matrix: [f64; 6], src: [f64; 2], tgt: &mut [f64; 2]) {
+ tgt[0] = matrix[0] * src[0] + matrix[1] * src[1] + matrix[2];
+ tgt[1] = matrix[3] * src[0] + matrix[4] * src[1] + matrix[5];
+}
+
+// Concrete conversion routines.
+pub fn spectrum_xy_to_xystar(xy: [f64; 2], xystar: &mut [f64; 2]) {
+ spectrum_apply_3x2(spectrum_mat_xy_to_xystar, xy, xystar);
+}
+pub fn spectrum_xystar_to_xy(xystar: [f64; 2], xy: &mut [f64; 2]) {
+ spectrum_apply_3x2(spectrum_mat_xystar_to_xy, xystar, xy);
+}
+pub fn spectrum_xy_to_uv(xy: [f64; 2], uv: &mut [f64; 2]) {
+ spectrum_apply_3x2(spectrum_mat_xy_to_uv, xy, uv);
+}
+pub fn spectrum_uv_to_xy(uv: [f64; 2], xy: &mut [f64; 2]) {
+ spectrum_apply_3x2(spectrum_mat_uv_to_xy, uv, xy);
+}
+
+// Grid cells. Laid out in row-major format.
+// num_points = 0 for cells without data points.
+#[derive(Debug)]
+pub struct spectrum_grid_cell_t {
+ pub inside: usize,
+ pub num_points: usize,
+ pub idx: [isize; 6],
+}
+
+#[rustfmt::skip]
+pub const spectrum_grid: [spectrum_grid_cell_t; 168] = [
+ spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [148, 110, 0, 12, 111, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [0, 1, 12, 13, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [1, 2, 13, 14, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [2, 3, 14, 15, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [3, 4, 15, 16, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [4, 5, 16, 17, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [5, 6, 17, 18, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [6, 7, 18, 19, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [7, 8, 19, 20, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [8, 9, 20, 21, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [9, 10, 21, 22, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [149, 10, 11, 145, 22, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [150, 111, 12, 23, 112, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [12, 13, 23, 24, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [13, 14, 24, 25, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [14, 15, 25, 26, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [15, 16, 26, 27, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [16, 17, 27, 28, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [17, 18, 28, 29, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [18, 19, 29, 30, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [19, 20, 30, 31, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [20, 21, 31, 32, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [21, 22, 32, 33, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [151, 22, 145, 146, 33, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [152, 112, 23, 34, 113, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [23, 24, 34, 35, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [24, 25, 35, 36, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [25, 26, 36, 37, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [26, 27, 37, 38, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [27, 28, 38, 39, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [28, 29, 39, 40, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [29, 30, 40, 41, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [30, 31, 41, 42, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [31, 32, 42, 43, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [153, 32, 33, 147, 141, 43], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [154, 33, 146, 147, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [155, 113, 34, 44, 114, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [34, 35, 44, 45, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [35, 36, 45, 46, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [36, 37, 46, 47, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [37, 38, 47, 48, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [38, 39, 48, 49, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [39, 40, 49, 50, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [40, 41, 50, 51, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [41, 42, 51, 52, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [42, 43, 52, 53, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [156, 43, 141, 142, 53, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [157, 114, 44, 54, 115, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [44, 45, 54, 55, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [45, 46, 55, 56, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [46, 47, 56, 57, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [47, 48, 57, 58, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [48, 49, 58, 59, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [49, 50, 59, 60, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [50, 51, 60, 61, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [51, 52, 61, 62, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [52, 53, 62, 63, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [158, 53, 142, 143, 63, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [159, 115, 54, 116, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [160, 116, 54, 55, 64, 117], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [55, 56, 64, 65, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [56, 57, 65, 66, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [57, 58, 66, 67, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [58, 59, 67, 68, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [59, 60, 68, 69, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [60, 61, 69, 70, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [61, 62, 70, 71, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [161, 62, 63, 144, 138, 71], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [162, 63, 143, 144, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [163, 117, 64, 72, 118, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [64, 65, 72, 73, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [65, 66, 73, 74, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [66, 67, 74, 75, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [67, 68, 75, 76, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [68, 69, 76, 77, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [69, 70, 77, 78, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [70, 71, 78, 79, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [164, 71, 138, 139, 79, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [165, 118, 72, 80, 119, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [72, 73, 80, 81, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [73, 74, 81, 82, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [74, 75, 82, 83, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [75, 76, 83, 84, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [76, 77, 84, 85, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [77, 78, 85, 86, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [166, 78, 79, 140, 134, 86], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [167, 79, 139, 140, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [168, 119, 80, 120, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [169, 80, 81, 87, 121, 120], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [81, 82, 87, 88, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [82, 83, 88, 89, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [83, 84, 89, 90, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [84, 85, 90, 91, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [85, 86, 91, 92, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [170, 86, 134, 135, 92, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [171, 121, 87, 93, 122, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [87, 88, 93, 94, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [88, 89, 94, 95, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [89, 90, 95, 96, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [90, 91, 96, 97, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [91, 92, 97, 98, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [172, 92, 135, 136, 98, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [173, 122, 93, 99, 123, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [93, 94, 99, 100, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [94, 95, 100, 101, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [95, 96, 101, 102, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [96, 97, 102, 103, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [174, 97, 98, 137, 131, 103], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [175, 98, 136, 137, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [176, 123, 99, 124, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [177, 124, 99, 100, 104, 125], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [100, 101, 104, 105, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [101, 102, 105, 106, -1, -1], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [102, 103, 106, 107, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [178, 103, 131, 132, 107, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [179, 125, 104, 126, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [180, 104, 105, 108, 127, 126], }, spectrum_grid_cell_t { inside: 1, num_points: 4, idx: [105, 106, 108, 109, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 6, idx: [181, 106, 107, 133, 129, 109], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [182, 107, 132, 133, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [183, 127, 108, 128, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 5, idx: [184, 108, 109, 130, 128, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 4, idx: [185, 109, 129, 130, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], }, spectrum_grid_cell_t { inside: 0, num_points: 0, idx: [-1, -1, -1, -1, -1, -1], },
+];
+
+// Grid data points.
+#[derive(Debug)]
+pub struct spectrum_data_point_t {
+ pub xystar: [f64; 2],
+ pub uv: [f64; 2],
+ pub spectrum: [f64; 81], // X+Y+Z = 1
+}
+
+#[rustfmt::skip]
+pub const spectrum_data_points: [spectrum_data_point_t; 186] = [
+ spectrum_data_point_t { xystar: [-0.2730599976959221, -0.22593929649394454], uv: [1.0, 0.0], spectrum: [ 0.0254585661435, 0.0254522837222, 0.0254350677521, 0.0253976483946, 0.025323907854, 0.0251819538591, 0.0249304383689, 0.0244765987892, 0.0236680837374, 0.0222722024021, 0.020026196415, 0.0168335337637, 0.0128503686647, 0.00847600843427, 0.00430773842015, 0.00115019839385, 0.0, 0.0, 0.0, 0.0, 0.0, 6.110194812e-19, 0.0, 0.0, 0.0, 0.0, 4.96529841167e-18, 0.0, 0.0, 4.97688472401e-17, 6.42158160797e-18, 0.0, 2.70243428816e-17, 0.0, 8.66880413823e-18, 0.0, 5.94822166417e-18, 1.49646918938e-17, 0.0, 0.0, 0.0, 2.66043174773e-17, 4.07611299633e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.3067101609e-19, 6.10705373879e-19, 1.22579462575e-18, 0.000222158936213, 0.000780519255798, 0.00142595962105, 0.00203306327779, 0.00254859436028, 0.00295991785315, 0.0032784444078, 0.00351968511024, 0.00369934230045, 0.00382942826596, 0.00392212358524, 0.00398812362781, 0.00403503310349, 0.00406829810979, 0.00409198942868, 0.00410836277454, 0.0041198306567, 0.00412794441995, 0.00413346398971, 0.00413715864266, 0.00413970729069, 0.0041413984228, 0.00414251331383, 0.00414349789867, 0.00414389409983, 0.00414406911746, 0.00414408142048, 0.00414403637657, 0.004143986692 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, -0.22593929649394454], uv: [1.9999999999999991, 0.0], spectrum: [ 0.025703212686, 0.0256958123579, 0.0256754714378, 0.0256307574533, 0.0255433019159, 0.0253755794002, 0.025079069654, 0.0245453008523, 0.0235953544541, 0.0219602062429, 0.0193528399602, 0.0157020705667, 0.01125698102, 0.00657370479699, 0.0024618841587, 4.91278504304e-18, 0.0, 9.89913688466e-19, 0.0, 9.56279823772e-18, 0.0, 1.07761566206e-17, 0.0, 0.0, 0.0, 7.27442750244e-18, 0.0, 0.0, 0.0, 0.0, 3.50560885187e-17, 1.61118947862e-17, 0.0, 0.0, 9.23262376455e-17, 1.24122622978e-16, 1.10157773429e-16, 6.82656157849e-17, 0.0, 0.0, 0.0, 0.0, 1.77980795897e-17, 0.0, 0.0, 5.39515326386e-18, 0.0, 1.60374413182e-17, 1.26174957953e-17, 0.0, 0.00017801467656, 0.00166387500998, 0.00367423897568, 0.00573129520175, 0.00758006831586, 0.00912366609602, 0.0103482843063, 0.0112851181961, 0.0119877454231, 0.0125087134888, 0.0128904161959, 0.0131640314008, 0.0133579452518, 0.0134954912424, 0.0135935314476, 0.0136630487119, 0.0137120620992, 0.0137469335741, 0.0137712316315, 0.0137888561752, 0.0138011448892, 0.013809269721, 0.0138151723777, 0.0138187163145, 0.013821109359, 0.013823076674, 0.0138240636589, 0.0138246033642, 0.0138249160415, 0.0138250323546, 0.0138253404443 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, -0.22593929649394454], uv: [3.0, 0.0], spectrum: [ 0.0273015839617, 0.0272909016632, 0.0272627783319, 0.0272015940763, 0.0270826032284, 0.0268558259227, 0.0264557392346, 0.025736895219, 0.024459762729, 0.0222800642963, 0.018865035797, 0.0142191998826, 0.00884394451225, 0.00370682062664, 0.000162135082284, 1.65416834651e-17, 0.0, 1.91531797173e-18, 4.72427753235e-18, 0.0, 0.0, 3.04313213571e-17, 1.47708145718e-16, 1.4051315342e-16, 6.58767975115e-17, 0.0, 1.18960958141e-17, 1.15255889085e-16, 1.94853795902e-17, 0.0, 8.20141019166e-17, 0.0, 7.14866463642e-17, 0.0, 1.5632540932e-17, 0.0, 0.0, 0.0, 8.67480062718e-17, 0.0, 3.2299737916e-17, 1.30821128916e-16, 2.3444717416e-17, 1.15555448214e-17, 8.47684298339e-18, 4.02980464871e-18, 0.0, 3.60264171961e-17, 0.0, 2.03455285038e-17, 0.000590829809805, 0.00329081860838, 0.00680772730724, 0.0103587914006, 0.0135302271332, 0.016168601135, 0.0182562261668, 0.0198507331248, 0.0210470817991, 0.0219334156928, 0.0225818078691, 0.0230462024419, 0.0233761741504, 0.0236110816736, 0.0237775682178, 0.0238950208618, 0.023977210895, 0.0240350583992, 0.0240759151433, 0.0241043225528, 0.0241245482236, 0.0241384367901, 0.0241488837023, 0.0241559834645, 0.0241605593132, 0.024163017671, 0.0241633909172, 0.0241633173015, 0.0241646004231, 0.0241653268645, 0.0241654545742 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, -0.22593929649394454], uv: [3.9999999999999996, 0.0], spectrum: [ 0.0292172476514, 0.0292027005593, 0.0291648304843, 0.0290818838268, 0.0289171082704, 0.0286001877055, 0.0280378658396, 0.0270325685501, 0.0252674921324, 0.0222951298899, 0.0177550175831, 0.0118621347373, 0.00564140013975, 0.000880464827564, 0.0, 1.57043123017e-17, 1.6981481375e-18, 4.22789382289e-18, 0.0, 0.0, 2.16794680928e-17, 3.00401396716e-16, 1.40402888474e-16, 3.01340296417e-16, 1.85195273035e-16, 0.0, 7.39734400528e-17, 2.1707461176e-17, 1.36248306826e-16, 0.0, 0.0, 0.0, 5.16258776681e-17, 1.0911751589e-16, 0.0, 0.0, 2.53016676934e-16, 6.72967183504e-17, 8.94333998676e-17, 1.96842991909e-16, 0.0, 4.50481408826e-17, 0.0, 2.73593181097e-17, 0.0, 0.0, 1.99208945611e-17, 2.18188898375e-17, 7.02509560632e-17, 1.85246672914e-17, 0.0, 0.00375570918052, 0.00918013002447, 0.0148501874848, 0.0200006195184, 0.0243293419706, 0.027777160142, 0.0304215752458, 0.0324069334709, 0.0338789644763, 0.0349594246855, 0.0357382450642, 0.036292085401, 0.0366848202854, 0.0369627375642, 0.0371601771328, 0.0372997698954, 0.0373982391711, 0.0374665364183, 0.0375132822485, 0.03754637491, 0.0375676160202, 0.0375830896076, 0.0375932316943, 0.0375989076926, 0.037603775793, 0.0376085150451, 0.0376112268996, 0.0376136199132, 0.0376140835345, 0.0376138615994 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, -0.22593929649394454], uv: [5.0, 0.0], spectrum: [ 0.0319947912671, 0.031972888111, 0.0319152568658, 0.0317897922402, 0.0315444972706, 0.0310746312805, 0.0302486329565, 0.0287810136446, 0.0262224685696, 0.021987133602, 0.0157758902291, 0.0083486497378, 0.00190729060743, 0.0, 9.76878844319e-18, 1.4986393037e-17, 0.0, 0.0, 1.61872055124e-16, 4.07318009966e-17, 5.36629917985e-17, 1.41458964372e-16, 4.25041849411e-16, 4.02050909309e-16, 1.77833338239e-16, 0.0, 0.0, 0.0, 6.86380086555e-16, 1.39376513197e-15, 0.0, 0.0, 0.0, 4.51089755673e-16, 2.03497527304e-16, 1.54646019859e-16, 0.0, 1.53567443985e-16, 5.43288144149e-17, 0.0, 0.0, 1.03660823127e-16, 2.13836674987e-16, 0.0, 4.42389221806e-17, 0.0, 7.46715833319e-17, 7.05983931598e-17, 0.0, 4.23684949094e-17, 0.0, 0.00287282413321, 0.00993014942054, 0.0182601682041, 0.0262270604094, 0.0331101202341, 0.0386847489274, 0.0430092686054, 0.0462871140152, 0.0487359656759, 0.0505383093944, 0.0518345635124, 0.0527559334459, 0.0534116340728, 0.0538789261771, 0.0542101932192, 0.0544441754576, 0.0546084283259, 0.0547231882103, 0.0548032329068, 0.0548589410642, 0.0548975595968, 0.0549239138902, 0.054942222879, 0.0549548412603, 0.0549633351862, 0.0549691836946, 0.0549732634794, 0.0549758439358, 0.0549772782421, 0.0549778984077 ] }, spectrum_data_point_t { xystar: [0.0, -0.22593929649394454], uv: [6.0, 0.0], spectrum: [ 0.0337362695207, 0.033705308509, 0.0336251723538, 0.0334531844717, 0.0331193435471, 0.0324812154317, 0.0313626944383, 0.0293893129373, 0.0259821353036, 0.0204589636198, 0.0127665325009, 0.00460238343076, 0.0, 0.0, 0.0, 4.20650140412e-18, 1.03527382291e-17, 2.64577265852e-17, 9.25797438713e-17, 0.0, 2.18413763557e-17, 0.0, 2.05484725171e-16, 0.0, 8.94262649139e-17, 6.78032255625e-16, 0.0, 0.0, 0.0, 1.04902065121e-15, 0.0, 0.0, 0.0, 0.0, 0.0, 2.77995325641e-16, 6.39398236268e-16, 2.53631080707e-16, 6.25087313364e-16, 6.56056301013e-17, 5.43916149375e-16, 0.0, 0.0, 0.0, 1.73048817161e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.000526513917857, 0.00918548463373, 0.0208260903052, 0.0324928378915, 0.0428135326211, 0.0512862826491, 0.0579153676656, 0.0629708317891, 0.0667648306009, 0.0695674330034, 0.0715886773735, 0.073026736204, 0.0740499144551, 0.0747790771392, 0.0752958034878, 0.0756606902402, 0.0759180521534, 0.0760989817872, 0.0762253916593, 0.0763136492823, 0.0763744388629, 0.0764160807809, 0.0764449691519, 0.0764646375783, 0.0764780046467, 0.0764869198009, 0.07649281644, 0.0764962580195, 0.0764982582092, 0.0764990008958 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, -0.22593929649394454], uv: [6.999999999999999, 0.0], spectrum: [ 0.0370491213617, 0.037004149959, 0.0368868972596, 0.0366335576836, 0.0361428474736, 0.0352051748469, 0.033568022512, 0.0307046415968, 0.0258289749619, 0.0181566041349, 0.00828519947133, 0.0, 1.53653396174e-17, 3.16741138109e-17, 3.69846185468e-17, 3.07206552056e-18, 0.0, 8.90048364299e-17, 1.42228760564e-16, 0.0, 0.0, 8.04628132715e-16, 3.13650321597e-16, 0.0, 0.0, 2.08116080254e-15, 0.0, 3.89656550142e-16, 0.0, 0.0, 1.60741184505e-15, 2.70875538529e-16, 8.84032301654e-16, 0.0, 0.0, 0.0, 2.3496653485e-16, 0.0, 5.74876693894e-16, 9.72389342015e-16, 9.52322863699e-16, 0.0, 1.84349104556e-17, 0.0, 0.0, 0.0, 1.68507755794e-16, 0.0, 6.08222805419e-17, 0.0, 3.95636337354e-16, 0.0, 0.00635088634917, 0.0205951683775, 0.036752365582, 0.0518577483115, 0.0646384529837, 0.07482136171, 0.0826866403387, 0.0886437476255, 0.0930744866946, 0.0962854923306, 0.0985766256666, 0.100209959852, 0.101374703855, 0.102200019116, 0.102782706242, 0.103192387509, 0.103479347871, 0.103679721738, 0.103818911072, 0.1039151256, 0.103981482892, 0.104027090038, 0.104058853557, 0.104080846412, 0.104096017301, 0.104106200336, 0.104112476501, 0.104116343443, 0.104117862769 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, -0.22593929649394454], uv: [8.0, 0.0], spectrum: [ 0.0368902744869, 0.0368311764427, 0.0366777048889, 0.0363467253374, 0.035708402853, 0.034490469283, 0.0323767859305, 0.0287332344481, 0.022662677267, 0.0135697877335, 0.00354554029151, 1.30773152593e-16, 5.50232492303e-17, 0.0, 2.08036560891e-17, 9.54413790121e-17, 0.0, 6.57732655147e-17, 4.55702302408e-18, 0.0, 0.0, 0.0, 0.0, 1.52610131497e-15, 7.68127598397e-16, 3.1262333067e-15, 1.671401742e-15, 0.0, 3.83081903592e-16, 0.0, 8.33499909446e-16, 0.0, 3.59755453296e-16, 2.12056514795e-15, 6.70993224202e-16, 9.92000637553e-16, 0.0, 0.0, 6.77800203463e-16, 0.0, 2.06611543271e-15, 0.0, 1.70897605594e-17, 0.0, 8.16479017786e-16, 3.09366292851e-16, 0.0, 0.0, 0.0, 1.02695967386e-15, 0.0, 0.0, 0.00082766716114, 0.0175268405932, 0.0394028724852, 0.0609734613779, 0.0797181612257, 0.0948858791009, 0.106726058786, 0.115761237854, 0.122518498379, 0.12743345782, 0.130946320139, 0.133452401185, 0.13523960252, 0.136505742022, 0.137399229712, 0.138027451439, 0.138467577476, 0.138774907132, 0.138988605889, 0.139136835604, 0.13923917277, 0.139309793909, 0.139358870555, 0.139393268018, 0.139416476003, 0.139432544604, 0.139442391365, 0.139448338262, 0.139450688188 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, -0.22593929649394454], uv: [9.0, 0.0], spectrum: [ 0.0376545905526, 0.0375706477456, 0.0373530820132, 0.0368857493298, 0.035993830402, 0.0342994172816, 0.0313883735902, 0.0264818832246, 0.0186008746124, 0.00784310233523, 1.56174190013e-15, 1.49224846955e-16, 9.36567550174e-17, 5.68558706208e-16, 0.0, 0.0, 0.0, 2.05644240937e-16, 0.0, 0.0, 0.0, 5.85735861979e-16, 1.96049678331e-15, 0.0, 0.0, 0.0, 0.0, 7.46683287634e-15, 6.84582101646e-15, 0.0, 0.0, 0.0, 1.7550705372e-14, 6.3398082955e-16, 0.0, 0.0, 0.0, 2.86817664347e-15, 1.43672284961e-15, 5.68360808523e-15, 0.0, 0.0, 0.0, 7.92305345273e-16, 0.0, 1.02303322823e-15, 1.62569068191e-15, 3.79466967881e-16, 7.96620907545e-16, 1.95081046084e-16, 0.0, 3.00204424391e-16, 0.0, 0.0105393478538, 0.0363868311364, 0.0658479497484, 0.0931122030506, 0.115937677903, 0.134154871709, 0.148268814268, 0.158941591396, 0.166762934991, 0.172374320597, 0.176385140371, 0.179247298845, 0.1812752709, 0.182707124043, 0.183713843687, 0.184419374896, 0.184912516252, 0.185256539999, 0.185495022472, 0.185659747444, 0.18577292787, 0.185851039807, 0.185905212542, 0.185942085542, 0.185967296164, 0.185982914932, 0.185992780157, 0.185996762263 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, -0.22593929649394454], uv: [10.0, 0.0], spectrum: [ 0.034665808729, 0.034559476579, 0.0342862436974, 0.0337026255603, 0.0326010258466, 0.0305164278605, 0.0269724021235, 0.021152685968, 0.0122112864061, 0.00150315541515, 0.0, 2.6589560777e-15, 0.0, 1.60467374511e-15, 7.28676221146e-15, 0.0, 8.31303920914e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 7.87494091259e-15, 0.0, 0.0, 0.0, 0.0, 0.0, 1.34778218395e-14, 3.81624185694e-16, 8.48792256724e-15, 1.4232738218e-14, 0.0, 0.0, 0.0, 0.0, 0.0, 2.57433238906e-15, 1.86807910948e-15, 0.0, 0.0, 4.45921928876e-16, 0.0, 0.0, 1.10482105732e-15, 1.44595029353e-15, 0.0, 0.0, 1.08601118934e-15, 2.60261014912e-15, 0.0, 4.30941871895e-15, 3.19037661838e-15, 0.00127700838243, 0.0308826120767, 0.0692949175984, 0.106532631729, 0.138434836145, 0.164265518471, 0.184469345282, 0.199851701892, 0.211174539744, 0.219317070603, 0.225142288622, 0.229301016522, 0.232248712346, 0.234329947954, 0.235794023955, 0.236820629944, 0.237539755271, 0.238039289358, 0.238386628905, 0.238625310364, 0.238787412261, 0.238902012036, 0.238980896507, 0.239036086903, 0.239074586444, 0.239096715389, 0.239112697578, 0.239118807577 ] }, spectrum_data_point_t { xystar: [0.2730599976959221, -0.22593929649394454], uv: [11.0, 0.0], spectrum: [ 0.026622521527, 0.0264912261276, 0.0261578057518, 0.0254529121405, 0.0241594371452, 0.0217390020646, 0.017752842583, 0.011728629248, 0.00389292549461, 1.90772987891e-15, 0.0, 0.0, 0.0, 0.0, 2.26098780708e-14, 6.14914267782e-16, 0.0, 0.0, 5.45391070788e-15, 4.59497071466e-15, 0.0, 1.88391341712e-14, 6.36089223653e-15, 1.43083445864e-14, 0.0, 5.45291480142e-15, 0.0, 5.39111472265e-15, 0.0, 0.0, 0.0, 1.15995672037e-14, 0.0, 2.57001523924e-15, 2.16730327545e-14, 0.0, 7.87503366771e-15, 1.30896553583e-14, 3.37987402433e-15, 0.0, 5.14764764659e-15, 0.0, 0.0, 0.0, 1.34483723995e-15, 0.0, 4.73527625937e-16, 0.0, 4.63983547971e-16, 1.18431240908e-15, 5.90099797345e-15, 3.55253585192e-15, 7.5467478642e-15, 0.0, 0.0172179777888, 0.0619424979483, 0.112416189458, 0.158586433302, 0.19741889671, 0.228537471812, 0.252630721358, 0.270560614202, 0.283523078009, 0.292821209495, 0.299465568261, 0.304173621676, 0.307498378409, 0.309837912709, 0.311479295109, 0.312628481015, 0.313429671212, 0.313984894403, 0.314367857731, 0.314630402115, 0.314813455872, 0.31494164252, 0.315031841959, 0.315094810867, 0.3151337249, 0.315159022787, 0.315168914674 ] }, spectrum_data_point_t { xystar: [0.32767199723510654, -0.22593929649394454], uv: [12.0, 0.0], spectrum: [ 0.00813094897749, 0.00802437302395, 0.00776438303151, 0.00723588848047, 0.00634729938504, 0.00473860672718, 0.00237392534919, 1.22370313877e-15, 0.0, 0.0, 0.0, 0.0, 1.04774520779e-15, 0.0, 3.08795470432e-15, 1.25389829762e-14, 1.02987703315e-14, 0.0, 1.1204606546e-15, 1.24574397704e-14, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.79280167586e-15, 0.0, 0.0, 3.22221180153e-14, 0.0, 0.0, 2.92350630152e-14, 3.57764356753e-15, 0.0, 1.20422318725e-14, 0.0, 3.68533388834e-15, 0.0, 0.0, 0.0, 2.99102352474e-15, 1.30776870422e-14, 4.16255456584e-15, 1.38005915147e-15, 0.0, 2.99699137135e-15, 0.0, 0.0, 5.17263166272e-17, 0.0, 0.0, 0.0, 0.0, 0.00141517143732, 0.0521087392278, 0.116784137086, 0.178583733526, 0.231785220136, 0.275022768402, 0.308815996415, 0.334116183907, 0.352463161006, 0.365642909281, 0.375065324925, 0.38174109052, 0.386455597068, 0.389773836843, 0.392102007165, 0.393733062495, 0.394870597351, 0.395658546784, 0.39620342768, 0.396576433186, 0.396836852782, 0.397018854809, 0.397147391827, 0.397238722798, 0.39729439021, 0.397330819675, 0.397345809825 ] }, spectrum_data_point_t { xystar: [-0.2730599976959221, -0.1694544723704584], uv: [1.0, 1.0], spectrum: [ 0.0091653433919, 0.00916498698908, 0.00916469134351, 0.0091634582558, 0.00916100358378, 0.00915667695405, 0.00914836144249, 0.00913288486691, 0.00910466758446, 0.00905390137782, 0.00896534153466, 0.00882926798736, 0.0086360622584, 0.00838305090033, 0.00807033414915, 0.00769922813107, 0.0072729224796, 0.00679476685394, 0.00627242780753, 0.00571624280945, 0.00513759245271, 0.00454835849645, 0.00395711147733, 0.00337464154654, 0.00280749740456, 0.00226682876686, 0.00176310907453, 0.00130547471702, 0.000905779759447, 0.000571855092001, 0.000311208875046, 0.000127002966088, 2.27290703342e-5, 0.0, 4.78273447194e-20, 2.08894233235e-20, 0.0, 0.0, 0.0, 4.22264438881e-21, 2.13855148983e-20, 9.51864157305e-21, 1.19983930184e-5, 4.60782307405e-5, 9.53880869555e-5, 0.000152298212592, 0.000211118272231, 0.000269189597303, 0.000323096365829, 0.000370094033468, 0.000408679255951, 0.00044053743596, 0.000465518193849, 0.000484648753775, 0.000500103078853, 0.000512722774307, 0.000521401761115, 0.000527616921397, 0.000532543346179, 0.000536659517574, 0.000539778041574, 0.000541540251261, 0.000542524937145, 0.000543291291334, 0.000543586141659, 0.000543507179099, 0.000542980324501, 0.00054312233015, 0.000543037271523, 0.000542917430232, 0.000542865224303, 0.000542777005272, 0.000542616042571, 0.000542808861172, 0.000543012894916, 0.00054314869738, 0.00054318880014, 0.000543195645874, 0.000542803873351, 0.000542632005164, 0.000542519585918 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, -0.1694544723704584], uv: [1.9999999999999991, 1.0], spectrum: [ 0.00839492804249, 0.00839499038748, 0.00839468362032, 0.00839368052039, 0.00839127535537, 0.00838666836435, 0.00837748085054, 0.00836141803414, 0.00833258711089, 0.00828324081127, 0.00819776042453, 0.00806692381432, 0.00788190770521, 0.00764092973918, 0.00734291154708, 0.00699038325809, 0.00658437708841, 0.00613143437739, 0.00563855984548, 0.0051140905654, 0.00456996990388, 0.00401793568637, 0.00346663658146, 0.00292618641689, 0.00240268838536, 0.00190810043077, 0.00144920753665, 0.00103909243282, 0.000686575774405, 0.00040214387125, 0.000189962126253, 5.50465245159e-5, 0.0, 8.85573359543e-23, 1.79084072014e-20, 0.0, 0.0, 4.14777285739e-21, 4.54079455467e-5, 0.000137448200226, 0.000265371643783, 0.000420176813866, 0.000590957746775, 0.000769874255758, 0.000948583809085, 0.00111984232434, 0.00127902828812, 0.00142175246388, 0.00154554078944, 0.00164945007676, 0.00173568080995, 0.00180526058114, 0.00186065007927, 0.00190420681578, 0.00193641969961, 0.00196031899743, 0.00197813589321, 0.00199052602258, 0.00199898312215, 0.00200530138527, 0.00200999558568, 0.00201261850619, 0.00201418704115, 0.00201526559776, 0.00201612026596, 0.00201705295963, 0.00201683028133, 0.00201685125805, 0.00201702650973, 0.00201676165219, 0.0020164595663, 0.00201633912206, 0.00201599119367, 0.0020155289953, 0.00201528129426, 0.00201538017063, 0.00201580106315, 0.00201537016576, 0.00201530490518, 0.00201554875076, 0.00201565565895 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, -0.1694544723704584], uv: [3.0, 1.0], spectrum: [ 0.00767811432245, 0.00767775377923, 0.00767714311449, 0.00767546684836, 0.0076722593789, 0.00766780138506, 0.00766150430755, 0.00764828248498, 0.00762166738982, 0.0075738938148, 0.00749056138353, 0.00736093257103, 0.0071783716266, 0.0069374300444, 0.00664018978972, 0.0062895737139, 0.00588711608196, 0.00544003627112, 0.0049551008379, 0.00444392072753, 0.00391644861239, 0.00338467794526, 0.00286021683149, 0.0023497886131, 0.00186216964569, 0.00140699594211, 0.0009980116798, 0.000645114334099, 0.000360137326434, 0.000154606351326, 3.40069959193e-5, 0.0, 0.0, 0.0, 0.0, 4.11831751021e-20, 2.3293744841e-5, 0.00011774364124, 0.000269648291546, 0.000471126901272, 0.000708688760511, 0.000971352220509, 0.0012493322688, 0.00152724310317, 0.00179738511587, 0.00204985611134, 0.00227993169894, 0.00248244222316, 0.00265779981318, 0.00280613873506, 0.00292867383738, 0.00302734024171, 0.00310528119219, 0.0031664478888, 0.00321279685767, 0.00324754440086, 0.00327383073593, 0.00329186086353, 0.00330535947096, 0.00331425243951, 0.00332027332125, 0.00332467981801, 0.00332762575118, 0.00332924644811, 0.00332981654218, 0.00333080994195, 0.00333221998209, 0.00333308507448, 0.00333387213126, 0.00333453735507, 0.00333510572326, 0.00333528664867, 0.00333522087215, 0.00333538115091, 0.00333545507987, 0.00333474934044, 0.00333418806291, 0.00333356184059, 0.00333272153693, 0.00333151737397, 0.00333131345192 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, -0.1694544723704584], uv: [3.9999999999999996, 1.0], spectrum: [ 0.00701596130472, 0.00701615415616, 0.00701520291077, 0.00701365007873, 0.00701151900927, 0.00700656018327, 0.00699769732651, 0.00698236975156, 0.00695331835866, 0.00690170977653, 0.00681519504633, 0.00668042997879, 0.00649214546345, 0.00624638748619, 0.00594293025738, 0.00558583732045, 0.00517938439134, 0.0047301947648, 0.0042468853195, 0.00373756607593, 0.00321703724609, 0.00269892143381, 0.00219307531026, 0.00171064759491, 0.00126169830645, 0.000859042307779, 0.000517381398865, 0.000250823796011, 7.42390516803e-5, 2.88024063323e-20, 0.0, 0.0, 0.0, 0.0, 1.80661823869e-20, 6.09936392273e-21, 7.11254531393e-5, 0.000228904242077, 0.000462607717281, 0.00075660552303, 0.00109667030956, 0.0014663572739, 0.00185211946331, 0.00223874346944, 0.00261182802274, 0.00296124145658, 0.00327807878431, 0.00355861454368, 0.00379977853116, 0.00400309245177, 0.00417116137415, 0.00430510770107, 0.00441165167943, 0.00449322998461, 0.00455505130234, 0.00460122730021, 0.00463531798527, 0.00466055430637, 0.0046781403112, 0.0046901410586, 0.00469912334416, 0.00470527245142, 0.00470990760806, 0.00471264051839, 0.00471429030676, 0.00471528393711, 0.00471681540732, 0.00471732577771, 0.00471748287811, 0.00471813591174, 0.00471780920218, 0.00471799963079, 0.00471819929994, 0.004718295843, 0.00471788677538, 0.0047178142311, 0.00471782325458, 0.00471805237195, 0.00471782590236, 0.00471758618461, 0.00471732328234 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, -0.1694544723704584], uv: [5.0, 1.0], spectrum: [ 0.0063983829998, 0.00639824022135, 0.00639814664179, 0.00639703533916, 0.00639498579412, 0.00638900730293, 0.00637881551576, 0.00636117588553, 0.00632917364181, 0.00627261703232, 0.00617735237015, 0.00603133866201, 0.00582926242079, 0.00556626221656, 0.0052453337476, 0.00487084602802, 0.00444820946359, 0.00398421524471, 0.00348948610414, 0.00297830556106, 0.00246423106059, 0.00196192907654, 0.0014849937282, 0.00104671893395, 0.000661719420598, 0.00034672961837, 0.00011904303773, 0.0, 0.0, 6.4193715954e-21, 4.66917717797e-20, 0.0, 5.5746165655e-20, 0.0, 0.0, 3.01751628849e-20, 6.35453725244e-5, 0.000250440824636, 0.000544541567449, 0.000926112836636, 0.00137352044499, 0.00186545218134, 0.00238059634178, 0.00289797502087, 0.00339973741487, 0.00387211010452, 0.00430143230946, 0.00468237626283, 0.00500999466586, 0.00528371224713, 0.00551021407344, 0.00569394345884, 0.00583914504863, 0.00595321839944, 0.00604004769673, 0.00610332636849, 0.00614988380176, 0.00618322784567, 0.00620707436507, 0.00622507508064, 0.00623823358947, 0.00624734775927, 0.00625303500551, 0.00625757555353, 0.00626049018215, 0.0062625474724, 0.00626421788803, 0.00626468044102, 0.00626495370045, 0.0062647024066, 0.00626408139647, 0.00626403343904, 0.00626391572191, 0.00626362994517, 0.00626319260155, 0.0062633416368, 0.0062640053027, 0.00626483076247, 0.00626538367444, 0.00626597157241, 0.00626609233573 ] }, spectrum_data_point_t { xystar: [0.0, -0.1694544723704584], uv: [6.0, 1.0], spectrum: [ 0.00574815383247, 0.00574711318398, 0.00574654793152, 0.00574416020215, 0.0057405919086, 0.00573435004394, 0.00572410290025, 0.00570378811122, 0.00566951088376, 0.00561081453014, 0.00551005456041, 0.00535716638995, 0.00514574715664, 0.00486996203721, 0.00453529318348, 0.00414597230657, 0.00370809226859, 0.00323519610087, 0.0027373241048, 0.00223081867748, 0.0017356432862, 0.00126739887037, 0.000844719587227, 0.000485691282014, 0.000209115970882, 3.80557806793e-5, 0.0, 0.0, 2.9854374757e-20, 1.18164771316e-19, 0.0, 4.94199797966e-20, 8.73204412191e-20, 5.92009699475e-20, 2.212439525e-20, 3.94875394705e-20, 0.0, 0.000153418225016, 0.000470814227253, 0.000923055466247, 0.00148281896968, 0.00211475708442, 0.00278790091735, 0.00347610670683, 0.00414999743195, 0.00478505750385, 0.00536628115509, 0.00588437352019, 0.00633158984448, 0.00670869251448, 0.00702125802586, 0.00727308104844, 0.00747297468173, 0.00762773580549, 0.00774498557903, 0.0078334245293, 0.00789919729784, 0.00794672474799, 0.00797941404259, 0.00800407830442, 0.00802011871726, 0.00803078717453, 0.00803816695784, 0.00804369722261, 0.0080470622045, 0.00804812446762, 0.00804959970669, 0.00805148048789, 0.00805249249339, 0.0080530805998, 0.00805431642253, 0.00805491575006, 0.00805560812885, 0.00805658755203, 0.00805761492105, 0.00805836525355, 0.0080588678243, 0.00805921722062, 0.00806026167895, 0.00806121051464, 0.00806114439675 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, -0.1694544723704584], uv: [6.999999999999999, 1.0], spectrum: [ 0.00508160879249, 0.00508183030739, 0.00508015531729, 0.00507808188463, 0.00507481215783, 0.00506868702619, 0.00505858993497, 0.00503890282052, 0.00500360359643, 0.0049408680199, 0.00483448298606, 0.00467110726279, 0.00444365189686, 0.00415143121523, 0.00379819582199, 0.00339124237903, 0.00294184807132, 0.0024632382464, 0.00197130411849, 0.00148832180799, 0.00103552357841, 0.000636357494857, 0.000314749653172, 9.33592760653e-5, 0.0, 8.44354950505e-22, 0.0, 0.0, 6.06592675176e-20, 1.37237159172e-20, 1.23853500272e-19, 1.09508371483e-19, 9.52242884934e-20, 0.0, 0.0, 2.9585828203e-20, 0.0, 2.17588212669e-5, 0.000296697194983, 0.000783737662041, 0.00143957978926, 0.00221725678764, 0.00307187310033, 0.00395933084543, 0.00483854732085, 0.00567774858431, 0.00645186384277, 0.00714397512038, 0.0077457578688, 0.00825518509633, 0.008676187242, 0.00901741662513, 0.00928849387571, 0.00949838890916, 0.00965800908498, 0.00977747409673, 0.00986514521251, 0.00992869553314, 0.00997479440317, 0.0100066665578, 0.0100282914455, 0.0100440070232, 0.0100553558932, 0.0100629796566, 0.0100685526281, 0.0100723663023, 0.0100748482899, 0.0100770992771, 0.0100793677269, 0.0100808864307, 0.01008184652, 0.0100827169856, 0.0100833819215, 0.0100833731468, 0.0100834147128, 0.010083659439, 0.0100838858128, 0.0100841550653, 0.01008423477, 0.0100841416358, 0.0100838509531 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, -0.1694544723704584], uv: [8.0, 1.0], spectrum: [ 0.00439659935982, 0.00439572751779, 0.00439437888518, 0.00439243803779, 0.00438872060303, 0.00438189564819, 0.00437011494404, 0.00434769869393, 0.00430714098325, 0.00423716318288, 0.00412069999124, 0.00394358643042, 0.0036995670256, 0.00339042976313, 0.00302149477749, 0.00260382061543, 0.0021535385106, 0.00168633340098, 0.00122459340136, 0.000795725910538, 0.000428888518445, 0.000154446633593, 1.51869202371e-6, 7.99497423011e-20, 0.0, 0.0, 1.28991729604e-20, 1.1214553828e-19, 0.0, 1.11033432409e-19, 0.0, 1.12490572532e-19, 0.0, 0.0, 5.69632689538e-20, 4.84539192727e-20, 7.81041591058e-20, 0.0, 0.000100894785792, 0.000536865313025, 0.00124202932118, 0.0021499297251, 0.0031930646226, 0.00430765490662, 0.00543435421855, 0.00652462129938, 0.00754004596388, 0.00845383204517, 0.00925156920511, 0.00992793350983, 0.0104888188705, 0.0109435910911, 0.0113048089352, 0.0115853126805, 0.0117981732322, 0.0119571645275, 0.0120745323223, 0.0121595010308, 0.0122207722825, 0.0122651282019, 0.0122964475346, 0.0123182533916, 0.0123327512964, 0.0123431166505, 0.0123503273104, 0.0123557200631, 0.0123601109927, 0.0123629523273, 0.0123651897024, 0.0123666200533, 0.0123675240875, 0.0123682704381, 0.0123690980221, 0.012369207189, 0.0123694695014, 0.0123697816943, 0.0123698369295, 0.0123696653438, 0.0123699474002, 0.0123698922088, 0.0123699916741 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, -0.1694544723704584], uv: [9.0, 1.0], spectrum: [ 0.00360493902558, 0.00360521307732, 0.00360506106578, 0.00360370835813, 0.00360054054931, 0.0035941163166, 0.00358238570302, 0.00356080084339, 0.00351964439419, 0.0034446315539, 0.0033195728984, 0.00313336720427, 0.00288150066021, 0.00256428729402, 0.00219351252505, 0.00178646617367, 0.00136156976967, 0.000944044427957, 0.000562478701042, 0.000252144135337, 5.16374996654e-5, 0.0, 0.0, 8.01131136633e-20, 0.0, 0.0, 0.0, 1.61499890876e-19, 0.0, 0.0, 2.79156887484e-19, 2.79507129142e-19, 6.98141267987e-19, 0.0, 0.0, 0.0, 2.80992890826e-19, 0.0, 0.0, 0.000249212200087, 0.000921346610147, 0.00191827757901, 0.00314190203944, 0.00450055303035, 0.00590858024271, 0.00729421609238, 0.00860000992484, 0.00978647457442, 0.0108298112598, 0.0117205941288, 0.0124626885491, 0.0130659508819, 0.0135448598969, 0.0139170955653, 0.0142006456007, 0.0144119354121, 0.0145658619418, 0.0146768161242, 0.0147573648936, 0.0148154847328, 0.0148571483909, 0.01488722853, 0.0149092060423, 0.0149252082663, 0.0149365468809, 0.0149440248419, 0.0149490795296, 0.0149529490277, 0.0149547964257, 0.014955329918, 0.0149554700491, 0.0149560229653, 0.0149562588585, 0.0149563683591, 0.0149566257204, 0.0149571567335, 0.0149582771645, 0.0149592873147, 0.0149598225096, 0.0149602200942, 0.0149604291109 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, -0.1694544723704584], uv: [10.0, 1.0], spectrum: [ 0.0026796856922, 0.00267946889169, 0.00267897840498, 0.00267755073063, 0.00267403906866, 0.00266644466302, 0.0026525481375, 0.00262768883646, 0.0025848838816, 0.00251008187093, 0.00238720399438, 0.00220445839216, 0.00195895232804, 0.00165907150637, 0.0013190318517, 0.000961608195904, 0.000614835572719, 0.000312005987011, 9.2133858297e-5, 0.0, 0.0, 0.0, 0.0, 0.0, 6.98289250013e-20, 1.75972233435e-19, 0.0, 0.0, 4.08407281595e-19, 2.74941590235e-19, 7.68295468369e-19, 0.0, 0.0, 0.0, 0.0, 2.14268501292e-19, 3.36778764522e-19, 5.23784744878e-20, 7.3355919185e-20, 0.0, 0.000523546674898, 0.00154641251956, 0.0029308724895, 0.00454335258808, 0.00626272603527, 0.00798619486697, 0.00963055848517, 0.0111371920679, 0.0124700587701, 0.0136127126513, 0.0145673472416, 0.0153466853377, 0.0159675827123, 0.0164512804688, 0.0168197815852, 0.017095702706, 0.0172992444383, 0.0174473122575, 0.0175543757826, 0.0176312319831, 0.017686383902, 0.017724860799, 0.0177520517818, 0.0177711203571, 0.0177846790961, 0.0177934891329, 0.017800182937, 0.0178041077657, 0.0178073190917, 0.01780948718, 0.0178107807746, 0.0178117734908, 0.0178127791518, 0.0178132074325, 0.0178135346059, 0.0178140085655, 0.017814437519, 0.0178150568949, 0.0178157925943, 0.0178162766986, 0.0178166354888 ] }, spectrum_data_point_t { xystar: [0.2730599976959221, -0.1694544723704584], uv: [11.0, 1.0], spectrum: [ 0.00150001049456, 0.00149922715985, 0.00149874751096, 0.00149715381062, 0.00149380433665, 0.00148710488357, 0.00147402718844, 0.00145296303512, 0.0014142068518, 0.0013464484812, 0.00123950985326, 0.00108453350833, 0.000884357078099, 0.000654970529247, 0.000417409140197, 0.000201799313398, 4.77128434536e-5, 1.69897632368e-19, 0.0, 9.13567593428e-20, 0.0, 3.34227125935e-19, 1.39671230268e-19, 0.0, 5.04428015115e-19, 3.16488164263e-19, 5.20278671406e-20, 6.86421490334e-19, 0.0, 7.90541007511e-21, 0.0, 2.19264683817e-20, 1.9625747786e-19, 7.22720043988e-20, 6.64502163632e-19, 2.1386511653e-18, 0.0, 0.0, 0.0, 2.78804996031e-19, 0.000140383624207, 0.00104340098905, 0.00251361744502, 0.0043617154796, 0.0064131265141, 0.00852259516693, 0.0105688973614, 0.0124647711012, 0.014155373332, 0.0156138787943, 0.0168382044585, 0.0178414290057, 0.0186431260282, 0.0192688366489, 0.0197462441196, 0.0201039014816, 0.0203681417731, 0.0205608946265, 0.0206998206181, 0.0207999559632, 0.0208712043532, 0.0209210775364, 0.0209565110781, 0.0209815313598, 0.0209991480071, 0.0210112718921, 0.0210201806193, 0.0210263021828, 0.0210302695507, 0.0210334298039, 0.0210356295413, 0.0210371800133, 0.0210383472363, 0.0210390390254, 0.0210399758961, 0.0210406611907, 0.0210410230866, 0.0210412931925, 0.0210411720895, 0.0210409118326, 0.0210406700292 ] }, spectrum_data_point_t { xystar: [-0.2730599976959221, -0.11296964824697227], uv: [1.0, 2.0], spectrum: [ 0.00657459797733, 0.00657497516789, 0.00657523969135, 0.00657625161468, 0.00657890910456, 0.00658387150984, 0.0065916897665, 0.00660532749878, 0.00662911207909, 0.00666943092088, 0.00673444565286, 0.00683151735784, 0.00695439640797, 0.00710063838987, 0.00725903660535, 0.00741506184612, 0.00754991728178, 0.00764301149465, 0.00767418301077, 0.00762683998849, 0.00748885432828, 0.00725469937676, 0.00691987579626, 0.00648429199114, 0.00594714633425, 0.0053119729627, 0.00458005900719, 0.00376928407591, 0.00290892315132, 0.00204584578196, 0.00124285467109, 0.000575632029241, 0.000131717403769, 2.05994803339e-19, 2.65669565665e-19, 4.56648452889e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 1.15855745367e-18, 0.0, 0.0, 5.57697449056e-19, 1.36398480437e-18, 0.0, 7.16300867474e-19, 0.0, 8.98488950143e-19, 3.24649313193e-19, 2.31449584162e-19, 4.39683047971e-19, 9.04141152178e-19, 1.5961855464e-19, 2.51366148605e-19, 0.0, 0.0, 1.90454934407e-19, 2.82972234862e-19, 3.04523064889e-19, 5.75478082499e-20, 1.51610204509e-19, 1.13889121868e-19, 1.43480022427e-19, 1.458654729e-19, 1.75104416305e-19, 0.0, 3.0907113298e-19, 5.29395592034e-22, 0.0, 2.70909405429e-20, 0.0, 0.0, 1.18478733497e-19, 0.0, 0.0, 0.0, 2.27664984616e-20, 5.15643724855e-8, 4.92226953473e-8 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, -0.11296964824697227], uv: [1.9999999999999991, 2.0], spectrum: [ 0.0075068708971, 0.00750633553593, 0.00750629299254, 0.00750549452998, 0.00750324430209, 0.00750023141704, 0.00749548483957, 0.00748474792303, 0.00746675898835, 0.00743513618121, 0.00738059222107, 0.00729920526366, 0.00718338555975, 0.00703218905267, 0.00684403495748, 0.00661968360696, 0.00636139740633, 0.0060702606978, 0.00575038945691, 0.00540677859821, 0.00504746041909, 0.00467721361608, 0.00430050982905, 0.00392277121769, 0.00354720221626, 0.00318012815255, 0.00282401024935, 0.00248338222285, 0.00216563104152, 0.00187208063118, 0.00160622478472, 0.00136972190724, 0.00115942209385, 0.000981168360501, 0.000834669342213, 0.000715892595743, 0.000625704619285, 0.000561135026679, 0.000518384118201, 0.000497154132093, 0.00049485974625, 0.00050603684916, 0.000529468261048, 0.000559895999445, 0.000594729220336, 0.00063145181566, 0.00066699407372, 0.000700058730272, 0.000729740218875, 0.000755102633028, 0.000775648134965, 0.000792860373073, 0.000805555706541, 0.000814418854392, 0.000820995702936, 0.00082439800314, 0.000826939512146, 0.000829399497687, 0.000830852878206, 0.000831597666629, 0.000831875712611, 0.000832234222349, 0.000832344261244, 0.0008322928163, 0.000832881013893, 0.000832670033687, 0.000832377529489, 0.000832190817177, 0.000831500810692, 0.00083094359621, 0.000830603541969, 0.000830218188216, 0.000829829238413, 0.000829874665364, 0.000829540489098, 0.000829529056337, 0.000829974337199, 0.000830068936063, 0.000829918521374, 0.000830124203625, 0.000830172388724 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, -0.11296964824697227], uv: [3.0, 2.0], spectrum: [ 0.00680057676561, 0.00680006916227, 0.00679932392527, 0.00679836628328, 0.00679709007247, 0.00679337669888, 0.0067874378209, 0.00677662332176, 0.00675747597901, 0.00672496028737, 0.00667030408209, 0.00658924828157, 0.00647484420004, 0.0063258441237, 0.00614107303534, 0.00592108418806, 0.00566896359807, 0.00538623720302, 0.00507683496944, 0.00474752610753, 0.0044045560556, 0.00405160162348, 0.00369570131621, 0.00334223617761, 0.00299466137712, 0.00265675491507, 0.00233707258136, 0.00203937640733, 0.00176886936566, 0.00152922443576, 0.00132264823861, 0.00115043678091, 0.00101589290025, 0.000916193760366, 0.000849887778772, 0.000816855706273, 0.000812952759201, 0.000837766191215, 0.00088699168548, 0.000956150343635, 0.00104016502775, 0.00113520891918, 0.001234776515, 0.00133790095601, 0.00144014593183, 0.00153686158816, 0.00162551452141, 0.0017036995962, 0.00177150227938, 0.00182849899708, 0.00187618798137, 0.00191512950153, 0.0019454369797, 0.00196575438814, 0.00197966453347, 0.00199025973617, 0.00199801497935, 0.00200438416756, 0.00200773772291, 0.00201039831049, 0.00201347085389, 0.00201603207422, 0.00201727478225, 0.00201839753984, 0.00201878787023, 0.00201898434987, 0.00201880917913, 0.0020177800029, 0.00201673566427, 0.00201647198415, 0.00201654820434, 0.002016366092, 0.00201598382868, 0.00201594238778, 0.00201573419244, 0.00201633788366, 0.00201652356978, 0.00201670439058, 0.00201653167254, 0.00201704228868, 0.0020171706637 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, -0.11296964824697227], uv: [3.9999999999999996, 2.0], spectrum: [ 0.00607402140199, 0.00607419025768, 0.00607365995433, 0.00607294056836, 0.00607152803151, 0.006069072098, 0.00606473659214, 0.00605646981441, 0.00603978133068, 0.00600920749828, 0.00595847522352, 0.00587930259179, 0.00576671952365, 0.00562020927988, 0.00543840685687, 0.00522373954771, 0.00497821993875, 0.00470423756951, 0.00440578037004, 0.00408934146815, 0.00376090462498, 0.00342671058458, 0.00309336940951, 0.0027660309595, 0.00244788472683, 0.00214502297595, 0.00186238416835, 0.00160485920406, 0.0013800648237, 0.0011925066083, 0.00104347329559, 0.000936131666758, 0.000869496467266, 0.000843031301802, 0.000857230609298, 0.00090804839954, 0.000993212254669, 0.00110836616662, 0.00124791618879, 0.00140778886299, 0.00158070299557, 0.00176160688756, 0.00194469127098, 0.0021231508127, 0.00229229344043, 0.00244846927282, 0.00258844788389, 0.00271140032767, 0.00281676014768, 0.00290370091398, 0.0029752637467, 0.00303314043032, 0.0030791881438, 0.00311543235958, 0.00314202239332, 0.00316225685483, 0.00317657406379, 0.00318640073083, 0.00319394346287, 0.00319882263424, 0.00320232498161, 0.00320444138614, 0.00320620425667, 0.00320764682252, 0.00320866876492, 0.00320972017638, 0.00321099509007, 0.00321139379773, 0.00321155291438, 0.00321154215497, 0.00321130137532, 0.00321114959025, 0.00321061866201, 0.00321032667814, 0.00320989570491, 0.0032094755909, 0.00320944726689, 0.00320928202574, 0.00320903090043, 0.00320944056757, 0.00320948643808 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, -0.11296964824697227], uv: [5.0, 2.0], spectrum: [ 0.00534955144175, 0.00534930395637, 0.00534882900068, 0.00534796544221, 0.00534703648261, 0.00534443153644, 0.00533982098548, 0.00533167188887, 0.00531576530997, 0.00528725607651, 0.00523811264196, 0.00516156348388, 0.00505402575999, 0.00491278956136, 0.00473816051605, 0.00453120816016, 0.00429390018079, 0.00402907066297, 0.00374117612319, 0.00343641035811, 0.00312202121628, 0.0028058678798, 0.00249319043783, 0.00218802531598, 0.00189610271641, 0.00162435800179, 0.00137762522538, 0.00116366823822, 0.000986453256786, 0.000850938945514, 0.000762417192113, 0.000719869897221, 0.000725091695971, 0.000777821139175, 0.000872769029309, 0.0010079473664, 0.001180650069, 0.00138452512878, 0.00161344963566, 0.00186049860275, 0.00211914911843, 0.00238267429869, 0.0026442643415, 0.00289656856916, 0.003133245178, 0.00335131440556, 0.00354611689231, 0.00371718151295, 0.00386417531173, 0.00398585203263, 0.0040851574681, 0.00416447484352, 0.00422708774504, 0.00427624002387, 0.00431248100506, 0.00433976832185, 0.00435944124968, 0.00437343217009, 0.00438309344167, 0.00438999242829, 0.00439462787592, 0.00439829165636, 0.00440094364871, 0.00440291918815, 0.00440432127641, 0.00440533008395, 0.00440594432052, 0.0044063561076, 0.00440695389436, 0.0044071895311, 0.00440730632018, 0.0044072437867, 0.00440687541419, 0.00440673200816, 0.00440652484214, 0.00440619992355, 0.00440628042674, 0.00440607883061, 0.00440572293917, 0.00440540393501, 0.00440525305976 ] }, spectrum_data_point_t { xystar: [0.0, -0.11296964824697227], uv: [6.0, 2.0], spectrum: [ 0.00464276374014, 0.00464263592165, 0.00464187405482, 0.00464063680532, 0.00463904202136, 0.00463608074625, 0.00463036219555, 0.00461984409946, 0.00460218076812, 0.00457188213153, 0.00452211522235, 0.00444778301325, 0.00434264734067, 0.00420278580249, 0.00403251573238, 0.00383218733209, 0.00360156838377, 0.00334701195383, 0.00307262204116, 0.00278307721742, 0.00248527830985, 0.00218851066491, 0.00189602917645, 0.00161577182805, 0.00135271977937, 0.00111265181201, 0.000902164684452, 0.0007286154272, 0.000597991208932, 0.000515330435303, 0.000483616087781, 0.000505847949909, 0.000580316814711, 0.000705889191258, 0.000880864496031, 0.00110050525808, 0.00135970708924, 0.00165148658752, 0.00197117991215, 0.00230909615231, 0.00265780568366, 0.00300771689321, 0.00335017495716, 0.00367747050817, 0.00398270661293, 0.00426269380635, 0.00451132507313, 0.00472775173022, 0.00491139290682, 0.00506462869955, 0.00519058405364, 0.00529113372622, 0.00536991235657, 0.00543027785674, 0.00547583326855, 0.00550988855591, 0.00553584942373, 0.0055541988507, 0.00556747057935, 0.00557674634602, 0.00558281667906, 0.00558725256673, 0.00559037627113, 0.00559198510155, 0.00559282275304, 0.00559332673652, 0.00559332646161, 0.00559355835089, 0.00559382185818, 0.00559395718574, 0.00559394133549, 0.00559407891146, 0.00559471767796, 0.00559528283683, 0.00559540841205, 0.0055959556412, 0.00559595781589, 0.00559592570662, 0.00559630766549, 0.00559619534873, 0.00559604259506 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, -0.11296964824697227], uv: [6.999999999999999, 2.0], spectrum: [ 0.00391286636201, 0.00391314334244, 0.0039120933558, 0.00390890371173, 0.00390610197383, 0.00390382378503, 0.00390002240267, 0.00389190214025, 0.00387702162423, 0.00384903530555, 0.00380285799922, 0.00373059893013, 0.00362737505632, 0.00349710043184, 0.00333166240246, 0.00313726128758, 0.0029150699683, 0.00266762883028, 0.00240777953647, 0.00213300855017, 0.00185351906824, 0.0015725853396, 0.00130151934802, 0.00104576933365, 0.000807439524692, 0.000597258458401, 0.000420853399247, 0.000286689976975, 0.000204485316192, 0.000175167899681, 0.000199526259907, 0.000287762922171, 0.000433783434349, 0.000638838698422, 0.00089447112966, 0.0011969303016, 0.00154692247868, 0.00192838937491, 0.00233636366365, 0.002761160992, 0.00319728492108, 0.0036286856325, 0.00404821821862, 0.00445011872421, 0.00482488227973, 0.0051636563628, 0.00546804652424, 0.00573458128675, 0.00595939071209, 0.00614843420656, 0.00630046788225, 0.00642386254648, 0.0065217596258, 0.0065947023785, 0.00665101031192, 0.00669137714234, 0.00672376431673, 0.00674566257388, 0.00675979880432, 0.00676958568069, 0.00677575348051, 0.00677946716627, 0.00678164083367, 0.00678250799987, 0.00678278495189, 0.00678451914112, 0.00678595266774, 0.00678686430471, 0.00678910874587, 0.00679129029023, 0.00679279782522, 0.00679381779886, 0.00679504256569, 0.0067962226041, 0.00679621167108, 0.00679592173177, 0.00679569364897, 0.00679660683226, 0.00679789845594, 0.00679794671715, 0.00679858292589 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, -0.11296964824697227], uv: [8.0, 2.0], spectrum: [ 0.00319376958325, 0.00319372111247, 0.00319380847994, 0.00319290676229, 0.00319112728527, 0.00318773857552, 0.00318141411055, 0.0031708152922, 0.00315339376798, 0.00312421793586, 0.00307799777621, 0.00300562444374, 0.00290364166467, 0.00277347355471, 0.00261449464582, 0.0024278762021, 0.00221667210594, 0.00198533415724, 0.00173790348345, 0.00148170698715, 0.00122512559226, 0.000975285853349, 0.000738959033566, 0.00052151689557, 0.000330531601578, 0.000173125674288, 6.00873117117e-5, 5.89712282303e-7, 4.37547740415e-8, 2.68262131582e-20, 1.74707686962e-5, 0.000108516283191, 0.000274593996735, 0.000512313106111, 0.0008183218823, 0.00118647641017, 0.00160885079331, 0.00207696940387, 0.00258123153266, 0.00310906125621, 0.0036496130078, 0.00418872846006, 0.00471385706798, 0.00521395567019, 0.00567994042039, 0.00610469011529, 0.0064811744402, 0.00680801513227, 0.00708478655996, 0.00731412455838, 0.00750057644708, 0.00764867864575, 0.00776570012403, 0.00785700163927, 0.00792462615785, 0.00797625225651, 0.00801509293263, 0.00804340182954, 0.00806352611106, 0.00807877471266, 0.00808940004263, 0.0080958472978, 0.00810026180248, 0.00810272681504, 0.00810356806126, 0.00810433855506, 0.00810452638041, 0.00810416800344, 0.00810459979494, 0.0081044746693, 0.00810481304857, 0.00810478253974, 0.00810468419974, 0.00810424428878, 0.00810354026186, 0.00810359002128, 0.00810309210883, 0.00810287024481, 0.00810300658597, 0.00810311040118, 0.00810305655429 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, -0.11296964824697227], uv: [9.0, 2.0], spectrum: [ 0.00242272835128, 0.00242238613717, 0.00242198852002, 0.00242097418961, 0.002419471157, 0.00241733661108, 0.0024127197374, 0.00240438812716, 0.00238922618733, 0.00236236394225, 0.00231641283358, 0.00224584791359, 0.00214857331076, 0.00202373454706, 0.00187261524043, 0.0016981216874, 0.00150302201679, 0.00129318987903, 0.00107543256408, 0.000857011834563, 0.00064625792258, 0.000450663343156, 0.000279102578382, 0.00014132247013, 4.42684636607e-5, 5.64793676357e-21, 0.0, 0.0, 0.0, 4.79479247204e-20, 1.14758208832e-20, 0.0, 9.46099358769e-5, 0.000295622805923, 0.000598372215271, 0.000994533945497, 0.00147367771232, 0.0020236501247, 0.00262960974454, 0.00327531162619, 0.00394483412109, 0.00462069907725, 0.00528622765178, 0.00592532953147, 0.00652394472042, 0.00707104270856, 0.00755926803897, 0.00798508966619, 0.00834755330342, 0.00864901076776, 0.00889493838738, 0.0090920725291, 0.00924738038568, 0.0093666560527, 0.00945664941828, 0.00952396528788, 0.00957301912108, 0.00960830309865, 0.00963419511165, 0.00965214389769, 0.00966447280453, 0.00967334959719, 0.00967970416419, 0.00968377123959, 0.00968698415831, 0.00968926627539, 0.00969083924164, 0.0096922418898, 0.00969289719267, 0.00969342325491, 0.00969369810947, 0.00969383066099, 0.00969428343856, 0.00969432838931, 0.00969429528188, 0.00969443331557, 0.00969414822827, 0.00969397666862, 0.00969389131663, 0.00969363797103, 0.00969353143494 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, -0.11296964824697227], uv: [10.0, 2.0], spectrum: [ 0.00158365153312, 0.00158299175173, 0.00158222014893, 0.00158014961785, 0.00157599062625, 0.00157206354742, 0.00156577806453, 0.00155683797928, 0.00154300755404, 0.0015205068312, 0.00148169319526, 0.00142236308814, 0.00133806539727, 0.00123101204234, 0.0011027146001, 0.000953832475481, 0.000793433129427, 0.000626066211454, 0.000460662182153, 0.000307568536479, 0.000174927249185, 7.44545808063e-5, 1.29809277217e-5, 0.0, 1.43941877952e-20, 0.0, 0.0, 3.32993299319e-22, 7.76973970202e-20, 1.9923187181e-20, 0.0, 4.654263628e-20, 3.21810078283e-20, 9.29853525356e-5, 0.000335749720549, 0.000717128169107, 0.00122409248442, 0.00184048006794, 0.00254517415889, 0.00331648787933, 0.00413308339077, 0.00496869649505, 0.00580042646404, 0.00660586844948, 0.0073644809186, 0.00806253034517, 0.00868788658349, 0.00923402878204, 0.00969974557007, 0.0100869747711, 0.0104036564687, 0.0106584440928, 0.0108594335694, 0.0110139528976, 0.0111321647085, 0.0112204932528, 0.0112866324326, 0.011335491018, 0.0113712037683, 0.0113966326557, 0.0114137512999, 0.0114250805373, 0.0114324647377, 0.0114370965785, 0.0114394565373, 0.0114414107599, 0.0114428058207, 0.0114433582715, 0.0114434406972, 0.0114441399962, 0.0114440899311, 0.0114439859929, 0.0114443804177, 0.0114438427978, 0.0114437458109, 0.0114431495845, 0.0114430186644, 0.0114431159561, 0.0114426826591, 0.0114431134052, 0.011442821454 ] }, spectrum_data_point_t { xystar: [0.2730599976959221, -0.11296964824697227], uv: [11.0, 2.0], spectrum: [ 0.000567425227685, 0.000568323083189, 0.000569869182485, 0.00057177447853, 0.000573302855024, 0.000573288204346, 0.0005732347917, 0.000569788579197, 0.000560096505801, 0.000542862219994, 0.000512786146878, 0.00047042650045, 0.000413520837123, 0.000344076318742, 0.000267006277607, 0.000188822887701, 0.00011399070588, 5.28895598499e-5, 1.01477361057e-5, 0.0, 1.92585639129e-20, 1.38297511107e-20, 4.7962629998e-20, 3.27958849831e-20, 5.6595615619e-20, 0.0, 6.22056661722e-20, 7.16831510448e-20, 1.00471025132e-19, 0.0, 7.92499764939e-20, 4.85791582168e-20, 0.0, 0.0, 9.97659976206e-5, 0.00040398624498, 0.000893837113635, 0.00154667829226, 0.00233640400006, 0.00323176993496, 0.00420099769501, 0.00521298761491, 0.00623365147996, 0.00723240615371, 0.00818203429856, 0.0090606207645, 0.00985258807255, 0.0105467197727, 0.0111401446501, 0.0116366442129, 0.0120438508103, 0.012370628584, 0.0126279497356, 0.0128260427855, 0.0129759923849, 0.013087358482, 0.0131690297966, 0.0132292136389, 0.0132729510059, 0.0133048361058, 0.0133275470415, 0.013343757872, 0.0133549134038, 0.0133622071808, 0.0133666163172, 0.0133694894637, 0.0133710531446, 0.0133721606236, 0.0133728200561, 0.0133735699317, 0.0133735265731, 0.0133734364488, 0.0133733834544, 0.0133736584553, 0.0133736986293, 0.0133738499843, 0.0133741321744, 0.0133743872194, 0.0133740485714, 0.0133738857807, 0.0133736721899 ] }, spectrum_data_point_t { xystar: [-0.2730599976959221, -0.056484824123486134], uv: [1.0, 3.0], spectrum: [ 7.39272681924e-6, 9.88589425456e-6, 1.34053842214e-5, 2.39212322117e-5, 4.30674801746e-5, 7.82180302187e-5, 0.000140112872956, 0.000249166128882, 0.000441251753038, 0.000775065057139, 0.00132372125819, 0.00213623568183, 0.00322517113799, 0.00456540094306, 0.00609997344814, 0.00774751686262, 0.00940231847301, 0.010946525476, 0.0122586848734, 0.013235538963, 0.0138061037606, 0.0139253592007, 0.01357320212, 0.0127574470022, 0.0114948243108, 0.00981808737438, 0.00778727868416, 0.00552618441809, 0.00325517241671, 0.00129556146454, 5.13112007262e-5, 0.0, 0.0, 1.89240393223e-18, 0.0, 0.0, 6.56972440746e-18, 0.0, 0.0, 5.92491490825e-18, 3.58094200082e-18, 1.42320660909e-18, 2.31914134795e-18, 0.0, 0.0, 3.10297888934e-18, 0.0, 0.0, 0.0, 2.28179064446e-18, 2.63529844906e-18, 2.89551881087e-19, 2.63184909893e-20, 8.22358832285e-19, 1.27581188899e-18, 0.0, 0.0, 2.97855675018e-19, 4.58448540155e-19, 2.9237012697e-19, 0.0, 1.56304104388e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.59284284161e-8, 0.0, 2.10687607001e-7, 5.96713466262e-8 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, -0.056484824123486134], uv: [1.9999999999999991, 3.0], spectrum: [ 0.00666714889846, 0.00666721004471, 0.00666724733729, 0.00666687610048, 0.00666627700021, 0.0066648612728, 0.00666232669208, 0.00665780418187, 0.00665042889485, 0.00663665446958, 0.00661263728283, 0.00657574303646, 0.00652267644151, 0.0064518164528, 0.0063634936218, 0.00625684167588, 0.00613087721445, 0.00598639966099, 0.0058239290139, 0.0056457823479, 0.00545302845562, 0.00524897706746, 0.00503454579931, 0.00481078578476, 0.00457902830681, 0.00433887943579, 0.00409153102439, 0.00383649782783, 0.00357450352472, 0.00330647151698, 0.00303302509746, 0.00275626022534, 0.0024781470992, 0.00220044317677, 0.00192585953986, 0.00165767242686, 0.00139859102278, 0.00115116860143, 0.000919792697792, 0.000707593853209, 0.000518156763583, 0.000354325283481, 0.000218994062135, 0.000114651962298, 4.29108285402e-5, 4.68297604933e-6, 3.01080635779e-20, 6.78567342435e-21, 2.41691195925e-21, 0.0, 3.21047877981e-20, 0.0, 0.0, 2.04004549378e-20, 2.48819058218e-20, 1.0584807119e-20, 7.93175258574e-21, 0.0, 1.69672242707e-20, 0.0, 0.0, 5.85956073872e-7, 6.20237426128e-7, 5.01095169004e-7, 5.94964102165e-7, 6.56673481904e-7, 8.13457670864e-7, 7.90884919598e-7, 6.72257159206e-7, 8.09555645166e-7, 1.11675404872e-6, 1.18497727211e-6, 1.28065370123e-6, 1.43371247875e-6, 1.4489556084e-6, 1.50298802118e-6, 1.55046363513e-6, 1.49705909003e-6, 1.71077381992e-6, 2.07893291163e-6, 1.85908428586e-6 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, -0.056484824123486134], uv: [3.0, 3.0], spectrum: [ 0.00603574035349, 0.0060352435093, 0.00603450340475, 0.00603259269304, 0.00603065769774, 0.0060305752104, 0.00602999993492, 0.00602426818172, 0.00601499865553, 0.00600057366325, 0.00597353875501, 0.00592962207811, 0.00586786781162, 0.00578844776546, 0.00568783098092, 0.00556590987364, 0.00542655489852, 0.00526941879998, 0.0050963211811, 0.00491082990584, 0.00471510532315, 0.00451025364024, 0.00429963383446, 0.00408551467612, 0.00387072396544, 0.00365678020306, 0.00344454834765, 0.00323409535326, 0.00302790325092, 0.00282923387614, 0.00263921236128, 0.00245732459577, 0.00228471322739, 0.00212323225967, 0.00197106571378, 0.00182763958111, 0.00169468471743, 0.00157443843745, 0.00146456261442, 0.00136571361205, 0.00127575720415, 0.00119386467779, 0.00112375182321, 0.00106407944822, 0.00101067437892, 0.000963676736619, 0.000927255782433, 0.000897186189383, 0.000870582832092, 0.000849791394067, 0.000833078633231, 0.000819998617153, 0.000810891491724, 0.000804435983203, 0.000797590260622, 0.000791213277149, 0.000789221169983, 0.000788619874428, 0.000785381459968, 0.000783123493354, 0.000782869086795, 0.00078339756614, 0.000782433960735, 0.000780542716405, 0.000779780356445, 0.000779481044835, 0.000779954753851, 0.000779781598603, 0.000778533163201, 0.000778666857058, 0.000779095411229, 0.000778727865732, 0.000779460062165, 0.000779899309631, 0.000778937257051, 0.000779393261438, 0.000779727760254, 0.00077891584195, 0.00077782295813, 0.000777893270236, 0.000777962609729 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, -0.056484824123486134], uv: [3.9999999999999996, 3.0], spectrum: [ 0.00531254948408, 0.00531217037528, 0.00531175501081, 0.00531149948422, 0.00531099661964, 0.00530880082248, 0.00530552828476, 0.005300775058, 0.00529231193258, 0.00527670513296, 0.00525110899887, 0.00521036940838, 0.00515282452036, 0.00507585898869, 0.0049817877157, 0.00486966894897, 0.0047406142978, 0.00459514072829, 0.00443534734366, 0.00426293650422, 0.0040827667122, 0.00389733329542, 0.00370886074778, 0.00351940592667, 0.00333164718406, 0.0031466893306, 0.00296739152614, 0.00279662632769, 0.00263741020922, 0.00249203120747, 0.00236027637243, 0.00224311498544, 0.00214053665815, 0.00205197775233, 0.00197988263919, 0.00192152455548, 0.00187609400559, 0.00184351575851, 0.00182184212534, 0.00181134725433, 0.00180831191121, 0.00181407239534, 0.00182591824056, 0.00184037221382, 0.00185707975915, 0.00187437019328, 0.00189172284401, 0.00190732601855, 0.00192181657848, 0.00193484557572, 0.00194528966854, 0.00195306770439, 0.00195973329347, 0.00196435780386, 0.00196743901938, 0.00196980663885, 0.0019707372224, 0.00197160880586, 0.00197270503319, 0.00197305191288, 0.00197345555156, 0.00197362509687, 0.00197391771794, 0.00197331830039, 0.00197280013519, 0.00197213487375, 0.00197105781538, 0.00197061051046, 0.00197064624013, 0.00197106861305, 0.00197116685645, 0.00197134980416, 0.00197156556992, 0.00197192729331, 0.00197195975536, 0.0019714203292, 0.00197117336872, 0.00197094212193, 0.00197014296989, 0.00196938380053, 0.00196946765212 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, -0.056484824123486134], uv: [5.0, 3.0], spectrum: [ 0.00459639437541, 0.00459710992884, 0.00459676307971, 0.00459595363054, 0.00459521928536, 0.004593514887, 0.00458968644814, 0.00458339513557, 0.0045742872902, 0.00455809113369, 0.00453106140229, 0.00449021401849, 0.00443362926162, 0.00436151322519, 0.00427328444087, 0.00417009945864, 0.00405126733866, 0.0039180727404, 0.00377252774658, 0.00361657998661, 0.00345465240043, 0.00328916623173, 0.00312284051452, 0.00295797989812, 0.00279675963124, 0.00264251368648, 0.00249864127128, 0.00236875517004, 0.00225484055058, 0.00215819198936, 0.00208210617771, 0.00202600124235, 0.00198983064192, 0.00197468996557, 0.00198113806998, 0.00200739759285, 0.00205028262753, 0.00210741488471, 0.00217738179885, 0.00225757177364, 0.00234615611922, 0.00243803959034, 0.00253166443316, 0.00262273767195, 0.00270785377092, 0.00278786347255, 0.00286020003846, 0.00292351572779, 0.00297616685915, 0.00301875977858, 0.00305467041317, 0.00308218015638, 0.00310361538224, 0.00311941159952, 0.00312995287037, 0.00313818349298, 0.00314399112784, 0.00314857534847, 0.00315204541434, 0.00315469474046, 0.00315616031941, 0.0031564594051, 0.00315723116261, 0.00315793280775, 0.0031575484154, 0.00315746932358, 0.0031577681266, 0.00315719329992, 0.00315634782921, 0.00315676644375, 0.00315695966287, 0.00315639851765, 0.00315668532226, 0.00315723651577, 0.00315727608705, 0.0031570384566, 0.00315677622086, 0.00315685614379, 0.00315682322074, 0.00315687001233, 0.00315692977572 ] }, spectrum_data_point_t { xystar: [0.0, -0.056484824123486134], uv: [6.0, 3.0], spectrum: [ 0.00393061128765, 0.00393015204309, 0.00392602139435, 0.00392381216193, 0.00392150849447, 0.00391726141708, 0.00391090846804, 0.00390097367815, 0.00388643818206, 0.00386753180586, 0.00384055679577, 0.00379588542058, 0.00373867574634, 0.00366725849013, 0.00357765171778, 0.00346937354498, 0.00334960361697, 0.00322235406519, 0.00308126353323, 0.00293418545807, 0.0027780002437, 0.00262759102448, 0.0024857855331, 0.0023481730074, 0.00221999173469, 0.00210208229272, 0.00200441521187, 0.00192227538174, 0.00185753572925, 0.00181889169911, 0.001807231056, 0.00181508871018, 0.00185896941018, 0.00192229591425, 0.00200523325957, 0.00211533822181, 0.00224557649699, 0.00239115988518, 0.00255225877031, 0.00272060249933, 0.00289310631273, 0.0030657331214, 0.00323058147807, 0.00339195851958, 0.00354308156314, 0.00367899255909, 0.00380180377391, 0.00391332529309, 0.00400513862491, 0.00408282155861, 0.00415154121793, 0.00420415478297, 0.00425220836406, 0.00428704483905, 0.00431422686818, 0.00433368848828, 0.00434576272821, 0.00435382572986, 0.00435992284346, 0.00436342967654, 0.00436526906208, 0.00436816811466, 0.00436647795583, 0.00436418010608, 0.00436280155202, 0.00436243917703, 0.00436237759614, 0.00436150054786, 0.00436082729711, 0.0043622305523, 0.00436391444554, 0.00436456041716, 0.00436513668501, 0.00436791445276, 0.00437045725046, 0.00437207814499, 0.00437465138395, 0.00437709512096, 0.00437787247127, 0.00437845343911, 0.00437863417119 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, -0.056484824123486134], uv: [6.999999999999999, 3.0], spectrum: [ 0.00315625371272, 0.00315606964643, 0.00315503417792, 0.00315425864744, 0.00315330636532, 0.00315194609843, 0.00314918156101, 0.00314578659348, 0.00313973377083, 0.00312792028666, 0.00310500959599, 0.0030704263246, 0.00302061609733, 0.00295467566719, 0.00287393127619, 0.00277858835306, 0.00267063759051, 0.00255200930136, 0.00242686936608, 0.00229707009486, 0.00216461601972, 0.00203410119135, 0.00190996324241, 0.00179431296109, 0.00169163283302, 0.00160441792138, 0.0015350617766, 0.00148996870116, 0.0014704047751, 0.0014801840155, 0.00152319500139, 0.00159854032637, 0.00170620967788, 0.00184419747217, 0.00201089010616, 0.00220465828838, 0.00242039474739, 0.00265473266721, 0.00290462752568, 0.00316305303841, 0.00342421930477, 0.00368306189706, 0.00393409833917, 0.00417343798497, 0.0043956005232, 0.00459721017843, 0.00477593135646, 0.00493170194002, 0.005064910656, 0.00517519341098, 0.00526670512845, 0.00533974939944, 0.00539618656533, 0.00544012115741, 0.00547383908853, 0.00549801056722, 0.00551445412923, 0.00552563095085, 0.00553294916322, 0.00553713752937, 0.00553951145115, 0.00554064112295, 0.00554144226995, 0.00554196372988, 0.00554179217471, 0.00554239107708, 0.00554388270608, 0.00554536535364, 0.00554693525123, 0.00554822433636, 0.00554974017029, 0.0055513165446, 0.00555245731041, 0.00555404808789, 0.00555531809237, 0.00555630076429, 0.00555742884806, 0.0055578486979, 0.00555840473838, 0.00555910237882, 0.00555910151016 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, -0.056484824123486134], uv: [8.0, 3.0], spectrum: [ 0.00244245242755, 0.00244260645252, 0.00244287841899, 0.00244237210085, 0.00244051565323, 0.00243744055582, 0.00243369234713, 0.00242870706689, 0.00242019914808, 0.00240784212081, 0.00238511158952, 0.00234983617145, 0.00230378180714, 0.00224398459268, 0.00217076558271, 0.00208336729175, 0.0019842454745, 0.00187642839306, 0.00176196891131, 0.00164530033608, 0.001529910705, 0.00141996243401, 0.00131663625599, 0.00122484623367, 0.00114777899286, 0.00108980090742, 0.00105539283108, 0.00104878482934, 0.00107590000433, 0.00113968048787, 0.00124093397457, 0.00138125173281, 0.00155934618513, 0.00177342074415, 0.00202100226914, 0.00229902866706, 0.00260237304152, 0.00292585475281, 0.0032657492211, 0.00361356758758, 0.00396336999779, 0.00430996381645, 0.00464349362923, 0.00495798619372, 0.00524871769621, 0.00551203107098, 0.00574453941117, 0.00594448146811, 0.00611391614456, 0.00625380176911, 0.00636637135735, 0.00645619046559, 0.00652671062058, 0.00658119533683, 0.00662304026727, 0.00665442966498, 0.00667804847309, 0.00669592697835, 0.00670981925304, 0.0067199003272, 0.00672664024839, 0.00673059838508, 0.00673295183594, 0.00673417355808, 0.00673404844003, 0.00673370294516, 0.0067330721698, 0.00673240221114, 0.00673278683395, 0.00673350927803, 0.00673378538953, 0.00673376488808, 0.00673329651546, 0.00673316664283, 0.00673273970257, 0.00673225106716, 0.00673180592486, 0.00673149994812, 0.00673135472592, 0.00673118822262, 0.0067312337202 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, -0.056484824123486134], uv: [9.0, 3.0], spectrum: [ 0.00172080149781, 0.00172020640998, 0.00171975225391, 0.00171962329779, 0.0017192845796, 0.00171776994193, 0.00171452632813, 0.00170890084926, 0.00170101091071, 0.00168873681602, 0.0016676707614, 0.00163568748578, 0.00159125035757, 0.00153431105432, 0.00146518805967, 0.0013853274161, 0.00129519257452, 0.00119748876587, 0.00109598254733, 0.00099396043034, 0.000895215010063, 0.000802999741659, 0.000721572889693, 0.00065427631373, 0.000604653536297, 0.000577621377919, 0.000577859201212, 0.000612350791838, 0.000685326985215, 0.000800050475538, 0.000959394951715, 0.00116323286984, 0.00141116124623, 0.00170180114163, 0.0020304096578, 0.00239363250686, 0.00278531177128, 0.00319882148113, 0.00362911476138, 0.00406675155026, 0.00450492308039, 0.00493430979168, 0.00534620343903, 0.00573519922576, 0.0060934463446, 0.00641752560069, 0.00670360373041, 0.00695019076046, 0.00715928764724, 0.007332540019, 0.00747395154535, 0.0075868673746, 0.00767462149801, 0.00774272770115, 0.00779468110157, 0.00783388815622, 0.00786303475491, 0.00788381365238, 0.00789872185956, 0.00790951751076, 0.00791696811372, 0.00792170981711, 0.00792451701423, 0.00792640488086, 0.00792823198613, 0.00792938760199, 0.00792942308841, 0.00792928010311, 0.00792967918448, 0.0079299017576, 0.00793021670115, 0.00793025425097, 0.00793028404725, 0.00793048624509, 0.00793010851222, 0.00792998261994, 0.00792942803491, 0.00792890461874, 0.00792857051087, 0.00792815304605, 0.00792815295643 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, -0.056484824123486134], uv: [10.0, 3.0], spectrum: [ 0.00100096294693, 0.00100182548072, 0.00100211292297, 0.00100100572591, 0.00100096723868, 0.000999787581884, 0.000998203171022, 0.000992590817659, 0.000984585951298, 0.000971593584882, 0.000950055015715, 0.000919914354842, 0.000878832053144, 0.000825343931605, 0.000759766146604, 0.000685260492545, 0.000604587141212, 0.00051861601157, 0.000431189626245, 0.000343443769176, 0.000260059405153, 0.00018586357365, 0.000126574514387, 8.3124172198e-5, 6.02415637378e-5, 6.44260407239e-5, 0.000100563509042, 0.000174324914975, 0.000291869582702, 0.000458824878455, 0.000676777868181, 0.000945881295666, 0.00126458135947, 0.00163183703549, 0.00204143474323, 0.0024884320181, 0.00296783814768, 0.00347141620573, 0.00399161511726, 0.00451936676256, 0.00504464654522, 0.0055589193243, 0.00605155871109, 0.00651409352131, 0.0069413952258, 0.00732659746625, 0.00766530523083, 0.00795939813547, 0.00820740319142, 0.00841182614975, 0.00857842531627, 0.00871200941073, 0.00881674393624, 0.0088975009236, 0.00895807326527, 0.00900439656065, 0.00903863867402, 0.00906278591225, 0.00907984860632, 0.00909248249778, 0.00910150418769, 0.00910717965638, 0.00911080800811, 0.00911356320777, 0.00911553387799, 0.00911690210187, 0.00911772336507, 0.0091187852523, 0.00911929490021, 0.00911908986892, 0.00911917993919, 0.00911928838455, 0.0091191010821, 0.00911820906827, 0.00911787651327, 0.00911782306086, 0.00911763389396, 0.0091173496952, 0.00911748838504, 0.00911718873298, 0.00911721769789 ] }, spectrum_data_point_t { xystar: [-0.2730599976959221, 0.0], uv: [1.0, 4.0], spectrum: [ 0.0, 7.34970943041e-19, 5.45503065922e-18, 0.0, 0.0, 0.0, 9.75623841823e-19, 0.0, 1.88576745316e-18, 0.0, 2.7977886011e-19, 7.25113687654e-19, 1.04041861011e-18, 2.50167481476e-17, 0.00067195117861, 0.0031395113783, 0.00689741118884, 0.0113358891872, 0.0158146944665, 0.0197105971371, 0.0225415507409, 0.0239953045536, 0.0239009037508, 0.0222682881531, 0.019210086771, 0.0149540353254, 0.00990407874367, 0.00482110462061, 0.000943643692416, 0.0, 0.0, 0.0, 0.0, 8.05758374867e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 1.77485641151e-18, 0.0, 5.55100914606e-18, 1.76040199416e-17, 0.0, 1.2323224417e-17, 5.96943765875e-18, 3.67299810624e-17, 1.24275746475e-17, 1.16607094585e-17, 0.0, 0.0, 0.0, 0.0, 5.86249108776e-18, 9.36481197e-18, 1.06156308151e-17, 9.29702613401e-18, 1.06375612436e-17, 2.07334287139e-18, 4.97512651402e-18, 1.78291373045e-18, 1.45101418679e-18, 2.4699695361e-18, 2.06945072518e-19, 3.74036665317e-19, 0.0, 5.7360861104e-19, 2.45866517923e-18, 7.2090070054e-20, 5.04845466993e-18, 5.58158043335e-18, 2.62562652335e-18, 3.75061575006e-18, 0.0, 0.0, 0.0, 0.0, 1.8038214061e-18, 3.76285401269e-19, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, 0.0], uv: [1.9999999999999991, 4.0], spectrum: [ 0.0047111122482, 0.00471097654301, 0.00471199494176, 0.00471263403933, 0.00471474736159, 0.00471963914107, 0.00472850154286, 0.00474244345159, 0.00476901147566, 0.00481552097018, 0.00489192410948, 0.00500598574501, 0.00516270108874, 0.00536105169139, 0.00559489221057, 0.00585491044911, 0.0061327604702, 0.00641489538161, 0.00668943563591, 0.0069401241346, 0.00715260636924, 0.00731504575244, 0.00741765438149, 0.00745153631072, 0.0074068558056, 0.00727085472306, 0.0070335172244, 0.00668323595037, 0.00621708269667, 0.00563805708196, 0.00496295322676, 0.00421501647291, 0.00342282762472, 0.00262037325374, 0.00184647238977, 0.00114539354285, 0.000567666739966, 0.000166995201203, 1.67108245467e-19, 0.0, 1.76021325916e-19, 0.0, 4.78299785589e-20, 0.0, 0.0, 1.10374932892e-19, 9.64585822128e-20, 1.48850577813e-19, 1.44176091827e-19, 1.10962149306e-19, 0.0, 0.0, 2.60051259087e-19, 1.51409265984e-19, 0.0, 1.40592573089e-19, 0.0, 1.81860741026e-19, 2.86607091755e-19, 2.75522710518e-19, 0.0, 4.78610473046e-20, 0.0, 6.92693897926e-20, 1.97297744438e-19, 0.0, 6.86415642387e-20, 2.55371036069e-19, 1.53410538288e-19, 1.26057679943e-19, 1.95440799414e-7, 1.71950764079e-7, 6.13812443287e-8, 9.55826799821e-8, 4.82483756235e-7, 1.10014569165e-6, 1.77482476218e-7, 3.13075577495e-7, 5.16356407315e-7, 4.69158614298e-7, 1.27336333149e-7 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.0], uv: [3.0, 4.0], spectrum: [ 0.00522780422835, 0.00522780976156, 0.00522906500383, 0.00522878998589, 0.00522649942294, 0.00522539887765, 0.00522428617914, 0.00522363013027, 0.00522494603157, 0.00522251551035, 0.00522021561275, 0.00521940192811, 0.00521761134955, 0.00521474296461, 0.0052143314855, 0.00520724849121, 0.00519533292074, 0.00518013111474, 0.0051600715153, 0.00513318388079, 0.0050959233612, 0.00505179416361, 0.00499954148174, 0.00493145272949, 0.00485417934339, 0.00476768298563, 0.00466251087821, 0.00453519012639, 0.00438835465804, 0.00422049153168, 0.00403271429804, 0.00382292425035, 0.00359080815733, 0.00334176637949, 0.00307785408642, 0.00279928942049, 0.002512506439, 0.00222281217713, 0.00193181911421, 0.0016416407816, 0.00136431168334, 0.00110312223536, 0.000862946254159, 0.000644981027984, 0.000454825627491, 0.000302830398902, 0.000184858880548, 9.0913133883e-5, 3.1225729491e-5, 4.7643348223e-6, 0.0, 1.97094378929e-20, 2.88982141311e-20, 8.22679629879e-20, 0.0, 4.84807968812e-20, 7.58129119221e-20, 6.10836553228e-20, 1.16467030247e-20, 2.7581510345e-20, 0.0, 1.43138948914e-8, 5.6751207466e-20, 1.02195495672e-6, 1.0812368607e-6, 2.37777121163e-7, 3.01152423184e-7, 1.28360511258e-6, 0.0, 0.0, 1.63600436894e-6, 1.49027016449e-7, 0.0, 2.50276831198e-6, 2.56667587891e-6, 1.21011871554e-6, 1.93741406675e-6, 3.04101083657e-6, 2.41309712546e-6, 2.31479218271e-6, 2.50070681774e-6 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.0], uv: [3.9999999999999996, 4.0], spectrum: [ 0.00456361922635, 0.00456339763694, 0.00456281443906, 0.00456290597464, 0.0045626360729, 0.00456187600007, 0.0045607587863, 0.00455937956918, 0.00455767825376, 0.00455502668218, 0.00455154171887, 0.00454698846633, 0.00454188946384, 0.00453515906997, 0.00452571339776, 0.00451409899251, 0.00449842739848, 0.00447942718446, 0.00445586274256, 0.00442786000764, 0.00439560319887, 0.00435801541358, 0.0043144769202, 0.00426537840237, 0.00420921799419, 0.00414526042925, 0.0040724545436, 0.00399017272705, 0.0038976979278, 0.00379346302725, 0.00367732847978, 0.00354950582284, 0.00341068100482, 0.00326069373626, 0.00310075989828, 0.00293305081485, 0.00275874083094, 0.00258001680424, 0.00239977503092, 0.00221926219835, 0.00204205699795, 0.00187187013837, 0.00171052834463, 0.00156078181184, 0.00142412169924, 0.00130167150716, 0.00119437906465, 0.00110174530961, 0.0010238187969, 0.000959945641219, 0.000907439060184, 0.000865393123514, 0.000832325176084, 0.000805950795877, 0.000785595613989, 0.00077054016766, 0.000759134164045, 0.000750636254094, 0.000744366816568, 0.00073973384067, 0.000737230103908, 0.000735490256825, 0.000734305878382, 0.000733098289542, 0.000732243891888, 0.000730845525057, 0.000729315802119, 0.000727827082801, 0.000727295499174, 0.000726797684323, 0.000726789459451, 0.000726824997815, 0.000726482636827, 0.000726979343669, 0.00072719156914, 0.000727124533341, 0.000727125785832, 0.000726945373683, 0.000726596993193, 0.000725934256068, 0.000725640939597 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.0], uv: [5.0, 4.0], spectrum: [ 0.00385154747171, 0.00385139717529, 0.00385159157077, 0.00385214114018, 0.00385246908587, 0.00385119298808, 0.00384906348993, 0.00384613758634, 0.00384363947472, 0.00384051768098, 0.00383852187679, 0.00383459037204, 0.00383288350061, 0.00382889423259, 0.00382459737421, 0.00381761809856, 0.00380922418757, 0.00379754230315, 0.00378470787736, 0.00377021103245, 0.00375275670102, 0.00373299721526, 0.00370896268526, 0.00368323790968, 0.00365371862835, 0.00362242516022, 0.003586816978, 0.00354714256297, 0.00350154957619, 0.00345061347157, 0.00339429612314, 0.00333275180989, 0.00326645931411, 0.00319481601207, 0.0031176590355, 0.00303425223244, 0.00294670904685, 0.00285779019579, 0.00276693820455, 0.00267619011913, 0.00258518112416, 0.00249731617789, 0.00241465214837, 0.00233857656348, 0.00226952663651, 0.00220595948876, 0.00215148599341, 0.00210574604909, 0.00206738699869, 0.0020370353472, 0.00200978301984, 0.00198767293787, 0.00197030891442, 0.00195760063231, 0.00194763723274, 0.001939912577, 0.00193482086212, 0.0019315172632, 0.00192947881271, 0.00192840955996, 0.00192741982832, 0.00192706792456, 0.00192742474457, 0.00192709043554, 0.0019259085001, 0.00192445624474, 0.00192249182727, 0.00192091557326, 0.00191942486978, 0.00191785840517, 0.00191635941035, 0.0019151171566, 0.00191449354501, 0.00191477161504, 0.00191501631454, 0.00191561692223, 0.00191621597999, 0.00191727665316, 0.00191793401367, 0.00191859751273, 0.00191919200403 ] }, spectrum_data_point_t { xystar: [0.0, 0.0], uv: [6.0, 4.0], spectrum: [ 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108, 0.00311941566108 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.0], uv: [6.999999999999999, 4.0], spectrum: [ 0.00239438477804, 0.00239432495366, 0.00239404693311, 0.00239434535352, 0.00239460005516, 0.00239549662376, 0.00239622233678, 0.00239690262653, 0.00239898315315, 0.00240023707386, 0.00240284409441, 0.00240603527935, 0.00240903685689, 0.00241303245372, 0.00241805513267, 0.00242357284352, 0.00243067097412, 0.00243979863872, 0.00245069880366, 0.00246340131083, 0.00247884623546, 0.00249726952549, 0.00251908605816, 0.00254536976296, 0.00257463140537, 0.00260699034481, 0.00264406189366, 0.00268523500016, 0.00273135621277, 0.00278309832948, 0.00284095829688, 0.00290456640647, 0.00297377105491, 0.00304885344578, 0.00312869389208, 0.00321261903734, 0.00330024006258, 0.00338967292438, 0.00348028583292, 0.00357049645765, 0.00365883192957, 0.00374301463642, 0.0038219374236, 0.00389630467267, 0.00396342301787, 0.00402492323675, 0.00407935057238, 0.00412659447834, 0.00416768384286, 0.00420082268876, 0.00422864462602, 0.00425129410551, 0.00426847848918, 0.00428205891935, 0.00429192718536, 0.00429812048385, 0.0043015273519, 0.00430363561007, 0.00430492935484, 0.00430550085979, 0.00430599703053, 0.00430601141027, 0.00430623723837, 0.00430650280706, 0.00430658332484, 0.00430681194503, 0.00430685089432, 0.00430736301456, 0.00430765002507, 0.00430776906572, 0.004308020915, 0.00430817936541, 0.00430902095077, 0.00430976686988, 0.00431059221643, 0.00431163432133, 0.00431204409082, 0.00431274146112, 0.00431341587713, 0.00431386571606, 0.0043142158751 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.0], uv: [8.0, 4.0], spectrum: [ 0.00168151964593, 0.00168166497133, 0.00168142913432, 0.00168171725571, 0.0016823939839, 0.00168260864625, 0.00168223202137, 0.00168120435028, 0.00168089011601, 0.00168122457362, 0.00168348581048, 0.00168648888748, 0.00169270181671, 0.00170135621271, 0.00171231611666, 0.00172546442073, 0.00174262182991, 0.00176393443627, 0.00178765239225, 0.00181585709001, 0.00184710668882, 0.00188304782377, 0.00192497072787, 0.00197357556517, 0.00202935693408, 0.00209282593097, 0.00216442770466, 0.00224581540078, 0.00233896540542, 0.0024437223774, 0.00256016961383, 0.00268851480645, 0.00282792468748, 0.00297733089646, 0.00313700248684, 0.00330608123809, 0.00348093252582, 0.00366010702991, 0.00384190835411, 0.00402179643114, 0.00419842893575, 0.00436862826911, 0.00452970525098, 0.00467907815769, 0.00481527380496, 0.00493769311235, 0.00504542698645, 0.00513761291551, 0.00521439755734, 0.00527834030758, 0.00533082897643, 0.00537243513974, 0.00540454710997, 0.005429494443, 0.00544845757789, 0.00546257379381, 0.0054733948279, 0.00548070253144, 0.00548625427693, 0.00549074027774, 0.00549421467806, 0.00549650563841, 0.00549746400292, 0.00549793760269, 0.00549837443061, 0.00549847213651, 0.00549825343908, 0.00549795135771, 0.00549727943386, 0.00549620965684, 0.00549595497732, 0.00549607155778, 0.00549616681442, 0.00549607265627, 0.00549616375305, 0.00549611058095, 0.00549607050569, 0.00549571992612, 0.00549552543932, 0.0054952132659, 0.00549529104192 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, 0.0], uv: [9.0, 4.0], spectrum: [ 0.000961796447815, 0.000961438586324, 0.000961548949336, 0.000960869548428, 0.000960301499152, 0.000959892509604, 0.000960179070233, 0.000961515599434, 0.000962653834818, 0.000964033300623, 0.000967556817925, 0.000973000090996, 0.000981450458787, 0.000993275998974, 0.00100843682413, 0.00102942505193, 0.00105478171408, 0.00108369528891, 0.00111847772831, 0.00115988808096, 0.00120816672384, 0.00126392738783, 0.00132747072268, 0.00140057647202, 0.00148408635097, 0.0015787929821, 0.00168662533925, 0.00180917052219, 0.00194811227535, 0.00210445470526, 0.00227963919406, 0.00247144017509, 0.00268105648075, 0.00290727145927, 0.00314840214443, 0.00340239503191, 0.00366552797318, 0.0039340107633, 0.0042043805217, 0.00447297091332, 0.00473650563211, 0.00498994467376, 0.00523052938627, 0.00545567185141, 0.00566123626946, 0.00584531668676, 0.00600706642097, 0.00614652664093, 0.00626362295172, 0.00636053160723, 0.00643915343459, 0.0065010281726, 0.00654937689891, 0.00658666803006, 0.00661476994524, 0.00663598781598, 0.0066513015516, 0.00666286493956, 0.00667079700346, 0.00667580629407, 0.00667954560854, 0.00668183979808, 0.00668315534328, 0.00668381519406, 0.00668509763138, 0.00668595727897, 0.00668666944548, 0.00668717512676, 0.00668740525774, 0.00668730813239, 0.00668707575391, 0.00668709691761, 0.00668684137864, 0.00668650245038, 0.00668660813628, 0.00668668676056, 0.00668680954768, 0.00668695893347, 0.00668691369629, 0.00668730491888, 0.00668735244817 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, 0.0], uv: [10.0, 4.0], spectrum: [ 0.00024739191151, 0.000246986597168, 0.000245920885842, 0.00024375918344, 0.000241626523433, 0.00023831740756, 0.000237461577668, 0.000236953490834, 0.000236346937166, 0.000238018047384, 0.000243952724016, 0.000253873296527, 0.000266627523068, 0.000284685353492, 0.000306557965747, 0.000334206756875, 0.00036758129978, 0.000407887281203, 0.000455983575801, 0.00051214946085, 0.000576341683264, 0.000650388317591, 0.000735257437693, 0.000831033603429, 0.000941683413645, 0.00106598502816, 0.00120755486908, 0.00136849479048, 0.00155338720609, 0.00176150448873, 0.00199455114954, 0.00225344820204, 0.00253510344724, 0.00283843503124, 0.00315943000955, 0.00349778492625, 0.00384741989183, 0.00420597904008, 0.00456745722975, 0.0049266015182, 0.00527770743539, 0.00561532279009, 0.00593673888476, 0.00623471929802, 0.00650837463726, 0.00675278601872, 0.00696876216717, 0.00715355692307, 0.00730958922917, 0.00743811367082, 0.00754268125846, 0.00762714608393, 0.00769237304323, 0.00774232339771, 0.00778081241705, 0.00781035177792, 0.00783188525038, 0.00784806950592, 0.00785893386194, 0.00786597326943, 0.00787068329028, 0.00787445259861, 0.00787607308465, 0.00787699704094, 0.00787782997311, 0.00787776711718, 0.00787824406096, 0.0078787775357, 0.00787954848701, 0.00787917104757, 0.00787894085046, 0.00787866324289, 0.00787821345775, 0.00787758172517, 0.00787706607528, 0.00787706637962, 0.00787710704867, 0.00787767670104, 0.00787710545435, 0.00787795160569, 0.00787790807527 ] }, spectrum_data_point_t { xystar: [-0.2730599976959221, 0.056484824123486106], uv: [1.0, 4.999999999999999], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.85237060658e-17, 4.18099983294e-17, 0.0, 0.0, 4.82153413681e-18, 2.68506736141e-17, 6.0177867623e-17, 0.0, 2.19126714725e-17, 0.0, 0.0, 0.0067266335743, 0.0202098412247, 0.0340744334966, 0.0438085417018, 0.0466386326056, 0.0420824711269, 0.0311873831919, 0.0166307586267, 0.00335384314773, 3.30892090639e-16, 0.0, 1.79141742449e-16, 1.92647677192e-17, 0.0, 5.89433168697e-17, 1.45581734823e-16, 0.0, 8.43241996102e-17, 0.0, 0.0, 8.47957736444e-17, 0.0, 0.0, 3.18169543125e-16, 3.63106333836e-16, 4.20567076156e-16, 0.0, 1.72108854926e-17, 1.39089143551e-16, 4.98184849966e-17, 0.0, 1.94574073657e-16, 3.4508733992e-16, 3.3473492278e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.92620839966e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, 0.056484824123486106], uv: [1.9999999999999991, 4.999999999999999], spectrum: [ 0.00129107923288, 0.00129047703083, 0.00129245279151, 0.00129713352878, 0.00130631555156, 0.00132340898607, 0.00135410819668, 0.00140841030044, 0.00150714801137, 0.00167755349863, 0.00196023327236, 0.00238737393316, 0.00297170067044, 0.00370800044038, 0.00457695581511, 0.00554964849544, 0.00658930353455, 0.00765046235515, 0.0086827182255, 0.00963323396617, 0.0104604106838, 0.0111243345935, 0.0115926467624, 0.0118426727456, 0.0118488002624, 0.0115872341063, 0.0110301847432, 0.0101648250334, 0.00900358274553, 0.0075889480961, 0.00600773746559, 0.00436706622789, 0.00279316018551, 0.00142752736096, 0.000435939965155, 8.63941841197e-20, 0.0, 3.93501148297e-19, 3.68025316263e-19, 0.0, 4.8066524196e-19, 0.0, 1.10941050142e-18, 8.06554960317e-19, 6.9848389505e-19, 0.0, 3.68065384467e-19, 0.0, 1.08201693935e-18, 1.46233628452e-18, 0.0, 0.0, 0.0, 2.04720640446e-19, 2.57243340993e-19, 0.0, 0.0, 0.0, 5.74903945475e-19, 6.1638895931e-19, 5.37364997354e-20, 0.0, 6.00484871252e-19, 2.67214161652e-19, 0.0, 5.8884713061e-19, 0.0, 0.0, 7.89080673539e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.70913021719e-7, 6.75195129323e-7, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.056484824123486106], uv: [3.0, 4.999999999999999], spectrum: [ 0.00394914483256, 0.00394901346557, 0.00394978062204, 0.00395158066983, 0.00395360384565, 0.00395561489703, 0.00396054336989, 0.00397227436417, 0.00399103436988, 0.00402425045599, 0.00407846378912, 0.00416117049222, 0.0042772673491, 0.0044251129034, 0.00460342220623, 0.00480982418143, 0.0050370291405, 0.00527798471542, 0.00552676615083, 0.0057747307465, 0.00601070736282, 0.00622668131906, 0.00641510266503, 0.00656681677141, 0.00667603538159, 0.0067306201198, 0.0067229953394, 0.00664418763842, 0.00648366693805, 0.00623338777265, 0.00589980048742, 0.00548804418047, 0.0050054964408, 0.0044647998467, 0.0038763455879, 0.00325714512181, 0.00263332855056, 0.0020224042872, 0.00144684281845, 0.000933424141857, 0.000507063280928, 0.00019312325851, 1.78224243016e-5, 0.0, 0.0, 1.53248703848e-19, 0.0, 0.0, 6.81708689641e-20, 3.37259226504e-19, 0.0, 0.0, 0.0, 1.29384849287e-19, 0.0, 2.31255180324e-19, 3.18616453759e-19, 1.89325168066e-19, 1.42701348634e-19, 1.23037451739e-19, 0.0, 3.22978799606e-19, 0.0, 7.67888306245e-20, 0.0, 0.0, 3.56283233439e-19, 1.39866315415e-19, 3.03240536019e-7, 4.24936059448e-7, 3.79866125731e-7, 4.24575264811e-20, 5.48217955941e-7, 3.09475112561e-7, 8.35079464777e-7, 7.81259630246e-7, 3.72059222081e-19, 2.67044169078e-7, 5.35193882e-7, 6.01222641095e-7, 2.31120990028e-8 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.056484824123486106], uv: [3.9999999999999996, 4.999999999999999], spectrum: [ 0.00375571080025, 0.00375562996312, 0.00375618417943, 0.00375550596142, 0.00375630507335, 0.0037583224569, 0.00376091076677, 0.00376428906255, 0.00377152583874, 0.00378592706995, 0.00381069163626, 0.00384874867626, 0.00390212359679, 0.00397110969114, 0.00405506576673, 0.00415250322712, 0.00426221602141, 0.00438241690639, 0.00450893557115, 0.00463798255329, 0.00476515081565, 0.00488599181464, 0.00499835529711, 0.00509812331119, 0.00518031342489, 0.00524189700063, 0.00527746947784, 0.00528092848041, 0.00524885500596, 0.00517671999976, 0.0050638855301, 0.00491049060937, 0.00471632761407, 0.00448263732318, 0.00421264887837, 0.00391166098995, 0.00358473262695, 0.00323730138801, 0.00287668241959, 0.00251095651274, 0.00214727124604, 0.0017956574309, 0.00146267760685, 0.00115499093476, 0.000880427708607, 0.000641250378439, 0.000440896540554, 0.000281225942953, 0.000159614098133, 7.46793966478e-5, 2.31568718448e-5, 0.0, 1.13686565366e-20, 0.0, 1.0519432823e-20, 5.21249455664e-20, 9.65643071233e-21, 0.0, 4.61904315613e-20, 0.0, 1.95792471034e-20, 0.0, 0.0, 5.19540424989e-7, 3.22216144777e-7, 3.98041097298e-7, 9.35727425867e-7, 1.09525398858e-6, 7.41912643343e-7, 1.11452959576e-6, 1.37316151998e-6, 1.14899955984e-6, 1.03179785652e-6, 1.08558720525e-6, 6.84134826494e-7, 7.22125925451e-7, 9.36173770132e-7, 1.06476281226e-6, 1.30807547629e-6, 1.55335609635e-6, 1.54549140859e-6 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.056484824123486106], uv: [5.0, 4.999999999999999], spectrum: [ 0.00306831535751, 0.00306816417356, 0.00306889286396, 0.00306981336722, 0.00307095108822, 0.00307266896186, 0.00307744084702, 0.00308287986109, 0.00309259374462, 0.00310751600647, 0.00313351503369, 0.00317068780008, 0.00322125690902, 0.0032880187649, 0.00336678408051, 0.00345915352035, 0.00356562585802, 0.00368391352753, 0.00381098458484, 0.00394244262917, 0.00407556068704, 0.00420703382845, 0.00433239703439, 0.00444707355824, 0.00455141889007, 0.00464107289129, 0.00471016165756, 0.00475497957256, 0.00477273404837, 0.0047615454848, 0.00471801113441, 0.00464318787835, 0.00453507911784, 0.00439419864066, 0.00422489273236, 0.00402938476063, 0.00381191131641, 0.00357664090238, 0.00332829044026, 0.00307117725682, 0.00281169285477, 0.00255418513679, 0.00230470456786, 0.00206722497792, 0.00184651485486, 0.00164493705024, 0.00146515203579, 0.00130967505521, 0.00117574219618, 0.00106400864034, 0.000973749042299, 0.000902376017481, 0.00084737757163, 0.000804787764687, 0.00077234614347, 0.000747251030533, 0.000728109664737, 0.000714346281301, 0.000703620310904, 0.00069590430287, 0.000690466295825, 0.00068725139835, 0.000684770161693, 0.000682645833429, 0.000681281115655, 0.000680870603331, 0.000679992164812, 0.000680380087715, 0.000681510370349, 0.000681181328598, 0.000680047038844, 0.000679125644796, 0.000677905748199, 0.000677413668657, 0.000677115338526, 0.000677595481073, 0.000677730867656, 0.000677604120123, 0.000677375344955, 0.000677097093364, 0.00067702115062 ] }, spectrum_data_point_t { xystar: [0.0, 0.056484824123486106], uv: [6.0, 4.999999999999999], spectrum: [ 0.00235213438702, 0.00235265948988, 0.00235382772128, 0.00235484489216, 0.00235632646404, 0.00235929683999, 0.00236392463197, 0.00237007500814, 0.00237917440915, 0.00239566082626, 0.00242187159191, 0.00246053995227, 0.00251380919143, 0.00258326633886, 0.00266715345575, 0.00276673143901, 0.00287875751682, 0.00300165763988, 0.00313707933093, 0.00328009433915, 0.00342726013728, 0.0035750900858, 0.00371970538538, 0.00385889689933, 0.00399098448078, 0.00411224535857, 0.00421888514676, 0.00430849971081, 0.00437681107144, 0.00441959406407, 0.00443706229616, 0.00442822548326, 0.00439394942875, 0.00433437522273, 0.00424701730467, 0.00413737576545, 0.00400815078487, 0.00385964243551, 0.00369716835388, 0.00352479627566, 0.00334924261693, 0.00317320665147, 0.00299939148291, 0.00283411471357, 0.00268152501153, 0.00254347550684, 0.00241920971519, 0.00231179793878, 0.00222124832083, 0.0021468260241, 0.00208540686005, 0.00203538653323, 0.00199655475654, 0.00196692780348, 0.00194338681373, 0.00192551211147, 0.00191272716803, 0.001902832915, 0.00189546922533, 0.00188991480947, 0.00188674429609, 0.00188485911368, 0.00188381115312, 0.00188300242205, 0.00188308503211, 0.00188355376617, 0.00188323873731, 0.00188289208729, 0.0018822028972, 0.0018815904869, 0.00188053398409, 0.00187969344382, 0.00187884504801, 0.00187815386496, 0.00187791032921, 0.00187773212108, 0.0018774415505, 0.00187700959805, 0.00187626135945, 0.00187590285437, 0.00187576927877 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.056484824123486106], uv: [6.999999999999999, 4.999999999999999], spectrum: [ 0.00165777522963, 0.001656525778, 0.00165593611864, 0.00165511109416, 0.00165470643693, 0.00165478945993, 0.00165660377194, 0.00165980509329, 0.00166764588984, 0.00168238770536, 0.00170739016494, 0.00174415808846, 0.00179758980823, 0.00186905135196, 0.00195705524194, 0.00206300327231, 0.00218531401422, 0.00232298074709, 0.00247234548606, 0.00262985720938, 0.00279403299192, 0.00296089603906, 0.00312930631638, 0.00329487078212, 0.00345576565975, 0.0036077944776, 0.00374993401874, 0.00387926219479, 0.0039916903305, 0.00408688497433, 0.00416145173041, 0.0042151967398, 0.00424786032728, 0.00425912682197, 0.00425095064673, 0.0042239566837, 0.0041807623104, 0.00412324663942, 0.00405335611041, 0.00397333318045, 0.00388553184001, 0.00379422849694, 0.00370265347298, 0.00361315758125, 0.00352876243993, 0.00345094978266, 0.00338215450527, 0.00332239069275, 0.00327140084643, 0.00323084825143, 0.00319731215625, 0.00317078470113, 0.00315046382848, 0.00313414791567, 0.00311962253712, 0.00310841596364, 0.00309994115588, 0.00309255086322, 0.00308526303144, 0.00307915458666, 0.0030740100993, 0.0030698146508, 0.00306768581233, 0.00306622878064, 0.00306430201927, 0.00306446696012, 0.00306482725386, 0.00306506691514, 0.00306578825, 0.00306658864747, 0.00306776803171, 0.00306902701361, 0.00307063528288, 0.00307259052644, 0.00307424593331, 0.00307589484245, 0.0030778182551, 0.00307924699596, 0.0030799818391, 0.00308044876128, 0.00308071074913 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.056484824123486106], uv: [8.0, 4.999999999999999], spectrum: [ 0.000905383547515, 0.000905747696491, 0.000905264577302, 0.000905702679382, 0.000907487011437, 0.000911778812856, 0.000917598221972, 0.000924308380533, 0.000934303143024, 0.000951424067209, 0.000981562795743, 0.0010263771701, 0.00108670094492, 0.00116363704921, 0.0012602320375, 0.00137220271357, 0.00150366469274, 0.00164942238931, 0.00180834327855, 0.00197915532718, 0.00215817487604, 0.0023421971812, 0.00252961146654, 0.00271894466694, 0.00290713886638, 0.00309266594821, 0.00327314568368, 0.00344390339776, 0.00360368238701, 0.00375121585332, 0.0038834928459, 0.00400177814501, 0.00410348988691, 0.00419112484866, 0.00426282104683, 0.00431936314882, 0.00436235782145, 0.00439209705064, 0.00441131216098, 0.00442011253807, 0.004419750869, 0.00441281536001, 0.00440186377865, 0.00438797949772, 0.00437418092426, 0.00435986859341, 0.00434637818574, 0.00433482036157, 0.00432363629345, 0.00431366052174, 0.00430628052834, 0.00429990799492, 0.00429518231373, 0.00429203274399, 0.00428918391482, 0.00428685704247, 0.00428511457041, 0.0042836443852, 0.00428236320381, 0.00428089608249, 0.00428003673737, 0.00427949188901, 0.00427918972251, 0.00427997144231, 0.0042812285868, 0.00428225838757, 0.00428315017992, 0.00428264286945, 0.0042831701661, 0.00428248665587, 0.00428110423914, 0.0042803550837, 0.00427922666412, 0.00427881952925, 0.0042775554499, 0.0042773552462, 0.00427780803932, 0.00427689146418, 0.00427695533535, 0.00427661587444, 0.00427652879209 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, 0.056484824123486106], uv: [9.0, 4.999999999999999], spectrum: [ 0.000201458296072, 0.000201259401349, 0.000201430023599, 0.000201736772141, 0.000201983085725, 0.000203042652675, 0.000205339570811, 0.000210561401801, 0.000219561935994, 0.00023525290151, 0.000262993538742, 0.000307683269754, 0.000370507445664, 0.000453140961103, 0.000554139713855, 0.000673865699715, 0.000813497531867, 0.00096993538239, 0.00114267147581, 0.00132964039245, 0.00152578674005, 0.00172907660719, 0.00193845065896, 0.00215121074533, 0.00236568028265, 0.00257994094478, 0.00279327769184, 0.00300347359388, 0.00320757307985, 0.00340571717795, 0.0035968214302, 0.0037786303484, 0.00395185204283, 0.00411592054115, 0.00426964462452, 0.00441294720115, 0.00454506315122, 0.00466617172538, 0.00477709211401, 0.00487743220089, 0.00496795710661, 0.00504819547675, 0.00511822346822, 0.00517861185971, 0.00523023795334, 0.00527430271769, 0.00531128872292, 0.00534133470817, 0.0053655299078, 0.00538466438133, 0.00540035173943, 0.00541324257988, 0.00542281281688, 0.0054299125453, 0.00543568364028, 0.00544033582313, 0.00544350668884, 0.00544653421249, 0.00544864298621, 0.00544956449157, 0.00545024017474, 0.00545047088216, 0.0054502091305, 0.00545010632812, 0.0054497136449, 0.00544930627879, 0.00544886001995, 0.00544877153702, 0.00544868855019, 0.00544845762105, 0.00544824813646, 0.00544794352807, 0.00544773459553, 0.00544743473349, 0.0054472189396, 0.00544679384257, 0.00544679026659, 0.00544662240018, 0.00544626606821, 0.0054465642138, 0.00544625099672 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, 0.056484824123486106], uv: [10.0, 4.999999999999999], spectrum: [ 1.92769335555e-6, 1.37360513351e-6, 8.71790612511e-7, 1.79955584192e-7, 5.34021427589e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.09268516026e-20, 1.62677904939e-19, 0.0, 6.54152757036e-20, 2.73290434806e-19, 1.12593212447e-20, 0.0, 2.92449053213e-5, 0.000157340453052, 0.000360608604748, 0.000620949978232, 0.000920848294074, 0.00124918850638, 0.00159602116369, 0.001955410364, 0.00231824765963, 0.00267687269206, 0.00302799591741, 0.0033660515505, 0.00368727267715, 0.00398985604878, 0.00427186279908, 0.0045329454216, 0.00477256585964, 0.00499013185653, 0.00518717429914, 0.0053626416973, 0.00551670146383, 0.00565312371109, 0.00577126511647, 0.00587300866495, 0.00595918290972, 0.00603071544765, 0.00609051371103, 0.00613894871351, 0.00617886257874, 0.00621111258979, 0.00623610455212, 0.00625502830803, 0.00626955299347, 0.00628030342276, 0.00628802500874, 0.0062956340952, 0.00630138434623, 0.00630467402934, 0.00630663083975, 0.00630810824094, 0.00630894391625, 0.00630911033534, 0.00630942712143, 0.00630933127776, 0.00630946841462, 0.0063094029945, 0.0063091919095, 0.00630926518525, 0.00630978362664, 0.00631044192253, 0.00631085664534, 0.00631094414133, 0.00631098205033, 0.00631074942486, 0.00631035757024, 0.00630996662911, 0.006309738591, 0.00630957988118, 0.00630955124689, 0.00630951117535, 0.00631005903714, 0.00631012577622 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, 0.11296964824697227], uv: [1.9999999999999991, 6.0], spectrum: [ 0.0, 2.1352544766e-18, 1.15721348301e-18, 4.97865859685e-19, 0.0, 0.0, 0.0, 3.18378374817e-19, 1.90790444059e-18, 0.0, 0.0, 1.00359216809e-18, 0.00021699481259, 0.000999400716976, 0.00229699033323, 0.00402745384529, 0.0060786702822, 0.0083164191158, 0.0105926874383, 0.0127486185162, 0.0146468692817, 0.0161757052815, 0.0172477979987, 0.0178030842649, 0.0177865347557, 0.0171509500654, 0.0158548216626, 0.013910924974, 0.0114104976727, 0.00853812103564, 0.00557506735269, 0.00288507830299, 0.00087729637637, 1.20930452995e-18, 0.0, 0.0, 4.93270500484e-19, 2.69843558616e-18, 0.0, 4.0373965693e-18, 0.0, 0.0, 0.0, 0.0, 6.1507295012e-18, 0.0, 7.28393606997e-18, 2.28406581297e-18, 4.13196120664e-18, 0.0, 6.12811278368e-18, 2.33730854174e-18, 1.7310770908e-18, 9.94624228027e-19, 1.4248512339e-18, 1.73165261796e-18, 7.51822360409e-19, 0.0, 5.74837749165e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.44045035743e-19, 1.45979239035e-18, 1.0246925209e-18, 1.0650655192e-18, 3.9753966372e-19, 4.8252077173e-19, 4.17702104545e-19, 0.0, 0.0, 0.0, 0.0, 2.04876094117e-19, 4.95302515907e-19, 2.02421238299e-7, 4.59727132122e-19 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.11296964824697227], uv: [3.0, 6.0], spectrum: [ 0.00198748905677, 0.00198795988863, 0.00198905701, 0.00199139753607, 0.00199626841049, 0.00200524163139, 0.0020214612418, 0.00205135097116, 0.00210350660477, 0.00219605336277, 0.00235072824535, 0.00258626173639, 0.00291148973663, 0.00332729412851, 0.00382779596523, 0.00440179929899, 0.00503423996835, 0.00570590438603, 0.00639436322126, 0.00707269670381, 0.00771798437081, 0.00830726556172, 0.00881944223714, 0.0092361374907, 0.00953924359265, 0.00970811160498, 0.00971800710684, 0.0095479442521, 0.00918637682873, 0.00863206760331, 0.00789630220062, 0.00700493352175, 0.00599387489393, 0.00490527293229, 0.00378676772686, 0.00269616304312, 0.00169939099519, 0.00086680490808, 0.000273698830734, 0.0, 2.46041822463e-19, 2.04463913231e-19, 1.78201708691e-19, 0.0, 1.54164929862e-19, 1.81463923939e-19, 4.00030119035e-19, 0.0, 5.85299177945e-20, 0.0, 9.31782101984e-20, 0.0, 0.0, 0.0, 9.96222103271e-20, 0.0, 1.3745688206e-19, 5.21841632779e-20, 1.70504213453e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.68058309424e-8, 1.63791735401e-7, 0.0, 7.00747417016e-8, 4.24525579494e-7, 2.70157185847e-7, 6.9493400322e-7, 8.76207407963e-7, 4.25890222851e-7, 4.47661280186e-7, 5.30160906003e-7, 4.19235863366e-7, 2.79212864207e-7 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.11296964824697227], uv: [3.9999999999999996, 6.0], spectrum: [ 0.00262927878119, 0.00263001325013, 0.00262965078328, 0.00263105216766, 0.00263365004897, 0.00263810287821, 0.00264663897094, 0.00266234177351, 0.00268545211246, 0.00273382672703, 0.00280651555404, 0.00291336718575, 0.00306626377037, 0.00326110113109, 0.00349805045516, 0.00377117930361, 0.00407693895865, 0.00441228453599, 0.00476664071786, 0.00512800093172, 0.00548517236295, 0.00583178696348, 0.00615695962558, 0.00645224725252, 0.00670886557543, 0.00691261064299, 0.00705483138293, 0.00712570216733, 0.0071121061889, 0.00700858042627, 0.00680912322123, 0.006518288525, 0.00614469989507, 0.00569420912738, 0.00517646478696, 0.00460274655016, 0.0039908183025, 0.00335931840011, 0.00272847806822, 0.00211790082099, 0.00154628023143, 0.00103746375879, 0.000611348860169, 0.000288408873156, 8.10244116455e-5, 3.99375895093e-20, 0.0, 1.12748181582e-19, 1.48623874871e-19, 5.07656531098e-20, 2.23006069104e-19, 2.10965887916e-19, 7.4197188647e-20, 2.02530672574e-19, 1.14494233647e-20, 0.0, 9.94740170483e-21, 0.0, 0.0, 3.47816299462e-19, 4.45381502754e-22, 2.17321196857e-19, 1.40476046753e-19, 0.0, 1.99681742091e-19, 6.75041334532e-20, 5.32756361759e-20, 2.71630441448e-7, 0.0, 0.0, 6.14444764316e-7, 1.13657006912e-6, 8.46653924712e-8, 0.0, 1.02912041671e-6, 8.23053666693e-7, 4.21840856958e-8, 0.0, 7.88040365724e-8, 0.0, 5.50571415715e-21 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.11296964824697227], uv: [5.0, 6.0], spectrum: [ 0.00225414649188, 0.00225393204463, 0.00225356635707, 0.00225483333669, 0.0022579583522, 0.00226151327277, 0.00226680731867, 0.00227813923991, 0.00229635503756, 0.00232846537743, 0.00238412108243, 0.00246252616622, 0.0025715004516, 0.0027173193105, 0.00289692496542, 0.00310317452929, 0.0033360692436, 0.00359421859803, 0.00387311736703, 0.00416308287017, 0.00445575566297, 0.00474450040415, 0.00502365860022, 0.00528758749779, 0.005530829797, 0.0057447851193, 0.00591978643527, 0.00604932357462, 0.00612827829032, 0.00614823403118, 0.0061048048623, 0.00599815303584, 0.00583542150129, 0.00561031696457, 0.00533104581421, 0.00500301990676, 0.00463334415746, 0.00423014763966, 0.0038033516419, 0.00336515646968, 0.00291992725192, 0.00247969967736, 0.0020575977827, 0.00166223181664, 0.00130372043484, 0.000986532947471, 0.00071201480831, 0.000486726711618, 0.000310938461599, 0.000174392275519, 8.10211799422e-5, 2.86447119124e-5, 0.0, 8.30048139007e-20, 1.85351298645e-19, 1.79647647315e-19, 5.21352171678e-20, 1.48586011312e-19, 5.61825871117e-20, 1.30042152484e-19, 0.0, 9.28640194261e-20, 9.56264753312e-20, 6.00334601366e-20, 8.99972506458e-20, 1.10262405327e-19, 1.21337469694e-19, 3.02444473606e-7, 1.30075391988e-6, 2.22537671325e-6, 1.85079964919e-6, 1.17364088918e-6, 1.37859829839e-6, 2.00183311872e-6, 1.20402499058e-6, 3.1122989287e-7, 4.02187186643e-7, 3.84853747502e-7, 1.45290354886e-7, 1.16043513774e-19, 1.46907276789e-19 ] }, spectrum_data_point_t { xystar: [0.0, 0.11296964824697227], uv: [6.0, 6.0], spectrum: [ 0.00161820540752, 0.00161861221842, 0.00161847474526, 0.00161932507098, 0.00162109394845, 0.00162332860202, 0.00162770472117, 0.00163633807548, 0.00165298575559, 0.00168062667003, 0.00172729280498, 0.00180075071072, 0.00190330028589, 0.00203984605965, 0.0022068264401, 0.0024051788726, 0.0026317608521, 0.00288345848041, 0.00315647555887, 0.00344534815993, 0.00374129105277, 0.00403700363526, 0.00432731683862, 0.00460584594723, 0.00486957670298, 0.00510946925661, 0.00532151661864, 0.00549961093426, 0.0056330180178, 0.00571949651007, 0.00575483664973, 0.005738868099, 0.00566795679979, 0.00554452360873, 0.00536983396914, 0.00514893747959, 0.00488852278959, 0.00459348475746, 0.00427333526484, 0.00393243498596, 0.00358188644017, 0.00322945510136, 0.0028865804626, 0.00255852010844, 0.00225269459647, 0.00197259722519, 0.0017227862845, 0.00150561663224, 0.00131991079874, 0.00116788309546, 0.00104351669536, 0.000944024135699, 0.000866526870846, 0.000807271726222, 0.000761697771237, 0.000727847210657, 0.00070272942851, 0.000684737507003, 0.000672321431001, 0.000663199444457, 0.000656232100349, 0.000649658255272, 0.000645278308165, 0.000642093880398, 0.000639350796459, 0.000637278149328, 0.000635212611648, 0.000633883921856, 0.00063324321748, 0.000632343354473, 0.000632595180188, 0.000632750662456, 0.000632891504076, 0.000632754050132, 0.000631619471173, 0.000631045978761, 0.000631577625447, 0.000631061770993, 0.000631154240244, 0.00063165085361, 0.000631459605078 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.11296964824697227], uv: [6.999999999999999, 6.0], spectrum: [ 0.000882493777461, 0.000881705278857, 0.000882602183772, 0.000883409980422, 0.000884756224786, 0.000888376377698, 0.000893675769878, 0.000903455701221, 0.000921105729404, 0.000951138169817, 0.00100195236053, 0.00107833093428, 0.00118622983748, 0.00132683698536, 0.00150204459779, 0.00170972960208, 0.00194667628475, 0.00221101684444, 0.00249735813735, 0.00280078664911, 0.00311395207575, 0.00343026091326, 0.00374278601684, 0.0040471865385, 0.00433801345009, 0.00460953016285, 0.00485602300237, 0.00507182555464, 0.00525029922473, 0.00538574427431, 0.00547491277416, 0.00551770393462, 0.00551326718183, 0.00546294367959, 0.00536853389381, 0.00523334969729, 0.00506214509627, 0.00485974624109, 0.00463072341496, 0.00438212804714, 0.00412163801792, 0.00385713870389, 0.00359551172963, 0.00334131813335, 0.00310264851378, 0.00288429659739, 0.0026883534835, 0.00251735271588, 0.00237220688175, 0.00225221341279, 0.00215322214657, 0.00207435856748, 0.00201160680469, 0.0019639546382, 0.0019274565918, 0.00189920505731, 0.00187886198183, 0.00186367787351, 0.00185223893691, 0.00184391457655, 0.00183701292948, 0.00183276541422, 0.00182995569392, 0.00182808730412, 0.00182662005211, 0.0018259220627, 0.00182559170935, 0.00182493371959, 0.00182447614855, 0.00182435761227, 0.00182400159488, 0.00182408440876, 0.00182384844699, 0.00182347322961, 0.00182325891488, 0.00182301969513, 0.00182304253017, 0.00182311723576, 0.00182302931489, 0.00182359408429, 0.00182370657821 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.11296964824697227], uv: [8.0, 6.0], spectrum: [ 0.00016488961411, 0.000164102352059, 0.000165801979055, 0.000167874232366, 0.000170313081751, 0.000172031427297, 0.000178858261908, 0.000188937302795, 0.000206355159034, 0.000239092822234, 0.000291178821009, 0.000369927492607, 0.000480309359486, 0.00062500866921, 0.00080001869199, 0.00100811867362, 0.00125648073207, 0.00153013148141, 0.00182375433065, 0.00213953216841, 0.00246578952273, 0.00280235815495, 0.00313748808963, 0.00346564643398, 0.0037849192405, 0.00408770792591, 0.00437139774911, 0.00462916148962, 0.00485496553953, 0.00504214130417, 0.00519119864458, 0.0052972021616, 0.00536407200572, 0.00539328179037, 0.00538228464542, 0.00533363238693, 0.00524731403538, 0.00513355839821, 0.00499856151071, 0.00484015337325, 0.0046685850846, 0.0044892027732, 0.00430951873824, 0.00413123581832, 0.00395892546847, 0.00379762203604, 0.00365484150858, 0.00352927397233, 0.0034201051603, 0.00332956714422, 0.00325095654926, 0.00318561903807, 0.00313544563458, 0.00309428976861, 0.00306033854408, 0.00303455666855, 0.00301595948436, 0.00300241854872, 0.00298748758715, 0.00297673004049, 0.00297059081424, 0.00296690615978, 0.00296372534142, 0.00296244493857, 0.00296102145707, 0.00296186235101, 0.00296449094466, 0.00296198307317, 0.00296245828049, 0.00296430662036, 0.0029656846385, 0.0029685722772, 0.00297009753231, 0.00297156094345, 0.00297281649209, 0.00297369231323, 0.00297409370313, 0.0029758009901, 0.00297467073241, 0.00297567638229, 0.00297584332186 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, 0.11296964824697227], uv: [9.0, 6.0], spectrum: [ 1.72895767792e-8, 8.08524299853e-8, 5.48817661668e-8, 2.05892750533e-20, 0.0, 5.49441061985e-20, 0.0, 2.83072186135e-20, 0.0, 0.0, 0.0, 0.0, 7.46798798285e-20, 3.33754935221e-20, 3.2590496735e-5, 0.000133780473526, 0.000300376979773, 0.000527801488319, 0.000808837599054, 0.0011318868907, 0.00148717597586, 0.00186410993522, 0.00225389817958, 0.0026479948129, 0.00303784312699, 0.00341820404614, 0.00378182528557, 0.00412262181891, 0.00443534511463, 0.00471289483633, 0.00495105319878, 0.00514942557844, 0.0053059366488, 0.00542029096026, 0.00549357832563, 0.00552876423123, 0.00552876998317, 0.00549729426051, 0.00543762858095, 0.00535335634115, 0.00525101513815, 0.00513663836408, 0.00501605309479, 0.00489400443295, 0.0047741285448, 0.00466021076982, 0.00455629434294, 0.00446353698641, 0.00438325351486, 0.00431593306967, 0.00425996484556, 0.00421493576159, 0.00418019176743, 0.00415269824786, 0.00413171277582, 0.00411670941757, 0.00410590577836, 0.00409823647104, 0.0040931396611, 0.00408872023732, 0.0040850724277, 0.00408223409946, 0.00408056064816, 0.00407909356647, 0.00407737171798, 0.00407604611186, 0.00407563002539, 0.00407498373302, 0.00407456175032, 0.00407455409576, 0.00407372837359, 0.00407329693058, 0.0040734896966, 0.00407253201456, 0.00407212461703, 0.00407202925703, 0.00407109825312, 0.00407100159636, 0.00407094512658, 0.00407021206718, 0.00407027181173 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, 0.16945447237045844], uv: [1.9999999999999991, 7.0], spectrum: [ 1.00324917886e-18, 5.53108447967e-18, 3.11828901548e-18, 5.5057087059e-18, 1.92405961356e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 1.51345380295e-18, 0.0, 6.46228592287e-18, 1.82967724254e-18, 0.0, 0.0, 0.00175837508494, 0.0051086555725, 0.00947521898763, 0.0142054824268, 0.0187229839026, 0.0225588346612, 0.0253530132414, 0.0268756875729, 0.026957301587, 0.0254889539137, 0.0224342364451, 0.0179589818298, 0.012523321047, 0.00692834668445, 0.00229306075975, 0.0, 0.0, 0.0, 0.0, 2.45915935639e-17, 1.22937186245e-17, 0.0, 0.0, 3.68312639482e-18, 0.0, 0.0, 2.53711087387e-17, 7.50598252114e-18, 3.30745532888e-18, 4.68630883914e-18, 8.52146487664e-18, 0.0, 7.60091114649e-19, 1.26943925342e-17, 0.0, 0.0, 0.0, 0.0, 1.57356106699e-18, 0.0, 0.0, 0.0, 2.25913004989e-19, 0.0, 1.18458906935e-18, 0.0, 5.4866327814e-19, 1.72597898863e-18, 0.0, 1.12498243732e-18, 2.79005988202e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.47232364546e-18, 6.54676033147e-20, 5.03136649389e-19, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.16945447237045844], uv: [3.0, 7.0], spectrum: [ 1.15743165544e-19, 0.0, 1.09770033088e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.69617148166e-5, 0.000193544207676, 0.000538786613928, 0.00108473856172, 0.00183256735001, 0.00276601122186, 0.00385705562488, 0.00507139088168, 0.00636538053708, 0.0076883401117, 0.00898364366252, 0.0101977581165, 0.0112828691563, 0.0121990688182, 0.0129136312579, 0.0133902476701, 0.0135914773209, 0.0134751722763, 0.0130109019999, 0.0121876844141, 0.0110231415145, 0.00956624270525, 0.00789557784145, 0.00610982029053, 0.00432140911166, 0.00266031746231, 0.00127517971551, 0.000329470675459, 4.55768191655e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.18035687855e-19, 2.78953862625e-18, 3.77527808635e-19, 1.21159999337e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 2.530707565e-19, 6.5751162674e-19, 0.0, 0.0, 2.32307425394e-19, 0.0, 0.0, 9.14953171361e-20, 0.0, 1.0499651231e-19, 4.81498546498e-19, 0.0, 1.95808790488e-20, 1.49106968617e-19, 1.41126886319e-19, 1.66448601873e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.35633978014e-19, 1.70646856102e-19, 1.52944808236e-7, 1.92712471547e-7 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.16945447237045844], uv: [3.9999999999999996, 7.0], spectrum: [ 0.00115873621413, 0.00115929670516, 0.00116045356524, 0.00116271987518, 0.00116701528728, 0.00117553931187, 0.00119072326331, 0.00121727852919, 0.00126534199166, 0.00134850164349, 0.00148740569878, 0.00169916267577, 0.00199330178565, 0.00237176561652, 0.00283117088615, 0.00336335048895, 0.00395899279681, 0.00460242401727, 0.00527740010213, 0.00596309467986, 0.00663912819498, 0.00728560151263, 0.007882938979, 0.0084168846055, 0.00886939618161, 0.00921968461558, 0.00944793988847, 0.00953313638189, 0.00945740721202, 0.00921093395605, 0.00879530471792, 0.00822067831597, 0.00750401662751, 0.00666808391263, 0.00574106582812, 0.00475622918955, 0.0037537579058, 0.00277649694128, 0.00187275481428, 0.0010929847855, 0.000488320385958, 0.000108187095042, 0.0, 0.0, 0.0, 5.61286719647e-20, 5.8876416571e-20, 0.0, 7.99871652998e-20, 0.0, 1.28234817948e-19, 0.0, 0.0, 6.98755120264e-20, 2.38723851719e-19, 2.91013251543e-19, 0.0, 1.3169907363e-19, 0.0, 1.88601006723e-19, 1.5349857474e-19, 4.21442245785e-19, 3.17180082928e-19, 5.35320536919e-19, 4.06940372797e-8, 2.78507644942e-19, 4.0508948797e-7, 5.73694327217e-7, 7.15507203868e-7, 8.07934753441e-7, 1.63843722886e-6, 2.30697905025e-6, 1.79772365321e-6, 1.46131034262e-6, 1.28547133647e-6, 1.35481174738e-6, 1.18220603741e-6, 1.21040725936e-6, 1.38067866522e-6, 1.20124384248e-6, 1.37276378593e-6 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.16945447237045844], uv: [5.0, 7.0], spectrum: [ 0.0012537484039, 0.00125332419504, 0.0012544515174, 0.00125726779142, 0.00125967512714, 0.00126438915539, 0.00127452008628, 0.00129118247446, 0.00132213044587, 0.00137502264339, 0.00146418515679, 0.00160219515685, 0.00179456881183, 0.00204606628439, 0.0023546197472, 0.00271633186239, 0.00312694529195, 0.00357800423002, 0.0040613507904, 0.00456254882731, 0.00506835176514, 0.00556727188032, 0.00604801968309, 0.00649767185926, 0.00690583656437, 0.00725977532605, 0.00754758883539, 0.00775380493596, 0.00786706496354, 0.00787591929892, 0.00777760337144, 0.00757054254782, 0.00726154661542, 0.00685625739552, 0.00636495030494, 0.00580005133705, 0.00517629459866, 0.00451071224441, 0.00382452333013, 0.00313697713328, 0.00247141066386, 0.00184711720303, 0.00128808066868, 0.000812575381552, 0.000436550378989, 0.000173348294329, 2.73253250975e-5, 1.82538537761e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.58391461091e-20, 0.0, 0.0, 6.34473049936e-8, 7.82280219055e-8, 2.27227947472e-20, 1.13518481073e-20, 3.32422836006e-7, 3.19746063258e-7, 4.20048425851e-7, 4.92256952386e-7, 5.25267650336e-7, 7.56897833923e-7, 3.26520965927e-7, 3.07709840257e-8, 2.94720740877e-7, 1.84154202342e-7, 3.12041358615e-7, 4.48603512631e-7, 2.32232115726e-7, 4.33291170677e-7 ] }, spectrum_data_point_t { xystar: [0.0, 0.16945447237045844], uv: [6.0, 7.0], spectrum: [ 0.000817431207442, 0.000817701931495, 0.000817489702638, 0.000818393403807, 0.000820701557474, 0.000824733596875, 0.000832335086187, 0.000845363905191, 0.000868053702417, 0.000911100880877, 0.000985532765366, 0.00110164594974, 0.00126422658346, 0.00147631479187, 0.00173797993504, 0.00204557368986, 0.00239770310187, 0.00278857025075, 0.00320943461199, 0.0036503447432, 0.00410277460106, 0.00455777715179, 0.00500636355515, 0.00543628551199, 0.00583952786326, 0.00620758682913, 0.00652988348664, 0.00679559185874, 0.0069924099607, 0.00711142479481, 0.00714799719819, 0.00710091361259, 0.00697079790649, 0.00676151798533, 0.00647623143203, 0.00612373310143, 0.00571115083682, 0.00525013977948, 0.00474941350686, 0.00422451895564, 0.00368868061711, 0.00315585374832, 0.0026409925672, 0.00215382392821, 0.00170677194807, 0.0013077526039, 0.000964731647765, 0.000677709106011, 0.000447782874199, 0.000271393366789, 0.000145943560148, 6.23864492186e-5, 1.50541172303e-5, 9.49633637992e-21, 0.0, 0.0, 0.0, 4.43760612514e-20, 5.09081419119e-20, 0.0, 0.0, 3.70128308284e-20, 3.23762984869e-20, 1.23283242811e-20, 0.0, 4.05851737477e-8, 5.75398975948e-7, 2.51598044213e-7, 3.27723957156e-7, 4.04111272838e-7, 3.44466201599e-7, 3.74078735681e-7, 4.98015323176e-7, 7.45340069726e-7, 1.07096958757e-6, 1.26980426947e-6, 1.35816214948e-6, 1.7764146569e-6, 2.32577381875e-6, 2.21033579737e-6, 2.57002465945e-6 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.16945447237045844], uv: [6.999999999999999, 7.0], spectrum: [ 0.000123470784956, 0.000125795322865, 0.000124841881045, 0.00012464217155, 0.000125497661553, 0.000130410215895, 0.000138568671191, 0.000152379097111, 0.00017805118245, 0.000221104619245, 0.000296551039953, 0.000411434392809, 0.000575274436747, 0.0007864524321, 0.0010486048463, 0.00135447234652, 0.00170610450676, 0.00209909944064, 0.00252181591418, 0.00297131083764, 0.00343038831909, 0.00389435188675, 0.00435144401306, 0.0047956959147, 0.0052182378245, 0.00561180604206, 0.00596698198893, 0.00627046410867, 0.00651346100632, 0.0066913165508, 0.00679552467609, 0.00682935070607, 0.00678889563497, 0.00667327642512, 0.0064899027481, 0.00624256014001, 0.00594111004332, 0.00558938791173, 0.00519893120257, 0.00478257801236, 0.00434609774803, 0.00390645764436, 0.00347370093354, 0.00305683260711, 0.0026673651643, 0.00231140233792, 0.00199351090092, 0.00171588983599, 0.00148333655333, 0.00128537365315, 0.00112682509748, 0.000997968433538, 0.000896017214386, 0.000817180100542, 0.000756407372833, 0.000709098674681, 0.000675048876521, 0.000649098503623, 0.000627867738875, 0.00061305216602, 0.000603226830062, 0.00059422761148, 0.000587716867816, 0.000583695810775, 0.00058247439678, 0.000580864504859, 0.000579218126224, 0.000578621039087, 0.0005792580081, 0.00057968991984, 0.000580490922012, 0.000581668119013, 0.000581669918877, 0.00058416354397, 0.000586748817985, 0.000586771860098, 0.000588154413673, 0.000589072991631, 0.000590133761552, 0.000590204912664, 0.000590050022171 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.16945447237045844], uv: [8.0, 7.0], spectrum: [ 1.17780477003e-7, 4.30972465934e-8, 2.06847476991e-8, 5.12658028331e-8, 8.51983875354e-8, 0.0, 0.0, 0.0, 2.78620628933e-20, 4.32669317324e-20, 6.52961307177e-20, 2.6178249261e-20, 9.32141316482e-6, 8.97942738923e-5, 0.000246058816506, 0.000476471830472, 0.000773480339697, 0.00113694390684, 0.00155629872122, 0.00201696700795, 0.0025066446994, 0.00300971545155, 0.00351736074581, 0.00402008837596, 0.00450515506443, 0.00496755979101, 0.0053943725039, 0.00577318126165, 0.00609607379467, 0.00635477421165, 0.00654363933453, 0.00666046774664, 0.00670647248332, 0.00667915397174, 0.00658368568603, 0.0064245287692, 0.00620749962606, 0.00594117425656, 0.00563362993809, 0.00529437697032, 0.00493440933484, 0.00456394596578, 0.00419526265977, 0.00383744308036, 0.00349821075282, 0.00318602652359, 0.00290672764276, 0.00266246430095, 0.00245405382357, 0.00227894953773, 0.00213735221361, 0.00202320582689, 0.00193234227451, 0.0018641914668, 0.0018120293016, 0.00177227778131, 0.00174323366202, 0.00172109690904, 0.00170580065178, 0.00169409496648, 0.00168572357589, 0.00168044025397, 0.00167690364494, 0.00167499898635, 0.00167283135787, 0.0016712557074, 0.00167073597055, 0.00166995119061, 0.00166883069575, 0.00166790842941, 0.00166776530537, 0.00166728594735, 0.00166702413312, 0.0016671533362, 0.00166670217763, 0.0016671916947, 0.00166730764897, 0.00166727637584, 0.00166755352906, 0.00166731484089, 0.00166739542715 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, 0.16945447237045844], uv: [9.0, 7.0], spectrum: [ 0.0, 0.0, 4.29651284701e-20, 0.0, 0.0, 0.0, 0.0, 1.45247848932e-19, 0.0, 0.0, 8.70021509786e-20, 1.60702609378e-19, 0.0, 2.17581417559e-19, 3.7981715774e-20, 0.0, 4.50866365081e-19, 0.0, 1.88826734862e-20, 6.4352110369e-5, 0.000353785894203, 0.000810838603668, 0.00138456298171, 0.00203504198293, 0.00272791763533, 0.00343433633662, 0.00412857431135, 0.00478732075364, 0.00538972825105, 0.0059165593689, 0.00635429982966, 0.00669688565158, 0.00694041990745, 0.00708387764299, 0.00713051824326, 0.00708438293872, 0.00695393765734, 0.00674777154317, 0.00647633909568, 0.00615357372261, 0.00579269504551, 0.00540824455041, 0.00501478357961, 0.00462527057032, 0.00425198091749, 0.00390361181486, 0.0035883880797, 0.00331033905461, 0.00307054305261, 0.00286938946847, 0.00270509423268, 0.00257294376162, 0.00246786339579, 0.0023869542821, 0.00232506237735, 0.0022793413613, 0.00224560509447, 0.00222034241994, 0.0022028985343, 0.00218988577368, 0.00218081398682, 0.00217426546123, 0.00216992294849, 0.00216705364262, 0.00216444825874, 0.00216246507797, 0.00216111254753, 0.00215986516774, 0.00215951878429, 0.00215901137028, 0.00215863926745, 0.00215841604998, 0.00215801444528, 0.00215837915499, 0.00215855489475, 0.00215860019113, 0.00215857137368, 0.00215855050682, 0.00215849173016, 0.00215850957937, 0.00215842200504 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, 0.22593929649394454], uv: [1.9999999999999991, 8.0], spectrum: [ 0.0, 0.0, 0.0, 0.0, 7.56462927966e-18, 4.4197680874e-18, 0.0, 0.0, 3.38215562424e-18, 0.0, 0.0, 1.52668283426e-17, 1.69321187674e-17, 0.0, 1.3666063215e-18, 0.0, 1.54936459218e-17, 4.37354254198e-18, 0.000509586877105, 0.00789037250479, 0.0184608381156, 0.0292084626302, 0.0378245912223, 0.0428910195784, 0.0435345681236, 0.0394004640909, 0.0307300456435, 0.0189395915214, 0.00707923296529, 7.94242154775e-17, 0.0, 1.59737460554e-16, 1.62437176154e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 5.50004537727e-17, 0.0, 4.46463919619e-17, 0.0, 0.0, 1.40523725316e-16, 1.90882266375e-16, 0.0, 1.3892505383e-16, 0.0, 2.92978030348e-17, 1.49594699025e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.99301249534e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.45344071248e-18, 5.06908663547e-18, 8.15141519915e-19, 2.93252662558e-19, 0.0, 9.64334535637e-19, 0.0, 2.17742806627e-18, 4.97533478236e-18, 5.07921139064e-18, 2.12467440537e-18, 4.54252930488e-19, 7.81374479164e-18, 1.02859849585e-17, 1.24055491983e-17, 1.92537506713e-17, 1.71398989735e-17, 2.40413233157e-17 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.22593929649394454], uv: [3.0, 8.0], spectrum: [ 0.0, 0.0, 1.46142178274e-18, 5.43425955348e-19, 5.99280560586e-19, 2.27730061452e-18, 0.0, 3.44746124243e-19, 7.97171769262e-19, 0.0, 9.58958877852e-19, 0.0, 5.9392790888e-19, 0.0, 0.000524416374598, 0.00166387920113, 0.00332545007305, 0.00538702058481, 0.00770385415052, 0.0101077751071, 0.0124414410895, 0.0145680700899, 0.0163735136852, 0.0177658336642, 0.0186595381864, 0.018973705745, 0.0186292357661, 0.0175795802956, 0.0158336103919, 0.013476980118, 0.0106820988557, 0.00768986886325, 0.00478207355017, 0.0022812953904, 0.000552385559844, 0.0, 0.0, 0.0, 3.56778726115e-18, 2.18845101785e-19, 8.27745488259e-19, 6.29225575521e-20, 0.0, 1.18246360023e-18, 0.0, 5.77384841248e-18, 7.65251353963e-18, 0.0, 0.0, 0.0, 6.15483568748e-19, 0.0, 0.0, 3.84967027766e-19, 5.64791080255e-20, 0.0, 0.0, 8.47627713942e-19, 5.86819219654e-19, 7.59324383674e-19, 5.10483278518e-19, 9.25482652137e-20, 8.02411063435e-19, 8.08437795217e-19, 1.38463195432e-18, 9.15995169758e-19, 1.87736083037e-18, 2.26585366544e-19, 0.0, 0.0, 1.46626293557e-18, 1.96030846926e-19, 2.97185314575e-19, 9.0844283593e-20, 9.64452889567e-19, 1.38384007758e-18, 9.02090088826e-19, 1.41115689013e-18, 1.7658432489e-7, 2.37042170289e-18, 7.43368528019e-8 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.22593929649394454], uv: [3.9999999999999996, 8.0], spectrum: [ 2.92540044307e-19, 1.82277245419e-7, 0.0, 3.26901989366e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.85714991694e-5, 0.0002827584854, 0.0006597045278, 0.00120372025123, 0.00190804536803, 0.00275566970226, 0.00372480594099, 0.00478791485685, 0.00591177430212, 0.00705420182253, 0.00817297709187, 0.00923025811061, 0.0101940296102, 0.0110345355522, 0.0117211235176, 0.0122205037945, 0.0124952011026, 0.0125144053664, 0.0122530998602, 0.0117039094885, 0.0108808075687, 0.00981453587461, 0.00854924678279, 0.00713928314459, 0.00565027768456, 0.00415979066991, 0.00275512920101, 0.00153211324164, 0.0005930245744, 4.55396567845e-5, 5.71045304426e-20, 0.0, 1.36505005611e-19, 0.0, 1.72209004881e-19, 6.10266414079e-20, 6.94017844189e-19, 0.0, 2.67765376456e-19, 1.33670610948e-19, 0.0, 3.47574921219e-19, 4.96620483579e-20, 1.37994522527e-19, 5.41780634636e-19, 2.6637211968e-19, 0.0, 0.0, 1.29016747737e-19, 1.5113599984e-19, 4.01665153867e-19, 1.78348438348e-19, 2.16946019026e-19, 2.70277890388e-19, 2.01884624346e-19, 1.93141133839e-20, 0.0, 2.49124134832e-19, 0.0, 0.0, 5.4039744943e-7, 2.95648672441e-7, 0.0, 4.25358339875e-7, 3.81835189631e-7, 4.2767387722e-7, 4.1641820813e-7, 1.64692885726e-7, 2.47245726962e-7, 1.73016942183e-7, 1.89414024977e-7 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.22593929649394454], uv: [5.0, 8.0], spectrum: [ 1.57971644663e-19, 4.32833836047e-19, 6.62665532281e-7, 2.60039114807e-19, 1.17204908541e-6, 6.70846769028e-6, 1.54953175903e-5, 3.88644742553e-5, 8.52533294443e-5, 0.000168261847351, 0.000316868727693, 0.000537517589908, 0.000847936530438, 0.00125249198377, 0.00174572761575, 0.00232029601632, 0.00297233070499, 0.00367966930581, 0.00443013015852, 0.00521043136583, 0.00598503437016, 0.00674263382228, 0.00746762900978, 0.00813394580271, 0.00872816241882, 0.00923172752883, 0.00962213514035, 0.00987998925188, 0.00997597519707, 0.0099089475088, 0.00967148628256, 0.00926732537248, 0.0087041251558, 0.00800144191385, 0.0071809988305, 0.00626870037961, 0.00529318556188, 0.00429642334327, 0.00330714703195, 0.0023718347551, 0.0015422154332, 0.000853006625022, 0.00033838584431, 4.53334709259e-5, 0.0, 0.0, 1.04492363518e-19, 3.28855402727e-19, 0.0, 0.0, 4.63798358987e-20, 0.0, 3.97323363439e-20, 2.90325801671e-19, 3.95149936606e-20, 0.0, 0.0, 2.93643160178e-19, 1.58026893759e-19, 2.63497306809e-19, 4.88643757594e-19, 4.00524800711e-19, 3.1208486352e-19, 0.0, 6.9945730853e-19, 5.19050215671e-8, 1.13078898458e-18, 1.36457007803e-18, 3.14882675709e-7, 6.92872950854e-19, 1.18584612616e-18, 1.12239079082e-6, 6.46467213784e-7, 6.74139450422e-7, 9.13799582086e-7, 1.62291512694e-18, 1.23968986092e-6, 1.62628826088e-6, 1.6362240113e-6, 1.3526102775e-6, 1.54244699695e-18 ] }, spectrum_data_point_t { xystar: [0.0, 0.22593929649394454], uv: [6.0, 8.0], spectrum: [ 2.0174225742e-7, 2.10170050037e-19, 0.0, 0.0, 0.0, 0.0, 1.01345040333e-19, 0.0, 1.04855463874e-5, 4.75432124484e-5, 0.000133854793248, 0.000284729339352, 0.00050835374287, 0.000811565295193, 0.00119126405086, 0.00164120829995, 0.00215448081606, 0.00272677187185, 0.00334556613741, 0.00399234227173, 0.00465280313812, 0.00531299122071, 0.00595516127301, 0.00656735544283, 0.00713569512836, 0.00764767442951, 0.00808646741561, 0.00843093852623, 0.00866849036413, 0.00878891817632, 0.00878385210506, 0.00865010843429, 0.0083936771503, 0.00802185615773, 0.00754307903531, 0.00696764185712, 0.006315594587, 0.00560298700222, 0.00485206981614, 0.00408594963247, 0.00332646750341, 0.0026014567884, 0.00193277899591, 0.00133911907324, 0.000841454425851, 0.000453472615449, 0.000183134835826, 3.36503715519e-5, 1.27111135889e-19, 3.36453542162e-19, 2.12407130248e-19, 1.25702482569e-19, 1.63268240341e-19, 2.52425420786e-19, 0.0, 0.0, 0.0, 2.46046557496e-19, 0.0, 1.80364384735e-19, 3.61355478585e-20, 5.18457890325e-20, 0.0, 1.77570716277e-19, 2.01461492549e-19, 1.15514118182e-19, 2.05776066624e-19, 1.50983622848e-19, 4.25475237318e-19, 1.63821465955e-19, 2.69568235464e-19, 2.03261437561e-19, 5.12534342428e-19, 2.11042695934e-7, 5.61726396997e-7, 8.89593672483e-7, 6.75405968361e-7, 2.06071201368e-7, 1.49289556954e-19, 3.56489734831e-7, 2.59927364839e-7 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.22593929649394454], uv: [6.999999999999999, 8.0], spectrum: [ 0.0, 5.83091007189e-8, 5.69914544676e-8, 8.7235589821e-20, 0.0, 0.0, 4.23357394871e-20, 0.0, 1.00537321473e-19, 5.02892080112e-20, 0.0, 0.0, 6.16443087267e-5, 0.000214315452984, 0.000460525268129, 0.000795713167534, 0.00121389026471, 0.00170817467884, 0.00226707644361, 0.00287285096673, 0.00350849467369, 0.00415757932591, 0.00480395283437, 0.00543400231228, 0.00603566234513, 0.00659412814279, 0.00709425696147, 0.00752205285692, 0.00786211724197, 0.00810186530231, 0.00823547306457, 0.00826013736684, 0.00817483811063, 0.00798327906626, 0.00769162757206, 0.00730792982269, 0.00684335211094, 0.0063116930062, 0.00572686975508, 0.00510494512726, 0.00446473261873, 0.00382472528017, 0.00320113821107, 0.00261164259528, 0.00206926822009, 0.00158422944231, 0.00116527315423, 0.000816420471355, 0.000536374474073, 0.000322696989904, 0.000169202964233, 6.89552234426e-5, 1.5061518083e-5, 0.0, 0.0, 1.23870075954e-20, 0.0, 0.0, 1.11455074081e-21, 0.0, 0.0, 0.0, 0.0, 0.0, 1.80740200243e-7, 2.22954811986e-7, 4.5923834441e-7, 3.3021904364e-7, 2.30808678761e-7, 1.47242438181e-19, 2.47411409963e-7, 2.52651567253e-7, 1.42392710292e-7, 3.39141587398e-7, 4.30851077412e-7, 5.42002570397e-7, 6.52034766829e-7, 6.94074271936e-7, 7.96149183052e-7, 8.92049300998e-7, 8.77709473785e-7 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.22593929649394454], uv: [8.0, 8.0], spectrum: [ 4.21913394903e-7, 8.79041152544e-20, 0.0, 0.0, 2.4144528837e-20, 4.97948677704e-20, 1.19987056653e-19, 4.78983607297e-20, 5.63441270706e-20, 7.87222405715e-21, 1.17523488322e-19, 3.83649875941e-20, 0.0, 1.02536512303e-19, 0.0, 6.69712522937e-20, 8.42538462995e-5, 0.000344006772946, 0.000760732391116, 0.00130100742756, 0.00193265668681, 0.00262566420455, 0.00335169917482, 0.0040886612954, 0.00481602344963, 0.00551399884053, 0.00616495549284, 0.00674894610307, 0.00724776951998, 0.00764671494755, 0.00793786197634, 0.00811648666365, 0.00818042435392, 0.00813045928736, 0.00797198851748, 0.00771301144021, 0.00736247007926, 0.00693203695087, 0.00643612729762, 0.00589079426802, 0.00531276681393, 0.00471939405306, 0.00412776589215, 0.00355370296618, 0.00301131856906, 0.00251236590789, 0.00206468802983, 0.00167231616182, 0.00133673201072, 0.00105621366663, 0.000826819959571, 0.000642489299627, 0.000496990482907, 0.000384843300765, 0.000300220947743, 0.000237891283879, 0.000192459475052, 0.000159783145323, 0.000136303331131, 0.000119535743113, 0.000107922024361, 9.955889446e-5, 9.33646234974e-5, 8.84645387886e-5, 8.4933966917e-5, 8.23834535006e-5, 8.04261181935e-5, 7.88317554134e-5, 7.76549578168e-5, 7.68614403582e-5, 7.6554756699e-5, 7.61124919678e-5, 7.56917457626e-5, 7.54689339133e-5, 7.51054863267e-5, 7.51715335682e-5, 7.48603764359e-5, 7.46748410058e-5, 7.46553682777e-5, 7.45513109309e-5, 7.46236061547e-5 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.28242412061743066], uv: [3.0, 9.0], spectrum: [ 0.0, 5.40638980202e-18, 3.79097030517e-18, 0.0, 0.0, 3.65588574062e-18, 2.3450752937e-18, 0.0, 5.42328368498e-19, 6.10413390233e-19, 0.0, 1.15231008677e-17, 0.0, 0.0, 0.0, 1.5401123701e-18, 1.13526070435e-18, 0.0018606310292, 0.00512513077078, 0.00922286536706, 0.0136229039882, 0.0178599480328, 0.0215558946223, 0.0244275897182, 0.0262363240179, 0.0267812216293, 0.0258902242939, 0.0235101219185, 0.0197662916631, 0.0150087208629, 0.00982081324337, 0.00498247433139, 0.00137654737518, 0.0, 0.0, 2.99706351585e-18, 2.03187410024e-17, 0.0, 0.0, 1.08794156507e-17, 0.0, 0.0, 0.0, 0.0, 6.418695001e-18, 6.03369843634e-18, 0.0, 1.71562159006e-17, 3.67661954042e-18, 5.83616717755e-19, 0.0, 0.0, 8.30479377815e-18, 0.0, 4.13420703492e-18, 0.0, 1.75549242947e-18, 0.0, 1.53168382422e-18, 1.90830344867e-18, 8.09184631019e-19, 1.39554076469e-18, 3.50007817028e-18, 1.24540691789e-18, 1.44565913256e-18, 1.06289088685e-18, 0.0, 6.22511144003e-20, 2.43432973759e-18, 9.71706264603e-20, 0.0, 4.036897289e-18, 4.84159427198e-18, 4.66578111812e-18, 2.4261294153e-18, 5.62126981846e-18, 5.24272312429e-18, 1.16833724037e-18, 3.62953617898e-18, 3.37182640478e-18, 5.08072347351e-18 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.28242412061743066], uv: [3.9999999999999996, 9.0], spectrum: [ 3.00730998403e-19, 3.56316982458e-19, 0.0, 0.0, 6.2306507841e-19, 3.03152447431e-20, 1.01060290001e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 4.13327465058e-19, 1.72716093828e-18, 0.000346748749437, 0.00113167041717, 0.00230652058811, 0.00380395453246, 0.00553762644701, 0.00740094236316, 0.00928913530358, 0.011108095069, 0.012774127102, 0.0142208738771, 0.0153818611128, 0.0161921692042, 0.0165770370065, 0.0164806687389, 0.0158702788937, 0.0147497987897, 0.0131732037987, 0.0112318399265, 0.00903955455708, 0.00673395619958, 0.00448018982043, 0.00246596200363, 0.000897292801075, 0.0, 1.53257526022e-19, 9.43386752759e-19, 0.0, 7.42309769397e-20, 1.15394705913e-18, 1.24826575038e-18, 7.46302667942e-19, 1.95379559107e-18, 0.0, 0.0, 4.4543038961e-19, 0.0, 0.0, 0.0, 3.39128315882e-19, 4.75979961655e-19, 8.02652414021e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.04751369663e-7, 0.0, 0.0, 1.98870157e-8, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.28242412061743066], uv: [5.0, 9.0], spectrum: [ 7.57292035957e-20, 7.52732853788e-19, 7.97589038632e-19, 6.46994361399e-19, 1.25684377956e-19, 0.0, 0.0, 0.0, 0.0, 3.02751471778e-19, 3.43912812892e-19, 3.959604922e-19, 1.07197340187e-5, 0.000241266822017, 0.000687017135726, 0.00133431730834, 0.00216544913264, 0.00315329058162, 0.00426148328263, 0.00544253416929, 0.00665093925688, 0.00784258176773, 0.00897972796072, 0.0100272998072, 0.0109495752545, 0.0117130871756, 0.0122774734626, 0.0126064672917, 0.0126689409387, 0.0124483462418, 0.011947142067, 0.0111854112849, 0.0101872768478, 0.00899557179493, 0.00765465714475, 0.00622610003527, 0.00477475247047, 0.00337709904641, 0.00211562615352, 0.00107271225829, 0.000338602579869, 0.0, 0.0, 9.12999645599e-19, 0.0, 0.0, 0.0, 0.0, 1.38891275596e-19, 1.62481575619e-19, 3.41771366047e-19, 2.57024136795e-19, 2.50369411189e-19, 0.0, 5.55890333807e-19, 0.0, 0.0, 9.96045949746e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.07380144796e-19, 8.17901585076e-20, 1.57700536083e-19, 1.78249812563e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 4.92974990947e-8, 1.97811105507e-8, 1.43130866538e-7, 2.02299362634e-7, 3.32008864541e-7, 3.67843259317e-7, 1.94264526291e-7, 2.73354161192e-7, 3.44352074641e-7 ] }, spectrum_data_point_t { xystar: [0.0, 0.28242412061743066], uv: [6.0, 9.0], spectrum: [ 6.61802184708e-7, 5.04394836815e-7, 1.97825146718e-7, 2.04709059976e-19, 0.0, 1.31747845215e-19, 5.84153580382e-20, 2.63684222702e-20, 0.0, 0.0, 1.66740417461e-19, 0.0, 6.24380370148e-20, 9.72825240113e-5, 0.000368960775949, 0.000804378840019, 0.00139467084639, 0.00212189887813, 0.00296314078482, 0.00388408305091, 0.00485081788101, 0.00582947634265, 0.00679183106046, 0.00771297677575, 0.00856579473681, 0.00932556115362, 0.00996306836228, 0.0104491902937, 0.010761027937, 0.010877430418, 0.0107947060676, 0.0105144387864, 0.0100441697627, 0.00940098717996, 0.00860399784966, 0.00768053586801, 0.00666266070611, 0.00558732781591, 0.00449459632889, 0.00342712866052, 0.00243157161341, 0.00155327329938, 0.000835892703292, 0.000318100346174, 3.21223634966e-5, 0.0, 1.12025785602e-19, 3.11973652159e-19, 3.70677934067e-21, 0.0, 0.0, 0.0, 0.0, 0.0, 1.92853228667e-20, 0.0, 1.52285321399e-19, 1.13246756002e-19, 4.93733266858e-8, 2.9996040988e-8, 1.00945172233e-8, 4.48220966647e-19, 1.1994679198e-7, 3.43658449481e-7, 6.19571013753e-7, 5.21767412854e-7, 7.19963942128e-7, 5.58733038417e-7, 7.76789618379e-7, 1.08680622914e-6, 1.1854628502e-6, 8.62655066776e-7, 9.13978055399e-7, 1.24120563238e-6, 1.0036158403e-6, 7.25423478913e-7, 8.21699323118e-7, 6.02373072106e-7, 4.3804428522e-7, 5.13608489352e-7, 2.70489580131e-7 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.28242412061743066], uv: [6.999999999999999, 9.0], spectrum: [ 0.0, 1.92083412172e-7, 1.16904795978e-19, 0.0, 0.0, 1.86548263929e-19, 0.0, 1.10840212376e-19, 0.0, 4.34548951496e-20, 1.21355802834e-19, 1.63899055637e-20, 0.0, 0.0, 9.86781430993e-20, 8.17420396487e-5, 0.000375187486363, 0.000861530992309, 0.00151436064773, 0.00229570571201, 0.003167027381, 0.00408992367622, 0.00503341585342, 0.00596603994654, 0.006862672521, 0.00769753742515, 0.00844292109482, 0.00907278018588, 0.00956264619838, 0.00989276776823, 0.0100549896741, 0.0100443006402, 0.0098623185847, 0.00951801126762, 0.00902368250213, 0.00839529809601, 0.0076528301214, 0.00682085434301, 0.00592686236388, 0.0050015328825, 0.00407719705132, 0.00318600033, 0.00235978948548, 0.00162605166628, 0.00101111191809, 0.000533865495508, 0.000204017858382, 2.64017136096e-5, 0.0, 3.55357225353e-20, 0.0, 1.74393975263e-19, 6.13011692302e-20, 0.0, 1.13297607006e-19, 1.56171251367e-19, 3.76401262564e-19, 0.0, 1.47133706754e-19, 2.98408802344e-19, 1.47477843245e-19, 2.62978663448e-19, 5.37678322758e-19, 3.5757798749e-19, 3.64146290475e-19, 2.10774353314e-8, 5.29199431901e-19, 3.30887055813e-19, 4.50749083595e-19, 1.20279392449e-7, 4.09135916891e-19, 1.77867097494e-7, 4.46630189094e-7, 2.06868628067e-7, 3.01304981596e-7, 5.63581992211e-7, 6.13185061189e-7, 6.1752419189e-7, 7.90241956453e-7, 8.5779437675e-7, 1.07451267222e-6 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.28242412061743066], uv: [8.0, 9.0], spectrum: [ 2.31373773206e-19, 2.66034702223e-20, 1.23690456243e-20, 6.12492627809e-20, 8.50070507806e-20, 1.13277060005e-19, 4.43637521591e-19, 0.0, 1.26152539765e-18, 2.27943504313e-19, 0.0, 4.7545973389e-19, 1.05018533903e-20, 0.0, 0.0, 4.63799339497e-20, 4.46258545057e-19, 1.58757139277e-19, 0.0, 6.28210262341e-6, 0.000435425900031, 0.00117048473427, 0.00211522586906, 0.00318936658476, 0.00432978507354, 0.00548081971868, 0.00659071361738, 0.0076104493537, 0.00849686555039, 0.00921423312209, 0.00974130214451, 0.0100659830518, 0.0101866339112, 0.0101072343857, 0.00983383232542, 0.00938257076723, 0.00877472861586, 0.00803644177781, 0.00719442024366, 0.00628093862922, 0.00533077602033, 0.00438267327784, 0.00346954276415, 0.0026200001667, 0.00186498119347, 0.00122502048207, 0.000714338096734, 0.000339775721961, 0.000103327523389, 0.0, 6.74941725571e-20, 8.11994368614e-20, 1.18480520166e-19, 0.0, 0.0, 0.0, 0.0, 1.058220135e-19, 0.0, 3.49256066233e-20, 0.0, 2.16949958168e-20, 1.84696076612e-20, 6.52788491424e-20, 0.0, 3.32376845878e-7, 4.02241202854e-20, 3.14300694718e-7, 4.18608498743e-7, 0.0, 5.61781809452e-7, 1.97118008289e-7, 0.0, 1.07065410249e-6, 1.10072072249e-6, 2.34758903268e-7, 5.93297878948e-7, 6.46293368442e-7, 7.103818921e-7, 9.49057756177e-7, 1.08387246588e-6 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.33890894474091676], uv: [3.0, 10.0], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.10183188095e-19, 0.0, 6.22878967624e-18, 0.0, 2.00214405627e-17, 2.45445828631e-17, 3.10288828689e-18, 0.0, 0.0, 0.0, 1.12193710768e-17, 1.31700199555e-18, 5.77682426923e-18, 0.00262253007048, 0.00941019242549, 0.0180367507612, 0.0266018315085, 0.0337382268519, 0.0384038300368, 0.0398322782868, 0.037513974516, 0.0314746549639, 0.0225172017541, 0.0123904818394, 0.00375536797553, 1.0115913883e-17, 0.0, 0.0, 0.0, 0.0, 3.17397014961e-17, 1.86758059803e-17, 7.82615951541e-18, 6.85444090872e-17, 1.31489674273e-17, 1.21334795842e-17, 0.0, 3.85456080126e-17, 1.0974458023e-16, 8.25675454905e-19, 0.0, 0.0, 0.0, 4.2195969464e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.02181226917e-18, 2.73609295482e-18, 0.0, 3.75341702877e-19, 2.36618906657e-18, 1.13537378421e-17, 4.34387619627e-18, 4.47297747457e-18, 4.60108792392e-18, 8.59598487671e-18, 8.2335530463e-18, 1.04012657485e-18, 1.8120935944e-18, 3.89843803535e-18, 1.01677589344e-17, 5.61735790949e-18, 2.39972500764e-18, 4.23796082009e-18, 5.84601175326e-18, 2.30392644615e-18, 8.37371893909e-18, 8.46429436279e-18 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.33890894474091676], uv: [3.9999999999999996, 10.0], spectrum: [ 0.0, 0.0, 3.64421863289e-19, 1.5768013001e-18, 0.0, 3.89771418036e-19, 3.82470574833e-19, 0.0, 0.0, 0.0, 2.39068910148e-18, 4.65822589427e-19, 0.0, 2.56490204354e-18, 0.0, 7.55391667828e-19, 7.92993153796e-5, 0.00132833222928, 0.0035085800684, 0.00630580197011, 0.00942252857371, 0.012587067184, 0.01557084108, 0.0181871202621, 0.0202729729598, 0.0216735231307, 0.0222404122576, 0.0218654759367, 0.0205106620591, 0.0182370048605, 0.0152160051788, 0.0117092907724, 0.00803489253651, 0.00457015046398, 0.00174641090244, 4.72299288342e-5, 2.07049116066e-18, 0.0, 0.0, 0.0, 4.66152984291e-18, 0.0, 0.0, 2.11326176984e-18, 6.71206928174e-18, 2.50974857456e-18, 6.53738361899e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.07448841641e-19, 2.37207416489e-18, 5.79605911622e-19, 0.0, 9.34505169046e-19, 3.1597856761e-19, 6.18681265312e-19, 1.03311744307e-18, 1.45725593136e-18, 2.8405812309e-18, 2.33566914545e-18, 1.0075931809e-18, 1.61488308474e-18, 1.52449599038e-18, 8.97894975302e-19, 6.22891202293e-19, 2.05299908473e-18, 1.96048615229e-18, 2.25425598364e-18, 1.80905466007e-18, 2.80883021965e-18, 1.83996731967e-18, 2.8576774058e-18, 3.25775231221e-18, 2.41997313031e-18, 2.39456214189e-18, 2.69880913684e-18, 1.88676589001e-18 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.33890894474091676], uv: [5.0, 10.0], spectrum: [ 8.54200800672e-19, 4.7405807764e-19, 6.58905854975e-19, 0.0, 9.95394892463e-19, 4.92281795371e-19, 0.0, 4.94243113564e-19, 5.22311249785e-20, 0.0, 0.0, 0.0, 1.7501375963e-18, 1.69532453474e-18, 0.0, 6.19277927485e-5, 0.00066087425909, 0.00172476096042, 0.00316799709797, 0.00487646531872, 0.00673155423252, 0.00862954715367, 0.0104715574683, 0.0121798537796, 0.0136801805622, 0.0149013133731, 0.0157669380774, 0.0162094774248, 0.0161771545314, 0.0156531846534, 0.0146597088809, 0.0132480749759, 0.0114937718101, 0.00949413315908, 0.007359969543, 0.00522641183257, 0.00325034789156, 0.00159794878128, 0.000452717804744, 4.23039648486e-19, 0.0, 0.0, 2.67559407185e-19, 1.55215689656e-20, 0.0, 1.43727943682e-18, 1.34648832452e-18, 5.42156233234e-19, 0.0, 1.32815360831e-19, 0.0, 0.0, 1.4052557702e-18, 0.0, 0.0, 0.0, 3.38122472378e-20, 0.0, 0.0, 5.35569612127e-19, 5.5714886613e-19, 3.50950846698e-19, 0.0, 6.73844693076e-19, 7.93191941813e-19, 2.3683129416e-19, 0.0, 1.30253994965e-19, 0.0, 7.09834254458e-19, 1.27222481653e-18, 0.0, 0.0, 0.0, 5.27860344817e-19, 0.0, 5.44789528737e-8, 6.7455586337e-19, 4.940525233e-7, 5.61291881634e-8, 2.24420297752e-7 ] }, spectrum_data_point_t { xystar: [0.0, 0.33890894474091676], uv: [6.0, 10.0], spectrum: [ 7.07092679493e-19, 7.45260839054e-19, 5.07916211955e-19, 1.39767069705e-20, 2.54565896015e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 1.40225062424e-19, 0.0, 0.0, 8.39781976816e-19, 0.0, 3.12167343192e-19, 0.000270989836599, 0.00092002311246, 0.00189258944265, 0.00311076320993, 0.00448441976452, 0.00593897874236, 0.00740905055683, 0.00883462550218, 0.0101642915605, 0.0113443029013, 0.0123209865309, 0.0130399666995, 0.0134613713304, 0.0135565922827, 0.0133193902438, 0.0127631905995, 0.0119106732058, 0.0108008973017, 0.00947420322866, 0.00799638086706, 0.00643658937707, 0.00486658835213, 0.00337511539639, 0.00204863699656, 0.000985694279395, 0.000275575557673, 0.0, 8.32875048698e-20, 2.92849117256e-20, 7.37719684244e-19, 2.67673588013e-19, 2.68209590434e-20, 0.0, 0.0, 1.00070800105e-18, 2.63275824935e-19, 0.0, 4.10847826905e-19, 3.33986398028e-20, 2.5158626774e-19, 0.0, 8.13896677264e-21, 0.0, 0.0, 6.52929181465e-19, 8.24188394269e-19, 8.80644424712e-19, 4.46952023249e-19, 8.08219235543e-19, 5.02611119771e-19, 4.17928574007e-19, 0.0, 6.68972540215e-19, 6.47444304939e-19, 1.02118189983e-18, 7.32026153592e-7, 1.92406322534e-7, 4.78573615199e-19, 8.74832009928e-7, 2.11466891187e-7, 1.03909766804e-18, 1.84674576273e-7, 2.00002016713e-7, 1.40205128594e-18, 6.82087376686e-7 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.33890894474091676], uv: [6.999999999999999, 10.0], spectrum: [ 0.0, 0.0, 0.0, 4.48229998564e-20, 0.0, 1.46988330187e-19, 0.0, 0.0, 0.0, 7.1793064613e-19, 0.0, 0.0, 4.66323447543e-20, 1.36478489986e-18, 1.15650076291e-18, 2.20644485762e-19, 0.0, 0.0, 0.000241205142551, 0.000928416094249, 0.0019437484852, 0.00318339028338, 0.00455226082949, 0.00597853190677, 0.00738805963278, 0.0087272065947, 0.00993469771355, 0.0109533740172, 0.0117383374258, 0.0122469061049, 0.0124627239684, 0.0123887966016, 0.0120268867795, 0.0113988711276, 0.0105348687458, 0.00947057774119, 0.00824896163386, 0.00692104210966, 0.00555557648967, 0.00420364168517, 0.00293358324211, 0.00182755351608, 0.000925562922255, 0.000304854814874, 1.42593018648e-19, 7.07122753194e-19, 0.0, 0.0, 0.0, 6.41488694753e-19, 9.537859504e-19, 0.0, 0.0, 1.06270404557e-19, 0.0, 8.31690363523e-20, 2.4806442944e-19, 8.67507768151e-19, 2.83242974598e-19, 4.22016079989e-19, 0.0, 5.92740648049e-19, 1.05953974567e-18, 7.73756907064e-19, 1.30019972476e-18, 8.4111405583e-19, 1.27054942088e-18, 1.07942044097e-18, 1.12265072601e-18, 1.83446670137e-18, 7.86308812203e-7, 5.47186382558e-7, 1.54555422535e-18, 1.76983610722e-6, 1.61059921815e-6, 1.06311103203e-18, 1.84378810307e-6, 1.16369049393e-6, 7.7964998179e-9, 1.56342212404e-6, 5.64836426902e-7 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.33890894474091676], uv: [8.0, 10.0], spectrum: [ 4.54795448894e-18, 4.05613172572e-18, 1.76599797167e-17, 5.73050971753e-19, 6.88291149196e-19, 0.0, 0.0, 4.21117137951e-18, 0.0, 0.0, 1.40145252626e-17, 0.0, 4.31357694498e-18, 3.12568417858e-17, 8.42784101866e-19, 4.57534365956e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.59357581974e-17, 0.000966451398451, 0.00303382650674, 0.00565997155596, 0.00836767094471, 0.0108097051031, 0.0127848008516, 0.0141731704053, 0.0149066053916, 0.014966449472, 0.0143798754193, 0.0132145658759, 0.011571953371, 0.00958570395488, 0.00741162879427, 0.00522295681663, 0.00320503350913, 0.00154689356277, 0.000425090444686, 0.0, 7.98683409882e-18, 0.0, 0.0, 1.07509763987e-18, 5.94542792644e-18, 0.0, 1.25345488663e-17, 7.08773566664e-18, 8.4783253258e-18, 1.11143243148e-18, 0.0, 1.2187549346e-18, 6.59186575997e-19, 1.34425633436e-18, 1.33849349353e-18, 1.68850485519e-18, 3.05575695107e-18, 1.84835719544e-18, 1.05964892456e-18, 1.07799228883e-18, 1.65487384637e-18, 1.87819921196e-18, 3.31047481185e-18, 3.53579836876e-18, 3.72556853938e-18, 4.10221488321e-18, 4.38174695662e-18, 3.15753648725e-18, 4.73625792585e-18, 2.69499539836e-18, 3.87491103589e-19, 4.15140920517e-19, 8.92560968169e-20, 1.33337148412e-7, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.395393768864403], uv: [3.0, 11.0], spectrum: [ 1.09257509883e-16, 4.81642433672e-16, 2.86958382618e-16, 0.0, 0.0, 5.08822773798e-18, 1.21906079306e-17, 1.64463469709e-16, 3.32697772125e-17, 0.0, 0.0, 0.0, 2.07274335622e-16, 2.27895275643e-16, 0.0, 0.0, 2.20088402543e-16, 0.0, 0.0, 0.0, 5.9350642001e-17, 3.09535737057e-18, 0.0179615829772, 0.0437873594427, 0.064545914329, 0.0718821518494, 0.0617961871737, 0.0368302305873, 0.00874221859225, 1.68970462155e-16, 9.06189439253e-16, 0.0, 0.0, 0.0, 0.0, 1.05659083048e-15, 0.0, 0.0, 4.02748449369e-16, 0.0, 0.0, 5.26448992742e-16, 0.0, 1.93369511633e-16, 0.0, 9.58855916149e-16, 9.05892281937e-16, 1.82351096149e-16, 5.75174985315e-16, 0.0, 0.0, 1.2784221253e-16, 1.65546091859e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 9.39769974228e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 1.239056335e-17, 6.38299507721e-18, 0.0, 9.16347098997e-19, 1.36179624017e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.02969697325e-17, 1.2554405157e-17 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.395393768864403], uv: [3.9999999999999996, 11.0], spectrum: [ 0.0, 1.11933150362e-18, 0.0, 0.0, 0.0, 0.0, 5.34012333426e-19, 0.0, 0.0, 4.15702695384e-18, 0.0, 1.65258441434e-17, 9.68357512311e-18, 0.0, 0.0, 3.78711436039e-19, 5.47878978392e-18, 0.0, 8.07957272606e-18, 0.00206379606169, 0.00629951649421, 0.0116780401358, 0.0173306927649, 0.0225871556217, 0.0268930441435, 0.0297844249851, 0.0308565857834, 0.0298544594983, 0.0267550283348, 0.0218485937014, 0.0157668525926, 0.00940687834093, 0.00382627658408, 0.000234888614935, 5.44771073058e-18, 0.0, 0.0, 6.37913742661e-18, 1.0243374251e-17, 9.51702909189e-18, 0.0, 2.14720955539e-17, 2.25412999684e-17, 1.11611209012e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 4.90403175879e-18, 0.0, 1.31368340164e-17, 0.0, 2.04055390252e-18, 2.28859272905e-18, 7.43206505809e-18, 0.0, 1.4643908319e-18, 2.5526348031e-18, 1.45279903727e-18, 8.44821047427e-19, 6.89150125706e-19, 0.0, 1.88781055972e-19, 0.0, 2.41226356749e-19, 0.0, 1.00480919216e-18, 1.82633385178e-18, 1.01467373899e-18, 1.44804428099e-18, 3.46128775899e-18, 5.40158581683e-18, 1.72687683335e-18, 3.48223925237e-18, 1.80801333832e-18, 3.32389074202e-18, 8.13522206279e-19, 3.96436392418e-18, 0.0, 3.44766232334e-18 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.395393768864403], uv: [5.0, 11.0], spectrum: [ 2.92092847556e-18, 2.33171391612e-18, 0.0, 0.0, 1.79832861724e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0183779081e-18, 0.0, 3.42014616844e-18, 0.0, 7.22555359434e-19, 0.0, 1.22310081882e-18, 0.0, 0.000814426861543, 0.00269109895638, 0.00527793692024, 0.00825160418219, 0.011333179011, 0.014295573569, 0.0169404659571, 0.019088527838, 0.0205686839214, 0.0212383936378, 0.0210032906842, 0.0198508632315, 0.0178638065047, 0.0152012419632, 0.0120727297687, 0.00873509689335, 0.00549277880905, 0.00269093710335, 0.000717842671042, 0.0, 0.0, 2.80597530456e-18, 5.23127904473e-19, 0.0, 1.49337571699e-18, 0.0, 2.62183688851e-18, 2.17586367255e-18, 0.0, 0.0, 0.0, 1.72297262818e-18, 0.0, 7.90249769121e-19, 4.06358539891e-19, 1.80338595195e-18, 0.0, 1.40978714958e-18, 1.15571286057e-18, 8.52076463783e-20, 0.0, 3.20049836474e-19, 8.52084651253e-19, 3.79104056633e-19, 2.35937427845e-19, 7.57625057054e-19, 1.59516365301e-19, 1.15330432346e-18, 8.88128029202e-19, 6.6370816837e-19, 1.41679355031e-18, 1.76647505334e-18, 1.68873889186e-18, 1.89227452954e-18, 1.88793056031e-18, 1.96010284319e-18, 9.69998385563e-19, 1.51195381085e-19, 2.31932343683e-18, 2.01255028268e-18, 1.47862322356e-7, 1.32772414482e-18, 4.3181739651e-18 ] }, spectrum_data_point_t { xystar: [0.0, 0.395393768864403], uv: [6.0, 11.0], spectrum: [ 1.69417341016e-19, 1.52064615538e-18, 4.58485220328e-19, 3.21420042668e-19, 2.19331256233e-19, 1.46246863133e-19, 1.72702597469e-19, 5.39367386263e-19, 7.6283144781e-20, 0.0, 1.58185682857e-20, 4.07851688047e-19, 2.35256766751e-18, 0.0, 1.21143275761e-18, 1.0371116701e-18, 2.4144155246e-19, 0.0, 0.000134516504783, 0.00115233960685, 0.00280659710099, 0.0048701534114, 0.00714011578665, 0.00945614539795, 0.0116749820078, 0.0136729068322, 0.01532604901, 0.0165228051303, 0.017172667355, 0.0172239058363, 0.0166796489067, 0.0155830060386, 0.0140017171368, 0.0120291144533, 0.00978544796488, 0.00741519990991, 0.0050802544822, 0.00296644078672, 0.00127352744436, 0.000213812923643, 1.7471716129e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 2.31371115364e-19, 6.55545181469e-19, 5.38267060664e-19, 9.50658531625e-19, 0.0, 3.94788360428e-19, 3.12351109861e-19, 0.0, 3.92247940242e-19, 4.80012922916e-20, 8.82313723023e-19, 6.5004371025e-19, 1.54934030612e-19, 0.0, 1.10579060567e-18, 1.52039265105e-19, 8.36876025323e-19, 5.79766221322e-19, 5.11675871171e-19, 4.68514989512e-19, 0.0, 0.0, 9.03198727406e-19, 0.0, 4.97044413327e-19, 9.53388201323e-20, 9.55444185584e-8, 7.21744646467e-19, 1.16801164876e-7, 2.73232259582e-8, 0.0, 1.03202943675e-7, 1.67561990597e-7, 3.23471550501e-7, 0.0 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.395393768864403], uv: [6.999999999999999, 11.0], spectrum: [ 0.0, 3.20212887661e-19, 5.12427074349e-19, 0.0, 0.0, 0.0, 0.0, 1.66280801278e-19, 0.0, 3.87435299857e-18, 0.0, 0.0, 6.31310213024e-18, 0.0, 0.0, 0.0, 0.0, 3.00536596386e-18, 4.51338586286e-18, 0.0, 1.65889016729e-18, 0.00046552066355, 0.00188449268829, 0.00391437036935, 0.00627397159803, 0.00873387446399, 0.0110923196571, 0.0131608389518, 0.0147754701081, 0.015824455953, 0.0162643953515, 0.0160960803895, 0.0153469731751, 0.0140747120851, 0.0123658505069, 0.0103304436406, 0.00810380572284, 0.00584120420266, 0.00371310265504, 0.00190508545341, 0.000605329015392, 2.2400604973e-18, 7.40136092994e-19, 1.43630600416e-18, 1.96042678627e-18, 2.38168975948e-19, 2.35821603619e-18, 0.0, 0.0, 0.0, 6.06544434873e-20, 1.50660900227e-18, 4.27499715313e-19, 0.0, 4.30784291968e-19, 2.16708041448e-18, 6.25424725459e-19, 0.0, 9.42866430381e-19, 1.19176670244e-18, 1.71421688143e-18, 6.94815021938e-19, 1.83927211833e-18, 0.0, 1.48074270026e-18, 1.92254591163e-18, 1.60140499952e-18, 1.61166218776e-18, 1.52379298145e-18, 1.13580084567e-18, 3.90852765599e-19, 2.69189607791e-18, 1.89401860962e-18, 1.42642995296e-18, 6.13809783872e-8, 2.07523072077e-18, 1.72964127829e-18, 1.48442524006e-18, 2.24040214549e-19, 2.47545378835e-18, 2.76250179057e-7 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.45187859298788907], uv: [3.9999999999999996, 12.0], spectrum: [ 5.10322970972e-17, 3.1984680414e-17, 1.91897358802e-17, 0.0, 0.0, 5.52073557066e-19, 2.7450854699e-18, 0.0, 1.04299935282e-17, 0.0, 0.0, 0.0, 9.16460814286e-17, 1.51102358421e-18, 4.6606734707e-17, 3.0214816869e-17, 4.18892236822e-17, 0.0, 0.0, 2.13301176864e-17, 0.0, 0.00252483673143, 0.0118795873382, 0.0238817562668, 0.0352896620212, 0.043603741295, 0.0469031352686, 0.0441111771058, 0.0354091975219, 0.0226228177266, 0.00923491481519, 2.61196274884e-17, 0.0, 0.0, 0.0, 4.38109436461e-17, 0.0, 0.0, 5.54953583576e-17, 0.0, 9.62209437669e-17, 0.0, 8.22890849435e-18, 6.57706458566e-17, 4.43511251827e-18, 0.0, 2.07220838685e-16, 0.0, 0.0, 0.0, 3.54345406497e-17, 0.0, 0.0, 0.0, 0.0, 1.63760748034e-17, 1.87411566267e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.58910925116e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.45187859298788907], uv: [5.0, 12.0], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.44263588095e-17, 0.0, 1.77618821449e-17, 3.80908320661e-19, 0.0, 2.20379367851e-17, 0.0, 7.20337814011e-18, 0.0, 0.0, 0.0, 0.000839749208003, 0.00433354066664, 0.00930758170203, 0.0148398802156, 0.0201793740441, 0.0247007954971, 0.0278550431796, 0.0292079418147, 0.0285127028067, 0.0258024849227, 0.0214240512226, 0.0159591917684, 0.0101409022829, 0.00484315492935, 0.00108071967134, 1.03104584515e-17, 2.06295782595e-18, 7.69259049884e-18, 3.89110874029e-18, 0.0, 0.0, 2.3273966035e-17, 7.35550816628e-18, 2.86305568038e-18, 0.0, 0.0, 1.32062980688e-17, 1.15161925698e-18, 0.0, 1.76259135678e-18, 7.49525654088e-18, 8.66079097807e-19, 0.0, 0.0, 0.0, 3.6435991575e-18, 0.0, 1.09602476123e-18, 5.24498323065e-19, 9.41977752923e-19, 0.0, 2.38431961276e-18, 1.5365994542e-18, 0.0, 1.77164491291e-18, 0.0, 0.0, 6.8905346627e-20, 2.77843773007e-18, 1.34067495594e-21, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.0, 0.45187859298788907], uv: [6.0, 12.0], spectrum: [ 7.79420686093e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.68025853611e-19, 7.13831323523e-18, 1.16525706401e-17, 4.53180319521e-18, 0.0, 0.0, 0.0, 0.0, 7.46660056019e-18, 5.61188616576e-18, 0.0, 3.41019890177e-18, 0.0, 0.00101647261779, 0.00376169787902, 0.00749827415762, 0.011629608743, 0.0156626925376, 0.0191662503232, 0.0217579959148, 0.0231437971625, 0.0231801100955, 0.0219042040229, 0.0194794239739, 0.0161476187929, 0.0122432891689, 0.0081642289982, 0.00439253257591, 0.0014696600049, 1.8855009376e-18, 2.7578266753e-18, 0.0, 6.40655431208e-18, 0.0, 4.89518270812e-18, 6.04047466445e-18, 6.61583022459e-18, 0.0, 5.17446932132e-19, 0.0, 3.92074657769e-19, 3.17162627728e-19, 1.65630662746e-18, 0.0, 5.7363840438e-20, 3.34625311211e-18, 4.33676537014e-20, 0.0, 0.0, 0.0, 0.0, 3.02743416246e-19, 0.0, 0.0, 8.25390715111e-19, 3.31068583769e-19, 0.0, 2.63469399605e-19, 0.0, 3.55531626399e-19, 6.49407294855e-19, 1.10525238382e-18, 0.0, 0.0, 2.31388480138e-19, 8.53005580957e-19, 0.0, 5.61371085793e-19, 0.0, 7.37269117283e-7, 4.78926105395e-20, 8.65773551212e-19, 4.83468217328e-8 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.45187859298788907], uv: [6.999999999999999, 12.0], spectrum: [ 3.7958994134e-17, 0.0, 7.2630274982e-17, 1.94925070461e-18, 0.0, 9.68649352526e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.55909024599e-18, 6.6269167129e-17, 2.39739407069e-17, 6.04932177904e-17, 0.0, 1.09027955613e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 7.50769328961e-18, 0.0, 4.38175547014e-18, 0.00445918711721, 0.0116867338701, 0.0189096373051, 0.0242917448172, 0.0270258549444, 0.0269164158114, 0.0241343122798, 0.0192118370937, 0.0130236312018, 0.00674793245384, 0.00184392689362, 0.0, 1.91021660042e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.36634970812e-17, 3.30036116925e-17, 5.17844649204e-17, 4.9153623329e-17, 1.74795681383e-17, 1.28452118759e-17, 1.23611223692e-17, 0.0, 2.17853035095e-18, 0.0, 0.0, 0.0, 1.00524584904e-17, 2.53690721039e-17, 6.60832304822e-18, 1.58076654468e-17, 2.89521272058e-17, 0.0, 0.0, 9.30896479455e-18, 1.13613317492e-17, 1.51272881431e-18, 8.37949976152e-19, 7.06947046093e-18, 2.94859994909e-18, 5.00455310754e-18, 2.77909824869e-18, 0.0, 0.0, 8.05634211957e-18, 9.67656612816e-18, 4.4270529574e-18, 1.06617730828e-17, 1.20765722455e-17, 7.37935355514e-7 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.5083634171113752], uv: [5.0, 13.0], spectrum: [ 2.29702870014e-17, 3.55320953493e-17, 2.45080012414e-17, 0.0, 2.18682930421e-18, 0.0, 1.67525845147e-18, 3.58576205696e-18, 0.0, 1.44199618562e-16, 0.0, 7.94324710254e-17, 9.27992243121e-17, 5.30470243548e-17, 0.0, 0.0, 0.0, 0.0, 3.07116532335e-17, 4.55861893302e-17, 0.0, 0.0, 8.10764913042e-17, 0.00522146233622, 0.0169092359245, 0.0301108996308, 0.0408532671093, 0.0460865281035, 0.0441569949205, 0.0354247623204, 0.0223496658039, 0.00890932080935, 0.0, 0.0, 0.0, 1.52099701664e-17, 6.52015259592e-18, 0.0, 3.05216737775e-17, 4.94566049892e-17, 0.0, 0.0, 8.58733946611e-17, 0.0, 0.0, 0.0, 1.86897584401e-17, 0.0, 7.05265306253e-17, 0.0, 8.33336025788e-18, 1.12087957608e-16, 1.89176398544e-17, 2.06702315231e-17, 4.65906462632e-17, 0.0, 3.60073953035e-17, 8.72187984542e-18, 6.9710400256e-18, 4.81459031089e-18, 0.0, 0.0, 5.26319179545e-18, 0.0, 0.0, 1.03141402209e-17, 0.0, 0.0, 0.0, 0.0, 1.04234387508e-17, 1.34459834792e-17, 1.18885690388e-17, 0.0, 0.0, 4.40695822989e-18, 1.1207638957e-17, 1.72388860734e-17, 1.1130864521e-17, 1.27644491506e-17, 1.00120341893e-17 ] }, spectrum_data_point_t { xystar: [0.0, 0.5083634171113752], uv: [6.0, 13.0], spectrum: [ 0.0, 6.65246068751e-17, 1.10391230502e-16, 1.31295745213e-17, 4.43770580676e-18, 1.39674343078e-17, 5.11932721054e-18, 2.73715249786e-17, 1.54559946027e-17, 4.75678167839e-17, 5.61720819121e-17, 0.0, 0.0, 0.0, 5.37186817957e-18, 0.0, 0.0, 1.32590768238e-16, 8.34550416772e-17, 0.0, 5.3241113977e-17, 5.35860970029e-17, 2.81223517282e-17, 1.44983149141e-18, 0.000823075389301, 0.00943263596448, 0.0210265313527, 0.0314666711465, 0.0376453141446, 0.0381046284009, 0.0331557426235, 0.024294707718, 0.0137564470958, 0.0044595663871, 2.3481998511e-17, 0.0, 3.3454817154e-18, 1.96931117254e-18, 0.0, 0.0, 3.89114544572e-17, 0.0, 8.26657997198e-17, 0.0, 6.80120453366e-17, 0.0, 0.0, 0.0, 0.0, 3.27682143299e-17, 0.0, 0.0, 3.29232181995e-17, 4.85073244613e-17, 0.0, 1.89181923757e-17, 1.37686296634e-17, 3.57540377825e-17, 1.2925834341e-17, 3.10551809457e-18, 2.50155389724e-18, 0.0, 7.22474978301e-19, 1.369944539e-17, 0.0, 0.0, 6.66321719372e-18, 9.05921427072e-18, 1.45769800638e-19, 0.0, 1.08879187166e-17, 5.3844328188e-18, 7.68845376826e-18, 9.88221941605e-18, 6.41882379988e-18, 4.1640139687e-18, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2730601043599837, -0.22593929649394454], uv: [0.9999980468750005, 0.0], spectrum: [ 0.025458568698, 0.0254522907037, 0.0254350759272, 0.0253976554885, 0.0253239131175, 0.025181959663, 0.024930444763, 0.0244766041004, 0.0236680861767, 0.0222722028285, 0.0200261960681, 0.0168335355523, 0.0128503726222, 0.00847601270268, 0.00430773971387, 0.0011501996693, 0.0, 3.49224861096e-19, 2.83282269904e-18, 3.64589712698e-18, 1.05629785327e-18, 7.33857384866e-18, 0.0, 3.20616251913e-17, 0.0, 1.78543677294e-18, 8.34074991691e-17, 1.38565681533e-18, 0.0, 0.0, 2.73379727277e-17, 0.0, 1.35763670728e-17, 4.06451502046e-17, 0.0, 1.09428735223e-17, 0.0, 0.0, 4.54273212192e-18, 0.0, 4.59320784997e-18, 3.24804040306e-18, 0.0, 1.45719641965e-18, 1.18925375581e-17, 3.79347085933e-18, 8.10744949464e-18, 9.4911197767e-18, 9.58634869442e-18, 9.80058091678e-18, 9.29623971271e-18, 4.49946261274e-18, 0.000222156168043, 0.000780511862472, 0.00142594745134, 0.00203304525644, 0.00254857679431, 0.00295989797031, 0.00327842431908, 0.00351965880673, 0.00369931535729, 0.00382939945648, 0.00392209274448, 0.00398808765363, 0.00403499242544, 0.00406825696207, 0.00409195119678, 0.00410832738755, 0.00411979507489, 0.00412790912725, 0.00413342988336, 0.0041371254824, 0.0041396717457, 0.00414136171983, 0.00414247669735, 0.0041434630639, 0.00414385912547, 0.00414403175741, 0.00414404302662, 0.0041439979421, 0.00414394822128 ] }, spectrum_data_point_t { xystar: [-0.29433958464917753, -0.1694544723704584], uv: [0.6103496093750023, 1.0], spectrum: [ 0.00280771860646, 0.00280906543723, 0.00281342940028, 0.00282571779213, 0.00284916782655, 0.00289029215394, 0.00296006720587, 0.00308489676504, 0.00330181647043, 0.00366577522555, 0.00424577784394, 0.00507815252025, 0.00614802103695, 0.00740142066712, 0.00873800288285, 0.0100213719543, 0.0110836680854, 0.011738379582, 0.0118360730956, 0.0113001874502, 0.0101379189077, 0.00843758082973, 0.00635027929827, 0.00411031486384, 0.00202172151668, 0.000474489247833, 0.0, 1.76972626909e-18, 0.0, 0.0, 9.64072922949e-19, 6.95599799609e-18, 1.02606950418e-17, 3.06295173928e-18, 5.66153456149e-19, 0.0, 0.0, 5.99261203803e-18, 0.0, 1.09222234244e-17, 0.0, 8.27231846708e-18, 6.37859817249e-18, 0.0, 0.0, 6.78342993486e-18, 0.0, 1.07361500509e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.03079075276e-18, 0.0, 0.0, 0.0, 1.84976792439e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.97854308871e-20, 0.0, 1.32184899746e-19, 0.0, 0.0, 3.87757938731e-7, 0.0, 0.0, 0.0, 3.17456176347e-19, 1.77622189228e-7 ] }, spectrum_data_point_t { xystar: [-0.29625953775797703, -0.11296964824697227], uv: [0.5751933593750014, 2.0], spectrum: [ 8.40054425038e-18, 0.0, 7.99007468102e-19, 1.93292552458e-18, 2.59882484002e-17, 3.48110434946e-19, 0.0, 4.6688644902e-18, 2.64328639104e-19, 2.55809861366e-19, 0.0, 0.0, 1.96249466248e-17, 0.000553304367836, 0.00318802839749, 0.00728062875434, 0.0120562361625, 0.0166262431958, 0.0201495723771, 0.0219829551186, 0.02179739411, 0.0196038845158, 0.0156787350543, 0.0106622256279, 0.0054653805916, 0.00132773345356, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.86091636623e-17, 0.0, 3.69986205597e-17, 0.0, 0.0, 8.77326349187e-17, 0.0, 3.70282900141e-17, 7.70760446269e-17, 0.0, 0.0, 0.0, 0.0, 8.31657390006e-17, 0.0, 0.0, 1.93563147604e-17, 4.30166712105e-17, 0.0, 1.43699504269e-17, 0.0, 0.0, 0.0, 0.0, 8.21058955597e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 3.1732409973e-18, 0.0, 1.25780440695e-18, 0.0, 0.0, 0.0, 0.0, 5.72087471549e-19, 5.48920920802e-19, 0.0, 3.2298674589e-18, 6.75856130536e-18, 1.02918517601e-17, 1.31663110293e-17, 1.26992544264e-17, 1.42465572408e-17, 1.07196472306e-17, 1.25547469676e-17 ] }, spectrum_data_point_t { xystar: [-0.29204630732477826, -0.056484824123486134], uv: [0.6523417968750005, 3.0], spectrum: [ 1.77581253401e-16, 2.66794698842e-17, 0.0, 8.0070909039e-18, 0.0, 4.15316822345e-18, 1.01715765886e-18, 3.46144629307e-18, 0.0, 0.0, 0.0, 0.0, 3.18373955135e-17, 2.0986803773e-17, 0.0, 5.36199523164e-18, 0.00431971649893, 0.0120226971156, 0.0205647438168, 0.0275905007363, 0.0314816634358, 0.0314572903172, 0.0274419820826, 0.0203124041528, 0.0116175863781, 0.00368153420424, 8.05545806103e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.68426495543e-17, 1.24755207593e-16, 2.75024044355e-16, 1.11389614129e-16, 0.0, 0.0, 3.07627251388e-17, 0.0, 4.8071840656e-17, 3.11009065187e-16, 0.0, 0.0, 0.0, 1.65160514468e-16, 0.0, 0.0, 0.0, 0.0, 6.98479287935e-17, 4.35951071902e-17, 0.0, 0.0, 3.87842906573e-17, 2.69076180977e-17, 0.0, 0.0, 3.4736566476e-17, 0.0, 0.0, 0.0, 0.0, 5.90283252621e-18, 9.80608434088e-19, 7.74136614574e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.80750683432e-19, 2.9339936105e-18, 2.65848857561e-18, 6.0973518583e-18, 3.50403360065e-18, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2850598112899802, 0.0], uv: [0.7802714843750014, 4.0], spectrum: [ 2.94036137591e-16, 9.84635495295e-18, 2.77266848334e-17, 0.0, 3.00249380115e-18, 5.57906428508e-17, 1.39881372722e-17, 1.36576867161e-17, 0.0, 0.0, 2.03810921274e-17, 0.0, 0.0, 0.0, 4.45972984724e-17, 1.93267533048e-17, 6.86338611475e-17, 0.00188553214198, 0.0130308296741, 0.0267427664856, 0.0378640943973, 0.0431697516961, 0.0411320435819, 0.0325052716484, 0.0196231323092, 0.00658553750943, 0.0, 0.0, 0.0, 7.04948464729e-17, 0.0, 0.0, 2.7023526489e-16, 0.0, 2.15866396723e-16, 8.15619001767e-17, 1.308148876e-16, 4.07930168068e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.98115894452e-16, 0.0, 3.37442800726e-16, 1.24168563895e-16, 3.46849948298e-16, 2.53499045085e-16, 9.92188499734e-17, 3.60879583755e-16, 2.00922877964e-16, 2.04961568081e-16, 1.72235508061e-17, 1.40334218578e-16, 3.52838684191e-18, 2.95594862186e-18, 7.85115428164e-18, 2.05662348852e-18, 1.9660527093e-17, 2.3777270106e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 9.32019306398e-18, 3.9539506093e-18, 3.36054236435e-18, 0.0, 1.54445722694e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 5.19192028345e-18, 1.09436656785e-18, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2743400730991833, 0.056484824123486106], uv: [0.9765605468750005, 4.999999999999999], spectrum: [ 1.21364952901e-16, 2.40858997747e-16, 0.0, 1.52652901729e-18, 0.0, 1.54527033622e-16, 1.88240116904e-17, 1.63222808603e-17, 1.9766318292e-18, 5.78102942309e-17, 0.0, 3.49476065621e-17, 1.41050169496e-17, 4.52696453405e-17, 9.10649253577e-17, 0.0, 5.65097797859e-17, 0.0, 0.00448508610633, 0.018972690031, 0.0350262814425, 0.0466488334421, 0.050128390313, 0.0447633454348, 0.031892616638, 0.0150262220377, 0.000629810286052, 1.45161516834e-15, 0.0, 0.0, 0.0, 1.55628882578e-17, 0.0, 0.0, 2.31871675062e-16, 0.0, 1.39624732435e-16, 5.3699567593e-16, 5.659734614e-17, 0.0, 0.0, 0.0, 0.0, 5.32773873869e-16, 0.0, 0.0, 1.68996001376e-17, 5.8280122873e-16, 1.55142006009e-16, 6.5703318439e-16, 8.22097287972e-17, 6.24066366442e-16, 1.27681801549e-16, 9.61594479649e-17, 4.16354867674e-16, 3.95505255287e-16, 1.37891398561e-16, 1.38462465031e-16, 1.23624475486e-16, 8.9864392715e-17, 4.89174005783e-17, 5.28698146418e-17, 5.8322103397e-17, 4.7432791578e-17, 7.27766817318e-17, 0.0, 1.44963038571e-17, 1.21318446683e-17, 1.9911787308e-17, 1.49610085674e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.18913526271e-18, 9.20738760271e-18, 1.37887779093e-18 ] }, spectrum_data_point_t { xystar: [-0.2730599976959221, 0.06321457169449544], uv: [1.0, 5.1191425781249995], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 1.93242012001e-17, 5.66281194976e-18, 0.0, 5.30690629092e-19, 0.0, 1.60621306643e-17, 0.0, 8.86779604881e-17, 1.88646488152e-17, 0.0, 0.0, 6.39905619239e-18, 2.01736753549e-17, 0.00325112740521, 0.0178470919398, 0.0346775821044, 0.0472163214706, 0.0513800755825, 0.0462909047611, 0.0332454237249, 0.0158465902763, 0.000818687659331, 1.48188268012e-16, 7.89562668533e-17, 0.0, 0.0, 2.95293921991e-16, 1.19657941454e-16, 0.0, 0.0, 0.0, 3.15756888825e-16, 0.0, 0.0, 0.0, 9.4766789851e-16, 0.0, 7.8270663917e-16, 6.10384428787e-16, 0.0, 1.20301443884e-16, 0.0, 0.0, 0.0, 0.0, 4.47268365457e-17, 1.13547153462e-16, 5.27341548924e-17, 0.0, 2.67727232785e-17, 7.35922219196e-18, 7.20838895175e-17, 0.0, 0.0, 1.89331168714e-17, 5.08470702839e-17, 1.98445986793e-17, 4.79920778439e-17, 8.55750966633e-18, 3.14903932277e-17, 3.6868408198e-18, 0.0, 0.0, 0.0, 2.33358870515e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.15154283953e-17 ] }, spectrum_data_point_t { xystar: [-0.26175371383038687, 0.11296964824697227], uv: [1.2070292968750014, 6.0], spectrum: [ 0.0, 0.0, 0.0, 7.3898005543e-18, 0.0, 0.0, 3.44730118886e-18, 7.39136644662e-18, 1.3866307553e-16, 1.25587868214e-16, 1.37730567829e-16, 5.79762030103e-17, 0.0, 5.55955784696e-17, 0.0, 9.72501691515e-17, 0.0, 3.08368437077e-17, 1.53114942159e-16, 0.00926192045301, 0.0283524688373, 0.0465230329428, 0.0564626987463, 0.05537299185, 0.0435026496726, 0.0242315190807, 0.00501833116112, 3.06451519274e-17, 0.0, 1.9174775142e-16, 1.12165668991e-16, 0.0, 4.28416190915e-16, 0.0, 4.7169481508e-16, 0.0, 3.19328141396e-16, 2.53337984749e-16, 4.12789303943e-16, 0.0, 0.0, 3.38508990617e-16, 0.0, 0.0, 8.35470086493e-16, 0.0, 6.72661873414e-16, 0.0, 0.0, 0.0, 3.90611991936e-16, 0.0, 3.83995021112e-16, 3.69350336521e-16, 1.45107125077e-16, 0.0, 0.0, 2.69244257834e-17, 6.63958692767e-17, 4.85912983659e-18, 5.23029253722e-17, 0.0, 1.166195788e-17, 1.97222726093e-17, 0.0, 7.22033808563e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.06830712825e-17, 2.67696219838e-17, 7.74319509267e-18, 5.39398863526e-18, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.24756739363759098, 0.16945447237045844], uv: [1.4667949218749996, 7.0], spectrum: [ 9.46423366347e-17, 0.0, 0.0, 0.0, 8.16378990522e-18, 2.00856791204e-18, 2.79025772457e-16, 0.0, 2.69932179232e-17, 9.97460570545e-18, 0.0, 1.63012658611e-16, 1.53789299641e-17, 0.0, 6.96301966375e-17, 0.0, 0.0, 0.0, 1.02316899201e-16, 0.0, 0.018771300452, 0.0421575895222, 0.0589637924345, 0.0638040533495, 0.055176301103, 0.03548305091, 0.0119984824187, 0.0, 2.71533578726e-17, 1.42854319535e-15, 0.0, 1.26294233342e-15, 1.78925571147e-16, 7.90531646738e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.66216607832e-15, 2.73271332788e-16, 1.37362023519e-15, 0.0, 1.76664694973e-16, 0.0, 0.0, 1.11831885389e-15, 0.0, 1.72449657079e-16, 1.54865145445e-16, 0.0, 0.0, 2.46723035875e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.94426571403e-17, 0.0, 0.0, 0.0, 0.0, 5.98547889343e-17, 5.5213102402e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.66000532753e-18 ] }, spectrum_data_point_t { xystar: [-0.23130112424359567, 0.22593929649394454], uv: [1.7646464843749987, 8.0], spectrum: [ 0.0, 0.0, 0.0, 5.7766226827e-19, 0.0, 2.05735188166e-17, 0.0, 0.0, 0.0, 1.51475621159e-16, 0.0, 0.0, 3.87187969921e-17, 0.0, 5.91875358968e-18, 1.89152318308e-16, 1.67280072672e-16, 7.18199641588e-17, 2.0556244397e-16, 0.0, 0.00636533334136, 0.0303921698653, 0.0544368033805, 0.0683211349906, 0.0669270497601, 0.0499587691537, 0.0227800814372, 2.80935186836e-16, 0.0, 3.43202138067e-16, 3.75055708872e-16, 6.52294740564e-16, 2.12968102832e-16, 1.3993946272e-16, 0.0, 0.0, 6.1302699193e-16, 0.0, 0.0, 0.0, 1.51350337969e-15, 2.43452855133e-16, 4.00162487331e-16, 0.0, 8.40448003637e-16, 0.0, 1.13707644254e-15, 2.89272609661e-16, 1.59126039235e-16, 0.0, 1.16767893974e-15, 4.22558346818e-17, 0.0, 0.0, 0.0, 3.10707114057e-16, 0.0, 0.0, 1.68326605247e-17, 0.0, 9.57678671557e-17, 2.77896981457e-18, 0.0, 0.0, 0.0, 0.0, 5.87315809708e-17, 0.0, 1.11787206613e-17, 4.80485763267e-17, 7.4016851366e-17, 9.42977449306e-17, 0.0, 0.0, 1.02797227588e-17, 6.72221015034e-18, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2184479981567377, 0.2662069083882738], uv: [1.9999999999999991, 8.712892578125], spectrum: [ 0.0, 0.0, 2.4614774312e-16, 1.48208774955e-17, 3.47634625238e-17, 6.28204367129e-17, 1.25854276666e-16, 0.0, 3.38207578091e-16, 0.0, 4.34798575082e-17, 0.0, 0.0, 7.36573974655e-17, 0.0, 6.92823913035e-17, 2.01881626825e-16, 1.49407722696e-16, 0.0, 6.37930032245e-17, 0.0, 0.0222148245648, 0.0493658327136, 0.0685730097922, 0.0724769778849, 0.0588780897662, 0.031398459148, 0.00310814642272, 1.29475692305e-15, 7.45516471629e-16, 3.71836506459e-16, 0.0, 0.0, 0.0, 0.0, 1.41671699528e-16, 2.28281371612e-16, 0.0, 0.0, 5.03581464361e-16, 0.0, 0.0, 5.65019568747e-16, 1.22161775617e-15, 4.76194061813e-16, 2.14930843579e-16, 0.0, 9.36346582503e-16, 2.17392007073e-16, 0.0, 0.0, 0.0, 1.9349585372e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.52689904524e-17, 0.0, 1.78123012597e-17, 0.0, 0.0, 0.0, 8.95634968285e-18, 8.49384392692e-18, 0.0, 0.0, 5.22015858384e-17, 8.83051505993e-17, 8.7936924452e-17, 5.28946618602e-17, 6.30650487366e-17, 8.45275547396e-17, 6.49183533893e-17, 4.15764967743e-17, 2.0931479236e-17, 1.00222632972e-17 ] }, spectrum_data_point_t { xystar: [-0.2132215658024008, 0.28242412061743066], uv: [2.0957011718749996, 9.0], spectrum: [ 1.45003753505e-16, 0.0, 0.0, 2.94762948228e-17, 0.0, 4.13015924695e-18, 0.0, 3.05754188446e-17, 2.34913373218e-16, 2.62079876099e-16, 3.77746674352e-16, 9.84109231799e-17, 0.0, 0.0, 0.0, 0.0, 4.29353347665e-17, 0.0, 0.0, 2.26336728862e-16, 2.09818867176e-16, 0.0163483970256, 0.0450455039511, 0.0684887150875, 0.0761275461676, 0.0639116947297, 0.0349177955991, 0.00359813724513, 6.51722048717e-16, 2.26829325135e-15, 0.0, 1.41546125026e-16, 0.0, 2.26400609763e-16, 0.0, 3.1818158734e-16, 1.10494105493e-16, 1.23517759837e-15, 0.0, 0.0, 5.24706965576e-16, 0.0, 5.62344201973e-16, 2.77288397786e-16, 0.0, 0.0, 9.5371381841e-16, 1.16184432997e-15, 0.0, 2.05139016913e-15, 0.0, 0.0, 1.40485180037e-15, 6.52350048446e-16, 0.0, 5.30141216643e-16, 2.42894472331e-16, 2.98893834318e-17, 0.0, 2.88594003531e-16, 1.26192663363e-16, 1.40988908747e-16, 1.75286325157e-16, 9.66517991928e-17, 1.31327559461e-16, 7.44634187829e-17, 6.0847824074e-17, 9.2752842199e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 1.90385791025e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.19268873394440664, 0.33890894474091676], uv: [2.471677734375, 10.0], spectrum: [ 1.88294649365e-16, 0.0, 4.32530248926e-16, 4.87645534882e-17, 9.94475971335e-18, 2.71290582087e-17, 2.09228664434e-16, 0.0, 1.23456160018e-16, 4.90167597368e-17, 3.71715789731e-16, 0.0, 0.0, 2.65486227089e-16, 0.0, 1.46556102031e-16, 9.36288167135e-17, 0.0, 0.0, 9.72776848234e-17, 5.26490718925e-17, 0.00108992252072, 0.0307013796994, 0.0628523911901, 0.0807890520478, 0.0760413697979, 0.0488186311697, 0.0127510013556, 2.58017896231e-16, 0.0, 0.0, 2.66409978841e-15, 0.0, 0.0, 1.77336168856e-15, 0.0, 0.0, 4.2465635743e-16, 0.0, 2.06867491976e-15, 0.0, 0.0, 2.60848859431e-15, 0.0, 0.0, 2.82753271716e-15, 0.0, 3.69847680302e-16, 2.86213835567e-16, 0.0, 9.49658970156e-16, 0.0, 2.77989429166e-16, 0.0, 9.08762696307e-16, 6.5231190274e-16, 3.89202650096e-16, 6.77196961758e-16, 1.41546435973e-16, 1.17371882176e-16, 2.3141219704e-16, 1.51946983497e-16, 1.4337808553e-16, 1.65472302249e-16, 1.1855435559e-16, 7.24897775444e-17, 0.0, 2.57886853636e-18, 6.99635242439e-18, 4.80436212021e-17, 1.26099468772e-16, 1.65765750845e-16, 1.56383072063e-17, 0.0, 0.0, 0.0, 6.92300201492e-18, 6.62440336256e-17, 3.94626350784e-17, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.16922264039241333, 0.395393768864403], uv: [2.901365234375, 11.0], spectrum: [ 0.0, 4.50147706485e-16, 9.73827943658e-17, 0.0, 0.0, 0.0, 8.25311707159e-17, 0.0, 0.0, 0.0, 7.43184267785e-16, 0.0, 8.98493132502e-16, 0.0, 6.80149404892e-16, 6.82216224251e-17, 6.4699181244e-16, 0.0, 2.85588830445e-16, 0.0, 1.03917007884e-16, 3.37482203252e-16, 0.00805623578478, 0.0441632315596, 0.0770019286002, 0.0873600151687, 0.067754824668, 0.0276168020359, 5.24604360716e-16, 0.0, 0.0, 8.15076392354e-17, 0.0, 8.71311069122e-18, 2.76197684488e-15, 0.0, 0.0, 0.0, 0.0, 1.11134746524e-15, 1.60837331105e-15, 0.0, 0.0, 6.11361137695e-16, 0.0, 8.44872578576e-16, 0.0, 0.0, 4.36657946866e-16, 0.0, 6.89863891225e-16, 7.5367198601e-16, 0.0, 1.21459396589e-15, 0.0, 0.0, 0.0, 0.0, 0.0, 1.98469083681e-16, 0.0, 0.0, 0.0, 0.0, 5.88953899449e-18, 0.0, 7.77710452546e-18, 0.0, 1.76521362725e-17, 0.0, 0.0, 0.0, 0.0, 9.06876297561e-18, 6.07751921827e-18, 4.70502463889e-18, 2.87241579507e-17, 2.47073879411e-17, 1.34477706586e-17, 3.37724961567e-17, 3.58917387095e-17 ] }, spectrum_data_point_t { xystar: [-0.16383599861755327, 0.40846702695709286], uv: [3.0, 11.231447265625], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 2.83259094598e-16, 0.0, 0.0, 0.0, 5.43414747194e-16, 0.0, 8.060038466e-16, 0.0, 3.90376170477e-16, 5.6322280581e-17, 0.0, 3.11262762276e-16, 6.91734764714e-16, 0.0, 0.0, 0.0, 1.87306638686e-16, 0.00237972722479, 0.0396489133272, 0.0763584088976, 0.0903601332827, 0.0722383549714, 0.0307484032911, 8.43888351473e-16, 0.0, 0.0, 0.0, 9.36032083766e-16, 0.0, 0.0, 2.41242470149e-15, 1.37609877002e-15, 0.0, 2.57090414216e-15, 1.0285722206e-15, 5.49562909392e-16, 0.0, 1.39209961924e-15, 0.0, 0.0, 0.0, 0.0, 3.70386108365e-16, 0.0, 2.11446836599e-15, 0.0, 1.03247747115e-15, 0.0, 0.0, 3.05004953815e-17, 4.82857444269e-16, 0.0, 2.28333214469e-16, 1.23535991222e-16, 1.01337508173e-16, 1.44267523701e-16, 2.21184033712e-17, 0.0, 2.82160122469e-17, 1.58780503713e-16, 2.52046086171e-17, 2.17610049559e-16, 0.0, 1.14666955452e-17, 2.28925893654e-17, 2.29523305711e-17, 4.65581888637e-17, 0.0, 1.1005229769e-17, 8.81885391306e-17, 8.75422470641e-17, 5.93807684387e-17, 3.36977674886e-17, 5.22559542724e-17, 1.00906177132e-16, 1.19243376534e-16 ] }, spectrum_data_point_t { xystar: [-0.13957003126762177, 0.45187859298788907], uv: [3.4443339843750005, 12.0], spectrum: [ 0.0, 0.0, 7.5483218602e-16, 0.0, 1.72341034265e-17, 1.46366915346e-16, 0.0, 1.12450263853e-15, 0.0, 0.0, 7.63047156379e-16, 0.0, 3.33295929163e-16, 0.0, 9.75805056865e-16, 0.0, 0.0, 3.21366173872e-16, 2.32610540087e-16, 0.0, 0.0, 1.80219848073e-16, 0.0, 0.0180429021861, 0.0560997781373, 0.0840209682896, 0.0835900299159, 0.0521420631349, 0.00901622740845, 9.23926255859e-16, 2.08483547913e-15, 4.58375645352e-16, 1.06716742432e-15, 0.0, 0.0, 7.74007764343e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 2.56588010924e-15, 2.88766101094e-15, 1.24692208895e-15, 3.76928348522e-16, 1.1929861541e-15, 0.0, 0.0, 0.0, 3.07258427078e-15, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.14350887484e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 4.20669812313e-17, 0.0, 0.0, 0.0, 5.57218194638e-18, 0.0, 1.46276524092e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.10922399907836886, 0.5003100271188159], uv: [3.9999999999999996, 12.857423828124999], spectrum: [ 7.33695763444e-16, 1.56037786376e-16, 0.0, 0.0, 0.0, 0.0, 4.39110119942e-17, 3.12793482673e-15, 0.0, 0.0, 0.0, 3.05568863523e-15, 0.0, 1.11296710399e-15, 0.0, 0.0, 0.0, 0.0, 2.23350993789e-15, 0.0, 1.41017567231e-15, 1.30625126069e-15, 1.58668953668e-17, 2.54962939272e-16, 0.0227485854853, 0.0691546296174, 0.0942323477709, 0.0762934209453, 0.0270250639761, 1.00462287358e-16, 0.0, 0.0, 6.5305325525e-17, 0.0, 0.0, 5.17939625312e-16, 0.0, 5.88881153928e-16, 0.0, 2.84129490082e-15, 0.0, 0.0, 0.0, 0.0, 1.61973891173e-15, 0.0, 4.04710669048e-15, 0.0, 5.04725361618e-16, 5.88725382792e-16, 1.03629965792e-15, 8.03836708282e-16, 1.19981229601e-15, 0.0, 2.77116641121e-16, 0.0, 0.0, 0.0, 5.73020715044e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.50112998668e-17, 0.0, 2.33231992598e-17, 0.0, 7.20131268396e-18, 6.02645778257e-17, 3.52062265269e-17, 0.0, 2.87050906795e-17, 3.1675279995e-17, 4.78399496723e-17, 4.24895684556e-17, 4.65454177752e-17, 5.99684629833e-17, 6.71354666085e-17, 1.19842611601e-16 ] }, spectrum_data_point_t { xystar: [-0.1006909808144329, 0.5083634171113752], uv: [4.156248046875, 13.0], spectrum: [ 7.90052596911e-16, 4.58770605494e-16, 2.6836369942e-15, 8.27830937588e-17, 8.97750072757e-17, 1.17189275731e-16, 1.57086299504e-16, 5.30936386322e-16, 1.54844260788e-15, 1.08224117044e-15, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.65284582822e-17, 1.73181492394e-15, 0.0, 0.0, 5.01230461346e-16, 3.57442184861e-16, 4.10426755787e-16, 7.81845865646e-16, 0.0196105488732, 0.0588771325627, 0.0849834187504, 0.0789353110393, 0.0417020446218, 5.50902335347e-18, 1.24712586833e-16, 0.0, 1.38996138835e-16, 5.97014324018e-16, 1.87186423712e-16, 0.0, 0.0, 0.0, 0.0, 4.64397163158e-17, 0.0, 5.7068795194e-16, 1.97229712642e-15, 2.16328843969e-16, 1.33181041332e-15, 0.0, 0.0, 0.0, 2.17227366923e-15, 3.83460726607e-16, 5.06124342319e-16, 0.0, 0.0, 9.43770281494e-17, 0.0, 0.0, 0.0, 7.26340362459e-18, 0.0, 2.37892219763e-17, 0.0, 1.85834640928e-16, 2.43967524442e-17, 6.11294866121e-17, 1.54882729435e-18, 4.3406933944e-17, 4.26064956721e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 7.17168901551e-18, 7.48451353429e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.05461199953918444, 0.5425081623282562], uv: [5.0, 13.604494140624999], spectrum: [ 1.7509171881e-16, 5.52363246205e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.67566165382e-16, 0.0, 9.67078810562e-17, 1.87583899594e-15, 2.25880005837e-15, 0.0, 1.70921626479e-15, 1.87509955645e-16, 2.58511165502e-16, 0.0, 1.04949901853e-16, 0.0, 0.0139761562766, 0.0521687402921, 0.0782974458149, 0.0723952436192, 0.0375364883789, 0.0, 3.94488695626e-16, 0.0, 1.10255501821e-16, 0.0, 8.7215356e-18, 0.0, 0.0, 0.0, 5.62781883961e-16, 4.00471472326e-16, 0.0, 5.01119699859e-16, 8.35852105057e-16, 1.48001921374e-15, 7.96077893328e-17, 6.16368931941e-16, 9.99725472714e-17, 4.40616213003e-16, 0.0, 9.75802600664e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.10042356181e-16, 1.2296300719e-16, 7.91156971432e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 1.06200185319e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.6619593928e-18 ] }, spectrum_data_point_t { xystar: [0.018026333074456447, 0.5083634171113752], uv: [6.3300800781249995, 13.0], spectrum: [ 5.29530767441e-16, 0.0, 3.98958315409e-16, 3.11518847462e-17, 7.2455611149e-18, 1.81627434234e-17, 8.08045823881e-17, 0.0, 1.6724435972e-16, 6.51852500559e-16, 2.07625369103e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 2.17758760012e-16, 3.88817377271e-16, 3.01461402165e-16, 2.80575931726e-16, 1.15873181895e-16, 1.79275282083e-16, 4.55175115275e-16, 1.2513229913e-16, 0.0, 1.61010029021e-16, 0.00552329641193, 0.0221443445367, 0.037521441276, 0.0445579362778, 0.041685363425, 0.0308227500077, 0.0159593308467, 0.00305401227779, 1.67882870467e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.73268940634e-16, 7.22423344817e-16, 2.71338291654e-17, 0.0, 1.72846471868e-16, 9.78860892802e-17, 8.57498943604e-17, 0.0, 0.0, 4.88959454605e-17, 0.0, 0.0, 2.73452823513e-17, 0.0, 0.0, 0.0, 2.73561867227e-17, 2.17785418013e-17, 2.6840145458e-17, 0.0, 0.0, 2.67205474176e-17, 9.75927665541e-18, 0.0, 8.26362270983e-18, 3.39930457927e-17, 5.17523247617e-17, 3.60414181345e-17, 9.8500821035e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.30781887056e-18, 8.85795882233e-18, 1.85415512154e-17, 0.0 ] }, spectrum_data_point_t { xystar: [0.0, 0.5275595418815132], uv: [6.0, 13.339845703124999], spectrum: [ 3.58119409508e-16, 1.91517822224e-17, 0.0, 0.0, 0.0, 0.0, 2.34653067444e-17, 1.37895557215e-16, 0.0, 4.27384324412e-16, 0.0, 9.5334090719e-16, 1.2737331372e-15, 0.0, 0.0, 1.12587680606e-15, 8.37653051334e-17, 0.0, 0.0, 0.0, 0.0, 4.65298983935e-16, 2.50460910758e-17, 7.09715798075e-17, 1.04834502465e-17, 0.0, 0.00900421884556, 0.0330953976524, 0.0518374945718, 0.0548536928508, 0.0422730320255, 0.0210198440676, 0.00201710247202, 0.0, 7.78833020354e-18, 1.92059104053e-17, 1.23564516236e-17, 0.0, 0.0, 1.06453905302e-16, 0.0, 0.0, 1.11473694024e-15, 9.34839324062e-16, 0.0, 0.0, 0.0, 4.42292569184e-16, 0.0, 0.0, 1.94082281799e-16, 1.33968680564e-16, 2.35194830576e-16, 1.58370430992e-16, 0.0, 0.0, 0.0, 0.0, 4.02803964421e-17, 0.0, 0.0, 7.2054024174e-18, 1.45512935298e-17, 0.0, 4.62296569577e-17, 2.23022582286e-18, 4.57272274242e-17, 1.77964836222e-17, 5.41432907383e-17, 2.34466602096e-17, 8.09603993913e-18, 2.05055550505e-17, 1.56496264061e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.08735797311443666, 0.395393768864403], uv: [7.5996113281249995, 11.0], spectrum: [ 1.7951152072e-18, 2.42687023766e-17, 7.14389135811e-18, 0.0, 0.0, 0.0, 3.09872804226e-18, 5.661115257e-18, 0.0, 0.0, 0.0, 5.26731860507e-17, 7.05349197427e-17, 4.65808469994e-17, 2.10411838842e-17, 0.0, 0.0, 0.0, 1.14389718659e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.000577535707165, 0.00451834456686, 0.00970474988625, 0.0146464738623, 0.0185243176133, 0.0208978174603, 0.0215474897364, 0.0204671678441, 0.0178543393759, 0.0140914972665, 0.00971450250153, 0.00538665544921, 0.00187077254129, 2.09364314515e-17, 0.0, 0.0, 0.0, 3.06062186962e-17, 9.33704564276e-19, 0.0, 1.35575214695e-17, 3.07190118131e-17, 0.0, 0.0, 0.0, 0.0, 2.59749865897e-18, 0.0, 6.16471649602e-18, 4.95856840741e-18, 1.90399820513e-18, 0.0, 1.65714840202e-18, 1.41216969578e-18, 6.9100371601e-18, 3.98637220123e-18, 4.46696780318e-18, 1.99076889199e-18, 4.75378201856e-18, 3.14920427226e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.97224568824e-18, 0.0, 3.95960551484e-7, 0.0, 1.99735241309e-7, 7.81919533145e-8, 3.30226189524e-7, 2.60399587908e-7 ] }, spectrum_data_point_t { xystar: [0.05759869992804513, 0.45187859298788907], uv: [7.0546894531249995, 12.0], spectrum: [ 4.39555449169e-17, 0.0, 0.0, 0.0, 6.74220016775e-18, 0.0, 0.0, 1.11369225153e-17, 0.0, 0.0, 3.99795798807e-17, 0.0, 0.0, 0.0, 2.70536194249e-17, 0.0, 1.09208348957e-16, 1.34409432186e-16, 3.90741960306e-17, 1.34452370998e-16, 0.0, 1.39533027894e-17, 3.50845090222e-17, 2.89565430931e-17, 2.15089453962e-17, 4.73535118338e-17, 0.00121322613622, 0.0085356793802, 0.0173307438099, 0.0245055847268, 0.0286050788056, 0.029148617648, 0.0262354367599, 0.0205143512251, 0.0131664089603, 0.0058529724634, 0.000655832646077, 0.0, 0.0, 2.96971044205e-17, 1.26124940592e-16, 2.32900368463e-17, 0.0, 2.86436795492e-17, 0.0, 1.60331797995e-18, 1.23281954051e-17, 0.0, 9.5233505498e-17, 5.73097635986e-17, 0.0, 0.0, 6.35063605741e-17, 0.0, 3.39959770196e-18, 4.68422542756e-18, 5.82433847127e-18, 1.09573051875e-17, 3.88463750905e-18, 7.86632989919e-18, 1.11040091737e-18, 1.645604969e-18, 3.7380054529e-18, 8.72430519799e-19, 0.0, 0.0, 2.48680014875e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.39291046226e-18, 1.57668835266e-18, 0.0, 3.76207209619e-19 ] }, spectrum_data_point_t { xystar: [0.054611999539184386, 0.45651222403869096], uv: [6.999999999999999, 12.082033203125], spectrum: [ 1.45407359517e-17, 0.0, 1.01501793124e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.79500683767e-17, 0.0, 4.40854328985e-16, 2.15811070814e-16, 0.0, 0.0, 9.00457713593e-17, 1.07114364291e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.00168787882254, 0.00938336739884, 0.0183724452714, 0.0255291334158, 0.0294143971596, 0.0295930742348, 0.0262290249989, 0.0200565523879, 0.0123654048854, 0.00494053005172, 1.12284351985e-18, 0.0, 2.59354527487e-17, 0.0, 0.0, 1.76418862381e-16, 4.8011627064e-17, 0.0, 0.0, 0.0, 0.0, 2.52087415078e-17, 1.45550670118e-17, 0.0, 5.11009593131e-17, 0.0, 5.73822397676e-17, 0.0, 2.73832947487e-17, 0.0, 5.63941670123e-18, 0.0, 1.1213158193e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.69612257625e-18, 5.28564626656e-18, 1.31757257059e-17, 9.98122167698e-18, 1.16619915263e-17, 1.73185303161e-17, 1.30773292026e-17, 1.75234176128e-17, 1.59066309398e-17, 1.85632291472e-17, 1.98273472293e-17 ] }, spectrum_data_point_t { xystar: [0.15839623814001638, 0.22593929649394454], uv: [8.900392578125, 8.0], spectrum: [ 3.6525513374e-18, 3.67597672557e-18, 1.19943205309e-17, 2.51766576114e-19, 9.53829040571e-19, 5.15312908686e-19, 7.42046888094e-19, 1.41829532638e-18, 9.11749207457e-18, 0.0, 0.0, 0.0, 0.0, 1.21189895418e-17, 4.6207900054e-18, 1.0001357954e-17, 0.0, 0.0, 9.68634257443e-18, 3.27814388636e-18, 0.0, 1.41866760671e-18, 0.0, 4.32732386021e-18, 0.0, 0.000379209093989, 0.00138898384087, 0.00277173505094, 0.0043014897429, 0.00580734901758, 0.00718454786774, 0.00836139883294, 0.00928631672964, 0.00992806593486, 0.0102744418708, 0.0103281628571, 0.0101060222873, 0.00963566777399, 0.00895343667686, 0.00810300594928, 0.00713289737019, 0.00609439336644, 0.00503791331937, 0.00400987863608, 0.00305372210016, 0.00220154593093, 0.00147785599604, 0.000897224926751, 0.000466178629771, 0.000181090220948, 3.0462022939e-5, 1.96516133425e-19, 1.94743855685e-19, 5.21595279568e-19, 5.97748474506e-19, 6.31782603606e-19, 8.96008980765e-19, 4.2237229001e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.69457559449e-8, 1.04760770219e-19, 2.27348936999e-19, 1.61942111603e-19, 1.35489884052e-19, 1.13894550664e-19, 6.87034323163e-8 ] }, spectrum_data_point_t { xystar: [0.1363701094196227, 0.28242412061743066], uv: [8.497072265625, 9.0], spectrum: [ 0.0, 0.0, 0.0, 1.9454655954e-20, 0.0, 2.20678978126e-19, 0.0, 0.0, 0.0, 7.21226120623e-18, 0.0, 4.67194561352e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 3.92941001063e-18, 1.87380519621e-18, 2.86447749352e-18, 4.76954445784e-19, 4.15648336554e-18, 0.0, 6.55775379551e-18, 0.0, 0.0, 0.000980124266585, 0.00280882636852, 0.00500556456938, 0.00721425141125, 0.00921887168185, 0.0108770473237, 0.0120895064135, 0.0127986133767, 0.0129882616993, 0.0126755066206, 0.0119065831697, 0.0107526227224, 0.00930242850147, 0.00766007946214, 0.0059407280515, 0.0042578955928, 0.00272958426083, 0.00145984977787, 0.000541205538607, 3.92842598682e-5, 0.0, 8.29787739345e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.96461822484e-7, 0.0, 0.0, 6.9811365538e-8, 0.0, 1.87503503055e-7, 7.38725178644e-7, 7.45053115182e-7, 8.69296053337e-7, 1.12757480718e-6, 4.85230486268e-7, 9.16556248703e-7 ] }, spectrum_data_point_t { xystar: [0.11301067992922935, 0.33890894474091676], uv: [8.069337890625, 10.0], spectrum: [ 1.37657460266e-18, 0.0, 0.0, 1.65614860674e-18, 1.19846525505e-18, 1.51829481315e-18, 0.0, 2.16085981077e-18, 5.69728029177e-18, 2.6161217258e-17, 0.0, 0.0, 0.0, 4.1254249919e-17, 0.0, 2.11275929725e-17, 0.0, 7.47953292923e-18, 0.0, 9.14256678283e-18, 3.65036327947e-17, 0.0, 3.86018284473e-17, 0.0, 0.0, 5.16537735245e-19, 0.000631607151995, 0.00310909256591, 0.00638278034158, 0.00969769445106, 0.0126048315821, 0.0148269329226, 0.0161871923598, 0.0166030587316, 0.0160832707935, 0.0147173604194, 0.0126619050926, 0.0101281912443, 0.00736944482321, 0.00467156618174, 0.00234128917625, 0.000685346299466, 9.25692530305e-18, 0.0, 0.0, 2.47878311548e-17, 5.80402990391e-19, 0.0, 0.0, 8.24956597665e-18, 0.0, 2.23006100445e-17, 0.0, 1.17320394179e-17, 0.0, 0.0, 3.50172794004e-19, 3.70574334459e-19, 1.38332877979e-18, 2.10999111455e-18, 0.0, 0.0, 2.05504189628e-18, 1.53270394923e-18, 1.70897461145e-18, 1.90716147304e-18, 3.79339370079e-18, 1.86317663427e-18, 1.87602714301e-18, 3.79022922965e-18, 1.77119608813e-18, 1.41429354576e-18, 4.12194447801e-18, 5.23540476786e-18, 4.28850134217e-18, 4.3434817178e-18, 5.35605402328e-18, 6.23488204372e-18, 4.1553038842e-18, 7.24287285285e-18, 4.92126142355e-18 ] }, spectrum_data_point_t { xystar: [0.10922399907836883, 0.34767964787107547], uv: [8.0, 10.155275390624999], spectrum: [ 6.01782784005e-18, 1.97127008665e-17, 7.78721760731e-18, 4.69850073443e-19, 0.0, 6.12055663697e-18, 0.0, 1.78481157374e-18, 2.45480461973e-18, 0.0, 2.30418855147e-18, 2.56264606851e-17, 3.24561399788e-17, 0.0, 0.0, 3.69038315517e-18, 1.88218401475e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.11362485084e-18, 1.65867778186e-17, 2.76718775366e-17, 0.000591670845246, 0.00322479232413, 0.00672450130779, 0.0102535537531, 0.0133141200551, 0.0156076959599, 0.0169464778605, 0.017246406452, 0.0165252776007, 0.014897161459, 0.0125523752934, 0.00973890614628, 0.00675438854928, 0.00393467104073, 0.00163333947632, 0.000207994449075, 0.0, 2.25149639359e-17, 6.24088375963e-18, 0.0, 0.0, 9.48161890733e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.19633383869e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.22853310424e-6, 1.32286582092e-7, 7.58913445096e-7, 1.7135235437e-19, 7.44929393284e-8, 3.20726813663e-7, 7.24226763506e-7, 1.540659735e-18, 7.41926615395e-7, 9.69051773543e-7 ] }, spectrum_data_point_t { xystar: [0.20116852684160416, 0.11296964824697227], uv: [9.683595703124999, 6.0], spectrum: [ 3.50368088116e-8, 2.61066157516e-8, 0.0, 7.44377472502e-20, 0.0, 7.07520478481e-19, 6.90149131282e-19, 1.21685292749e-19, 1.33504713127e-18, 1.87735433754e-18, 0.0, 1.78152944876e-19, 0.0, 3.88430161982e-20, 0.0, 0.0, 0.0, 5.80164643082e-19, 2.10312086999e-19, 2.65570806742e-19, 4.84365618936e-19, 3.3015524107e-19, 0.0, 0.000119387037252, 0.000503441613987, 0.00107329070153, 0.00176215729295, 0.00251025727997, 0.00326593949606, 0.00398869266936, 0.00465093560303, 0.0052332398227, 0.00572303485424, 0.00611530971875, 0.00640560085077, 0.00659490943399, 0.00668739929153, 0.00669119423993, 0.00661557640721, 0.006472103852, 0.0062767785039, 0.006042149305, 0.00578340380996, 0.00551424599057, 0.0052469010167, 0.00499273506686, 0.00475715981165, 0.00454577534381, 0.00436184951929, 0.00420448616812, 0.00407375814464, 0.00396729149397, 0.00388299134249, 0.00381864158515, 0.0037697999242, 0.00373390564058, 0.00370698174712, 0.00368705747989, 0.0036735155059, 0.00366428577758, 0.0036580732033, 0.00365338973172, 0.00364980971045, 0.00364674048075, 0.00364389140921, 0.00364179738781, 0.00364070559308, 0.00363994014195, 0.00363894663229, 0.0036386929704, 0.00363832716458, 0.00363760931287, 0.00363747068492, 0.00363718925509, 0.00363699472667, 0.0036367030429, 0.00363642700698, 0.00363626753744, 0.00363644346577, 0.00363612011681, 0.00363625132928 ] }, spectrum_data_point_t { xystar: [0.17994237858321024, 0.16945447237045844], uv: [9.294923828125, 7.0], spectrum: [ 6.02862078815e-19, 8.68441944075e-19, 2.34537007447e-18, 0.0, 6.27888418374e-19, 0.0, 5.56605679962e-19, 1.04109794558e-18, 0.0, 0.0, 2.34374100216e-18, 0.0, 5.70484153056e-19, 0.0, 2.78237008076e-18, 4.62135822211e-20, 1.66957933982e-19, 2.96979249044e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.000149248678795, 0.000740555938732, 0.00163274904059, 0.0026902164825, 0.00379936911097, 0.00487002184304, 0.00584723226955, 0.00669122778614, 0.00737496199289, 0.00788407758213, 0.00821154567685, 0.00835970541161, 0.00833614197692, 0.00815544234688, 0.007834581913, 0.00739962618903, 0.00687679127318, 0.00629061108964, 0.00567071943097, 0.00504283679456, 0.00443091157061, 0.00385404770005, 0.00332759959285, 0.00286091549551, 0.00245712882249, 0.00211763910917, 0.00183861642588, 0.00161210810556, 0.00143267383591, 0.00129397066179, 0.00118781909534, 0.00110852033669, 0.00105025283351, 0.00100855688042, 0.000978757508639, 0.000958004657367, 0.000942814797126, 0.00093192695595, 0.000924587454003, 0.000918680456024, 0.000914783301914, 0.000911779984036, 0.000909336716392, 0.000908011918495, 0.000906697118524, 0.000905992400369, 0.000905558269344, 0.000904815726833, 0.000904832853876, 0.000904501062485, 0.000904328001438, 0.000904560314264, 0.000904032100006, 0.000904508117066, 0.000904382369149, 0.00090430502962, 0.000904426242138 ] }, spectrum_data_point_t { xystar: [0.1638359986175532, 0.21181820078499514], uv: [9.0, 7.750001953125], spectrum: [ 1.25991328825e-18, 1.55218774116e-18, 4.17583700953e-18, 0.0, 6.53196676819e-20, 0.0, 1.11913171094e-17, 0.0, 1.00371323101e-17, 0.0, 0.0, 2.89446616167e-18, 0.0, 6.32754599791e-18, 1.1381263843e-18, 2.37424794065e-18, 3.47256108127e-18, 6.1012486253e-18, 0.0, 0.0, 0.0, 1.46546005436e-18, 1.14905371362e-18, 1.9472658127e-18, 8.03410470719e-20, 0.000475385486038, 0.00146797307899, 0.00276050711896, 0.0041629773546, 0.00553959000652, 0.00679881268906, 0.00787996505675, 0.00874005843762, 0.00934794288374, 0.00969762922548, 0.00979213669128, 0.00964470885532, 0.00927602344524, 0.00871397501467, 0.00799900654503, 0.00716918257672, 0.00626726781083, 0.00533829089081, 0.00441865077402, 0.00354354009044, 0.00274205514529, 0.00203424520111, 0.00143509098055, 0.000949260750661, 0.000573932592177, 0.000304674153089, 0.000127136800861, 2.91799003773e-5, 0.0, 0.0, 5.50079836257e-20, 0.0, 4.03526105304e-19, 2.54254006807e-19, 5.3686676338e-19, 5.68269304374e-19, 6.47334015871e-19, 4.85356536956e-19, 7.95502923817e-19, 7.54388718648e-19, 4.18948428437e-7, 5.79052898567e-19, 4.9509075767e-19, 4.76591474971e-7, 5.7619416237e-19, 4.15332233448e-7, 2.3353396124e-7, 4.98902405933e-19, 7.35928878591e-7, 1.68572566867e-7, 2.10064170919e-19, 8.74422623282e-7, 2.88485131765e-7, 1.57548128189e-19, 1.18154094349e-6, 5.66628300343e-7 ] }, spectrum_data_point_t { xystar: [0.2643669833395862, -0.056484824123486134], uv: [10.840822265625, 3.0], spectrum: [ 0.000197266690303, 0.000198152168969, 0.000200158671912, 0.000201993484934, 0.000202684086341, 0.000202951237512, 0.000203442197363, 0.000202646316007, 0.000198603015646, 0.000190734640024, 0.000179901709764, 0.000164986807702, 0.000146304692939, 0.000123941258487, 9.80771375313e-5, 7.03997162666e-5, 4.34858983975e-5, 2.10540600902e-5, 5.62013524649e-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.48149903065e-5, 8.77217783064e-5, 0.000224106502557, 0.000426937663405, 0.000697374555392, 0.00103316482522, 0.00143087641527, 0.00188653745693, 0.00239456439959, 0.00294652468658, 0.00353376055151, 0.00414547904511, 0.004769805468, 0.0053950766636, 0.00600931123683, 0.00660156584368, 0.00715996278197, 0.00767572123742, 0.00814232099162, 0.00855543748198, 0.0089133441319, 0.00921702368003, 0.00946898720108, 0.00967416095561, 0.00983771874064, 0.00996543736158, 0.0100633512863, 0.0101369520928, 0.0101917054939, 0.0102321154905, 0.0102611300875, 0.0102815582846, 0.0102964161932, 0.0103067202694, 0.010314597233, 0.0103204197844, 0.010324052614, 0.0103264123852, 0.0103278697478, 0.0103286166586, 0.0103294487376, 0.0103296460324, 0.010329897046, 0.0103301779555, 0.0103300739557, 0.0103303483782, 0.010330610225, 0.0103304271712, 0.0103305283816, 0.0103303485414, 0.0103303692294, 0.0103304598938, 0.0103304557358, 0.0103305385195 ] }, spectrum_data_point_t { xystar: [0.24330083117359216, 0.0], uv: [10.455080078125, 4.0], spectrum: [ 2.61091875907e-5, 2.45148807571e-5, 2.13309194485e-5, 1.75006199267e-5, 1.33685310797e-5, 7.69899191129e-6, 3.39392244037e-6, 7.58607242128e-7, 2.11738148668e-7, 5.17161684891e-8, 0.0, 1.5366994319e-21, 1.92941609236e-21, 1.42887817516e-20, 0.0, 1.99132561581e-20, 3.93018315379e-6, 2.62943022815e-5, 6.63768995861e-5, 0.000122744584833, 0.000194352615969, 0.000282442535906, 0.000386641145667, 0.000501253315675, 0.000633701070245, 0.000784117491975, 0.000955833483923, 0.00115098128176, 0.0013681186745, 0.00161061735353, 0.00187890448042, 0.00217281903229, 0.00249148687179, 0.00283206074673, 0.00319177215103, 0.00356802178026, 0.0039550588609, 0.00435253307183, 0.00475144170296, 0.00514853440264, 0.00553585647432, 0.0059078528839, 0.00625971086764, 0.00658715931642, 0.00688587502847, 0.00715478267545, 0.00739088039123, 0.00759370588758, 0.00776622943861, 0.00790804059326, 0.00802352091482, 0.00811535273471, 0.00818749234729, 0.00824200685753, 0.00828262697081, 0.00831155643231, 0.00833310772056, 0.00834888418932, 0.00835980698664, 0.00836759118204, 0.0083729765562, 0.00837660219359, 0.00837888080868, 0.00838092396409, 0.00838218739949, 0.00838337978547, 0.00838458041727, 0.00838519519703, 0.00838591302841, 0.00838646324552, 0.00838686829778, 0.00838728540291, 0.00838749796865, 0.00838763612021, 0.00838798793896, 0.00838816576414, 0.00838835683372, 0.00838867412732, 0.00838876131718, 0.00838885746089, 0.00838884108449 ] }, spectrum_data_point_t { xystar: [0.22223467900759816, 0.056484824123486106], uv: [10.069337890625, 4.999999999999999], spectrum: [ 1.15245369929e-6, 9.62205469091e-7, 7.33539395123e-7, 3.92202380037e-7, 2.49548099144e-7, 6.62809605248e-8, 0.0, 1.10377923726e-19, 0.0, 8.46658585474e-20, 0.0, 0.0, 1.90029684359e-22, 1.36167834134e-19, 2.27945285961e-19, 5.94855375533e-20, 0.0, 0.0, 3.58243940574e-20, 2.50029741756e-20, 0.0, 3.66376143805e-5, 0.000207466083063, 0.000477091394478, 0.000815736511219, 0.00120181771987, 0.00162042210042, 0.00205529141596, 0.0024934826583, 0.00292001345618, 0.00332778936495, 0.00371213641288, 0.0040700891032, 0.00439619810132, 0.00469172063958, 0.00495258812946, 0.00518248480222, 0.00538059330468, 0.00554848129215, 0.00568880081819, 0.00580423314037, 0.00589841688165, 0.00597191835943, 0.00602936817431, 0.00607168893394, 0.0061035266168, 0.00612714275208, 0.00614321360763, 0.00615559287424, 0.00616397689129, 0.00616948538103, 0.00617394395678, 0.00617718871124, 0.00617863926894, 0.00618057502373, 0.00618213672607, 0.00618210805646, 0.00618223760933, 0.00618199790448, 0.00618102087895, 0.00618042267272, 0.00617968939389, 0.00617975444142, 0.00617989858915, 0.00617967913644, 0.00617944224954, 0.00617939580299, 0.00617918678318, 0.00617899757526, 0.00617899863369, 0.00617862299668, 0.00617836729145, 0.00617827992355, 0.00617797523226, 0.00617752443119, 0.00617743068767, 0.00617728515152, 0.0061767128634, 0.00617691971029, 0.00617662862058, 0.00617665313231 ] }, spectrum_data_point_t { xystar: [0.21844799815673765, 0.06663455128009714], uv: [10.0, 5.1796894531249995], spectrum: [ 4.14745397026e-7, 2.6406579284e-7, 1.32543140345e-8, 0.0, 2.75495565463e-19, 0.0, 2.70983577987e-19, 0.0, 1.77154247881e-20, 6.33183019564e-21, 3.98538652876e-19, 0.0, 0.0, 5.1335596135e-19, 0.0, 0.0, 5.6188777859e-20, 9.53086135943e-20, 0.0, 4.27380301912e-21, 0.0, 0.0, 0.000144432222291, 0.000417457882222, 0.000784922708878, 0.00121681885915, 0.00168727824607, 0.00217487417672, 0.00266226724035, 0.00313378141315, 0.00357803687254, 0.00398980010864, 0.00436381753161, 0.00469425024955, 0.00498234937385, 0.00522776844067, 0.00543186118725, 0.00559543268191, 0.0057226461721, 0.00581433841058, 0.0058759852019, 0.00591247721153, 0.00592868320681, 0.00592930404143, 0.00592011225973, 0.00590457268879, 0.00588635577304, 0.00586647115122, 0.00584701926815, 0.00582894366649, 0.00581342037123, 0.00579978906515, 0.00578920736767, 0.00578025916366, 0.00577239092841, 0.00576648803916, 0.00576153967446, 0.00575760922614, 0.00575633707961, 0.00575573049605, 0.00575431578051, 0.00575390730731, 0.00575350145022, 0.00575251029974, 0.00575230760153, 0.00575149890848, 0.00575041376758, 0.0057497177596, 0.00574895173949, 0.00574835165223, 0.00574766343335, 0.00574797937354, 0.00574887434323, 0.00574897964003, 0.00574930562132, 0.00574987114721, 0.00574990453568, 0.00574907920559, 0.0057492693093, 0.00574928483388, 0.00574896095856 ] }, spectrum_data_point_t { xystar: [0.3066059517331741, -0.1694544723704584], uv: [11.614259765625, 1.0], spectrum: [ 0.000448055179387, 0.000448111689116, 0.000447763932322, 0.000446467048857, 0.000444232548124, 0.000440260980813, 0.000432951124157, 0.000419162769014, 0.00039477850985, 0.000354098362627, 0.000291604158114, 0.000209597314334, 0.000118180638486, 3.80368407636e-5, 0.0, 0.0, 3.74640257717e-19, 0.0, 4.4722304919e-19, 0.0, 0.0, 6.83511926982e-19, 6.89487756518e-19, 1.40936532967e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 1.00612536347e-18, 0.0, 0.0, 1.45763898626e-18, 2.64802378963e-18, 6.15739066088e-19, 0.0, 4.9942949242e-19, 0.0, 0.0, 0.0, 0.0, 0.000719361878561, 0.00217933380534, 0.00414526243809, 0.00640272288636, 0.00876694380689, 0.0110874459866, 0.0132565694278, 0.0152030782577, 0.0168893978039, 0.0183091036733, 0.0194752536715, 0.0204093434852, 0.0211395436545, 0.0216981192036, 0.0221180406573, 0.0224272162135, 0.0226519026663, 0.0228145724589, 0.0229316754484, 0.0230151189971, 0.0230737956959, 0.0231151486631, 0.0231439670959, 0.0231644786995, 0.0231792678069, 0.0231894907254, 0.0231965410465, 0.0232017514248, 0.0232049688033, 0.0232071333853, 0.0232089167722, 0.0232099117811, 0.0232107733958, 0.0232113474929, 0.0232116643364, 0.0232118727112, 0.0232118449145, 0.0232120348202, 0.0232119499786, 0.0232118551746 ] }, spectrum_data_point_t { xystar: [0.28548646753638013, -0.11296964824697227], uv: [11.227541015625, 2.0], spectrum: [ 0.000250549602027, 0.000251452709583, 0.000251035319131, 0.000251943054287, 0.000253356039742, 0.000252561421046, 0.000252810777263, 0.000250887773992, 0.000248485473205, 0.000240406275936, 0.000224181770295, 0.000198124505456, 0.000164778686088, 0.000125295331646, 8.26336566789e-5, 4.20660201895e-5, 1.16170064803e-5, 3.64812680522e-20, 0.0, 6.64503738095e-20, 3.28898900598e-20, 1.09526564068e-19, 1.08270809735e-19, 9.36651261686e-20, 0.0, 0.0, 0.0, 0.0, 1.33087073689e-19, 0.0, 1.12462849073e-19, 0.0, 4.47598299087e-20, 7.29091486159e-21, 5.25636536023e-5, 0.000328468748266, 0.000806997405984, 0.00146270484608, 0.00226944272939, 0.00319417459694, 0.00420107028513, 0.0052568451724, 0.00632408175829, 0.00737057841626, 0.00836617000443, 0.00928929219789, 0.0101209441927, 0.0108522020281, 0.011480120857, 0.0120051038558, 0.0124352216198, 0.0127803214614, 0.0130523887267, 0.0132630343465, 0.0134227726385, 0.0135410111012, 0.0136261601491, 0.0136877818466, 0.0137330371035, 0.0137658147759, 0.013789171165, 0.0138048276006, 0.0138154722901, 0.0138233251254, 0.0138293686576, 0.0138336051554, 0.0138363546725, 0.0138380840147, 0.0138391997799, 0.0138397904444, 0.0138401579417, 0.0138401551041, 0.0138402280602, 0.0138404329634, 0.0138401161483, 0.0138394661719, 0.0138392315997, 0.0138392665857, 0.0138396501605, 0.0138388950849, 0.0138388370719 ] }, spectrum_data_point_t { xystar: [0.2730599976959221, -0.07970747840702072], uv: [11.0, 2.588869140625], spectrum: [ 0.000118565233311, 0.000123262040414, 0.000128919358877, 0.000139190956308, 0.000140326915506, 0.000157471454122, 0.000164553709047, 0.000182120311266, 0.000190177335556, 0.000189546102185, 0.000195127430367, 0.000182270437578, 0.000161461630374, 0.000136216617639, 0.00010589690497, 7.3536774755e-5, 4.47953376902e-5, 2.1127706825e-5, 6.14098886759e-20, 1.18165568349e-19, 1.50522700947e-19, 6.19343199438e-20, 1.13089071484e-19, 5.40449164441e-20, 9.98284769573e-20, 9.45989491016e-20, 6.39750717648e-20, 0.0, 0.0, 0.0, 9.72512544466e-6, 0.000129756423003, 0.000359526905722, 0.000692954002978, 0.00112094150231, 0.00163698486081, 0.00223516061296, 0.0028968306559, 0.00360977044185, 0.00435946901225, 0.00511963142006, 0.0058852465185, 0.00663261560781, 0.00734299364325, 0.00800844966575, 0.00861233980877, 0.00915233244937, 0.00961890929621, 0.010016391677, 0.0103459101367, 0.0106127431111, 0.0108310173006, 0.0110007794012, 0.0111327219081, 0.0112328513819, 0.0113046598165, 0.0113554850397, 0.0113932040434, 0.0114203543128, 0.0114412190468, 0.0114574135164, 0.0114679205428, 0.0114744459452, 0.0114788104756, 0.0114822299725, 0.0114841035555, 0.011485297945, 0.0114868723497, 0.0114878306557, 0.0114875512019, 0.0114882853944, 0.0114892791972, 0.0114878998915, 0.0114882732002, 0.0114890356065, 0.0114877882955, 0.0114880021172, 0.0114878653648, 0.0114876576551, 0.0114879115507, 0.0114882008427 ] }, spectrum_data_point_t { xystar: [-0.27837992110025134, -0.19769688443220146], uv: [0.9025869140625007, 0.5000000000000004], spectrum: [ 0.0104239281828, 0.0104239093013, 0.0104229471446, 0.0104211184481, 0.0104169564189, 0.010408216388, 0.0103929509585, 0.0103650252649, 0.0103148743768, 0.0102253950866, 0.010074312785, 0.00984414033678, 0.00952251077563, 0.0091031779513, 0.00858951154235, 0.00798611371659, 0.0073002158701, 0.00654543063713, 0.00573574655304, 0.00489253073012, 0.00404232472016, 0.00320927491592, 0.00241685966162, 0.00168918816507, 0.00105208067036, 0.000533318191267, 0.000169723536218, 0.0, 0.0, 2.05547286963e-19, 1.01246688419e-19, 0.0, 1.3423878586e-19, 0.0, 2.47380442251e-19, 0.0, 1.16821137258e-19, 0.0, 0.0, 1.19172225885e-19, 3.529192653e-20, 3.37005404328e-20, 0.0, 7.63534051944e-21, 0.0, 4.45847010194e-5, 0.000121839773594, 0.000215053284068, 0.000311612312683, 0.000402844751035, 0.000485504286605, 0.000556412386062, 0.000615387823003, 0.00066195958945, 0.000698421425605, 0.000727925927516, 0.00075035783929, 0.000765864509279, 0.000777051723653, 0.000785015438684, 0.000790608775099, 0.00079535055359, 0.000797696058588, 0.000797728962314, 0.000797930398981, 0.000798554947388, 0.000799046532587, 0.000800028470433, 0.000799954971347, 0.00079901501148, 0.000798881494173, 0.000799315881926, 0.000799565780648, 0.000799590722935, 0.000799529583795, 0.000799530247533, 0.000799107199109, 0.000798712733053, 0.000798422038851, 0.000798286255173, 0.000798186621819 ] }, spectrum_data_point_t { xystar: [0.29509948609003117, -0.1976968844322015], uv: [11.403564941406248, 0.5], spectrum: [ 0.00202603816895, 0.00202403245604, 0.00202007600043, 0.00201281260783, 0.00199987976248, 0.00197814511307, 0.00193686933683, 0.00186300688783, 0.00173373309939, 0.00151841612431, 0.00119451680024, 0.000777205571495, 0.000342882992414, 2.18585131267e-5, 0.0, 0.0, 0.0, 0.0, 1.02415054517e-17, 1.01536539658e-17, 1.19524346786e-17, 1.06557291661e-17, 0.0, 8.95060455358e-18, 1.19674666925e-17, 3.64006502636e-18, 1.20440200833e-18, 5.1700636838e-18, 0.0, 2.29924269662e-17, 0.0, 1.58561845174e-17, 7.57756747454e-18, 0.0, 0.0, 0.0, 4.79240253004e-18, 0.0, 3.53258656541e-18, 0.0, 1.66234562567e-18, 0.0, 1.08946097758e-18, 2.51275764394e-18, 4.65285657194e-18, 0.00199235898488, 0.0055726815377, 0.00993720590368, 0.0144975700138, 0.018859172437, 0.0227914499404, 0.0261822930656, 0.0289959240621, 0.0312539140942, 0.033014557628, 0.0343566350042, 0.0353585737046, 0.0360936554356, 0.0366264025905, 0.0370109591966, 0.0372868007575, 0.037480731712, 0.0376161240696, 0.0377130158091, 0.0377825055681, 0.0378310271749, 0.0378658632156, 0.0378888063078, 0.0379052001371, 0.0379166245115, 0.0379245071062, 0.0379296596166, 0.0379336508317, 0.0379356057728, 0.0379369372027, 0.0379382931141, 0.0379386614219, 0.0379395856283, 0.0379392066324, 0.0379397376926, 0.0379403418607 ] }, spectrum_data_point_t { xystar: [-0.2841797794497497, -0.14121206030871533], uv: [0.7963857421875007, 1.5], spectrum: [ 0.00628432809638, 0.00628485644276, 0.00628571579639, 0.00628729205915, 0.00629178668877, 0.00630159758807, 0.00631787002298, 0.00634579461141, 0.00639401596786, 0.00647672081292, 0.00661126442131, 0.0068064107176, 0.00705936463416, 0.00735894883271, 0.00768162405171, 0.00799681783528, 0.0082671122759, 0.00845105209757, 0.00851058063867, 0.00841611210741, 0.00814900399084, 0.00770319560397, 0.00708339224395, 0.00630702792456, 0.0053918736992, 0.00436329447628, 0.00326102882011, 0.0021488987035, 0.00113528214777, 0.000361717222617, 1.0264905558e-18, 1.93668452079e-18, 0.0, 8.44920321304e-19, 2.35231843066e-20, 4.7753076814e-19, 1.53635208407e-18, 1.06725833199e-18, 0.0, 0.0, 5.10557869414e-19, 0.0, 0.0, 0.0, 5.87712433658e-18, 1.83197370939e-18, 1.24019885473e-18, 2.46747966784e-18, 2.71768790479e-18, 2.40252753623e-18, 0.0, 2.12063487708e-18, 7.54881276926e-19, 0.0, 2.003172608e-19, 0.0, 0.0, 2.32555013113e-19, 0.0, 0.0, 2.40088727814e-19, 5.35762943763e-20, 1.20849244774e-19, 7.73259704349e-20, 3.59009267938e-19, 1.50120540917e-19, 5.30257254457e-20, 0.0, 0.0, 2.28063621048e-19, 6.69156028331e-20, 5.12228686942e-20, 2.69304961117e-19, 3.91276282072e-19, 8.92560968169e-19, 5.1838416372e-19, 2.99849663328e-19, 1.66214413328e-7, 0.0, 2.4055735702e-19, 6.2139423229e-8 ] }, spectrum_data_point_t { xystar: [0.28455310366534964, -0.14121206030871533], uv: [11.2104501953125, 1.5], spectrum: [ 0.00061168578911, 0.000611388358419, 0.000613327629482, 0.000615241332934, 0.000617636141757, 0.000619479482398, 0.000620039116978, 0.000618678698867, 0.000611730662792, 0.000587932550076, 0.000551990186117, 0.000493798688772, 0.000413022101087, 0.000321884859896, 0.000219647275369, 0.000119744078958, 4.2075357895e-5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.62463103768e-20, 2.4742769796e-19, 2.80998930815e-19, 2.06585776493e-19, 0.0, 1.16890215176e-19, 0.0, 3.93228171495e-19, 2.29114683494e-19, 4.18108437463e-19, 4.56428557146e-19, 0.0, 0.0, 0.0, 0.000111855929556, 0.000619107207326, 0.00144776237315, 0.00254093636305, 0.00382753952878, 0.00522551767313, 0.00666767903981, 0.00809458055253, 0.00945624381695, 0.0107068686378, 0.0118248546796, 0.0127970826411, 0.0136162785903, 0.0142929632984, 0.0148409834083, 0.0152752750056, 0.0156113990122, 0.0158658090268, 0.0160562549776, 0.0161966265002, 0.0162980844999, 0.0163714360797, 0.0164233367231, 0.0164611575073, 0.0164881179952, 0.0165073719261, 0.0165211747086, 0.0165305481645, 0.0165363859483, 0.0165404483045, 0.0165431889098, 0.0165447766755, 0.0165460649647, 0.016546892921, 0.0165477585326, 0.0165485879385, 0.0165485931888, 0.0165489554011, 0.0165487661503, 0.0165494749914, 0.0165496786678, 0.016549860804, 0.0165502033349, 0.0165498362918 ] }, spectrum_data_point_t { xystar: [-0.2836064601186499, -0.0847272361852292], uv: [0.8068837890625007, 2.5], spectrum: [ 0.0, 3.94809627166e-18, 3.02699515885e-18, 2.17149279486e-18, 0.0, 0.0, 7.6271275878e-21, 2.66467507353e-18, 3.30558065903e-18, 0.000111068415549, 0.000555692036095, 0.0014090663424, 0.00268804719301, 0.00435479163993, 0.00631341410395, 0.00843497006169, 0.0105477516672, 0.0124582767358, 0.0139851163365, 0.0149790597822, 0.0153444876279, 0.0150424407853, 0.0140773946108, 0.0125083244581, 0.0104227528833, 0.00794384520377, 0.00525814732927, 0.00268691471403, 0.000714062213391, 0.0, 2.9151186855e-19, 0.0, 9.20456545404e-19, 6.32164411287e-18, 2.8996929534e-18, 3.24382839127e-18, 0.0, 7.55865094739e-18, 4.59959782125e-18, 2.55186210695e-18, 2.32345402355e-18, 0.0, 0.0, 0.0, 0.0, 7.81009093921e-18, 2.84338642649e-18, 1.15561073346e-17, 3.88932860562e-18, 1.16513639743e-17, 3.75720181218e-18, 7.96441876388e-19, 4.15830994252e-18, 0.0, 0.0, 0.0, 0.0, 7.81398551357e-19, 0.0, 2.0868446057e-19, 1.38585373884e-19, 0.0, 2.63419148966e-19, 0.0, 0.0, 1.75820027074e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.24947659500898114, -0.08372328462958752], uv: [10.568164453125, 2.5177738281249997], spectrum: [ 0.000752303150972, 0.000751456115099, 0.000751035637707, 0.000750179301397, 0.000747828322906, 0.000745257809108, 0.000741344422098, 0.000735917512662, 0.000726382278368, 0.000711889436019, 0.000687801510296, 0.000653729599782, 0.000606037169972, 0.000546898964558, 0.000476982007756, 0.000399444641529, 0.000317184415035, 0.000234230143908, 0.000155349859027, 8.69619110553e-5, 3.5073840106e-5, 3.53500195796e-6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.18139317575e-20, 2.34827716577e-20, 2.75645830151e-5, 0.000156344487784, 0.000384694991594, 0.000709784218776, 0.00112579649752, 0.00162477292269, 0.0021966748174, 0.00282800560565, 0.00350526243252, 0.00421374599292, 0.00493776094158, 0.00565989320097, 0.00636421462851, 0.00703491709098, 0.0076592244585, 0.00822833161981, 0.00873443375859, 0.00917506233502, 0.00954929929845, 0.00986077771228, 0.0101145915146, 0.0103177823394, 0.010477342052, 0.0106000016092, 0.0106924994993, 0.0107609817433, 0.0108109707158, 0.0108472063131, 0.0108732191221, 0.0108911621871, 0.0109040667093, 0.0109129889531, 0.0109189299024, 0.0109233139571, 0.0109267050423, 0.0109291390504, 0.0109307338737, 0.0109320766972, 0.0109330123631, 0.010933823782, 0.0109343381271, 0.0109346626673, 0.0109349196971, 0.010935015115, 0.0109349580342, 0.0109347746162, 0.0109346520556, 0.0109344422283, 0.0109343426886, 0.0109342433988, 0.0109342172561 ] }, spectrum_data_point_t { xystar: [0.2772021543094081, -0.10188225830032176], uv: [11.075847005208333, 2.196289713541667], spectrum: [ 0.000358595883835, 0.000358136654626, 0.000357147902036, 0.000356617878124, 0.000355719270239, 0.000354163201973, 0.000352286992504, 0.00035002619395, 0.000343982638754, 0.000331249374502, 0.000311894967909, 0.00028431972171, 0.000245906684169, 0.000198756537478, 0.000148457169262, 0.000100677501953, 5.49731457986e-5, 1.87737833635e-5, 3.453926489e-20, 2.57985631011e-21, 1.34691964862e-19, 0.0, 1.71853904884e-19, 5.86343663743e-20, 1.47942985084e-19, 2.37339839612e-19, 3.33867623516e-19, 9.69812164142e-20, 0.0, 0.0, 3.01100353513e-19, 1.67915999764e-19, 0.0, 9.86819250572e-5, 0.000362758815861, 0.000787514989029, 0.00135025276354, 0.00203749021394, 0.00282292036392, 0.00368283254374, 0.00459693070347, 0.00553234140117, 0.0064625570474, 0.00736365913062, 0.00821467396964, 0.00899667286052, 0.00969824301379, 0.0103101556098, 0.0108324475101, 0.0112698337638, 0.0116251924829, 0.01190980112, 0.0121330897322, 0.0123073493895, 0.0124389487782, 0.0125367914282, 0.0126106147833, 0.0126632186827, 0.0127017459994, 0.0127297043156, 0.0127494833486, 0.0127620632374, 0.0127714438172, 0.0127777787991, 0.0127818962192, 0.0127842524222, 0.0127860709515, 0.0127867562672, 0.0127870539959, 0.0127874290906, 0.0127872376033, 0.0127873448076, 0.0127878776929, 0.0127875851096, 0.0127879490375, 0.0127876578295, 0.0127879611665, 0.0127883219496, 0.0127885174689, 0.012788998643, 0.0127891146099 ] }, spectrum_data_point_t { xystar: [-0.28080652850165067, -0.028242412061743067], uv: [0.8581533203125007, 3.5], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 1.03905484239e-17, 2.52200600561e-18, 0.0, 0.0, 0.0, 1.41670588112e-18, 0.0, 4.5002581257e-18, 0.0, 0.000742413333543, 0.00348969793744, 0.00762659958119, 0.012418656254, 0.0171073331756, 0.0209814252783, 0.0235150602453, 0.0243994054981, 0.0235076122298, 0.0209452708732, 0.0169587847365, 0.0119595684164, 0.00661105458348, 0.00204151894314, 0.0, 0.0, 0.0, 0.0, 4.68967262573e-18, 1.62189714791e-17, 0.0, 0.0, 2.10645102638e-17, 7.47702732521e-18, 1.82274150802e-17, 2.50777549836e-17, 4.10244944262e-18, 0.0, 7.72763743982e-19, 0.0, 1.30240876472e-17, 3.42162051697e-17, 4.68744716196e-18, 0.0, 4.6445437645e-17, 0.0, 2.81172855514e-17, 0.0, 1.75918940592e-17, 0.0, 0.0, 0.0, 8.68507639257e-18, 6.085151361e-18, 0.0, 4.07438727531e-18, 1.61203657002e-19, 0.0, 0.0, 0.0, 0.0, 1.60877592671e-18, 2.97809586842e-18, 6.10163294179e-19, 2.52673430511e-19, 1.84695888253e-18, 3.34459284765e-18, 3.35066269943e-18, 2.91004688899e-18, 1.89829556874e-18, 3.8902047515e-18, 0.0, 0.0, 0.0, 2.34354257907e-20, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.2361409527066634, -0.028242412061743067], uv: [10.3239755859375, 3.5], spectrum: [ 0.000422675476941, 0.000422108207641, 0.000420755723936, 0.000419493177081, 0.000416731579472, 0.000412052238997, 0.000408581824492, 0.000403584801514, 0.000397478228573, 0.000389983475625, 0.000379647897349, 0.000365388411384, 0.000348374190835, 0.00032756809369, 0.000303358071464, 0.000278591866108, 0.000254675537217, 0.000233343570976, 0.000214804811719, 0.000202032800285, 0.000197118526265, 0.000204448657043, 0.000226528251958, 0.00026268449046, 0.000317902691239, 0.000393807861407, 0.000495917480694, 0.000628775139543, 0.000796123284222, 0.00100252808788, 0.00124862630972, 0.00153480725621, 0.00185923579138, 0.00221806903148, 0.00260903510467, 0.00302829596251, 0.00346986476441, 0.00392815927198, 0.00439615475303, 0.00486681158281, 0.00533216480308, 0.00578435117732, 0.00621690521278, 0.00662314756107, 0.0069958983748, 0.00733168700041, 0.0076284433092, 0.00788378936149, 0.00809913141202, 0.00827702372775, 0.00842094314075, 0.00853620430526, 0.008626004853, 0.00869511369428, 0.00874760124817, 0.00878756530691, 0.00881646747115, 0.00883703713124, 0.00885230602343, 0.00886313107718, 0.00887000169776, 0.00887488156119, 0.00887831727617, 0.00888042890482, 0.00888164394655, 0.00888222971367, 0.008882952011, 0.00888337591855, 0.0088836627569, 0.00888349960944, 0.00888367089236, 0.00888337577171, 0.00888324545732, 0.00888351763733, 0.00888289266708, 0.0088829691338, 0.00888297250303, 0.00888313844468, 0.00888297711787, 0.0088830202079, 0.0088829905212 ] }, spectrum_data_point_t { xystar: [-0.27637996994525194, 0.028242412061743053], uv: [0.9392080078125007, 4.5], spectrum: [ 0.0, 1.62782010939e-17, 0.0, 0.0, 1.18099882308e-17, 0.0, 1.03793225146e-18, 0.0, 0.0, 2.32974588978e-17, 1.65685757543e-17, 0.0, 1.60319161671e-18, 0.0, 0.0, 0.0, 6.35189851361e-17, 0.00416838400449, 0.0131513287851, 0.0233835543041, 0.0320273655621, 0.0371658612996, 0.0377135879972, 0.0336703917765, 0.0257510940043, 0.0154519258439, 0.00537470904257, 0.0, 2.02985312349e-18, 5.1210815568e-17, 0.0, 0.0, 0.0, 2.99226733637e-17, 5.0054823244e-17, 4.63171287601e-17, 4.35394960715e-17, 5.36527081047e-17, 0.0, 0.0, 5.63187072993e-18, 5.11501429198e-17, 0.0, 1.75578901584e-16, 0.0, 7.33221006647e-18, 2.82872426985e-16, 0.0, 1.38037253533e-16, 4.25354817307e-17, 2.3883157815e-17, 0.0, 0.0, 5.72387607076e-17, 2.13359862942e-17, 5.46425178445e-18, 4.98793868261e-17, 1.67913900667e-18, 2.34774698486e-17, 0.0, 8.46999961289e-18, 2.01953274217e-17, 1.03834782653e-17, 1.42715524626e-18, 4.70207459287e-18, 0.0, 3.85404896879e-18, 5.67376148605e-18, 0.0, 0.0, 0.0, 0.0, 2.79282500729e-18, 6.19602475011e-18, 1.02577869928e-17, 1.17746344909e-19, 0.0, 0.0, 3.70647133437e-18, 7.2431504743e-18, 2.03864872474e-18 ] }, spectrum_data_point_t { xystar: [0.2256078766236664, 0.028242412061743053], uv: [10.1311044921875, 4.5], spectrum: [ 3.56822771849e-6, 3.28535339104e-6, 3.46459065097e-6, 3.23413499301e-6, 2.09478448538e-6, 6.76952524611e-7, 3.60338079119e-8, 0.0, 0.0, 1.78724906014e-21, 9.05418544163e-21, 2.8362742546e-22, 2.27772384505e-20, 0.0, 0.0, 1.94291012302e-20, 2.98116715124e-5, 9.25724191243e-5, 0.000187303685197, 0.000306767381721, 0.000450990643083, 0.000612991359464, 0.000791233597256, 0.000982838759525, 0.00118631701779, 0.00140223850396, 0.00162740935805, 0.00186368709263, 0.0021121000246, 0.00236987934724, 0.00263738143968, 0.00291354934657, 0.00319827542284, 0.00348808166506, 0.00378125685508, 0.00407726845996, 0.00437213180057, 0.00466434476539, 0.00495089917922, 0.00522813950758, 0.00549335824092, 0.00574411849873, 0.00597766609531, 0.00619237131576, 0.00638691072167, 0.00655992162656, 0.00671138413008, 0.00684147797815, 0.00695113830262, 0.00704170019385, 0.00711454548749, 0.00717303206736, 0.00721839616562, 0.00725322885894, 0.00727870243206, 0.00729660816371, 0.00730926134828, 0.00731826789221, 0.00732424221034, 0.00732813461069, 0.00733059582607, 0.00733238506714, 0.00733418627978, 0.00733493423057, 0.00733582575091, 0.0073366122451, 0.00733753879191, 0.00733776574005, 0.00733865296276, 0.00733955925629, 0.007340117399, 0.00734018101016, 0.00734021767528, 0.00734066264857, 0.00734023952901, 0.00734042841943, 0.00734029824306, 0.00734003290058, 0.00734009248939, 0.00733997425641, 0.00733986631081 ] }, spectrum_data_point_t { xystar: [-0.27348668949700916, 0.05872807331382255], uv: [0.9921868489583341, 5.039714192708333], spectrum: [ 2.44113460454e-17, 0.0, 0.0, 3.55265263486e-18, 0.0, 1.12067147207e-18, 0.0, 2.64812602344e-17, 0.0, 1.124394965e-17, 1.72841406419e-16, 5.6334814203e-17, 4.41460939176e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.00482114341753, 0.0190097407467, 0.0345922844069, 0.0458910272793, 0.0493836127166, 0.0443789867593, 0.0321076562599, 0.0158344620622, 0.00160099595098, 0.0, 4.95964018682e-17, 3.6542527507e-17, 0.0, 0.0, 2.27976665718e-16, 0.0, 8.968619849e-17, 6.95136060138e-17, 0.0, 0.0, 2.22111338683e-16, 0.0, 0.0, 6.87590478109e-16, 0.0, 3.35408269012e-16, 5.08701717706e-16, 0.0, 1.1587462913e-16, 0.0, 0.0, 2.15042331923e-16, 0.0, 5.99814515245e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.22079176272e-17, 3.77118404989e-18, 0.0, 0.0, 0.0, 4.63396217674e-18, 4.33037048797e-18, 1.89378577264e-17, 0.0, 0.0, 3.02095785665e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.2489539411071413, 0.08042470328708243], uv: [1.441405859375, 5.423828515624999], spectrum: [ 0.0, 0.0, 0.0, 0.0, 9.12713508213e-19, 0.0, 0.0, 1.99421385301e-19, 0.0, 0.0, 1.84422225597e-18, 0.0, 7.54212691991e-19, 0.0, 1.53554118497e-18, 0.00151073543639, 0.00450490091926, 0.008482993115, 0.012896669955, 0.0171671514551, 0.0208113349644, 0.0234654054327, 0.0248762269414, 0.0249227678097, 0.0235519535324, 0.0207807358377, 0.0167228681884, 0.0117389060147, 0.00652576437705, 0.00214923152153, 0.0, 0.0, 3.71128472563e-18, 0.0, 1.25674422066e-17, 0.0, 2.87314872693e-17, 9.47517482423e-18, 2.7478802935e-17, 2.97170013013e-17, 0.0, 0.0, 0.0, 5.21164120456e-18, 0.0, 1.33735423557e-17, 3.64317892689e-18, 0.0, 3.83205909411e-17, 4.97468528582e-19, 5.4047555843e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.65317357529e-18, 9.17240039857e-19, 6.31868632858e-19, 9.82370784106e-19, 2.52716876647e-18, 7.54116704491e-19, 1.03706340135e-18, 5.84678012374e-19, 0.0, 0.0, 1.62536998505e-18, 8.61378232776e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.19314730407803718, 0.08110869920420279], uv: [9.536719140625, 5.4359378906249995], spectrum: [ 4.79286979596e-7, 4.46521825788e-7, 2.99223083087e-7, 1.11783605605e-7, 0.0, 6.28775257147e-21, 0.0, 0.0, 0.0, 2.10997600259e-20, 5.06846960705e-20, 0.0, 0.0, 0.0, 0.0, 8.95766203389e-7, 6.80861166029e-5, 0.000200502664505, 0.000388918609223, 0.000625218865231, 0.000899592791724, 0.00120178807441, 0.00152283258162, 0.0018553020836, 0.00219409802072, 0.0025325784328, 0.002866626155, 0.00319103552929, 0.00350133362405, 0.00379205139792, 0.00406123771418, 0.00430644706028, 0.0045260498583, 0.00472068756607, 0.00489016031345, 0.00503510594858, 0.00515489419286, 0.00525199995499, 0.00532796586272, 0.0053840726922, 0.00542382438126, 0.00544946836135, 0.00546387257781, 0.00546942768054, 0.00546815709723, 0.00546139744381, 0.00545285567053, 0.00544325508505, 0.00543333004013, 0.00542431998126, 0.00541568667676, 0.00540883405426, 0.00540373719161, 0.00539915930357, 0.00539607514366, 0.00539416971331, 0.00539227405056, 0.00539099131333, 0.005389907946, 0.00538878176491, 0.00538801149523, 0.00538744788875, 0.0053875040432, 0.00538710673839, 0.00538681370397, 0.00538684377415, 0.00538674351413, 0.00538676334072, 0.00538667216733, 0.00538658012229, 0.00538671885963, 0.00538636408309, 0.00538599483529, 0.00538582339257, 0.00538555369455, 0.00538584338772, 0.00538532501754, 0.0053851033934, 0.00538480329743, 0.00538449505942, 0.00538461199443 ] }, spectrum_data_point_t { xystar: [0.2197102251070245, 0.05986806650902312], uv: [10.023112630208333, 5.059896484375], spectrum: [ 2.9726370987e-6, 2.10185099262e-6, 1.50938270532e-6, 7.66841661172e-7, 3.13079954619e-7, 6.50129467434e-20, 0.0, 0.0, 1.52673830577e-19, 0.0, 1.09904462481e-19, 0.0, 0.0, 0.0, 0.0, 4.53516059772e-20, 2.84911034655e-19, 1.40041211603e-19, 0.0, 1.4967243856e-20, 0.0, 0.000120700984084, 0.0003453991763, 0.000644009762968, 0.000996175133115, 0.00138676099201, 0.00179955464307, 0.00221916885181, 0.00263682500299, 0.00304299378117, 0.00343083320797, 0.00379485918909, 0.00413271817456, 0.00444041835384, 0.00471784076755, 0.00496408782439, 0.00517916119572, 0.00536684780006, 0.00552413367066, 0.00565537296934, 0.0057630943374, 0.00584848934344, 0.00591657442715, 0.00596768373325, 0.00600706461198, 0.00603752905902, 0.00605943064913, 0.00607504710419, 0.00608573349947, 0.00609424518234, 0.00609923828802, 0.0061034300974, 0.00610660606482, 0.00610676370427, 0.0061068788204, 0.00610721819996, 0.00610767941899, 0.00610793181374, 0.00610783209325, 0.0061080462127, 0.00610875519837, 0.00610743279431, 0.0061067291821, 0.00610658027906, 0.00610574400508, 0.00610535333693, 0.00610462580558, 0.00610446732049, 0.00610408708307, 0.00610342794756, 0.00610347715537, 0.0061029550209, 0.00610260885136, 0.00610232408282, 0.00610206634736, 0.00610198984901, 0.00610165091713, 0.0061013801542, 0.0061008858311, 0.00610095918613, 0.0061004590366 ] }, spectrum_data_point_t { xystar: [-0.2365542759453633, 0.14121206030871536], uv: [1.6684560546875007, 6.5], spectrum: [ 5.44188461488e-18, 1.26529925677e-17, 1.1949402695e-17, 1.08961043637e-17, 0.0, 0.0, 0.0, 4.78284367836e-19, 7.35449686716e-18, 3.30439980732e-18, 1.52922501725e-18, 0.0, 0.0, 0.0, 8.36513538545e-18, 0.0, 0.000436219914947, 0.00399555564422, 0.00957857760789, 0.0159568363483, 0.0220635132764, 0.0270613672829, 0.0303362397401, 0.0315495799939, 0.0305138624785, 0.0271914144067, 0.0217416560727, 0.0147601569587, 0.00745717581835, 0.00171438572147, 0.0, 0.0, 2.05656257207e-17, 7.02685346587e-18, 0.0, 0.0, 0.0, 1.7242137684e-17, 5.70146382433e-18, 0.0, 2.94716490339e-17, 7.62115925321e-18, 0.0, 1.73966272367e-17, 3.51204619184e-17, 3.80777984231e-17, 0.0, 1.02054361413e-17, 0.0, 0.0, 1.45932175301e-17, 0.0, 0.0, 0.0, 6.62827718194e-18, 1.0383911796e-17, 1.81935629418e-18, 0.0, 1.02637504904e-18, 0.0, 2.11710587482e-18, 3.69776000207e-19, 4.42708905839e-19, 1.07314924371e-18, 3.64734455341e-18, 0.0, 5.33469640003e-18, 2.25935552658e-18, 5.16738091197e-18, 5.16335372555e-18, 4.79792942913e-18, 8.0817497964e-18, 2.61614142321e-18, 1.53409039825e-18, 2.935704131e-18, 4.43930885586e-18, 5.15587165821e-18, 4.35160663999e-18, 6.46467730251e-18, 9.39222615617e-18, 1.0424954829e-17 ] }, spectrum_data_point_t { xystar: [0.1771957256649802, 0.14121206030871536], uv: [9.2446298828125, 6.5], spectrum: [ 1.80913947623e-6, 1.26854734392e-6, 9.30529052092e-7, 2.88281153356e-7, 4.24884300322e-19, 0.0, 0.0, 0.0, 0.0, 4.74823410911e-19, 0.0, 1.25688477822e-19, 0.0, 0.0, 0.0, 8.76271769087e-20, 2.27283515817e-20, 1.80506685191e-20, 0.0, 6.67714463269e-5, 0.000324141830579, 0.000722343445089, 0.00121854399859, 0.00178000157555, 0.0023811199909, 0.00299486586087, 0.00360237049854, 0.00418372249665, 0.0047220693487, 0.0052002800617, 0.00560955173595, 0.00594357465679, 0.00619911123069, 0.00637556892061, 0.00647387282169, 0.0064976151736, 0.00645169755833, 0.00634521276402, 0.00618656641839, 0.00598265271636, 0.00574834803417, 0.00549136190308, 0.00522147451528, 0.00495309658835, 0.00469241315744, 0.00444868213332, 0.00422707340787, 0.00403002670269, 0.00385970647372, 0.00371361505597, 0.00359533563833, 0.00350049352596, 0.00342470559355, 0.00336600023703, 0.0033206584701, 0.00328756673851, 0.00326343297669, 0.00324592003785, 0.00323281957026, 0.00322342164438, 0.0032174480003, 0.00321336354506, 0.00321007632489, 0.00320747269641, 0.00320589575507, 0.00320426215322, 0.00320276961645, 0.00320195423677, 0.00320093821711, 0.00320001443213, 0.00319997456505, 0.00319925062551, 0.00319886221595, 0.00319887687527, 0.00319857320185, 0.00319847565903, 0.00319827327387, 0.00319828753023, 0.00319815388279, 0.00319838867105, 0.003198331053 ] }, spectrum_data_point_t { xystar: [-0.2289411285486655, 0.1976968844322015], uv: [1.8078603515624998, 7.5], spectrum: [ 1.44282289301e-17, 0.0, 0.0, 8.65701283388e-17, 0.0, 0.0, 0.0, 3.28271605947e-17, 0.0, 4.39405548661e-18, 0.0, 0.0, 4.11639235029e-17, 4.07787410885e-17, 0.0, 3.08656297397e-19, 3.02936355368e-17, 0.0, 0.00132485859169, 0.00955680871733, 0.0207157532139, 0.0315959819512, 0.0397879503046, 0.0438998167667, 0.043168436147, 0.0374421884081, 0.027300141317, 0.0147241852945, 0.00362439930526, 0.0, 6.73246840079e-17, 0.0, 0.0, 0.0, 4.22363469916e-17, 6.64539259184e-17, 3.01801949345e-17, 1.07889836121e-16, 0.0, 0.0, 0.0, 1.1979043399e-16, 0.0, 0.0, 1.79775083887e-16, 0.0, 7.11000644605e-17, 0.0, 0.0, 0.0, 1.44067387889e-16, 0.0, 3.62978529874e-17, 0.0, 3.03108135165e-17, 0.0, 0.0, 7.16407908708e-18, 4.96784891896e-18, 0.0, 0.0, 7.53891130709e-19, 0.0, 6.65682164036e-18, 3.37602685924e-18, 6.76169504924e-19, 2.34647922769e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.28850078495e-18, 3.99074961775e-18, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.14090324670637208, 0.20052114770276025], uv: [8.580078515624999, 7.550000390625001], spectrum: [ 1.5901864328e-6, 3.17164797282e-7, 4.69701640619e-7, 1.33101485698e-20, 0.0, 0.0, 0.0, 0.0, 6.47957060658e-20, 3.76743538766e-19, 0.0, 7.61345095506e-20, 1.18036418491e-19, 0.0, 2.91458586065e-19, 0.0, 0.0, 0.0, 0.000105641555067, 0.000424639837621, 0.000910410160789, 0.00151463800537, 0.00219588848271, 0.00292167644086, 0.00366464808519, 0.00440011603675, 0.00510532201929, 0.00575859233034, 0.0063375764386, 0.00682436735294, 0.00720810253951, 0.00748536294964, 0.00765189893022, 0.00770797569231, 0.00765785998305, 0.00750693343932, 0.00726417138078, 0.0069417528939, 0.00655207919317, 0.0061101611907, 0.00563194054096, 0.00513334946955, 0.00463084566089, 0.0041398716853, 0.00367129145778, 0.00323679309015, 0.0028449686056, 0.00250021771185, 0.00220286531906, 0.00195414209329, 0.00175036299464, 0.00158619207023, 0.00145709715925, 0.00135765045608, 0.00128315236153, 0.00122855743814, 0.00118837743458, 0.00115967326537, 0.00113930262722, 0.00112480266888, 0.00111428843206, 0.00110659456747, 0.00110049130828, 0.00109622081055, 0.00109343398133, 0.00109046436037, 0.00108808041843, 0.00108717877226, 0.00108592325984, 0.00108512468077, 0.00108462963925, 0.00108409642757, 0.00108381802199, 0.0010831974599, 0.00108241882262, 0.00108179531492, 0.0010810585339, 0.00108084896474, 0.00108064838418, 0.00108029696837, 0.00108047975349 ] }, spectrum_data_point_t { xystar: [0.1692047919394389, 0.183575715175304], uv: [9.098307942708333, 7.250000651041667], spectrum: [ 0.0, 0.0, 1.20389380979e-19, 0.0, 5.51476977449e-19, 1.07165364637e-18, 0.0, 1.19244705078e-18, 0.0, 3.09275001802e-19, 0.0, 3.77342329279e-19, 9.88928817581e-20, 0.0, 0.0, 0.0, 1.56165820098e-18, 0.0, 6.14696999574e-19, 0.0, 1.80133868371e-19, 1.86127239512e-19, 0.000128590532217, 0.000598134182674, 0.00130273958172, 0.00216027033064, 0.00310326964769, 0.00406706601025, 0.00499062045874, 0.00582826383141, 0.00655346080557, 0.007147659001, 0.00759917820367, 0.00789939232473, 0.00804885699682, 0.00805561628696, 0.00792813834688, 0.00768017850784, 0.00732766475966, 0.00688761913844, 0.00638363866701, 0.00583724277804, 0.0052700113843, 0.00470494650379, 0.00416077073098, 0.00365162626752, 0.00318825337313, 0.00277842613561, 0.0024255221369, 0.00212938171545, 0.00188781802547, 0.00169247238182, 0.00153611290172, 0.00141463012305, 0.00132249218056, 0.00125364276506, 0.00120377443182, 0.00116736177781, 0.0011400990554, 0.00112123116723, 0.00110847883893, 0.0010996647689, 0.00109304791818, 0.00108777297019, 0.00108360531727, 0.00108046488071, 0.00107845449886, 0.0010768712855, 0.00107577231459, 0.00107517651518, 0.00107431464886, 0.00107352546329, 0.00107337015523, 0.00107267997038, 0.00107230566623, 0.00107229472508, 0.00107203085497, 0.00107170081206, 0.00107114305081, 0.00107081022417, 0.00107083850779 ] }, spectrum_data_point_t { xystar: [-0.22273237351902372, 0.2393618337920543], uv: [1.9215488281249993, 8.237630859375], spectrum: [ 0.0, 0.0, 0.0, 4.94554227559e-17, 2.46906963833e-19, 0.0, 0.0, 6.03850122437e-17, 8.05283157717e-17, 9.30687695604e-17, 0.0, 0.0, 2.58851235384e-17, 2.40014241846e-17, 0.0, 1.11590531137e-17, 2.73269487897e-17, 3.23805580555e-17, 0.0, 0.0, 0.0117854464603, 0.0301931703029, 0.0469217960641, 0.0570083987806, 0.0577075840058, 0.0483658403969, 0.0307485120478, 0.0107565110327, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.65676416919e-16, 1.7878365547e-16, 0.0, 1.68549712547e-16, 3.28589388356e-16, 2.54857533697e-16, 0.0, 4.96578334366e-16, 1.77858203427e-16, 0.0, 0.0, 0.0, 1.04324144144e-16, 0.0, 0.0, 1.08272437594e-16, 0.0, 1.5889679549e-16, 2.93723508073e-16, 1.70892197557e-16, 1.29281087105e-16, 2.12318333764e-17, 0.0, 1.03846905665e-16, 0.0, 0.0, 1.61502866592e-17, 4.00068641356e-18, 3.30085688909e-17, 0.0, 0.0, 0.0, 2.44188236793e-18, 2.04173132788e-17, 1.89775813742e-17, 1.14922168207e-17, 5.23964226715e-17, 0.0, 4.1784919608e-17, 4.43792133801e-17, 2.83244361889e-17, 4.63155043685e-18, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.19555791187019658, 0.25658674852220487], uv: [2.419140234374999, 8.542578515625], spectrum: [ 2.30266772318e-17, 6.64318453347e-18, 0.0, 0.0, 9.83805947592e-19, 0.0, 0.0, 0.0, 0.0, 1.61232761768e-17, 8.70682910321e-19, 2.57930627017e-18, 1.57560807526e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.00327880875082, 0.00925214559275, 0.0163956775009, 0.0234294467744, 0.0293388409381, 0.0334319574341, 0.0352035518563, 0.0343091217496, 0.0305804220486, 0.0242644094387, 0.0162001823665, 0.00792228307174, 0.00161628012678, 0.0, 5.40372445955e-18, 0.0, 2.65697991288e-17, 0.0, 0.0, 0.0, 6.48064308497e-17, 3.08548459088e-17, 1.82212675149e-17, 0.0, 0.0, 0.0, 0.0, 5.21348351828e-17, 4.59198633338e-17, 8.79053816212e-18, 1.31863381152e-17, 0.0, 0.0, 0.0, 4.2424308662e-17, 0.0, 0.0, 1.15578681403e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 1.31379172226e-18, 1.91125269754e-18, 0.0, 0.0, 9.54938460528e-19, 0.0, 0.0, 6.2002607238e-19, 9.00028041233e-19, 4.73944085501e-19, 9.88314004553e-19, 3.10412625318e-18, 2.67653060268e-18, 4.0754594382e-18, 1.85987842528e-18, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.12830358642909417, 0.2541817085556876], uv: [8.3493662109375, 8.5], spectrum: [ 7.74237155221e-20, 2.5747909799e-19, 6.07788508574e-19, 1.57629369784e-19, 1.61080529997e-20, 1.37923531744e-19, 5.62242965412e-19, 5.65961371764e-19, 0.0, 0.0, 0.0, 6.32750995829e-19, 0.0, 0.0, 4.73122070413e-19, 4.31840930675e-19, 1.87930667792e-19, 3.47728348155e-19, 4.44136529076e-19, 0.0, 0.00013613339044, 0.00063549658256, 0.00139098037326, 0.00231541849329, 0.00333924178875, 0.00440477256841, 0.00546100955567, 0.00645913631421, 0.00735315337319, 0.00810861820624, 0.00870404394031, 0.00912573973968, 0.00936769297627, 0.00942968225599, 0.00931663017848, 0.00904103303515, 0.00861651345988, 0.00806242798682, 0.00740175809308, 0.00666063600018, 0.00586710162274, 0.00505007463064, 0.0042388447056, 0.00345869099725, 0.00273337843845, 0.00208127185969, 0.00151589740733, 0.00104444135323, 0.0006683269092, 0.000384299152639, 0.00018526710977, 6.20294800893e-5, 2.92208942676e-6, 7.68376005464e-20, 0.0, 0.0, 0.0, 1.13392976746e-19, 1.9517126126e-19, 0.0, 2.25011478202e-19, 1.54187483865e-19, 2.41083053587e-19, 9.70837730932e-20, 3.73385826334e-19, 1.05630272436e-7, 1.62906363083e-19, 1.98238658804e-8, 3.10044411361e-7, 4.08702395167e-7, 3.99990871252e-7, 5.26244822101e-7, 4.49411014286e-7, 6.47917193915e-7, 7.3714349113e-7, 6.5578461788e-7, 5.92672464043e-7, 7.24097067544e-7, 4.83927699747e-7, 4.2357300216e-7, 2.60797850332e-7 ] }, spectrum_data_point_t { xystar: [-0.1833955742454785, 0.3106665326791737], uv: [2.6418447265625, 9.5], spectrum: [ 0.0, 0.0, 4.70805104299e-18, 1.75627853881e-17, 0.0, 2.60164280582e-18, 0.0, 1.88684552895e-17, 3.17531285463e-20, 0.0, 0.0, 4.07116076955e-17, 2.51386354887e-18, 7.77602486606e-20, 8.60466142095e-18, 0.0, 0.0, 1.12571560462e-17, 0.0, 0.00242111344562, 0.0105871092634, 0.0211196787864, 0.031303233555, 0.0392623730551, 0.0436443767409, 0.0435565521804, 0.0385740815825, 0.0291857893235, 0.017183314106, 0.00588367565427, 3.04389296001e-17, 0.0, 5.01264524126e-17, 1.12320729862e-16, 4.07829629754e-17, 0.0, 0.0, 0.0, 1.19796195223e-16, 0.0, 1.61136214296e-17, 5.69794922676e-17, 0.0, 0.0, 2.05774116056e-18, 0.0, 7.94271357626e-17, 9.91909037779e-17, 1.0126227779e-17, 3.14211022597e-17, 1.18468850913e-16, 0.0, 2.16600237922e-17, 0.0, 0.0, 2.14463290529e-17, 2.26694661126e-17, 2.97487108515e-17, 3.2316071497e-17, 2.49180114773e-17, 1.24776358524e-17, 8.33339818406e-18, 3.92890176765e-18, 1.14660735314e-17, 8.44614302982e-19, 0.0, 1.6036046024e-18, 4.84280150301e-18, 8.60572395419e-18, 1.8376237628e-18, 1.54237692128e-17, 1.22520187456e-17, 8.88686900106e-18, 1.08652849071e-17, 1.22069707946e-17, 1.19990263212e-17, 1.32421962169e-17, 1.4716291894e-18, 4.38581391545e-18, 1.35362747114e-17, 1.38134297119e-17 ] }, spectrum_data_point_t { xystar: [0.11695719687639743, 0.3106665326791737], uv: [8.1416025390625, 9.5], spectrum: [ 3.5102566516e-19, 9.19919246988e-19, 9.75121902453e-19, 2.43699221275e-19, 7.38027499366e-20, 4.99379641513e-19, 0.0, 5.82327680539e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 2.15381251731e-18, 0.0, 5.05470864793e-18, 1.98583767893e-18, 0.0, 3.56957726207e-18, 0.0, 3.57532929263e-19, 0.0, 1.82910280378e-19, 0.000164402478579, 0.00114332103699, 0.00265879901573, 0.00448197293626, 0.00639735696064, 0.00821500701222, 0.0097951755794, 0.0110546559441, 0.0119446310119, 0.0124371124359, 0.0125257433154, 0.0122251147748, 0.0115678179675, 0.0106011419053, 0.00938376795285, 0.00798636134079, 0.00648957822429, 0.00497655243402, 0.00353532456709, 0.00225050704446, 0.00119711829747, 0.000440016087199, 2.95503379179e-5, 9.19898511497e-19, 9.91496905163e-19, 9.46371295945e-19, 0.0, 0.0, 1.37419012135e-20, 6.40554870125e-19, 0.0, 0.0, 1.02991188318e-18, 2.52006290523e-18, 5.88846819464e-19, 4.5132396371e-19, 1.47321607005e-18, 1.0259315596e-18, 2.1001926085e-19, 1.40138384126e-19, 3.45022198159e-19, 0.0, 6.89103953948e-9, 3.81941111064e-7, 2.28107717928e-7, 3.7831361708e-7, 3.1967435292e-7, 2.03037838448e-7, 1.2708257638e-7, 1.92454689329e-7, 3.19218710892e-7, 4.74796435277e-7, 4.04011537932e-7, 4.44133715418e-7, 9.28189568219e-7, 9.23105652193e-7, 8.68469055884e-7, 8.91258188586e-7 ] }, spectrum_data_point_t { xystar: [-0.17239584289298163, 0.36715135680265987], uv: [2.8432607421875, 10.5], spectrum: [ 6.66978481294e-17, 0.0, 0.0, 0.0, 1.23698548427e-17, 0.0, 2.78110883146e-17, 0.0, 3.23929366214e-17, 0.0, 2.20415725551e-16, 0.0, 1.01735932573e-16, 0.0, 6.01147181899e-17, 1.05533600282e-16, 5.58815377179e-17, 0.0, 0.0, 1.89380148397e-17, 0.0, 0.00850868570575, 0.026886393489, 0.0458725007271, 0.0589439684282, 0.0619436066879, 0.0530196751933, 0.034014486174, 0.0119822601373, 2.99940779574e-16, 0.0, 1.27535463774e-16, 0.0, 0.0, 0.0, 2.18476937039e-16, 0.0, 1.02167960481e-16, 0.0, 3.86795325381e-16, 0.0, 3.59585922973e-16, 2.65451321869e-16, 0.0, 2.47402256807e-16, 0.0, 1.41218805641e-16, 0.0, 5.25757641063e-17, 8.23087899085e-17, 5.89974018844e-16, 0.0, 1.91108511648e-16, 1.48563012461e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.36497159584e-17, 0.0, 0.0, 0.0, 2.09367425873e-18, 0.0, 0.0, 0.0, 2.83663129423e-18, 0.0, 9.7451401807e-19, 2.12165639255e-17, 5.2949699853e-18, 4.94695111542e-19, 4.70402080404e-17, 7.92177761606e-17, 9.74901051703e-17 ] }, spectrum_data_point_t { xystar: [0.08300599406990862, 0.363257015016343], uv: [7.5199222656249995, 10.431055078124999], spectrum: [ 5.71796587953e-20, 6.46930694911e-19, 0.0, 2.14458449064e-19, 1.62263265406e-18, 0.0, 4.89325168456e-19, 0.0, 0.0, 0.0, 0.0, 0.0, 3.39291929138e-18, 7.28503736267e-18, 5.73539378507e-19, 3.79453538551e-19, 0.0, 1.17916594971e-18, 0.0, 0.0, 3.19496556876e-18, 1.49600689261e-18, 0.000504307016975, 0.00182619929741, 0.00366458945654, 0.00577762240418, 0.00796065793936, 0.0100231744273, 0.0117968617005, 0.0131620439249, 0.0140539818246, 0.0144458570026, 0.0143368316732, 0.0137501089963, 0.0127317323754, 0.0113500689194, 0.00969058357835, 0.00785304910936, 0.00595288824071, 0.00411534886557, 0.0024748600205, 0.00116074418905, 0.000297935112677, 1.06931458966e-18, 1.23720942236e-18, 0.0, 8.17409344411e-19, 1.42192416031e-18, 0.0, 0.0, 0.0, 1.04362864604e-18, 1.82540507844e-18, 9.56169198786e-20, 0.0, 0.0, 0.0, 4.84380936351e-21, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.40647507972e-8, 0.0, 2.29098193342e-8, 4.57474690925e-7, 1.1248796319e-7, 0.0, 3.13275254995e-7, 0.0 ] }, spectrum_data_point_t { xystar: [0.11048622602865567, 0.34183251245096963], uv: [8.023112630208333, 10.051758463541665], spectrum: [ 0.0, 7.79476879092e-18, 0.0, 0.0, 0.0, 1.49316980148e-18, 0.0, 0.0, 0.0, 2.48405772926e-17, 3.54363573011e-18, 1.16527751606e-17, 0.0, 3.0322270968e-17, 1.628145479e-17, 3.3672907156e-18, 0.0, 0.0, 3.74654415978e-18, 1.59647859363e-17, 0.0, 1.34329309845e-19, 0.0, 1.58750953783e-18, 0.0, 0.0, 0.00152884857421, 0.0042607500802, 0.00740869271718, 0.0104141114935, 0.012947700883, 0.014807985521, 0.0158699337835, 0.0160873828966, 0.0154850550875, 0.0141443721204, 0.0122002272358, 0.00984136848306, 0.00728238789434, 0.00475866596266, 0.00253377300131, 0.000864652608379, 1.33741493695e-17, 0.0, 2.43026268091e-17, 0.0, 0.0, 3.68618378002e-18, 0.0, 0.0, 1.9876212397e-18, 4.23350470371e-18, 2.15533068191e-17, 0.0, 7.8639766722e-18, 9.83108344763e-19, 2.87429652883e-18, 1.37758873933e-18, 1.61376175765e-18, 4.31193441099e-18, 1.8166140782e-18, 4.1156305323e-18, 4.80171732827e-18, 8.42922645034e-18, 1.14017526525e-17, 1.20928481543e-17, 1.36939728116e-17, 1.47133423213e-17, 1.71133858719e-17, 1.55570306257e-17, 1.65183600813e-17, 1.41925830818e-17, 1.19936688958e-17, 1.27698687128e-17, 1.28776536553e-17, 9.84379339652e-18, 1.09449362279e-17, 1.3383025144e-7, 1.07699931856e-7, 4.90008559987e-18, 1.74467611311e-18 ] }, spectrum_data_point_t { xystar: [-0.16563154587583995, 0.39975152156196625], uv: [2.9671217447916667, 11.077149088541667], spectrum: [ 0.0, 7.64719810675e-16, 2.46027592254e-16, 0.0, 1.97424194247e-17, 0.0, 0.0, 4.90678201913e-16, 0.0, 3.71006221883e-16, 8.61189827443e-16, 0.0, 0.0, 3.33299601832e-16, 0.0, 0.0, 3.19739601647e-16, 3.24860974776e-16, 0.0, 2.6304875546e-16, 7.92708696601e-17, 0.0, 0.0111285266392, 0.0419644275041, 0.0702181760694, 0.0814661616267, 0.0686674024288, 0.0354686732299, 0.000532900602666, 3.59945948538e-16, 3.39632582107e-16, 1.58692622923e-16, 0.0, 7.33154804514e-16, 9.67809027452e-16, 0.0, 0.0, 0.0, 9.67679832371e-16, 3.43780596737e-16, 5.09721501476e-16, 0.0, 0.0, 0.0, 0.0, 1.12798886131e-15, 2.42569599963e-15, 1.7551114349e-15, 5.78736374519e-16, 0.0, 0.0, 0.0, 2.61730704938e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.07742675505e-16, 2.17342748402e-17, 0.0, 4.85216106491e-18, 0.0, 0.0, 5.95629258436e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.30747676921e-18, 3.81658260145e-17, 5.12214378248e-17, 6.23271411159e-17, 5.85391789472e-17, 3.76245401549e-17 ] }, spectrum_data_point_t { xystar: [-0.13713800533189321, 0.42060235013233543], uv: [3.4888667968749996, 11.446289453125], spectrum: [ 1.03550356466e-16, 7.28749494497e-17, 5.69449237428e-17, 0.0, 0.0, 2.97065321731e-18, 1.94227811545e-17, 2.8086205469e-17, 0.0, 4.22699887129e-17, 0.0, 1.66699614342e-16, 0.0, 4.26116191556e-18, 0.0, 6.63960207085e-17, 3.37155873265e-17, 3.97198665293e-17, 0.0, 0.0, 0.0, 0.00498319061689, 0.0174720390516, 0.0319953430799, 0.04441921795, 0.0517021526124, 0.0517142440591, 0.0438007418104, 0.0294046616919, 0.0126051223228, 3.77333655729e-17, 3.71462560039e-16, 0.0, 0.0, 0.0, 2.35058455014e-17, 0.0, 0.0, 1.25487701712e-16, 0.0, 0.0, 0.0, 0.0, 3.87224575731e-16, 0.0, 0.0, 3.56738502902e-17, 1.43774035531e-16, 7.03877329806e-18, 6.64028767907e-17, 1.74844177883e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 3.11992142954e-17, 0.0, 0.0, 0.0, 1.98646622963e-17, 5.92369435444e-18, 0.0, 0.0, 0.0, 0.0, 3.27604710759e-20, 0.0, 0.0, 0.0, 0.0, 4.64438187528e-18, 8.16362149928e-19, 0.0, 1.63229799354e-18, 1.04282569661e-17, 2.91899550824e-17, 2.27106473818e-17, 2.11392644328e-17, 1.77045688367e-17, 1.10351452368e-17 ] }, spectrum_data_point_t { xystar: [0.06354516803021264, 0.423636180926146], uv: [7.163575195312499, 11.5], spectrum: [ 1.83208984536e-18, 3.89631688794e-18, 8.93817268559e-18, 2.12280527863e-18, 1.37295819301e-18, 0.0, 0.0, 1.88365227071e-18, 0.0, 1.00315003328e-17, 1.42534971908e-18, 3.5889070279e-17, 2.60053835047e-17, 0.0, 3.46149186407e-18, 0.0, 1.74892742305e-17, 0.0, 0.0, 0.0, 4.47759084751e-18, 0.0, 0.0, 0.0, 0.000559251813694, 0.00344069838865, 0.00759636590377, 0.0120737597484, 0.0160580334882, 0.0190015817344, 0.0206497515996, 0.0209279665796, 0.0198785326936, 0.0176567122248, 0.0145141482971, 0.0108051386543, 0.00696552144046, 0.00350238783766, 0.000979175478232, 6.58513328198e-18, 0.0, 0.0, 2.43854125407e-17, 1.88565782295e-17, 0.0, 4.65919738906e-18, 9.69818195244e-18, 0.0, 0.0, 1.24011631896e-17, 0.0, 0.0, 5.34046280923e-18, 3.07870725492e-18, 5.03394928831e-19, 3.13215122532e-18, 4.11336188077e-19, 3.76854273383e-19, 0.0, 7.35149746763e-19, 5.18928379699e-19, 8.11539116747e-19, 3.87077868717e-21, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.2027867851e-19, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.11933934314145316, 0.46802240436486464], uv: [3.8147779947916667, 12.285807942708333], spectrum: [ 4.02526813228e-16, 0.0, 5.65207872888e-17, 0.0, 0.0, 1.24210470189e-17, 0.0, 0.0, 0.0, 2.2411274972e-16, 2.30138915965e-16, 0.0, 1.77016538637e-16, 0.0, 0.0, 8.90779746699e-17, 0.0, 0.0, 6.36822555872e-17, 2.19607701261e-17, 1.00868129247e-16, 0.0, 0.00104922353747, 0.020207928254, 0.0436371684629, 0.0612850981267, 0.0662425335065, 0.0557297689484, 0.0329253494732, 0.00844405144273, 6.24795252045e-16, 5.08157516648e-16, 3.38067138422e-16, 0.0, 0.0, 1.89937218673e-16, 0.0, 0.0, 0.0, 0.0, 0.0, 9.61984605696e-17, 5.52585840442e-16, 5.43643501646e-17, 5.7738434974e-16, 3.48457476069e-16, 0.0, 4.60136386371e-16, 1.67170384409e-16, 1.7646473694e-16, 0.0, 3.38697744292e-17, 7.6508977977e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.14395327497e-17, 1.9559092254e-16, 0.0, 0.0, 4.97860917985e-17, 0.0, 3.40068771501e-18, 8.02749777429e-17, 3.18170890541e-17, 1.75856473865e-17, 5.66842718733e-17, 5.89868031095e-17, 0.0, 0.0, 8.37253110201e-18, 0.0, 1.41245560488e-17 ] }, spectrum_data_point_t { xystar: [-0.08567259560990789, 0.4841588094634689], uv: [4.431249609375, 12.571484765625], spectrum: [ 3.01585976714e-17, 2.41407144961e-17, 0.0, 0.0, 0.0, 0.0, 7.21315236394e-18, 0.0, 2.84401822678e-17, 7.66519241567e-19, 0.0, 0.0, 3.95677097919e-17, 1.06971794701e-16, 1.78785932487e-17, 3.96517646432e-17, 2.98091020371e-17, 0.0, 0.0, 0.0, 0.0, 4.83945484713e-17, 0.00398585808348, 0.0151888068479, 0.0285891112712, 0.0403075911461, 0.0473095357681, 0.0475774265785, 0.0406181577953, 0.0279951257129, 0.0133715388476, 0.00198768536234, 0.0, 8.92197151224e-20, 1.91006653713e-17, 6.59731248547e-17, 0.0, 0.0, 5.71383844025e-18, 1.31879070644e-16, 1.8021783967e-16, 8.87996539995e-17, 0.0, 9.684059183e-17, 0.0, 9.99056127953e-19, 0.0, 2.34266749343e-17, 0.0, 1.16047649676e-17, 1.3183413082e-16, 0.0, 0.0, 0.0, 1.21546810426e-17, 0.0, 0.0, 2.69513969322e-18, 4.51427641884e-18, 3.42452923022e-18, 0.0, 0.0, 0.0, 0.0, 9.43357690638e-18, 2.134345427e-17, 6.29425369641e-18, 3.53759406393e-18, 1.63696193967e-17, 1.83264711967e-17, 7.7195056548e-18, 1.96112609374e-17, 1.30392115784e-17, 1.46916058601e-17, 1.3273039183e-17, 0.0, 2.30442678878e-18, 0.0, 0.0, 1.23492567868e-17, 4.01529693826e-17 ] }, spectrum_data_point_t { xystar: [0.025450066430565042, 0.475399248847444], uv: [6.466016015625, 12.416406640625002], spectrum: [ 0.0, 1.25749485278e-17, 0.0, 3.76319920014e-18, 0.0, 3.38063433126e-18, 4.42665585835e-19, 0.0, 3.11756990532e-17, 0.0, 2.13297557153e-17, 0.0, 3.531765107e-17, 2.36666429072e-17, 0.0, 4.21219669749e-18, 0.0, 3.97870109784e-18, 0.0, 0.0, 0.0, 2.86934073203e-17, 9.98020207202e-18, 2.44902223875e-18, 0.00233187872092, 0.00780890582714, 0.0145792536175, 0.0209922052059, 0.0257182942132, 0.0279799275337, 0.0276081594865, 0.0248353200718, 0.0201440802426, 0.0142558848071, 0.00812676945665, 0.00292168174833, 0.0, 0.0, 5.19935258333e-18, 0.0, 7.72309293574e-18, 1.34359788547e-17, 3.90417242735e-17, 5.67203986808e-18, 0.0, 5.17558136252e-18, 1.12764891677e-18, 0.0, 0.0, 9.61051082588e-18, 0.0, 8.21519492467e-18, 0.0, 4.07834901578e-18, 4.14127604637e-18, 5.65807098428e-19, 0.0, 3.3978325519e-18, 7.33677894554e-19, 0.0, 0.0, 9.56901250035e-19, 0.0, 0.0, 0.0, 3.88781611218e-18, 5.46852600797e-18, 6.79892511272e-18, 2.34592691828e-18, 4.7866650401e-18, 1.66986544669e-18, 4.71326134001e-18, 3.64365016959e-18, 2.17811558375e-18, 1.02971962999e-18, 3.99765908717e-18, 4.35762862069e-18, 5.11081079226e-18, 3.11122263852e-18, 5.75045358578e-18, 5.98577008001e-18 ] }, spectrum_data_point_t { xystar: [0.055607566335471294, 0.45342313667148976], uv: [7.018229817708333, 12.027344401041667], spectrum: [ 0.0, 1.68296145143e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.46210085566e-16, 0.0, 5.75024210688e-17, 0.0, 0.0, 1.80468397118e-17, 6.74280682275e-17, 6.90743394104e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.00245724958278, 0.00986889550888, 0.0182004123204, 0.0247704006299, 0.0283437370609, 0.0285503056793, 0.0255363670802, 0.0199360083769, 0.0128599144043, 0.00585038923915, 0.000821267524753, 8.14212536757e-18, 0.0, 8.45214724806e-18, 7.14713935401e-17, 5.82039651852e-17, 0.0, 0.0, 0.0, 4.56860778618e-17, 9.279858144e-18, 0.0, 3.35685644089e-17, 3.24672608151e-17, 4.63109898541e-17, 0.0, 0.0, 2.76422474461e-17, 3.31501021153e-17, 1.79618601923e-17, 1.22677220069e-17, 0.0, 0.0, 0.0, 3.81708170948e-18, 0.0, 0.0, 0.0, 0.0, 0.0, 2.14252222626e-18, 0.0, 0.0, 1.81659048918e-19, 0.0, 0.0, 0.0, 0.0, 2.08648543118e-19, 0.0, 0.0, 4.71739504507e-19, 5.1519608558e-19, 7.05207868148e-19, 0.0 ] }, spectrum_data_point_t { xystar: [-0.06997165996426725, 0.5197449988503355], uv: [4.718749348958333, 13.201498046874999], spectrum: [ 1.66303660376e-16, 0.0, 1.78463423974e-16, 1.44836563284e-17, 0.0, 3.45957881756e-17, 0.0, 0.0, 1.16774112254e-16, 4.08199133519e-16, 0.0, 7.68602270882e-16, 4.31088185823e-16, 7.47159296604e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.39352266752e-17, 9.89049825668e-17, 0.0148922538986, 0.0375322204041, 0.0561019055159, 0.0622309967762, 0.0526526356358, 0.0310815874729, 0.0082447704983, 0.0, 8.36513861024e-17, 0.0, 0.0, 1.61381796489e-16, 6.06891349369e-17, 1.0360820431e-16, 3.6756942106e-16, 0.0, 0.0, 1.55602190724e-16, 1.29003064495e-16, 8.09730405772e-17, 0.0, 0.0, 0.0, 2.18906424179e-16, 0.0, 0.0, 0.0, 2.53911287022e-16, 0.0, 0.0, 7.44129384506e-17, 2.35273543183e-16, 8.2557691736e-18, 2.32333214366e-17, 0.0, 2.19390900094e-17, 0.0, 2.05765307129e-17, 2.95147795093e-18, 2.71759282593e-17, 0.0, 1.87150371653e-17, 4.64717802319e-17, 1.35898045842e-17, 1.53987068718e-17, 1.65796582348e-17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [-0.02730599976959222, 0.5216986346081299], uv: [5.5, 13.236084960937498], spectrum: [ 9.24752934342e-17, 3.90563690066e-17, 9.51272382456e-17, 0.0, 7.99552564236e-19, 8.84485041987e-18, 1.38783261533e-18, 0.0, 8.03517774241e-17, 4.65598741548e-17, 2.54980623824e-16, 1.75109722558e-17, 7.84679737391e-17, 0.0, 0.0, 1.73983451201e-16, 0.0, 0.0, 1.24712522376e-17, 0.0, 0.0, 0.0, 1.67480335165e-17, 0.0, 0.00411137974316, 0.0172599536975, 0.0323691454927, 0.0436056883339, 0.0470594033699, 0.0417058133204, 0.0295621396273, 0.0147617271419, 0.00270647272222, 2.68132611769e-17, 0.0, 2.31240516408e-17, 4.37475755621e-17, 0.0, 5.66634765588e-18, 6.9853716488e-17, 8.55821315204e-18, 0.0, 2.04628020305e-16, 1.3434165859e-16, 7.51815277509e-17, 0.0, 0.0, 0.0, 1.9547701934e-16, 0.0, 0.0, 0.0, 3.92167072591e-17, 4.32460748909e-17, 4.48693233433e-17, 0.0, 0.0, 2.5627120413e-17, 5.8157167117e-18, 9.33975339626e-18, 0.0, 1.81647938611e-18, 1.12475627173e-17, 0.0, 4.98766018453e-18, 0.0, 0.0, 1.15098397577e-18, 0.0, 0.0, 0.0, 3.99283787308e-18, 4.33499210544e-18, 4.10460994484e-19, 3.03905884059e-19, 3.80979757403e-18, 0.0, 0.0, 4.44313844788e-19, 0.0, 0.0 ] }, spectrum_data_point_t { xystar: [0.0060087776914854825, 0.514762125368088], uv: [6.110026692708333, 13.113281901041667], spectrum: [ 0.0, 0.0, 0.0, 0.0, 0.0, 1.93338217864e-17, 2.53350889881e-17, 0.0, 0.0, 0.0, 1.61227265611e-16, 0.0, 0.0, 0.0, 4.59037251366e-16, 1.27019079857e-17, 6.40160211435e-17, 9.92561789615e-17, 0.0, 2.18941745526e-16, 2.1460175331e-16, 0.0, 1.07051751967e-16, 0.0, 1.75546062865e-17, 0.00151145772444, 0.0146955129168, 0.0303109047478, 0.0412510589145, 0.0438507111116, 0.03809308886, 0.0263481927175, 0.0124188863197, 0.00146807277693, 1.50637835621e-16, 0.0, 0.0, 1.22214262388e-17, 0.0, 6.84691033944e-17, 3.74224599397e-16, 0.0, 0.0, 0.0, 0.0, 2.99910514975e-17, 1.30789826894e-16, 1.48059435997e-16, 4.05916616996e-16, 6.45026506533e-17, 2.6606584345e-16, 0.0, 0.0, 1.07893205131e-17, 0.0, 0.0, 1.35742938534e-17, 1.44397414332e-17, 0.0, 2.2658646767e-17, 1.50832660872e-17, 0.0, 1.40668523737e-17, 5.95205406137e-18, 0.0, 6.99200268532e-18, 0.0, 0.0, 0.0, 0.0, 3.42386013034e-19, 5.04498636532e-18, 0.0, 5.16084971135e-19, 0.0, 0.0, 4.14328825874e-18, 2.56492279833e-18, 4.66543423876e-18, 9.39355685547e-18, 9.85269802799e-18 ] }
+];
+
+// Color matching functions.
+pub const cmf_wavelength: [f64; spectrum_num_samples] = [
+ 380.0, 385.0, 390.0, 395.0, 400.0, 405.0, 410.0, 415.0, 420.0, 425.0, 430.0, 435.0, 440.0,
+ 445.0, 450.0, 455.0, 460.0, 465.0, 470.0, 475.0, 480.0, 485.0, 490.0, 495.0, 500.0, 505.0,
+ 510.0, 515.0, 520.0, 525.0, 530.0, 535.0, 540.0, 545.0, 550.0, 555.0, 560.0, 565.0, 570.0,
+ 575.0, 580.0, 585.0, 590.0, 595.0, 600.0, 605.0, 610.0, 615.0, 620.0, 625.0, 630.0, 635.0,
+ 640.0, 645.0, 650.0, 655.0, 660.0, 665.0, 670.0, 675.0, 680.0, 685.0, 690.0, 695.0, 700.0,
+ 705.0, 710.0, 715.0, 720.0, 725.0, 730.0, 735.0, 740.0, 745.0, 750.0, 755.0, 760.0, 765.0,
+ 770.0, 775.0, 780.0,
+];
+const cmf_x: [f64; spectrum_num_samples] = [
+ 0.001368, 0.002236, 0.004243, 0.00765, 0.01431, 0.02319, 0.04351, 0.07763, 0.13438, 0.21477,
+ 0.2839, 0.3285, 0.34828, 0.34806, 0.3362, 0.3187, 0.2908, 0.2511, 0.19536, 0.1421, 0.09564,
+ 0.05795, 0.03201, 0.0147, 0.0049, 0.0024, 0.0093, 0.0291, 0.06327, 0.1096, 0.1655, 0.22575,
+ 0.2904, 0.3597, 0.43345, 0.51205, 0.5945, 0.6784, 0.7621, 0.8425, 0.9163, 0.9786, 1.0263,
+ 1.0567, 1.0622, 1.0456, 1.0026, 0.9384, 0.85445, 0.7514, 0.6424, 0.5419, 0.4479, 0.3608,
+ 0.2835, 0.2187, 0.1649, 0.1212, 0.0874, 0.0636, 0.04677, 0.0329, 0.0227, 0.01584, 0.011359,
+ 0.008111, 0.00579, 0.004109, 0.002899, 0.002049, 0.00144, 0.001, 0.00069, 0.000476, 0.000332,
+ 0.000235, 0.000166, 0.000117, 8.3e-05, 5.9e-05, 4.2e-05,
+];
+const cmf_y: [f64; spectrum_num_samples] = [
+ 3.9e-05, 6.4e-05, 0.00012, 0.000217, 0.000396, 0.00064, 0.00121, 0.00218, 0.004, 0.0073,
+ 0.0116, 0.01684, 0.023, 0.0298, 0.038, 0.048, 0.06, 0.0739, 0.09098, 0.1126, 0.13902, 0.1693,
+ 0.20802, 0.2586, 0.323, 0.4073, 0.503, 0.6082, 0.71, 0.7932, 0.862, 0.91485, 0.954, 0.9803,
+ 0.99495, 1.0, 0.995, 0.9786, 0.952, 0.9154, 0.87, 0.8163, 0.757, 0.6949, 0.631, 0.5668, 0.503,
+ 0.4412, 0.381, 0.321, 0.265, 0.217, 0.175, 0.1382, 0.107, 0.0816, 0.061, 0.04458, 0.032,
+ 0.0232, 0.017, 0.01192, 0.00821, 0.005723, 0.004102, 0.002929, 0.002091, 0.001484, 0.001047,
+ 0.00074, 0.00052, 0.000361, 0.000249, 0.000172, 0.00012, 8.5e-05, 6e-05, 4.2e-05, 3e-05,
+ 2.1e-05, 1.5e-05,
+];
+const cmf_z: [f64; spectrum_num_samples] = [
+ 0.00645, 0.01055, 0.02005, 0.03621, 0.06785, 0.1102, 0.2074, 0.3713, 0.6456, 1.03905, 1.3856,
+ 1.62296, 1.74706, 1.7826, 1.77211, 1.7441, 1.6692, 1.5281, 1.28764, 1.0419, 0.81295, 0.6162,
+ 0.46518, 0.3533, 0.272, 0.2123, 0.1582, 0.1117, 0.07825, 0.05725, 0.04216, 0.02984, 0.0203,
+ 0.0134, 0.00875, 0.00575, 0.0039, 0.00275, 0.0021, 0.0018, 0.00165, 0.0014, 0.0011, 0.001,
+ 0.0008, 0.0006, 0.00034, 0.00024, 0.00019, 0.0001, 5e-05, 3e-05, 2e-05, 1e-05, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0,
+];
+
+pub fn xyz_from_spectrum(spectrum: &[f64; spectrum_num_samples], xyz: &mut [f64; 3]) {
+ *xyz = [0.0, 0.0, 0.0];
+ for i in 0..spectrum_num_samples {
+ xyz[0] += spectrum[i] * cmf_x[i];
+ xyz[1] += spectrum[i] * cmf_y[i];
+ xyz[2] += spectrum[i] * cmf_z[i];
+ }
+ xyz[0] *= spectrum_bin_size;
+ xyz[1] *= spectrum_bin_size;
+ xyz[2] *= spectrum_bin_size;
+}
+
+// This is 1 over the integral over either CMF.
+// Spectra can be mapped so that xyz=(1,1,1) is converted to constant 1 by
+// dividing by this value. This is important for valid reflectances.
+pub const equal_energy_reflectance: f64 = 0.009358239977091027;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +
//! Hand-converted from `spectrum_grid.h` in the supplemental material from [Physically Meaningful Rendering using Tristimulus Colours](https://doi.org/10.1111/cgf.12676)
+
+#![allow(clippy::pedantic)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_upper_case_globals)]
+#![allow(non_snake_case)]
+
+use alloc::vec::Vec;
+
+use super::spectra_xyz_5nm_380_780_097::*;
+
+/*
+ * Evaluate the spectrum for xyz at the given wavelength.
+ */
+pub(super) fn spectrum_xyz_to_p(lambda: f64, xyz: [f64; 3]) -> f64 {
+ assert!(lambda >= spectrum_sample_min);
+ assert!(lambda <= spectrum_sample_max);
+ let mut xyY: [f64; 3] = [0.0, 0.0, 0.0];
+ let mut uv: [f64; 2] = [0.0, 0.0];
+
+ let norm: f64 = 1.0 / (xyz[0] + xyz[1] + xyz[2]);
+ #[allow(clippy::neg_cmp_op_on_partial_ord)]
+ if !(norm < f64::MAX) {
+ return 0.0;
+ }
+ // convert to xy chromaticities
+ xyY[0] = xyz[0] * norm;
+ xyY[1] = xyz[1] * norm;
+ xyY[2] = xyz[1];
+
+ // rotate to align with grid
+ spectrum_xy_to_uv([xyY[0], xyY[1]], &mut uv);
+
+ if uv[0] < 0.0
+ || uv[0] >= spectrum_grid_width_f
+ || uv[1] < 0.0
+ || uv[1] >= spectrum_grid_height_f
+ {
+ return 0.0;
+ }
+
+ let uvi: [usize; 2] = [uv[0] as usize, uv[1] as usize];
+ assert!(uvi[0] < spectrum_grid_width);
+ assert!(uvi[1] < spectrum_grid_height);
+
+ let cell_idx: usize = uvi[0] + spectrum_grid_width * uvi[1];
+ assert!(cell_idx < spectrum_grid_width * spectrum_grid_height);
+ // assert!(cell_idx >= 0);
+
+ let spectrum_grid_cell_t {
+ inside,
+ num_points,
+ idx,
+ } = spectrum_grid[cell_idx];
+ let num = num_points;
+
+ // TODO: can this alloc be removed?
+
+ // get linearly interpolated spectral power for the corner vertices:
+ let mut p = core::iter::repeat(0.0).take(num).collect::<Vec<_>>();
+ // this clamping is only necessary if lambda is not sure to be >= spectrum_sample_min and <= spectrum_sample_max:
+ let sb: f64 = //fminf(spectrum_num_samples-1e-4, fmaxf(0.0,
+ (lambda - spectrum_sample_min)/(spectrum_sample_max-spectrum_sample_min) * (spectrum_num_samples_f-1.0); //));
+ assert!(sb >= 0.0);
+ assert!(sb <= spectrum_num_samples_f);
+
+ let sb0: usize = sb as usize;
+ let sb1: usize = if sb0 + 1 < spectrum_num_samples {
+ sb0 + 1
+ } else {
+ spectrum_num_samples - 1
+ };
+ let sbf: f64 = sb - sb0 as f64;
+ for i in 0..num {
+ let index = idx[i];
+ assert!(index >= 0);
+ let index = index as usize;
+ assert!(sb0 < spectrum_num_samples);
+ assert!(sb1 < spectrum_num_samples);
+ let spectrum = spectrum_data_points[index].spectrum;
+ p[i] = spectrum[sb0] * (1.0 - sbf) + spectrum[sb1] * sbf;
+ }
+
+ let mut interpolated_p: f64 = 0.0;
+
+ if inside == 1 {
+ // fast path for normal inner quads:
+ uv[0] -= uvi[0] as f64;
+ uv[1] -= uvi[1] as f64;
+
+ assert!(uv[0] >= 0.0 && uv[0] <= 1.0);
+ assert!(uv[1] >= 0.0 && uv[1] <= 1.0);
+
+ // the layout of the vertices in the quad is:
+ // 2 3
+ // 0 1
+ interpolated_p = p[0] * (1.0 - uv[0]) * (1.0 - uv[1])
+ + p[2] * (1.0 - uv[0]) * uv[1]
+ + p[3] * uv[0] * uv[1]
+ + p[1] * uv[0] * (1.0 - uv[1]);
+ } else {
+ // need to go through triangulation :(
+ // we get the indices in such an order that they form a triangle fan around idx[0].
+ // compute barycentric coordinates of our xy* point for all triangles in the fan:
+ let ex: f64 = uv[0] - spectrum_data_points[idx[0] as usize].uv[0];
+ let ey: f64 = uv[1] - spectrum_data_points[idx[0] as usize].uv[1];
+ let mut e0x: f64 = spectrum_data_points[idx[1] as usize].uv[0]
+ - spectrum_data_points[idx[0] as usize].uv[0];
+ let mut e0y: f64 = spectrum_data_points[idx[1] as usize].uv[1]
+ - spectrum_data_points[idx[0] as usize].uv[1];
+ let mut uu: f64 = e0x * ey - ex * e0y;
+ for i in 0..(num - 1) {
+ let e1x: f64;
+ let e1y: f64;
+ if i == num - 2 {
+ // close the circle
+ e1x = spectrum_data_points[idx[1] as usize].uv[0]
+ - spectrum_data_points[idx[0] as usize].uv[0];
+ e1y = spectrum_data_points[idx[1] as usize].uv[1]
+ - spectrum_data_points[idx[0] as usize].uv[1];
+ } else {
+ e1x = spectrum_data_points[idx[i + 2] as usize].uv[0]
+ - spectrum_data_points[idx[0] as usize].uv[0];
+ e1y = spectrum_data_points[idx[i + 2] as usize].uv[1]
+ - spectrum_data_points[idx[0] as usize].uv[1];
+ }
+ let vv: f64 = ex * e1y - e1x * ey;
+
+ // TODO: with some sign magic, this division could be deferred to the last iteration!
+ let area: f64 = e0x * e1y - e1x * e0y;
+ // normalise
+ let u: f64 = uu / area;
+ let v: f64 = vv / area;
+ let w: f64 = 1.0 - u - v;
+ // outside spectral locus (quantized version at least) or outside grid
+ if u < 0.0 || v < 0.0 || w < 0.0 {
+ uu = -vv;
+ e0x = e1x;
+ e0y = e1y;
+ continue;
+ }
+
+ // This seems to be the triangle we've been looking for.
+ interpolated_p = p[0] * w + p[i + 1] * v + p[if i == num - 2 { 1 } else { i + 2 }] * u;
+ break;
+ }
+ }
+
+ // now we have a spectrum which corresponds to the xy chromaticities of the input. need to scale according to the
+ // input brightness X+Y+Z now:
+ interpolated_p / norm
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +
//! Textures enable different surface textures for colorizing objects in various ways.
+
+pub mod solid_color;
+pub mod spatial_checker;
+pub mod surface_checker;
+
+use enum_dispatch::enum_dispatch;
+use palette::{white_point::E, Xyz};
+pub use solid_color::*;
+pub use spatial_checker::*;
+pub use surface_checker::*;
+
+use crate::{Float, Position};
+
+#[enum_dispatch(TextureTrait)]
+#[derive(Clone, Debug)]
+/// A texture enum.
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(tag = "kind"))]
+pub enum Texture {
+ /// SolidColor texture
+ SolidColor(SolidColor),
+ /// SpatialChecker texture
+ SpatialChecker(SpatialChecker),
+ /// SurfaceChecker texture
+ SurfaceChecker(SurfaceChecker),
+}
+
+#[enum_dispatch]
+pub(crate) trait TextureTrait {
+ /// Evaluates the color of the texture at the given surface coordinates or spatial coordinate.
+ #[must_use]
+ fn color(&self, u: Float, v: Float, position: Position) -> Xyz<E>;
+}
+
+impl Default for Texture {
+ fn default() -> Self {
+ SolidColor::default().into()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +
//! A solid color texture.
+
+use palette::{convert::IntoColorUnclamped, white_point::E, Xyz};
+
+use super::TextureTrait;
+use crate::colorinit::ColorInit;
+use crate::{Float, Position};
+
+/// Initialization structure for a solid color texture.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct SolidColorInit {
+ /// Initialization struct for the color.
+ pub color: ColorInit,
+}
+
+impl From<SolidColorInit> for SolidColor {
+ fn from(value: SolidColorInit) -> Self {
+ SolidColor {
+ color: value.color.into(),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+/// A solid color texture. Simplest possible [Texture](crate::textures::Texture): returns a solid color at any surface coordinate or spatial position.
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(from = "SolidColorInit"))]
+pub struct SolidColor {
+ /// The color of the [Texture](crate::textures::Texture).
+ pub color: Xyz<E>,
+}
+
+impl TextureTrait for SolidColor {
+ /// Evaluates the color ignoring the given surface coordinates and spatial position - always returns the solid color.
+ #[must_use]
+ fn color(&self, _u: Float, _v: Float, _position: Position) -> Xyz<E> {
+ self.color.into_color_unclamped()
+ }
+}
+
+impl SolidColor {
+ /// Creates a new solid color texture with the specified color.
+ #[must_use]
+ pub fn new(color: impl Into<Xyz<E>>) -> Self {
+ SolidColor {
+ color: color.into(),
+ }
+ }
+}
+
+impl Default for SolidColor {
+ fn default() -> Self {
+ // middle grey
+ Self {
+ color: Xyz::new(0.5, 0.5, 0.5),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +
//! Checkered texture based on the world coordinates.
+
+// TODO: object-aligned spatial checker?
+
+use palette::convert::IntoColorUnclamped;
+use palette::white_point::E;
+use palette::Xyz;
+
+use super::TextureTrait;
+use crate::colorinit::ColorInit;
+#[cfg(feature = "serde-derive")]
+use crate::colorinit::TypedColorInit;
+use crate::{Float, Position, PI};
+
+/// A standard checkered texture based on spatial 3D texturing.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+pub struct SpatialCheckerInit {
+ /// Uniform color for the even-numbered checkers of the texture.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_even"))]
+ pub even: ColorInit,
+ /// Uniform color for the odd-numbered checkers of the texture.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_odd"))]
+ pub odd: ColorInit,
+ /// Controls the density of the checkered pattern. Default value is 1.0, which corresponds to filling a 1.0 unit cube in the coordinate system with one color of the pattern. Even values preferred - odd values may create a visually thicker stripe due to two stripes with same color being next to each other.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_density_spatial"))]
+ pub density: Float,
+}
+
+impl From<SpatialCheckerInit> for SpatialChecker {
+ fn from(value: SpatialCheckerInit) -> Self {
+ SpatialChecker {
+ even: value.even.into(),
+ odd: value.odd.into(),
+ density: value.density,
+ }
+ }
+}
+
+/// A standard checkered texture based on spatial 3D texturing.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(from = "SpatialCheckerInit"))]
+pub struct SpatialChecker {
+ /// Uniform color for the even-numbered checkers of the texture.
+ pub even: Xyz<E>,
+ /// Uniform color for the odd-numbered checkers of the texture.
+ pub odd: Xyz<E>,
+ /// Controls the density of the checkered pattern. Default value is 1.0, which corresponds to filling a 1.0 unit cube in the coordinate system with one color of the pattern. Even values preferred - odd values may create a visually thicker stripe due to two stripes with same color being next to each other.
+ pub density: Float,
+}
+
+#[cfg(feature = "serde-derive")]
+fn default_even() -> ColorInit {
+ // TODO: what would be a sensible color here?
+ ColorInit::TypedColor(TypedColorInit::XyzE(Xyz::new(0.8, 0.8, 0.8)))
+}
+
+#[cfg(feature = "serde-derive")]
+fn default_odd() -> ColorInit {
+ // Middle gray
+ ColorInit::TypedColor(TypedColorInit::XyzE(Xyz::new(0.5, 0.5, 0.5)))
+}
+
+#[cfg(feature = "serde-derive")]
+fn default_density_spatial() -> Float {
+ 1.0
+}
+
+impl SpatialChecker {
+ /// Create a new `SpatialChecker` object with the specified colors and density.
+ #[must_use]
+ pub fn new(color1: impl Into<Xyz<E>>, color2: impl Into<Xyz<E>>, density: Float) -> Self {
+ SpatialChecker {
+ even: color1.into(),
+ odd: color2.into(),
+ density,
+ }
+ }
+}
+
+impl TextureTrait for SpatialChecker {
+ /// Evaluates the color at the given spatial position coordinate. Note that the `SpatialChecker` is spatial - surface coordinates are ignored.
+ #[must_use]
+ fn color(&self, _u: Float, _v: Float, position: Position) -> Xyz<E> {
+ // TODO: convert ahead-of-time. NOTE: take into account serde-i-fication; not enough to do in `new` alone
+ let density = self.density * PI;
+ let sines = 1.0 // cosmetic 1 for readability of following lines :)
+ * (density * position.x).sin()
+ * (density * position.y).sin()
+ * (density * position.z).sin();
+ if sines < 0.0 {
+ self.odd.into_color_unclamped()
+ } else {
+ self.even.into_color_unclamped()
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +
//! Checkered texture based on the surface coordinates of an object.
+
+use palette::convert::IntoColorUnclamped;
+use palette::white_point::E;
+use palette::Xyz;
+
+use super::TextureTrait;
+use crate::colorinit::ColorInit;
+#[cfg(feature = "serde-derive")]
+use crate::colorinit::TypedColorInit;
+use crate::{Float, Position, PI};
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+/// A standard checkered texture based on 2D surface UV coordinates.
+pub struct SurfaceCheckerInit {
+ /// Uniform color for the even-numbered checkers of the texture.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_even"))]
+ pub even: ColorInit,
+ /// Uniform color for the odd-numbered checkers of the texture.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_odd"))]
+ pub odd: ColorInit,
+ /// Controls the density of the checkered pattern. Default value is 10, which corresponds to using 10 tiles over the width of the object. On spheres, this means 10 tiles around the sphere.
+ #[cfg_attr(feature = "serde-derive", serde(default = "default_density_surface"))]
+ pub density: Float,
+}
+
+impl From<SurfaceCheckerInit> for SurfaceChecker {
+ fn from(value: SurfaceCheckerInit) -> Self {
+ SurfaceChecker {
+ even: value.even.into(),
+ odd: value.odd.into(),
+ density: value.density,
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde-derive", serde(from = "SurfaceCheckerInit"))]
+/// A standard checkered texture based on 2D surface UV coordinates.
+pub struct SurfaceChecker {
+ /// Uniform color for the even-numbered checkers of the texture.
+ pub(crate) even: Xyz<E>,
+ /// Uniform color for the odd-numbered checkers of the texture.
+ pub(crate) odd: Xyz<E>,
+ /// Controls the density of the checkered pattern. Default value is 10, which corresponds to using 10 tiles over the width of the object. On spheres, this means 10 tiles around the sphere.
+ pub(crate) density: Float,
+}
+
+#[cfg(feature = "serde-derive")]
+fn default_even() -> ColorInit {
+ // TODO: what would be a sensible color here?
+ ColorInit::TypedColor(TypedColorInit::XyzE(Xyz::new(0.8, 0.8, 0.8)))
+}
+
+#[cfg(feature = "serde-derive")]
+fn default_odd() -> ColorInit {
+ // Middle gray
+ ColorInit::TypedColor(TypedColorInit::XyzE(Xyz::new(0.5, 0.5, 0.5)))
+}
+
+#[cfg(feature = "serde-derive")]
+fn default_density_surface() -> Float {
+ 10.0
+}
+
+impl SurfaceChecker {
+ /// Create a new `SurfaceChecker` object with the specified colors and density.
+ #[must_use]
+ pub fn new(color1: impl Into<Xyz<E>>, color2: impl Into<Xyz<E>>, density: Float) -> Self {
+ SurfaceChecker {
+ even: color1.into(),
+ odd: color2.into(),
+ density,
+ }
+ }
+}
+
+impl TextureTrait for SurfaceChecker {
+ /// Evaluates the color at the given surface position coordinates. Note that `SurfaceChecker` is surface-based, and thus ignores the spatial position coordinate.
+ #[must_use]
+ fn color(&self, u: Float, v: Float, _position: Position) -> Xyz<E> {
+ // TODO: convert ahead-of-time. NOTE: take into account serde-i-fication; not enough to do in `new` alone
+ let density = self.density * PI;
+ let sines = 1.0 // cosmetic 1 for readability of following lines :)
+ * (density * u).sin()
+ * (density * v).sin();
+ if sines < 0.0 {
+ self.odd.into_color_unclamped()
+ } else {
+ self.even.into_color_unclamped()
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +
//! The fundamental building blocks of spectral rendering.
+
+use core::{array::from_fn, ops::Range};
+use palette::{white_point::E, Xyz};
+use rand::rngs::SmallRng;
+use rand_distr::uniform::SampleRange;
+
+use crate::Float;
+
+/// Wavelength in nanometers
+pub type Wavelength = usize;
+
+/// The lower bound for the wavelengths, inclusive
+pub const MIN_WAVELENGTH: Wavelength = 380;
+/// The upper bound for the wavelenghts, exclusive
+pub const MAX_WAVELENGTH: Wavelength = 780;
+/// The range of wavelenghts used, inclusive low, exclusive high
+pub const SPECTRUM: Range<Wavelength> = MIN_WAVELENGTH..MAX_WAVELENGTH;
+/// The length of the wavelength spectrum used
+pub const SPECTRUM_SIZE: usize = MAX_WAVELENGTH - MIN_WAVELENGTH;
+/// The probability of picking a specific wavelength
+#[allow(clippy::cast_precision_loss)]
+pub const WAVELENGTH_PROBABILITY: Float = 1.0 / (SPECTRUM_SIZE as Float);
+/// The count of wavelenghts used per ray in Hero Wavelength Sampling
+pub const WAVE_SAMPLE_COUNT: usize = 4;
+
+/// Return a random wavelength, sampled uniformly from the visible spectrum.
+pub fn random_wavelength(rng: &mut SmallRng) -> Wavelength {
+ SPECTRUM.sample_single(rng)
+}
+
+// TODO: clippy fixes possible?
+/// Given a sample seed from a sampler, return the approximate wavelenght.
+#[must_use]
+#[allow(clippy::cast_possible_truncation)]
+#[allow(clippy::cast_sign_loss)]
+#[allow(clippy::cast_precision_loss)]
+pub fn sample_wavelength(sample: Float) -> Wavelength {
+ let pick = (sample * SPECTRUM_SIZE as Float).floor() as usize + MIN_WAVELENGTH;
+ assert!(pick <= MAX_WAVELENGTH);
+ assert!(pick >= MIN_WAVELENGTH);
+ pick
+}
+
+/// Given a hero wavelength, create additional equidistant wavelengths in the visible spectrum. Returns an array of wavelengths, with the original hero wavelength as the first one.
+#[must_use]
+pub fn rotate_wavelength(hero: Wavelength) -> [Wavelength; WAVE_SAMPLE_COUNT] {
+ from_fn(|j| {
+ (hero - MIN_WAVELENGTH + (j * SPECTRUM_SIZE / WAVE_SAMPLE_COUNT)) % SPECTRUM_SIZE
+ + MIN_WAVELENGTH
+ })
+}
+
+/// Helper function adapted from <https://en.wikipedia.org/wiki/CIE_1931_color_space#Analytical_approximation>
+fn gaussian(x: Float, alpha: Float, mu: Float, sigma1: Float, sigma2: Float) -> Float {
+ let t = (x - mu) / (if x < mu { sigma1 } else { sigma2 });
+ alpha * (-(t * t) / 2.0).exp()
+}
+
+/// Helper function adapted from <https://en.wikipedia.org/wiki/CIE_1931_color_space#Analytical_approximation>
+#[allow(clippy::cast_precision_loss)]
+#[must_use]
+pub fn wavelength_into_xyz(lambda: Wavelength) -> Xyz<E> {
+ // With the wavelength λ measured in nanometers, we then approximate the 1931 color matching functions:
+ let l: Float = lambda as Float;
+ let x = 0.0 // for readability of next lines
+ + gaussian(l, 1.056, 599.8, 37.9, 31.0)
+ + gaussian(l, 0.362, 442.0, 16.0, 26.7)
+ + gaussian(l, -0.065, 501.1, 20.4, 26.2);
+ let y = gaussian(l, 0.821, 568.8, 46.9, 40.5) + gaussian(l, 0.286, 530.9, 16.3, 31.1);
+ let z = gaussian(l, 1.217, 437.0, 11.8, 36.0) + gaussian(l, 0.681, 459.0, 26.0, 13.8);
+
+ // The functions above have been designed for the whitepoint E
+ Xyz::<E>::new(x, y, z)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +
//! An opinionated colorize method. Given a [Ray] and a [Scene], evaluates the ray's path and returns a color.
+
+use clovers::{
+ hitable::HitableTrait,
+ materials::MaterialType,
+ pdf::{HitablePDF, MixturePDF, PDFTrait, PDF},
+ ray::Ray,
+ scenes::Scene,
+ spectrum::spectrum_xyz_to_p,
+ wavelength::wavelength_into_xyz,
+ Float, EPSILON_SHADOW_ACNE,
+};
+use nalgebra::Unit;
+use palette::{
+ chromatic_adaptation::AdaptInto, convert::IntoColorUnclamped, white_point::E, Clamp, Xyz,
+};
+use rand::rngs::SmallRng;
+
+use crate::sampler::SamplerTrait;
+
+/// The main coloring function. Sends a [`Ray`] to the [`Scene`], sees if it hits anything, and eventually returns a color. Taking into account the [Material](clovers::materials::Material) that is hit, the method recurses with various adjustments, with a new [`Ray`] started from the location that was hit.
+#[must_use]
+#[allow(clippy::only_used_in_recursion)] // TODO: use sampler in more places!
+pub fn colorize(
+ ray: &Ray,
+ scene: &Scene,
+ depth: u32,
+ max_depth: u32,
+ rng: &mut SmallRng,
+ sampler: &dyn SamplerTrait,
+) -> Xyz<E> {
+ let bg: Xyz = scene.background_color.into_color_unclamped();
+ let bg: Xyz<E> = bg.adapt_into();
+ // Have we reached the maximum recursion i.e. ray bounce depth?
+ if depth > max_depth {
+ // Ray bounce limit reached, early return background_color
+ return bg;
+ }
+
+ // Send the ray to the scene, and see if it hits anything.
+ // distance_min is set to an epsilon to avoid "shadow acne" that can happen when set to zero
+ let Some(hit_record) = scene
+ .hitables
+ .hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng)
+ else {
+ // If the ray hits nothing, early return the background color.
+ return bg;
+ };
+
+ // Get the emitted color from the surface that we just hit
+ // TODO: spectral light sources!
+ let emitted = hit_record.material.emit(
+ ray,
+ &hit_record,
+ hit_record.u,
+ hit_record.v,
+ hit_record.position,
+ );
+ let tint: Xyz<E> = wavelength_into_xyz(ray.wavelength);
+ let emitted = emitted * tint;
+
+ // Do we scatter?
+ let Some(scatter_record) = hit_record.material.scatter(ray, &hit_record, rng) else {
+ // No scatter, early return the emitted color only
+ return emitted;
+ };
+ // We have scattered, and received an attenuation from the material.
+ let wavelength = ray.wavelength;
+ let attenuation_factor = spectrum_xyz_to_p(wavelength, scatter_record.attenuation);
+ let attenuation = (scatter_record.attenuation * attenuation_factor).clamp();
+
+ // Check the material type and recurse accordingly:
+ match scatter_record.material_type {
+ MaterialType::Specular => {
+ // If we hit a specular material, generate a specular ray, and multiply it with the attenuation
+ let specular = colorize(
+ // a scatter_record from a specular material should always have this ray
+ &scatter_record.specular_ray.unwrap(),
+ scene,
+ depth + 1,
+ max_depth,
+ rng,
+ sampler,
+ );
+ specular * attenuation
+ }
+ MaterialType::Diffuse => {
+ // Use a probability density function to figure out where to scatter a new ray
+ // TODO: this weighed priority sampling should be adjusted or removed - doesn't feel ideal.
+ let light_ptr = PDF::HitablePDF(HitablePDF::new(
+ &scene.priority_hitables,
+ hit_record.position,
+ ));
+ let mixture_pdf = MixturePDF::new(light_ptr, scatter_record.pdf_ptr);
+ let direction = mixture_pdf.generate(rng);
+ let direction = Unit::new_normalize(direction);
+ let scatter_ray = Ray {
+ origin: hit_record.position,
+ direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ };
+ let pdf_val = mixture_pdf.value(scatter_ray.direction, ray.wavelength, ray.time, rng);
+ if pdf_val <= 0.0 {
+ // scattering impossible, prevent division by zero below
+ // for more ctx, see https://github.com/RayTracing/raytracing.github.io/issues/979#issuecomment-1034517236
+ return emitted;
+ }
+
+ // Calculate the PDF weighting for the scatter // TODO: understand the literature for this, and explain
+ let Some(scattering_pdf) =
+ hit_record
+ .material
+ .scattering_pdf(&hit_record, &scatter_ray, rng)
+ else {
+ // No scatter, only emit
+ return emitted;
+ };
+
+ // Recurse for the scattering ray
+ let recurse = colorize(&scatter_ray, scene, depth + 1, max_depth, rng, sampler);
+ // Tint and weight it according to the PDF
+ let scattered = attenuation * scattering_pdf * recurse / pdf_val;
+ // Ensure positive color
+ // let scattered = scattered.non_negative();
+ // Blend it all together
+ emitted + scattered
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +
//! An opinionated method for drawing a scene using the CPU for rendering.
+
+use clovers::wavelength::random_wavelength;
+use clovers::Vec2;
+use clovers::{ray::Ray, scenes::Scene, Float, RenderOpts};
+use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
+use palette::chromatic_adaptation::AdaptInto;
+use palette::convert::IntoColorUnclamped;
+use palette::white_point::E;
+use palette::{LinSrgb, Srgb, Xyz};
+use rand::rngs::SmallRng;
+use rand::{Rng, SeedableRng};
+use rayon::prelude::*;
+
+use crate::colorize::colorize;
+use crate::normals::normal_map;
+use crate::sampler::blue::BlueSampler;
+use crate::sampler::random::RandomSampler;
+use crate::sampler::{Randomness, Sampler, SamplerTrait};
+
+/// The main drawing function, returns a `Vec<Srgb>` as a pixelbuffer.
+pub fn draw(opts: RenderOpts, scene: &Scene, sampler: Sampler) -> Vec<Srgb<u8>> {
+ let width = opts.width as usize;
+ let height = opts.height as usize;
+ let bar = progress_bar(&opts);
+
+ let pixelbuffer: Vec<Srgb<u8>> = (0..height)
+ .into_par_iter()
+ .map(|row_index| {
+ let mut sampler_rng = SmallRng::from_entropy();
+ let mut sampler: Box<dyn SamplerTrait> = match sampler {
+ Sampler::Blue => Box::new(BlueSampler::new(&opts)),
+ Sampler::Random => Box::new(RandomSampler::new(&mut sampler_rng)),
+ };
+
+ let mut rng = SmallRng::from_entropy();
+ let mut row = Vec::with_capacity(width);
+
+ for index in 0..width {
+ let index = index + row_index * width;
+ if opts.normalmap {
+ row.push(render_pixel_normalmap(scene, &opts, index, &mut rng));
+ } else {
+ row.push(render_pixel(scene, &opts, index, &mut rng, &mut *sampler));
+ }
+ }
+ bar.inc(1);
+ row
+ })
+ .flatten()
+ .collect();
+
+ pixelbuffer
+}
+
+// Render a single pixel, including possible multisampling
+fn render_pixel(
+ scene: &Scene,
+ opts: &RenderOpts,
+ index: usize,
+ rng: &mut SmallRng,
+ sampler: &mut dyn SamplerTrait,
+) -> Srgb<u8> {
+ let (x, y, width, height) = index_to_params(opts, index);
+ let pixel_location = Vec2::new(x, y);
+ let canvas_size = Vec2::new(width, height);
+ let max_depth = opts.max_depth;
+ let mut pixel_color: Xyz<E> = Xyz::new(0.0, 0.0, 0.0);
+ for sample in 0..opts.samples {
+ let Randomness {
+ pixel_offset,
+ lens_offset,
+ time,
+ wavelength,
+ } = sampler.sample(x as i32, y as i32, sample as i32);
+ let pixel_uv: Vec2 = Vec2::new(
+ (pixel_location.x + pixel_offset.x) / canvas_size.x,
+ (pixel_location.y + pixel_offset.y) / canvas_size.y,
+ );
+ // note get_ray wants uv 0..1 location
+ let ray: Ray = scene
+ .camera
+ .get_ray(pixel_uv, lens_offset, time, wavelength);
+ let sample_color: Xyz<E> = colorize(&ray, scene, 0, max_depth, rng, sampler);
+ if sample_color.x.is_finite() && sample_color.y.is_finite() && sample_color.z.is_finite() {
+ pixel_color += sample_color;
+ }
+ }
+ pixel_color /= opts.samples as Float;
+ let color: Srgb = pixel_color.adapt_into();
+ let color: Srgb<u8> = color.into_format();
+ color
+}
+
+// Render a single pixel in normalmap mode
+fn render_pixel_normalmap(
+ scene: &Scene,
+ opts: &RenderOpts,
+ index: usize,
+ rng: &mut SmallRng,
+) -> Srgb<u8> {
+ let (x, y, width, height) = index_to_params(opts, index);
+ let color: LinSrgb = {
+ let pixel_location = Vec2::new(x / width, y / height);
+ let lens_offset = Vec2::new(0.0, 0.0);
+ let wavelength = random_wavelength(rng);
+ let time = rng.gen();
+ let ray: Ray = scene
+ .camera
+ .get_ray(pixel_location, lens_offset, time, wavelength);
+ normal_map(&ray, scene, rng)
+ };
+ let color: Srgb = color.into_color_unclamped();
+ let color: Srgb<u8> = color.into_format();
+ color
+}
+
+fn index_to_params(opts: &RenderOpts, index: usize) -> (Float, Float, Float, Float) {
+ let x = (index % (opts.width as usize)) as Float;
+ let y = (index / (opts.width as usize)) as Float;
+ let width = opts.width as Float;
+ let height = opts.height as Float;
+ (x, y, width, height)
+}
+
+fn progress_bar(opts: &RenderOpts) -> ProgressBar {
+ let bar = ProgressBar::new(opts.height as u64);
+ if opts.quiet {
+ bar.set_draw_target(ProgressDrawTarget::hidden())
+ } else {
+ bar.set_style(ProgressStyle::default_bar().template(
+ "Elapsed: {elapsed_precise}\nRows: {bar} {pos}/{len}\nRemaining: {eta_precise}",
+ ).unwrap());
+ }
+ bar
+}
+
use clovers::scenes::{self, Scene, SceneFile};
+use std::error::Error;
+use std::fs::File;
+use std::io::Read;
+use std::path::Path;
+
+use tracing::info;
+
+pub(crate) fn initialize<'scene>(
+ path: &Path,
+ width: u32,
+ height: u32,
+) -> Result<Scene<'scene>, Box<dyn Error>> {
+ let mut file = File::open(path)?;
+ let mut contents: String = String::new();
+ file.read_to_string(&mut contents)?;
+ info!("Parsing the scene file");
+ let scene_file: SceneFile = serde_json::from_str(&contents)?;
+ info!("Initializing the scene");
+ let scene: Scene = scenes::initialize(scene_file, width, height);
+ info!("Count of nodes in the BVH tree: {}", scene.hitables.count());
+ Ok(scene)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +
//! Command Line Interface for the `clovers` raytracing renderer.
+
+#![deny(clippy::all)]
+
+// External imports
+use clap::Parser;
+use humantime::format_duration;
+use image::{ImageBuffer, ImageOutputFormat, Rgb, RgbImage};
+use img_parts::png::{Png, PngChunk};
+use std::fs::File;
+use std::io::Cursor;
+use std::path::Path;
+use std::{error::Error, fs, time::Instant};
+use time::OffsetDateTime;
+use tracing::{debug, info, Level};
+use tracing_subscriber::fmt::time::UtcTime;
+
+// Internal imports
+use clovers::*;
+#[doc(hidden)]
+mod colorize;
+#[doc(hidden)]
+mod draw_cpu;
+#[doc(hidden)]
+mod json_scene;
+#[doc(hidden)]
+pub mod normals;
+#[doc(hidden)]
+mod sampler;
+use sampler::Sampler;
+
+/// Command line parameters for the `clovers` raytracing renderer.
+#[derive(Parser)]
+#[clap(version = "0.1.0", author = "Walther", name = "clovers")]
+pub struct Opts {
+ /// Input filename / location
+ #[clap(short, long)]
+ input: String,
+ /// Output filename / location. Default: renders/unix_timestamp.png
+ #[clap(short, long)]
+ output: Option<String>,
+ /// Width of the image in pixels. Default: 1024
+ #[clap(short, long, default_value = "1024")]
+ width: u32,
+ /// Height of the image in pixels. Default: 1024
+ #[clap(short, long, default_value = "1024")]
+ height: u32,
+ /// Number of samples to generate per each pixel. Default: 64
+ #[clap(short, long, default_value = "64")]
+ samples: u32,
+ /// Maximum evaluated bounce depth for each ray. Default: 64
+ #[clap(short = 'd', long, default_value = "64")]
+ max_depth: u32,
+ /// Suppress most of the text output
+ #[clap(short, long)]
+ quiet: bool,
+ /// Use the GPU draw process instead of CPU
+ #[clap(long)]
+ gpu: bool,
+ /// Enable some debug logging
+ #[clap(long)]
+ debug: bool,
+ /// Render a normal map only. Experimental feature.
+ #[clap(long)]
+ normalmap: bool,
+ /// Sampler to use for rendering. Experimental feature.
+ #[clap(long, default_value = "random")]
+ sampler: Sampler,
+}
+
+#[doc(hidden)]
+fn main() -> Result<(), Box<dyn Error>> {
+ let Opts {
+ input,
+ output,
+ width,
+ height,
+ samples,
+ max_depth,
+ quiet,
+ gpu,
+ debug,
+ normalmap,
+ sampler,
+ } = Opts::parse();
+
+ if debug {
+ tracing_subscriber::fmt()
+ .with_max_level(Level::DEBUG)
+ .with_timer(UtcTime::rfc_3339())
+ .init();
+ debug!("Debug logging enabled");
+ } else {
+ tracing_subscriber::fmt()
+ .with_max_level(Level::ERROR)
+ .with_timer(UtcTime::rfc_3339())
+ .init();
+ }
+
+ // Pretty printing output, unless in quiet mode
+ if !quiet {
+ println!("clovers 🍀 path tracing renderer");
+ println!();
+ println!("{width}x{height} resolution");
+ println!("{samples} samples per pixel");
+ println!("using the {sampler} sampler");
+ println!("{max_depth} max bounce depth");
+ println!(); // Empty line before progress bar
+ }
+
+ if sampler == Sampler::Blue && !([1, 2, 4, 8, 16, 32, 64, 128, 256].contains(&samples)) {
+ panic!("the blue sampler only supports the following sample-per-pixel counts: [1, 2, 4, 8, 16, 32, 64, 128, 256]");
+ }
+
+ let renderopts: RenderOpts = RenderOpts {
+ width,
+ height,
+ samples,
+ max_depth,
+ quiet,
+ normalmap,
+ };
+ let threads = std::thread::available_parallelism()?;
+
+ info!("Reading the scene file");
+ let path = Path::new(&input);
+ let scene = match path.extension() {
+ Some(ext) => match &ext.to_str() {
+ Some("json") => json_scene::initialize(path, width, height),
+ _ => panic!("Unknown file type"),
+ },
+ None => panic!("Unknown file type"),
+ }?;
+
+ info!("Calling draw()");
+ let start = Instant::now();
+ let pixelbuffer = match gpu {
+ // Note: live progress bar printed within draw_cpu::draw
+ false => draw_cpu::draw(renderopts, &scene, sampler),
+ true => unimplemented!("GPU accelerated rendering is currently unimplemented"),
+ };
+ info!("Drawing a pixelbuffer finished");
+
+ info!("Converting pixelbuffer to an image");
+ let mut img: RgbImage = ImageBuffer::new(width, height);
+ img.enumerate_pixels_mut().for_each(|(x, y, pixel)| {
+ let index = y * width + x;
+ *pixel = Rgb(pixelbuffer[index as usize].into());
+ });
+
+ // Graphics assume origin at bottom left corner of the screen
+ // Our buffer writes pixels from top left corner. Simple fix, just flip it!
+ image::imageops::flip_vertical_in_place(&mut img);
+ // TODO: fix the coordinate system
+
+ let duration = Instant::now() - start;
+ let formatted_duration = format_duration(duration);
+ info!("Finished render in {}", formatted_duration);
+
+ if !quiet {
+ println!("Finished render in {}", formatted_duration);
+ }
+
+ info!("Writing an image file");
+
+ let mut bytes: Vec<u8> = Vec::new();
+ img.write_to(&mut Cursor::new(&mut bytes), ImageOutputFormat::Png)?;
+ let mut png = Png::from_bytes(bytes.into())?;
+
+ let comment = if normalmap {
+ format!("Comment\0{input} rendered with the clovers raytracing engine at {width}x{height} in normalmap mode. finished render in {formatted_duration}, using {threads} threads")
+ } else {
+ format!("Comment\0{input} rendered with the clovers raytracing engine at {width}x{height}, {samples} samples per pixel, {max_depth} max ray bounce depth. finished render in {formatted_duration}, using {threads} threads")
+ };
+ let software = "Software\0https://github.com/walther/clovers".to_string();
+
+ for metadata in [comment, software] {
+ let bytes = metadata.as_bytes().to_owned();
+ let chunk = PngChunk::new([b't', b'E', b'X', b't'], bytes.into());
+ png.chunks_mut().push(chunk);
+ }
+
+ let target = match output {
+ Some(filename) => filename,
+ None => {
+ // Default to using a timestamp & `renders/` directory
+ let timestamp = OffsetDateTime::now_utc().unix_timestamp();
+ fs::create_dir_all("renders")?;
+ format!("renders/{}.png", timestamp)
+ }
+ };
+
+ let output = File::create(&target)?;
+ png.encoder().write_to(output)?;
+
+ info!("Image saved to {}", target);
+ println!("Image saved to: {}", target);
+
+ Ok(())
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +
//! Alternative rendering method. Only returns a normalmap of the image, colorized using standard conventions.
+
+use clovers::{
+ hitable::HitableTrait, ray::Ray, scenes::Scene, Direction, Float, Vec3, EPSILON_SHADOW_ACNE,
+};
+use palette::LinSrgb;
+use rand::rngs::SmallRng;
+
+/// Rendering function for getting a normal map in tangent space. Sends a [Ray] to the [Scene], sees what it hits, gets the normal at that point, and returns a color based on the normal mapping colorization. Wikipedia: [Normal mapping](https://en.wikipedia.org/wiki/Normal_mapping).
+#[must_use]
+pub fn normal_map(ray: &Ray, scene: &Scene, rng: &mut SmallRng) -> LinSrgb {
+ let Some(hit_record) = scene
+ .hitables
+ .hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng)
+ else {
+ // If the ray hits nothing, early return black
+ return LinSrgb::new(0.0, 0.0, 0.0);
+ };
+
+ let normal: Direction = hit_record.normal;
+ normal_to_color(normal)
+}
+
+/// Given a surface normal, return a color based on normal mapping colorization.
+#[must_use]
+pub fn normal_to_color(normal: Direction) -> LinSrgb {
+ // flip the Z and X axes because the wikipedia example uses left-handed coordinate system and my renderer uses a right-handed one for some reason.
+ // TODO: figure out a good coordinate system to use... See also https://twitter.com/FreyaHolmer/status/1325556229410861056
+ let normal: Vec3 = Vec3::new(-normal.x, normal.y, -normal.z);
+ // TODO: verify correctness
+ let r = 0.5 + 0.5 * normal.x; // X -1 to 1 = 0.0 to 1.0
+ let g = 0.5 + 0.5 * normal.y; // Y -1 to 1 = 0.0 to 1.0
+ // Z 0 to 1 = 0.0 to 1.0
+ let b = if normal.z < 0.0 { 0.0 } else { normal.z };
+
+ LinSrgb::new(r, g, b)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +
//! Sampler architecture for the renderer, based on the sampling infrastructure described in the book Physically Based Rendering, chapter [8.3 Sampling Interface](https://pbr-book.org/4ed/Sampling_and_Reconstruction/Sampling_Interface#Sampler)
+
+use std::fmt::Display;
+
+use clap::ValueEnum;
+use clovers::{wavelength::Wavelength, Float, Vec2};
+
+pub mod blue;
+pub mod random;
+
+pub trait SamplerTrait<'scene> {
+ // TODO: better types
+ fn sample(&mut self, i: i32, j: i32, index: i32) -> Randomness;
+
+ /// Manually request a sample from the specific dimension
+ #[allow(dead_code)] // TODO: remove
+ fn sample_dimension(
+ &mut self,
+ i: i32,
+ j: i32,
+ index: i32,
+ dimension: SamplerDimension,
+ ) -> Float;
+}
+
+/// A collection of random values to be used for each sample. Returned as a struct to ensure the correct sampling order for the underlying source of randomness.
+pub struct Randomness {
+ /// Intra-pixel `(x,y)` offset, both in range `[0..1]`. Used for antialiasing.
+ pub pixel_offset: Vec2,
+ /// The `(x,y)` offset used in the lens equations for aperture / depth-of-field simulation. The coordinates are within the range `[-0.5..0.5]` and within a unit disk.
+ pub lens_offset: Vec2,
+ /// The time of the ray, in range `[0..1]`
+ pub time: Float,
+ /// Wavelength of the ray
+ pub wavelength: Wavelength,
+}
+
+/// Enum of the supported samplers.
+#[derive(Clone, Debug, PartialEq, ValueEnum)]
+pub enum Sampler {
+ /// Blue noise based sampler, see [BlueSampler](blue::BlueSampler)
+ Blue,
+ /// Random number generator based sampler, see [RandomSampler](random::RandomSampler)
+ Random,
+}
+
+impl Display for Sampler {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let s = match self {
+ Sampler::Blue => "blue",
+ Sampler::Random => "random",
+ };
+ write!(f, "{s}")
+ }
+}
+
+/// Various sampling dimensions used by the samplers
+#[derive(Clone, Copy)]
+pub enum SamplerDimension {
+ PixelOffsetX,
+ PixelOffsetY,
+ LensOffsetX,
+ LensOffsetY,
+ Time,
+ Wavelength,
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +
//! A sampler based on blue noise. Works especially well at low samples-per-pixel counts.
+//!
+//! Utilizes library code from <https://github.com/Jasper-Bekkers/blue-noise-sampler>.
+
+use clovers::{wavelength::sample_wavelength, Float, RenderOpts, Vec2, PI};
+
+use super::*;
+
+pub struct BlueSampler {
+ get: fn(i32, i32, i32, SamplerDimension) -> Float,
+}
+
+impl<'scene> BlueSampler {
+ pub fn new(opts: &'scene RenderOpts) -> Self {
+ let get = match opts.samples {
+ 1 => blue_sample_spp1,
+ 2 => blue_sample_spp2,
+ 4 => blue_sample_spp4,
+ 8 => blue_sample_spp8,
+ 16 => blue_sample_spp16,
+ 32 => blue_sample_spp32,
+ 64 => blue_sample_spp64,
+ 128 => blue_sample_spp128,
+ 256 => blue_sample_spp256,
+ _ => unimplemented!(
+ "blue sampler only supports sample-per-pixel counts that are powers of two of and up to 256"
+ ),
+ };
+ Self { get }
+ }
+}
+
+impl<'scene> SamplerTrait<'scene> for BlueSampler {
+ fn sample(&mut self, i: i32, j: i32, index: i32) -> Randomness {
+ let pixel_offset = Vec2::new(
+ (self.get)(i, j, index, SamplerDimension::PixelOffsetX),
+ (self.get)(i, j, index, SamplerDimension::PixelOffsetY),
+ );
+ let lens_offset = in_unit_disk(
+ (self.get)(i, j, index, SamplerDimension::LensOffsetX),
+ (self.get)(i, j, index, SamplerDimension::LensOffsetY),
+ );
+ let time = (self.get)(i, j, index, SamplerDimension::Time);
+ // TODO: verify uniformity & correctness for math?
+ let wavelength = sample_wavelength((self.get)(i, j, index, SamplerDimension::Wavelength));
+
+ Randomness {
+ pixel_offset,
+ lens_offset,
+ time,
+ wavelength,
+ }
+ }
+
+ fn sample_dimension(
+ &mut self,
+ i: i32,
+ j: i32,
+ index: i32,
+ dimension: SamplerDimension,
+ ) -> Float {
+ (self.get)(i, j, index, dimension)
+ }
+}
+
+macro_rules! define_blue_sampler {
+ ($spp:ident) => {
+ ::paste::paste! {
+ pub fn [<blue_sample_ $spp>](
+ mut pixel_i: i32,
+ mut pixel_j: i32,
+ mut sample_index: i32,
+ sample_dimension: SamplerDimension) -> Float {
+ let mut sample_dimension = sample_dimension as i32;
+
+ use blue_noise_sampler::$spp::*;
+
+ // Adapted from <https://dl.acm.org/doi/10.1145/3306307.3328191> and <https://github.com/Jasper-Bekkers/blue-noise-sampler>
+
+ // wrap arguments
+ pixel_i &= 127;
+ pixel_j &= 127;
+ sample_index &= 255;
+ sample_dimension &= 255;
+
+ // xor index based on optimized ranking
+ // jb: 1spp blue noise has all 0 in g_blueNoiseRankingTile so we can skip the load
+ let index = sample_dimension + (pixel_i + pixel_j * 128) * 8;
+ let index = index as usize;
+ let ranked_sample_index = sample_index ^ RANKING_TILE[index];
+
+ // fetch value in sequence
+ let index = sample_dimension + ranked_sample_index * 256;
+ let index = index as usize;
+ let value = SOBOL[index];
+
+ // If the dimension is optimized, xor sequence value based on optimized scrambling
+ let index = (sample_dimension % 8) + (pixel_i + pixel_j * 128) * 8;
+ let index = index as usize;
+ let value = value ^ SCRAMBLING_TILE[index];
+
+ // convert to float and return
+ let v: Float = (0.5 + value as Float) / 256.0;
+ v
+ }
+ }
+ };
+}
+
+define_blue_sampler!(spp1);
+define_blue_sampler!(spp2);
+define_blue_sampler!(spp4);
+define_blue_sampler!(spp8);
+define_blue_sampler!(spp16);
+define_blue_sampler!(spp32);
+define_blue_sampler!(spp64);
+define_blue_sampler!(spp128);
+define_blue_sampler!(spp256);
+
+/// Given two samples in range `[0..1]`, return a sample within `[-0.5 .. 0.5]` unit disk.
+/// Based on <https://stackoverflow.com/a/50746409>
+fn in_unit_disk(x: Float, y: Float) -> Vec2 {
+ // Polar coordinates + correcting for the distribution using sqrt
+ let r = x.sqrt();
+ let theta = y * 2.0 * PI;
+ // Conversion to Cartesian coordinates
+ let x = r * theta.cos();
+ let y = r * theta.sin();
+ Vec2::new(x, y)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +
//! A sampler based on a random number generator. This is the default sampler used in this renderer. It works especially well at high samples-per-pixel counts.
+
+use clovers::{random::random_in_unit_disk, wavelength::random_wavelength, Vec2};
+use rand::{rngs::SmallRng, Rng};
+
+use super::{Randomness, SamplerTrait};
+
+#[derive(Debug)]
+pub struct RandomSampler<'scene> {
+ rng: &'scene mut SmallRng,
+}
+
+impl<'scene> RandomSampler<'scene> {
+ pub fn new(rng: &'scene mut SmallRng) -> Self {
+ Self { rng }
+ }
+}
+
+impl<'scene> SamplerTrait<'scene> for RandomSampler<'scene> {
+ fn sample(&mut self, _i: i32, _j: i32, _index: i32) -> Randomness {
+ let pixel_offset = Vec2::new(self.rng.gen(), self.rng.gen());
+ let lens_offset = random_in_unit_disk(self.rng);
+ let time = self.rng.gen();
+ let wavelength = random_wavelength(self.rng);
+
+ Randomness {
+ pixel_offset,
+ lens_offset,
+ time,
+ wavelength,
+ }
+ }
+
+ fn sample_dimension(
+ &mut self,
+ _i: i32,
+ _j: i32,
+ _index: i32,
+ _dimension: super::SamplerDimension,
+ ) -> clovers::Float {
+ self.rng.gen()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +
//! An opinionated colorize method. Given a [Ray] and a [Scene], evaluates the ray's path and returns a color.
+
+use clovers::{
+ hitable::HitableTrait,
+ materials::MaterialType,
+ pdf::{HitablePDF, MixturePDF, PDFTrait, PDF},
+ ray::Ray,
+ scenes::Scene,
+ spectrum::spectrum_xyz_to_p,
+ wavelength::wavelength_into_xyz,
+ Float, EPSILON_SHADOW_ACNE,
+};
+use nalgebra::Unit;
+use palette::{
+ chromatic_adaptation::AdaptInto, convert::IntoColorUnclamped, white_point::E, Clamp, Xyz,
+};
+use rand::rngs::SmallRng;
+
+use crate::sampler::SamplerTrait;
+
+/// The main coloring function. Sends a [`Ray`] to the [`Scene`], sees if it hits anything, and eventually returns a color. Taking into account the [Material](clovers::materials::Material) that is hit, the method recurses with various adjustments, with a new [`Ray`] started from the location that was hit.
+#[must_use]
+#[allow(clippy::only_used_in_recursion)] // TODO: use sampler in more places!
+pub fn colorize(
+ ray: &Ray,
+ scene: &Scene,
+ depth: u32,
+ max_depth: u32,
+ rng: &mut SmallRng,
+ sampler: &dyn SamplerTrait,
+) -> Xyz<E> {
+ let bg: Xyz = scene.background_color.into_color_unclamped();
+ let bg: Xyz<E> = bg.adapt_into();
+ // Have we reached the maximum recursion i.e. ray bounce depth?
+ if depth > max_depth {
+ // Ray bounce limit reached, early return background_color
+ return bg;
+ }
+
+ // Send the ray to the scene, and see if it hits anything.
+ // distance_min is set to an epsilon to avoid "shadow acne" that can happen when set to zero
+ let Some(hit_record) = scene
+ .hitables
+ .hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng)
+ else {
+ // If the ray hits nothing, early return the background color.
+ return bg;
+ };
+
+ // Get the emitted color from the surface that we just hit
+ // TODO: spectral light sources!
+ let emitted = hit_record.material.emit(
+ ray,
+ &hit_record,
+ hit_record.u,
+ hit_record.v,
+ hit_record.position,
+ );
+ let tint: Xyz<E> = wavelength_into_xyz(ray.wavelength);
+ let emitted = emitted * tint;
+
+ // Do we scatter?
+ let Some(scatter_record) = hit_record.material.scatter(ray, &hit_record, rng) else {
+ // No scatter, early return the emitted color only
+ return emitted;
+ };
+ // We have scattered, and received an attenuation from the material.
+ let wavelength = ray.wavelength;
+ let attenuation_factor = spectrum_xyz_to_p(wavelength, scatter_record.attenuation);
+ let attenuation = (scatter_record.attenuation * attenuation_factor).clamp();
+
+ // Check the material type and recurse accordingly:
+ match scatter_record.material_type {
+ MaterialType::Specular => {
+ // If we hit a specular material, generate a specular ray, and multiply it with the attenuation
+ let specular = colorize(
+ // a scatter_record from a specular material should always have this ray
+ &scatter_record.specular_ray.unwrap(),
+ scene,
+ depth + 1,
+ max_depth,
+ rng,
+ sampler,
+ );
+ specular * attenuation
+ }
+ MaterialType::Diffuse => {
+ // Use a probability density function to figure out where to scatter a new ray
+ // TODO: this weighed priority sampling should be adjusted or removed - doesn't feel ideal.
+ let light_ptr = PDF::HitablePDF(HitablePDF::new(
+ &scene.priority_hitables,
+ hit_record.position,
+ ));
+ let mixture_pdf = MixturePDF::new(light_ptr, scatter_record.pdf_ptr);
+ let direction = mixture_pdf.generate(rng);
+ let direction = Unit::new_normalize(direction);
+ let scatter_ray = Ray {
+ origin: hit_record.position,
+ direction,
+ time: ray.time,
+ wavelength: ray.wavelength,
+ };
+ let pdf_val = mixture_pdf.value(scatter_ray.direction, ray.wavelength, ray.time, rng);
+ if pdf_val <= 0.0 {
+ // scattering impossible, prevent division by zero below
+ // for more ctx, see https://github.com/RayTracing/raytracing.github.io/issues/979#issuecomment-1034517236
+ return emitted;
+ }
+
+ // Calculate the PDF weighting for the scatter // TODO: understand the literature for this, and explain
+ let Some(scattering_pdf) =
+ hit_record
+ .material
+ .scattering_pdf(&hit_record, &scatter_ray, rng)
+ else {
+ // No scatter, only emit
+ return emitted;
+ };
+
+ // Recurse for the scattering ray
+ let recurse = colorize(&scatter_ray, scene, depth + 1, max_depth, rng, sampler);
+ // Tint and weight it according to the PDF
+ let scattered = attenuation * scattering_pdf * recurse / pdf_val;
+ // Ensure positive color
+ // let scattered = scattered.non_negative();
+ // Blend it all together
+ emitted + scattered
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +
//! An opinionated method for drawing a scene using the CPU for rendering.
+
+use clovers::wavelength::random_wavelength;
+use clovers::Vec2;
+use clovers::{ray::Ray, scenes::Scene, Float, RenderOpts};
+use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
+use palette::chromatic_adaptation::AdaptInto;
+use palette::convert::IntoColorUnclamped;
+use palette::white_point::E;
+use palette::{LinSrgb, Srgb, Xyz};
+use rand::rngs::SmallRng;
+use rand::{Rng, SeedableRng};
+use rayon::prelude::*;
+
+use crate::colorize::colorize;
+use crate::normals::normal_map;
+use crate::sampler::blue::BlueSampler;
+use crate::sampler::random::RandomSampler;
+use crate::sampler::{Randomness, Sampler, SamplerTrait};
+
+/// The main drawing function, returns a `Vec<Srgb>` as a pixelbuffer.
+pub fn draw(opts: RenderOpts, scene: &Scene, sampler: Sampler) -> Vec<Srgb<u8>> {
+ let width = opts.width as usize;
+ let height = opts.height as usize;
+ let bar = progress_bar(&opts);
+
+ let pixelbuffer: Vec<Srgb<u8>> = (0..height)
+ .into_par_iter()
+ .map(|row_index| {
+ let mut sampler_rng = SmallRng::from_entropy();
+ let mut sampler: Box<dyn SamplerTrait> = match sampler {
+ Sampler::Blue => Box::new(BlueSampler::new(&opts)),
+ Sampler::Random => Box::new(RandomSampler::new(&mut sampler_rng)),
+ };
+
+ let mut rng = SmallRng::from_entropy();
+ let mut row = Vec::with_capacity(width);
+
+ for index in 0..width {
+ let index = index + row_index * width;
+ if opts.normalmap {
+ row.push(render_pixel_normalmap(scene, &opts, index, &mut rng));
+ } else {
+ row.push(render_pixel(scene, &opts, index, &mut rng, &mut *sampler));
+ }
+ }
+ bar.inc(1);
+ row
+ })
+ .flatten()
+ .collect();
+
+ pixelbuffer
+}
+
+// Render a single pixel, including possible multisampling
+fn render_pixel(
+ scene: &Scene,
+ opts: &RenderOpts,
+ index: usize,
+ rng: &mut SmallRng,
+ sampler: &mut dyn SamplerTrait,
+) -> Srgb<u8> {
+ let (x, y, width, height) = index_to_params(opts, index);
+ let pixel_location = Vec2::new(x, y);
+ let canvas_size = Vec2::new(width, height);
+ let max_depth = opts.max_depth;
+ let mut pixel_color: Xyz<E> = Xyz::new(0.0, 0.0, 0.0);
+ for sample in 0..opts.samples {
+ let Randomness {
+ pixel_offset,
+ lens_offset,
+ time,
+ wavelength,
+ } = sampler.sample(x as i32, y as i32, sample as i32);
+ let pixel_uv: Vec2 = Vec2::new(
+ (pixel_location.x + pixel_offset.x) / canvas_size.x,
+ (pixel_location.y + pixel_offset.y) / canvas_size.y,
+ );
+ // note get_ray wants uv 0..1 location
+ let ray: Ray = scene
+ .camera
+ .get_ray(pixel_uv, lens_offset, time, wavelength);
+ let sample_color: Xyz<E> = colorize(&ray, scene, 0, max_depth, rng, sampler);
+ if sample_color.x.is_finite() && sample_color.y.is_finite() && sample_color.z.is_finite() {
+ pixel_color += sample_color;
+ }
+ }
+ pixel_color /= opts.samples as Float;
+ let color: Srgb = pixel_color.adapt_into();
+ let color: Srgb<u8> = color.into_format();
+ color
+}
+
+// Render a single pixel in normalmap mode
+fn render_pixel_normalmap(
+ scene: &Scene,
+ opts: &RenderOpts,
+ index: usize,
+ rng: &mut SmallRng,
+) -> Srgb<u8> {
+ let (x, y, width, height) = index_to_params(opts, index);
+ let color: LinSrgb = {
+ let pixel_location = Vec2::new(x / width, y / height);
+ let lens_offset = Vec2::new(0.0, 0.0);
+ let wavelength = random_wavelength(rng);
+ let time = rng.gen();
+ let ray: Ray = scene
+ .camera
+ .get_ray(pixel_location, lens_offset, time, wavelength);
+ normal_map(&ray, scene, rng)
+ };
+ let color: Srgb = color.into_color_unclamped();
+ let color: Srgb<u8> = color.into_format();
+ color
+}
+
+fn index_to_params(opts: &RenderOpts, index: usize) -> (Float, Float, Float, Float) {
+ let x = (index % (opts.width as usize)) as Float;
+ let y = (index / (opts.width as usize)) as Float;
+ let width = opts.width as Float;
+ let height = opts.height as Float;
+ (x, y, width, height)
+}
+
+fn progress_bar(opts: &RenderOpts) -> ProgressBar {
+ let bar = ProgressBar::new(opts.height as u64);
+ if opts.quiet {
+ bar.set_draw_target(ProgressDrawTarget::hidden())
+ } else {
+ bar.set_style(ProgressStyle::default_bar().template(
+ "Elapsed: {elapsed_precise}\nRows: {bar} {pos}/{len}\nRemaining: {eta_precise}",
+ ).unwrap());
+ }
+ bar
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +
//! Alternative rendering method. Only returns a normalmap of the image, colorized using standard conventions.
+
+use clovers::{
+ hitable::HitableTrait, ray::Ray, scenes::Scene, Direction, Float, Vec3, EPSILON_SHADOW_ACNE,
+};
+use palette::LinSrgb;
+use rand::rngs::SmallRng;
+
+/// Rendering function for getting a normal map in tangent space. Sends a [Ray] to the [Scene], sees what it hits, gets the normal at that point, and returns a color based on the normal mapping colorization. Wikipedia: [Normal mapping](https://en.wikipedia.org/wiki/Normal_mapping).
+#[must_use]
+pub fn normal_map(ray: &Ray, scene: &Scene, rng: &mut SmallRng) -> LinSrgb {
+ let Some(hit_record) = scene
+ .hitables
+ .hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng)
+ else {
+ // If the ray hits nothing, early return black
+ return LinSrgb::new(0.0, 0.0, 0.0);
+ };
+
+ let normal: Direction = hit_record.normal;
+ normal_to_color(normal)
+}
+
+/// Given a surface normal, return a color based on normal mapping colorization.
+#[must_use]
+pub fn normal_to_color(normal: Direction) -> LinSrgb {
+ // flip the Z and X axes because the wikipedia example uses left-handed coordinate system and my renderer uses a right-handed one for some reason.
+ // TODO: figure out a good coordinate system to use... See also https://twitter.com/FreyaHolmer/status/1325556229410861056
+ let normal: Vec3 = Vec3::new(-normal.x, normal.y, -normal.z);
+ // TODO: verify correctness
+ let r = 0.5 + 0.5 * normal.x; // X -1 to 1 = 0.0 to 1.0
+ let g = 0.5 + 0.5 * normal.y; // Y -1 to 1 = 0.0 to 1.0
+ // Z 0 to 1 = 0.0 to 1.0
+ let b = if normal.z < 0.0 { 0.0 } else { normal.z };
+
+ LinSrgb::new(r, g, b)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +
//! Sampler architecture for the renderer, based on the sampling infrastructure described in the book Physically Based Rendering, chapter [8.3 Sampling Interface](https://pbr-book.org/4ed/Sampling_and_Reconstruction/Sampling_Interface#Sampler)
+
+use std::fmt::Display;
+
+use clap::ValueEnum;
+use clovers::{wavelength::Wavelength, Float, Vec2};
+
+pub mod blue;
+pub mod random;
+
+pub trait SamplerTrait<'scene> {
+ // TODO: better types
+ fn sample(&mut self, i: i32, j: i32, index: i32) -> Randomness;
+
+ /// Manually request a sample from the specific dimension
+ #[allow(dead_code)] // TODO: remove
+ fn sample_dimension(
+ &mut self,
+ i: i32,
+ j: i32,
+ index: i32,
+ dimension: SamplerDimension,
+ ) -> Float;
+}
+
+/// A collection of random values to be used for each sample. Returned as a struct to ensure the correct sampling order for the underlying source of randomness.
+pub struct Randomness {
+ /// Intra-pixel `(x,y)` offset, both in range `[0..1]`. Used for antialiasing.
+ pub pixel_offset: Vec2,
+ /// The `(x,y)` offset used in the lens equations for aperture / depth-of-field simulation. The coordinates are within the range `[-0.5..0.5]` and within a unit disk.
+ pub lens_offset: Vec2,
+ /// The time of the ray, in range `[0..1]`
+ pub time: Float,
+ /// Wavelength of the ray
+ pub wavelength: Wavelength,
+}
+
+/// Enum of the supported samplers.
+#[derive(Clone, Debug, PartialEq, ValueEnum)]
+pub enum Sampler {
+ /// Blue noise based sampler, see [BlueSampler](blue::BlueSampler)
+ Blue,
+ /// Random number generator based sampler, see [RandomSampler](random::RandomSampler)
+ Random,
+}
+
+impl Display for Sampler {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let s = match self {
+ Sampler::Blue => "blue",
+ Sampler::Random => "random",
+ };
+ write!(f, "{s}")
+ }
+}
+
+/// Various sampling dimensions used by the samplers
+#[derive(Clone, Copy)]
+pub enum SamplerDimension {
+ PixelOffsetX,
+ PixelOffsetY,
+ LensOffsetX,
+ LensOffsetY,
+ Time,
+ Wavelength,
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +
//! A sampler based on blue noise. Works especially well at low samples-per-pixel counts.
+//!
+//! Utilizes library code from <https://github.com/Jasper-Bekkers/blue-noise-sampler>.
+
+use clovers::{wavelength::sample_wavelength, Float, RenderOpts, Vec2, PI};
+
+use super::*;
+
+pub struct BlueSampler {
+ get: fn(i32, i32, i32, SamplerDimension) -> Float,
+}
+
+impl<'scene> BlueSampler {
+ pub fn new(opts: &'scene RenderOpts) -> Self {
+ let get = match opts.samples {
+ 1 => blue_sample_spp1,
+ 2 => blue_sample_spp2,
+ 4 => blue_sample_spp4,
+ 8 => blue_sample_spp8,
+ 16 => blue_sample_spp16,
+ 32 => blue_sample_spp32,
+ 64 => blue_sample_spp64,
+ 128 => blue_sample_spp128,
+ 256 => blue_sample_spp256,
+ _ => unimplemented!(
+ "blue sampler only supports sample-per-pixel counts that are powers of two of and up to 256"
+ ),
+ };
+ Self { get }
+ }
+}
+
+impl<'scene> SamplerTrait<'scene> for BlueSampler {
+ fn sample(&mut self, i: i32, j: i32, index: i32) -> Randomness {
+ let pixel_offset = Vec2::new(
+ (self.get)(i, j, index, SamplerDimension::PixelOffsetX),
+ (self.get)(i, j, index, SamplerDimension::PixelOffsetY),
+ );
+ let lens_offset = in_unit_disk(
+ (self.get)(i, j, index, SamplerDimension::LensOffsetX),
+ (self.get)(i, j, index, SamplerDimension::LensOffsetY),
+ );
+ let time = (self.get)(i, j, index, SamplerDimension::Time);
+ // TODO: verify uniformity & correctness for math?
+ let wavelength = sample_wavelength((self.get)(i, j, index, SamplerDimension::Wavelength));
+
+ Randomness {
+ pixel_offset,
+ lens_offset,
+ time,
+ wavelength,
+ }
+ }
+
+ fn sample_dimension(
+ &mut self,
+ i: i32,
+ j: i32,
+ index: i32,
+ dimension: SamplerDimension,
+ ) -> Float {
+ (self.get)(i, j, index, dimension)
+ }
+}
+
+macro_rules! define_blue_sampler {
+ ($spp:ident) => {
+ ::paste::paste! {
+ pub fn [<blue_sample_ $spp>](
+ mut pixel_i: i32,
+ mut pixel_j: i32,
+ mut sample_index: i32,
+ sample_dimension: SamplerDimension) -> Float {
+ let mut sample_dimension = sample_dimension as i32;
+
+ use blue_noise_sampler::$spp::*;
+
+ // Adapted from <https://dl.acm.org/doi/10.1145/3306307.3328191> and <https://github.com/Jasper-Bekkers/blue-noise-sampler>
+
+ // wrap arguments
+ pixel_i &= 127;
+ pixel_j &= 127;
+ sample_index &= 255;
+ sample_dimension &= 255;
+
+ // xor index based on optimized ranking
+ // jb: 1spp blue noise has all 0 in g_blueNoiseRankingTile so we can skip the load
+ let index = sample_dimension + (pixel_i + pixel_j * 128) * 8;
+ let index = index as usize;
+ let ranked_sample_index = sample_index ^ RANKING_TILE[index];
+
+ // fetch value in sequence
+ let index = sample_dimension + ranked_sample_index * 256;
+ let index = index as usize;
+ let value = SOBOL[index];
+
+ // If the dimension is optimized, xor sequence value based on optimized scrambling
+ let index = (sample_dimension % 8) + (pixel_i + pixel_j * 128) * 8;
+ let index = index as usize;
+ let value = value ^ SCRAMBLING_TILE[index];
+
+ // convert to float and return
+ let v: Float = (0.5 + value as Float) / 256.0;
+ v
+ }
+ }
+ };
+}
+
+define_blue_sampler!(spp1);
+define_blue_sampler!(spp2);
+define_blue_sampler!(spp4);
+define_blue_sampler!(spp8);
+define_blue_sampler!(spp16);
+define_blue_sampler!(spp32);
+define_blue_sampler!(spp64);
+define_blue_sampler!(spp128);
+define_blue_sampler!(spp256);
+
+/// Given two samples in range `[0..1]`, return a sample within `[-0.5 .. 0.5]` unit disk.
+/// Based on <https://stackoverflow.com/a/50746409>
+fn in_unit_disk(x: Float, y: Float) -> Vec2 {
+ // Polar coordinates + correcting for the distribution using sqrt
+ let r = x.sqrt();
+ let theta = y * 2.0 * PI;
+ // Conversion to Cartesian coordinates
+ let x = r * theta.cos();
+ let y = r * theta.sin();
+ Vec2::new(x, y)
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +
//! A sampler based on a random number generator. This is the default sampler used in this renderer. It works especially well at high samples-per-pixel counts.
+
+use clovers::{random::random_in_unit_disk, wavelength::random_wavelength, Vec2};
+use rand::{rngs::SmallRng, Rng};
+
+use super::{Randomness, SamplerTrait};
+
+#[derive(Debug)]
+pub struct RandomSampler<'scene> {
+ rng: &'scene mut SmallRng,
+}
+
+impl<'scene> RandomSampler<'scene> {
+ pub fn new(rng: &'scene mut SmallRng) -> Self {
+ Self { rng }
+ }
+}
+
+impl<'scene> SamplerTrait<'scene> for RandomSampler<'scene> {
+ fn sample(&mut self, _i: i32, _j: i32, _index: i32) -> Randomness {
+ let pixel_offset = Vec2::new(self.rng.gen(), self.rng.gen());
+ let lens_offset = random_in_unit_disk(self.rng);
+ let time = self.rng.gen();
+ let wavelength = random_wavelength(self.rng);
+
+ Randomness {
+ pixel_offset,
+ lens_offset,
+ time,
+ wavelength,
+ }
+ }
+
+ fn sample_dimension(
+ &mut self,
+ _i: i32,
+ _j: i32,
+ _index: i32,
+ _dimension: super::SamplerDimension,
+ ) -> clovers::Float {
+ self.rng.gen()
+ }
+}
+
fn:
) to \
+ restrict the search to a given item kind.","Accepted kinds are: fn
, mod
, struct
, \
+ enum
, trait
, type
, macro
, \
+ and const
.","Search functions by type signature (e.g., vec -> usize
or \
+ -> vec
or String, enum:Cow -> bool
)","You can look for items with an exact name by putting double quotes around \
+ your request: \"string\"
","Look for functions that accept or return \
+ slices and \
+ arrays by writing \
+ square brackets (e.g., -> [u8]
or [] -> Option
)","Look for items inside another one by searching for a path: vec::Vec
",].map(x=>""+x+"
").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="${value.replaceAll(" ", " ")}
`}else{error[index]=value}});output+=`AbsDiffEq::abs_diff_eq
].RelativeEq::relative_eq
].Computes the spherical linear interpolation between two unit vectors.
\n\nlet v1 = Unit::new_normalize(Vector2::new(1.0, 2.0));\nlet v2 = Unit::new_normalize(Vector2::new(2.0, -3.0));\n\nlet v = v1.slerp(&v2, 1.0);\n\nassert_eq!(v, v2);
Computes the spherical linear interpolation between two unit vectors.
\nReturns None
if the two vectors are almost collinear and with opposite direction\n(in this case, there is an infinity of possible results).
Normalize the given vector and return it wrapped on a Unit
structure.
Attempts to normalize the given vector and return it wrapped on a Unit
structure.
Returns None
if the norm was smaller or equal to min_norm
.
Normalize the given vector and return it wrapped on a Unit
structure and its norm.
Normalize the given vector and return it wrapped on a Unit
structure and its norm.
Returns None
if the norm was smaller or equal to min_norm
.
Normalizes this vector again. This is useful when repeated computations\nmight cause a drift in the norm because of float inaccuracies.
\nReturns the norm before re-normalization. See .renormalize_fast
for a faster alternative\nthat may be slightly less accurate if self
drifted significantly from having a unit length.
Normalizes this vector again using a first-order Taylor approximation.\nThis is useful when repeated computations might cause a drift in the norm\nbecause of float inaccuracies.
\nWraps the given value, assuming it is already normalized.
\nWraps the given reference, assuming it is already normalized.
\nRetrieves the underlying value.
\n.into_inner()
insteadRetrieves the underlying value.\nDeprecated: use Unit::into_inner
instead.
Returns a mutable reference to the underlying value. This is _unchecked
because modifying\nthe underlying value in such a way that it no longer has unit length may lead to unexpected\nresults.