use glam::DVec3;
use super::{
TABLE_NY,
types::{SolidMesh, SolidMetrics},
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StoneProportions {
pub table_percent: Option<f64>,
pub crown_height: Option<f64>,
pub pavilion_depth: Option<f64>,
pub girdle_thickness: Option<f64>,
pub total_depth: f64,
pub length_to_width: Option<f64>,
pub crown_to_width_percent: Option<f64>,
pub pavilion_to_width_percent: Option<f64>,
pub girdle_to_width_percent: Option<f64>,
}
impl StoneProportions {
#[must_use]
pub fn from_solid(metrics: &SolidMetrics, mesh: &SolidMesh, planes: &[(DVec3, f64)]) -> Self {
let width = (metrics.width_axis > 1e-9).then_some(metrics.width_axis);
let to_width_percent =
|value: Option<f64>| Option::zip(value, width).map(|(v, w)| 100.0 * v / w);
Self {
table_percent: table_percent(metrics, mesh, planes),
crown_height: metrics.crown_height,
pavilion_depth: metrics.pavilion_depth,
girdle_thickness: metrics.girdle_thickness,
total_depth: metrics.total_height,
length_to_width: width.map(|w| metrics.length_axis / w),
crown_to_width_percent: to_width_percent(metrics.crown_height),
pavilion_to_width_percent: to_width_percent(metrics.pavilion_depth),
girdle_to_width_percent: to_width_percent(metrics.girdle_thickness),
}
}
#[must_use]
pub fn to_mm(&self, mm_per_unit: f64) -> Self {
Self {
table_percent: self.table_percent,
crown_height: self.crown_height.map(|v| v * mm_per_unit),
pavilion_depth: self.pavilion_depth.map(|v| v * mm_per_unit),
girdle_thickness: self.girdle_thickness.map(|v| v * mm_per_unit),
total_depth: self.total_depth * mm_per_unit,
length_to_width: self.length_to_width,
crown_to_width_percent: self.crown_to_width_percent,
pavilion_to_width_percent: self.pavilion_to_width_percent,
girdle_to_width_percent: self.girdle_to_width_percent,
}
}
}
fn table_percent(metrics: &SolidMetrics, mesh: &SolidMesh, planes: &[(DVec3, f64)]) -> Option<f64> {
if metrics.width_axis <= 1e-9 {
return None;
}
let mut best: Option<(f64, &Vec<DVec3>)> = None;
for (plane_idx, ring) in &mesh.rings {
let Some(&(normal, _)) = planes.get(*plane_idx) else {
continue;
};
if normal.y < TABLE_NY || ring.len() < 3 {
continue;
}
let top = ring.iter().map(|v| v.y).fold(f64::NEG_INFINITY, f64::max);
if best.is_none_or(|(best_top, _)| top > best_top) {
best = Some((top, ring));
}
}
let (_, ring) = best?;
let x_max = ring.iter().map(|v| v.x).fold(f64::NEG_INFINITY, f64::max);
let x_min = ring.iter().map(|v| v.x).fold(f64::INFINITY, f64::min);
let z_max = ring.iter().map(|v| v.z).fold(f64::NEG_INFINITY, f64::max);
let z_min = ring.iter().map(|v| v.z).fold(f64::INFINITY, f64::min);
let table_width = (x_max - x_min).min(z_max - z_min);
Some(100.0 * table_width / metrics.width_axis)
}