forked from 1b-agent/opencadstudio-upstream
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:
1 parent
b32a9b4083
commit
e7eb84b514
7 files changed
+705
-112
No files matched your search
@@ -92,15 +92,18 @@ fn v3(p: DVec3) -> Vector3 {
|
||||
Vector3::new(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
/// Build the chain segment for one entity, or `None` for an entity type
|
||||
/// JOIN can't carry (which aborts the whole join).
|
||||
fn seg_of(e: &EntityType) -> Option<Seg> {
|
||||
/// Build the chain segments for one entity, or `None` for an entity type
|
||||
/// JOIN can't carry (which aborts the whole join). Lines and arcs contribute
|
||||
/// one segment; an OPEN polyline contributes one per span (bulges kept), so
|
||||
/// polylines merge with their neighbours too — the PEDIT Join set always
|
||||
/// contains the target polyline (#263). A closed polyline can't be joined.
|
||||
fn segs_of(e: &EntityType) -> Option<Vec<Seg>> {
|
||||
match e {
|
||||
EntityType::Line(l) => Some(Seg {
|
||||
EntityType::Line(l) => Some(vec![Seg {
|
||||
a: DVec3::new(l.start.x, l.start.y, l.start.z),
|
||||
b: DVec3::new(l.end.x, l.end.y, l.end.z),
|
||||
bulge: 0.0,
|
||||
}),
|
||||
}]),
|
||||
EntityType::Arc(arc) => {
|
||||
// The bulge below assumes the arc lies in a +Z plane; a tilted
|
||||
// or flipped normal would invert the CCW sweep, so reject it.
|
||||
@@ -111,11 +114,42 @@ fn seg_of(e: &EntityType) -> Option<Seg> {
|
||||
let r = arc.radius;
|
||||
let (sa, ea) = (arc.start_angle, arc.end_angle);
|
||||
let swept = (ea - sa).rem_euclid(std::f64::consts::TAU);
|
||||
Some(Seg {
|
||||
Some(vec![Seg {
|
||||
a: DVec3::new(cx + r * sa.cos(), cy + r * sa.sin(), cz),
|
||||
b: DVec3::new(cx + r * ea.cos(), cy + r * ea.sin(), cz),
|
||||
bulge: (swept / 4.0).tan(),
|
||||
})
|
||||
}])
|
||||
}
|
||||
EntityType::LwPolyline(p) => {
|
||||
if p.is_closed || p.vertices.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let z = p.elevation;
|
||||
Some(
|
||||
p.vertices
|
||||
.windows(2)
|
||||
.map(|w| Seg {
|
||||
a: DVec3::new(w[0].location.x, w[0].location.y, z),
|
||||
b: DVec3::new(w[1].location.x, w[1].location.y, z),
|
||||
bulge: w[0].bulge,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
EntityType::Polyline2D(p) => {
|
||||
if p.is_closed() || p.vertices.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
p.vertices
|
||||
.windows(2)
|
||||
.map(|w| Seg {
|
||||
a: DVec3::new(w[0].location.x, w[0].location.y, w[0].location.z),
|
||||
b: DVec3::new(w[1].location.x, w[1].location.y, w[1].location.z),
|
||||
bulge: w[0].bulge,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -135,7 +169,7 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec<Handle>,
|
||||
|
||||
let mut segs = Vec::with_capacity(entities.len());
|
||||
for (_, e) in entities {
|
||||
segs.push(seg_of(e)?);
|
||||
segs.extend(segs_of(e)?);
|
||||
}
|
||||
let handles: Vec<Handle> = entities.iter().map(|(h, _)| *h).collect();
|
||||
let common = entities[0].1.common().clone();
|
||||
|
||||
Reference in new issue
Block a user