diff --git a/ROADMAP.md b/ROADMAP.md index c8c2cd70f..43458c453 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -279,7 +279,7 @@ Underlay (PDF/DWF/DGN) | Sağ tık bağlam menüsü | ✅ | | Araç çubuğu özelleştirme | ⬜ | | Tema / Renk şeması seçimi | ✅ | -| Klavye kısayol düzenleyici | ⬜ | +| Klavye kısayol düzenleyici | ✅ | | Komut geçmişi gezinme (↑↓) | ✅ | --- diff --git a/src/scene/mod.rs b/src/scene/mod.rs index a65894903..746c655e6 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1094,11 +1094,20 @@ impl Scene { } else { None }; - let mesh_seed = if let EntityType::Solid3D(s3d) = &entity { - let color = self.render_style(&entity).0; - solid3d_tess::tessellate_solid3d(s3d, color) - } else { - None + let mesh_seed = match &entity { + EntityType::Solid3D(s3d) => { + let color = self.render_style(&entity).0; + solid3d_tess::tessellate_solid3d(s3d, color) + } + EntityType::Region(r) => { + let color = self.render_style(&entity).0; + solid3d_tess::tessellate_region(r, color) + } + EntityType::Body(b) => { + let color = self.render_style(&entity).0; + solid3d_tess::tessellate_body(b, color) + } + _ => None, }; // Auto-create an ImageDefinition object for new RasterImage entities @@ -1605,25 +1614,30 @@ impl Scene { /// `Solid3D` entity is represented in the mesh cache. pub fn populate_meshes_from_document(&mut self) { self.meshes.clear(); - let entries: Vec<(Handle, Solid3D)> = self + // Collect all ACIS-bearing entities: Solid3D, Region, Body. + let entries: Vec<(Handle, EntityType)> = self .document .entities() - .filter_map(|e| { - if let EntityType::Solid3D(s) = e { - Some((s.common.handle, s.clone())) - } else { - None - } + .filter_map(|e| match e { + EntityType::Solid3D(_) | EntityType::Region(_) | EntityType::Body(_) => + Some((e.common().handle, e.clone())), + _ => None, }) .collect(); - for (handle, solid) in entries { + for (handle, entity) in entries { let color = if let Some(e) = self.document.get_entity(handle) { tessellate::aci_to_rgba(&e.common().color) } else { [0.7, 0.7, 0.7, 1.0] }; - if let Some(model) = solid3d_tess::tessellate_solid3d(&solid, color) { - self.meshes.insert(handle, model); + let model = match &entity { + EntityType::Solid3D(s) => solid3d_tess::tessellate_solid3d(s, color), + EntityType::Region(r) => solid3d_tess::tessellate_region(r, color), + EntityType::Body(b) => solid3d_tess::tessellate_body(b, color), + _ => None, + }; + if let Some(m) = model { + self.meshes.insert(handle, m); } } } diff --git a/src/scene/solid3d_tess.rs b/src/scene/solid3d_tess.rs index d4ce01cc2..1519b7069 100644 --- a/src/scene/solid3d_tess.rs +++ b/src/scene/solid3d_tess.rs @@ -18,7 +18,7 @@ use acadrust::entities::acis::{ SatPointer, SatSphereSurface, SatTorusSurface, SatVertex, }; use acadrust::entities::acis::types::Sense; -use acadrust::entities::Solid3D; +use acadrust::entities::{Body, Region, Solid3D}; use crate::scene::mesh_model::MeshModel; @@ -30,32 +30,24 @@ const GRID_V: usize = 16; // ── Public entry point ──────────────────────────────────────────────────────── -/// Tessellate a `Solid3D` entity into a GPU-ready `MeshModel`. -/// -/// Returns `None` when the entity has no parseable SAT data or produces no -/// triangles (e.g. the solid uses only unsupported surface types). -pub fn tessellate_solid3d(solid: &Solid3D, color: [f32; 4]) -> Option { - let sat = solid.parse_sat()?; - +/// Tessellate a SAT document into mesh buffers — shared by all ACIS entities. +fn tessellate_sat(sat: &SatDocument, name: String, color: [f32; 4]) -> Option { let mut verts: Vec<[f32; 3]> = Vec::new(); let mut normals: Vec<[f32; 3]> = Vec::new(); let mut indices: Vec = Vec::new(); for face in sat.faces() { let surf_ptr = face.surface(); - let Some(surf_rec) = sat.resolve(surf_ptr) else { - continue; - }; - + let Some(surf_rec) = sat.resolve(surf_ptr) else { continue }; match surf_rec.entity_type.as_str() { "plane-surface" => { if let Some(plane) = SatPlaneSurface::from_record(surf_rec) { - tess_plane_face(&sat, &face, &plane, &mut verts, &mut normals, &mut indices); + tess_plane_face(sat, &face, &plane, &mut verts, &mut normals, &mut indices); } } "cone-surface" => { if let Some(cone) = SatConeSurface::from_record(surf_rec) { - tess_cone_face(&sat, &face, &cone, &mut verts, &mut normals, &mut indices); + tess_cone_face(sat, &face, &cone, &mut verts, &mut normals, &mut indices); } } "sphere-surface" => { @@ -71,19 +63,32 @@ pub fn tessellate_solid3d(solid: &Solid3D, color: [f32; 4]) -> Option _ => {} } } + if indices.is_empty() { return None; } + Some(MeshModel { name, verts, normals, indices, color, selected: false }) +} - if indices.is_empty() { - return None; - } +/// Tessellate a `Region` entity (2D planar ACIS body) into a `MeshModel`. +pub fn tessellate_region(region: &Region, color: [f32; 4]) -> Option { + let sat = region.parse_sat()?; + let name = region.common.handle.value().to_string(); + tessellate_sat(&sat, name, color) +} - Some(MeshModel { - name: solid.common.handle.value().to_string(), - verts, - normals, - indices, - color, - selected: false, - }) +/// Tessellate a `Body` entity (3D ACIS body) into a `MeshModel`. +pub fn tessellate_body(body: &Body, color: [f32; 4]) -> Option { + let sat = body.parse_sat()?; + let name = body.common.handle.value().to_string(); + tessellate_sat(&sat, name, color) +} + +/// Tessellate a `Solid3D` entity into a GPU-ready `MeshModel`. +/// +/// Returns `None` when the entity has no parseable SAT data or produces no +/// triangles (e.g. the solid uses only unsupported surface types). +pub fn tessellate_solid3d(solid: &Solid3D, color: [f32; 4]) -> Option { + let sat = solid.parse_sat()?; + let name = solid.common.handle.value().to_string(); + tessellate_sat(&sat, name, color) } // ── Topology helpers ──────────────────────────────────────────────────────────