mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-28 12:56:47 -06:00
From AutoCAD 2013 on, a 3DSOLID, REGION or BODY doesn't carry its ACIS geometry: the binary SAB lives in the file's data store section (ACDSDATA), one ACDSRECORD per entity bound by a 320 handle. CADCraft kept those entities (as unknown entities, with their handles) but dropped the section, so a drawing saved as DWG or DXF had empty solids, and saving reported them as not saved. - doc: Foreign::data_store keeps the section's groups as read; data_store_entities() lists the entities it has records for. - io: the reader keeps the section; the writer puts it back for files of 2013 and later (and in the DXF the DWG bridge converts, which acadrust reads into the DWG), without the records of entities no longer written. Nothing is written for older files or when no record's entity remains. - engine: not_saved_as(d, version): from 2013 a solid, region or body with a data store record isn't reported; saves and document.bytes pass the version being written. On a 39,600-object permit set with 625 3D solids and 2 regions, DWG -> CADCraft -> DWG now keeps all 627 bodies with byte-identical SAB (before: none), and the save no longer reports them. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
4.1 KiB
Rust
85 lines
4.1 KiB
Rust
//! Saving a drawing with entities of types CADCraft doesn't model (read from another program's
|
|
//! file) as DWG says which ones the file doesn't keep, instead of dropping them silently. DXF
|
|
//! writes them back as read: nothing to report.
|
|
|
|
use cadcraft_engine::Session;
|
|
use cadcraft_engine::cmd::file::{IoHooks, set_io};
|
|
use cadcraft_engine::doc::{Block, Common, Drawing, EntityKind, RawTag, Space, Unknown};
|
|
use serde_json::json;
|
|
|
|
fn unknown(kind: &str) -> EntityKind {
|
|
EntityKind::Unknown(Unknown { dxf_type: kind.into(), tags: vec![RawTag { code: 8, value: "0".into() }] })
|
|
}
|
|
|
|
#[test]
|
|
fn save_lists_the_entities_the_file_does_not_keep() {
|
|
set_io(IoHooks { read: |_, _| Err("no reader in this test".into()), write: |_, _| Ok(b"0\nEOF\n".to_vec()), plot: None });
|
|
let mut d = Drawing::new_imperial();
|
|
for k in ["MLINE", "3DSOLID", "MLINE"] {
|
|
d.add(&Space::Model, Common::default(), unknown(k)).unwrap();
|
|
}
|
|
let mut b = Block::new("Part");
|
|
let h = d.new_handle();
|
|
b.entities.push(cadcraft_engine::doc::Entity { handle: h, common: Common::default(), kind: unknown("REGION") });
|
|
d.blocks.insert("Part".into(), std::sync::Arc::new(b));
|
|
let mut s = Session::empty();
|
|
s.open_drawing(d, "foreign.dxf", None);
|
|
|
|
let path = std::env::temp_dir().join(format!("cadcraft-not-saved-{}.dwg", std::process::id()));
|
|
let r = s.execute("saveas", &json!({ "path": path.to_string_lossy() })).unwrap();
|
|
let _ = std::fs::remove_file(&path);
|
|
assert_eq!(r["notSaved"], json!({ "3DSOLID": 1, "MLINE": 2, "REGION": 1 }));
|
|
let msg = "Not saved in the DWG file (its writer can't write these object types yet; DXF keeps them): 1 3DSOLID, 2 MLINE, 1 REGION";
|
|
assert!(s.log.iter().any(|l| l == msg), "{:?}", s.log);
|
|
|
|
// DXF keeps them.
|
|
let dxf = std::env::temp_dir().join(format!("cadcraft-not-saved-{}.dxf", std::process::id()));
|
|
let n = s.log.len();
|
|
let r = s.execute("saveas", &json!({ "path": dxf.to_string_lossy() })).unwrap();
|
|
let _ = std::fs::remove_file(&dxf);
|
|
assert!(r.get("notSaved").is_none() && s.log.len() == n);
|
|
|
|
// Exporting an image isn't a save of the drawing: no report.
|
|
let png = std::env::temp_dir().join(format!("cadcraft-not-saved-{}.png", std::process::id()));
|
|
let r = s.execute("saveas", &json!({ "path": png.to_string_lossy() })).unwrap();
|
|
let _ = std::fs::remove_file(&png);
|
|
assert!(r.get("notSaved").is_none());
|
|
|
|
// A drawing the file keeps completely: nothing to report.
|
|
s.open_drawing(Drawing::new_imperial(), "plain.dxf", None);
|
|
let n = s.log.len();
|
|
let r = s.execute("document.bytes", &json!({})).unwrap();
|
|
assert!(r.get("notSaved").is_none() && s.log.len() == n);
|
|
}
|
|
|
|
#[test]
|
|
fn templates_report_what_dwg_does_not_keep() {
|
|
use cadcraft_engine::cmd::file::drops_unknown;
|
|
assert!(drops_unknown("a.dwg") && drops_unknown("B.DWT"));
|
|
assert!(!drops_unknown("a.dxf"));
|
|
}
|
|
|
|
#[test]
|
|
fn solids_whose_geometry_the_data_store_keeps_are_not_reported_from_2013() {
|
|
use cadcraft_engine::cmd::file::{Version, not_saved_as};
|
|
use cadcraft_engine::doc::Handle;
|
|
use cadcraft_engine::doc::foreign::Foreign;
|
|
let mut d = Drawing::new_imperial();
|
|
let kept = d.add(&Space::Model, Common::default(), unknown("3DSOLID")).unwrap();
|
|
d.add(&Space::Model, Common::default(), unknown("3DSOLID")).unwrap();
|
|
d.add(&Space::Model, Common::default(), unknown("REGION")).unwrap();
|
|
let record = |h: Handle| {
|
|
vec![
|
|
RawTag { code: 0, value: "ACDSRECORD".into() },
|
|
RawTag { code: 320, value: h.hex() },
|
|
RawTag { code: 2, value: "ASM_Data".into() },
|
|
RawTag { code: 310, value: "00".into() },
|
|
]
|
|
};
|
|
d.foreign = std::sync::Arc::new(Foreign { data_store: record(kept), ..Foreign::default() });
|
|
// The solid with a data store record keeps its geometry from 2013 on; the others don't.
|
|
assert_eq!(not_saved_as(&d, Version::R2018), [("3DSOLID".to_string(), 1), ("REGION".to_string(), 1)].into_iter().collect());
|
|
// Before 2013 a file has no data store: every solid loses its geometry.
|
|
assert_eq!(not_saved_as(&d, Version::R2010), [("3DSOLID".to_string(), 2), ("REGION".to_string(), 1)].into_iter().collect());
|
|
}
|