feat(pedit): convert prompt, join, fit/spline/decurve; join fixes (#263)
PEDIT reworked around a mode machine: - Picking a line/arc asks "Object is not a polyline. Turn it into one? [Yes/No] <Y>" — Yes converts in place (arc keeps its bulge) and editing continues on the result. Pickfirst: an already-selected polyline skips the select step, a selected line/arc goes straight to the convert prompt. - Join gathers with the normal selection system (single picks AND window/crossing boxes) and merges through the JOIN machinery. - Fit replaces every segment with a classic equal-parameter BIARC: two arcs per segment meeting tangentially at an inserted knee, both segments at a vertex sharing that vertex's tangent — the whole run is tangent-continuous (verified: zero tangent jump on L/closed/zigzag/S shapes). Spline resamples the vertex frame as a uniform cubic B-spline (8 samples per span); Decurve straightens every bulge. - Earlier hardening kept: undo snapshot before the mutation, immediate repaint, polyline-only pick validation. - Vertex editing lives on grips, so no Edit-vertex submode; instead the vertex grip hover menu gains Break — a closed polyline opens at the vertex, an open one splits into two (interior vertices only), handled driver-side as an entity replacement. JOIN itself: segs_of now emits one segment per span of an OPEN polyline (bulges kept), so polylines merge with their neighbours — PEDIT Join's set always contains the target polyline and used to abort every time; plain JOIN had the same gap. JOIN also honours pickfirst: launched with two or more objects selected it joins them immediately. Closes #263 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b32a9b4083
commit
e7eb84b514
7 changed files with 704 additions and 111 deletions
|
|
@ -404,6 +404,42 @@ fn tapered_band_verts(pline: &LwPolyline) -> Option<Vec<([f64; 2], f64, f64, f64
|
|||
}
|
||||
}
|
||||
|
||||
/// Split at vertex `idx`: a closed polyline re-opens there (one piece); an
|
||||
/// open one splits into two (interior vertices only). `None` when invalid.
|
||||
pub(crate) fn break_at_vertex(
|
||||
p: &acadrust::LwPolyline,
|
||||
idx: usize,
|
||||
) -> Option<Vec<acadrust::EntityType>> {
|
||||
use acadrust::EntityType;
|
||||
let n = p.vertices.len();
|
||||
if n < 3 || idx >= n {
|
||||
return None;
|
||||
}
|
||||
if p.is_closed {
|
||||
let mut verts = Vec::with_capacity(n + 1);
|
||||
verts.extend_from_slice(&p.vertices[idx..]);
|
||||
verts.extend_from_slice(&p.vertices[..=idx]);
|
||||
let mut out = p.clone();
|
||||
out.common.handle = acadrust::Handle::NULL;
|
||||
out.is_closed = false;
|
||||
out.vertices = verts;
|
||||
return Some(vec![EntityType::LwPolyline(out)]);
|
||||
}
|
||||
if idx == 0 || idx == n - 1 {
|
||||
return None;
|
||||
}
|
||||
let mut a = p.clone();
|
||||
a.common.handle = acadrust::Handle::NULL;
|
||||
a.vertices = p.vertices[..=idx].to_vec();
|
||||
if let Some(last) = a.vertices.last_mut() {
|
||||
last.bulge = 0.0;
|
||||
}
|
||||
let mut b = p.clone();
|
||||
b.common.handle = acadrust::Handle::NULL;
|
||||
b.vertices = p.vertices[idx..].to_vec();
|
||||
Some(vec![EntityType::LwPolyline(a), EntityType::LwPolyline(b)])
|
||||
}
|
||||
|
||||
fn grips(pline: &LwPolyline) -> Vec<GripDef> {
|
||||
let elev = pline.elevation;
|
||||
let n = pline.vertices.len();
|
||||
|
|
@ -678,8 +714,10 @@ impl crate::entities::traits::Grippable for LwPolyline {
|
|||
use crate::scene::model::object::{GripMenuAction, GripMenuItem};
|
||||
let n = self.vertices.len();
|
||||
if grip_id < n {
|
||||
// Vertex grip.
|
||||
return vec![
|
||||
// Vertex grip. Break only where a split is possible: any vertex
|
||||
// of a closed polyline, interior vertices of an open one.
|
||||
let breakable = n >= 3 && (self.is_closed || (grip_id > 0 && grip_id < n - 1));
|
||||
let mut items = vec![
|
||||
GripMenuItem {
|
||||
label: "Stretch",
|
||||
action: GripMenuAction::Stretch,
|
||||
|
|
@ -693,6 +731,13 @@ impl crate::entities::traits::Grippable for LwPolyline {
|
|||
action: GripMenuAction::RemoveVertex,
|
||||
},
|
||||
];
|
||||
if breakable {
|
||||
items.push(GripMenuItem {
|
||||
label: "Break",
|
||||
action: GripMenuAction::BreakVertex,
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
// Segment midpoint grip.
|
||||
let seg = grip_id - n;
|
||||
|
|
|
|||
Loading…
Reference in a new issue