use crate::camera::*;
use crate::core::Error;
use crate::math::*;
pub struct CameraControl {
camera: Camera,
}
impl CameraControl {
pub fn new(camera: Camera) -> Self {
Self { camera }
}
pub fn translate(&mut self, change: &Vec3) -> Result<(), Error> {
let position = *self.position();
let target = *self.target();
let up = *self.up();
self.set_view(position + change, target + change, up)?;
Ok(())
}
pub fn rotate_around(&mut self, point: &Vec3, x: f32, y: f32) -> Result<(), Error> {
let dir = (point - self.position()).normalize();
let right = dir.cross(*self.up());
let up = right.cross(dir);
let new_dir = (point - self.position() + right * x - up * y).normalize();
let rotation = rotation_matrix_from_dir_to_dir(dir, new_dir);
let new_position = (rotation * (self.position() - point).extend(1.0)).truncate() + point;
let new_target = (rotation * (self.target() - point).extend(1.0)).truncate() + point;
self.set_view(new_position, new_target, up)?;
Ok(())
}
pub fn rotate_around_with_fixed_up(
&mut self,
point: &Vec3,
x: f32,
y: f32,
) -> Result<(), Error> {
let dir = (point - self.position()).normalize();
let right = dir.cross(*self.up());
let mut up = right.cross(dir);
let new_dir = (point - self.position() + right * x - up * y).normalize();
up = *self.up();
if new_dir.dot(up).abs() < 0.999 {
let rotation = rotation_matrix_from_dir_to_dir(dir, new_dir);
let new_position =
(rotation * (self.position() - point).extend(1.0)).truncate() + point;
let new_target = (rotation * (self.target() - point).extend(1.0)).truncate() + point;
self.set_view(new_position, new_target, up)?;
}
Ok(())
}
pub fn pan(&mut self, x: f32, y: f32) -> Result<(), Error> {
let right = self.right_direction();
let up = right.cross(self.view_direction());
let delta = -right * x + up * y;
self.translate(&delta)?;
Ok(())
}
pub fn zoom_towards(
&mut self,
point: &Vec3,
delta: f32,
minimum_distance: f32,
maximum_distance: f32,
) -> Result<(), Error> {
if minimum_distance <= 0.0 {
return Err(Error::CameraError {
message: "Zoom towards cannot take as input a negative minimum distance."
.to_string(),
});
}
if maximum_distance < minimum_distance {
return Err(Error::CameraError {
message: "Zoom towards cannot take as input a maximum distance which is smaller than the minimum distance."
.to_string(),
});
}
let position = *self.position();
let distance = point.distance(position);
let direction = (point - position).normalize();
let target = *self.target();
let up = *self.up();
let new_distance = (distance - delta)
.max(minimum_distance)
.min(maximum_distance);
let new_position = point - direction * new_distance;
self.set_view(new_position, new_position + (target - position), up)?;
match self.projection_type() {
ProjectionType::Orthographic {
width,
height,
depth,
} => {
let h = new_distance * height / distance;
let w = h * width / height;
let d = *depth;
self.set_orthographic_projection(w, h, d)?;
}
_ => {}
}
Ok(())
}
}
impl std::ops::Deref for CameraControl {
type Target = Camera;
fn deref(&self) -> &Self::Target {
&self.camera
}
}
impl std::ops::DerefMut for CameraControl {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.camera
}
}