use thiserror::Error;
#[derive(Clone, Debug)]
pub enum ShapeGeometry {
Rectangle {
x: f32,
y: f32,
width: f32,
height: f32,
},
Polygon {
vertices: Vec<[f32; 2]>,
},
Triangle {
vertices: [[f32; 2]; 3],
},
}
#[derive(Clone, Debug)]
pub struct Shape {
pub geometry: ShapeGeometry,
pub color: [f32; 3],
pub opacity: f32,
pub layer: i32,
pub border_color: Option<[f32; 3]>,
pub border_width: f32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct ShapeId(pub u64);
#[derive(Debug, Error)]
pub enum ShapeError {
#[error("Invalid dimensions: width ({width}) and height ({height}) must be > 0.0")]
InvalidDimensions {
width: f32,
height: f32,
},
#[error("Invalid border width: {0} must be >= 0.0")]
InvalidBorderWidth(f32),
#[error("Shape not found: {0:?}")]
NotFound(ShapeId),
#[error("Too few vertices: {count} (minimum 3 required)")]
TooFewVertices {
count: usize,
},
#[error("Too many vertices: {count} (maximum 1024 allowed)")]
TooManyVertices {
count: usize,
},
#[error("Invalid triangle vertex count: {count} (exactly 3 required)")]
InvalidTriangleVertexCount {
count: usize,
},
}
impl Shape {
pub fn validate(&mut self) -> Result<(), ShapeError> {
match &self.geometry {
ShapeGeometry::Rectangle { width, height, .. } => {
if *width <= 0.0 || *height <= 0.0 {
return Err(ShapeError::InvalidDimensions { width: *width, height: *height });
}
}
ShapeGeometry::Polygon { vertices } => {
let count = vertices.len();
if count < 3 { return Err(ShapeError::TooFewVertices { count }); }
if count > 1024 { return Err(ShapeError::TooManyVertices { count }); }
}
ShapeGeometry::Triangle { .. } => {}
}
if self.border_width < 0.0 {
return Err(ShapeError::InvalidBorderWidth(self.border_width));
}
self.opacity = self.opacity.clamp(0.0, 1.0);
Ok(())
}
}