use thiserror::Error;
#[derive(Debug, Error)]
pub enum ViewportError {
#[error("Invalid zoom factor: {0} must be > 0.0")]
InvalidZoom(f32),
}
#[derive(Clone, Debug)]
pub struct Viewport {
pub pan_x: f32,
pub pan_y: f32,
pub zoom: f32,
}
impl Viewport {
pub fn new() -> Self {
Self {
pan_x: 0.0,
pan_y: 0.0,
zoom: 1.0,
}
}
pub fn set_pan(&mut self, x: f32, y: f32) {
self.pan_x = x;
self.pan_y = y;
}
pub fn set_zoom(&mut self, zoom: f32) -> Result<(), ViewportError> {
if zoom <= 0.0 {
return Err(ViewportError::InvalidZoom(zoom));
}
self.zoom = zoom;
Ok(())
}
pub fn transform_matrix(&self, window_width: f32, window_height: f32) -> [f32; 12] {
let sx = self.zoom * 2.0 / window_width;
let sy = self.zoom * 2.0 / window_height;
[
sx, 0.0, 0.0, 0.0,
0.0, sy, 0.0, 0.0,
self.pan_x * sx, self.pan_y * sy, 1.0, 0.0,
]
}
pub fn window_to_scene(
&self,
window_x: f32,
window_y: f32,
window_width: f32,
window_height: f32,
) -> (f32, f32) {
let ndc_x = (window_x / window_width) * 2.0 - 1.0;
let ndc_y = 1.0 - (window_y / window_height) * 2.0;
let scene_x = ndc_x * window_width / (2.0 * self.zoom) - self.pan_x;
let scene_y = ndc_y * window_height / (2.0 * self.zoom) - self.pan_y;
(scene_x, scene_y)
}
}
impl Default for Viewport {
fn default() -> Self {
Self::new()
}
}