// 子类:DXF 里「同一个组码在不同类里含义不同」的那套机制。
//
// 新版本(R13 及以后)用 `100` 标记显式分段:
//
// 100 AcDbEntity ← 从这里开始,下面是实体通用字段
// 8 图层
// 100 AcDbCircle ← 换类,下面是圆特有字段
// 10/20/30 圆心
//
// 老版本(R10/R12)**一个 `100` 都没有**,字段全糊在一起。
// 但语义是一样的:图层属于实体通用部分、圆心半径属于圆特有部分,
// 只是文件里没写出来而已。
//
// 所以这里做一件事:**给老记录合成子类名**,让上层不用分两套代码写。
//
// 一个需要说清楚的地方:**取字段其实不靠子类**。
// `Record::get(10)` 会扫遍公共部分和所有子类,新旧两种格式都能拿到圆心。
// 真正需要子类名的是那些**同名组码含义不同**的场景——最典型的是组码 70:
//
// 100 AcDbSymbolTable ← 在这条子类里,70 是「表里有多少项」
// 100 AcDbLayerTableRecord ← 在这条子类里,70 是「图层标志位」
//
// 碰到这种就得用 `get_in(记录, "AcDbLayerTableRecord", 70)` 明说。
// `tables.mbt` 里就是这么取的。
///|
/// 表里的组码 70 在 `AcDbSymbolTable` 子类里是「表里有多少项」,
/// 在具体记录的子类里另有含义。取字段时要指定子类。
pub fn Record::has_subclass(self : Record, name : String) -> Bool {
for sc in self.subclasses {
if sc.name == name {
return true
}
}
false
}
///|
/// 这条记录是不是老格式(一个 `100` 标记都没有)。
///
/// 老格式的字段全在公共部分里,取字段照样用 `get`,不影响。
pub fn Record::is_legacy(self : Record) -> Bool {
self.subclasses.length() == 0
}
///|
/// 记录的子类名列表。
///
/// 老记录没有 `100`,这里**按记录类型合成一个**——`CIRCLE` 记录的字段
/// 在语义上就属于 `AcDbCircle`。合成之后,报告和检查逻辑看到的记录形状
/// 在新旧版本之间是一致的。
///
/// 合成表只列我们要解码的实体。认不出的记录类型返回空数组,
/// 上层按「老格式实体」处理即可——它们的字段全在公共部分,本来就取得出来。
pub fn Record::subclass_names(self : Record) -> Array[String] {
if self.subclasses.length() > 0 {
let out : Array[String] = []
for sc in self.subclasses {
out.push(sc.name)
}
return out
}
match synthesized_subclass(self.kind) {
Some(name) => [name]
None => []
}
}
///|
/// 记录类型名 → 新版本里对应的子类名。
///
/// 这张表只覆盖本项目要解码的实体,以及几个结构性的记录。
/// 不做全量映射:DXF 上百种实体,没解码的那些本来就走 `Unknown` 分支,
/// 给它们编个子类名没有意义。
fn synthesized_subclass(kind : String) -> String? {
match kind {
"LINE" => Some("AcDbLine")
"CIRCLE" => Some("AcDbCircle")
"ARC" => Some("AcDbArc")
"ELLIPSE" => Some("AcDbEllipse")
"POINT" => Some("AcDbPoint")
"SPLINE" => Some("AcDbSpline")
"TEXT" => Some("AcDbText")
"MTEXT" => Some("AcDbMText")
"INSERT" => Some("AcDbBlockReference")
"ATTRIB" => Some("AcDbAttribute")
"ATTDEF" => Some("AcDbAttributeDefinition")
"LWPOLYLINE" => Some("AcDbPolyline")
"POLYLINE" => Some("AcDb2dPolyline")
"VERTEX" => Some("AcDbVertex")
"SEQEND" => Some("AcDbEntity")
"HATCH" => Some("AcDbHatch")
"SOLID" => Some("AcDbTrace")
"TRACE" => Some("AcDbTrace")
"3DFACE" => Some("AcDbFace")
"DIMENSION" => Some("AcDbDimension")
"LAYER" => Some("AcDbLayerTableRecord")
"LTYPE" => Some("AcDbLinetypeTableRecord")
"STYLE" => Some("AcDbTextStyleTableRecord")
"DIMSTYLE" => Some("AcDbDimStyleTableRecord")
"BLOCK_RECORD" => Some("AcDbBlockTableRecord")
_ => None
}
}
///|
/// 这条记录在语义上有哪些类。
///
/// 新版本总是从 `AcDbEntity` 起头(实体通用字段),后面跟着自己那个类;
/// 老版本只合成出「自己那个类」,补上 `AcDbEntity` 让两边形状一致。
pub fn Record::effective_classes(self : Record) -> Array[String] {
let names = self.subclass_names()
if names.length() == 0 {
return names
}
// 新格式的记录本来就从 AcDbEntity 开始,不用补
if names[0] == "AcDbEntity" {
return names
}
// 结构性记录(LAYER、LTYPE 之类)不是实体,不该补 AcDbEntity
if !is_entity_kind(self.kind) {
return names
}
let out : Array[String] = ["AcDbEntity"]
for n in names {
out.push(n)
}
out
}
///|
/// 这个记录类型是不是「实体」(会画出来的东西),而不是表记录之类的结构。
fn is_entity_kind(kind : String) -> Bool {
match kind {
"LINE"
| "CIRCLE"
| "ARC"
| "ELLIPSE"
| "POINT"
| "SPLINE"
| "TEXT"
| "MTEXT"
| "INSERT"
| "ATTRIB"
| "ATTDEF"
| "LWPOLYLINE"
| "POLYLINE"
| "VERTEX"
| "SEQEND"
| "HATCH"
| "SOLID"
| "TRACE"
| "3DFACE"
| "DIMENSION" => true
_ => false
}
}