eyepiece 0.8.1

A crate to generate star fields as seen with different telescopes
Documentation
use std::fmt::Display;

use serde::Serialize;

use crate::Observer;

/// Generic circular telescope
///
/// # Example
/// ```
/// use eyepiece::Telescope;
/// let tel = Telescope::new(8.).build();
/// ```
#[derive(Debug, Clone, Copy, Serialize)]
pub struct Telescope {
    /// Primary mirror diameter D (Nyquist sampling criteria: λ/2D)
    pub diameter: f64,
    pub obscuration: Option<f64>,
}

impl Default for Telescope {
    fn default() -> Self {
        Self {
            diameter: 1f64,
            obscuration: Default::default(),
        }
    }
}
impl Display for Telescope {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(obscuration) = self.obscuration {
            write!(
                f,
                "Telescope: {}m diameter ({}m diameter obscuration), {:.3}m^2 collection area",
                self.diameter,
                obscuration,
                self.area()
            )
        } else {
            write!(
                f,
                "Telescope: {}m diameter, {:.3}m^2 collection area",
                self.diameter,
                self.area()
            )
        }
    }
}

/// Generic [Telescope] builder
pub struct TelescopeBuilder {
    diameter: f64,
    obscuration: Option<f64>,
}

impl Telescope {
    /// Creates a new telescope with the given `diameter`
    pub fn new(diameter: f64) -> TelescopeBuilder {
        TelescopeBuilder {
            diameter,
            obscuration: None,
        }
    }
}

impl TelescopeBuilder {
    /// Sets the diameter of the telescope central obscuration
    pub fn obscuration(mut self, obscuration: f64) -> Self {
        self.obscuration = Some(obscuration);
        self
    }
    /// Build the telescope
    pub fn build(self) -> Telescope {
        Telescope {
            diameter: self.diameter,
            obscuration: self.obscuration,
            ..Default::default()
        }
    }
}

impl Observer for Telescope {
    fn diameter(&self) -> f64 {
        self.diameter
    }

    fn inside_pupil(&self, x: f64, y: f64) -> bool {
        let r_outer = self.diameter * 0.5;
        let r_inner = self.obscuration.unwrap_or_default();
        let r = x.hypot(y);
        r >= r_inner && r <= r_outer
    }
}