feat: strengthen modeling, configuration, and assembly workflows
Preserve linked sketch identities through deterministic face projection and resizing. Fix origin-corner picking, persistent inline navigation, appearance editing, configuration styles, component properties, contact sweeps, sheet exports, analytic STEP offsets, and bounded rolling-ball fit accuracy. Validated with full cargo xtask ci, native workflow screenshots, repeated enclosure replays, and analytic geometry regressions.
This commit is contained in:
1 parent
22672585c9
commit
d71bdb78ec
54 files changed
+4179
-316
No files matched your search
@@ -458,6 +458,41 @@ fn sketch_entity(app: &SolveApp, hits: &[Hit]) -> Option<(String, bool)> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Corner tools accept endpoints shared by two editable curves. Pick among those endpoints
|
||||
/// directly so the origin, a circle centre or an overlapping loose point cannot hide a corner.
|
||||
pub(super) fn corner_point(app: &SolveApp, proj: &Proj, pos: Pos2, cmd: &str) -> Option<(Vec2, String)> {
|
||||
let st = app.session.world_state();
|
||||
let ss = st.sketch(active_sketch(app)?)?;
|
||||
let sk = &ss.sketch;
|
||||
sk.points
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(pi, point)| {
|
||||
let distance = proj.to_screen(ss.plane.to_world(point.pos))?.distance(pos);
|
||||
if distance >= 8.0 {
|
||||
return None;
|
||||
}
|
||||
let curves: Vec<_> = sk
|
||||
.curves
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(ci, c)| !sk.is_linked_curve(*ci) && c.kind.ends().is_some_and(|(a, b)| a == pi || b == pi))
|
||||
.take(3)
|
||||
.collect();
|
||||
if curves.len() != 2
|
||||
|| curves.iter().any(|(_, c)| {
|
||||
!matches!(c.kind, solvecraft_engine::sketch::CurveKind::Line { .. })
|
||||
&& (cmd != "sketch.fillet" || !matches!(c.kind, solvecraft_engine::sketch::CurveKind::Arc { .. }))
|
||||
})
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some((distance, point.pos, point.id.clone()))
|
||||
})
|
||||
.min_by(|a, b| a.0.total_cmp(&b.0))
|
||||
.map(|(_, at, id)| (at, id))
|
||||
}
|
||||
|
||||
fn xy(p: Vec2) -> Value {
|
||||
json!([p.x, p.y])
|
||||
}
|
||||
@@ -624,6 +659,12 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
Mode::Entities(n) => {
|
||||
if cmd == "sketch.fillet" || cmd.starts_with("sketch.chamfer.") {
|
||||
if let Some((_, id)) = corner_point(app, proj, pos, cmd) {
|
||||
run_corner(app, cmd, &id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if cmd == "sketch.offset" && !tool.picks.is_empty() {
|
||||
if let Some((p, _)) = at {
|
||||
offset_side(app, tool, p);
|
||||
@@ -640,12 +681,6 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if cmd == "sketch.fillet" || cmd.starts_with("sketch.chamfer.") {
|
||||
if is_point {
|
||||
run_corner(app, cmd, &id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if cmd == "sketch.centerline" {
|
||||
let _ = app.run(cmd, json!({"curves": [id]}));
|
||||
return;
|
||||
@@ -1132,3 +1167,7 @@ fn text_entry(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
None => TEXT.with(|t| *t.borrow_mut() = Some((at, text))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "sketch_corner_ui_tests.rs"]
|
||||
mod corner_ui_tests;
|
||||
Reference in new issue
Block a user