Extend PEDIT for polygon meshes

This commit is contained in:
ramox81 committed 2026-08-28 15:50:49 +03:00
1 parent 2c9c5d04e1
commit e345d89519
4 files changed
+203 -14

No files matched your search

+13
View File
@@ -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,
}
})
+1
View File
@@ -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) {
+1
View File
@@ -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())
}
+188 -14
View File
@@ -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<Handle>),
/// 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<crate::command::CmdOption> {
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<CmdResult> {
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,
}