mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-01 06:46:50 -06:00
Tolerances and alternate units copied the primary number format, so they were
rounded by DIMRND and took DIMZIN's zero suppression: DIMRND 0.5 turned a
±0.1 tolerance into ±0.0000, and 10.3 in showed as [261.50] mm. Tolerances are
no longer rounded and use DIMTZIN. Alternate units use the new DIMALTRND
(rounding), DIMALTU (unit format) and DIMALTZ (zero suppression). The new
variables can be set with SETVAR, DIMSTYLE and overrides. A DXF keeps them as
DIMSTYLE group codes 148, 273, 284 and 285.
DIMPOST is a prefix/suffix for linear dimensions, so angular dimensions no
longer get it ("90°mm"). A text override still wraps the angle.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a119873e20
commit
2f4ceea7a9
@@ -212,11 +212,19 @@ pub struct DimStyle {
|
||||
pub tol_decimals: u8,
|
||||
/// DIMTFAC tolerance text height relative to the dimension text.
|
||||
pub tol_scale: f64,
|
||||
/// DIMTZIN zero suppression of tolerance values (DIMZIN bits).
|
||||
pub tol_zero_suppression: u8,
|
||||
/// DIMALT alternate units, DIMALTF factor, DIMALTD decimals, DIMAPOST template.
|
||||
pub alt: bool,
|
||||
pub alt_factor: f64,
|
||||
pub alt_decimals: u8,
|
||||
pub alt_post: String,
|
||||
/// DIMALTRND rounding increment of alternate units (0 = none); DIMRND doesn't apply to them.
|
||||
pub alt_round: f64,
|
||||
/// DIMALTU: 1 sci, 2 dec, 3 eng, 4 arch stacked, 5 frac stacked, 6 arch, 7 frac, 8 windows.
|
||||
pub alt_unit: u8,
|
||||
/// DIMALTZ zero suppression of alternate units (DIMZIN bits).
|
||||
pub alt_zero_suppression: u8,
|
||||
/// DIMAUNIT: 0 decimal degrees, 1 deg/min/sec, 2 grads, 3 radians.
|
||||
pub angular_unit: u8,
|
||||
/// DIMSE1 / DIMSE2 suppress extension lines.
|
||||
@@ -262,10 +270,14 @@ pub const DIMVARS: &[(&str, &str)] = &[
|
||||
("DIMLIM", "limits"),
|
||||
("DIMTDEC", "tolDecimals"),
|
||||
("DIMTFAC", "tolScale"),
|
||||
("DIMTZIN", "tolZeroSuppression"),
|
||||
("DIMALT", "alt"),
|
||||
("DIMALTF", "altFactor"),
|
||||
("DIMALTD", "altDecimals"),
|
||||
("DIMAPOST", "altPost"),
|
||||
("DIMALTRND", "altRound"),
|
||||
("DIMALTU", "altUnit"),
|
||||
("DIMALTZ", "altZeroSuppression"),
|
||||
("DIMAUNIT", "angularUnit"),
|
||||
("DIMSE1", "suppressExt1"),
|
||||
("DIMSE2", "suppressExt2"),
|
||||
@@ -312,10 +324,14 @@ impl Default for DimStyle {
|
||||
limits: false,
|
||||
tol_decimals: 4,
|
||||
tol_scale: 1.0,
|
||||
tol_zero_suppression: 0,
|
||||
alt: false,
|
||||
alt_factor: 25.4,
|
||||
alt_decimals: 2,
|
||||
alt_post: String::new(),
|
||||
alt_round: 0.0,
|
||||
alt_unit: 2,
|
||||
alt_zero_suppression: 0,
|
||||
angular_unit: 0,
|
||||
suppress_ext1: false,
|
||||
suppress_ext2: false,
|
||||
|
||||
@@ -68,6 +68,7 @@ pub(crate) const DIM_CODES: &[(&str, i32, K)] = &[
|
||||
("linearFactor", 144, K::Real),
|
||||
("tolScale", 146, K::Real),
|
||||
("textGap", 147, K::Real),
|
||||
("altRound", 148, K::Real),
|
||||
("tolerance", 71, K::Bool),
|
||||
("limits", 72, K::Bool),
|
||||
("textInsideHorizontal", 73, K::Bool),
|
||||
@@ -84,11 +85,14 @@ pub(crate) const DIM_CODES: &[(&str, i32, K)] = &[
|
||||
("angularDecimals", 179, K::Int),
|
||||
("decimals", 271, K::Int),
|
||||
("tolDecimals", 272, K::Int),
|
||||
("altUnit", 273, K::Int),
|
||||
("angularUnit", 275, K::Int),
|
||||
("fractionFormat", 276, K::Int),
|
||||
("linearUnit", 277, K::Int),
|
||||
("decimalSeparator", 278, K::Char),
|
||||
("textJust", 280, K::Int),
|
||||
("tolZeroSuppression", 284, K::Int),
|
||||
("altZeroSuppression", 285, K::Int),
|
||||
("textStyle", 340, K::TextStyle),
|
||||
("arrowBlock", 342, K::Block),
|
||||
("arrowBlock1", 343, K::Block),
|
||||
|
||||
@@ -687,10 +687,14 @@ fn full_dim_style() -> DimStyle {
|
||||
limits: true,
|
||||
tol_decimals: 2,
|
||||
tol_scale: 0.75,
|
||||
tol_zero_suppression: 8,
|
||||
alt: true,
|
||||
alt_factor: 0.03937,
|
||||
alt_decimals: 3,
|
||||
alt_post: "[<>]".into(),
|
||||
alt_round: 0.005,
|
||||
alt_unit: 6,
|
||||
alt_zero_suppression: 4,
|
||||
angular_unit: 1,
|
||||
suppress_ext1: true,
|
||||
suppress_ext2: true,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! Dimension geometry generation from definition points and a dimension style: extension
|
||||
//! lines (DIMEXO/DIMEXE, DIMSE1/2), arrowheads (DIMBLK/DIMBLK1/DIMBLK2, drawn in code) or ticks
|
||||
//! (DIMTSZ, DIMDLE), text placement (DIMTAD, DIMJUST, DIMTIH/DIMTOH, DIMGAP) and text
|
||||
//! formatting (DIMLUNIT, DIMDEC, DIMZIN, DIMRND, DIMPOST, DIMTOL/DIMLIM, DIMALT).
|
||||
//! formatting (DIMLUNIT, DIMDEC, DIMZIN, DIMRND, DIMPOST, DIMTOL/DIMLIM with DIMTZIN, DIMALT with
|
||||
//! DIMALTU/DIMALTZ/DIMALTRND).
|
||||
|
||||
use cadcraft_doc::{DimKind, DimStyle, Dimension};
|
||||
use cadcraft_fonts::{MTextParams, TextFont};
|
||||
@@ -273,7 +274,9 @@ pub fn format_linear_value(v: f64, st: &DimStyle) -> String {
|
||||
}
|
||||
|
||||
/// The text of a linear measurement: (plain, MTEXT) with DIMPOST, tolerances/limits and
|
||||
/// alternate units, then the user's override text (`<>` = the measurement).
|
||||
/// alternate units, then the user's override text (`<>` = the measurement). DIMRND rounds the
|
||||
/// measurement (and so the limits) only: tolerances are never rounded and alternate units
|
||||
/// round by DIMALTRND.
|
||||
fn linear_text(meas: f64, d: &Dimension, st: &DimStyle, prefix: &str) -> String {
|
||||
let v = meas * st.linear_factor;
|
||||
let nf = NumFormat::from_style(st);
|
||||
@@ -286,7 +289,7 @@ fn linear_text(meas: f64, d: &Dimension, st: &DimStyle, prefix: &str) -> String
|
||||
};
|
||||
let mut s = post(&st.post, &main);
|
||||
if st.tolerance && !st.limits {
|
||||
let tf = NumFormat { decimals: st.tol_decimals, ..nf.clone() };
|
||||
let tf = NumFormat { decimals: st.tol_decimals, zin: st.tol_zero_suppression, round: 0.0, ..nf.clone() };
|
||||
if (st.tol_plus - st.tol_minus).abs() < 1e-12 {
|
||||
s += &format!("%%p{}", format_linear(st.tol_plus, &tf).1);
|
||||
} else {
|
||||
@@ -299,7 +302,14 @@ fn linear_text(meas: f64, d: &Dimension, st: &DimStyle, prefix: &str) -> String
|
||||
}
|
||||
}
|
||||
if st.alt {
|
||||
let af = NumFormat { decimals: st.alt_decimals, unit: 2, ..nf };
|
||||
// DIMALTU 6 and 7 are architectural and fractional without stacking, 8 is decimal.
|
||||
let (unit, frac) = match st.alt_unit {
|
||||
u @ 1..=5 => (u, nf.frac),
|
||||
6 => (4, 2),
|
||||
7 => (5, 2),
|
||||
_ => (2, nf.frac),
|
||||
};
|
||||
let af = NumFormat { unit, decimals: st.alt_decimals, zin: st.alt_zero_suppression, round: st.alt_round, frac, ..nf };
|
||||
let alt = format_linear(v * st.alt_factor, &af).1;
|
||||
s += &format!(" [{}]", post(&st.alt_post, &alt));
|
||||
}
|
||||
@@ -589,7 +599,8 @@ pub fn dimension_geometry_with(d: &Dimension, st: &DimStyle, dimscale: f64, font
|
||||
if cadcraft_geom::angle_in_sweep(vertex.angle_to(arc_pt), s, e) { Arc::new(vertex, r, s, e) } else { Arc::new(vertex, r, e, s) };
|
||||
let sweep = ccw_sweep(arc.start, arc.end);
|
||||
let txt = format_angle(sweep, st.angular_unit, st.angular_decimals, st.zero_suppression & 12);
|
||||
g.mtext = apply_override(&post(&st.post, &txt), d);
|
||||
// DIMPOST is a linear-dimension prefix/suffix; angles have their own format only.
|
||||
g.mtext = apply_override(&txt, d);
|
||||
let mut pts = Vec::new();
|
||||
arc.tessellate(r * 1e-3, &mut pts);
|
||||
g.dim(pts);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//! Dimension text formatting (issue #373): DIMRND rounds the measurement only, tolerances use
|
||||
//! DIMTZIN, alternate units DIMALTRND/DIMALTU/DIMALTZ, and DIMPOST is not added to angles.
|
||||
|
||||
use cadcraft_doc::{DimKind, DimStyle, Dimension};
|
||||
use cadcraft_geom::Vec3;
|
||||
use cadcraft_render::dimension_geometry;
|
||||
|
||||
fn linear(len: f64) -> Dimension {
|
||||
Dimension {
|
||||
kind: DimKind::Linear { rotation: 0.0 },
|
||||
defpt: Vec3::new(0.0, 5.0, 0.0),
|
||||
text_mid: Vec3::ZERO,
|
||||
p13: Vec3::ZERO,
|
||||
p14: Vec3::new(len, 0.0, 0.0),
|
||||
p15: Vec3::ZERO,
|
||||
p16: Vec3::ZERO,
|
||||
text: String::new(),
|
||||
style: "Standard".into(),
|
||||
measurement: 0.0,
|
||||
text_rotation: 0.0,
|
||||
user_text_pos: false,
|
||||
block: None,
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn text(d: &Dimension, vars: serde_json::Value) -> String {
|
||||
let mut st = DimStyle::default();
|
||||
let rejected = st.apply_fields(vars.as_object().unwrap());
|
||||
assert!(rejected.is_empty(), "unknown variables {rejected:?}");
|
||||
dimension_geometry(d, &st, 1.0).value
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimrnd_rounds_neither_tolerances_nor_alternate_units() {
|
||||
let d = linear(10.3);
|
||||
let tol = serde_json::json!({"DIMRND": 0.5, "DIMDEC": 2, "DIMTOL": 1, "DIMTP": 0.1, "DIMTM": 0.1, "DIMTDEC": 2});
|
||||
assert_eq!(text(&d, tol), "10.50±0.10");
|
||||
let dev = serde_json::json!({"DIMRND": 0.5, "DIMDEC": 2, "DIMTOL": 1, "DIMTP": 0.1, "DIMTM": 0.2, "DIMTDEC": 2});
|
||||
assert_eq!(text(&d, dev), "10.50+0.10/-0.20");
|
||||
// Tolerance zeros follow DIMTZIN, not DIMZIN.
|
||||
let tzin = serde_json::json!({"DIMZIN": 8, "DIMDEC": 2, "DIMTOL": 1, "DIMTP": 0.1, "DIMTM": 0.1, "DIMTDEC": 2});
|
||||
assert_eq!(text(&d, tzin.clone()), "10.3±0.10");
|
||||
let mut tzin8 = tzin;
|
||||
tzin8["DIMTZIN"] = 8.into();
|
||||
assert_eq!(text(&d, tzin8), "10.3±0.1");
|
||||
|
||||
// 10.3 in = 261.62 mm: DIMRND 0.5 rounds the primary value only.
|
||||
assert_eq!(text(&d, serde_json::json!({"DIMRND": 0.5, "DIMDEC": 1, "DIMALT": 1})), "10.5 [261.62]");
|
||||
// DIMALTRND rounds the alternate value.
|
||||
assert_eq!(text(&d, serde_json::json!({"DIMDEC": 1, "DIMALT": 1, "DIMALTRND": 0.5})), "10.3 [261.50]");
|
||||
// DIMALTZ, not DIMZIN, suppresses alternate zeros.
|
||||
assert_eq!(text(&linear(10.0), serde_json::json!({"DIMZIN": 8, "DIMALT": 1})), "10 [254.00]");
|
||||
assert_eq!(text(&linear(10.0), serde_json::json!({"DIMALT": 1, "DIMALTZ": 8})), "10.0000 [254]");
|
||||
// DIMALTU: alternate units in their own format (7 = fractional, not stacked).
|
||||
assert_eq!(text(&linear(2.0), serde_json::json!({"DIMALT": 1, "DIMALTF": 0.25, "DIMALTU": 7, "DIMALTD": 2})), "2.0000 [1/2]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimpost_is_not_added_to_angular_dimensions() {
|
||||
let mut a = linear(0.0);
|
||||
a.kind = DimKind::Angular3P;
|
||||
a.p15 = Vec3::ZERO;
|
||||
a.p13 = Vec3::new(10.0, 0.0, 0.0);
|
||||
a.p14 = Vec3::new(0.0, 10.0, 0.0);
|
||||
a.defpt = Vec3::new(3.0, 3.0, 0.0);
|
||||
assert_eq!(text(&a, serde_json::json!({"DIMPOST": "<>mm"})), "90°");
|
||||
// Linear dimensions keep it, and an angular text override still wraps the angle.
|
||||
assert_eq!(text(&linear(10.0), serde_json::json!({"DIMPOST": "<>mm", "DIMDEC": 1})), "10.0mm");
|
||||
a.text = "<> TYP".into();
|
||||
assert_eq!(text(&a, serde_json::json!({"DIMPOST": "<>mm"})), "90° TYP");
|
||||
}
|
||||
Reference in New Issue
Block a user