// HEADER 段:图纸的全局设置。
//
// 这一段和别处长得不一样,得单独说:
//
// SECTION
// 2 HEADER ← 段名
// 9 $ACADVER ← 变量名(组码 9)
// 1 AC1027 ← 变量的值(类型随变量而定)
// 9 $INSUNITS
// 70 4
// ...
// ENDSEC
//
// **变量之间没有组码 0 分隔**。别处的记录都是 `0/类型名` 开头,HEADER 里
// 一个 0 都没有——所有变量全挂在 `SECTION` 那一条记录上,靠 `9` 成对。
// 所以切记录那一层切完之后,HEADER 段是「一条巨长的记录」,得在这里
// 再做一次成对扫描。
///|
/// 一个 HEADER 变量。
pub(all) struct HeaderVar {
/// 变量名,带 `$` 前缀(`$ACADVER`)
name : String
/// 这个变量的全部值 tag。
///
/// **多数变量只有一个值,但坐标类的是三个**——`$EXTMIN` 的值写成
/// `10 / x`、`20 / y`、`30 / z` 三个 tag。所以这里存数组而不是单个值。
values : Array[Tag]
}
///|
/// HEADER 段。
pub(all) struct Header {
vars : Array[HeaderVar]
}
///|
/// 从 HEADER 段解析出变量表。
///
/// 走法:碰到 `9` 就开一个新变量,**把后面所有不是 `9` 的 tag 全收进去**,
/// 直到下一个 `9` 或者段结束。不能按「两个两个一组」扫——坐标类的变量
/// 有三个值,按对扫会把 y/z 丢掉,`$EXTMIN` 就只剩一个 x 了。
pub fn parse_header(section : Section) -> Header {
let vars : Array[HeaderVar] = []
for record in section.records {
let tags = record.common
let mut i = 0
while i < tags.length() {
if tags[i].code != 9 {
i = i + 1
continue
}
let values : Array[Tag] = []
let mut j = i + 1
while j < tags.length() && tags[j].code != 9 {
values.push(tags[j])
j = j + 1
}
vars.push({ name: tags[i].value.as_string(), values, })
i = j
}
}
{ vars, }
}
///|
/// 取一个变量的原始值。
pub fn Header::get(self : Header, name : String) -> HeaderVar? {
for v in self.vars {
if v.name == name {
return Some(v)
}
}
None
}
///|
/// 取一个变量的第一个值(字符串)。
pub fn Header::get_string(self : Header, name : String) -> String {
match self.get(name) {
Some(v) =>
if v.values.length() > 0 {
v.values[0].value.as_string()
} else {
""
}
None => ""
}
}
///|
/// 取一个变量的第一个值(数值)。没有就用默认值。
///
/// 注意 HEADER 变量的值**有时是字符串有时是数字**(`$ACADVER` 是字符串,
/// `$INSUNITS` 是整数),所以两个取值函数都要有。
pub fn Header::get_int(self : Header, name : String, default : Int) -> Int {
match self.get(name) {
Some(v) =>
if v.values.length() > 0 {
v.values[0].value.as_int()
} else {
default
}
None => default
}
}
///|
/// 取一个变量表示的三维点(`$EXTMIN` / `$EXTMAX` 这类)。
///
/// 值不够三个时返回 `None`——宁可说「不知道」,也不要拿 x 当 z 用。
pub fn Header::get_point(self : Header, name : String) -> Vec3? {
match self.get(name) {
Some(v) =>
if v.values.length() >= 3 {
Some(
Vec3::new(
v.values[0].value.as_double(),
v.values[1].value.as_double(),
v.values[2].value.as_double(),
),
)
} else {
None
}
None => None
}
}
// ── 几个常用的变量 ──────────────────────────────────────────────────────
//
// 这几个是加工检查要用到的,单独给个名字,省得到处写字符串。
///|
/// 图纸单位。
///
/// `$INSUNITS` 的值是个编号:0 = 没指定、1 = 英寸、4 = 毫米、6 = 米。
/// **0 是最危险的值**——没指定单位意味着图纸上的「1」到底是 1 毫米还是
/// 1 英寸无从判断,差 25.4 倍。加工检查会把这一条报出来。
pub fn Header::insunits(self : Header) -> Int {
self.get_int("$INSUNITS", 0)
}
///|
/// 仅显示HEADER中的单位声明,不改变坐标或自动换算检查阈值。
pub fn Drawing::declared_unit_label(self : Drawing) -> String {
match self.section("HEADER") {
Some(section) => unit_name(parse_header(section).insunits())
None => "未声明"
}
}
///|
/// 单位编号的中文名。
pub fn unit_name(code : Int) -> String {
match code {
0 => "未指定"
1 => "英寸"
2 => "英尺"
3 => "英里"
4 => "毫米"
5 => "厘米"
6 => "米"
7 => "千米"
8 => "微英寸"
9 => "密尔"
10 => "码"
11 => "埃"
12 => "纳米"
13 => "微米"
14 => "分米"
15 => "十米"
16 => "百米"
17 => "兆米"
18 => "天文单位"
19 => "光年"
20 => "秒差距"
_ => "未知(\{code})"
}
}
///|
/// 一个单位编号对应多少毫米。没指定(0)或认不出时返回 `None`——
/// **不猜**,猜错了尺寸差几百倍。
pub fn unit_to_mm(code : Int) -> Double? {
match code {
1 => Some(25.4)
2 => Some(304.8)
3 => Some(1609344.0)
4 => Some(1.0)
5 => Some(10.0)
6 => Some(1000.0)
7 => Some(1000000.0)
8 => Some(0.0000254)
9 => Some(0.0254)
10 => Some(914.4)
11 => Some(1.0e-7)
12 => Some(1.0e-6)
13 => Some(0.001)
14 => Some(100.0)
15 => Some(10000.0)
16 => Some(100000.0)
17 => Some(1.0e9)
18 => Some(1.495978707e14)
19 => Some(9.4607304725808e18)
20 => Some(3.0856775814913673e19)
_ => None
}
}
///|
/// 图纸范围的左下角(`$EXTMIN`)。
pub fn Header::extmin(self : Header) -> Vec3? {
self.get_point("$EXTMIN")
}
///|
/// 图纸范围的右上角(`$EXTMAX`)。
pub fn Header::extmax(self : Header) -> Vec3? {
self.get_point("$EXTMAX")
}