diff --git a/crates/engine/src/cmd/annotate.rs b/crates/engine/src/cmd/annotate.rs index b359681..9b235d4 100644 --- a/crates/engine/src/cmd/annotate.rs +++ b/crates/engine/src/cmd/annotate.rs @@ -322,7 +322,17 @@ fn last_linear(s: &Session) -> Option { /// Next dimension in a chain from `prev` to a new second origin. fn chained(s: &Session, prev: &Dimension, next: Vec2, baseline: bool) -> EntityKind { - let spacing = s.doc().ok().and_then(|d| d.dim_style(&prev.style).map(|st| st.baseline_spacing * st.scale.max(1e-9))).unwrap_or(0.38); + // DIMDLI × the overall scale of the dimension being continued: its overrides (SETVAR DIM*) + // included, and DIMSCALE 0 falling back to the drawing's DIMSCALE as when it is drawn. + let spacing = s + .doc() + .ok() + .map(|d| { + let st = d.dim_style(&prev.style).cloned().unwrap_or_default().with_overrides(&prev.overrides); + st.baseline_spacing * st.effective_scale(d.header.f64("DIMSCALE", 1.0)) + }) + .filter(|v| v.is_finite()) + .unwrap_or(0.38); let dir = match prev.kind { DimKind::Linear { rotation } => Vec2::from_angle(rotation), _ => (prev.p14.xy() - prev.p13.xy()).normalized(), @@ -671,8 +681,11 @@ fn run_dimspace(s: &mut Session, p: &Value) -> Result { let d = s.doc()?; let Some(EntityKind::Dimension(b)) = d.entity(base).map(|e| e.kind.clone()) else { return Err(bad("dimspace", "`base` is not a dimension")) }; let st = d.dim_style(&b.style).cloned().unwrap_or_default().with_overrides(&b.overrides); - let spacing = - p.get("spacing").and_then(Value::as_f64).filter(|v| v.is_finite() && *v >= 0.0).unwrap_or(st.baseline_spacing * st.scale.max(1e-9) * 2.0); + let spacing = p + .get("spacing") + .and_then(Value::as_f64) + .filter(|v| v.is_finite() && *v >= 0.0) + .unwrap_or(st.baseline_spacing * st.effective_scale(d.header.f64("DIMSCALE", 1.0)) * 2.0); let dir = match b.kind { DimKind::Linear { rotation } => Vec2::from_angle(rotation), DimKind::Aligned => (b.p14.xy() - b.p13.xy()).normalized(), diff --git a/crates/engine/tests/dim_baseline_spacing.rs b/crates/engine/tests/dim_baseline_spacing.rs new file mode 100644 index 0000000..78fb14c --- /dev/null +++ b/crates/engine/tests/dim_baseline_spacing.rs @@ -0,0 +1,48 @@ +//! DIMBASELINE spaces dimension lines by DIMDLI × the overall scale of the dimension it +//! continues: SETVAR DIM* overrides count, and DIMSCALE 0 falls back to the drawing's DIMSCALE +//! (issue #364). DIMSPACE's default spacing uses the same scale. + +use cadcraft_engine::Session; +use cadcraft_engine::doc::{EntityKind, Handle}; +use serde_json::{Value, json}; + +fn dim_line_y(s: &Session, h: &Value) -> f64 { + let h = Handle::parse_hex(h.as_str().unwrap()).unwrap(); + match &s.doc().unwrap().entity(h).unwrap().kind { + EntityKind::Dimension(dm) => dm.defpt.y, + k => panic!("not a dimension: {k:?}"), + } +} + +/// Spacing between a horizontal dimension with its line at y = 5 and the baseline one after it. +fn baseline_spacing(setup: &[(&str, Value)]) -> f64 { + let mut s = Session::new(); + for (cmd, p) in setup { + s.execute(cmd, p).unwrap(); + } + let first = s.execute("dimlinear", &json!({"p1": [0, 0], "p2": [10, 0], "at": [5, 5]})).unwrap(); + let next = s.execute("dimbaseline", &json!({"points": [[20, 0]]})).unwrap(); + dim_line_y(&s, &next["handles"][0]) - dim_line_y(&s, &first["handle"]) +} + +#[test] +fn baseline_spacing_follows_overrides_and_dimscale() { + let near = |a: f64, b: f64| (a - b).abs() < 1e-9; + let plain = baseline_spacing(&[]); + assert!(near(plain, 0.38), "{plain}"); + let scaled = baseline_spacing(&[("setvar", json!({"name": "DIMSCALE", "value": 10}))]); + assert!(near(scaled, 3.8), "SETVAR DIMSCALE scales the spacing: {scaled}"); + let dli = baseline_spacing(&[("setvar", json!({"name": "DIMDLI", "value": 1}))]); + assert!(near(dli, 1.0), "SETVAR DIMDLI sets the spacing: {dli}"); + let zero = baseline_spacing(&[("dimstyle", json!({"name": "Standard", "DIMSCALE": 0}))]); + assert!(near(zero, 0.38), "DIMSCALE 0 in model space is scale 1, not a collapsed spacing: {zero}"); + + // DIMSPACE's default spacing (twice DIMDLI) uses the same scale. + let mut s = Session::new(); + s.execute("dimstyle", &json!({"name": "Standard", "DIMSCALE": 0})).unwrap(); + let a = s.execute("dimlinear", &json!({"p1": [0, 0], "p2": [30, 0], "at": [15, 5]})).unwrap(); + let b = s.execute("dimlinear", &json!({"p1": [0, 0], "p2": [50, 0], "at": [25, 6]})).unwrap(); + s.execute("dimspace", &json!({"base": a["handle"], "handles": [b["handle"]]})).unwrap(); + let gap = dim_line_y(&s, &b["handle"]) - dim_line_y(&s, &a["handle"]); + assert!(near(gap, 0.76), "{gap}"); +}