254 lines
6.6 KiB
Rust
254 lines
6.6 KiB
Rust
//! Camera film logic
|
|
|
|
use crate::{Point, Vector};
|
|
|
|
/// Represent an abstract camera film, to know where each pixel is in space.
|
|
#[derive(Debug, PartialEq)]
|
|
pub struct Film {
|
|
x: u32,
|
|
y: u32,
|
|
center: Point,
|
|
ratio_up: Vector,
|
|
ratio_right: Vector,
|
|
}
|
|
|
|
impl Film {
|
|
/// Creates a new `Film`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use pathtracer::core::Film;
|
|
/// # use pathtracer::{Point, Vector};
|
|
/// #
|
|
/// let film = Film::new(
|
|
/// 1080,
|
|
/// 1080,
|
|
/// 10.0,
|
|
/// Point::origin(),
|
|
/// Vector::new(0.0, 1.0, 0.0),
|
|
/// Vector::new(1.0, 0.0, 0.0)
|
|
/// );
|
|
/// ```
|
|
pub fn new(x: u32, y: u32, screen_size: f32, center: Point, up: Vector, right: Vector) -> Self {
|
|
let (x_size, y_size) = if x > y {
|
|
(screen_size, screen_size * y as f32 / x as f32)
|
|
} else {
|
|
(screen_size * x as f32 / y as f32, screen_size)
|
|
};
|
|
Film {
|
|
x,
|
|
y,
|
|
center,
|
|
ratio_up: up.normalize() * y_size,
|
|
ratio_right: right.normalize() * x_size,
|
|
}
|
|
}
|
|
|
|
/// Get the `Film`'s width.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use pathtracer::core::Film;
|
|
/// #
|
|
/// let film = Film::default();
|
|
/// let width: u32 = film.width();
|
|
/// ```
|
|
pub fn width(&self) -> u32 {
|
|
self.x
|
|
}
|
|
|
|
/// Get the `Film`'s height.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use pathtracer::core::Film;
|
|
/// #
|
|
/// let film = Film::default();
|
|
/// let height: u32 = film.height();
|
|
/// ```
|
|
pub fn height(&self) -> u32 {
|
|
self.y
|
|
}
|
|
|
|
/// Get a ratio of the pixel's position on the screen.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use pathtracer::core::Film;
|
|
/// #
|
|
/// let film = Film::default(); // 1080x1080 film, width of 1.0
|
|
/// let (x, y) = film.pixel_ratio(108.0, 972.0);
|
|
/// assert_eq!(x, 0.1);
|
|
/// assert_eq!(y, 0.9);
|
|
/// ```
|
|
pub fn pixel_ratio(&self, x: f32, y: f32) -> (f32, f32) {
|
|
(x / self.x as f32, y / self.y as f32)
|
|
}
|
|
|
|
/// Get a pixel's absolute position from a relative screen ratio.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use pathtracer::core::Film;
|
|
/// use pathtracer::Point;
|
|
///
|
|
/// let film = Film::default(); // 1080x1080 film, width of 1.0
|
|
/// let (x, y) = film.pixel_ratio(108.0, 1080.0);
|
|
/// let pos: Point = film.pixel_at_ratio(x, y);
|
|
/// assert_eq!(pos, Point::new(-0.4, -0.5, 0.0));
|
|
/// ```
|
|
pub fn pixel_at_ratio(&self, x: f32, y: f32) -> Point {
|
|
let delt_x = x - 0.5;
|
|
let delt_y = 0.5 - y;
|
|
self.center + self.ratio_right * delt_x + self.ratio_up * delt_y
|
|
}
|
|
|
|
/// Get a pixel's absolute position from screen coordinates.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use pathtracer::core::Film;
|
|
/// use pathtracer::Point;
|
|
///
|
|
/// let film = Film::default(); // 1080x1080 film, width of 1.0
|
|
/// let pos: Point = film.pixel_at_coord(108, 1080);
|
|
/// assert_eq!(pos, Point::new(-0.4, -0.5, 0.0));
|
|
/// ```
|
|
pub fn pixel_at_coord(&self, x: u32, y: u32) -> Point {
|
|
let (x, y) = self.pixel_ratio(x as f32, y as f32);
|
|
self.pixel_at_ratio(x, y)
|
|
}
|
|
}
|
|
|
|
impl Default for Film {
|
|
/// Creates a simple 1080x1080 `Film`.
|
|
///
|
|
/// The screen size is 1.0, and the screen is centered at the origin.
|
|
fn default() -> Self {
|
|
Film::new(
|
|
1080,
|
|
1080,
|
|
1.0,
|
|
Point::origin(),
|
|
Vector::new(0.0, 1.0, 0.0),
|
|
Vector::new(1.0, 0.0, 0.0),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn simple_new_works() {
|
|
let film = Film::new(
|
|
1080,
|
|
1080,
|
|
1.,
|
|
Point::origin(),
|
|
Vector::new(0., 1., 0.),
|
|
Vector::new(0., 0., 1.),
|
|
);
|
|
assert_eq!(
|
|
film,
|
|
Film {
|
|
x: 1080,
|
|
y: 1080,
|
|
center: Point::origin(),
|
|
ratio_up: Vector::new(0., 1., 0.),
|
|
ratio_right: Vector::new(0., 0., 1.),
|
|
}
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn new_with_smaller_x_works() {
|
|
let film = Film::new(
|
|
1080,
|
|
1440,
|
|
1.,
|
|
Point::origin(),
|
|
Vector::new(0., 1., 0.),
|
|
Vector::new(0., 0., 1.),
|
|
);
|
|
assert_eq!(
|
|
film,
|
|
Film {
|
|
x: 1080,
|
|
y: 1440,
|
|
center: Point::origin(),
|
|
ratio_up: Vector::new(0., 1., 0.),
|
|
ratio_right: Vector::new(0., 0., 0.75),
|
|
}
|
|
)
|
|
}
|
|
#[test]
|
|
fn new_with_smaller_y_works() {
|
|
let film = Film::new(
|
|
1080,
|
|
540,
|
|
1.,
|
|
Point::origin(),
|
|
Vector::new(0., 1., 0.),
|
|
Vector::new(0., 0., 1.),
|
|
);
|
|
assert_eq!(
|
|
film,
|
|
Film {
|
|
x: 1080,
|
|
y: 540,
|
|
center: Point::origin(),
|
|
ratio_up: Vector::new(0., 0.5, 0.),
|
|
ratio_right: Vector::new(0., 0., 1.),
|
|
}
|
|
)
|
|
}
|
|
|
|
fn simple_film() -> Film {
|
|
Film::new(
|
|
1080,
|
|
1080,
|
|
1.,
|
|
Point::origin(),
|
|
Vector::new(0., 1., 0.),
|
|
Vector::new(0., 0., 1.),
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn pixel_ratio_works() {
|
|
let film = simple_film();
|
|
assert_eq!(film.pixel_ratio(0., 0.), (0., 0.));
|
|
assert_eq!(film.pixel_ratio(1080., 1080.), (1., 1.));
|
|
assert_eq!(film.pixel_ratio(1080., 540.), (1., 0.5));
|
|
assert_eq!(film.pixel_ratio(540., 1080.), (0.5, 1.));
|
|
assert_eq!(film.pixel_ratio(1080., 810.), (1., 0.75));
|
|
assert_eq!(film.pixel_ratio(810., 1080.), (0.75, 1.))
|
|
}
|
|
|
|
#[test]
|
|
fn pixel_at_ratio_works() {
|
|
let film = simple_film();
|
|
assert_eq!(film.pixel_at_ratio(0., 0.), Point::new(0., 0.5, -0.5));
|
|
assert_eq!(film.pixel_at_ratio(1., 1.), Point::new(0., -0.5, 0.5));
|
|
assert_eq!(film.pixel_at_ratio(1., 0.5), Point::new(0., 0., 0.5));
|
|
assert_eq!(film.pixel_at_ratio(0.5, 1.), Point::new(0., -0.5, 0.));
|
|
}
|
|
|
|
#[test]
|
|
fn pixel_at_coord_works() {
|
|
let film = simple_film();
|
|
assert_eq!(film.pixel_at_coord(0, 0), Point::new(0., 0.5, -0.5));
|
|
assert_eq!(film.pixel_at_coord(1080, 1080), Point::new(0., -0.5, 0.5));
|
|
assert_eq!(film.pixel_at_coord(1080, 540), Point::new(0., 0., 0.5));
|
|
assert_eq!(film.pixel_at_coord(540, 1080), Point::new(0., -0.5, 0.));
|
|
}
|
|
}
|