pytrace 0.3.3

A Python library for ray tracing and image generation
Documentation
use crate::external::*;
use pyo3::prelude::*;
use pyo3::PyObjectProtocol;
use pytrace_core::{composite, internal};
use std::sync::Arc;

#[pyclass]
#[derive(Clone)]
pub struct Prebuilt {
    contents: Arc<dyn Develop>,
}

impl Prebuilt {
    pub fn extract(&self) -> internal::Composite {
        self.contents.develop()
    }
}

pub trait Develop {
    fn develop(&self) -> internal::Composite;
}

#[pyclass]
#[derive(Copy, Clone)]
#[text_signature = "(scale, /)"]
pub struct Axes {
    #[pyo3(get, set)]
    pub scale: f64,
}

#[pymethods]
impl Axes {
    #[new]
    pub fn new(scale: f64) -> Self {
        Self { scale }
    }

    #[text_signature = "($self, /)"]
    pub fn build(self) -> Prebuilt {
        Prebuilt {
            contents: Arc::new(self),
        }
    }
}

impl Develop for Axes {
    fn develop(&self) -> internal::Composite {
        composite::Axes { scale: self.scale }.build()
    }
}

#[pyproto]
impl PyObjectProtocol for Axes {
    fn __repr__(self) -> PyResult<String> {
        Ok(format!("Axes({})", self.scale))
    }

    fn __str__(self) -> PyResult<String> {
        Ok(format!("<Axes object with scale {}>", self.scale))
    }
}

#[pyclass]
#[derive(Copy, Clone)]
#[text_signature = "(position, rotation, size)"]
pub struct Cradle {
    #[pyo3(get, set)]
    pub position: Vec,
    #[pyo3(get, set)]
    pub rotation: f64,
    #[pyo3(get, set)]
    pub size: f64,
    pub amplitude: f64,
    pub time: f64,
}

#[pymethods]
impl Cradle {
    #[new]
    pub fn new(position: Vec, rotation: f64, size: f64) -> Self {
        Self {
            position,
            rotation,
            size,
            amplitude: 0.,
            time: 0.,
        }
    }

    #[text_signature = "($self, /)"]
    pub fn build(self) -> Prebuilt {
        Prebuilt {
            contents: Arc::new(self),
        }
    }

    #[text_signature = "($self, amount, /)"]
    pub fn raise_ball(&mut self, amount: f64) {
        self.amplitude = amount;
        self.time = std::f64::consts::PI / 2.;
    }

    #[text_signature = "($self, dt, /)"]
    pub fn tick(&mut self, dt: f64) {
        self.time += dt;
    }

    #[text_signature = "($self, t, /)"]
    pub fn set_time(&mut self, t: f64) {
        self.time = t;
    }
}

impl Cradle {
    fn calc_balls(&self) -> [f64; 5] {
        let c = (self.time * 2. * std::f64::consts::PI).sin() * self.amplitude;
        if c < 0. {
            [c, 0., 0., 0., 0.]
        } else {
            [0., 0., 0., 0., c]
        }
    }
}

impl Develop for Cradle {
    fn develop(&self) -> internal::Composite {
        composite::NewtonCradle {
            a: self.position.to_internal(),
            angle: self.rotation,
            size: self.size,
            pos: Some(self.calc_balls()),
        }
        .build()
    }
}

#[pyproto]
impl PyObjectProtocol for Cradle {
    fn __repr__(self) -> PyResult<String> {
        Ok(format!("Cradle({}, {})", repr!(self.position), self.size))
    }

    fn __str__(self) -> PyResult<String> {
        Ok(format!(
            "<Cradle object at {} with size {}>",
            repr!(self.position),
            self.size
        ))
    }
}

#[pyclass]
#[derive(Copy, Clone)]
#[text_signature = "(position, rotation, size)"]
pub struct Die {
    #[pyo3(get, set)]
    pub position: Vec,
    #[pyo3(get, set)]
    pub direction: Vec,
    #[pyo3(get, set)]
    pub rotation: f64,
    pub side_texture: Texture,
    pub edge_texture: Texture,
    pub dot_texture: Texture,
}

