DIMBASELINE spacing follows DIM* overrides and DIMSCALE 0 (fixes #364) (#366)

Baseline dimensions were spaced by the bare style's DIMDLI × DIMSCALE, so
SETVAR DIMSCALE/DIMDLI (stored as overrides since #129) had no effect and a
style DIMSCALE of 0 collapsed the spacing to ~0. Use the continued
dimension's style with its overrides and the effective scale (the drawing's
DIMSCALE when the style's is 0), as drawing does; DIMSPACE's default spacing
uses the same scale.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Valentin Klein 2026-10-11 00:08:50 +02:00 committed by GitHub
parent 7f5b39fb25
commit 179ecc1f0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 3 deletions

View File

@ -322,7 +322,17 @@ fn last_linear(s: &Session) -> Option<Dimension> {
/// 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<Value> {
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(),

View File

@ -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}");
}