From 6aa47c6eada772ed3ade31ce19e3cf010a7e5baf Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 14 Jun 2026 09:42:11 +0300 Subject: [PATCH] fix(dimension): DIMZIN feet/inch suppression and DIMJUST 3/4 rotation (#94) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DIMZIN now drives architectural feet/inch zero-suppression (modes 0-3) inside format_architectural, threaded through format_with_unit (primary and alternate units). - DIMJUST 3 and 4 (over first / second extension) now rotate the text 90° to read along the extension line, not just shift its position. - DIMTAD 2 (outside) and 3 (JIS) resolve to the away-from-object side, which equals "above" for 2-D linear dims; clarified in a comment. Co-Authored-By: Claude Opus 4.8 --- src/entities/dimension.rs | 59 +++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index 86e19b5e1..f38ef0204 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -1888,6 +1888,7 @@ fn dimension_text_entity( // first, then horizontal_direction override, then DIMTIH/DIMTOH. let dimtih = style.map(|s| s.dimtih).unwrap_or(false); let dimtoh = style.map(|s| s.dimtoh).unwrap_or(false); + let dimjust = style.map(|s| s.dimjust).unwrap_or(0); let rotation = if base.text_rotation.abs() > 1e-9 { base.text_rotation } else if base.horizontal_direction.abs() > 1e-9 { @@ -1897,7 +1898,13 @@ fn dimension_text_entity( } else if dimtih || dimtoh { 0.0 } else { - dimension_text_natural_rotation(dim) + let mut r = dimension_text_natural_rotation(dim); + // DIMJUST 3/4: text reads along the extension lines, i.e. rotated 90° + // from the dimension line. + if dimjust == 3 || dimjust == 4 { + r += std::f64::consts::FRAC_PI_2; + } + r }; // Text style resolution priority: @@ -2128,7 +2135,7 @@ fn alternate_units_text(measurement: f64, style: Option<&DimStyle>) -> Option) -> String { v = (v / rnd).round() * rnd; } let dec = dec.max(0) as usize; - let raw = format_with_unit(v, lunit, dec, frac); + let raw = format_with_unit(v, lunit, dec, frac, zin); let suppressed = apply_linear_zero_suppression(&raw, zin); swap_decimal_sep(&suppressed, dsep) } @@ -2285,11 +2292,11 @@ fn format_linear_value(measurement: f64, style: Option<&DimStyle>) -> String { /// 6 = Windows desktop → falls back to Decimal /// `dimfrac` controls denominator power for arch/fractional output (0/1/2); /// rendered inline as "n/d" (stacked glyphs require MText support). -fn format_with_unit(value: f64, unit: i16, dec: usize, dimfrac: i16) -> String { +fn format_with_unit(value: f64, unit: i16, dec: usize, dimfrac: i16, zin: i16) -> String { match unit { 1 => format!("{:.*e}", dec, value), 3 => format_engineering(value, dec), - 4 => format_architectural(value, dimfrac), + 4 => format_architectural(value, dimfrac, zin), 5 => format_fractional(value, dimfrac), _ => format!("{:.*}", dec, value), } @@ -2303,7 +2310,7 @@ fn format_engineering(inches: f64, dec: usize) -> String { format!("{}{:.0}'-{:.*}\"", sign, feet, dec, rem_in) } -fn format_architectural(inches: f64, dimfrac: i16) -> String { +fn format_architectural(inches: f64, dimfrac: i16, zin: i16) -> String { let sign = if inches < 0.0 { "-" } else { "" }; let abs = inches.abs(); let feet = (abs / 12.0).trunc(); @@ -2311,11 +2318,39 @@ fn format_architectural(inches: f64, dimfrac: i16) -> String { let whole = rem_in_total.trunc(); let frac = rem_in_total - whole; let frac_str = fraction_string(frac, dimfrac); - if frac_str.is_empty() { - format!("{}{:.0}'-{:.0}\"", sign, feet, whole) + + // DIMZIN feet/inch suppression: + // 0 suppress zero feet & zero inches, 1 include both, + // 2 include zero feet / suppress zero inches, + // 3 suppress zero feet / include zero inches. + let suppress_zero_feet = zin == 0 || zin == 3; + let suppress_zero_inches = zin == 0 || zin == 2; + let feet_zero = feet.abs() < 0.5; + let inches_zero = whole.abs() < 0.5 && frac_str.is_empty(); + let show_feet = !feet_zero || !suppress_zero_feet; + let show_inches = !inches_zero || !suppress_zero_inches; + + let feet_part = if show_feet { + format!("{:.0}'", feet) } else { - format!("{}{:.0}'-{:.0} {}\"", sign, feet, whole, frac_str) - } + String::new() + }; + let inch_part = if show_inches { + if frac_str.is_empty() { + format!("{:.0}\"", whole) + } else { + format!("{:.0} {}\"", whole, frac_str) + } + } else { + String::new() + }; + let body = match (feet_part.is_empty(), inch_part.is_empty()) { + (false, false) => format!("{}-{}", feet_part, inch_part), + (false, true) => feet_part, + (true, false) => inch_part, + (true, true) => "0\"".to_string(), + }; + format!("{}{}", sign, body) } fn format_fractional(value: f64, dimfrac: i16) -> String { @@ -2552,7 +2587,9 @@ fn text_on_dim_line( fn dimension_text_pos_f64(dim: &Dimension, style: Option<&DimStyle>, text_height: f64) -> Vector3 { let base = dim.base(); - // DIMTAD: 0=centred (on the line), 1=above. 2/3/4 fall back to "above". + // DIMTAD: 0=centred (on the line), 1=above, 4=below. 2 (outside, i.e. the + // side farthest from the defining points) and 3 (JIS) both resolve to the + // away-from-object side, which is the same as "above" for 2-D linear dims. let dimtad = style.map(|s| s.dimtad).unwrap_or(1); let dimgap = style.map(|s| s.dimgap).unwrap_or(0.0); let dimjust = style.map(|s| s.dimjust).unwrap_or(0);