use serde::{Deserialize, Serialize};
use crate::scope::Scope;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum FaceProfile {
Rectangle,
Taper(f64),
Triangle { peak_offset: f64 },
Trapezoid { top_width: f64, offset_x: f64 },
Polygon(Vec<glam::DVec2>),
}
impl FaceProfile {
pub fn is_rectangle(&self) -> bool {
matches!(self, Self::Rectangle)
}
pub fn taper_coeff(&self) -> Option<f64> {
match self {
Self::Taper(t) => Some(*t),
Self::Rectangle => Some(0.0),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Terminal {
pub scope: Scope,
pub mesh_id: String,
pub face_profile: FaceProfile,
pub material: Option<String>,
}
impl Terminal {
pub fn new(scope: Scope, mesh_id: impl Into<String>) -> Self {
Self {
scope,
mesh_id: mesh_id.into(),
face_profile: FaceProfile::Rectangle,
material: None,
}
}
pub fn new_with_taper(scope: Scope, mesh_id: impl Into<String>, taper: f64) -> Self {
let face_profile = taper_to_profile(taper);
Self {
scope,
mesh_id: mesh_id.into(),
face_profile,
material: None,
}
}
pub fn new_full(
scope: Scope,
mesh_id: impl Into<String>,
taper: f64,
material: Option<String>,
) -> Self {
Self {
scope,
mesh_id: mesh_id.into(),
face_profile: taper_to_profile(taper),
material,
}
}
pub fn new_profiled(
scope: Scope,
mesh_id: impl Into<String>,
face_profile: FaceProfile,
material: Option<String>,
) -> Self {
Self {
scope,
mesh_id: mesh_id.into(),
face_profile,
material,
}
}
}
pub fn taper_to_profile(taper: f64) -> FaceProfile {
if taper <= 0.0 {
FaceProfile::Rectangle
} else if (taper - 1.0).abs() < 1e-9 {
FaceProfile::Triangle { peak_offset: 0.5 }
} else {
FaceProfile::Taper(taper.clamp(0.0, 1.0))
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ShapeModel {
pub terminals: Vec<Terminal>,
}
impl ShapeModel {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, terminal: Terminal) {
self.terminals.push(terminal);
}
pub fn len(&self) -> usize {
self.terminals.len()
}
pub fn is_empty(&self) -> bool {
self.terminals.is_empty()
}
}