From c653a3f38ecb0df07b4707d237cf8c069c4a611a Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Mon, 29 Jun 2026 09:16:44 +0300 Subject: [PATCH] feat(commands): add FLATSHOT and CONVTOSURFACE - FLATSHOT: project the selected solid's edges to 2D Line entities at Z=0 - CONVTOSURFACE: convert the selected solid(s) to Surface entities Both reuse the cached B-rep's edge wires. COMMANDS.md: 219/262. Co-Authored-By: Claude Opus 4.8 --- COMMANDS.md | 8 ++-- src/app/commands/draw.rs | 10 +++++ src/app/commands/mod.rs | 2 + src/app/model_ops.rs | 92 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/COMMANDS.md b/COMMANDS.md index cb46ae1cd..8c5f914d3 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -230,9 +230,9 @@ Status of every standard CAD command in Open CAD Studio: | `INTERFERE` | INF | Interference solid from overlap | ✅ | | `SECTION` | — | Cross-section outline of a solid | ✅ | | `CONVTOSOLID` | — | Convert to solid | ❌ | -| `CONVTOSURFACE` | — | Convert to surface | ❌ | +| `CONVTOSURFACE` | — | Convert to surface | ✅ | | `SECTIONPLANE` | — | Section plane object | ❌ | -| `FLATSHOT` | — | 2D view from 3D | ❌ | +| `FLATSHOT` | — | 2D view from 3D | ✅ | --- @@ -357,12 +357,12 @@ Status of every standard CAD command in Open CAD Studio: | Text & Table | 10 | 7 | 1 | 2 | | Layer | 19 | 17 | 0 | 2 | | Block & Reference | 23 | 18 | 2 | 3 | -| 3D Modeling | 27 | 23 | 0 | 4 | +| 3D Modeling | 27 | 25 | 0 | 2 | | View & Navigation | 32 | 15 | 8 | 9 | | Inquiry | 12 | 12 | 0 | 0 | | File & Plot | 17 | 16 | 0 | 1 | | Manage & Customize | 22 | 13 | 3 | 6 | -| **Total** | **262** | **217** | **15** | **30** | +| **Total** | **262** | **219** | **15** | **28** | > Counts include commands listed under more than one category (e.g. `SLICE`, `HELIX`, > `MINSERT`, `SUBTRACT`/`UNION`/`INTERSECT` appear in both their 2D and 3D groups). diff --git a/src/app/commands/draw.rs b/src/app/commands/draw.rs index 9e6d2d1bc..8bf570514 100644 --- a/src/app/commands/draw.rs +++ b/src/app/commands/draw.rs @@ -704,6 +704,16 @@ impl OpenCADStudio { return Some(self.solid_interfere()); } + // FLATSHOT — flatten the selected solid's edges to 2D lines at Z=0. + "FLATSHOT" => { + return Some(self.solid_flatshot()); + } + + // CONVTOSURFACE — convert the selected solid(s) to surface entities. + "CONVTOSURFACE" => { + return Some(self.solid_convtosurface()); + } + // POLYSOLID — extrude a selected polyline into a // wall-like solid. cmd if cmd == "POLYSOLID" || cmd.starts_with("POLYSOLID ") => { diff --git a/src/app/commands/mod.rs b/src/app/commands/mod.rs index dc385aed3..cf9e58b34 100644 --- a/src/app/commands/mod.rs +++ b/src/app/commands/mod.rs @@ -346,6 +346,8 @@ inventory::submit!(crate::command::CommandRegistration { "REG", "ARCHIVE", "ETRANSMIT", + "FLATSHOT", + "CONVTOSURFACE", // Slice/section + interference + press-pull/thicken + 3D transforms + wall/pyramid. "SLICE", "SL", diff --git a/src/app/model_ops.rs b/src/app/model_ops.rs index a7d77203c..accf338ee 100644 --- a/src/app/model_ops.rs +++ b/src/app/model_ops.rs @@ -748,4 +748,96 @@ impl super::OpenCADStudio { .push_output(&format!("SPLINEFIT: fit a spline through {n} points.")); Task::none() } + + /// FLATSHOT — project the selected solid's edges onto the XY plane (Z=0) as + /// Line entities, giving a flattened 2D shot of the model. Reuses the cached + /// solid's edge wires (the same source SECTION uses). + pub(super) fn solid_flatshot(&mut self) -> Task { + use acadrust::types::Vector3; + use acadrust::Line; + let i = self.active_tab; + let handles: Vec = self.tabs[i] + .scene + .selected + .iter() + .copied() + .filter(|h| self.tabs[i].scene.solid_models.contains_key(h)) + .collect(); + if handles.is_empty() { + self.command_line + .push_error("FLATSHOT: select a solid created this session."); + return Task::none(); + } + self.push_undo_snapshot(i, "FLATSHOT"); + let mut n = 0usize; + for h in &handles { + let solid = self.tabs[i].scene.solid_models[h].clone(); + for w in solid_model::edge_wires(&solid) { + for seg in w.points.windows(2) { + let line = Line::from_points( + Vector3::new(seg[0].x, seg[0].y, 0.0), + Vector3::new(seg[1].x, seg[1].y, 0.0), + ); + self.tabs[i].scene.add_entity(EntityType::Line(line)); + n += 1; + } + } + } + self.tabs[i].dirty = true; + self.refresh_properties(); + self.command_line + .push_output(&format!("FLATSHOT: created {n} projected edge(s) at Z=0.")); + Task::none() + } + + /// CONVTOSURFACE — convert the selected solid(s) into Surface entities, + /// carrying the solid's edge wires (reuses the cached B-rep's edge wires). + pub(super) fn solid_convtosurface(&mut self) -> Task { + use acadrust::entities::{Surface, SurfaceKind, Wire as AWire}; + use acadrust::types::Vector3; + let i = self.active_tab; + let handles: Vec = self.tabs[i] + .scene + .selected + .iter() + .copied() + .filter(|h| self.tabs[i].scene.solid_models.contains_key(h)) + .collect(); + if handles.is_empty() { + self.command_line + .push_error("CONVTOSURFACE: select a solid created this session."); + return Task::none(); + } + let mut surfaces: Vec = Vec::new(); + for h in &handles { + let solid = self.tabs[i].scene.solid_models[h].clone(); + let awires: Vec = solid_model::edge_wires(&solid) + .into_iter() + .map(|w| { + let mut aw = AWire::new(); + aw.points = w + .points + .iter() + .map(|p| Vector3::new(p.x, p.y, p.z)) + .collect(); + aw + }) + .collect(); + let mut surf = Surface::new(SurfaceKind::Generic); + surf.wires = awires; + surf.common.layer = self.tabs[i].active_layer.clone(); + surfaces.push(surf); + } + self.push_undo_snapshot(i, "CONVTOSURFACE"); + self.tabs[i].scene.erase_entities(&handles); + let n = surfaces.len(); + for surf in surfaces { + self.tabs[i].scene.add_entity(EntityType::Surface(surf)); + } + self.tabs[i].dirty = true; + self.refresh_properties(); + self.command_line + .push_output(&format!("CONVTOSURFACE: converted {n} solid(s) to surface(s).")); + Task::none() + } }