Sketch drawing: clicking a model vertex projects it into the sketch (linked point) and draws from it, as Fusion does ("vertex:x,y,z" point arguments)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 02:58:43 +00:00
1 parent 8c88fb8d2d
commit 13b72bb011
3 files changed
+19 -2

No files matched your search

+11 -1
View File
@@ -329,13 +329,23 @@ fn auto_project(s: &mut Session, p: &Value) -> Result<Value> {
/// Point arguments that snap to model geometry while drawing, rewritten to sketch point ids:
/// `{"vertex": [x,y,z]}` projects the vertex (a linked point); `{"on_edge": [x,y,z]}` projects
/// the edge and makes a free point on it (coincident with the projection); `"mid:<line>"`
/// makes a point held at the line's midpoint. Everything else is left as it is. The links are added to the active sketch in the session's document.
/// makes a point held at the line's midpoint (and `"vertex:x,y,z"` is the vertex form). Everything
/// else is left as it is. The links are added to the active sketch in the session's document.
pub(super) fn snap_points(s: &mut Session, p: &Value, cmd: &str) -> Result<Value> {
let Some(id) = s.active_sketch else { return Ok(p.clone()) };
let mut out = p.clone();
let mut doc: Option<(Document, Sketch)> = None;
let fix = |v: &mut Value, s: &Session, doc: &mut Option<(Document, Sketch)>| -> Result<()> {
let (vertex, edge) = (v.get("vertex").and_then(vec3), v.get("on_edge").and_then(vec3));
// String forms from interactive snapping: "vertex:x,y,z" and "mid:<line>".
let vertex = vertex.or_else(|| {
let t = v.as_str()?.strip_prefix("vertex:")?;
let n: Vec<f64> = t.split(',').filter_map(|x| x.trim().parse().ok()).collect();
match n[..] {
[x, y, z] if x.is_finite() && y.is_finite() && z.is_finite() => Some(Vec3::new(x, y, z)),
_ => None,
}
});
let mid = v.as_str().and_then(|x| x.strip_prefix("mid:")).map(str::to_string);
if vertex.is_none() && edge.is_none() && mid.is_none() {
return Ok(());
@@ -181,7 +181,7 @@ fn drawing_on_a_model_vertex_projects_it() {
let mut s = Session::default();
box_part(&mut s);
run(&mut s, "SketchCreate", json!({"plane": "XY"}));
let r = run(&mut s, "DrawPolyline", json!({"points": [{"vertex": [40, 30, 20]}, [60, 45]]}));
let r = run(&mut s, "DrawPolyline", json!({"points": ["vertex:40,30,20", [60, 45]]}));
let l = ids(&r["curves"])[0].clone();
let si = inspect(&mut s);
let start = si["curves"].as_array().unwrap().iter().find(|c| c["id"] == l).unwrap()["start"].clone();
+7
View File
@@ -40,6 +40,13 @@ pub fn refine_snap(
let (a, b) = (proj.to_screen(plane.to_world(lp))?, proj.to_screen(plane.to_world(lp + Vec2::X))?);
let px = 1.0 / (a.distance(b) as f64).max(1e-9);
let tol = 7.0 * px;
// A model vertex under the cursor: drawing on it projects it into the sketch.
for h in pick(app, proj, a) {
if let Hit::Vertex { point, .. } = h {
SNAP.with(|s| *s.borrow_mut() = Some(("Pt", plane.to_local(point))));
return Some((plane.to_local(point), Some(format!("vertex:{},{},{}", point.x, point.y, point.z))));
}
}
let mut best: Option<(f64, &'static str, Vec2)> = None;
let mut mid_of: Option<String> = None;
for (i, c) in sk.curves.iter().enumerate() {