#![no_std]
#![forbid(unsafe_code, rust_2018_idioms)]
#![warn(missing_docs)]
mod aspect;
pub use aspect::*;
use core::fmt::{self, Debug, Formatter};
use uom::{
si::{
f64::{Angle, Length},
length::meter,
},
typenum::*,
};
#[derive(Copy, Clone, Debug)]
pub struct MonitorConfiguration {
pub dimensions: MonitorDimensions,
pub distance: Length,
}
impl MonitorConfiguration {
pub fn fov(self) -> Angle {
let opposite = self.dimensions.width_and_height()[0] / 2.0;
let adjacent = self.distance;
let half_angle = (opposite / adjacent).atan();
half_angle * 2.0
}
pub fn monitor_fov_for_distance(self, distance: Length, relative_to_monitor: bool) -> Angle {
let distance_from_eye = if relative_to_monitor {
distance + self.distance
} else {
distance
};
let base_half_angle = self.fov() / 2.0;
let half_width_over_distance = base_half_angle.tan();
let half_width = half_width_over_distance * distance_from_eye;
let final_angle_tangent = half_width / distance;
let half_final_angle = final_angle_tangent.atan();
half_final_angle * 2.0
}
}
#[derive(Copy, Clone)]
pub enum MonitorDimensions {
#[allow(missing_docs)] WidthAndHeight { width: Length, height: Length },
DiagonalAndAspect {
diagonal: Length,
aspect: f64,
},
}
impl MonitorDimensions {
pub fn width_and_height(self) -> [Length; 2] {
match self {
Self::WidthAndHeight { width, height } => [width, height],
Self::DiagonalAndAspect { diagonal, aspect } => {
Self::diagonal_and_aspect_to_width_and_height(diagonal, aspect)
}
}
}
pub fn aspect(self) -> f64 {
match self {
Self::WidthAndHeight { width, height } => width.get::<meter>() / height.get::<meter>(),
Self::DiagonalAndAspect { aspect, .. } => aspect,
}
}
pub fn diagonal(self) -> Length {
match self {
Self::WidthAndHeight { width, height } => {
(width.powi(P2::new()) + height.powi(P2::new())).sqrt()
}
Self::DiagonalAndAspect { diagonal, .. } => diagonal,
}
}
fn diagonal_and_aspect_to_width_and_height(diagonal: Length, aspect: f64) -> [Length; 2] {
let height = diagonal / (aspect.powi(2) + 1.0).sqrt();
let width = height * aspect;
[width, height]
}
pub fn as_width_and_height(self) -> Self {
let [width, height] = self.width_and_height();
Self::WidthAndHeight { width, height }
}
pub fn as_diagonal_and_aspect(self) -> Self {
Self::DiagonalAndAspect {
diagonal: self.diagonal(),
aspect: self.aspect(),
}
}
}
impl Debug for MonitorDimensions {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let [width, height] = self.width_and_height();
#[derive(Debug)]
enum StoredAs {
WidthAndHeight,
DiagonalAndAspect,
}
let stored_as = match self {
Self::WidthAndHeight { .. } => StoredAs::WidthAndHeight,
Self::DiagonalAndAspect { .. } => StoredAs::DiagonalAndAspect,
};
f.debug_struct("MonitorDimensions")
.field("width", &width)
.field("height", &height)
.field("aspect", &self.aspect())
.field("diagonal", &self.diagonal())
.field("stored_as", &stored_as)
.finish()
}
}