Sketch: a tangent arc starts from a trimmed end (#1485) (#1604)

Thanks @DuBearte.
This commit is contained in:
DuBearte authored and GitHub committed 2026-10-16 03:00:49 -04:00
1 parent 26bf1af93b
commit 25c89a706e
2 files changed
+46 -8

No files matched your search

+19 -8
View File
@@ -659,15 +659,26 @@ fn arc_tangent(s: &mut Session, p: &Value) -> Result<Value> {
let end = req_vec2(cmd, p, "end")?;
let (out, info) = edit(s, p, cmd, false, |sk, _| {
let st = req_parg(sk, p, "start", cmd)?;
let pi = st.idx().ok_or_else(|| bad(cmd, "`start` must be the end point of a line or arc"))?;
let ci = match str_(p, "curve") {
Some(id) => sk.curve_index(id).ok_or_else(|| bad(cmd, format!("unknown curve `{id}`")))?,
None => sk
.curves
let picked = st.idx().ok_or_else(|| bad(cmd, "`start` must be the end point of a line or arc"))?;
// The picked point, then any other point at the same place: a trimmed arc's end is its own
// point, lying on the end of the curve that trimmed it, which is the one a click picks (#1485).
let at = st.pos();
let tol = 1e-6 * at.len().max(1.0);
let candidates: Vec<usize> =
std::iter::once(picked).chain((0..sk.points.len()).filter(|&q| q != picked && sk.point(q).is_some_and(|x| x.dist(at) <= tol))).collect();
let (ci, pi) = match str_(p, "curve") {
Some(id) => {
let ci = sk.curve_index(id).ok_or_else(|| bad(cmd, format!("unknown curve `{id}`")))?;
let pi = candidates
.iter()
.copied()
.find(|&q| leaving_dir(sk, ci, q).is_some())
.ok_or_else(|| bad(cmd, "`start` is not an end of that curve"))?;
(ci, pi)
}
None => candidates
.iter()
.enumerate()
.find(|(i, c)| !c.construction && leaving_dir(sk, *i, pi).is_some())
.map(|(i, _)| i)
.find_map(|&q| sk.curves.iter().enumerate().find(|(i, c)| !c.construction && leaving_dir(sk, *i, q).is_some()).map(|(i, _)| (i, q)))
.ok_or_else(|| bad(cmd, "no line or arc ends at `start`"))?,
};
let t = leaving_dir(sk, ci, pi).ok_or_else(|| bad(cmd, "`start` is not an end of that curve"))?;
@@ -310,6 +310,33 @@ fn offset_chain_mirror_patterns_move_scale() {
assert!(sk.center(sk.curve_index(&cp).unwrap()).unwrap().dist(Vec2::new(-110.0, 0.0)) < 1e-7);
}
/// #1485: after a circle is trimmed between two construction lines, clicking the trimmed end
/// picks the construction line's end lying there, not the arc's own; the tangent arc still starts
/// from the trimmed arc, tangent to it.
#[test]
fn a_tangent_arc_starts_from_a_trimmed_end() {
let mut s = new_sketch();
let c = ids(&run(&mut s, "sketch.circle.center", json!({"center": [0, 0], "radius": 10}))["curves"])[0].clone();
run(&mut s, "sketch.line", json!({"points": [[0, 0], [10, 0]], "ids": ["guide_a"]}));
run(&mut s, "sketch.line", json!({"points": [[0, 0], [0, 10]], "ids": ["guide_b"]}));
run(&mut s, "sketch.construction", json!({"curves": ["guide_a", "guide_b"]}));
run(&mut s, "sketch.trim", json!({"curve": c, "at": [7.07, 7.07]}));
let sk = sketch(&s);
let picked = sk.resolve_point("guide_a.end").unwrap();
assert!(sk.point(picked).unwrap().dist(Vec2::new(10.0, 0.0)) < 1e-6);
let trimmed = sk.curves.iter().position(|cu| !cu.construction && matches!(cu.kind, CurveKind::Arc { .. })).expect("the trimmed arc");
let r = run(&mut s, "sketch.arc.tangent", json!({"start": "guide_a.end", "end": [14, 6]}));
let sk = sketch(&s);
let a = sk.curve_index(&ids(&r["curves"])[0]).unwrap();
let a_ends: Vec<usize> = sk.curves[a].kind.point_ids();
let t_ends: Vec<usize> = sk.curves[trimmed].kind.point_ids();
assert!(a_ends.iter().any(|q| t_ends.contains(q) && *q != picked), "the new arc shares the trimmed arc's own end");
assert!(
sk.constraints.iter().any(|k| matches!(k.kind, ConstraintKind::Tangent { a: x, b: y } if (x, y) == (trimmed, a) || (x, y) == (a, trimmed))),
"and is tangent to it"
);
}
#[test]
fn new_creation_tools() {
let mut s = new_sketch();