use crate::MindMapError;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Point {
pub x: f64,
pub y: f64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Viewport {
pub translation: Point,
pub scale: f64,
}
impl Default for Viewport {
fn default() -> Self {
Self {
translation: Point::default(),
scale: 1.0,
}
}
}
impl Viewport {
pub fn to_canvas(self, point: Point) -> Result<Point, MindMapError> {
self.validate()?;
if !point.x.is_finite() || !point.y.is_finite() {
return Err(MindMapError::InvalidGeometry);
}
let result = Point {
x: (point.x - self.translation.x) / self.scale,
y: (point.y - self.translation.y) / self.scale,
};
if !result.x.is_finite() || !result.y.is_finite() {
return Err(MindMapError::InvalidGeometry);
}
Ok(result)
}
pub fn zoom_at(&mut self, scale: f64, anchor: Point) -> Result<(), MindMapError> {
let canvas = self.to_canvas(anchor)?;
if !scale.is_finite() {
return Err(MindMapError::InvalidGeometry);
}
let scale = scale.clamp(0.1, 4.0);
let translation = Point {
x: anchor.x - canvas.x * scale,
y: anchor.y - canvas.y * scale,
};
if !translation.x.is_finite() || !translation.y.is_finite() {
return Err(MindMapError::InvalidGeometry);
}
self.translation = translation;
self.scale = scale;
Ok(())
}
pub fn pan(&mut self, delta: Point) -> Result<(), MindMapError> {
self.validate()?;
let translation = Point {
x: self.translation.x + delta.x,
y: self.translation.y + delta.y,
};
if !translation.x.is_finite() || !translation.y.is_finite() {
return Err(MindMapError::InvalidGeometry);
}
self.translation = translation;
Ok(())
}
pub fn center(&mut self, target: Point, size: Point) -> Result<(), MindMapError> {
self.validate()?;
if ![target.x, target.y, size.x, size.y]
.into_iter()
.all(f64::is_finite)
|| size.x <= 0.0
|| size.y <= 0.0
{
return Err(MindMapError::InvalidGeometry);
}
let translation = Point {
x: size.x / 2.0 - target.x * self.scale,
y: size.y / 2.0 - target.y * self.scale,
};
if !translation.x.is_finite() || !translation.y.is_finite() {
return Err(MindMapError::InvalidGeometry);
}
self.translation = translation;
Ok(())
}
pub fn validate(self) -> Result<(), MindMapError> {
if [self.translation.x, self.translation.y, self.scale]
.into_iter()
.all(f64::is_finite)
&& (0.1..=4.0).contains(&self.scale)
{
Ok(())
} else {
Err(MindMapError::InvalidGeometry)
}
}
}