use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ProxyFlags(pub u16);
impl ProxyFlags {
pub const NONE: Self = Self(0);
pub const ERASE_ALLOWED: Self = Self(1);
pub const TRANSFORM_ALLOWED: Self = Self(2);
pub const COLOR_CHANGE_ALLOWED: Self = Self(4);
pub const LAYER_CHANGE_ALLOWED: Self = Self(8);
pub const LINETYPE_CHANGE_ALLOWED: Self = Self(16);
pub const LINETYPE_SCALE_CHANGE_ALLOWED: Self = Self(32);
pub const VISIBILITY_CHANGE_ALLOWED: Self = Self(64);
pub const CLONING_ALLOWED: Self = Self(128);
pub const LINEWEIGHT_CHANGE_ALLOWED: Self = Self(256);
pub const PLOT_STYLE_NAME_CHANGE_ALLOWED: Self = Self(512);
pub const ALL_OPERATIONS_EXCEPT_CLONING: Self = Self(895);
pub const ALL_OPERATIONS_ALLOWED: Self = Self(1023);
pub const DISABLES_PROXY_WARNING_DIALOG: Self = Self(1024);
pub const R13_FORMAT_PROXY: Self = Self(32768);
pub fn contains(self, flag: Self) -> bool {
(self.0 & flag.0) == flag.0
}
}
impl Default for ProxyFlags {
fn default() -> Self {
Self::NONE
}
}
impl From<u16> for ProxyFlags {
fn from(val: u16) -> Self {
Self(val)
}
}
impl From<i32> for ProxyFlags {
fn from(val: i32) -> Self {
Self(val as u16)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DxfClass {
pub dxf_name: String,
pub cpp_class_name: String,
pub application_name: String,
pub proxy_flags: ProxyFlags,
pub instance_count: i32,
pub was_zombie: bool,
pub is_an_entity: bool,
pub class_number: i16,
pub item_class_id: i16,
}
impl DxfClass {
pub fn new(dxf_name: impl Into<String>, cpp_class_name: impl Into<String>) -> Self {
Self {
dxf_name: dxf_name.into(),
cpp_class_name: cpp_class_name.into(),
application_name: "ObjectDBX Classes".to_string(),
proxy_flags: ProxyFlags::NONE,
instance_count: 0,
was_zombie: false,
is_an_entity: false,
class_number: 0,
item_class_id: 499, }
}
pub fn new_entity(dxf_name: impl Into<String>, cpp_class_name: impl Into<String>) -> Self {
let mut class = Self::new(dxf_name, cpp_class_name);
class.is_an_entity = true;
class.item_class_id = 498;
class
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DxfClassCollection {
entries: Vec<DxfClass>,
name_index: HashMap<String, usize>,
}
impl DxfClassCollection {
pub fn new() -> Self {
Self {
entries: Vec::new(),
name_index: HashMap::new(),
}
}
pub fn add_or_update(&mut self, mut class: DxfClass) {
let key = class.dxf_name.to_uppercase();
if let Some(&idx) = self.name_index.get(&key) {
self.entries[idx].instance_count = class.instance_count;
} else {
if class.class_number < 500 {
class.class_number = 500 + self.entries.len() as i16;
}
let idx = self.entries.len();
self.name_index.insert(key, idx);
self.entries.push(class);
}
}
pub fn get_by_name(&self, dxf_name: &str) -> Option<&DxfClass> {
let key = dxf_name.to_uppercase();
self.name_index.get(&key).map(|&idx| &self.entries[idx])
}
pub fn contains(&self, dxf_name: &str) -> bool {
self.name_index.contains_key(&dxf_name.to_uppercase())
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &DxfClass> {
self.entries.iter()
}
pub fn clear(&mut self) {
self.entries.clear();
self.name_index.clear();
}
pub fn update_defaults(&mut self) {
let defaults = default_classes();
for class in defaults {
if !self.contains(&class.dxf_name) {
self.add_or_update(class);
}
}
}
}
impl Default for DxfClassCollection {
fn default() -> Self {
Self::new()
}
}
impl<'a> IntoIterator for &'a DxfClassCollection {
type Item = &'a DxfClass;
type IntoIter = std::slice::Iter<'a, DxfClass>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}
fn default_classes() -> Vec<DxfClass> {
let defs: &[(&str, &str, u16, &str, bool)] = &[
("MESH", "AcDbSubDMesh", 4095,
"AcDbSubDMesh|Description: AutoCAD subD mesh", true),
("ACAD_TABLE", "AcDbTable", 1025, "ObjectDBX Classes", true),
("WIPEOUT", "AcDbWipeout", 127,
"WipeOut|AutoCAD Express Tool|www.autodesk.com", true),
("IMAGE", "AcDbRasterImage", 127, "ISM", true),
("PDFREFERENCE", "AcDbPdfReference", 1, "ObjectDBX Classes", true),
("DWFREFERENCE", "AcDbDwfReference", 1, "ObjectDBX Classes", true),
("DGNREFERENCE", "AcDbDgnReference", 1, "ObjectDBX Classes", true),
("MULTILEADER", "AcDbMLeader", 1025, "ACDB_MLEADER_CLASS", true),
("OLE2FRAME", "AcDbOle2Frame", 1, "ObjectDBX Classes", true),
("MLINE", "AcDbMline", 1, "ObjectDBX Classes", true),
("ACDBDICTIONARYWDFLT", "AcDbDictionaryWithDefault", 0, "ObjectDBX Classes", false),
("ACDBPLACEHOLDER", "AcDbPlaceHolder", 0, "ObjectDBX Classes", false),
("LAYOUT", "AcDbLayout", 0, "ObjectDBX Classes", false),
("DICTIONARYVAR", "AcDbDictionaryVar", 0, "ObjectDBX Classes", false),
("TABLESTYLE", "AcDbTableStyle", 1025, "ObjectDBX Classes", false),
("MATERIAL", "AcDbMaterial", 1025, "ObjectDBX Classes", false),
("VISUALSTYLE", "AcDbVisualStyle", 4095, "ObjectDBX Classes", false),
("SCALE", "AcDbScale", 1153, "ObjectDBX Classes", false),
("MLEADERSTYLE", "AcDbMLeaderStyle", 4095, "ACDB_MLEADERSTYLE_CLASS", false),
("CELLSTYLEMAP", "AcDbCellStyleMap", 1025, "ObjectDBX Classes", false),
("XRECORD", "AcDbXrecord", 0, "ObjectDBX Classes", false),
("SORTENTSTABLE", "AcDbSortentsTable", 0, "ObjectDBX Classes", false),
("WIPEOUTVARIABLES", "AcDbWipeoutVariables", 0,
"WipeOut|AutoCAD Express Tool|www.autodesk.com", false),
("DIMASSOC", "AcDbDimAssoc", 0,
"AcDbDimAssoc|Product Desc: AcDim ARX App For Dimension|Company: Autodesk|WEB Address: www.autodesk.com", false),
("TABLECONTENT", "AcDbTableContent", 1025, "ObjectDBX Classes", false),
("TABLEGEOMETRY", "AcDbTableGeometry", 1025, "ObjectDBX Classes", false),
("RASTERVARIABLES", "AcDbRasterVariables", 0, "ISM", false),
("IMAGEDEF", "AcDbRasterImageDef", 0, "ISM", false),
("IMAGEDEF_REACTOR", "AcDbRasterImageDefReactor", 1, "ISM", false),
("DBCOLOR", "AcDbColor", 1025, "ObjectDBX Classes", false),
("GEODATA", "AcDbGeoData", 1025, "ObjectDBX Classes", false),
("PDFDEFINITION", "AcDbPdfDefinition", 1, "ObjectDBX Classes", false),
("DWFDEFINITION", "AcDbDwfDefinition", 1, "ObjectDBX Classes", false),
("DGNDEFINITION", "AcDbDgnDefinition", 1, "ObjectDBX Classes", false),
("SPATIAL_FILTER", "AcDbSpatialFilter", 1, "ObjectDBX Classes", false),
("PLOTSETTINGS", "AcDbPlotSettings", 0, "ObjectDBX Classes", false),
("GROUP", "AcDbGroup", 0, "ObjectDBX Classes", false),
("MLINESTYLE", "AcDbMlineStyle", 0, "ObjectDBX Classes", false),
];
defs.iter().map(|&(dxf, cpp, flags, app, is_entity)| {
if is_entity {
let mut c = DxfClass::new_entity(dxf, cpp);
c.proxy_flags = ProxyFlags(flags);
c.application_name = app.to_string();
c
} else {
let mut c = DxfClass::new(dxf, cpp);
c.proxy_flags = ProxyFlags(flags);
c.application_name = app.to_string();
c
}
}).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dxf_class_creation() {
let class = DxfClass::new("MLEADERSTYLE", "AcDbMLeaderStyle");
assert_eq!(class.dxf_name, "MLEADERSTYLE");
assert_eq!(class.cpp_class_name, "AcDbMLeaderStyle");
assert!(!class.is_an_entity);
assert_eq!(class.item_class_id, 499);
}
#[test]
fn test_entity_class() {
let class = DxfClass::new_entity("MESH", "AcDbSubDMesh");
assert!(class.is_an_entity);
assert_eq!(class.item_class_id, 498);
}
#[test]
fn test_collection_add_or_update() {
let mut coll = DxfClassCollection::new();
let mut c = DxfClass::new("XRECORD", "AcDbXrecord");
c.instance_count = 5;
coll.add_or_update(c);
assert_eq!(coll.len(), 1);
assert_eq!(coll.get_by_name("XRECORD").unwrap().instance_count, 5);
assert_eq!(coll.get_by_name("XRECORD").unwrap().class_number, 500);
let mut c2 = DxfClass::new("xrecord", "AcDbXrecord");
c2.instance_count = 10;
coll.add_or_update(c2);
assert_eq!(coll.len(), 1);
assert_eq!(coll.get_by_name("XRECORD").unwrap().instance_count, 10);
}
#[test]
fn test_collection_defaults() {
let mut coll = DxfClassCollection::new();
coll.update_defaults();
assert!(coll.len() > 20);
assert!(coll.contains("MESH"));
assert!(coll.contains("LAYOUT"));
assert!(coll.contains("MLEADERSTYLE"));
}
#[test]
fn test_proxy_flags() {
let flags = ProxyFlags::ALL_OPERATIONS_ALLOWED;
assert!(flags.contains(ProxyFlags::ERASE_ALLOWED));
assert!(flags.contains(ProxyFlags::TRANSFORM_ALLOWED));
assert!(flags.contains(ProxyFlags::CLONING_ALLOWED));
}
}