From 9d947d75c5b68c8ed2f3abb13e16a6a1c2063f1e Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:13:12 +0300 Subject: [PATCH 1/3] Align polygon mesh properties --- locales/en-US/opencadstudio.ftl | 2 +- src/app/update/command.rs | 9 ++++- src/entities/mesh.rs | 64 ++++++++++++++++----------------- src/entities/names.rs | 2 +- src/locale_catalog.rs | 2 +- src/ui/properties.rs | 2 +- 6 files changed, 42 insertions(+), 39 deletions(-) diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 3f61bddb1..a90b105d2 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -2829,7 +2829,7 @@ properties = .point-y = Point Y .point-z = Point Z .polyfacemesh = PolyfaceMesh - .polygonmesh = PolygonMesh + .polygonmesh = Polygon Mesh .polyline2d = Polyline2D .polyline3d = 3D Polyline .predefined = Predefined diff --git a/src/app/update/command.rs b/src/app/update/command.rs index ce1b6256d..cd1793a08 100644 --- a/src/app/update/command.rs +++ b/src/app/update/command.rs @@ -2628,7 +2628,10 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task { } else { raw_val }; - if matches!(field, "current_fit_point" | "current_control_point") { + if matches!( + field, + "current_fit_point" | "current_control_point" | "pm_current_vertex" + ) { let count = handles .iter() .filter_map(|handle| { @@ -2644,6 +2647,10 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task { ) => Some( crate::entities::spline::control_vertex_count(spline), ), + ( + "pm_current_vertex", + acadrust::EntityType::PolygonMesh(mesh), + ) => Some(mesh.vertices.len()), _ => None, } }) diff --git a/src/entities/mesh.rs b/src/entities/mesh.rs index 0e60322d4..772214544 100644 --- a/src/entities/mesh.rs +++ b/src/entities/mesh.rs @@ -449,24 +449,16 @@ impl PropertyEditable for PolygonMesh { let current = crate::scene::view::dispatch::prop_current_vertex() .min(vertex_count.saturating_sub(1)); let vertex = self.vertices.get(current); - let display = if vertex_count == 0 { - "—".to_string() - } else { - format!("{} / {}", current + 1, vertex_count) - }; - let smoothed = self.smooth_type != SurfaceSmoothType::NoSmooth; - let density_prop = |label: &str, field: &'static str, value: i16| { - if smoothed { - edit_scalar(label, field, value as f64) - } else { - ro(label, field, value.to_string()) - } - }; + let current_vertex = if vertex_count == 0 { 0 } else { current + 1 }; vec![ PropSection { title: t!("Geometry").into_owned(), props: vec![ - stepper(t!("Current Vertex").as_ref(), "pm_current_vertex", display), + edit_scalar( + t!("Current Vertex").as_ref(), + "pm_current_vertex", + current_vertex as f64, + ), edit(t!("Vertex X").as_ref(), "pm_vx", vertex.map_or(0.0, |vertex| vertex.location.x), @@ -479,26 +471,34 @@ impl PropertyEditable for PolygonMesh { "pm_vz", vertex.map_or(0.0, |vertex| vertex.location.z), ), - ro(t!("M vertex count").as_ref(), "pm_m", self.m_vertex_count.to_string()), - ro(t!("N vertex count").as_ref(), "pm_n", self.n_vertex_count.to_string()), Property { label: t!("M closed").into_owned(), field: "pm_closed_m", - value: PropValue::BoolToggle { - field: "pm_closed_m", - value: self.is_closed_m(), + value: PropValue::Choice { + selected: if self.is_closed_m() { "Yes" } else { "No" }.to_string(), + options: vec!["No".to_string(), "Yes".to_string()], }, }, Property { label: t!("N closed").into_owned(), field: "pm_closed_n", - value: PropValue::BoolToggle { - field: "pm_closed_n", - value: self.is_closed_n(), + value: PropValue::Choice { + selected: if self.is_closed_n() { "Yes" } else { "No" }.to_string(), + options: vec!["No".to_string(), "Yes".to_string()], }, }, - density_prop(t!("M density").as_ref(), "pm_smooth_m", self.m_smooth_density), - density_prop(t!("N density").as_ref(), "pm_smooth_n", self.n_smooth_density), + edit_scalar( + t!("M density").as_ref(), + "pm_smooth_m", + self.m_smooth_density as f64, + ), + edit_scalar( + t!("N density").as_ref(), + "pm_smooth_n", + self.n_smooth_density as f64, + ), + ro(t!("M vertex count").as_ref(), "pm_m", self.m_vertex_count.to_string()), + ro(t!("N vertex count").as_ref(), "pm_n", self.n_vertex_count.to_string()), ], }, PropSection { @@ -559,26 +559,22 @@ impl PropertyEditable for PolygonMesh { }; self.smooth_type = smooth; if self.smooth_type != SurfaceSmoothType::NoSmooth { - if !(2..=256).contains(&self.m_smooth_density) { + if !(2..=200).contains(&self.m_smooth_density) { self.m_smooth_density = 6; } - if !(2..=256).contains(&self.n_smooth_density) { + if !(2..=200).contains(&self.n_smooth_density) { self.n_smooth_density = 6; } } } "pm_smooth_m" => { - if self.smooth_type != SurfaceSmoothType::NoSmooth { - if let Ok(density @ 2..=256) = value.trim().parse::() { - self.m_smooth_density = density; - } + if let Ok(density @ 0..=200) = value.trim().parse::() { + self.m_smooth_density = density; } } "pm_smooth_n" => { - if self.smooth_type != SurfaceSmoothType::NoSmooth { - if let Ok(density @ 2..=256) = value.trim().parse::() { - self.n_smooth_density = density; - } + if let Ok(density @ 0..=200) = value.trim().parse::() { + self.n_smooth_density = density; } } "pm_vx" | "pm_vy" | "pm_vz" => { diff --git a/src/entities/names.rs b/src/entities/names.rs index 3dde0f074..9a00c9ffe 100644 --- a/src/entities/names.rs +++ b/src/entities/names.rs @@ -33,7 +33,7 @@ pub fn ui_name(e: &EntityType) -> &'static str { EntityType::Polyline2D(_) => "Polyline2D", EntityType::Polyline3D(_) => "Polyline3D", EntityType::PolyfaceMesh(_) => "PolyfaceMesh", - EntityType::PolygonMesh(_) => "PolygonMesh", + EntityType::PolygonMesh(_) => "Polygon Mesh", EntityType::Text(_) => "Text", EntityType::MText(_) => "MText", EntityType::Dimension(_) => "Dimension", diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index f86fcc48e..5de56c86a 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -2067,7 +2067,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Polygon - Circumscribed" => Some(("draw", "polygon-circumscribed")), "Polygon - Edge" => Some(("draw", "polygon-edge")), "Polygon - Inscribed" => Some(("draw", "polygon-inscribed")), - "PolygonMesh" => Some(("properties", "polygonmesh")), + "Polygon Mesh" => Some(("properties", "polygonmesh")), "Polygonal" => Some(("layout", "polygonal")), "Polyline" => Some(("common", "polyline")), "Polyline broken at vertex." => Some(("command", "polyline-broken-at-vertex")), diff --git a/src/ui/properties.rs b/src/ui/properties.rs index 8d7262233..fadb6e918 100644 --- a/src/ui/properties.rs +++ b/src/ui/properties.rs @@ -1788,7 +1788,7 @@ fn coord_suffix(label: &str) -> Option<(&str, usize)> { /// Length of the coordinate group starting at `idx`: consecutive text rows /// labelled " X", " Y" and optionally " Z". 0/1 = no group. fn coord_group_len(props: &[crate::scene::model::object::Property], idx: usize) -> usize { - if props[idx].field == "pl3_vertex_x" { + if matches!(props[idx].field, "pl3_vertex_x" | "pm_vx") { return 0; } let groupable = |p: &crate::scene::model::object::Property| { From def14b31957074f071563aee914f7f2c7242f303 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:13:19 +0300 Subject: [PATCH 2/3] Differentiate polygon mesh surface fits --- src/entities/mesh.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/entities/mesh.rs b/src/entities/mesh.rs index 772214544..af3775653 100644 --- a/src/entities/mesh.rs +++ b/src/entities/mesh.rs @@ -306,13 +306,15 @@ fn polygon_mesh_display(mesh: &PolygonMesh) -> (Vec<[f64; 3]>, usize, usize) { return (base, m, n); } - let degree = match mesh.smooth_type { + let (u_degree, v_degree) = match mesh.smooth_type { SurfaceSmoothType::NoSmooth => return (base, m, n), - SurfaceSmoothType::Quadratic => 2, - SurfaceSmoothType::Cubic | SurfaceSmoothType::Bezier => 3, + SurfaceSmoothType::Quadratic => (2.min(m.saturating_sub(1)), 2.min(n.saturating_sub(1))), + SurfaceSmoothType::Cubic => (3.min(m.saturating_sub(1)), 3.min(n.saturating_sub(1))), + // A Bezier surface uses every row/column in its polynomial patch. + // Giving it the cubic B-spline degree made both choices render the + // same surface even though the stored mesh type was different. + SurfaceSmoothType::Bezier => (m.saturating_sub(1), n.saturating_sub(1)), }; - let u_degree = degree.min(m.saturating_sub(1)); - let v_degree = degree.min(n.saturating_sub(1)); if u_degree == 0 || v_degree == 0 { return (base, m, n); } @@ -329,10 +331,10 @@ fn polygon_mesh_display(mesh: &PolygonMesh) -> (Vec<[f64; 3]>, usize, usize) { let display_m = usize::try_from(mesh.m_smooth_density.max(2)) .unwrap_or(2) - .min(256); + .min(200); let display_n = usize::try_from(mesh.n_smooth_density.max(2)) .unwrap_or(2) - .min(256); + .min(200); let parameter = |index: usize, count: usize, closed: bool| { if closed { index as f64 / count as f64 From 470e22c80fbc486ef20ef39fddcc19a66edadef1 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:13:27 +0300 Subject: [PATCH 3/3] Complete polygon mesh editing workflow --- src/app/commands/inquiry.rs | 12 +- src/app/commands/styleprops.rs | 39 +++--- src/modules/draw/modify/pedit.rs | 226 ++++++++++++++++++++++++------- 3 files changed, 211 insertions(+), 66 deletions(-) diff --git a/src/app/commands/inquiry.rs b/src/app/commands/inquiry.rs index d1577fc3e..9458f465b 100644 --- a/src/app/commands/inquiry.rs +++ b/src/app/commands/inquiry.rs @@ -253,6 +253,7 @@ impl OpenCADStudio { is_poly: true, convertible: false, mesh_size: None, + mesh_closed: None, }, )), acadrust::EntityType::Line(_) | acadrust::EntityType::Arc(_) => { @@ -262,6 +263,7 @@ impl OpenCADStudio { is_poly: false, convertible: true, mesh_size: None, + mesh_closed: None, }, )) } @@ -274,6 +276,7 @@ impl OpenCADStudio { mesh.m_vertex_count.max(0) as usize, mesh.n_vertex_count.max(0) as usize, )), + mesh_closed: Some((mesh.is_closed_m(), mesh.is_closed_n())), }, )), _ => None, @@ -284,7 +287,14 @@ impl OpenCADStudio { // the convert prompt) skips the select step. let preselected: Vec = self.tabs[i].scene.selected.iter().copied().collect(); - let cmd_obj = PeditCommand::new(info).with_preselection(&preselected); + let header = &self.tabs[i].scene.document.header; + let cmd_obj = PeditCommand::new( + info, + header.surface_type, + header.surface_u_density, + header.surface_v_density, + ) + .with_preselection(&preselected); self.command_line.push_info(&cmd_obj.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd_obj)); } diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index 381d1c186..471c67f08 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -1667,33 +1667,36 @@ impl OpenCADStudio { None => Ok((format!("SPLINESEGS = {}", h.spline_segments), false)), }, "SURFU" => match &value { - Some(v) => v - .parse::() - .map(|x| { + Some(v) => match v.parse::() { + Ok(x @ 0..=200) => { h.surface_u_density = x; - (format!("SURFU = {x}"), true) - }) - .map_err(|_| "SETVAR: integer value required.".into()), + Ok((format!("SURFU = {x}"), true)) + } + Ok(_) => Err("SETVAR: value must be between 0 and 200.".into()), + Err(_) => Err("SETVAR: integer value required.".into()), + }, None => Ok((format!("SURFU = {}", h.surface_u_density), false)), }, "SURFV" => match &value { - Some(v) => v - .parse::() - .map(|x| { + Some(v) => match v.parse::() { + Ok(x @ 0..=200) => { h.surface_v_density = x; - (format!("SURFV = {x}"), true) - }) - .map_err(|_| "SETVAR: integer value required.".into()), + Ok((format!("SURFV = {x}"), true)) + } + Ok(_) => Err("SETVAR: value must be between 0 and 200.".into()), + Err(_) => Err("SETVAR: integer value required.".into()), + }, None => Ok((format!("SURFV = {}", h.surface_v_density), false)), }, "SURFTYPE" => match &value { - Some(v) => v - .parse::() - .map(|x| { + Some(v) => match v.parse::() { + Ok(x @ (5 | 6 | 8)) => { h.surface_type = x; - (format!("SURFTYPE = {x}"), true) - }) - .map_err(|_| "SETVAR: integer value required.".into()), + Ok((format!("SURFTYPE = {x}"), true)) + } + Ok(_) => Err("SETVAR: value must be 5, 6, or 8.".into()), + Err(_) => Err("SETVAR: integer value required.".into()), + }, None => Ok((format!("SURFTYPE = {}", h.surface_type), false)), }, "SHADEDGE" => match &value { diff --git a/src/modules/draw/modify/pedit.rs b/src/modules/draw/modify/pedit.rs index 709c7f5cb..63051e5d0 100644 --- a/src/modules/draw/modify/pedit.rs +++ b/src/modules/draw/modify/pedit.rs @@ -22,6 +22,9 @@ pub struct PeditTarget { pub convertible: bool, /// M/N size for a legacy polygon mesh; absent for ordinary polylines. pub mesh_size: Option<(usize, usize)>, + /// Current M/N closure state for a polygon mesh. The option list exposes + /// only the operation applicable to each direction. + pub mesh_closed: Option<(bool, bool)>, } enum Mode { @@ -43,15 +46,40 @@ pub struct PeditCommand { info: HashMap, mode: Mode, undo_count: usize, + mesh_smooth_type: acadrust::entities::polygon_mesh::SurfaceSmoothType, + mesh_smooth_density: (i16, i16), + mesh_vertex_default: isize, + pending_mesh_closed: Option<(bool, bool)>, + mesh_closed_history: Vec>, } impl PeditCommand { - pub fn new(info: HashMap) -> Self { + pub fn new( + info: HashMap, + surface_type: i16, + surface_u_density: i16, + surface_v_density: i16, + ) -> Self { + use acadrust::entities::polygon_mesh::SurfaceSmoothType; + + let mesh_smooth_type = match surface_type { + 5 => SurfaceSmoothType::Quadratic, + 8 => SurfaceSmoothType::Bezier, + _ => SurfaceSmoothType::Cubic, + }; Self { target: None, info, mode: Mode::PickTarget, undo_count: 0, + mesh_smooth_type, + mesh_smooth_density: ( + surface_u_density.clamp(2, 200), + surface_v_density.clamp(2, 200), + ), + mesh_vertex_default: 1, + pending_mesh_closed: None, + mesh_closed_history: Vec::new(), } } @@ -79,6 +107,37 @@ impl PeditCommand { let handle = self.target?; self.info.get(&handle.value())?.mesh_size } + + fn mesh_closed(&self) -> Option<(bool, bool)> { + let handle = self.target?; + self.info.get(&handle.value())?.mesh_closed + } + + fn set_mesh_closed(&mut self, m_direction: bool, closed: bool) { + let Some(handle) = self.target else { + return; + }; + let Some(target) = self.info.get_mut(&handle.value()) else { + return; + }; + let Some((closed_m, closed_n)) = target.mesh_closed.as_mut() else { + return; + }; + if m_direction { + *closed_m = closed; + } else { + *closed_n = closed; + } + } + + fn replace_mesh_closed(&mut self, closed: (bool, bool)) { + let Some(handle) = self.target else { + return; + }; + if let Some(target) = self.info.get_mut(&handle.value()) { + target.mesh_closed = Some(closed); + } + } } impl CadCommand for PeditCommand { @@ -118,17 +177,26 @@ 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 if self.mesh_size().is_some() => { + let (closed_m, closed_n) = self.mesh_closed().unwrap_or((false, false)); + vec![ + CmdOption::new(t!("Edit vertex").as_ref(), "E"), + CmdOption::new(t!("Smooth surface").as_ref(), "S"), + CmdOption::new(t!("Desmooth").as_ref(), "D"), + if closed_m { + CmdOption::new(t!("M open").as_ref(), "MO") + } else { + CmdOption::new(t!("M close").as_ref(), "MC") + }, + if closed_n { + CmdOption::new(t!("N open").as_ref(), "NO") + } else { + CmdOption::new(t!("N close").as_ref(), "NC") + }, + 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"), @@ -214,6 +282,7 @@ impl CadCommand for PeditCommand { is_poly: true, convertible: false, mesh_size: None, + mesh_closed: None, }, ); self.target = Some(nh); @@ -223,6 +292,13 @@ impl CadCommand for PeditCommand { fn on_pedit_applied(&mut self) { self.undo_count = self.undo_count.saturating_add(1); + if let Some((m_direction, closed)) = self.pending_mesh_closed.take() { + let previous = self.mesh_closed(); + self.mesh_closed_history.push(previous); + self.set_mesh_closed(m_direction, closed); + } else { + self.mesh_closed_history.push(None); + } } fn wants_text_input(&self) -> bool { @@ -230,6 +306,7 @@ impl CadCommand for PeditCommand { } fn on_text_input(&mut self, text: &str) -> Option { + self.pending_mesh_closed = None; let up = text.trim().to_uppercase(); let mesh_size = self.mesh_size(); match &mut self.mode { @@ -273,12 +350,21 @@ impl CadCommand for PeditCommand { 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, + "N" | "NEXT" => { + self.mesh_vertex_default = 1; + if *index + 1 < count { + *index += 1; + } + } + "P" | "PREVIOUS" => { + self.mesh_vertex_default = -1; + *index = index.saturating_sub(1); + } + "L" | "LEFT" if column > 0 => *index -= 1, + "R" | "RIGHT" if column + 1 < n => *index += 1, + "U" | "UP" if row + 1 < m => *index += n, + "D" | "DOWN" if row > 0 => *index -= n, + "L" | "LEFT" | "R" | "RIGHT" | "U" | "UP" | "D" | "DOWN" => {} "M" | "MOVE" => self.mode = Mode::MeshVertexMove(*index), "G" | "REGEN" => { // Mesh display is regenerated after every edit; keep the @@ -300,34 +386,54 @@ impl CadCommand for PeditCommand { } "S" | "SMOOTH" | "SMOOTH SURFACE" => Some(CmdResult::PeditOp { handle, - op: PeditOp::SetMeshSmooth( - acadrust::entities::polygon_mesh::SurfaceSmoothType::Quadratic, - ), + op: PeditOp::SetMeshSmooth { + smooth: self.mesh_smooth_type, + m_density: self.mesh_smooth_density.0, + n_density: self.mesh_smooth_density.1, + }, }), "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), + op: PeditOp::SetMeshSmooth { + smooth: + acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth, + m_density: self.mesh_smooth_density.0, + n_density: self.mesh_smooth_density.1, + }, }), + "MC" | "MCLOSE" | "M CLOSE" => { + self.pending_mesh_closed = Some((true, true)); + Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedM(true), + }) + } + "MO" | "MOPEN" | "M OPEN" => { + self.pending_mesh_closed = Some((true, false)); + Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedM(false), + }) + } + "NC" | "NCLOSE" | "N CLOSE" => { + self.pending_mesh_closed = Some((false, true)); + Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedN(true), + }) + } + "NO" | "NOPEN" | "N OPEN" => { + self.pending_mesh_closed = Some((false, false)); + Some(CmdResult::PeditOp { + handle, + op: PeditOp::SetMeshClosedN(false), + }) + } "U" | "UNDO" if self.undo_count > 0 => { self.undo_count -= 1; + if let Some(Some(closed)) = self.mesh_closed_history.pop() { + self.replace_mesh_closed(closed); + } Some(CmdResult::UndoDocument) } "U" | "UNDO" => Some(CmdResult::NeedPoint), @@ -398,7 +504,9 @@ impl CadCommand for PeditCommand { } fn on_enter(&mut self) -> CmdResult { - match &self.mode { + let mesh_size = self.mesh_size(); + let vertex_default = self.mesh_vertex_default; + match &mut self.mode { Mode::JoinGather(list) if list.len() >= 2 => CmdResult::JoinEntities(list.clone()), Mode::JoinGather(_) => { self.mode = Mode::Options; @@ -408,6 +516,20 @@ impl CadCommand for PeditCommand { handle: *h, op: PeditOp::ConvertToPolyline, }, + Mode::MeshVertex(index) => { + let Some((m, n)) = mesh_size else { + return CmdResult::NeedPoint; + }; + let count = m.saturating_mul(n); + if vertex_default >= 0 { + if *index + 1 < count { + *index += 1; + } + } else { + *index = index.saturating_sub(1); + } + CmdResult::NeedPoint + } _ => CmdResult::Cancel, } } @@ -426,7 +548,11 @@ pub enum PeditOp { Decurve, SetMeshClosedM(bool), SetMeshClosedN(bool), - SetMeshSmooth(acadrust::entities::polygon_mesh::SurfaceSmoothType), + SetMeshSmooth { + smooth: acadrust::entities::polygon_mesh::SurfaceSmoothType, + m_density: i16, + n_density: i16, + }, MoveMeshVertex { index: usize, point: DVec3 }, } @@ -503,19 +629,25 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool { } _ => false, }, - PeditOp::SetMeshSmooth(smooth) => match entity { + PeditOp::SetMeshSmooth { + smooth, + m_density, + n_density, + } => match entity { EntityType::PolygonMesh(mesh) => { let mut changed = mesh.smooth_type != *smooth; mesh.smooth_type = *smooth; if mesh.smooth_type != acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth { - if !(2..=256).contains(&mesh.m_smooth_density) { - mesh.m_smooth_density = 6; + let m_density = (*m_density).clamp(2, 200); + let n_density = (*n_density).clamp(2, 200); + if mesh.m_smooth_density != m_density { + mesh.m_smooth_density = m_density; changed = true; } - if !(2..=256).contains(&mesh.n_smooth_density) { - mesh.n_smooth_density = 6; + if mesh.n_smooth_density != n_density { + mesh.n_smooth_density = n_density; changed = true; } }