rustic-zen 0.3.0

Photon-Garden raytracer for creating artistic renderings
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::sampler::{Sampler, SamplerPoint};
use rand::prelude::*;

/// Definition of a Light Source in for a scene.
///
/// # Examples
/// Manually make a warm white small round light:
/// ```
/// use rustic_zen::prelude::*;
///
/// let l = Light {
///     power: Sampler::new_const(1.0),
///     location: (512.0, 512.0).into(),
///     polar_angle: Sampler::new_const(0.0),
///     polar_distance: Sampler::new_const(0.0),
///     ray_angle: Sampler::new_range(360.0, 0.0),
///     wavelength: Sampler::new_blackbody(5800.0),
/// };
///
/// let scene = Scene::new(1920,1080).with_light(l);
/// ```
#[derive(Clone)]
pub struct Light<R: Rng> {
    /// coordinates of the light in the scene.
    pub location: SamplerPoint<R>,
    /// Brightness of this light relative to other lights in the scene. This is a proportion of the total
    /// power of all lights of the scene. So if you have 50 lights of power 1 each light has a 1 in 50
    /// chance of being selected to spawn a ray. However adding 1 more light with power 10 will lower the
    /// existing lights to 1 in 60 and the new light will have 1 in 6. once used this is quite intuitive,
    /// the power will change the relative brightness of the lights, without changing the overall brightness
    /// of the scene.
    pub power: Sampler<f64, R>,
    /// distance from `location` that the light ray will spawn,
    /// this can be used to create lights that are larger than a single pixel
    /// Fixing this to a single value will create a ring where the rays spawn,
    /// using a range of 0 - `size` will create a filled circle of spawn points.
    pub polar_distance: Sampler<f64, R>,
    /// combined with `polar_distance` this creates a vector that is added to `location`
    /// to generate the final point the light ray will spawn. Using a range of 360 - 0
    /// will create a full circle, constraining this can create an arc.
    pub polar_angle: Sampler<f64, R>,
    /// Angle at which the spawned ray will fly away from the light. This is in absolute
    /// screen angle (0 is right). Setting this to a range of 0 - 360 will create an omidirectional
    /// light, constraining this range creates a more cone like light. Constraining it all
    /// the way to a single fixed value will create a laser. Using a `polar_distance` > 0 is
    /// recomended if the `ray_angle` is constrained to a single value, as a ray 1 pixel wide can be
    /// hard to see.  
    pub ray_angle: Sampler<f64, R>,
    /// Wavelength of spawned ray in nanometers. Only values of  380.0 - 700.0 are visible.
    /// Using a fixed value will create a strongly coloured light of that wavelength. To create natural
    /// looking white lights use `Sampler::new_blackbody(<temp>)`. Blackbody tempratures of 1,000 to
    /// 10,000 kelvin are visible.
    ///
    /// # Warning
    /// __Rays are sampled until a visible wavelength is found, this is an optimisation, selecting a fixed value
    /// or range of values outside of the visible spectrum can crash the render. So can selecting extreme
    /// blackbody tempratures.__
    pub wavelength: Sampler<f64, R>,
}

impl<R> Light<R>
where
    R: Rng,
{
    /// Helper function to create a Light quickly, using common casts
    /// available in the library.
    ///
    /// # Example Usage
    /// Create the same small round light as above using the helper:
    /// ```
    /// use rustic_zen::prelude::*;
    ///
    /// let l = Light::new((512.0,512.0), 1.0, 0.0, 0.0, (360.0, 0.0), Sampler::new_blackbody(5800.0));
    ///
    /// let scene = Scene::new(1920,1080).with_light(l);
    /// ```
    pub fn new<A, B, C, D, E, F>(
        location: A,
        power: B,
        polar_distance: C,
        polar_angle: D,
        ray_angle: E,
        wavelength: F,
    ) -> Self
    where
        A: Into<SamplerPoint<R>>,
        B: Into<Sampler<f64, R>>,
        C: Into<Sampler<f64, R>>,
        D: Into<Sampler<f64, R>>,
        E: Into<Sampler<f64, R>>,
        F: Into<Sampler<f64, R>>,
    {
        Self {
            location: location.into(),
            power: power.into(),
            polar_distance: polar_distance.into(),
            polar_angle: polar_angle.into(),
            ray_angle: ray_angle.into(),
            wavelength: wavelength.into(),
        }
    }
}