EXPLODE and the DXF *D block took the text height from the bare style's DIMTXT × DIMSCALE, ignoring the dimension's overrides (SETVAR DIM*, DIMOVERRIDE), a fixed-height text style and the DIMSCALE 0 fallback (text ~1e-10 high), and EXPLODE always wrote rotation 0, so aligned text turned horizontal. Both now use the geometry the renderer draws (dimension_in): its text height, angle and the overridden text style; EXPLODE adds no MTEXT for suppressed text. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
409968ecbb
commit
8db51c2766
|
|
@ -1182,8 +1182,9 @@ pub(crate) fn explode_kind(d: &cadcraft_doc::Drawing, e: &Entity) -> Option<Vec<
|
|||
.collect(),
|
||||
),
|
||||
EntityKind::Dimension(dm) => {
|
||||
let style = d.dim_style(&dm.style).cloned().unwrap_or_default();
|
||||
let g = cadcraft_render::dimension_geometry(dm, &style, d.header.f64("DIMSCALE", 1.0));
|
||||
// The dimension as drawn: its overrides, DIMSCALE (0 = the drawing's) and text font.
|
||||
let style = d.dim_style(&dm.style).cloned().unwrap_or_default().with_overrides(&dm.overrides);
|
||||
let g = cadcraft_render::dimension_in(d, dm);
|
||||
let mut v: Vec<Entity> = g
|
||||
.lines
|
||||
.iter()
|
||||
|
|
@ -1208,21 +1209,22 @@ pub(crate) fn explode_kind(d: &cadcraft_doc::Drawing, e: &Entity) -> Option<Vec<
|
|||
});
|
||||
}
|
||||
}
|
||||
let th = style.text_height * style.scale.max(1e-9);
|
||||
v.push(Entity {
|
||||
handle: Handle(0),
|
||||
common: e.common.clone(),
|
||||
kind: EntityKind::MText(cadcraft_doc::MText {
|
||||
insert: v3(g.text_pos),
|
||||
height: th,
|
||||
width: 0.0,
|
||||
attach: 5,
|
||||
rotation: 0.0,
|
||||
style: style.text_style.clone(),
|
||||
contents: g.value.clone(),
|
||||
line_spacing: 1.0,
|
||||
}),
|
||||
});
|
||||
if !g.value.is_empty() {
|
||||
v.push(Entity {
|
||||
handle: Handle(0),
|
||||
common: e.common.clone(),
|
||||
kind: EntityKind::MText(cadcraft_doc::MText {
|
||||
insert: v3(g.text_pos),
|
||||
height: g.text_height,
|
||||
width: 0.0,
|
||||
attach: 5,
|
||||
rotation: g.text_angle,
|
||||
style: style.text_style.clone(),
|
||||
contents: g.value.clone(),
|
||||
line_spacing: 1.0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
Some(v)
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
//! EXPLODE turns a dimension's text into MTEXT that looks like the text that was drawn: the
|
||||
//! dimension's overrides and DIMSCALE (0 = the drawing's) set its height, and aligned text keeps
|
||||
//! its angle (issue #367).
|
||||
|
||||
use cadcraft_engine::Session;
|
||||
use cadcraft_engine::doc::{EntityKind, Handle, MText};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
/// Draw a vertical dimension after `setup`, explode it, and return (drawn text height, drawn
|
||||
/// text angle, the exploded MTEXT).
|
||||
fn explode_vertical(setup: &[(&str, Value)]) -> (f64, f64, MText) {
|
||||
let mut s = Session::new();
|
||||
for (cmd, p) in setup {
|
||||
s.execute(cmd, p).unwrap();
|
||||
}
|
||||
let r = s.execute("dimlinear", &json!({"p1": [0, 0], "p2": [0, 30], "at": [5, 15]})).unwrap();
|
||||
let h = Handle::parse_hex(r["handle"].as_str().unwrap()).unwrap();
|
||||
let drawn = match &s.doc().unwrap().entity(h).unwrap().kind {
|
||||
EntityKind::Dimension(dm) => cadcraft_engine::render::dimension_in(s.doc().unwrap(), dm),
|
||||
k => panic!("not a dimension: {k:?}"),
|
||||
};
|
||||
s.execute("explode", &json!({"handles": [h.hex()]})).unwrap();
|
||||
let mut texts = s.doc().unwrap().model.iter().filter_map(|e| match &e.kind {
|
||||
EntityKind::MText(m) => Some(m.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let m = texts.next().expect("the exploded dimension has its text");
|
||||
assert!(texts.next().is_none());
|
||||
(drawn.text_height, drawn.text_angle, m)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exploded_dimension_text_matches_the_drawn_text() {
|
||||
let near = |a: f64, b: f64| (a - b).abs() < 1e-9;
|
||||
// SETVAR DIMSCALE is an override on the dimension: 10 × DIMTXT 0.18.
|
||||
let (h, _, m) = explode_vertical(&[("setvar", json!({"name": "DIMSCALE", "value": 10}))]);
|
||||
assert!(near(h, 1.8) && near(m.height, h), "drawn {h}, exploded {}", m.height);
|
||||
// A style DIMSCALE of 0 draws at scale 1 in model space; the text must not shrink to ~0.
|
||||
let (h, _, m) = explode_vertical(&[("dimstyle", json!({"name": "Standard", "DIMSCALE": 0}))]);
|
||||
assert!(near(h, 0.18) && near(m.height, h), "drawn {h}, exploded {}", m.height);
|
||||
// Aligned text (DIMTIH off) on a vertical dimension is drawn at 90° and stays there.
|
||||
let (_, a, m) = explode_vertical(&[("dimstyle", json!({"name": "Standard", "DIMTIH": 0}))]);
|
||||
assert!(near(a, std::f64::consts::FRAC_PI_2) && near(m.rotation, a), "drawn {a}, exploded {}", m.rotation);
|
||||
assert_eq!(m.contents, "30.0000");
|
||||
}
|
||||
|
|
@ -935,8 +935,9 @@ fn hatch_xdata(w: &mut W, h: &Hatch) {
|
|||
|
||||
/// Anonymous dimension blocks (`*D1`…) with the rendered geometry, as consumers expect.
|
||||
fn dim_block_entities(d: &Drawing, dm: &Dimension, layer: &str) -> Vec<Entity> {
|
||||
let style = d.dim_style(&dm.style).cloned().unwrap_or_default();
|
||||
let g = cadcraft_render::dimension_geometry(dm, &style, d.header.f64("DIMSCALE", 1.0));
|
||||
// The dimension as drawn: its overrides, DIMSCALE (0 = the drawing's) and text font.
|
||||
let style = d.dim_style(&dm.style).cloned().unwrap_or_default().with_overrides(&dm.overrides);
|
||||
let g = cadcraft_render::dimension_in(d, dm);
|
||||
let mut out = Vec::new();
|
||||
let c = Common { layer: layer.into(), color: Color::ByBlock, ..Common::default() };
|
||||
for l in &g.lines {
|
||||
|
|
@ -961,7 +962,7 @@ fn dim_block_entities(d: &Drawing, dm: &Dimension, layer: &str) -> Vec<Entity> {
|
|||
common: c,
|
||||
kind: EntityKind::MText(MText {
|
||||
insert: g.text_pos.to3(0.0),
|
||||
height: style.text_height * style.scale.max(1e-9),
|
||||
height: g.text_height,
|
||||
width: 0.0,
|
||||
attach: 5,
|
||||
rotation: g.text_angle,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
//! The anonymous `*D` block written for a dimension (what other programs draw) holds its text at
|
||||
//! the height it is drawn at: the dimension's overrides and DIMSCALE (0 = the drawing's) count
|
||||
//! (issue #367).
|
||||
|
||||
use cadcraft_doc::{Common, DimKind, Dimension, Drawing, EntityKind, Space};
|
||||
use cadcraft_geom::Vec3;
|
||||
|
||||
/// The group 40 (height) of the first MTEXT in the BLOCKS section.
|
||||
fn block_mtext_height(dxf: &str) -> f64 {
|
||||
let lines: Vec<&str> = dxf.lines().map(str::trim).collect();
|
||||
let blocks = lines.windows(2).position(|w| w == ["2", "BLOCKS"]).unwrap();
|
||||
let mtext = blocks + lines[blocks..].windows(2).position(|w| w == ["0", "MTEXT"]).unwrap();
|
||||
let h = mtext + lines[mtext..].chunks(2).position(|w| w[0] == "40").unwrap() * 2;
|
||||
lines[h + 1].parse().unwrap()
|
||||
}
|
||||
|
||||
fn height_written(style_scale: f64, overrides: serde_json::Value) -> f64 {
|
||||
let mut d = Drawing::new_imperial();
|
||||
d.dim_styles.iter_mut().for_each(|s| s.scale = style_scale);
|
||||
let dm = Dimension {
|
||||
kind: DimKind::Linear { rotation: 0.0 },
|
||||
defpt: Vec3::new(15.0, 5.0, 0.0),
|
||||
text_mid: Vec3::ZERO,
|
||||
p13: Vec3::ZERO,
|
||||
p14: Vec3::new(30.0, 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: overrides.as_object().cloned().unwrap_or_default(),
|
||||
assoc: Vec::new(),
|
||||
};
|
||||
d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).unwrap();
|
||||
block_mtext_height(&cadcraft_io::write_dxf(&d))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimension_block_text_has_the_drawn_height() {
|
||||
let near = |a: f64, b: f64| (a - b).abs() < 1e-9;
|
||||
let plain = height_written(1.0, serde_json::json!({}));
|
||||
assert!(near(plain, 0.18), "{plain}");
|
||||
let scaled = height_written(1.0, serde_json::json!({"scale": 10.0}));
|
||||
assert!(near(scaled, 1.8), "a DIMSCALE override scales the block text: {scaled}");
|
||||
let txt = height_written(1.0, serde_json::json!({"DIMTXT": 0.5}));
|
||||
assert!(near(txt, 0.5), "a DIMTXT override sets the block text height: {txt}");
|
||||
let zero = height_written(0.0, serde_json::json!({}));
|
||||
assert!(near(zero, 0.18), "DIMSCALE 0 in model space is scale 1, not ~0: {zero}");
|
||||
}
|
||||
Loading…
Reference in New Issue