camera.rs 2.69 KB
//
// Basic geometric things...
//
// Georg Hopp <georg@steffers.org>
//
// Copyright © 2019 Georg Hopp
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Neg, Sub};

use crate::easel::canvas::{Canvas, Vertex};
use crate::math::transform::{TMatrix, Transformable};
use crate::math::trigonometry::Trig;
use crate::math::vector::Vector;

use super::point::Point;

#[derive(Debug, Clone, Copy)]
pub struct Camera<T>
where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
    width    :T,
    height   :T,
    distance :T,
    project  :TMatrix<T>,
}

impl<T> Camera<T>
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
       + Mul<Output = T> + Div<Output = T>
       + PartialEq + Debug + Copy + Trig + From<i32> {
    // This code assumes that the size of the viewport is always
    // equal to the size of the physical screen… e.g. window/canvas thus some
    // effects can't be done. See book for examples with different viewport
    // and screen sizes.
    pub fn new(c :&dyn Canvas<T>, angle :i32) -> Self {
        let  width :T = (c.width() as i32).into();
        let height :T = (c.height() as i32).into();
        let      d :T = 1.into();
        let    fov    = T::cot(angle) * width;
        let     wh    = width / 2.into();
        let     hh    = height / 2.into();

        Camera { width:    width
               , height:   height
               , distance: d
               , project:  TMatrix::new(
                     (     fov, 0.into(),       wh, 0.into())
                   , (0.into(),      fov,       hh, 0.into())
                   , (0.into(), 0.into(),        d, 1.into())
                   , (0.into(), 0.into(), 1.into(), 0.into()) ) }
    }

    pub fn get_distance(&self) -> T {
        self.distance
    }

    pub fn get_projection(&self) -> TMatrix<T> {
        self.project
    }

    pub fn project(&self, p :Point<T>) -> Vertex<T> {
        let v :Vector<T> = p.transform(&self.project).into();
        Vertex::new( T::round(&v.x())
                   , T::round(&v.y())
                   , v.z() - self.distance )
    }
}