From e345d895190cfc1ea09a5c4c230ad4e7b082306d Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:50:49 +0300 Subject: [PATCH] Extend PEDIT for polygon meshes --- src/app/commands/inquiry.rs | 13 ++ src/app/properties.rs | 1 + src/app/update/mod.rs | 1 + src/modules/draw/modify/pedit.rs | 202 ++++++++++++++++++++++++++++--- 4 files changed, 203 insertions(+), 14 deletions(-) diff --git a/src/app/commands/inquiry.rs b/src/app/commands/inquiry.rs index a49778ca1..bc05b5a7d 100644 --- a/src/app/commands/inquiry.rs +++ b/src/app/commands/inquiry.rs @@ -262,6 +262,7 @@ impl OpenCADStudio { PeditTarget { is_poly: true, convertible: false, + mesh_size: None, }, )), acadrust::EntityType::Line(_) | acadrust::EntityType::Arc(_) => { @@ -270,9 +271,21 @@ impl OpenCADStudio { PeditTarget { is_poly: false, convertible: true, + mesh_size: None, }, )) } + acadrust::EntityType::PolygonMesh(mesh) => Some(( + h, + PeditTarget { + is_poly: true, + convertible: false, + mesh_size: Some(( + mesh.m_vertex_count.max(0) as usize, + mesh.n_vertex_count.max(0) as usize, + )), + }, + )), _ => None, } }) diff --git a/src/app/properties.rs b/src/app/properties.rs index d884905a7..2a64f06e5 100644 --- a/src/app/properties.rs +++ b/src/app/properties.rs @@ -121,6 +121,7 @@ impl OpenCADStudio { .and_then(|entity| match entity { acadrust::EntityType::LwPolyline(polyline) => Some(polyline.vertices.len()), acadrust::EntityType::Polyline2D(polyline) => Some(polyline.vertices.len()), + acadrust::EntityType::PolygonMesh(mesh) => Some(mesh.vertices.len()), acadrust::EntityType::Leader(leader) => Some(leader.vertices.len()), acadrust::EntityType::Spline(spline) => { Some(if crate::entities::spline::shows_fit_points(spline) { diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index cb501dc7d..65835793d 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -4901,6 +4901,7 @@ impl OpenCADStudio { match self.tabs[i].scene.document.get_entity(handles[0]) { Some(acadrust::EntityType::LwPolyline(p)) => p.vertices.len(), Some(acadrust::EntityType::Polyline2D(p)) => p.vertices.len(), + Some(acadrust::EntityType::PolygonMesh(p)) => p.vertices.len(), Some(acadrust::EntityType::Table(table)) => { table.row_count().saturating_mul(table.column_count()) } diff --git a/src/modules/draw/modify/pedit.rs b/src/modules/draw/modify/pedit.rs index c8403f087..7e5a84d46 100644 --- a/src/modules/draw/modify/pedit.rs +++ b/src/modules/draw/modify/pedit.rs @@ -31,6 +31,8 @@ pub struct PeditTarget { pub is_poly: bool, /// Line / Arc — offered for conversion on pick. pub convertible: bool, + /// M/N size for a legacy polygon mesh; absent for ordinary polylines. + pub mesh_size: Option<(usize, usize)>, } enum Mode { @@ -41,6 +43,10 @@ enum Mode { AwaitWidth, /// Join: gathering additional segments; Enter merges. JoinGather(Vec), + /// Polygon-mesh vertex navigation uses the mesh's row-major control net. + MeshVertex(usize), + /// Waiting for the replacement location of the selected mesh vertex. + MeshVertexMove(usize), } pub struct PeditCommand { @@ -78,6 +84,11 @@ impl PeditCommand { self } + fn mesh_size(&self) -> Option<(usize, usize)> { + let handle = self.target?; + self.info.get(&handle.value())?.mesh_size + } + } impl CadCommand for PeditCommand { @@ -100,6 +111,16 @@ impl CadCommand for PeditCommand { count = list.len().saturating_sub(1) ) .into_owned(), + Mode::MeshVertex(index) => t!( + "PEDIT Edit mesh vertex %{vertex} Enter option:", + vertex = index + 1 + ) + .into_owned(), + Mode::MeshVertexMove(index) => t!( + "PEDIT Specify new location for mesh vertex %{vertex}:", + vertex = index + 1 + ) + .into_owned(), Mode::Options => t!("PEDIT Enter option:").into_owned(), } } @@ -107,20 +128,43 @@ impl CadCommand for PeditCommand { fn options(&self) -> Vec { use crate::command::CmdOption; match &self.mode { + Mode::Options if self.mesh_size().is_some() => vec![ + CmdOption::new(t!("Edit vertex").as_ref(), "E"), + CmdOption::new(t!("Smooth surface").as_ref(), "S"), + CmdOption::new(t!("Desmooth").as_ref(), "D"), + CmdOption::new(t!("M close").as_ref(), "MC"), + CmdOption::new(t!("M open").as_ref(), "MO"), + CmdOption::new(t!("N close").as_ref(), "NC"), + CmdOption::new(t!("N open").as_ref(), "NO"), + CmdOption::new(t!("Undo").as_ref(), "U"), + CmdOption::new(t!("eXit").as_ref(), "X"), + ], Mode::Options => vec![ - CmdOption::new(t!("Close").as_ref(), "C"), - CmdOption::new(t!("Open").as_ref(), "O"), - CmdOption::new(t!("Join").as_ref(), "J"), - CmdOption::new(t!("Width").as_ref(), "W"), - CmdOption::new(t!("Fit").as_ref(), "F"), - CmdOption::new(t!("Spline").as_ref(), "S"), - CmdOption::new(t!("Decurve").as_ref(), "D"), + CmdOption::new(t!("Close").as_ref(), "C"), + CmdOption::new(t!("Open").as_ref(), "O"), + CmdOption::new(t!("Join").as_ref(), "J"), + CmdOption::new(t!("Width").as_ref(), "W"), + CmdOption::new(t!("Fit").as_ref(), "F"), + CmdOption::new(t!("Spline").as_ref(), "S"), + CmdOption::new(t!("Decurve").as_ref(), "D"), + CmdOption::new(t!("eXit").as_ref(), "X"), + ], + Mode::MeshVertex(_) => vec![ + CmdOption::new(t!("Next").as_ref(), "N"), + CmdOption::new(t!("Previous").as_ref(), "P"), + CmdOption::new(t!("Left").as_ref(), "L"), + CmdOption::new(t!("Right").as_ref(), "R"), + CmdOption::new(t!("Up").as_ref(), "U"), + CmdOption::new(t!("Down").as_ref(), "D"), + CmdOption::new(t!("Move").as_ref(), "M"), + CmdOption::new(t!("Regen").as_ref(), "G"), CmdOption::new(t!("eXit").as_ref(), "X"), ], Mode::ConvertPrompt(_) => { vec![CmdOption::new(t!("Yes").as_ref(), "Y"), CmdOption::new(t!("No").as_ref(), "N")] } Mode::JoinGather(_) => vec![CmdOption::enter(t!("Join").as_ref())], + Mode::MeshVertexMove(_) => vec![], _ => vec![], } } @@ -174,12 +218,13 @@ impl CadCommand for PeditCommand { // piece as the live target and carry its bookkeeping over. if let Some(&nh) = new_handles.first() { self.info.remove(&old.value()); - self.info.insert( + self.info.insert( nh.value(), - PeditTarget { - is_poly: true, - convertible: false, - }, + PeditTarget { + is_poly: true, + convertible: false, + mesh_size: None, + }, ); self.target = Some(nh); self.mode = Mode::Options; @@ -187,11 +232,12 @@ impl CadCommand for PeditCommand { } fn wants_text_input(&self) -> bool { - !matches!(self.mode, Mode::PickTarget) + !matches!(self.mode, Mode::PickTarget | Mode::MeshVertexMove(_)) } fn on_text_input(&mut self, text: &str) -> Option { let up = text.trim().to_uppercase(); + let mesh_size = self.mesh_size(); match &mut self.mode { Mode::PickTarget => None, Mode::ConvertPrompt(handle) => { @@ -222,8 +268,75 @@ impl CadCommand for PeditCommand { }) } Mode::JoinGather(_) => None, + Mode::MeshVertexMove(_) => None, + Mode::MeshVertex(index) => { + let (m, n) = mesh_size?; + let count = m.saturating_mul(n); + if count == 0 { + self.mode = Mode::Options; + return Some(CmdResult::NeedPoint); + } + let row = *index / n; + let column = *index % n; + match up.as_str() { + "N" | "NEXT" => *index = (*index + 1) % count, + "P" | "PREVIOUS" => *index = (*index + count - 1) % count, + "L" | "LEFT" => *index = row * n + (column + n - 1) % n, + "R" | "RIGHT" => *index = row * n + (column + 1) % n, + "U" | "UP" => *index = ((row + m - 1) % m) * n + column, + "D" | "DOWN" => *index = ((row + 1) % m) * n + column, + "M" | "MOVE" => self.mode = Mode::MeshVertexMove(*index), + "G" | "REGEN" => { + // Mesh display is regenerated after every edit; keep the + // command active without creating a false undo record. + return Some(CmdResult::NeedPoint); + } + "X" | "EXIT" => self.mode = Mode::Options, + _ => return None, + } + Some(CmdResult::NeedPoint) + } Mode::Options => { let handle = self.target?; + if mesh_size.is_some() { + return match up.as_str() { + "E" | "EDIT" | "EDIT VERTEX" => { + self.mode = Mode::MeshVertex(0); + Some(CmdResult::NeedPoint) + } + "S" | "SMOOTH" | "SMOOTH SURFACE" => Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshSmooth( + acadrust::entities::polygon_mesh::SurfaceSmoothType::Quadratic, + ), + }), + "D" | "DESMOOTH" => Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshSmooth( + acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth, + ), + }), + "MC" | "MCLOSE" | "M CLOSE" => Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedM(true), + }), + "MO" | "MOPEN" | "M OPEN" => Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedM(false), + }), + "NC" | "NCLOSE" | "N CLOSE" => Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedN(true), + }), + "NO" | "NOPEN" | "N OPEN" => Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedN(false), + }), + "U" | "UNDO" => Some(CmdResult::UndoDocument), + "X" | "EXIT" => Some(CmdResult::Cancel), + _ => None, + }; + } match up.as_str() { "X" | "EXIT" => Some(CmdResult::Cancel), "C" | "CLOSE" => Some(CmdResult::PeditOp { @@ -272,7 +385,17 @@ impl CadCommand for PeditCommand { } } - fn on_point(&mut self, _pt: DVec3) -> CmdResult { + fn on_point(&mut self, point: DVec3) -> CmdResult { + if let Mode::MeshVertexMove(index) = self.mode { + let Some(handle) = self.target else { + return CmdResult::Cancel; + }; + self.mode = Mode::MeshVertex(index); + return CmdResult::PeditOp { + handle, + op: PeditOp::MoveMeshVertex { index, point }, + }; + } CmdResult::NeedPoint } @@ -303,6 +426,10 @@ pub enum PeditOp { Fit, Spline, Decurve, + SetMeshClosedM(bool), + SetMeshClosedN(bool), + SetMeshSmooth(acadrust::entities::polygon_mesh::SurfaceSmoothType), + MoveMeshVertex { index: usize, point: DVec3 }, } // ── Apply logic (pure entity edits; driver handles convert/break/marker) ── @@ -352,6 +479,53 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool { } _ => false, }, + PeditOp::SetMeshClosedM(closed) => match entity { + EntityType::PolygonMesh(mesh) => { + mesh.flags.set( + acadrust::entities::polygon_mesh::PolygonMeshFlags::CLOSED_M, + *closed, + ); + true + } + _ => false, + }, + PeditOp::SetMeshClosedN(closed) => match entity { + EntityType::PolygonMesh(mesh) => { + mesh.flags.set( + acadrust::entities::polygon_mesh::PolygonMeshFlags::CLOSED_N, + *closed, + ); + true + } + _ => false, + }, + PeditOp::SetMeshSmooth(smooth) => match entity { + EntityType::PolygonMesh(mesh) => { + mesh.smooth_type = *smooth; + if mesh.smooth_type + != acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth + { + if mesh.m_smooth_density < 2 { + mesh.m_smooth_density = 6; + } + if mesh.n_smooth_density < 2 { + mesh.n_smooth_density = 6; + } + } + true + } + _ => false, + }, + PeditOp::MoveMeshVertex { index, point } => match entity { + EntityType::PolygonMesh(mesh) => { + let Some(vertex) = mesh.vertices.get_mut(*index) else { + return false; + }; + vertex.location = acadrust::types::Vector3::new(point.x, point.y, point.z); + true + } + _ => false, + }, // Handled by the driver (it replaces the entity, not edits in place). PeditOp::ConvertToPolyline => false, }