mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-31 06:16:51 -06:00
* test(io): a centred ATTDEF keeps its insertion point in a DWG (#2464) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(io): an ATTDEF with no default value is placed by its tag's width in DWG (#2464) The writer keeps a justified text's stored insertion point only when it is near where the baseline starts for the text's width; an ATTDEF with an empty default value has width 0, so every AutoCAD insertion point further than half a height from the alignment point was replaced by the alignment point. The tag is what is drawn: judge against its width. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(io): keep a stored ATTDEF point that is close to the empty text's start The tag-width baseline check replaced a point read from a drawing whenever the font's width of the tag put the computed start further than the allowance away, e.g. a centred LONGTAG with group 10 at x=99.5 and group 11 at x=100 moved to about 92.17 in DXF and DWG. A stored point close to the empty text's own start is now kept as before; only otherwise is it judged against the tag. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(io): store an ATTDEF point that differs from the computed baseline `assert_kept(start)` stored exactly the baseline the tag-width fallback computes, so a write that replaced group 10 with that baseline passed too. The point is now 1 off the start, within the tolerance. Review of #2466. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(io): an empty-value ATTDEF keeps its point with an empty or absent prompt too The tag-width check applies to every ATTDEF with no default value, since the CAD displays the tag whatever the prompt: the guard now says `attdef` instead of reading as a prompt test, and the test covers a prompt, an empty one and none. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
b4b8ae294a
commit
4792ca2941
@@ -241,8 +241,19 @@ fn computed_text_insert(d: &Drawing, t: &Text) -> Vec3 {
|
||||
if q.is_finite() { Vec3::new(q.x, q.y, t.insert.z) } else { t.insert }
|
||||
}
|
||||
|
||||
/// `attdef_prompt` is `Some` for an ATTDEF (its prompt, which may be empty) and `None` for an ATTRIB.
|
||||
pub(super) fn text_tags(w: &mut W, d: &Drawing, t: &Text, attrib: Option<(&Attrib, Version)>, attdef_prompt: Option<&str>) {
|
||||
w.p(10, text_insert(d, t));
|
||||
let attdef = attdef_prompt.is_some();
|
||||
// An attribute definition with no default value is drawn as its tag, whatever its prompt: the
|
||||
// baseline start AutoCAD stored is judged against the tag's width too. A stored point that is
|
||||
// close to the empty text's own start is kept as it is, so the font's width of the tag never
|
||||
// moves it.
|
||||
let own = text_insert(d, t);
|
||||
let insert = match attrib {
|
||||
Some((a, _)) if attdef && t.value.is_empty() && own != t.insert => text_insert(d, &Text { value: a.tag.clone(), ..t.clone() }),
|
||||
_ => own,
|
||||
};
|
||||
w.p(10, insert);
|
||||
w.f(40, t.height);
|
||||
w.s(1, &t.value);
|
||||
if t.rotation != 0.0 {
|
||||
@@ -277,7 +288,7 @@ pub(super) fn text_tags(w: &mut W, d: &Drawing, t: &Text, attrib: Option<(&Attri
|
||||
}
|
||||
match attrib {
|
||||
Some((a, target)) => {
|
||||
w.s(100, if attdef_prompt.is_some() { "AcDbAttributeDefinition" } else { "AcDbAttribute" });
|
||||
w.s(100, if attdef { "AcDbAttributeDefinition" } else { "AcDbAttribute" });
|
||||
if target >= Version::R2010 {
|
||||
w.i(280, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//! A centre-justified ATTDEF keeps its insertion point in a DWG (#2464): AutoCAD puts it where the
|
||||
//! baseline of the displayed tag starts, which for an attribute with an empty default value is
|
||||
//! further from the alignment point than half a height.
|
||||
|
||||
use cadcraft_doc::EntityKind;
|
||||
use cadcraft_io::{SaveOptions, Version, read, write_with};
|
||||
|
||||
/// The `insert` x of the only ATTDEF in `d`.
|
||||
fn attdef_x(d: &cadcraft_doc::Drawing) -> f64 {
|
||||
d.model.iter().find_map(|e| if let EntityKind::AttDef(a) = &e.kind { Some(a.text.insert.x) } else { None }).expect("an ATTDEF")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centred_attdef_with_an_empty_value_keeps_its_insertion_point() {
|
||||
// The tag "LONGTAG" at height 2.5, centred on x = 100: its baseline starts half a width left.
|
||||
// The stored point is 1 off that start (within the tolerance), so a write that recomputed
|
||||
// it from the tag would not pass for keeping it.
|
||||
let width = cadcraft_fonts::line_width("LONGTAG", 2.5, 1.0);
|
||||
let start = 100.0 - width / 2.0 - 1.0;
|
||||
assert!(100.0 - start > 3.0, "the point is well over a height from the alignment point");
|
||||
assert_kept(start);
|
||||
}
|
||||
|
||||
/// A stored point that a tag-width baseline would move is kept too (review of #2466): the check
|
||||
/// does not rely on the font's width of the tag, so this one is only 0.5 from the alignment point.
|
||||
#[test]
|
||||
fn centred_attdef_point_near_the_alignment_point_is_kept() {
|
||||
assert_kept(99.5);
|
||||
}
|
||||
|
||||
/// A centred ATTDEF with an empty default value and group 10 at `start`, group 11 at x = 100 is
|
||||
/// read from DXF and kept by a write as DXF and as DWG (both versions). The displayed text is the
|
||||
/// tag whatever the prompt: with a prompt, an empty one (group 3 empty) and none (no group 3).
|
||||
fn assert_kept(start: f64) {
|
||||
for prompt in ["3\nprompt\n", "3\n\n", ""] {
|
||||
let dxf = format!(
|
||||
"0\nSECTION\n2\nENTITIES\n0\nATTDEF\n8\n0\n10\n{start}\n20\n5.0\n40\n2.5\n1\n\n7\nSTANDARD\n72\n1\n11\n100.0\n21\n5.0\n{prompt}2\nLONGTAG\n70\n8\n0\nENDSEC\n0\nEOF\n"
|
||||
);
|
||||
let d = read(dxf.as_bytes(), "t.dxf").unwrap();
|
||||
assert!((attdef_x(&d) - start).abs() < 1e-6, "read from DXF");
|
||||
let as_dxf = cadcraft_io::write(&d, "t.dxf").unwrap();
|
||||
assert!((attdef_x(&read(&as_dxf, "t.dxf").unwrap()) - start).abs() < 1e-6, "DXF, prompt {prompt:?}: insertion point changed");
|
||||
for version in [Version::R2000, Version::R2018] {
|
||||
let dwg = write_with(&d, "t.dwg", &SaveOptions { version: Some(version), binary: false }).unwrap();
|
||||
let back = read(&dwg, "t.dwg").unwrap();
|
||||
assert!((attdef_x(&back) - start).abs() < 1e-6, "{version:?}, prompt {prompt:?}: insertion point {} instead of {start}", attdef_x(&back));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ mod angle_header;
|
||||
mod annotative_orientation;
|
||||
mod annotative_roundtrip;
|
||||
mod assoc_array_roundtrip;
|
||||
mod attdef_insert_dwg;
|
||||
mod attrib_modes;
|
||||
mod attrib_props;
|
||||
mod block_expansion;
|
||||
|
||||
Reference in New Issue
Block a user