From 2a260be31d32d30594febce0edc2301b1ad2360e Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sat, 20 Jun 2026 17:38:57 +0300 Subject: [PATCH] fix(dim): restore DIMTAD=Outside placement, route all DIMTAD values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous DIMTAD fix made every non-below value use the text-up side, which broke DIMTAD=2 (Outside) — that should sit on the side farthest from the measured points. Pass the DIMTAD value through instead of a below flag: above (1) / JIS (3) / centred (0) use the text-up side, below (4) the opposite, and outside (2) the away-from-object side. Tests cover above-up and outside-away. (#144) Co-Authored-By: Claude Opus 4.8 --- src/entities/dimension.rs | 58 +++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index ac2fda3bf..59057d42d 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -2597,13 +2597,11 @@ fn text_on_dim_line( text_w: f64, arrow: f64, dimtix: bool, - below: bool, + tad: i16, ) -> Vector3 { - // "Above" (DIMTAD 1) places the text on the side its top points to — the - // perpendicular of the dimension line normalised so the text reads - // left-to-right (or bottom-to-top when vertical), NOT the side away from - // the measured object. Keying the side off the object flipped "above" into - // "below" whenever the dimension ran the opposite way. (#144) + // The perpendicular "up" side: the perpendicular of the dimension line + // normalised so the text reads left-to-right (or bottom-to-top when + // vertical). DIMTAD "above" (1) and JIS (3) sit on this side. let (mut nx, mut ny) = (ax, ay); if nx < 0.0 || (nx == 0.0 && ny < 0.0) { nx = -nx; @@ -2611,8 +2609,24 @@ fn text_on_dim_line( } let px = -ny; let py = nx; - // +perp is "above"; DIMTAD=4 (below) flips to the other side. - let perp_sign = if below { -1.0 } else { 1.0 }; + // DIMTAD side: + // 1 (above) / 3 (JIS) / 0 (centred) → the text-up side, independent of the + // object — keying it off the object flipped "above" to "below" whenever + // the dimension ran the opposite way (#144); + // 4 (below) → the opposite side; + // 2 (outside) → the side farthest from the defining points (the object). + let perp_sign = match tad { + 4 => -1.0, + 2 => { + let off = (defpt.x - first.x) * px + (defpt.y - first.y) * py; + if off >= 0.0 { + 1.0 + } else { + -1.0 + } + } + _ => 1.0, + }; // Along-axis positions of the extension points relative to the dim line. let t1 = (first.x - defpt.x) * ax + (first.y - defpt.y) * ay; let t2 = (second.x - defpt.x) * ax + (second.y - defpt.y) * ay; @@ -2667,7 +2681,6 @@ fn dimension_text_pos_f64( } else { text_height * 0.5 + dimgap }; - let tad_below = dimtad == 4; // Rough text width + arrow allowance, used to decide text-outside fit. let text_w = dimension_text_value(dim, style) .map(|t| t.chars().count() as f64 * text_height * 0.6 + 2.0 * dimgap) @@ -2699,7 +2712,7 @@ fn dimension_text_pos_f64( text_w, arrow, dimtix, - tad_below, + dimtad, ) } Dimension::Aligned(d) => { @@ -2717,7 +2730,7 @@ fn dimension_text_pos_f64( text_w, arrow, dimtix, - tad_below, + dimtad, ) } _ => { @@ -2769,13 +2782,30 @@ mod dimtad_tests { let (first, second, defpt) = (v(0.0, 10.0), v(20.0, 10.0), v(0.0, 0.0)); let perp = 5.0; let fwd = - text_on_dim_line(first, second, defpt, 1.0, 0.0, 0, perp, 0.0, 1.0, false, false); + text_on_dim_line(first, second, defpt, 1.0, 0.0, 0, perp, 0.0, 1.0, false, 1); let rev = - text_on_dim_line(first, second, defpt, -1.0, 0.0, 0, perp, 0.0, 1.0, false, false); + text_on_dim_line(first, second, defpt, -1.0, 0.0, 0, perp, 0.0, 1.0, false, 1); assert!(fwd.y > 0.0, "above must be +Y, got {}", fwd.y); assert!(rev.y > 0.0, "above must be +Y even reversed, got {}", rev.y); let below = - text_on_dim_line(first, second, defpt, 1.0, 0.0, 0, perp, 0.0, 1.0, false, true); + text_on_dim_line(first, second, defpt, 1.0, 0.0, 0, perp, 0.0, 1.0, false, 4); assert!(below.y < 0.0, "below must be -Y, got {}", below.y); } + + // DIMTAD=Outside (2) stays on the side farthest from the measured points, + // whichever side of the geometry the dimension line is on. + #[test] + fn outside_is_away_from_object() { + let perp = 5.0; + // Object at y=0, dim line above it (y=10): away → further above. + let above = text_on_dim_line( + v(0.0, 0.0), v(20.0, 0.0), v(0.0, 10.0), 1.0, 0.0, 0, perp, 0.0, 1.0, false, 2, + ); + assert!(above.y > 10.0, "outside must clear the object side, got {}", above.y); + // Object at y=0, dim line below it (y=-10): away → further below. + let below = text_on_dim_line( + v(0.0, 0.0), v(20.0, 0.0), v(0.0, -10.0), 1.0, 0.0, 0, perp, 0.0, 1.0, false, 2, + ); + assert!(below.y < -10.0, "outside must clear the object side, got {}", below.y); + } }