KiCad PCB EDA Suite
Loading...
Searching...
No Matches
DXF_plotter.cpp
Go to the documentation of this file.
1
5/*
6 * This program source code file is part of KiCad, a free EDA CAD application.
7 *
8 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24#include <cstring>
26#include <macros.h>
27#include <string_utils.h>
29#include <geometry/shape_rect.h>
30#include <trigo.h>
31#include <fmt/format.h>
32#include <algorithm>
33
38static const double DXF_OBLIQUE_ANGLE = 15;
39
40// No support for linewidths in DXF
41#define DXF_LINE_WIDTH DO_NOT_SET_LINE_WIDTH
42
59static const struct
60{
61 const char* name;
62 int index;
64{
65 { "BLACK", 250 },
66 { "RED", 14 },
67 { "YELLOW", 52 },
68 { "GREEN", 94 },
69 { "CYAN", 134 },
70 { "BLUE", 174 },
71 { "MAGENTA", 214 },
72 { "WHITE", 250 },
73 { "BROWN", 54 },
74 { "ORANGE", 32 },
75 { "LIGHTRED", 12 },
76 { "LIGHTYELLOW", 51 },
77 { "LIGHTGREEN", 92 },
78 { "LIGHTCYAN", 132 },
79 { "LIGHTBLUE", 172 },
80 { "LIGHTMAGENTA", 212 },
81 { "LIGHTORANGE", 30 },
82 { "LIGHTGRAY", 9 },
83 { "DARKRED", 16 },
84 { "DARKYELLOW", 41 },
85 { "DARKGREEN", 96 },
86 { "DARKCYAN", 136 },
87 { "DARKBLUE", 176 },
88 { "DARKMAGENTA", 216 },
89 { "DARKBROWN", 56 },
90 { "DARKORANGE", 34 },
91 { "DARKGRAY", 8 },
92 { "PURERED", 1 },
93 { "PUREYELLOW", 2 },
94 { "PUREGREEN", 3 },
95 { "PURECYAN", 4 },
96 { "PUREBLUE", 5 },
97 { "PUREMAGENTA", 6 },
98 { "PUREORANGE", 40 },
99 { "PUREGRAY", 57 },
100 { "REDONE", 11 },
101 { "REDTWO", 13 },
102 { "REDTHREE", 15 },
103 { "REDFOUR", 17 },
104 { "REDFIVE", 18 },
105 { "REDSIX", 19 },
106 { "ORANGEONE", 20 },
107 { "ORANGETWO", 21 },
108 { "ORANGETHREE", 22 },
109 { "ORANGEFOUR", 23 },
110 { "ORANGEFIVE", 24 },
111 { "REDSEVEN", 25 },
112 { "REDEIGHT", 26 },
113 { "ORANGESIX", 27 },
114 { "REDNINE", 28 },
115 { "ORANGESEVEN", 29 },
116 { "ORANGEEIGHT", 31 },
117 { "ORANGENINE", 33 },
118 { "ORANGETEN", 35 },
119 { "ORANGEELEVEN", 36 },
120 { "ORANGETWELVE", 37 },
121 { "ORANGETHIRTEEN", 38 },
122 { "ORANGEFOURTEEN", 39 },
123 { "YELLOWONE", 42 },
124 { "YELLOWTWO", 43 },
125 { "YELLOWTHREE", 44 },
126 { "YELLOWFOUR", 45 },
127 { "YELLOWFIVE", 46 },
128 { "YELLOWSIX", 47 },
129 { "YELLOWSEVEN", 48 },
130 { "YELLOWEIGHT", 49 },
131 { "YELLOWNINE", 53 },
132 { "YELLOWTEN", 55 },
133 { "YELLOWELEVEN", 58 },
134 { "YELLOWTWELVE", 59 },
135 { "YELLOWTHIRTEEN", 60 },
136 { "YELLOWFOURTEEN", 61 },
137 { "GREENONE", 62 },
138 { "GREENTWO", 63 },
139 { "GREENTHREE", 64 },
140 { "GREENFOUR", 65 },
141 { "GREENFIVE", 66 },
142 { "GREENSIX", 67 },
143 { "GREENSEVEN", 68 },
144 { "GREENEIGHT", 69 },
145 { "GREENNINE", 70 },
146 { "GREENTEN", 71 },
147 { "GREENELEVEN", 72 },
148 { "GREENTWELVE", 73 },
149 { "GREENTHIRTEEN", 74 },
150 { "GREENFOURTEEN", 75 },
151 { "GREENFIFTEEN", 76 },
152 { "GREENSIXTEEN", 77 },
153 { "GREENSEVENTEEN", 78 },
154 { "GREENEIGHTEEN", 79 },
155 { "GREENNINETEEN", 80 },
156 { "GREENTWENTY", 81 },
157 { "GREENTWENTYONE", 82 },
158 { "GREENTWENTYTWO", 83 },
159 { "GREENTWENTYTHREE", 84 },
160 { "GREENTWENTYFOUR", 85 },
161 { "GREENTWENTYFIVE", 86 },
162 { "GREENTWENTYSIX", 87 },
163 { "GREENTWENTYSEVEN", 88 },
164 { "GREENTWENTYEIGHT", 89 },
165 { "GREENTWENTYNINE", 90 },
166 { "GREENTHIRTY", 91 },
167 { "GREENTHIRTYONE", 93 },
168 { "GREENTHIRTYTWO", 95 },
169 { "GREENTHIRTYTHREE", 97 },
170 { "GREENTHIRTYFOUR", 98 },
171 { "GREENTHIRTYFIVE", 99 },
172 { "GREENTHIRTYSIX", 100 },
173 { "GREENTHIRTYSEVEN", 101 },
174 { "GREENTHIRTYEIGHT", 102 },
175 { "GREENTHIRTYNINE", 103 },
176 { "GREENFORTY", 104 },
177 { "GREENFORTYONE", 105 },
178 { "GREENFORTYTWO", 106 },
179 { "GREENFORTYTHREE", 107 },
180 { "GREENFORTYFOUR", 108 },
181 { "GREENFORTYFIVE", 109 },
182 { "GREENFORTYSIX", 110 },
183 { "GREENFORTYSEVEN", 111 },
184 { "GREENFORTYEIGHT", 112 },
185 { "GREENFORTYNINE", 113 },
186 { "GREENFIFTY", 114 },
187 { "GREENFIFTYONE", 115 },
188 { "GREENFIFTYTWO", 116 },
189 { "GREENFIFTYTHREE", 117 },
190 { "GREENFIFTYFOUR", 118 },
191 { "GREENFIFTYFIVE", 119 },
192 { "GREENFIFTYSIX", 120 },
193 { "GREENFIFTYSEVEN", 121 },
194 { "GREENFIFTYEIGHT", 122 },
195 { "GREENFIFTYNINE", 123 },
196 { "GREENSIXTY", 124 },
197 { "GREENSIXTYONE", 125 },
198 { "GREENSIXTYTWO", 126 },
199 { "GREENSIXTYTHREE", 127 },
200 { "GREENSIXTYFOUR", 128 },
201 { "GREENSIXTYFIVE", 129 },
202 { "CYANONE", 131 },
203 { "CYANTWO", 133 },
204 { "CYANTHREE", 135 },
205 { "CYANFOUR", 137 },
206 { "CYANFIVE", 138 },
207 { "CYANSIX", 139 },
208 { "CYANSEVEN", 140 },
209 { "BLUEONE", 141 },
210 { "BLUETWO", 142 },
211 { "BLUETHREE", 143 },
212 { "BLUEFOUR", 144 },
213 { "BLUEFIVE", 145 },
214 { "BLUESIX", 146 },
215 { "BLUESEVEN", 147 },
216 { "BLUEEIGHT", 148 },
217 { "BLUENINE", 149 },
218 { "BLUETEN", 150 },
219 { "BLUEELEVEN", 151 },
220 { "BLUETWELVE", 152 },
221 { "BLUETHIRTEEN", 153 },
222 { "BLUEFOURTEEN", 154 },
223 { "BLUEFIFTEEN", 155 },
224 { "BLUESIXTEEN", 156 },
225 { "BLUESEVENTEEN", 157 },
226 { "BLUEEIGHTEEN", 158 },
227 { "BLUENINETEEN", 159 },
228 { "BLUETWENTY", 160 },
229 { "BLUETWENTYONE", 161 },
230 { "BLUETWENTYTWO", 162 },
231 { "BLUETWENTYTHREE", 163 },
232 { "BLUETWENTYFOUR", 164 },
233 { "BLUETWENTYFIVE", 165 },
234 { "BLUETWENTYSIX", 166 },
235 { "BLUETWENTYSEVEN", 167 },
236 { "BLUETWENTYEIGHT", 168 },
237 { "BLUETWENTYNINE", 169 },
238 { "BLUETHIRTY", 170 },
239 { "BLUETHIRTYONE", 171 },
240 { "BLUETHIRTYTWO", 177 },
241 { "BLUETHIRTYETHREE", 178 },
242 { "BLUETHIRTYFOUR", 179 },
243 { "VIOLETONE", 180 },
244 { "VIOLETTWO", 181 },
245 { "VIOLETTHREE", 182 },
246 { "VIOLETFOUR", 183 },
247 { "VIOLETFIVE", 184 },
248 { "VIOLETSIX", 185 },
249 { "VIOLETSEVEN", 186 },
250 { "VIOLETEIGHT", 187 },
251 { "VIOLETNINE", 188 },
252 { "VIOLETTEN", 189 },
253 { "VIOLETELEVEN", 190 },
254 { "VIOLETTWELVE", 191 },
255 { "VIOLETTHIRTEEN", 192 },
256 { "VIOLETFOURTEEN", 193 },
257 { "VIOLETFIFTEEN", 194 },
258 { "VIOLETSIXTEEN", 195 },
259 { "VIOLETSEVENTEEN", 196 },
260 { "VIOLETEIGHTEEN", 197 },
261 { "VIOLETNINETEEN", 198 },
262 { "VIOLETTWENTY", 199 },
263 { "VIOLETTWENTYONE", 200 },
264 { "VIOLETTWENTYTWO", 201 },
265 { "VIOLETTWENTYTHREE", 202 },
266 { "VIOLETTWENTYFOUR", 203 },
267 { "VIOLETTWENTYFIVE", 204 },
268 { "VIOLETTWENTYSIX", 205 },
269 { "VIOLETTWENTYSEVEN", 206 },
270 { "VIOLETTWENTYEIGHT", 207 },
271 { "VIOLETTWENTYNINE", 208 },
272 { "VIOLETTHIRTY", 209 },
273 { "MAGENTAONE", 210 },
274 { "MAGENTATWO", 211 },
275 { "MAGENTATHREE", 213 },
276 { "MAGENTAFOUR", 215 },
277 { "MAGENTAFIVE", 217 },
278 { "MAGENTASIX", 218 },
279 { "MAGENTASEVEN", 219 },
280 { "MAGENTAEIGHT", 220 },
281 { "MAGENTANINE", 221 },
282 { "MAGENTATEN", 222 },
283 { "MAGENTAELEVEN", 223 },
284 { "MAGENTATWELVE", 224 },
285 { "MAGENTATHIRTEEN", 225 },
286 { "MAGENTAFOURTEEN", 226 },
287 { "REDTEN", 227 },
288 { "REDELEVEN", 228 },
289 { "VIOLETFIFTEEN", 229 },
290 { "REDTWELVE", 230 },
291 { "REDTHIRTEEN", 231 },
292 { "REDFOURTEEN", 232 },
293 { "REDFIFTEEN", 233 },
294 { "REDSIXTEEN", 234 },
295 { "REDSEVENTEEN", 235 },
296 { "REDEIGHTEEN", 236 },
297 { "REDNINETEEN", 237 },
298 { "REDTWENTY", 238 },
299 { "REDTWENTYONE", 239 },
300 { "REDTWENTYTWO", 240 },
301 { "REDTWENTYTHREE", 241 },
302 { "REDTWENTYFOUR", 242 },
303 { "REDTWENTYFIVE", 243 },
304 { "REDTWENTYSIX", 244 },
305 { "REDTWENTYSEVEN", 245 },
306 { "REDTWENTYEIGHT", 246 },
307 { "REDTWENTYNINE", 247 },
308 { "REDTHIRTY", 248 },
309 { "REDTHIRTYONE", 249 },
310 { "GRAYONE", 251 },
311 { "GRAYTWO", 252 },
312 { "GRAYTHREE", 253 },
313 { "GRAYFOUR", 254 }
315
316// Array of predefined DXF color values, each entry containing blue, green, red components and a corresponding
317// color number.
318static const struct
319{
320 int blue;
321 int green;
322 int red;
325{
326 { 0, 0, 0, DXF_COLOR_T::BLACK },
327 { 0, 0, 127, DXF_COLOR_T::RED, },
328 { 0, 165, 165, DXF_COLOR_T::YELLOW, },
329 { 0, 127, 0, DXF_COLOR_T::GREEN, },
330 { 127, 127, 0, DXF_COLOR_T::CYAN, },
331 { 127, 0, 0, DXF_COLOR_T::BLUE, },
332 { 127, 0, 127, DXF_COLOR_T::MAGENTA, },
333 { 255, 255, 255, DXF_COLOR_T::WHITE, },
334 { 0, 127, 127, DXF_COLOR_T::BROWN, },
335 { 0, 82, 165, DXF_COLOR_T::ORANGE, },
336 { 0, 0, 165, DXF_COLOR_T::LIGHTRED, },
337 { 127, 255, 255, DXF_COLOR_T::LIGHTYELLOW, },
338 { 0, 165, 0, DXF_COLOR_T::LIGHTGREEN, },
339 { 165, 165, 0, DXF_COLOR_T::LIGHTCYAN, },
340 { 165, 0, 0, DXF_COLOR_T::LIGHTBLUE, },
341 { 165, 0, 165, DXF_COLOR_T::LIGHTMAGENTA, },
342 { 0, 127, 255, DXF_COLOR_T::LIGHTORANGE, },
343 { 192, 192, 192, DXF_COLOR_T::LIGHTGRAY, },
344 { 0, 0, 76, DXF_COLOR_T::DARKRED, },
345 { 127, 223, 255, DXF_COLOR_T::DARKYELLOW, },
346 { 0, 76, 0, DXF_COLOR_T::DARKGREEN, },
347 { 76, 76, 0, DXF_COLOR_T::DARKCYAN, },
348 { 76, 0, 0, DXF_COLOR_T::DARKBLUE, },
349 { 76, 0, 76, DXF_COLOR_T::DARKMAGENTA, },
350 { 0, 76, 76, DXF_COLOR_T::DARKBROWN, },
351 { 0, 63, 127, DXF_COLOR_T::DARKORANGE, },
352 { 128, 128, 128, DXF_COLOR_T::DARKGRAY, },
353 { 0, 0, 255, DXF_COLOR_T::PURERED, },
354 { 0, 255, 255, DXF_COLOR_T::PUREYELLOW, },
355 { 0, 255, 0, DXF_COLOR_T::PUREGREEN, },
356 { 255, 255, 0, DXF_COLOR_T::PURECYAN, },
357 { 255, 0, 0, DXF_COLOR_T::PUREBLUE, },
358 { 255, 0, 255, DXF_COLOR_T::PUREMAGENTA, },
359 { 0, 191, 255, DXF_COLOR_T::PUREORANGE, },
360 { 38, 76, 76, DXF_COLOR_T::PUREGRAY, },
361 { 127, 127, 255, DXF_COLOR_T::REDONE, },
362 { 82, 82, 165, DXF_COLOR_T::REDTWO, },
363 { 63, 63, 127, DXF_COLOR_T::REDTHREE, },
364 { 38, 38, 76, DXF_COLOR_T::REDFOUR, },
365 { 0, 0, 38, DXF_COLOR_T::REDFIVE, },
366 { 19, 19, 38, DXF_COLOR_T::REDSIX, },
367 { 0, 63, 255, DXF_COLOR_T::ORANGEONE, },
368 { 127, 159, 255, DXF_COLOR_T::ORANGETWO, },
369 { 0, 41, 165, DXF_COLOR_T::ORANGETHREE, },
370 { 82, 103, 165, DXF_COLOR_T::ORANGEFOUR, },
371 { 0, 31, 127, DXF_COLOR_T::ORANGEFIVE, },
372 { 63, 79, 127, DXF_COLOR_T::REDSEVEN, },
373 { 0, 19, 76, DXF_COLOR_T::REDEIGHT, },
374 { 38, 47, 76, DXF_COLOR_T::ORANGESIX, },
375 { 0, 9, 38, DXF_COLOR_T::REDNINE, },
376 { 19, 23, 38, DXF_COLOR_T::ORANGESEVEN, },
377 { 127, 191, 255, DXF_COLOR_T::ORANGEEIGHT, },
378 { 82, 124, 165, DXF_COLOR_T::ORANGENINE, },
379 { 63, 95, 127, DXF_COLOR_T::ORANGETEN, },
380 { 0, 38, 76, DXF_COLOR_T::ORANGEELEVEN, },
381 { 38, 57, 76, DXF_COLOR_T::ORANGETWELVE, },
382 { 0, 19, 38, DXF_COLOR_T::ORANGETHIRTEEN, },
383 { 19, 28, 38, DXF_COLOR_T::ORANGEFOURTEEN, },
384 { 0, 124, 165, DXF_COLOR_T::YELLOWONE, },
385 { 82, 145, 165, DXF_COLOR_T::YELLOWTWO, },
386 { 0, 95, 127, DXF_COLOR_T::YELLOWTHREE, },
387 { 63, 111, 127, DXF_COLOR_T::YELLOWFOUR, },
388 { 0, 57, 76, DXF_COLOR_T::YELLOWFIVE, },
389 { 38, 66, 76, DXF_COLOR_T::YELLOWSIX, },
390 { 0, 28, 38, DXF_COLOR_T::YELLOWSEVEN, },
391 { 19, 33, 38, DXF_COLOR_T::YELLOWEIGHT, },
392 { 82, 165, 165, DXF_COLOR_T::YELLOWNINE, },
393 { 63, 127, 127, DXF_COLOR_T::YELLOWTEN, },
394 { 0, 38, 38, DXF_COLOR_T::YELLOWELEVEN, },
395 { 19, 38, 38, DXF_COLOR_T::YELLOWTWELVE, },
396 { 0, 255, 191, DXF_COLOR_T::YELLOWTHIRTEEN, },
397 { 127, 255, 223, DXF_COLOR_T::YELLOWFOURTEEN, },
398 { 0, 165, 124, DXF_COLOR_T::GREENONE, },
399 { 82, 165, 145, DXF_COLOR_T::GREENTWO, },
400 { 0, 127, 95, DXF_COLOR_T::GREENTHREE, },
401 { 63, 127, 111, DXF_COLOR_T::GREENFOUR, },
402 { 0, 76, 57, DXF_COLOR_T::GREENFIVE, },
403 { 38, 76, 66, DXF_COLOR_T::GREENSIX, },
404 { 0, 38, 28, DXF_COLOR_T::GREENSEVEN, },
405 { 19, 38, 33, DXF_COLOR_T::GREENEIGHT, },
406 { 0, 255, 127, DXF_COLOR_T::GREENNINE, },
407 { 127, 255, 191, DXF_COLOR_T::GREENTEN, },
408 { 0, 165, 82, DXF_COLOR_T::GREENELEVEN, },
409 { 82, 165, 124, DXF_COLOR_T::GREENTWELVE, },
410 { 0, 127, 63, DXF_COLOR_T::GREENTHIRTEEN, },
411 { 63, 127, 95, DXF_COLOR_T::GREENFOURTEEN, },
412 { 0, 76, 38, DXF_COLOR_T::GREENFIFTEEN, },
413 { 38, 76, 57, DXF_COLOR_T::GREENSIXTEEN, },
414 { 0, 38, 19, DXF_COLOR_T::GREENSEVENTEEN, },
415 { 19, 38, 28, DXF_COLOR_T::GREENEIGHTEEN, },
416 { 0, 255, 63, DXF_COLOR_T::GREENNINETEEN, },
417 { 127, 255, 159, DXF_COLOR_T::GREENTWENTY, },
418 { 0, 165, 41, DXF_COLOR_T::GREENTWENTYONE, },
419 { 82, 165, 103, DXF_COLOR_T::GREENTWENTYTWO, },
420 { 0, 127, 31, DXF_COLOR_T::GREENTWENTYTHREE, },
421 { 63, 127, 79, DXF_COLOR_T::GREENTWENTYFOUR, },
422 { 0, 76, 19, DXF_COLOR_T::GREENTWENTYFIVE, },
423 { 38, 76, 47, DXF_COLOR_T::GREENTWENTYSIX, },
424 { 0, 38, 9, DXF_COLOR_T::GREENTWENTYSEVEN, },
425 { 19, 38, 23, DXF_COLOR_T::GREENTWENTYEIGHT, },
426 { 0, 255, 0, DXF_COLOR_T::GREENTWENTYNINE, },
427 { 127, 255, 127, DXF_COLOR_T::GREENTHIRTY, },
428 { 82, 165, 82, DXF_COLOR_T::GREENTHIRTYONE, },
429 { 63, 127, 63, DXF_COLOR_T::GREENTHIRTYTWO, },
430 { 38, 76, 38, DXF_COLOR_T::GREENTHIRTYTHREE, },
431 { 0, 38, 0, DXF_COLOR_T::GREENTHIRTYFOUR, },
432 { 19, 38, 19, DXF_COLOR_T::GREENTHIRTYFIVE, },
433 { 63, 255, 0, DXF_COLOR_T::GREENTHIRTYSIX, },
434 { 159, 255, 127, DXF_COLOR_T::GREENTHIRTYSEVEN, },
435 { 41, 165, 0, DXF_COLOR_T::GREENTHIRTYEIGHT, },
436 { 103, 165, 82, DXF_COLOR_T::GREENTHIRTYNINE, },
437 { 31, 127, 0, DXF_COLOR_T::GREENFORTY, },
438 { 79, 127, 63, DXF_COLOR_T::GREENFORTYONE, },
439 { 19, 76, 0, DXF_COLOR_T::GREENFORTYTWO, },
440 { 47, 76, 38, DXF_COLOR_T::GREENFORTYTHREE, },
441 { 9, 38, 0, DXF_COLOR_T::GREENFORTYFOUR, },
442 { 23, 88, 19, DXF_COLOR_T::GREENFORTYFIVE, },
443 { 127, 255, 0, DXF_COLOR_T::GREENFORTYSIX, },
444 { 191, 255, 127, DXF_COLOR_T::GREENFORTYSEVEN, },
445 { 82, 165, 0, DXF_COLOR_T::GREENFORTYEIGHT, },
446 { 95, 127, 63, DXF_COLOR_T::GREENFORTYNINE, },
447 { 63, 127, 0, DXF_COLOR_T::GREENFIFTY, },
448 { 95, 127, 63, DXF_COLOR_T::GREENFIFTYONE, },
449 { 38, 76, 0, DXF_COLOR_T::GREENFIFTYTWO, },
450 { 57, 76, 38, DXF_COLOR_T::GREENFIFTYTHREE, },
451 { 19, 38, 0, DXF_COLOR_T::GREENFIFTYFOUR, },
452 { 28, 88, 19, DXF_COLOR_T::GREENFIFTYFIVE, },
453 { 191, 255, 0, DXF_COLOR_T::GREENFIFTYSIX, },
454 { 223, 255, 127, DXF_COLOR_T::GREENFIFTYSEVEN, },
455 { 124, 165, 0, DXF_COLOR_T::GREENFIFTYEIGHT, },
456 { 145, 165, 82, DXF_COLOR_T::GREENFIFTYNINE, },
457 { 95, 127, 0, DXF_COLOR_T::GREENSIXTY, },
458 { 111, 127, 63, DXF_COLOR_T::GREENSIXTYONE, },
459 { 57, 76, 0, DXF_COLOR_T::GREENSIXTYTWO, },
460 { 66, 76, 38, DXF_COLOR_T::GREENSIXTYTHREE, },
461 { 28, 38, 0, DXF_COLOR_T::GREENSIXTYFOUR, },
462 { 88, 88, 19, DXF_COLOR_T::GREENSIXTYFIVE, },
463 { 255, 255, 127, DXF_COLOR_T::CYANONE, },
464 { 165, 165, 82, DXF_COLOR_T::CYANTWO, },
465 { 127, 127, 63, DXF_COLOR_T::CYANTHREE, },
466 { 76, 76, 38, DXF_COLOR_T::CYANFOUR, },
467 { 38, 38, 0, DXF_COLOR_T::CYANFIVE, },
468 { 88, 88, 19, DXF_COLOR_T::CYANSIX, },
469 { 255, 191, 0, DXF_COLOR_T::CYANSEVEN, },
470 { 255, 223, 127, DXF_COLOR_T::BLUEONE, },
471 { 165, 124, 0, DXF_COLOR_T::BLUETWO, },
472 { 165, 145, 82, DXF_COLOR_T::BLUETHREE, },
473 { 127, 95, 0, DXF_COLOR_T::BLUEFOUR, },
474 { 127, 111, 63, DXF_COLOR_T::BLUEFIVE, },
475 { 76, 57, 0, DXF_COLOR_T::BLUESIX, },
476 { 126, 66, 38, DXF_COLOR_T::BLUESEVEN, },
477 { 38, 28, 0, DXF_COLOR_T::BLUEEIGHT, },
478 { 88, 88, 19, DXF_COLOR_T::BLUENINE, },
479 { 255, 127, 0, DXF_COLOR_T::BLUETEN, },
480 { 255, 191, 127, DXF_COLOR_T::BLUEELEVEN, },
481 { 165, 82, 0, DXF_COLOR_T::BLUETWELVE, },
482 { 165, 124, 82, DXF_COLOR_T::BLUETHIRTEEN, },
483 { 127, 63, 0, DXF_COLOR_T::BLUEFOURTEEN, },
484 { 127, 95, 63, DXF_COLOR_T::BLUEFIFTEEN, },
485 { 76, 38, 0, DXF_COLOR_T::BLUESIXTEEN, },
486 { 126, 57, 38, DXF_COLOR_T::BLUESEVENTEEN, },
487 { 38, 19, 0, DXF_COLOR_T::BLUEEIGHTEEN, },
488 { 88, 28, 19, DXF_COLOR_T::BLUENINETEEN, },
489 { 255, 63, 0, DXF_COLOR_T::BLUETWENTY, },
490 { 255, 159, 127, DXF_COLOR_T::BLUETWENTYONE, },
491 { 165, 41, 0, DXF_COLOR_T::BLUETWENTYTWO, },
492 { 165, 103, 82, DXF_COLOR_T::BLUETWENTYTHREE, },
493 { 127, 31, 0, DXF_COLOR_T::BLUETWENTYFOUR, },
494 { 127, 79, 63, DXF_COLOR_T::BLUETWENTYFIVE, },
495 { 76, 19, 0, DXF_COLOR_T::BLUETWENTYSIX, },
496 { 126, 47, 38, DXF_COLOR_T::BLUETWENTYSEVEN, },
497 { 38, 9, 0, DXF_COLOR_T::BLUETWENTYEIGHT, },
498 { 88, 23, 19, DXF_COLOR_T::BLUETWENTYNINE, },
499 { 255, 0, 0, DXF_COLOR_T::BLUETHIRTY, },
500 { 255, 127, 127, DXF_COLOR_T::BLUETHIRTYONE, },
501 { 126, 38, 38, DXF_COLOR_T::BLUETHIRTYTWO, },
502 { 38, 0, 0, DXF_COLOR_T::BLUETHIRTYETHREE, },
503 { 88, 19, 19, DXF_COLOR_T::BLUETHIRTYFOUR, },
504 { 255, 0, 63, DXF_COLOR_T::VIOLETONE, },
505 { 255, 127, 159, DXF_COLOR_T::VIOLETTWO, },
506 { 165, 0, 41, DXF_COLOR_T::VIOLETTHREE, },
507 { 165, 82, 103, DXF_COLOR_T::VIOLETFOUR, },
508 { 127, 0, 31, DXF_COLOR_T::VIOLETFIVE, },
509 { 127, 63, 79, DXF_COLOR_T::VIOLETSIX, },
510 { 76, 0, 19, DXF_COLOR_T::VIOLETSEVEN, },
511 { 126, 38, 47, DXF_COLOR_T::VIOLETEIGHT, },
512 { 38, 0, 9, DXF_COLOR_T::VIOLETNINE, },
513 { 88, 19, 23, DXF_COLOR_T::VIOLETTEN, },
514 { 255, 0, 127, DXF_COLOR_T::VIOLETELEVEN, },
515 { 255, 127, 191, DXF_COLOR_T::VIOLETTWELVE, },
516 { 165, 0, 82, DXF_COLOR_T::VIOLETTHIRTEEN, },
517 { 165, 82, 124, DXF_COLOR_T::VIOLETFOURTEEN, },
518 { 127, 0, 63, DXF_COLOR_T::VIOLETFIFTEEN, },
519 { 127, 63, 95, DXF_COLOR_T::VIOLETSIXTEEN, },
520 { 76, 0, 38, DXF_COLOR_T::VIOLETSEVENTEEN, },
521 { 126, 38, 57, DXF_COLOR_T::VIOLETEIGHTEEN, },
522 { 38, 0, 19, DXF_COLOR_T::VIOLETNINETEEN, },
523 { 88, 19, 28, DXF_COLOR_T::VIOLETTWENTY, },
524 { 255, 0, 191, DXF_COLOR_T::VIOLETTWENTYONE, },
525 { 255, 127, 223, DXF_COLOR_T::VIOLETTWENTYTWO, },
526 { 165, 0, 124, DXF_COLOR_T::VIOLETTWENTYTHREE, },
527 { 165, 82, 145, DXF_COLOR_T::VIOLETTWENTYFOUR, },
528 { 127, 0, 95, DXF_COLOR_T::VIOLETTWENTYFIVE, },
529 { 127, 63, 111, DXF_COLOR_T::VIOLETTWENTYSIX, },
530 { 76, 0, 57, DXF_COLOR_T::VIOLETTWENTYSEVEN, },
531 { 76, 38, 66, DXF_COLOR_T::VIOLETTWENTYEIGHT, },
532 { 38, 0, 28, DXF_COLOR_T::VIOLETTWENTYNINE, },
533 { 88, 19, 88, DXF_COLOR_T::VIOLETTHIRTY, },
534 { 255, 0, 255, DXF_COLOR_T::MAGENTAONE, },
535 { 255, 127, 255, DXF_COLOR_T::MAGENTATWO, },
536 { 165, 82, 165, DXF_COLOR_T::MAGENTATHREE, },
537 { 127, 63, 127, DXF_COLOR_T::MAGENTAFOUR, },
538 { 76, 38, 76, DXF_COLOR_T::MAGENTAFIVE, },
539 { 38, 0, 38, DXF_COLOR_T::MAGENTASIX, },
540 { 88, 19, 88, DXF_COLOR_T::MAGENTASEVEN, },
541 { 191, 0, 255, DXF_COLOR_T::MAGENTAEIGHT, },
542 { 223, 127, 255, DXF_COLOR_T::MAGENTANINE, },
543 { 124, 0, 165, DXF_COLOR_T::MAGENTATEN, },
544 { 145, 82, 165, DXF_COLOR_T::MAGENTAELEVEN, },
545 { 95, 0, 127, DXF_COLOR_T::MAGENTATWELVE, },
546 { 111, 63, 127, DXF_COLOR_T::MAGENTATHIRTEEN, },
547 { 57, 0, 76, DXF_COLOR_T::MAGENTAFOURTEEN, },
548 { 66, 38, 76, DXF_COLOR_T::REDTEN, },
549 { 28, 0, 38, DXF_COLOR_T::REDELEVEN, },
550 { 88, 19, 88, DXF_COLOR_T::VIOLETFIFTEEN, },
551 { 127, 0, 255, DXF_COLOR_T::REDTWELVE, },
552 { 191, 127, 255, DXF_COLOR_T::REDTHIRTEEN, },
553 { 82, 0, 165, DXF_COLOR_T::REDFOURTEEN, },
554 { 124, 82, 165, DXF_COLOR_T::REDFIFTEEN, },
555 { 63, 0, 127, DXF_COLOR_T::REDSIXTEEN, },
556 { 95, 63, 127, DXF_COLOR_T::REDSEVENTEEN, },
557 { 38, 0, 76, DXF_COLOR_T::REDEIGHTEEN, },
558 { 57, 38, 76, DXF_COLOR_T::REDNINETEEN, },
559 { 19, 0, 38, DXF_COLOR_T::REDTWENTY, },
560 { 28, 19, 88, DXF_COLOR_T::REDTWENTYONE, },
561 { 63, 0, 255, DXF_COLOR_T::REDTWENTYTWO, },
562 { 159, 127, 255, DXF_COLOR_T::REDTWENTYTHREE, },
563 { 41, 0, 165, DXF_COLOR_T::REDTWENTYFOUR, },
564 { 103, 82, 165, DXF_COLOR_T::REDTWENTYFIVE, },
565 { 31, 0, 127, DXF_COLOR_T::REDTWENTYSIX, },
566 { 79, 63, 127, DXF_COLOR_T::REDTWENTYSEVEN, },
567 { 19, 0, 76, DXF_COLOR_T::REDTWENTYEIGHT, },
568 { 47, 38, 76, DXF_COLOR_T::REDTWENTYNINE, },
569 { 9, 0, 38, DXF_COLOR_T::REDTHIRTY, },
570 { 23, 19, 88, DXF_COLOR_T::REDTHIRTYONE, },
571 { 101, 101, 101, DXF_COLOR_T::GRAYONE, },
572 { 102, 102, 102, DXF_COLOR_T::GRAYTWO, },
573 { 153, 153, 153, DXF_COLOR_T::GRAYTHREE, },
574 { 204, 204, 204, DXF_COLOR_T::GRAYFOUR, }
576
577
578static const char* getDXFLineType( LINE_STYLE aType )
579{
580 switch( aType )
581 {
584 return "CONTINUOUS";
585 case LINE_STYLE::DASH:
586 return "DASHED";
587 case LINE_STYLE::DOT:
588 return "DOTTED";
590 return "DASHDOT";
592 return "DIVIDE";
593 default:
594 wxFAIL_MSG( "Unhandled LINE_STYLE" );
595 return "CONTINUOUS";
596 }
597}
598
599
600int DXF_PLOTTER::FindNearestLegacyColor( int aR, int aG, int aB )
601{
602 int nearestColorValueIndex = static_cast<int>(DXF_COLOR_T::BLACK);
603
604 int nearestDistance = std::numeric_limits<int>::max();
605
606 for( int trying = static_cast<int>(DXF_COLOR_T::BLACK); trying < static_cast<int>(DXF_COLOR_T::NBCOLORS); trying++ )
607 {
608 auto c = acad_dxf_color_values[trying];
609 int distance = ( aR - c.red ) * ( aR - c.red ) + ( aG - c.green ) * ( aG - c.green )
610 + ( aB - c.blue ) * ( aB - c.blue );
611
612 if( distance < nearestDistance )
613 {
614 nearestDistance = distance;
615 nearestColorValueIndex = trying;
616 }
617 }
618
619 return nearestColorValueIndex;
620}
621
622
623wxString DXF_PLOTTER::GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE aMode, std::optional<PCB_LAYER_ID> aLayerId )
624{
625 PCB_LAYER_ID actualLayerId = ( aLayerId.has_value() ) ? aLayerId.value() : m_layer;
626
627 switch( aMode )
628 {
631 {
632 if( m_layersToExport.empty() )
633 {
634 COLOR4D layerColor = ( aMode == DXF_LAYER_OUTPUT_MODE::Current_Layer_Name )
636 : RenderSettings()->GetLayerColor( actualLayerId );
637
638 int color = FindNearestLegacyColor(
639 int( layerColor.r * 255 ), int( layerColor.g * 255 ), int( layerColor.b * 255 ) );
640 return wxString( acad_dxf_color_names[color].name );
641 }
642
643 auto it = std::find_if( m_layersToExport.begin(), m_layersToExport.end(),
644 [actualLayerId]( const std::pair<int, wxString>& element )
645 {
646 return element.first == actualLayerId;
647 } );
648
649 return ( it != m_layersToExport.end() ) ? it->second : wxString( "BLACK" );
650 }
651
654 {
657 : RenderSettings()->GetLayerColor( actualLayerId );
658
659 int color = FindNearestLegacyColor(
660 int( layerColor.r * 255 ), int( layerColor.g * 255 ), int( layerColor.b * 255 ) );
661 wxString cname( acad_dxf_color_names[color].name );
662 return cname;
663 }
664
665 default: return wxString( "Unknown Mode" );
666 }
667}
668
669
671{
672 m_plotUnits = aUnit;
673
674 switch( aUnit )
675 {
676 case DXF_UNITS::MM:
677 m_unitScalingFactor = 0.00254;
679 m_insUnits = 4;
680 break;
681
682 case DXF_UNITS::INCH:
683 default:
684 m_unitScalingFactor = 0.0001;
686 m_insUnits = 1;
687 }
688}
689
690
691// convert aValue to a string, and remove trailing zeros
692// In DXF files coordinates need a high precision: at least 9 digits when given
693// in inches and 7 digits when in mm.
694// So we use 16 digits and remove trailing 0 (if any)
695static std::string formatCoord( double aValue )
696{
697 std::string buf;
698
699 buf = fmt::format( "{:.16f}", aValue );
700
701 // remove trailing zeros
702 while( !buf.empty() && buf[buf.size() - 1] == '0' )
703 {
704 buf.pop_back();
705 }
706
707 return buf;
708}
709
710
711void DXF_PLOTTER::SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil,
712 double aScale, bool aMirror )
713{
714 m_plotOffset = aOffset;
715 m_plotScale = aScale;
716
717 /* DXF paper is 'virtual' so there is no need of a paper size.
718 Also this way we can handle the aux origin which can be useful
719 (for example when aligning to a mechanical drawing) */
720 m_paperSize.x = 0;
721 m_paperSize.y = 0;
722
723 /* Like paper size DXF units are abstract too. Anyway there is a
724 * system variable (MEASUREMENT) which will be set to 0 to indicate
725 * english units */
726 m_IUsPerDecimil = aIusPerDecimil;
727 m_iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils
728 m_iuPerDeviceUnit *= GetUnitScaling(); // Get the scaling factor for the current units
729
730 m_plotMirror = false; // No mirroring on DXF
732}
733
734
736{
737 return fmt::format( "{:X}", ++m_handle );
738}
739
740
741std::string DXF_PLOTTER::emitEntityHandle( const char* aEntityType, const char* aSubclass,
742 const std::string& aLayerName,
743 const std::string& aOwner )
744{
745 std::string handle = nextHandle();
746 const std::string& owner = aOwner.empty() ? m_modelSpaceHandle : aOwner;
747
748 fmt::print( m_outputFile,
749 " 0\n{}\n"
750 " 5\n{}\n"
751 "330\n{}\n"
752 "100\nAcDbEntity\n"
753 " 8\n{}\n",
754 aEntityType,
755 handle,
756 owner,
757 aLayerName );
758
759 if( aSubclass )
760 fmt::print( m_outputFile, "100\n{}\n", aSubclass );
761
762 return handle;
763}
764
765
766std::string DXF_PLOTTER::emitSymbolTableHeader( const char* aTableName, int aCount )
767{
768 std::string handle = nextHandle();
769
770 fmt::print( m_outputFile,
771 " 0\n"
772 "TABLE\n"
773 " 2\n{}\n"
774 " 5\n{}\n"
775 "330\n0\n"
776 "100\nAcDbSymbolTable\n"
777 " 70\n{}\n",
778 aTableName,
779 handle,
780 aCount );
781
782 return handle;
783}
784
785
786bool DXF_PLOTTER::StartPlot( const wxString& aPageNumber )
787{
788 wxASSERT( m_outputFile );
789
790 // Reset state so a single DXF_PLOTTER instance can be reused for multiple plots.
791 m_handle = 0;
792 m_modelSpaceHandle.clear();
793
794 // Tagged AC1018 (R2004) because the LAYER table emits the 420 true-color group,
795 // which AutoCAD only accepts in R2004+. That in turn requires handles on every
796 // record and entity plus a full R2000 table and blocks skeleton.
797 fmt::print( m_outputFile,
798 " 0\n"
799 "SECTION\n"
800 " 2\n"
801 "HEADER\n"
802 " 9\n"
803 "$ACADVER\n"
804 " 1\n"
805 "AC1018\n"
806 " 9\n"
807 "$HANDSEED\n"
808 " 5\n"
809 "FFFFFFFF\n"
810 " 9\n"
811 "$ANGBASE\n"
812 " 50\n"
813 "0.0\n"
814 " 9\n"
815 "$ANGDIR\n"
816 " 70\n"
817 "1\n"
818 " 9\n"
819 "$MEASUREMENT\n"
820 " 70\n"
821 "{}\n"
822 " 9\n"
823 "$INSUNITS\n"
824 " 70\n"
825 "{}\n"
826 " 0\n"
827 "ENDSEC\n",
829
830 fmt::print( m_outputFile,
831 " 0\n"
832 "SECTION\n"
833 " 2\n"
834 "TABLES\n" );
835
836 // AutoCAD refuses to load R2000+ files lacking an ACAD APPID entry.
837 std::string appidTableHandle = emitSymbolTableHeader( "APPID", 1 );
838
839 fmt::print( m_outputFile,
840 " 0\n"
841 "APPID\n"
842 " 5\n{}\n"
843 "330\n{}\n"
844 "100\nAcDbSymbolTableRecord\n"
845 "100\nAcDbRegAppTableRecord\n"
846 " 2\nACAD\n"
847 " 70\n0\n"
848 " 0\n"
849 "ENDTAB\n",
850 nextHandle(), appidTableHandle );
851
852 // CONTINUOUS, DASHDOT, DASHED and DOTTED cover every LINE_STYLE we emit via the
853 // 6/<name> group on LINE entities. The 49 group is the per-element dash length
854 // (positive) or gap length (negative); each 49 must be followed by a 74 (complex
855 // linetype element type, 0 = plain) in R2000+ files.
856 struct LtypePattern
857 {
858 const char* name;
859 const char* description;
860 int elementCount;
861 double patternLength;
862 const char* dashes;
863 };
864
865 // AutoCAD looks up ByBlock and ByLayer by name and aborts when either is absent.
866 // Both are zero-element solid patterns; the actual appearance is inherited from
867 // the owning block or layer at render time.
868 static const LtypePattern ltypes[] = {
869 { "ByBlock", "", 0, 0.0, "" },
870 { "ByLayer", "", 0, 0.0, "" },
871 { "CONTINUOUS", "Solid line", 0, 0.0, "" },
872 { "DASHDOT", "Dash Dot ____ _ ____ _", 4, 2.0, " 49\n1.25\n 74\n0\n 49\n-0.25\n 74\n0\n"
873 " 49\n0.25\n 74\n0\n 49\n-0.25\n 74\n0\n" },
874 { "DASHED", "Dashed __ __ __ __ __", 2, 0.75, " 49\n0.50\n 74\n0\n 49\n-0.25\n 74\n0\n" },
875 { "DOTTED", "Dotted . . . .", 2, 0.2, " 49\n0.00\n 74\n0\n 49\n-0.20\n 74\n0\n" },
876 { "DIVIDE", "Divide ____ _ _ ____", 6, 2.5, " 49\n1.25\n 74\n0\n 49\n-0.25\n 74\n0\n"
877 " 49\n0.25\n 74\n0\n 49\n-0.25\n 74\n0\n"
878 " 49\n0.25\n 74\n0\n 49\n-0.25\n 74\n0\n" },
879 };
880
881 std::string ltypeTableHandle = emitSymbolTableHeader( "LTYPE", static_cast<int>( std::size( ltypes ) ) );
882
883 for( const LtypePattern& lt : ltypes )
884 {
885 fmt::print( m_outputFile,
886 " 0\n"
887 "LTYPE\n"
888 " 5\n{}\n"
889 "330\n{}\n"
890 "100\nAcDbSymbolTableRecord\n"
891 "100\nAcDbLinetypeTableRecord\n"
892 " 2\n{}\n"
893 " 70\n0\n"
894 " 3\n{}\n"
895 " 72\n65\n"
896 " 73\n{}\n"
897 " 40\n{}\n"
898 "{}",
899 nextHandle(),
900 ltypeTableHandle,
901 lt.name, lt.description, lt.elementCount, lt.patternLength, lt.dashes );
902 }
903
904 fmt::print( m_outputFile,
905 " 0\n"
906 "ENDTAB\n" );
907
908 // STYLE table - one entry per bold/italic combination.
909 std::string styleTableHandle = emitSymbolTableHeader( "STYLE", 4 );
910
911 static const char* style_name[4] = { "KICAD", "KICADB", "KICADI", "KICADBI" };
912
913 for( int i = 0; i < 4; i++ )
914 {
915 fmt::print( m_outputFile,
916 " 0\n"
917 "STYLE\n"
918 " 5\n{}\n"
919 "330\n{}\n"
920 "100\nAcDbSymbolTableRecord\n"
921 "100\nAcDbTextStyleTableRecord\n"
922 " 2\n{}\n"
923 " 70\n0\n"
924 " 40\n0\n"
925 " 41\n1\n"
926 " 42\n1\n"
927 " 50\n{:g}\n"
928 " 71\n0\n"
929 // The standard ISO font (when kicad is built with it the dxf text in
930 // acad matches *perfectly*)
931 " 3\nisocp.shx\n",
932 nextHandle(),
933 styleTableHandle,
934 style_name[i],
935 i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
936 }
937
938 fmt::print( m_outputFile,
939 " 0\n"
940 "ENDTAB\n" );
941
942 int numLayers = m_layersToExport.empty() ? static_cast<int>( DXF_COLOR_T::NBCOLORS )
943 : static_cast<int>( m_layersToExport.size() );
944
945 // If printing in monochrome, only output the black layer
946 if( !GetColorMode() && m_layersToExport.empty() )
947 numLayers = 1;
948
949 // Every LAYER record must carry a 390 hard-pointer to a PlotStyleName object.
950 // We share one ACDBPLACEHOLDER named "Normal" across every layer; allocate its
951 // handle (and that of its owning dictionary) now so the LAYER records can cite it.
952 // The +1 on the count is the default layer "0" emitted next.
953 std::string layerTableHandle = emitSymbolTableHeader( "LAYER", numLayers + 1 );
954
957
958 // Default layer "0" is required by name in the LAYER table. Entities may
959 // reference undeclared layers (spec page 230) but the "0" entry itself must
960 // exist. Color 7 / CONTINUOUS are the conventional defaults.
961 fmt::print( m_outputFile,
962 " 0\n"
963 "LAYER\n"
964 " 5\n{}\n"
965 "330\n{}\n"
966 "100\nAcDbSymbolTableRecord\n"
967 "100\nAcDbLayerTableRecord\n"
968 " 2\n0\n"
969 " 70\n0\n"
970 " 62\n7\n"
971 " 6\nCONTINUOUS\n"
972 "390\n{}\n",
973 nextHandle(), layerTableHandle, m_plotStyleNormalHandle );
974
975 /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
976
977 - The primary colors (1 - 9)
978 - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
979 - Greys (251 - 255)
980 */
981
982 wxString layerName;
983 int colorNumber;
984
985 bool hasActualColor = false;
986 COLOR4D actualColor;
987
988 for( int i = 0; i < numLayers; i++ )
989 {
990 if( !m_layersToExport.empty() )
991 {
994 m_layersToExport.at( i ).first );
995
996 auto it = std::find_if( std::begin( acad_dxf_color_names ), std::end( acad_dxf_color_names ),
997 [colorName](const auto& layer)
998 {
999 return std::strcmp( layer.name, colorName.ToStdString().c_str() ) == 0;
1000 });
1001
1002 if( it != std::end( acad_dxf_color_names ) )
1003 colorNumber = it->index;
1004 else
1005 colorNumber = 7; // Default to white/black
1006
1007 actualColor = RenderSettings()->GetLayerColor( m_layersToExport.at( i ).first );
1008 hasActualColor = true;
1009 }
1010 else
1011 {
1012 layerName = wxString( acad_dxf_color_names[i].name );
1014 }
1015
1016 fmt::print( m_outputFile,
1017 " 0\n"
1018 "LAYER\n"
1019 " 5\n{}\n"
1020 "330\n{}\n"
1021 "100\nAcDbSymbolTableRecord\n"
1022 "100\nAcDbLayerTableRecord\n"
1023 " 2\n{}\n"
1024 " 70\n0\n"
1025 " 62\n{}\n",
1026 nextHandle(),
1027 layerTableHandle,
1028 TO_UTF8( layerName ),
1029 colorNumber );
1030
1031 if( hasActualColor )
1032 {
1033 // Group 420 carries the 24-bit true color introduced in R2004; the legacy
1034 // 62 color index alone would otherwise lose the user-chosen layer colour.
1035 int r = static_cast<int>( actualColor.r * 255 );
1036 int g = static_cast<int>( actualColor.g * 255 );
1037 int b = static_cast<int>( actualColor.b * 255 );
1038
1039 int trueColorValue = ( r << 16 ) | ( g << 8 ) | b;
1040
1041 fmt::print( m_outputFile, "420\n{}\n", trueColorValue );
1042 }
1043
1044 // 6 (linetype) and 390 (plot style) are mandatory on every R2000+ LAYER.
1045 fmt::print( m_outputFile,
1046 " 6\nCONTINUOUS\n"
1047 "390\n{}\n",
1049 }
1050
1051 fmt::print( m_outputFile,
1052 " 0\n"
1053 "ENDTAB\n" );
1054
1055 // AutoCAD's R2000+ reader walks a fixed list of expected symbol tables and aborts
1056 // when one is absent. Empty headers satisfy the parser. Spec page 35 only
1057 // constrains table order via LTYPE preceding LAYER, which is already true above.
1058 for( const char* tableName : { "VPORT", "VIEW", "UCS" } )
1059 {
1060 emitSymbolTableHeader( tableName, 0 );
1061 fmt::print( m_outputFile, " 0\nENDTAB\n" );
1062 }
1063
1064 // DIMSTYLE is the only symbol table whose record handle uses group code 105
1065 // instead of 5, and whose header carries an extra AcDbDimStyleTable subclass
1066 // marker (spec page 35). AutoCAD requires the Standard entry.
1067 std::string dimstyleTableHandle = emitSymbolTableHeader( "DIMSTYLE", 1 );
1068
1069 fmt::print( m_outputFile,
1070 "100\nAcDbDimStyleTable\n"
1071 " 71\n0\n"
1072 " 0\n"
1073 "DIMSTYLE\n"
1074 "105\n{}\n"
1075 "330\n{}\n"
1076 "100\nAcDbSymbolTableRecord\n"
1077 "100\nAcDbDimStyleTableRecord\n"
1078 " 2\nStandard\n"
1079 " 70\n0\n"
1080 " 0\n"
1081 "ENDTAB\n",
1082 nextHandle(),
1083 dimstyleTableHandle );
1084
1085 // R2004 mandates three empty layout blocks (*Model_Space, *Paper_Space,
1086 // *Paper_Space0); each BLOCK_RECORD entry carries a 340 hard-pointer to its
1087 // associated LAYOUT object. Pre-allocate the LAYOUT handles now so EndPlot()
1088 // can emit the back-pointers when it writes OBJECTS.
1089 std::string blockRecordTableHandle = emitSymbolTableHeader( "BLOCK_RECORD", 3 );
1090
1092 std::string paperSpaceBR = nextHandle();
1093 std::string paperSpace0BR = nextHandle();
1094
1095 m_dxfLayouts.clear();
1096 m_dxfLayouts.reserve( 3 );
1097 m_dxfLayouts.push_back( { "Model", "*Model_Space", m_modelSpaceHandle, nextHandle(), false } );
1098 m_dxfLayouts.push_back( { "Layout1", "*Paper_Space", paperSpaceBR, nextHandle(), true } );
1099 m_dxfLayouts.push_back( { "Layout2", "*Paper_Space0", paperSpace0BR, nextHandle(), true } );
1100
1101 // The root Named Object Dictionary and ACAD_LAYOUT live in OBJECTS, but their
1102 // handles are referenced from LAYOUT objects via 330, so allocate them now.
1105
1106 for( const DxfLayout& l : m_dxfLayouts )
1107 {
1108 fmt::print( m_outputFile,
1109 " 0\n"
1110 "BLOCK_RECORD\n"
1111 " 5\n{}\n"
1112 "330\n{}\n"
1113 "100\nAcDbSymbolTableRecord\n"
1114 "100\nAcDbBlockTableRecord\n"
1115 " 2\n{}\n"
1116 "340\n{}\n"
1117 " 70\n0\n"
1118 "280\n1\n"
1119 "281\n0\n",
1120 l.blockRecordHandle,
1121 blockRecordTableHandle,
1122 l.blockName,
1123 l.layoutHandle );
1124 }
1125
1126 fmt::print( m_outputFile,
1127 " 0\n"
1128 "ENDTAB\n"
1129 " 0\n"
1130 "ENDSEC\n" );
1131
1132 // Three empty BLOCK/ENDBLK pairs back the three BLOCK_RECORD entries. Paperspace
1133 // blocks carry the 67/1 paperspace flag inside AcDbEntity.
1134 fmt::print( m_outputFile,
1135 " 0\n"
1136 "SECTION\n"
1137 " 2\n"
1138 "BLOCKS\n" );
1139
1140 for( const DxfLayout& l : m_dxfLayouts )
1141 {
1142 fmt::print( m_outputFile,
1143 " 0\n"
1144 "BLOCK\n"
1145 " 5\n{}\n"
1146 "330\n{}\n"
1147 "100\nAcDbEntity\n"
1148 "{}"
1149 " 8\n0\n"
1150 "100\nAcDbBlockBegin\n"
1151 " 2\n{}\n"
1152 " 70\n0\n"
1153 " 10\n0.0\n 20\n0.0\n 30\n0.0\n"
1154 " 3\n{}\n"
1155 " 1\n\n"
1156 " 0\n"
1157 "ENDBLK\n"
1158 " 5\n{}\n"
1159 "330\n{}\n"
1160 "100\nAcDbEntity\n"
1161 "{}"
1162 " 8\n0\n"
1163 "100\nAcDbBlockEnd\n",
1164 nextHandle(),
1165 l.blockRecordHandle,
1166 l.isPaperSpace ? " 67\n1\n" : "",
1167 l.blockName,
1168 l.blockName,
1169 nextHandle(),
1170 l.blockRecordHandle,
1171 l.isPaperSpace ? " 67\n1\n" : "" );
1172 }
1173
1174 fmt::print( m_outputFile,
1175 " 0\n"
1176 "ENDSEC\n" );
1177
1178 // Begin ENTITIES section
1179 fmt::print( m_outputFile,
1180 " 0\n"
1181 "SECTION\n"
1182 " 2\n"
1183 "ENTITIES\n" );
1184
1185 return true;
1186}
1187
1188
1190{
1191 // Root Named Object Dictionary. 281/1 marks dict elements as hard-owned, matching
1192 // AutoCAD. The empty ACAD_GROUP dict is required by the named-object root, and
1193 // the ACAD_PLOTSTYLENAME dict resolves the 390 plot-style references on LAYER
1194 // records.
1195 std::string acadGroupDictHandle = nextHandle();
1196
1197 fmt::print( m_outputFile,
1198 " 0\n"
1199 "SECTION\n"
1200 " 2\n"
1201 "OBJECTS\n"
1202 " 0\n"
1203 "DICTIONARY\n"
1204 " 5\n{}\n"
1205 "330\n0\n"
1206 "100\nAcDbDictionary\n"
1207 "281\n1\n"
1208 " 3\nACAD_GROUP\n"
1209 "350\n{}\n"
1210 " 3\nACAD_LAYOUT\n"
1211 "350\n{}\n"
1212 " 3\nACAD_PLOTSTYLENAME\n"
1213 "350\n{}\n",
1215 acadGroupDictHandle,
1218
1219 // ACAD_GROUP - empty, but the named-object root requires it.
1220 fmt::print( m_outputFile,
1221 " 0\n"
1222 "DICTIONARY\n"
1223 " 5\n{}\n"
1224 "330\n{}\n"
1225 "100\nAcDbDictionary\n"
1226 "281\n1\n",
1227 acadGroupDictHandle,
1229
1230 // ACDBDICTIONARYWDFLT is a dictionary with a default entry; the 340 points to the
1231 // same Normal placeholder as the dict's "Normal" entry, and every LAYER's 390
1232 // resolves through here.
1233 fmt::print( m_outputFile,
1234 " 0\n"
1235 "ACDBDICTIONARYWDFLT\n"
1236 " 5\n{}\n"
1237 "330\n{}\n"
1238 "100\nAcDbDictionary\n"
1239 "281\n1\n"
1240 " 3\nNormal\n"
1241 "350\n{}\n"
1242 "100\nAcDbDictionaryWithDefault\n"
1243 "340\n{}\n",
1248
1249 // ACDBPLACEHOLDER carries no payload; it just gives the "Normal" plot style a
1250 // handle that LAYER's 390 can resolve.
1251 fmt::print( m_outputFile,
1252 " 0\n"
1253 "ACDBPLACEHOLDER\n"
1254 " 5\n{}\n"
1255 "330\n{}\n",
1258
1259 // ACAD_LAYOUT names every LAYOUT object emitted below.
1260 fmt::print( m_outputFile,
1261 " 0\n"
1262 "DICTIONARY\n"
1263 " 5\n{}\n"
1264 "330\n{}\n"
1265 "100\nAcDbDictionary\n"
1266 "281\n1\n",
1269
1270 for( const DxfLayout& l : m_dxfLayouts )
1271 {
1272 fmt::print( m_outputFile,
1273 " 3\n{}\n"
1274 "350\n{}\n",
1275 l.name,
1276 l.layoutHandle );
1277 }
1278
1279 // The 4/<name> and 44/45 (width/height) fields are correlated and must change
1280 // together. ±1e+20 in extmin/extmax is the AcDbLayout sentinel for uninitialised
1281 // extents.
1282 static constexpr const char* PAPER_NAME = "A3";
1283 static constexpr double PAPER_WIDTH_MM = 420.0;
1284 static constexpr double PAPER_HEIGHT_MM = 297.0;
1285 static constexpr int PLOT_FLAG_MODELTYPE = 1024;
1286
1287 // Field set and ordering match what ODA File Converter writes for R2004. The
1288 // reader is order-sensitive here and rejects deviations like a missing group 2,
1289 // ModelType set on a paperspace layout, or 147 appearing before 76/77/78.
1290 for( std::size_t i = 0; i < m_dxfLayouts.size(); ++i )
1291 {
1292 const DxfLayout& l = m_dxfLayouts[i];
1293 int plotLayoutFlag = l.isPaperSpace ? 0 : PLOT_FLAG_MODELTYPE;
1294
1295 fmt::print( m_outputFile,
1296 " 0\n"
1297 "LAYOUT\n"
1298 " 5\n{}\n"
1299 "330\n{}\n"
1300 "100\nAcDbPlotSettings\n"
1301 " 1\n\n"
1302 " 2\nnone_device\n"
1303 " 4\n{}\n"
1304 " 6\n\n"
1305 " 40\n0.0\n 41\n0.0\n 42\n0.0\n 43\n0.0\n"
1306 " 44\n{:.1f}\n 45\n{:.1f}\n 46\n0.0\n 47\n0.0\n"
1307 " 48\n0.0\n 49\n0.0\n"
1308 "140\n0.0\n141\n0.0\n142\n1.0\n143\n1.0\n"
1309 " 70\n{}\n"
1310 " 72\n1\n 73\n0\n 74\n5\n"
1311 " 7\n\n"
1312 " 75\n16\n"
1313 " 76\n0\n 77\n2\n 78\n300\n"
1314 "147\n1.0\n"
1315 "148\n0.0\n149\n0.0\n"
1316 "100\nAcDbLayout\n"
1317 " 1\n{}\n"
1318 " 70\n1\n"
1319 " 71\n{}\n"
1320 " 10\n0.0\n 20\n0.0\n"
1321 " 11\n{:.1f}\n 21\n{:.1f}\n"
1322 " 12\n0.0\n 22\n0.0\n 32\n0.0\n"
1323 " 14\n1e+20\n 24\n1e+20\n 34\n1e+20\n"
1324 " 15\n-1e+20\n 25\n-1e+20\n 35\n-1e+20\n"
1325 "146\n0.0\n"
1326 " 13\n0.0\n 23\n0.0\n 33\n0.0\n"
1327 " 16\n1.0\n 26\n0.0\n 36\n0.0\n"
1328 " 17\n0.0\n 27\n1.0\n 37\n0.0\n"
1329 " 76\n0\n"
1330 "330\n{}\n",
1331 l.layoutHandle,
1333 PAPER_NAME, PAPER_WIDTH_MM, PAPER_HEIGHT_MM,
1334 plotLayoutFlag,
1335 l.name,
1336 static_cast<int>( i ),
1337 PAPER_WIDTH_MM, PAPER_HEIGHT_MM,
1339 }
1340
1341 fmt::print( m_outputFile,
1342 " 0\n"
1343 "ENDSEC\n" );
1344}
1345
1346
1348{
1349 wxASSERT( m_outputFile );
1350
1351 fmt::print( m_outputFile,
1352 " 0\n"
1353 "ENDSEC\n" );
1354
1356
1357 fmt::print( m_outputFile,
1358 " 0\n"
1359 "EOF\n" );
1360 fclose( m_outputFile );
1361 m_outputFile = nullptr;
1362
1363 return true;
1364}
1365
1366
1367void DXF_PLOTTER::SetColor( const COLOR4D& color )
1368{
1369 if( ( m_colorMode )
1370 || ( color == COLOR4D::BLACK )
1371 || ( color == COLOR4D::WHITE ) )
1372 {
1373 m_currentColor = color;
1374 }
1375 else
1376 {
1378 }
1379}
1380
1381
1382void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width, int aCornerRadius )
1383{
1384 wxASSERT( m_outputFile );
1385
1386 if( aCornerRadius > 0 )
1387 {
1388 BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
1389 box.Normalize();
1390 SHAPE_RECT rect( box );
1391 rect.SetRadius( aCornerRadius );
1392 PlotPoly( rect.Outline(), fill, width, nullptr );
1393 return;
1394 }
1395
1396 if( p1 != p2 )
1397 {
1398 MoveTo( p1 );
1399 LineTo( VECTOR2I( p1.x, p2.y ) );
1400 LineTo( VECTOR2I( p2.x, p2.y ) );
1401 LineTo( VECTOR2I( p2.x, p1.y ) );
1402 FinishTo( VECTOR2I( p1.x, p1.y ) );
1403 }
1404 else
1405 {
1406 // Draw as a point
1408
1409 VECTOR2D point_dev = userToDeviceCoordinates( p1 );
1410
1411 emitEntityHandle( "POINT", "AcDbPoint", TO_UTF8( cLayerName ) );
1412 fmt::print( m_outputFile, " 10\n{}\n 20\n{}\n 30\n0\n",
1413 formatCoord( point_dev.x ),
1414 formatCoord( point_dev.y ) );
1415 }
1416}
1417
1418
1419void DXF_PLOTTER::Circle( const VECTOR2I& centre, int diameter, FILL_T fill, int width )
1420{
1421 wxASSERT( m_outputFile );
1422 double radius = userToDeviceSize( diameter / 2.0 );
1423 VECTOR2D centre_dev = userToDeviceCoordinates( centre );
1424
1426 std::string layer = TO_UTF8( cLayerName );
1427 const char* lineStyleName = getDXFLineType( static_cast<LINE_STYLE>( m_currentLineType ) );
1428
1429 if( radius > 0 )
1430 {
1431 if( fill == FILL_T::NO_FILL )
1432 {
1433 emitEntityHandle( "CIRCLE", "AcDbCircle", layer );
1434 fmt::print( m_outputFile, " 10\n{}\n 20\n{}\n 30\n0\n 40\n{}\n 6\n{}\n",
1435 formatCoord( centre_dev.x ),
1436 formatCoord( centre_dev.y ),
1438 lineStyleName );
1439 }
1440 else if( fill == FILL_T::FILLED_SHAPE )
1441 {
1442 double r = radius * 0.5;
1443
1444 // 10/20/30 is the 2D polyline elevation dummy point; AutoCAD rejects the
1445 // entity when it's missing. Owner of the VERTEX records and the terminating
1446 // SEQEND is the POLYLINE handle, not *Model_Space.
1447 std::string polyHandle = emitEntityHandle( "POLYLINE", "AcDb2dPolyline", layer );
1448 std::string rStr = formatCoord( radius );
1449
1450 fmt::print( m_outputFile,
1451 " 66\n1\n"
1452 " 10\n0\n 20\n0\n 30\n0\n"
1453 " 70\n1\n 40\n{}\n 41\n{}\n",
1454 rStr, rStr );
1455
1456 for( double offset : { -r, r } )
1457 {
1458 emitEntityHandle( "VERTEX", "AcDbVertex", layer, polyHandle );
1459 fmt::print( m_outputFile,
1460 "100\nAcDb2dVertex\n"
1461 " 10\n{}\n 20\n{}\n 30\n0\n 42\n1.0\n",
1462 formatCoord( centre_dev.x + offset ),
1463 formatCoord( centre_dev.y ) );
1464 }
1465
1466 emitEntityHandle( "SEQEND", nullptr, layer, polyHandle );
1467 }
1468 }
1469 else
1470 {
1471 // Draw as a point
1472 emitEntityHandle( "POINT", "AcDbPoint", layer );
1473 fmt::print( m_outputFile, " 10\n{}\n 20\n{}\n 30\n0\n",
1474 formatCoord( centre_dev.x ),
1475 formatCoord( centre_dev.y ) );
1476 }
1477}
1478
1479
1480void DXF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth, void* aData )
1481{
1482 if( aCornerList.size() <= 1 )
1483 return;
1484
1485 unsigned last = aCornerList.size() - 1;
1486
1487 // Plot outlines with lines (thickness = 0) to define the polygon
1488 if( aWidth <= 0 )
1489 {
1490 MoveTo( aCornerList[0] );
1491
1492 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1493 LineTo( aCornerList[ii] );
1494
1495 // Close polygon if 'fill' requested
1496 if( aFill != FILL_T::NO_FILL )
1497 {
1498 if( aCornerList[last] != aCornerList[0] )
1499 LineTo( aCornerList[0] );
1500 }
1501
1502 PenFinish();
1503
1504 return;
1505 }
1506 // If the polygon outline has thickness, and is not filled (i.e. is a polyline) plot outlines
1507 // with thick segments
1508 else if( aFill == FILL_T::NO_FILL )
1509 {
1510 MoveTo( aCornerList[0] );
1511
1512 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1513 ThickSegment( aCornerList[ii-1], aCornerList[ii], aWidth, aData );
1514
1515 return;
1516 }
1517
1518 // The polygon outline has thickness, and is filled
1519 // Build and plot the polygon which contains the initial polygon and its thick outline
1520 SHAPE_POLY_SET bufferOutline;
1521 SHAPE_POLY_SET bufferPolybase;
1522
1523 bufferPolybase.NewOutline();
1524
1525 // enter outline as polygon:
1526 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1527 {
1528 TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
1530 }
1531
1532 // enter the initial polygon:
1533 for( const VECTOR2I& corner : aCornerList )
1534 bufferPolybase.Append( corner );
1535
1536 // Merge polygons to build the polygon which contains the initial polygon and its thick outline
1537
1538 // create the outline which contains thick outline:
1539 bufferPolybase.BooleanAdd( bufferOutline );
1540 bufferPolybase.Fracture();
1541
1542 if( bufferPolybase.OutlineCount() < 1 ) // should not happen
1543 return;
1544
1545 const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
1546
1547 if( path.PointCount() < 2 ) // should not happen
1548 return;
1549
1550 // Now, output the final polygon to DXF file:
1551 last = path.PointCount() - 1;
1552 VECTOR2I point = path.CPoint( 0 );
1553
1554 VECTOR2I startPoint( point.x, point.y );
1555 MoveTo( startPoint );
1556
1557 for( int ii = 1; ii < path.PointCount(); ii++ )
1558 {
1559 point = path.CPoint( ii );
1560 LineTo( VECTOR2I( point.x, point.y ) );
1561 }
1562
1563 // Close polygon, if needed
1564 point = path.CPoint( last );
1565 VECTOR2I endPoint( point.x, point.y );
1566
1567 if( endPoint != startPoint )
1568 LineTo( startPoint );
1569
1570 PenFinish();
1571}
1572
1573
1574void DXF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aLineChain, FILL_T aFill, int aWidth, void* aData )
1575{
1576 if( aLineChain.PointCount() == 0 )
1577 return;
1578
1579 std::vector<VECTOR2I> cornerList;
1580 cornerList.reserve( aLineChain.PointCount() );
1581
1582 for( int ii = 0; ii < aLineChain.PointCount(); ++ii )
1583 cornerList.emplace_back( aLineChain.CPoint( ii ) );
1584
1585 if( aLineChain.IsClosed() && cornerList.front() != cornerList.back() )
1586 cornerList.emplace_back( aLineChain.CPoint( 0 ) );
1587
1588 PlotPoly( cornerList, aFill, aWidth, aData );
1589}
1590
1591
1592void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
1593{
1594 wxASSERT( m_outputFile );
1595
1596 if( plume == 'Z' )
1597 return;
1598
1599 VECTOR2D pos_dev = userToDeviceCoordinates( pos );
1600 VECTOR2D pen_lastpos_dev = userToDeviceCoordinates( m_penLastpos );
1601
1602 if( m_penLastpos != pos && plume == 'D' )
1603 {
1605
1606 // DXF LINE
1608 std::string layer = TO_UTF8( cLayerName );
1609 const char* lineStyleName = getDXFLineType( static_cast<LINE_STYLE>( m_currentLineType ) );
1610
1611 // The linetype name (6) sits on the AcDbEntity side, before the AcDbLine
1612 // marker. Emitted inline so group 6 can interleave between the two markers.
1613 fmt::print( m_outputFile,
1614 " 0\nLINE\n"
1615 " 5\n{}\n"
1616 "330\n{}\n"
1617 "100\nAcDbEntity\n"
1618 " 8\n{}\n"
1619 " 6\n{}\n"
1620 "100\nAcDbLine\n"
1621 " 10\n{}\n 20\n{}\n 30\n0\n 11\n{}\n 21\n{}\n 31\n0\n",
1622 nextHandle(),
1624 layer,
1625 lineStyleName,
1626 formatCoord( pen_lastpos_dev.x ),
1627 formatCoord( pen_lastpos_dev.y ),
1628 formatCoord( pos_dev.x ),
1629 formatCoord( pos_dev.y ) );
1630 }
1631
1632 m_penLastpos = pos;
1633}
1634
1635
1636void DXF_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
1637{
1638 wxASSERT( aLineStyle >= LINE_STYLE::FIRST_TYPE && aLineStyle <= LINE_STYLE::LAST_TYPE );
1639
1640 m_currentLineType = aLineStyle;
1641}
1642
1643
1644void DXF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
1645 const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
1646{
1647 wxASSERT( m_outputFile );
1648
1649 if( aRadius <= 0 )
1650 return;
1651
1652 EDA_ANGLE startAngle = -aStartAngle;
1653 EDA_ANGLE endAngle = startAngle - aAngle;
1654
1655 // In DXF, arcs are drawn CCW.
1656 // If startAngle > endAngle, it is CW. So transform it to CCW
1657 if( endAngle < startAngle )
1658 std::swap( startAngle, endAngle );
1659
1660 VECTOR2D centre_device = userToDeviceCoordinates( aCenter );
1661 double radius_device = userToDeviceSize( aRadius );
1662
1663 // ARC carries two subclass markers, AcDbCircle (centre and radius) then AcDbArc
1664 // (angle pair). Reversing them trips AutoCAD's AcDb validator.
1666 std::string layer = TO_UTF8( cLayerName );
1667 const char* lineStyleName = getDXFLineType( static_cast<LINE_STYLE>( m_currentLineType ) );
1668
1669 emitEntityHandle( "ARC", "AcDbCircle", layer );
1670 fmt::print( m_outputFile,
1671 " 10\n{}\n 20\n{}\n 30\n0\n 40\n{}\n"
1672 "100\nAcDbArc\n"
1673 " 50\n{:.8f}\n 51\n{:.8f}\n"
1674 " 6\n{}\n",
1675 formatCoord( centre_device.x ),
1676 formatCoord( centre_device.y ),
1677 formatCoord( radius_device ),
1678 startAngle.AsDegrees(),
1679 endAngle.AsDegrees(),
1680 lineStyleName );
1681}
1682
1683
1684void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth, void* aData )
1685{
1686 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1687
1688 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1689 {
1690 std::vector<VECTOR2I> cornerList;
1691 SHAPE_POLY_SET outlineBuffer;
1692 TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(), ERROR_INSIDE );
1693 const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );
1694
1695 cornerList.reserve( path.PointCount() );
1696
1697 for( int jj = 0; jj < path.PointCount(); jj++ )
1698 cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );
1699
1700 // Ensure the polygon is closed
1701 if( cornerList[0] != cornerList[cornerList.size() - 1] )
1702 cornerList.push_back( cornerList[0] );
1703
1704 PlotPoly( cornerList, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1705 }
1706 else
1707 {
1708 MoveTo( aStart );
1709 FinishTo( aEnd );
1710 }
1711}
1712
1713
1714void DXF_PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle,
1715 double aRadius, int aWidth, void* aData )
1716{
1717 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1718
1719 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1720 {
1721 Arc( centre, aStartAngle, aAngle, aRadius - aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1722 Arc( centre, aStartAngle, aAngle, aRadius + aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1723 }
1724 else
1725 {
1726 Arc( centre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1727 }
1728}
1729
1730
1731void DXF_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1732{
1733 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1734
1735 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1736 {
1737 VECTOR2I offsetp1( p1.x - width/2, p1.y - width/2 );
1738 VECTOR2I offsetp2( p2.x + width/2, p2.y + width/2 );
1739 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1740
1741 offsetp1.x += width;
1742 offsetp1.y += width;
1743 offsetp2.x -= width;
1744 offsetp2.y -= width;
1745 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1746 }
1747 else
1748 {
1749 Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1750 }
1751}
1752
1753
1754void DXF_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1755{
1756 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1757
1758 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1759 {
1760 Circle( pos, diametre - width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1761 Circle( pos, diametre + width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1762 }
1763 else
1764 {
1765 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1766 }
1767}
1768
1769
1770void DXF_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1771{
1772 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1773
1774 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1775 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1776 else
1777 Circle( pos, diametre, FILL_T::FILLED_SHAPE, 0 );
1778}
1779
1780
1781void DXF_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1782{
1783 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1784
1785 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1786 {
1787 SHAPE_POLY_SET outline = aPoly.CloneDropTriangulation();
1790
1791 outline = aPoly.CloneDropTriangulation();
1794 }
1795 else
1796 {
1797 PLOTTER::PlotPoly( aPoly.COutline( 0 ), FILL_T::NO_FILL, aWidth, aData );
1798 }
1799}
1800
1801
1802void DXF_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient, void* aData )
1803{
1804 wxASSERT( m_outputFile );
1805
1806 VECTOR2I size( aSize );
1807 EDA_ANGLE orient( aOrient );
1808
1809 /* The chip is reduced to an oval tablet with size.y > size.x
1810 * (Oval vertical orientation 0) */
1811 if( size.x > size.y )
1812 {
1813 std::swap( size.x, size.y );
1814 orient += ANGLE_90;
1815 }
1816
1817 ThickOval( aPos, size, orient, DXF_LINE_WIDTH, aData );
1818}
1819
1820
1821void DXF_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1822{
1823 wxASSERT( m_outputFile );
1824 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1825}
1826
1827
1828void DXF_PLOTTER::FlashPadRect( const VECTOR2I& aPos, const VECTOR2I& aPadSize, const EDA_ANGLE& aOrient,
1829 void* aData )
1830{
1831 wxASSERT( m_outputFile );
1832
1833 VECTOR2I size, start, end;
1834
1835 size.x = aPadSize.x / 2;
1836 size.y = aPadSize.y / 2;
1837
1838 if( size.x < 0 )
1839 size.x = 0;
1840
1841 if( size.y < 0 )
1842 size.y = 0;
1843
1844 // If a dimension is zero, the trace is reduced to 1 line
1845 if( size.x == 0 )
1846 {
1847 start = VECTOR2I( aPos.x, aPos.y - size.y );
1848 end = VECTOR2I( aPos.x, aPos.y + size.y );
1849 RotatePoint( start, aPos, aOrient );
1850 RotatePoint( end, aPos, aOrient );
1851 MoveTo( start );
1852 FinishTo( end );
1853 return;
1854 }
1855
1856 if( size.y == 0 )
1857 {
1858 start = VECTOR2I( aPos.x - size.x, aPos.y );
1859 end = VECTOR2I( aPos.x + size.x, aPos.y );
1860 RotatePoint( start, aPos, aOrient );
1861 RotatePoint( end, aPos, aOrient );
1862 MoveTo( start );
1863 FinishTo( end );
1864 return;
1865 }
1866
1867 start = VECTOR2I( aPos.x - size.x, aPos.y - size.y );
1868 RotatePoint( start, aPos, aOrient );
1869 MoveTo( start );
1870
1871 end = VECTOR2I( aPos.x - size.x, aPos.y + size.y );
1872 RotatePoint( end, aPos, aOrient );
1873 LineTo( end );
1874
1875 end = VECTOR2I( aPos.x + size.x, aPos.y + size.y );
1876 RotatePoint( end, aPos, aOrient );
1877 LineTo( end );
1878
1879 end = VECTOR2I( aPos.x + size.x, aPos.y - size.y );
1880 RotatePoint( end, aPos, aOrient );
1881 LineTo( end );
1882
1883 FinishTo( start );
1884}
1885
1886
1887void DXF_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize, int aCornerRadius,
1888 const EDA_ANGLE& aOrient, void* aData )
1889{
1890 SHAPE_POLY_SET outline;
1891 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0, 0, 0,
1893
1894 // TransformRoundRectToPolygon creates only one convex polygon
1895 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1896
1897 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1898
1899 for( int ii = 1; ii < poly.PointCount(); ++ii )
1900 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1901
1902 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1903}
1904
1905
1906void DXF_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient,
1907 SHAPE_POLY_SET* aPolygons, void* aData )
1908{
1909 for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
1910 {
1911 SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
1912
1913 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1914
1915 for( int ii = 1; ii < poly.PointCount(); ++ii )
1916 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1917
1918 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1919 }
1920}
1921
1922
1923void DXF_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners, const EDA_ANGLE& aPadOrient,
1924 void* aData )
1925{
1926 wxASSERT( m_outputFile );
1927 VECTOR2I coord[4]; /* coord actual corners of a trapezoidal trace */
1928
1929 for( int ii = 0; ii < 4; ii++ )
1930 {
1931 coord[ii] = aCorners[ii];
1932 RotatePoint( coord[ii], aPadOrient );
1933 coord[ii] += aPadPos;
1934 }
1935
1936 // Plot edge:
1937 MoveTo( coord[0] );
1938 LineTo( coord[1] );
1939 LineTo( coord[2] );
1940 LineTo( coord[3] );
1941 FinishTo( coord[0] );
1942}
1943
1944
1945void DXF_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aRadius, int aCornerCount,
1946 const EDA_ANGLE& aOrient, void* aData )
1947{
1948 // Do nothing
1949 wxASSERT( 0 );
1950}
1951
1952
1960bool containsNonAsciiChars( const wxString& string )
1961{
1962 for( unsigned i = 0; i < string.length(); i++ )
1963 {
1964 wchar_t ch = string[i];
1965
1966 if( ch > 255 )
1967 return true;
1968 }
1969 return false;
1970}
1971
1972
1973void DXF_PLOTTER::Text( const VECTOR2I& aPos,
1974 const COLOR4D& aColor,
1975 const wxString& aText,
1976 const EDA_ANGLE& aOrient,
1977 const VECTOR2I& aSize,
1978 enum GR_TEXT_H_ALIGN_T aH_justify,
1979 enum GR_TEXT_V_ALIGN_T aV_justify,
1980 int aWidth,
1981 bool aItalic,
1982 bool aBold,
1983 bool aMultilineAllowed,
1984 KIFONT::FONT* aFont,
1985 const KIFONT::METRICS& aFontMetrics,
1986 void* aData )
1987{
1988 // Fix me: see how to use DXF text mode for multiline texts
1989 if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
1990 aMultilineAllowed = false; // the text has only one line.
1991
1992 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
1993
1994 if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
1995 {
1996 // output text as graphics.
1997 // Perhaps multiline texts could be handled as DXF text entity
1998 // but I do not want spend time about this (JPC)
1999 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
2000 aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
2001 }
2002 else
2003 {
2004 TEXT_ATTRIBUTES attrs;
2005 attrs.m_Halign = aH_justify;
2006 attrs.m_Valign = aV_justify;
2007 attrs.m_Size = aSize;
2008 attrs.m_StrokeWidth = aWidth;
2009 attrs.m_Angle = aOrient;
2010 attrs.m_Italic = aItalic;
2011 attrs.m_Bold = aBold;
2012 attrs.m_Mirrored = aSize.x < 0;
2013 attrs.m_Multiline = false;
2014 plotOneLineOfText( aPos, aColor, aText, attrs );
2015 }
2016}
2017
2018
2020 const COLOR4D& aColor,
2021 const wxString& aText,
2022 const TEXT_ATTRIBUTES& aAttributes,
2023 KIFONT::FONT* aFont,
2024 const KIFONT::METRICS& aFontMetrics,
2025 void* aData )
2026{
2027 TEXT_ATTRIBUTES attrs = aAttributes;
2028
2029 // Fix me: see how to use DXF text mode for multiline texts
2030 if( attrs.m_Multiline && !aText.Contains( wxT( "\n" ) ) )
2031 attrs.m_Multiline = false; // the text has only one line.
2032
2033 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
2034
2035 if( m_textAsLines || containsNonAsciiChars( aText ) || attrs.m_Multiline || processSuperSub )
2036 {
2037 // output text as graphics.
2038 // Perhaps multiline texts could be handled as DXF text entity
2039 // but I do not want spend time about that (JPC)
2040 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
2041 }
2042 else
2043 {
2044 plotOneLineOfText( aPos, aColor, aText, attrs );
2045 }
2046}
2047
2048
2049void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor,
2050 const wxString& aText, const TEXT_ATTRIBUTES& aAttributes )
2051{
2052 /* Emit text as a text entity. This loses formatting and shape but it's
2053 more useful as a CAD object */
2054 VECTOR2D origin_dev = userToDeviceCoordinates( aPos );
2055 SetColor( aColor );
2057
2058 VECTOR2D size_dev = userToDeviceSize( aAttributes.m_Size );
2059 int h_code = 0, v_code = 0;
2060
2061 switch( aAttributes.m_Halign )
2062 {
2063 case GR_TEXT_H_ALIGN_LEFT: h_code = 0; break;
2064 case GR_TEXT_H_ALIGN_CENTER: h_code = 1; break;
2065 case GR_TEXT_H_ALIGN_RIGHT: h_code = 2; break;
2066 case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
2067 }
2068
2069 switch( aAttributes.m_Valign )
2070 {
2071 case GR_TEXT_V_ALIGN_TOP: v_code = 3; break;
2072 case GR_TEXT_V_ALIGN_CENTER: v_code = 2; break;
2073 case GR_TEXT_V_ALIGN_BOTTOM: v_code = 1; break;
2074 case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
2075 }
2076
2077 std::string textStyle = "KICAD";
2078 if( aAttributes.m_Bold )
2079 {
2080 if( aAttributes.m_Italic )
2081 textStyle = "KICADBI";
2082 else
2083 textStyle = "KICADB";
2084 }
2085 else if( aAttributes.m_Italic )
2086 textStyle = "KICADI";
2087
2088 // The DXF spec requires two AcDbText subclass markers on TEXT, with group 73
2089 // (vertical alignment) scoped under the second one. The text string (group 1)
2090 // sits between height and rotation inside the first AcDbText scope.
2091 emitEntityHandle( "TEXT", "AcDbText", TO_UTF8( cLayerName ) );
2092
2093 fmt::print( m_outputFile,
2094 " 10\n{}\n 20\n{}\n 30\n0\n"
2095 " 40\n{}\n",
2096 formatCoord( origin_dev.x ),
2097 formatCoord( origin_dev.y ),
2098 formatCoord( size_dev.y ) );
2099
2100 /* There are two issue in emitting the text:
2101 - Our overline character (~) must be converted to the appropriate
2102 control sequence %%O or %%o
2103 - Text encoding in DXF is more or less unspecified since depends on
2104 the DXF declared version, the acad version reading it *and* some
2105 system variables to be put in the header handled only by newer acads
2106 Also before R15 unicode simply is not supported (you need to use
2107 bigfonts which are a massive PITA). Common denominator solution:
2108 use Latin1 (and however someone could choke on it, anyway). Sorry
2109 for the extended latin people. If somewant want to try fixing this
2110 recent version seems to use UTF-8 (and not UCS2 like the rest of
2111 Windows)
2112
2113 XXX Actually there is a *third* issue: older DXF formats are limited
2114 to 255 bytes records (it was later raised to 2048); since I'm lazy
2115 and text so long is not probable I just don't implement this rule.
2116 If someone is interested in fixing this, you have to emit the first
2117 partial lines with group code 3 (max 250 bytes each) and then finish
2118 with a group code 1 (less than 250 bytes). The DXF refs explains it
2119 in no more details...
2120 */
2121
2122 int braceNesting = 0;
2123 int overbarDepth = -1;
2124
2125 fmt::print( m_outputFile, " 1\n" );
2126
2127 for( unsigned int i = 0; i < aText.length(); i++ )
2128 {
2129 /* Here I do a bad thing: writing the output one byte at a time!
2130 but today I'm lazy and I have no idea on how to coerce a Unicode
2131 wxString to spit out latin1 encoded text ...
2132
2133 At least stdio is *supposed* to do output buffering, so there is
2134 hope is not too slow */
2135 wchar_t ch = aText[i];
2136
2137 if( ch > 255 )
2138 {
2139 // I can't encode this...
2140 putc( '?', m_outputFile );
2141 }
2142 else
2143 {
2144 if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
2145 {
2146 fmt::print( m_outputFile, "%%o" );
2147 overbarDepth = braceNesting;
2148
2149 // Skip the '{'
2150 i++;
2151 continue;
2152 }
2153 else if( aText[i] == '{' )
2154 {
2155 braceNesting++;
2156 }
2157 else if( aText[i] == '}' )
2158 {
2159 if( braceNesting > 0 )
2160 braceNesting--;
2161
2162 if( braceNesting == overbarDepth )
2163 {
2164 fmt::print( m_outputFile, "%%O" );
2165 overbarDepth = -1;
2166 continue;
2167 }
2168 }
2169
2170 putc( ch, m_outputFile );
2171 }
2172 }
2173
2174 fmt::print( m_outputFile, "\n" );
2175
2176 // Remaining AcDbText fields, plus the second AcDbText marker scoping vertical align.
2177 fmt::print( m_outputFile,
2178 " 50\n{:.8f}\n"
2179 " 41\n{}\n"
2180 " 51\n{:.8f}\n"
2181 " 7\n{}\n"
2182 " 71\n{}\n"
2183 " 72\n{}\n"
2184 " 11\n{}\n 21\n{}\n 31\n0\n"
2185 "100\nAcDbText\n"
2186 " 73\n{}\n",
2187 aAttributes.m_Angle.GetAngle().AsDegrees(),
2188 formatCoord( fabs( size_dev.x / size_dev.y ) ),
2189 aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0,
2190 textStyle,
2191 aAttributes.m_Mirrored ? 2 : 0,
2192 h_code,
2193 formatCoord( origin_dev.x ),
2194 formatCoord( origin_dev.y ),
2195 v_code );
2196}
int blue
DXF_COLOR_T colorNumber
static const double DXF_OBLIQUE_ANGLE
Oblique angle for DXF native text (I don't remember if 15 degrees is the ISO value....
static std::string formatCoord(double aValue)
int red
bool containsNonAsciiChars(const wxString &string)
Check if a given string contains non-ASCII characters.
int green
int index
static const struct @110355335012326036072316124131344246226335322120 acad_dxf_color_names[]
The layer/colors palette.
static const char * getDXFLineType(LINE_STYLE aType)
const char * name
static const struct @021340374267035355154031176330133165301272264217 acad_dxf_color_values[]
#define DXF_LINE_WIDTH
@ ERROR_INSIDE
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
static const COLOR4D WHITE
Definition color4d.h:403
static const COLOR4D BLACK
Definition color4d.h:404
void plotOneLineOfText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttrs)
DXF_UNITS m_plotUnits
unsigned int m_insUnits
uint64_t m_handle
unsigned int GetInsUnits() const
Get the correct value for the $INSUNITS header variable given the current units.
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr) override
bool m_textAsLines
virtual void FlashPadCustom(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, SHAPE_POLY_SET *aPolygons, void *aData) override
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, void *aData) override
std::string m_layoutDictHandle
void SetUnits(DXF_UNITS aUnit)
Set the units to use for plotting the DXF file.
std::vector< DxfLayout > m_dxfLayouts
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the scale/position for the DXF plot.
virtual void ThickArc(const VECTOR2D &aCentre, const EDA_ANGLE &aStAngle, const EDA_ANGLE &aAngle, double aRadius, int aWidth, void *aData) override
virtual void Arc(const VECTOR2D &aCenter, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, FILL_T aFill, int aWidth) override
double GetUnitScaling() const
Get the scale factor to apply to convert the device units to be in the currently set units.
virtual void FlashPadOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
DXF oval pad: always done in sketch mode.
std::string emitEntityHandle(const char *aEntityType, const char *aSubclass, const std::string &aLayerName, const std::string &aOwner="")
std::string m_plotStyleNameDictHandle
virtual void FlashPadRect(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
DXF rectangular pad: always done in sketch mode.
virtual void ThickRect(const VECTOR2I &p1, const VECTOR2I &p2, int width, void *aData) override
virtual void FlashPadRoundRect(const VECTOR2I &aPadPos, const VECTOR2I &aSize, int aCornerRadius, const EDA_ANGLE &aOrient, void *aData) override
virtual void ThickPoly(const SHAPE_POLY_SET &aPoly, int aWidth, void *aData) override
virtual bool StartPlot(const wxString &aPageNumber) override
Open the DXF plot with a skeleton header.
unsigned int GetMeasurementDirective() const
Get the correct value for the $MEASUREMENT field given the current units.
virtual void FlashPadTrapez(const VECTOR2I &aPadPos, const VECTOR2I *aCorners, const EDA_ANGLE &aPadOrient, void *aData) override
DXF trapezoidal pad: only sketch mode is supported.
virtual void Circle(const VECTOR2I &pos, int diametre, FILL_T fill, int width) override
DXF circle: full functionality; it even does 'fills' drawing a circle with a dual-arc polyline wide a...
virtual void FlashPadCircle(const VECTOR2I &pos, int diametre, void *aData) override
DXF round pad: always done in sketch mode; it could be filled but it isn't pretty if other kinds of p...
virtual void FilledCircle(const VECTOR2I &pos, int diametre, void *aData) override
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth, void *aData=nullptr) override
DXF polygon: doesn't fill it but at least it close the filled ones DXF does not know thick outline.
std::string m_modelSpaceHandle
wxString GetCurrentLayerName(DXF_LAYER_OUTPUT_MODE aMode, std::optional< PCB_LAYER_ID > aLayerId=std::nullopt)
Retrieves the current layer name or layer color name for DXF plotting.
std::string emitSymbolTableHeader(const char *aTableName, int aCount)
std::string m_namedObjectDictHandle
void writeObjectsSection()
unsigned int m_measurementDirective
virtual void Rect(const VECTOR2I &p1, const VECTOR2I &p2, FILL_T fill, int width, int aCornerRadius=0) override
DXF rectangle: fill not supported.
virtual bool EndPlot() override
virtual void FlashRegularPolygon(const VECTOR2I &aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE &aOrient, void *aData) override
Flash a regular polygon.
virtual void PenTo(const VECTOR2I &pos, char plume) override
Moveto/lineto primitive, moves the 'pen' to the specified direction.
virtual void SetColor(const COLOR4D &color) override
The DXF exporter handles 'colors' as layers...
int FindNearestLegacyColor(int aR, int aG, int aB)
virtual void Text(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const EDA_ANGLE &aOrient, const VECTOR2I &aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr) override
Draw text with the plotter.
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData) override
std::string m_plotStyleNormalHandle
double m_unitScalingFactor
virtual void SetDash(int aLineWidth, LINE_STYLE aLineStyle) override
COLOR4D m_currentColor
std::string nextHandle()
LINE_STYLE m_currentLineType
double AsDegrees() const
Definition eda_angle.h:115
EDA_ANGLE GetAngle() const
Definition eda_angle.h:533
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:391
double g
Green component.
Definition color4d.h:392
double b
Blue component.
Definition color4d.h:393
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
PCB_LAYER_ID GetLayer() const
Gets the ID of the current layer.
Definition plotter.h:246
std::vector< std::pair< PCB_LAYER_ID, wxString > > m_layersToExport
Definition plotter.h:749
bool m_plotMirror
Definition plotter.h:721
void MoveTo(const VECTOR2I &pos)
Definition plotter.h:310
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:388
void FinishTo(const VECTOR2I &pos)
Definition plotter.h:320
double m_iuPerDeviceUnit
Definition plotter.h:718
PCB_LAYER_ID m_layer
Definition plotter.h:751
RENDER_SETTINGS * RenderSettings()
Definition plotter.h:167
VECTOR2I m_plotOffset
Definition plotter.h:720
VECTOR2I m_penLastpos
Definition plotter.h:734
virtual VECTOR2D userToDeviceCoordinates(const VECTOR2I &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition plotter.cpp:91
VECTOR2I m_paperSize
Definition plotter.h:742
virtual VECTOR2D userToDeviceSize(const VECTOR2I &size)
Modify size according to the plotter scale factors (VECTOR2I version, returns a VECTOR2D).
Definition plotter.cpp:116
int GetPlotterArcHighDef() const
Definition plotter.h:276
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition plotter.h:710
bool GetColorMode() const
Definition plotter.h:164
FILE * m_outputFile
Output file.
Definition plotter.h:727
void LineTo(const VECTOR2I &pos)
Definition plotter.h:315
void PenFinish()
Definition plotter.h:326
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont=nullptr, const KIFONT::METRICS &aFontMetrics=KIFONT::METRICS::Default(), void *aData=nullptr)
Definition plotter.cpp:617
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth, void *aData)=0
Draw a polygon ( filled or not ).
virtual void Text(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const EDA_ANGLE &aOrient, const VECTOR2I &aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aPenWidth, bool aItalic, bool aBold, bool aMultilineAllowed, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr)
Draw text with the plotter.
Definition plotter.cpp:547
double m_IUsPerDecimil
Definition plotter.h:716
bool m_colorMode
Definition plotter.h:730
virtual DXF_OUTLINE_MODE GetDXFPlotMode() const
Definition plotter.h:109
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
int OutlineCount() const
Return the number of outlines in the set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
SHAPE_POLY_SET CloneDropTriangulation() const
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const SHAPE_LINE_CHAIN Outline() const
void SetRadius(int aRadius)
Definition shape_rect.h:201
EDA_ORIENTATION m_Angle
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
@ ROUND_ALL_CORNERS
All angles are rounded.
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
FILL_T
Definition eda_fill.h:29
@ NO_FILL
Definition eda_fill.h:30
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
This file contains miscellaneous commonly used macros and functions.
DXF_UNITS
Definition plotter.h:51
DXF_LAYER_OUTPUT_MODE
Specifies the output mode for the DXF layer.
Definition plotter.h:122
@ SKETCH
Definition plotter.h:81
DXF_COLOR_T
Legacy colors for DXF file.
Definition plotter_dxf.h:30
@ NBCOLORS
Number of colors.
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
LINE_STYLE
Dashed line types.
std::string blockRecordHandle
std::string path
int radius
VECTOR2I end
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_H_ALIGN_INDETERMINATE
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707