use bytemuck::{Pod, Zeroable};
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct CameraUniform {
pub transform_matrix: [[f32; 4]; 4],
pub color: [f32; 4],
pub line_width: f32,
pub _padding: [f32; 3],
}
impl Default for CameraUniform {
fn default() -> Self {
Self {
transform_matrix: [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
color: [0.2, 0.6, 1.0, 1.0], line_width: 1.5,
_padding: [0.0; 3],
}
}
}
pub struct ViewportCamera {
pub x_min: f64,
pub x_max: f64,
pub y_min: f32,
pub y_max: f32,
pub color: [f32; 4],
pub line_width: f32,
}
impl Default for ViewportCamera {
fn default() -> Self {
Self {
x_min: 0.0,
x_max: 100.0,
y_min: -1.0,
y_max: 1.0,
color: [0.2, 0.7, 1.0, 1.0],
line_width: 1.5,
}
}
}
impl ViewportCamera {
pub fn new(x_min: f64, x_max: f64, y_min: f32, y_max: f32) -> Self {
Self {
x_min,
x_max,
y_min,
y_max,
..Default::default()
}
}
pub fn pan_x(&mut self, delta_x: f64) {
self.x_min += delta_x;
self.x_max += delta_x;
}
pub fn zoom_x(&mut self, center_x: f64, factor: f64) {
let span = self.x_max - self.x_min;
let new_span = (span * factor).max(1e-6);
let ratio = if span > 0.0 {
(center_x - self.x_min) / span
} else {
0.5
};
self.x_min = center_x - ratio * new_span;
self.x_max = self.x_min + new_span;
}
pub fn build_uniform(&self) -> CameraUniform {
let span_x = (self.x_max - self.x_min) as f32;
let span_y = self.y_max - self.y_min;
let scale_x = if span_x > 0.0 { 2.0 / span_x } else { 1.0 };
let scale_y = if span_y > 0.0 { 2.0 / span_y } else { 1.0 };
let offset_x = -1.0 - (self.x_min as f32) * scale_x;
let offset_y = -1.0 - self.y_min * scale_y;
let transform_matrix = [
[scale_x, 0.0, 0.0, 0.0],
[0.0, scale_y, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[offset_x, offset_y, 0.0, 1.0],
];
CameraUniform {
transform_matrix,
color: self.color,
line_width: self.line_width,
_padding: [0.0; 3],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_camera_uniform_transformation() {
let camera = ViewportCamera::new(0.0, 100.0, -1.0, 1.0);
let uniform = camera.build_uniform();
let scale_x = uniform.transform_matrix[0][0];
let offset_x = uniform.transform_matrix[3][0];
let ndc_x = 50.0 * scale_x + offset_x;
assert!((ndc_x - 0.0).abs() < 1e-5);
}
}