use acadrust::entities::{Ole2Frame, OleObjectType}; use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip}; use crate::entities::traits::TruckConvertible; use crate::scene::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::object::{GripApply, GripDef, PropSection}; use crate::scene::wire_model::SnapHint; fn to_truck(ole: &Ole2Frame) -> TruckEntity { let x0 = ole.upper_left_corner.x; let y0 = ole.lower_right_corner.y; let x1 = ole.lower_right_corner.x; let y1 = ole.upper_left_corner.y; let z = ole.upper_left_corner.z; if (x1 - x0).abs() < 1e-6 && (y1 - y0).abs() < 1e-6 { let s = 0.5_f64; return TruckEntity { object: TruckObject::Lines(vec![[-s, 0.0, z], [s, 0.0, z]]), snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], fill_tris: vec![], }; } let cx = (x0 + x1) * 0.5; let cy = (y0 + y1) * 0.5; let pts: Vec<[f64; 3]> = vec![ // Rectangle [x0, y0, z], [x1, y0, z], [x1, y0, z], [x1, y1, z], [x1, y1, z], [x0, y1, z], [x0, y1, z], [x0, y0, z], // Diagonal X [x0, y0, z], [x1, y1, z], [f64::NAN; 3], [x1, y0, z], [x0, y1, z], ]; let center = Vec3::new(cx as f32, cy as f32, z as f32); TruckEntity { object: TruckObject::Lines(pts), snap_pts: vec![(center, SnapHint::Center)], tangent_geoms: vec![], key_vertices: vec![[x0, y0, z], [x1, y1, z]], fill_tris: vec![], } } fn grips(ole: &Ole2Frame) -> Vec { let ul = glam::DVec3::new( ole.upper_left_corner.x, ole.upper_left_corner.y, ole.upper_left_corner.z, ); let lr = glam::DVec3::new( ole.lower_right_corner.x, ole.lower_right_corner.y, ole.lower_right_corner.z, ); let center = (ul + lr) * 0.5; vec![ square_grip(0, ul), square_grip(1, lr), center_grip(2, center), ] } fn properties(ole: &Ole2Frame) -> PropSection { let type_str = match ole.ole_object_type { OleObjectType::Link => "Link", OleObjectType::Embedded => "Embedded", OleObjectType::Static => "Static", }; PropSection { title: "Geometry".into(), props: vec![ ro("Type", "ole_type", type_str), edit("Upper Left X", "ole_ulx", ole.upper_left_corner.x), edit("Upper Left Y", "ole_uly", ole.upper_left_corner.y), edit("Lower Right X", "ole_lrx", ole.lower_right_corner.x), edit("Lower Right Y", "ole_lry", ole.lower_right_corner.y), ro("Version", "ole_version", ole.version.to_string()), ro( "Source App", "ole_source_app", if ole.source_application.is_empty() { "(unknown)".to_string() } else { ole.source_application.clone() }, ), ro( "Data Size", "ole_data_size", format!("{} bytes", ole.binary_data.len()), ), ro("DWG Mode", "ole_dwg_mode", ole.dwg_mode.to_string()), ro( "DWG Trailing Byte", "ole_dwg_trailing", format!("{:#04x}", ole.dwg_trailing_byte), ), ro( "Paper Space", "ole_paper_space", if ole.is_paper_space { "Yes" } else { "No" }, ), ], } } fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) { let Ok(v) = value.trim().parse::() else { return; }; match field { "ole_ulx" => ole.upper_left_corner.x = v, "ole_uly" => ole.upper_left_corner.y = v, "ole_lrx" => ole.lower_right_corner.x = v, "ole_lry" => ole.lower_right_corner.y = v, _ => {} } } fn apply_grip(ole: &mut Ole2Frame, grip_id: usize, apply: GripApply) { match (grip_id, apply) { (0, GripApply::Absolute(p)) => { ole.upper_left_corner.x = p.x as f64; ole.upper_left_corner.y = p.y as f64; } (1, GripApply::Absolute(p)) => { ole.lower_right_corner.x = p.x as f64; ole.lower_right_corner.y = p.y as f64; } (2, GripApply::Translate(d)) => { ole.upper_left_corner.x += d.x as f64; ole.upper_left_corner.y += d.y as f64; ole.lower_right_corner.x += d.x as f64; ole.lower_right_corner.y += d.y as f64; } _ => {} } } fn apply_transform(ole: &mut Ole2Frame, t: &EntityTransform) { match t { EntityTransform::Translate(d) => { ole.upper_left_corner.x += d.x as f64; ole.upper_left_corner.y += d.y as f64; ole.upper_left_corner.z += d.z as f64; ole.lower_right_corner.x += d.x as f64; ole.lower_right_corner.y += d.y as f64; ole.lower_right_corner.z += d.z as f64; } EntityTransform::Scale { center, factor } => { let scale = |v: f64, c: f64| c + (v - c) * (*factor as f64); ole.upper_left_corner.x = scale(ole.upper_left_corner.x, center.x as f64); ole.upper_left_corner.y = scale(ole.upper_left_corner.y, center.y as f64); ole.lower_right_corner.x = scale(ole.lower_right_corner.x, center.x as f64); ole.lower_right_corner.y = scale(ole.lower_right_corner.y, center.y as f64); } _ => {} } } impl TruckConvertible for Ole2Frame { fn to_truck(&self, _document: &acadrust::CadDocument) -> Option { Some(to_truck(self)) } } crate::impl_entity_basics!(Ole2Frame); impl crate::entities::traits::FallbackTess for Ole2Frame { fn fallback_geometry( &self, world_offset: [f64; 3], ) -> crate::scene::tess_util::FallbackGeometry { // OLE objects carry a bounding rectangle in model space. // Render a simple X-through-rectangle placeholder. let [ox, oy, oz] = world_offset; let x0 = (self.upper_left_corner.x - ox) as f32; let y0 = (self.lower_right_corner.y - oy) as f32; let x1 = (self.lower_right_corner.x - ox) as f32; let y1 = (self.upper_left_corner.y - oy) as f32; let z = (self.upper_left_corner.z - oz) as f32; if (x1 - x0).abs() < 1e-6 && (y1 - y0).abs() < 1e-6 { // Degenerate / unknown size — show a small cross. let s = 0.5_f32; return (vec![[-s, 0.0, 0.0], [s, 0.0, 0.0]], vec![], vec![], vec![]); } let pts = vec![ // Outer rectangle [x0, y0, z], [x1, y0, z], [x1, y0, z], [x1, y1, z], [x1, y1, z], [x0, y1, z], [x0, y1, z], [x0, y0, z], // Diagonal X [x0, y0, z], [x1, y1, z], [f32::NAN, f32::NAN, f32::NAN], [x1, y0, z], [x0, y1, z], ]; (pts, vec![], vec![], vec![[x0, y0, z], [x1, y1, z]]) } }