Sketch: accept redundant but consistent constraints (#687) (#699)

A clover of same-size rectangles drawn around a shared corner was refused as over-constrained.

Constraints that remove no degree of freedom but still hold (zero residual) are now kept and shown dimmed as redundant. Conflicting ones are still refused. A redundant driving dimension is refused with "already determined by ...". Typed sizes become driven (reference) dimensions. Drawing no longer adds Horizontal/Vertical to a side that is already implied through two coincident corners. The solver reports redundant constraints using a Jacobian row-dependence test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-13 10:12:55 -04:00
1 parent add4a18961
commit 9ba72e118b
14 files changed
+804 -77

No files matched your search

+4 -2
View File
@@ -156,13 +156,15 @@ pub fn lines(sk: &Sketch, prev: Vec2, lp: Vec2, tol: f64) -> Option<Inference> {
pub fn commit(app: &mut SolveApp, line: &str, end: Vec2) {
let Some(i) = current().filter(|i| i.at.dist(end) < 1e-9) else { return };
clear();
let (cmd, params) = match i.kind {
let (cmd, mut params) = match i.kind {
Kind::Parallel => ("sketch.constraint.parallel", json!({"a": i.curve, "b": line})),
Kind::Perpendicular => ("sketch.constraint.perpendicular", json!({"a": i.curve, "b": line})),
Kind::Tangent => ("sketch.constraint.tangent", json!({"a": i.curve, "b": line})),
Kind::Extension => ("sketch.constraint.coincident", json!({"a": format!("{line}.end"), "b": i.curve})),
};
// An inference that would over-constrain the sketch is simply not kept.
// An inference only adds what the sketch does not already have: one that is already implied
// (redundant) or would conflict is simply not kept.
params["if_redundant"] = json!("refuse");
let _ = app.session.execute(cmd, &params);
}