mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 10:56:51 -06:00
fix(render): honor DIMDSEP in angular dimensions (#1670)
Use the effective dimension style's decimal separator when formatting angular labels, preserving the public formatter's decimal-point default. Add rendering and stored DXF/DWG block regression tests, and update the file-format progress documentation. Fixes #1646. Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
@@ -10,7 +10,7 @@ use cadcraft_doc::{DimKind, DimStyle, Dimension};
|
||||
use cadcraft_fonts::{MTextParams, TextFont};
|
||||
use cadcraft_geom::{Arc, Vec2, ccw_sweep};
|
||||
|
||||
use crate::units::{NumFormat, format_angle, format_decimal, format_linear};
|
||||
use crate::units::{NumFormat, format_angle_with_separator, format_decimal, format_linear};
|
||||
|
||||
/// Most DIMBREAK gaps drawn per dimension.
|
||||
const MAX_BREAKS: usize = 1000;
|
||||
@@ -887,7 +887,7 @@ pub fn dimension_geometry_with(d: &Dimension, st: &DimStyle, dimscale: f64, font
|
||||
g.measurement = sweep;
|
||||
// DIMAZIN 1 / 2 suppress leading / trailing zeros (DIMZIN's bits 4 / 8).
|
||||
let azin = (st.angular_zero_suppression & 3) * 4;
|
||||
let txt = format_angle(sweep, st.angular_unit, st.angular_decimals, azin);
|
||||
let txt = format_angle_with_separator(sweep, st.angular_unit, st.angular_decimals, azin, &st.decimal_separator);
|
||||
// DIMPOST is a linear-dimension prefix/suffix; angles have their own format only.
|
||||
g.mtext = inspected(apply_override(&txt, d), d);
|
||||
let mut pts = Vec::new();
|
||||
|
||||
@@ -188,8 +188,13 @@ pub fn format_linear(v: f64, f: &NumFormat) -> (String, String) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format an angle (radians, non-negative sweep) per DIMAUNIT / DIMADEC.
|
||||
/// Format an angle (radians, non-negative sweep) per DIMAUNIT / DIMADEC, with a decimal point.
|
||||
pub fn format_angle(rad: f64, unit: u8, decimals: u8, zin: u8) -> String {
|
||||
format_angle_with_separator(rad, unit, decimals, zin, ".")
|
||||
}
|
||||
|
||||
/// Format an angle with the dimension style's DIMDSEP separator.
|
||||
pub(crate) fn format_angle_with_separator(rad: f64, unit: u8, decimals: u8, zin: u8, separator: &str) -> String {
|
||||
let rad = if rad.is_finite() { rad } else { 0.0 };
|
||||
let deg = rad.to_degrees();
|
||||
match unit {
|
||||
@@ -209,9 +214,9 @@ pub fn format_angle(rad: f64, unit: u8, decimals: u8, zin: u8) -> String {
|
||||
_ => format!("{d}°{m}'{s}\""),
|
||||
}
|
||||
}
|
||||
2 => format!("{}g", format_decimal(deg / 0.9, decimals, zin, ".")),
|
||||
3 => format!("{}r", format_decimal(rad, decimals, zin, ".")),
|
||||
_ => format!("{}°", format_decimal(deg, decimals, zin, ".")),
|
||||
2 => format!("{}g", format_decimal(deg / 0.9, decimals, zin, separator)),
|
||||
3 => format!("{}r", format_decimal(rad, decimals, zin, separator)),
|
||||
_ => format!("{}°", format_decimal(deg, decimals, zin, separator)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,3 +72,59 @@ fn dimpost_is_not_added_to_angular_dimensions() {
|
||||
a.text = "<> TYP".into();
|
||||
assert_eq!(text(&a, serde_json::json!({"DIMPOST": "<>mm"})), "90° TYP");
|
||||
}
|
||||
|
||||
fn angular(radians: f64) -> Dimension {
|
||||
let mut d = linear(0.0);
|
||||
d.kind = DimKind::Angular3P;
|
||||
d.p13 = Vec3::new(10.0, 0.0, 0.0);
|
||||
d.p14 = Vec3::new(10.0 * radians.cos(), 10.0 * radians.sin(), 0.0);
|
||||
d.defpt = Vec3::new(5.0 * (radians / 2.0).cos(), 5.0 * (radians / 2.0).sin(), 0.0);
|
||||
d
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn angular_dimensions_use_the_style_decimal_separator() {
|
||||
for (unit, radians, decimals, dot, comma) in
|
||||
[(0, 39.4_f64.to_radians(), 1, "39.4°", "39,4°"), (2, std::f64::consts::FRAC_PI_4, 1, "50.0g", "50,0g"), (3, 1.25, 2, "1.25r", "1,25r")]
|
||||
{
|
||||
let three_point = angular(radians);
|
||||
let mut two_line = three_point.clone();
|
||||
two_line.kind = DimKind::Angular;
|
||||
two_line.p16 = three_point.defpt;
|
||||
two_line.p15 = three_point.p14;
|
||||
two_line.p14 = three_point.p13;
|
||||
two_line.p13 = Vec3::ZERO;
|
||||
two_line.defpt = Vec3::ZERO;
|
||||
for d in [three_point, two_line] {
|
||||
let vars = serde_json::json!({"DIMAUNIT": unit, "DIMADEC": decimals});
|
||||
assert_eq!(text(&d, vars.clone()), dot);
|
||||
let mut vars = vars;
|
||||
vars["DIMDSEP"] = serde_json::json!(",");
|
||||
assert_eq!(text(&d, vars), comma);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn angular_decimal_separator_respects_overrides_and_literal_text() {
|
||||
let mut d = angular(39.4_f64.to_radians());
|
||||
d.text = "<> at 1.5".into();
|
||||
d.overrides.insert("DIMDSEP".into(), serde_json::json!(","));
|
||||
assert_eq!(text(&d, serde_json::json!({"DIMADEC": 1, "DIMDSEP": "."})), "39,4° at 1.5");
|
||||
d.overrides.insert("DIMDSEP".into(), serde_json::json!("."));
|
||||
assert_eq!(text(&d, serde_json::json!({"DIMADEC": 1, "DIMDSEP": ","})), "39.4° at 1.5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn angular_zero_suppression_works_with_a_decimal_comma() {
|
||||
let d = angular(0.5_f64.to_radians());
|
||||
for (zin, expected) in [(0, "0,500°"), (1, ",500°"), (2, "0,5°"), (3, ",5°")] {
|
||||
assert_eq!(text(&d, serde_json::json!({"DIMADEC": 3, "DIMAZIN": zin, "DIMDSEP": ","})), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn angular_decimal_separator_keeps_dms_notation() {
|
||||
let d = angular(45.5125_f64.to_radians());
|
||||
assert_eq!(text(&d, serde_json::json!({"DIMAUNIT": 1, "DIMADEC": 3, "DIMDSEP": ","})), "45°30'45\"");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user