mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-10 12:30:16 -06:00
* dxf: proxy objects and entities keep their class through a save (#1812) A proxy (ACAD_PROXY_OBJECT, ACAD_PROXY_ENTITY) names its class by number in group 91: 500 plus the class's place in the CLASSES section (DXF Reference). Saving rewrites CLASSES (CADCraft's own classes, then the kept ones), but kept proxies were written with their read number, which then named another class: an R14 file's visual styles came back as SCALE, TABLESTYLE or MLEADERSTYLE, and numbers past the last class stayed. The reader now keeps the file's class order (Foreign::class_order) when the file has proxies, and the CLASS records its proxies name. The writer renumbers each kept proxy to its class's place in the written CLASSES and drops a proxy whose number names no class (with what it owns and its dictionary entry). The DWG bridge writes through the same path. Test proxies_keep_their_class_when_classes_are_rewritten (a file with VISUALSTYLE and WIDGETX first in CLASSES, proxies of both and of a number past the last class, and a proxy entity; also through a second save). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP * docs: the proxy class fix is for DXF save (#1812) From the local CodeRabbit review: DWG save drops proxy entities (acadrust) and proxy objects through DWG weren't checked, so the progress log says DXF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP * dxf: a proxy's class number is found outside 102 groups (#1812) From the PR review: an application's 102 {NAME ... 102 } group before a proxy's own groups can hold a group 91, which was read as the class number and overwritten on save. class_group finds the first 91 outside 102 groups (before xdata), for reading and renumbering alike (test proxies_keep_their_class_when_classes_are_rewritten, now with a {MYAPP group holding a 91 that stays as it was). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP --------- Co-authored-by: Tyrel Haveman <1434399+tyrel@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
97 lines
4.6 KiB
Rust
97 lines
4.6 KiB
Rust
//! Data from a drawing file that CADCraft does not model, kept so that saving the drawing writes
|
|
//! it back: other programs' objects, extension dictionaries, xdata and properties (#528).
|
|
//!
|
|
//! It is kept as the file's own DXF groups, with the file's handles. The file reader fills it and
|
|
//! the writer puts it back, pointing references at whatever now has the referenced role (the
|
|
//! named object dictionary, a table record, a block, an entity); references to objects that are
|
|
//! gone are dropped. Commands don't edit it.
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use crate::{Handle, RawTag};
|
|
|
|
/// What a file handle stood for when the object it named is one CADCraft writes itself (under a
|
|
/// new handle on save): a kind and an upper-case name, e.g. `("LAYER", "WALLS")`,
|
|
/// `("TABLE", "LAYER")`, `("BLOCK", "DOOR")`, `("NOD", "")` for the named object dictionary and
|
|
/// `("NOD", "ACAD_GROUP")` for one of its entries.
|
|
pub type ForeignKey = (String, String);
|
|
|
|
/// A non-graphical object of a type or role CADCraft does not model.
|
|
#[derive(Clone, Debug, Default, PartialEq)]
|
|
pub struct ForeignObject {
|
|
/// Its handle in the file it came from (kept on save).
|
|
pub handle: Handle,
|
|
/// Its DXF type (group 0), e.g. `XRECORD`.
|
|
pub kind: String,
|
|
/// Its groups after the type, as read.
|
|
pub tags: Vec<RawTag>,
|
|
}
|
|
|
|
/// What an entity, table record or block carries beyond what CADCraft models.
|
|
#[derive(Clone, Debug, Default, PartialEq)]
|
|
pub struct ForeignExtra {
|
|
/// Its extension dictionary (one of [`Foreign::objects`]).
|
|
pub xdict: Option<Handle>,
|
|
/// Objects of [`Foreign::objects`] that react to it (persistent reactors).
|
|
pub reactors: Vec<Handle>,
|
|
/// Other applications' extended data of a table record or block: groups from `1001` on. An
|
|
/// entity's is part of the entity ([`crate::Common::xdata`]), so it follows the entity's copies.
|
|
pub xdata: Vec<RawTag>,
|
|
/// The colour book colour (group 430) and the true colour (group 420) it was read with: it is
|
|
/// written back while the colour is unchanged.
|
|
pub color_book: Option<(String, String)>,
|
|
/// Other unmodelled property groups (material 347, shadow mode 284).
|
|
pub props: Vec<RawTag>,
|
|
}
|
|
|
|
impl ForeignExtra {
|
|
pub fn is_empty(&self) -> bool {
|
|
self.xdict.is_none() && self.reactors.is_empty() && self.xdata.is_empty() && self.color_book.is_none() && self.props.is_empty()
|
|
}
|
|
}
|
|
|
|
/// Everything of a file CADCraft keeps without modelling it.
|
|
#[derive(Clone, Debug, Default, PartialEq)]
|
|
pub struct Foreign {
|
|
/// CLASS records (groups after `0 CLASS`) of the kept objects' and entities' types.
|
|
pub classes: Vec<Vec<RawTag>>,
|
|
/// Kept objects, in file order.
|
|
pub objects: Vec<ForeignObject>,
|
|
/// Entries of the named object dictionary that CADCraft doesn't write itself: (key, object).
|
|
pub dictionary: Vec<(String, Handle)>,
|
|
/// Extras of entities, by handle.
|
|
pub entities: BTreeMap<Handle, ForeignExtra>,
|
|
/// Extras of table records (`("LAYER", "WALLS")`), tables (`("TABLE", "LAYER")`) and blocks
|
|
/// (`("BLOCK", "DOOR")`), by [`ForeignKey`].
|
|
pub records: BTreeMap<ForeignKey, ForeignExtra>,
|
|
/// The roles of the file handles the kept data refers to, for objects CADCraft rewrites.
|
|
pub targets: BTreeMap<Handle, ForeignKey>,
|
|
/// The upper-case names of the file's classes in its CLASSES order, kept when the file has
|
|
/// proxy objects or entities: a proxy's class number (group 91) is 500 plus its class's place
|
|
/// here, and saving renumbers it to the written CLASSES (#1812).
|
|
pub class_order: Vec<String>,
|
|
/// The file's data store section (ACDSDATA, AutoCAD 2013 and later), as read: its groups
|
|
/// after the section name. It holds the ACIS geometry of 3D solids, regions, bodies and
|
|
/// surfaces, which from 2013 on those entities don't carry themselves: each `ACDSRECORD`
|
|
/// names its entity with a 320 handle (see [`Foreign::data_store_entities`]).
|
|
pub data_store: Vec<RawTag>,
|
|
}
|
|
|
|
impl Foreign {
|
|
pub fn is_empty(&self) -> bool {
|
|
self.objects.is_empty()
|
|
&& self.dictionary.is_empty()
|
|
&& self.entities.is_empty()
|
|
&& self.records.is_empty()
|
|
&& self.classes.is_empty()
|
|
&& self.targets.is_empty()
|
|
&& self.class_order.is_empty()
|
|
&& self.data_store.is_empty()
|
|
}
|
|
|
|
/// The entities the data store has records for (their 320 handles).
|
|
pub fn data_store_entities(&self) -> std::collections::BTreeSet<Handle> {
|
|
self.data_store.iter().filter(|t| t.code == 320).filter_map(|t| Handle::parse_hex(t.value.trim())).collect()
|
|
}
|
|
}
|