// AREA command — compute area and perimeter of a polygon picked point by point. // Press Enter to close and calculate. use glam::{DVec3, Vec3}; use crate::command::{CadCommand, CmdResult}; use crate::scene::model::wire_model::WireModel; pub struct AreaCommand { points: Vec, } impl AreaCommand { pub fn new() -> Self { Self { points: vec![] } } } impl CadCommand for AreaCommand { fn name(&self) -> &'static str { "AREA" } fn prompt(&self) -> String { if self.points.is_empty() { "AREA Specify first corner point (Enter to cancel):".into() } else { format!( "AREA Specify next point ({} picked, Enter to calculate):", self.points.len() ) } } fn on_point(&mut self, pt: DVec3) -> CmdResult { let pt = pt.as_vec3(); self.points.push(pt); CmdResult::NeedPoint } fn on_enter(&mut self) -> CmdResult { if self.points.len() < 3 { return CmdResult::Cancel; } // Shoelace formula in the world XY plane. let n = self.points.len(); let mut area_sum = 0.0f32; let mut perimeter = 0.0f32; for idx in 0..n { let a = self.points[idx]; let b = self.points[(idx + 1) % n]; area_sum += a.x * b.y - b.x * a.y; perimeter += (b - a).length(); } let area = (area_sum * 0.5).abs(); let msg = format!("Area = {area:.4}, Perimeter = {perimeter:.4}"); CmdResult::Measurement(msg) } fn on_mouse_move(&mut self, pt: DVec3) -> Option { let pt = pt.as_vec3(); if self.points.is_empty() { return None; } let mut pts: Vec<[f32; 3]> = self.points.iter().map(|p| [p.x, p.y, p.z]).collect(); pts.push([pt.x, pt.y, pt.z]); pts.push([self.points[0].x, self.points[0].y, self.points[0].z]); Some(WireModel { name: "area_preview".into(), points: pts, points_low: Vec::new(), color: WireModel::CYAN, selected: false, pattern_length: 0.0, pattern: [0.0; 8], line_weight_px: 1.0, snap_pts: vec![], tangent_geoms: vec![], aci: 0, key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, vp_scissor: None, fill_tris: vec![], fill_tris_low: Vec::new(), }) } } // ── Autocomplete registry ───────────────────────────────── inventory::submit!(crate::command::CommandRegistration { names: &["AREA"] }); // AreaCommand