1use std::fmt::Write;
6
7use super::{Anchor, LineType, Shape, Sheet};
8
9fn text(s: &str) -> String {
11 s.replace('⌀', "%%c")
12 .replace('°', "%%d")
13 .replace('±', "%%p")
14}
15
16struct Writer(String);
17
18impl Writer {
19 fn pair(&mut self, code: i32, value: impl std::fmt::Display) {
20 let _ = write!(self.0, "{code}\n{value}\n");
21 }
22
23 fn point(&mut self, base: i32, p: [f64; 2]) {
24 self.pair(base, p[0]);
25 self.pair(base + 10, p[1]);
26 self.pair(base + 20, 0.0);
27 }
28}
29
30pub fn to_dxf(sheet: &Sheet) -> String {
32 let mut w = Writer(String::new());
33 w.pair(0, "SECTION");
34 w.pair(2, "HEADER");
35 w.pair(9, "$ACADVER");
36 w.pair(1, "AC1009");
37 w.pair(9, "$EXTMIN");
38 w.point(10, [0.0, 0.0]);
39 w.pair(9, "$EXTMAX");
40 w.point(10, [sheet.width, sheet.height]);
41 w.pair(0, "ENDSEC");
42
43 w.pair(0, "SECTION");
44 w.pair(2, "TABLES");
45 w.pair(0, "TABLE");
46 w.pair(2, "LTYPE");
47 w.pair(70, LineType::ALL.len());
48 for line_type in LineType::ALL {
49 let dashes = line_type.dashes();
50 w.pair(0, "LTYPE");
51 w.pair(2, line_type.name());
52 w.pair(70, 0);
53 w.pair(3, line_type.description());
54 w.pair(72, 65);
55 w.pair(73, dashes.len());
56 w.pair(40, dashes.iter().map(|d| d.abs()).sum::<f64>());
57 for d in dashes {
58 w.pair(49, d);
59 }
60 }
61 w.pair(0, "ENDTAB");
62 let layers: Vec<_> = (sheet.layers().into_iter())
64 .filter(|&layer| {
65 sheet.strokes.iter().any(|s| s.layer == layer)
66 || sheet.labels.iter().any(|l| l.layer == layer)
67 })
68 .collect();
69 w.pair(0, "TABLE");
70 w.pair(2, "LAYER");
71 w.pair(70, layers.len());
72 for layer in layers {
73 w.pair(0, "LAYER");
74 w.pair(2, layer.name);
75 w.pair(70, 0);
76 w.pair(62, layer.color);
77 w.pair(6, layer.line_type.name());
78 }
79 w.pair(0, "ENDTAB");
80 w.pair(0, "ENDSEC");
81
82 w.pair(0, "SECTION");
83 w.pair(2, "ENTITIES");
84 for stroke in &sheet.strokes {
85 let layer = stroke.layer.name;
86 match &stroke.shape {
87 Shape::Line(a, b) => {
88 w.pair(0, "LINE");
89 w.pair(8, layer);
90 w.point(10, *a);
91 w.point(11, *b);
92 }
93 Shape::Circle { center, radius } => {
94 w.pair(0, "CIRCLE");
95 w.pair(8, layer);
96 w.point(10, *center);
97 w.pair(40, radius);
98 }
99 Shape::Arc {
100 center,
101 radius,
102 start,
103 end,
104 } => {
105 w.pair(0, "ARC");
106 w.pair(8, layer);
107 w.point(10, *center);
108 w.pair(40, radius);
109 w.pair(50, start.to_degrees());
110 w.pair(51, end.to_degrees());
111 }
112 Shape::Polyline(points) => {
113 w.pair(0, "POLYLINE");
114 w.pair(8, layer);
115 w.pair(66, 1);
116 w.point(10, [0.0, 0.0]);
117 w.pair(70, 0);
118 for p in points {
119 w.pair(0, "VERTEX");
120 w.pair(8, layer);
121 w.point(10, *p);
122 }
123 w.pair(0, "SEQEND");
124 w.pair(8, layer);
125 }
126 Shape::Filled(points) => {
127 let corner = |k: usize| points[k.min(points.len() - 1)];
130 w.pair(0, "SOLID");
131 w.pair(8, layer);
132 w.point(10, corner(0));
133 w.point(11, corner(1));
134 w.point(12, corner(3));
135 w.point(13, corner(2));
136 }
137 }
138 }
139 for label in &sheet.labels {
140 w.pair(0, "TEXT");
141 w.pair(8, label.layer.name);
142 w.point(10, label.at);
143 w.pair(40, label.height);
144 w.pair(1, text(&label.text));
145 if label.angle != 0.0 {
146 w.pair(50, label.angle);
147 }
148 let justify = match label.anchor {
149 Anchor::Start => 0,
150 Anchor::Middle => 1,
151 Anchor::End => 2,
152 };
153 if justify != 0 {
154 w.pair(72, justify);
155 w.point(11, label.at);
156 }
157 }
158 w.pair(0, "ENDSEC");
159 w.pair(0, "EOF");
160 w.0
161}