forked from 1b-agent/opencadstudio-upstream
Extend PEDIT for polygon meshes
This commit is contained in:
1 parent
2c9c5d04e1
commit
e345d89519
4 files changed
+203
-14
No files matched your search
@@ -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,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user