Skip to main content

acadrust/objects/
mod.rs

1//! Non-graphical objects (OBJECTS section)
2//!
3//! Objects are non-graphical elements in a DXF file, such as dictionaries,
4//! layouts, groups, and other organizational structures.
5
6mod dictionary_variable;
7mod group;
8mod image_definition;
9mod mlinestyle;
10mod multileader_style;
11mod plot_settings;
12mod scale;
13mod sort_entities_table;
14mod table_style;
15mod xrecord;
16mod stub_objects;
17
18pub use dictionary_variable::DictionaryVariable;
19pub use group::Group;
20pub use image_definition::{ImageDefinition, ImageDefinitionReactor, ResolutionUnit};
21pub use mlinestyle::{MLineStyle, MLineStyleElement, MLineStyleFlags};
22pub use multileader_style::{
23    BlockContentConnectionType, LeaderContentType, LeaderDrawOrderType,
24    LeaderLinePropertyOverrideFlags, MultiLeaderDrawOrderType, MultiLeaderPathType,
25    MultiLeaderPropertyOverrideFlags, MultiLeaderStyle, TextAlignmentType, TextAngleType,
26    TextAttachmentDirectionType, TextAttachmentType,
27};
28pub use plot_settings::{
29    PaperMargin, PlotFlags, PlotPaperUnits, PlotRotation, PlotSettings, PlotType, PlotWindow,
30    ScaledType, ShadePlotMode, ShadePlotResolutionLevel,
31};
32pub use scale::Scale;
33pub use sort_entities_table::{SortEntsEntry, SortEntitiesTable};
34pub use table_style::{
35    CellAlignment, RowCellStyle, TableBorderPropertyFlags, TableBorderType, TableCellBorder,
36    TableCellStylePropertyFlags, TableFlowDirection, TableStyle, TableStyleFlags,
37};
38pub use xrecord::{DictionaryCloningFlags, XRecord, XRecordEntry, XRecordValue, XRecordValueType};
39pub use stub_objects::{
40    VisualStyle, Material, GeoData,
41    SpatialFilter, RasterVariables, BookColor, PlaceHolder,
42    DictionaryWithDefault, WipeoutVariables, StubObject,
43};
44
45use crate::types::Handle;
46
47/// Dictionary object - stores key-value pairs of object handles
48#[derive(Debug, Clone, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct Dictionary {
51    /// Unique handle
52    pub handle: Handle,
53    /// Owner handle (soft pointer)
54    pub owner: Handle,
55    /// Dictionary entries (key -> handle)
56    pub entries: Vec<(String, Handle)>,
57    /// Duplicate record cloning flag
58    pub duplicate_cloning: i16,
59    /// Hard owner flag
60    pub hard_owner: bool,
61    /// Reactor handles ({ACAD_REACTORS})
62    pub reactors: Vec<Handle>,
63    /// Extended dictionary handle ({ACAD_XDICTIONARY})
64    pub xdictionary_handle: Option<Handle>,
65}
66
67impl Dictionary {
68    /// Create a new dictionary
69    pub fn new() -> Self {
70        Self {
71            handle: Handle::NULL,
72            owner: Handle::NULL,
73            entries: Vec::new(),
74            duplicate_cloning: 1,
75            hard_owner: false,
76            reactors: Vec::new(),
77            xdictionary_handle: None,
78        }
79    }
80
81    /// Add an entry to the dictionary
82    pub fn add_entry(&mut self, key: impl Into<String>, handle: Handle) {
83        self.entries.push((key.into(), handle));
84    }
85
86    /// Get a handle by key
87    pub fn get(&self, key: &str) -> Option<Handle> {
88        self.entries
89            .iter()
90            .find(|(k, _)| k == key)
91            .map(|(_, h)| *h)
92    }
93
94    /// Get the number of entries
95    pub fn len(&self) -> usize {
96        self.entries.len()
97    }
98
99    /// Check if the dictionary is empty
100    pub fn is_empty(&self) -> bool {
101        self.entries.is_empty()
102    }
103}
104
105impl Default for Dictionary {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111/// Layout object - represents a layout (model space or paper space)
112#[derive(Debug, Clone, PartialEq)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct Layout {
115    /// Unique handle
116    pub handle: Handle,
117    /// Owner handle (soft pointer)
118    pub owner: Handle,
119    /// Layout name
120    pub name: String,
121    /// Layout flags
122    pub flags: i16,
123    /// Tab order
124    pub tab_order: i16,
125    /// Minimum limits
126    pub min_limits: (f64, f64),
127    /// Maximum limits
128    pub max_limits: (f64, f64),
129    /// Insertion base point
130    pub insertion_base: (f64, f64, f64),
131    /// Minimum extents
132    pub min_extents: (f64, f64, f64),
133    /// Maximum extents
134    pub max_extents: (f64, f64, f64),
135    /// Elevation (code 146)
136    pub elevation: f64,
137    /// UCS origin (codes 13/23/33)
138    pub ucs_origin: (f64, f64, f64),
139    /// UCS X axis direction (codes 16/26/36)
140    pub ucs_x_axis: (f64, f64, f64),
141    /// UCS Y axis direction (codes 17/27/37)
142    pub ucs_y_axis: (f64, f64, f64),
143    /// UCS orthographic type (code 76)
144    pub ucs_ortho_type: i16,
145    /// Associated block record handle
146    pub block_record: Handle,
147    /// Viewport handle
148    pub viewport: Handle,
149    /// Reactor handles ({ACAD_REACTORS})
150    pub reactors: Vec<Handle>,
151    /// Extended dictionary handle ({ACAD_XDICTIONARY})
152    pub xdictionary_handle: Option<Handle>,
153    /// Raw DXF AcDbPlotSettings group-code pairs for round-trip preservation.
154    /// Layouts embed PlotSettings in the DXF LAYOUT object; since our Layout
155    /// struct does not duplicate all PlotSettings fields we capture the raw
156    /// pairs on read and replay them verbatim on write.
157    #[cfg_attr(feature = "serde", serde(skip))]
158    pub raw_plot_settings_codes: Option<Vec<(i32, String)>>,
159}
160
161impl Layout {
162    /// Create a new layout
163    pub fn new(name: impl Into<String>) -> Self {
164        Self {
165            handle: Handle::NULL,
166            owner: Handle::NULL,
167            name: name.into(),
168            flags: 0,
169            tab_order: 0,
170            min_limits: (0.0, 0.0),
171            max_limits: (12.0, 9.0),
172            insertion_base: (0.0, 0.0, 0.0),
173            min_extents: (0.0, 0.0, 0.0),
174            max_extents: (12.0, 9.0, 0.0),
175            elevation: 0.0,
176            ucs_origin: (0.0, 0.0, 0.0),
177            ucs_x_axis: (1.0, 0.0, 0.0),
178            ucs_y_axis: (0.0, 1.0, 0.0),
179            ucs_ortho_type: 0,
180            block_record: Handle::NULL,
181            viewport: Handle::NULL,
182            reactors: Vec::new(),
183            xdictionary_handle: None,
184            raw_plot_settings_codes: None,
185        }
186    }
187}
188
189/// Object types
190#[derive(Debug, Clone, PartialEq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192pub enum ObjectType {
193    /// Dictionary object
194    Dictionary(Dictionary),
195    /// Layout object
196    Layout(Layout),
197    /// XRecord object - extended data storage
198    XRecord(XRecord),
199    /// Group object - named collection of entities
200    Group(Group),
201    /// MLineStyle object - multiline style definition
202    MLineStyle(MLineStyle),
203    /// ImageDefinition object - raster image definition
204    ImageDefinition(ImageDefinition),
205    /// PlotSettings object - plot configuration
206    PlotSettings(PlotSettings),
207    /// MultiLeaderStyle object - multileader style definition
208    MultiLeaderStyle(MultiLeaderStyle),
209    /// TableStyle object - table style definition
210    TableStyle(TableStyle),
211    /// Scale object - named scale definition
212    Scale(Scale),
213    /// SortEntitiesTable object - entity draw order
214    SortEntitiesTable(SortEntitiesTable),
215    /// DictionaryVariable object - named variable in dictionary
216    DictionaryVariable(DictionaryVariable),
217    /// VisualStyle object
218    VisualStyle(VisualStyle),
219    /// Material object
220    Material(Material),
221    /// ImageDefinitionReactor object
222    ImageDefinitionReactor(ImageDefinitionReactor),
223    /// GeoData object
224    GeoData(GeoData),
225    /// SpatialFilter object
226    SpatialFilter(SpatialFilter),
227    /// RasterVariables object
228    RasterVariables(RasterVariables),
229    /// BookColor (DBCOLOR) object
230    BookColor(BookColor),
231    /// PlaceHolder object
232    PlaceHolder(PlaceHolder),
233    /// DictionaryWithDefault object
234    DictionaryWithDefault(DictionaryWithDefault),
235    /// WipeoutVariables object
236    WipeoutVariables(WipeoutVariables),
237    /// Unknown object type (stored as raw data)
238    Unknown {
239        /// Object type name
240        type_name: String,
241        /// Object handle
242        handle: Handle,
243        /// Owner handle
244        owner: Handle,
245        /// Raw DXF object-specific group-code pairs.
246        ///
247        /// Each entry is `(group_code, value_string)`. When present the
248        /// DXF writer emits the object type, handle, owner and these
249        /// pairs, reproducing the original object content.
250        #[cfg_attr(feature = "serde", serde(skip))]
251        raw_dxf_codes: Option<Vec<(i32, String)>>,
252    },
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    #[test]
260    fn test_dictionary_creation() {
261        let mut dict = Dictionary::new();
262        assert!(dict.is_empty());
263
264        dict.add_entry("KEY1", Handle::new(100));
265        assert_eq!(dict.len(), 1);
266        assert_eq!(dict.get("KEY1"), Some(Handle::new(100)));
267        assert_eq!(dict.get("KEY2"), None);
268    }
269
270    #[test]
271    fn test_layout_creation() {
272        let layout = Layout::new("Layout1");
273        assert_eq!(layout.name, "Layout1");
274        assert_eq!(layout.tab_order, 0);
275    }
276}
277
278