mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 10:56:51 -06:00
acadrust writes every paper-space entity with entity mode 1 ("paper
space"), including those of *Paper_Space0, *Paper_Space1… and relies on
each block's entity list; AutoCAD reads mode 1 as the *Paper_Space
block's, so what was drawn on a second layout (and its viewports, in
drawings from AutoCAD with several layouts) landed on the first one, and
the second stayed empty. The DWG bridge now writes those entities owned
by their block (entity mode 0 with the owner handle), as the public DWG
specification describes; the first layout's entities stay mode 1.
The io tests read CADCraft's DWG back with acadrust (a dev-dependency,
the same version as cadcraft-dwg's) to check the modes as written.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
632c2d4f1f
commit
162d5c6820
Generated
+1
@@ -663,6 +663,7 @@ dependencies = [
|
|||||||
name = "cadcraft-io"
|
name = "cadcraft-io"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"acadrust",
|
||||||
"cadcraft-color",
|
"cadcraft-color",
|
||||||
"cadcraft-doc",
|
"cadcraft-doc",
|
||||||
"cadcraft-dwg",
|
"cadcraft-dwg",
|
||||||
|
|||||||
@@ -423,6 +423,7 @@ mod native {
|
|||||||
None if doc.version < MIN_DWG_VERSION => doc.version = MIN_DWG_VERSION,
|
None if doc.version < MIN_DWG_VERSION => doc.version = MIN_DWG_VERSION,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
other_layout_owners(&mut doc);
|
||||||
table_style_rows(&mut doc, true);
|
table_style_rows(&mut doc, true);
|
||||||
drop_unwritable(&mut doc);
|
drop_unwritable(&mut doc);
|
||||||
current_dimstyle(&mut doc);
|
current_dimstyle(&mut doc);
|
||||||
@@ -430,6 +431,31 @@ mod native {
|
|||||||
.map_err(|_| "the DWG writer failed on this drawing".to_string())?
|
.map_err(|_| "the DWG writer failed on this drawing".to_string())?
|
||||||
.map_err(|e| format!("DWG write: {e}"))
|
.map_err(|e| format!("DWG write: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Entities of a layout other than the one in `*Paper_Space` are written owned by their block
|
||||||
|
/// (entity mode 0, with its handle). acadrust writes every paper-space entity with mode 1,
|
||||||
|
/// "paper space", which AutoCAD reads as the `*Paper_Space` block's: everything drawn on a
|
||||||
|
/// second layout landed on the first one, and the second stayed empty (#1057).
|
||||||
|
fn other_layout_owners(doc: &mut acadrust::CadDocument) {
|
||||||
|
let others: std::collections::HashSet<acadrust::Handle> = doc
|
||||||
|
.block_records
|
||||||
|
.iter()
|
||||||
|
.filter(|br| {
|
||||||
|
let n = br.name.to_ascii_uppercase();
|
||||||
|
n.len() > 12 && n.starts_with("*PAPER_SPACE") && n[12..].bytes().all(|b| b.is_ascii_digit())
|
||||||
|
})
|
||||||
|
.map(|br| br.handle)
|
||||||
|
.collect();
|
||||||
|
if others.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for e in doc.entities_mut() {
|
||||||
|
let c = e.common_mut();
|
||||||
|
if others.contains(&c.owner_handle) {
|
||||||
|
c.entity_mode = Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ miniz_oxide = "0.8"
|
|||||||
quick-xml = { workspace = true }
|
quick-xml = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
# Reads CADCraft's DWG output as written (tests only; the same version as cadcraft-dwg's).
|
||||||
|
acadrust = "0.6.4"
|
||||||
tiny-skia = { workspace = true }
|
tiny-skia = { workspace = true }
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//! #1057: in a DWG, entities of a layout other than the first (`*Paper_Space`) are written owned
|
||||||
|
//! by their own block (entity mode 0 and its handle). Written as "paper space" (mode 1), AutoCAD
|
||||||
|
//! put them on the first layout and left theirs empty.
|
||||||
|
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use cadcraft_doc::{Circle, Common, Drawing, EntityKind, Layout, Line, Space};
|
||||||
|
use cadcraft_geom::Vec3;
|
||||||
|
use cadcraft_io::{SaveOptions, Version, read, write_with};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn entities_of_a_second_layout_are_owned_by_its_block() {
|
||||||
|
let mut d = Drawing::new_metric();
|
||||||
|
let order = d.layouts.iter().map(|l| l.tab_order).max().unwrap_or(0) + 1;
|
||||||
|
d.layouts.push(Layout::new("SHEET_A", order));
|
||||||
|
let line = |a: f64| EntityKind::Line(Line { a: Vec3::new(a, 0.0, 0.0), b: Vec3::new(a + 10.0, 10.0, 0.0) });
|
||||||
|
d.add(&Space::Model, Common::default(), EntityKind::Circle(Circle { center: Vec3::ZERO, radius: 5.0 })).unwrap();
|
||||||
|
let first = d.layouts.iter().min_by_key(|l| l.tab_order).unwrap().name.clone();
|
||||||
|
d.add(&Space::Paper(first.clone()), Common::default(), line(0.0)).unwrap();
|
||||||
|
d.add(&Space::Paper("SHEET_A".into()), Common::default(), line(20.0)).unwrap();
|
||||||
|
d.add(&Space::Paper("SHEET_A".into()), Common::default(), line(40.0)).unwrap();
|
||||||
|
let dwg = write_with(&d, "case.dwg", &SaveOptions { version: Some(Version::R2018), binary: false }).unwrap();
|
||||||
|
|
||||||
|
// As written: entity mode and owner of each entity.
|
||||||
|
let doc = acadrust::DwgReader::from_stream(Cursor::new(dwg.clone())).read().unwrap();
|
||||||
|
let block_of = |h: acadrust::Handle| doc.block_records.iter().find(|b| b.handle == h).map(|b| b.name.to_ascii_uppercase());
|
||||||
|
let sheet_a = doc
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.find_map(|(_, o)| match o {
|
||||||
|
acadrust::objects::ObjectType::Layout(l) if l.name == "SHEET_A" => Some(l.block_record),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let mut on_sheet_a = 0;
|
||||||
|
for e in doc.entities() {
|
||||||
|
let c = e.common();
|
||||||
|
let block = block_of(c.owner_handle).unwrap_or_default();
|
||||||
|
if c.owner_handle == sheet_a {
|
||||||
|
on_sheet_a += 1;
|
||||||
|
assert_eq!(c.entity_mode, Some(0), "an entity of SHEET_A ({block}) is owned by its block");
|
||||||
|
} else if block == "*PAPER_SPACE" {
|
||||||
|
assert_eq!(c.entity_mode, Some(1), "the first layout's entities stay paper space");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(on_sheet_a, 2);
|
||||||
|
|
||||||
|
// Read back, each layout has its own.
|
||||||
|
let back = read(&dwg, "case.dwg").unwrap();
|
||||||
|
let count = |n: &str| back.layouts.iter().find(|l| l.name == n).map_or(0, |l| l.entities.len());
|
||||||
|
assert_eq!((count(&first), count("SHEET_A"), back.model.len()), (1, 2, 1));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user