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 {
|
PeditTarget {
|
||||||
is_poly: true,
|
is_poly: true,
|
||||||
convertible: false,
|
convertible: false,
|
||||||
|
mesh_size: None,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
acadrust::EntityType::Line(_) | acadrust::EntityType::Arc(_) => {
|
acadrust::EntityType::Line(_) | acadrust::EntityType::Arc(_) => {
|
||||||
@@ -270,9 +271,21 @@ impl OpenCADStudio {
|
|||||||
PeditTarget {
|
PeditTarget {
|
||||||
is_poly: false,
|
is_poly: false,
|
||||||
convertible: true,
|
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,
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ impl OpenCADStudio {
|
|||||||
.and_then(|entity| match entity {
|
.and_then(|entity| match entity {
|
||||||
acadrust::EntityType::LwPolyline(polyline) => Some(polyline.vertices.len()),
|
acadrust::EntityType::LwPolyline(polyline) => Some(polyline.vertices.len()),
|
||||||
acadrust::EntityType::Polyline2D(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::Leader(leader) => Some(leader.vertices.len()),
|
||||||
acadrust::EntityType::Spline(spline) => {
|
acadrust::EntityType::Spline(spline) => {
|
||||||
Some(if crate::entities::spline::shows_fit_points(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]) {
|
match self.tabs[i].scene.document.get_entity(handles[0]) {
|
||||||
Some(acadrust::EntityType::LwPolyline(p)) => p.vertices.len(),
|
Some(acadrust::EntityType::LwPolyline(p)) => p.vertices.len(),
|
||||||
Some(acadrust::EntityType::Polyline2D(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)) => {
|
Some(acadrust::EntityType::Table(table)) => {
|
||||||
table.row_count().saturating_mul(table.column_count())
|
table.row_count().saturating_mul(table.column_count())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ pub struct PeditTarget {
|
|||||||
pub is_poly: bool,
|
pub is_poly: bool,
|
||||||
/// Line / Arc — offered for conversion on pick.
|
/// Line / Arc — offered for conversion on pick.
|
||||||
pub convertible: bool,
|
pub convertible: bool,
|
||||||
|
/// M/N size for a legacy polygon mesh; absent for ordinary polylines.
|
||||||
|
pub mesh_size: Option<(usize, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
@@ -41,6 +43,10 @@ enum Mode {
|
|||||||
AwaitWidth,
|
AwaitWidth,
|
||||||
/// Join: gathering additional segments; Enter merges.
|
/// Join: gathering additional segments; Enter merges.
|
||||||
JoinGather(Vec<Handle>),
|
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 {
|
pub struct PeditCommand {
|
||||||
@@ -78,6 +84,11 @@ impl PeditCommand {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mesh_size(&self) -> Option<(usize, usize)> {
|
||||||
|
let handle = self.target?;
|
||||||
|
self.info.get(&handle.value())?.mesh_size
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CadCommand for PeditCommand {
|
impl CadCommand for PeditCommand {
|
||||||
@@ -100,6 +111,16 @@ impl CadCommand for PeditCommand {
|
|||||||
count = list.len().saturating_sub(1)
|
count = list.len().saturating_sub(1)
|
||||||
)
|
)
|
||||||
.into_owned(),
|
.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(),
|
Mode::Options => t!("PEDIT Enter option:").into_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,20 +128,43 @@ impl CadCommand for PeditCommand {
|
|||||||
fn options(&self) -> Vec<crate::command::CmdOption> {
|
fn options(&self) -> Vec<crate::command::CmdOption> {
|
||||||
use crate::command::CmdOption;
|
use crate::command::CmdOption;
|
||||||
match &self.mode {
|
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![
|
Mode::Options => vec![
|
||||||
CmdOption::new(t!("Close").as_ref(), "C"),
|
CmdOption::new(t!("Close").as_ref(), "C"),
|
||||||
CmdOption::new(t!("Open").as_ref(), "O"),
|
CmdOption::new(t!("Open").as_ref(), "O"),
|
||||||
CmdOption::new(t!("Join").as_ref(), "J"),
|
CmdOption::new(t!("Join").as_ref(), "J"),
|
||||||
CmdOption::new(t!("Width").as_ref(), "W"),
|
CmdOption::new(t!("Width").as_ref(), "W"),
|
||||||
CmdOption::new(t!("Fit").as_ref(), "F"),
|
CmdOption::new(t!("Fit").as_ref(), "F"),
|
||||||
CmdOption::new(t!("Spline").as_ref(), "S"),
|
CmdOption::new(t!("Spline").as_ref(), "S"),
|
||||||
CmdOption::new(t!("Decurve").as_ref(), "D"),
|
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"),
|
CmdOption::new(t!("eXit").as_ref(), "X"),
|
||||||
],
|
],
|
||||||
Mode::ConvertPrompt(_) => {
|
Mode::ConvertPrompt(_) => {
|
||||||
vec![CmdOption::new(t!("Yes").as_ref(), "Y"), CmdOption::new(t!("No").as_ref(), "N")]
|
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::JoinGather(_) => vec![CmdOption::enter(t!("Join").as_ref())],
|
||||||
|
Mode::MeshVertexMove(_) => vec![],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,12 +218,13 @@ impl CadCommand for PeditCommand {
|
|||||||
// piece as the live target and carry its bookkeeping over.
|
// piece as the live target and carry its bookkeeping over.
|
||||||
if let Some(&nh) = new_handles.first() {
|
if let Some(&nh) = new_handles.first() {
|
||||||
self.info.remove(&old.value());
|
self.info.remove(&old.value());
|
||||||
self.info.insert(
|
self.info.insert(
|
||||||
nh.value(),
|
nh.value(),
|
||||||
PeditTarget {
|
PeditTarget {
|
||||||
is_poly: true,
|
is_poly: true,
|
||||||
convertible: false,
|
convertible: false,
|
||||||
},
|
mesh_size: None,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
self.target = Some(nh);
|
self.target = Some(nh);
|
||||||
self.mode = Mode::Options;
|
self.mode = Mode::Options;
|
||||||
@@ -187,11 +232,12 @@ impl CadCommand for PeditCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn wants_text_input(&self) -> bool {
|
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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
let up = text.trim().to_uppercase();
|
let up = text.trim().to_uppercase();
|
||||||
|
let mesh_size = self.mesh_size();
|
||||||
match &mut self.mode {
|
match &mut self.mode {
|
||||||
Mode::PickTarget => None,
|
Mode::PickTarget => None,
|
||||||
Mode::ConvertPrompt(handle) => {
|
Mode::ConvertPrompt(handle) => {
|
||||||
@@ -222,8 +268,75 @@ impl CadCommand for PeditCommand {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Mode::JoinGather(_) => None,
|
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 => {
|
Mode::Options => {
|
||||||
let handle = self.target?;
|
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() {
|
match up.as_str() {
|
||||||
"X" | "EXIT" => Some(CmdResult::Cancel),
|
"X" | "EXIT" => Some(CmdResult::Cancel),
|
||||||
"C" | "CLOSE" => Some(CmdResult::PeditOp {
|
"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
|
CmdResult::NeedPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,6 +426,10 @@ pub enum PeditOp {
|
|||||||
Fit,
|
Fit,
|
||||||
Spline,
|
Spline,
|
||||||
Decurve,
|
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) ──
|
// ── 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,
|
_ => 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).
|
// Handled by the driver (it replaces the entity, not edits in place).
|
||||||
PeditOp::ConvertToPolyline => false,
|
PeditOp::ConvertToPolyline => false,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user