diff --git a/crates/engine/src/cmd/constraints.rs b/crates/engine/src/cmd/constraints.rs index df37ecd2..54af88c8 100644 --- a/crates/engine/src/cmd/constraints.rs +++ b/crates/engine/src/cmd/constraints.rs @@ -956,6 +956,13 @@ fn run_constraintsettings(s: &mut Session, p: &Value) -> Result { set.angle_tolerance = f64_or(p, "angleTolerance", set.angle_tolerance).clamp(0.0, 45.0); set.bars_visible = bool_or(p, "barsVisible", set.bars_visible); set.dims_visible = bool_or(p, "dimsVisible", set.dims_visible); + // The checkboxes act like Show All / Hide All: exceptions are relative to the old value and would flip. + if set.bars_visible != cur.bars_visible { + set.bar_exceptions.clear(); + } + if set.dims_visible != cur.dims_visible { + set.dim_exceptions.clear(); + } if let Some(t) = p.get("barTransparency").and_then(Value::as_f64).filter(|v| v.is_finite()) { set.bar_transparency = t.clamp(0.0, 90.0) as u8; } diff --git a/crates/engine/src/cmd/constraints_tests.rs b/crates/engine/src/cmd/constraints_tests.rs index 83e13e86..0e2dbf82 100644 --- a/crates/engine/src/cmd/constraints_tests.rs +++ b/crates/engine/src/cmd/constraints_tests.rs @@ -296,3 +296,54 @@ fn dcangular_constrains_the_drawn_angle_whichever_way_the_lines_run() { assert!((got - 100.0).abs() < 1e-6, "{b_pts:?}: drawn angle {got}"); } } + +/// Same rule as the overlay in `ui-egui/src/parametric.rs` that draws dynamic constraints. +fn dim_shown(s: &Session, h: &str) -> bool { + let d = s.doc().unwrap(); + let set = &d.parametric.settings; + let c = d.constraints.iter().find(|c| c.refs.iter().any(|r| r.handle.hex() == h)).unwrap(); + set.dims_visible != c.refs.iter().any(|r| set.dim_exceptions.contains(&r.handle)) +} + +#[test] +fn settings_dims_checkbox_clears_exceptions() { + let mut s = Session::new(); + let a = line(&mut s, [0.0, 0.0], [10.0, 0.0]); + s.execute("dclinear", &json!({ "h1": a, "expr": "10" })).unwrap(); + s.execute("dcdisplay.hideall", &json!({})).unwrap(); + let b = line(&mut s, [20.0, 0.0], [25.0, 0.0]); + s.execute("dclinear", &json!({ "h1": b, "expr": "5" })).unwrap(); + let r = s.execute("dcdisplay", &json!({ "mode": "show", "handles": [b] })).unwrap(); + assert_eq!(r["exceptions"], json!([b])); + assert!(!dim_shown(&s, &a) && dim_shown(&s, &b)); + // Re-stating the current value keeps the exceptions. + s.execute("constraintsettings", &json!({ "dimsVisible": false })).unwrap(); + assert_eq!(s.doc().unwrap().parametric.settings.dim_exceptions.len(), 1); + // Ticking "Show dynamic constraints" shows all of them, like Show All. + s.execute("constraintsettings", &json!({ "dimsVisible": true })).unwrap(); + assert!(s.doc().unwrap().parametric.settings.dim_exceptions.is_empty()); + assert!(dim_shown(&s, &a) && dim_shown(&s, &b)); +} + +#[test] +fn settings_bars_checkbox_clears_exceptions() { + let mut s = Session::new(); + let a = line(&mut s, [0.0, 0.0], [10.0, 0.0]); + s.execute("gchorizontal", &json!({ "h1": a })).unwrap(); + s.execute("constraintbar.hideall", &json!({})).unwrap(); + let b = line(&mut s, [0.0, 5.0], [10.0, 5.0]); + s.execute("gchorizontal", &json!({ "h1": b })).unwrap(); + let r = s.execute("constraintbar", &json!({ "mode": "show", "handles": [b] })).unwrap(); + assert_eq!(r["exceptions"], json!([b])); + // Same rule as the bar overlay in `ui-egui/src/parametric.rs`. + let bar = |s: &Session, h: &str| { + let set = &s.doc().unwrap().parametric.settings; + set.bars_visible != set.bar_exceptions.contains(&Handle::parse_hex(h).unwrap()) + }; + assert!(!bar(&s, &a) && bar(&s, &b)); + s.execute("constraintsettings", &json!({ "barsVisible": false })).unwrap(); + assert_eq!(s.doc().unwrap().parametric.settings.bar_exceptions.len(), 1); + s.execute("constraintsettings", &json!({ "barsVisible": true })).unwrap(); + assert!(s.doc().unwrap().parametric.settings.bar_exceptions.is_empty()); + assert!(bar(&s, &a) && bar(&s, &b)); +}