#[pymethods]
impl Die {
    #[new]
    pub fn new(
        position: Vec,
        direction: Vec,
        rotation: f64,
        side_texture: Texture,
        edge_texture: Texture,
        dot_texture: Texture,
    ) -> Self {
        Self {
            position,
            direction,
            rotation,
            side_texture,
            edge_texture,
            dot_texture,
        }
    }

    #[text_signature = "($self, /)"]
    pub fn build(self) -> Prebuilt {
        Prebuilt {
            contents: Arc::new(self),
        }
    }
}

impl Develop for Die {
    fn develop(&self) -> internal::Composite {
        composite::Die {
            a: self.position.to_internal(),
            up: self.direction.to_internal(),
            rot: self.rotation,
            side_texture: self.side_texture.to_internal(),
            edge_texture: self.edge_texture.to_internal(),
            dot_texture: self.dot_texture.to_internal(),
        }
        .build()
    }
}

#[pyproto]
impl PyObjectProtocol for Die {
    fn __repr__(self) -> PyResult<String> {
        Ok(format!(
            "Die({}, {}, {}) with textures [{} {} {}]",
            repr!(self.position),
            repr!(self.direction),
            self.rotation,
            repr!(self.side_texture),
            repr!(self.edge_texture),
            repr!(self.dot_texture),
        ))
    }

    fn __str__(self) -> PyResult<String> {
        Ok(format!(
            "<Die object at {} facing {} ({})>",
            repr!(self.position),
            repr!(self.direction),
            self.rotation,
        ))
    }
}

#[pyclass]
#[derive(Clone, Copy)]
#[text_signature = "(origin, direction, rotation, structure, /)"]
pub struct Molecule {
    pub origin: Vec,
    pub direction: Vec,
    pub rotation: f64,
    structure: MoleculeStructure,
}

#[pymethods]
impl Molecule {
    #[new]
    pub fn new(origin: Vec, direction: Vec, rotation: f64, structure: String) -> Self {
        Self {
            origin,
            direction,
            rotation,
            structure: MoleculeStructure::from(structure),
        }
    }

    #[text_signature = "($self, /)"]
    pub fn build(self) -> Prebuilt {
        Prebuilt {
            contents: Arc::new(self),
        }
    }
}

impl Develop for Molecule {
    fn develop(&self) -> internal::Composite {
        let m = composite::Molecule {
            orig: self.origin.to_internal(),
            up: self.direction.to_internal(),
            rot: self.rotation,
        };
        match self.structure {
            MoleculeStructure::Cyclohexanol => m.cyclohexanol(),
            MoleculeStructure::Water => m.water(),
            MoleculeStructure::Methane => m.methane(),
            MoleculeStructure::Ethanol => m.ethanol(),
            MoleculeStructure::CarbonDioxide => m.carbon_dioxide(),
            MoleculeStructure::Dinitrogen => m.dinitrogen(),
            MoleculeStructure::Benzene => m.benzene(),
        }
        .build()
    }
}

#[derive(Clone, Copy)]
pub enum MoleculeStructure {
    Cyclohexanol,
    Water,
    Methane,
    Ethanol,
    CarbonDioxide,
    Dinitrogen,
    Benzene,
}

impl MoleculeStructure {
    pub fn from(s: String) -> Self {
        match &s[..] {
            "cyclohexanol" => Self::Cyclohexanol,
            "water" => Self::Water,
            "methane" => Self::Methane,
            "ethanol" => Self::Ethanol,
            "carbon dioxide" => Self::CarbonDioxide,
            "dinitrogen" => Self::Dinitrogen,
            "benzene" => Self::Benzene,
            _ => panic!(
                "Unknown structure.
Please provide one of:
- cyclohexanol
- water
- methane
- ethanol
- carbon dioxide
- dinitrogen
- benzene"
            ),
        }
    }
}

#[pyproto]
impl PyObjectProtocol for Molecule {
    fn __repr__(self) -> PyResult<String> {
        Ok(format!("<Molecule object at {} with orientation {} ({})>", repr!(self.origin), repr!(self.direction), self.rotation))
    }
}