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 std::string 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 " 6\n{}\n",
755 aEntityType,
756 handle,
757 owner,
758 aLayerName,
760
761 if( aSubclass )
762 fmt::print( m_outputFile, "100\n{}\n", aSubclass );
763
764 return handle;
765}
766
767
768std::string DXF_PLOTTER::emitSymbolTableHeader( const char* aTableName, int aCount )
769{
770 std::string handle = nextHandle();
771
772 fmt::print( m_outputFile,
773 " 0\n"
774 "TABLE\n"
775 " 2\n{}\n"
776 " 5\n{}\n"
777 "330\n0\n"
778 "100\nAcDbSymbolTable\n"
779 " 70\n{}\n",
780 aTableName,
781 handle,
782 aCount );
783
784 return handle;
785}
786
787
788bool DXF_PLOTTER::StartPlot( const wxString& aPageNumber )
789{
790 wxASSERT( m_outputFile );
791
792 // Reset state so a single DXF_PLOTTER instance can be reused for multiple plots.
793 m_handle = 0;
794 m_modelSpaceHandle.clear();
795
796 // Tagged AC1018 (R2004) because the LAYER table emits the 420 true-color group,
797 // which AutoCAD only accepts in R2004+. That in turn requires handles on every
798 // record and entity plus a full R2000 table and blocks skeleton.
799 fmt::print( m_outputFile,
800 " 0\n"
801 "SECTION\n"
802 " 2\n"
803 "HEADER\n"
804 " 9\n"
805 "$ACADVER\n"
806 " 1\n"
807 "AC1018\n"
808 " 9\n"
809 "$HANDSEED\n"
810 " 5\n"
811 "FFFFFFFF\n"
812 " 9\n"
813 "$ANGBASE\n"
814 " 50\n"
815 "0.0\n"
816 " 9\n"
817 "$ANGDIR\n"
818 " 70\n"
819 "1\n"
820 " 9\n"
821 "$MEASUREMENT\n"
822 " 70\n"
823 "{}\n"
824 " 9\n"
825 "$INSUNITS\n"
826 " 70\n"
827 "{}\n"
828 " 0\n"
829 "ENDSEC\n",
831
832 fmt::print( m_outputFile,
833 " 0\n"
834 "SECTION\n"
835 " 2\n"
836 "TABLES\n" );
837
838 // AutoCAD refuses to load R2000+ files lacking an ACAD APPID entry.
839 std::string appidTableHandle = emitSymbolTableHeader( "APPID", 1 );
840
841 fmt::print( m_outputFile,
842 " 0\n"
843 "APPID\n"
844 " 5\n{}\n"
845 "330\n{}\n"
846 "100\nAcDbSymbolTableRecord\n"
847 "100\nAcDbRegAppTableRecord\n"
848 " 2\nACAD\n"
849 " 70\n0\n"
850 " 0\n"
851 "ENDTAB\n",
852 nextHandle(), appidTableHandle );
853
854 // CONTINUOUS, DASHDOT, DASHED and DOTTED cover every LINE_STYLE we emit via the
855 // 6/<name> group on LINE entities. The 49 group is the per-element dash length
856 // (positive) or gap length (negative); each 49 must be followed by a 74 (complex
857 // linetype element type, 0 = plain) in R2000+ files.
858 struct LtypePattern
859 {
860 const char* name;
861 const char* description;
862 int elementCount;
863 double patternLength;
864 const char* dashes;
865 };
866
867 // AutoCAD looks up ByBlock and ByLayer by name and aborts when either is absent.
868 // Both are zero-element solid patterns; the actual appearance is inherited from
869 // the owning block or layer at render time.
870 static const LtypePattern ltypes[] = {
871 { "ByBlock", "", 0, 0.0, "" },
872 { "ByLayer", "", 0, 0.0, "" },
873 { "CONTINUOUS", "Solid line", 0, 0.0, "" },
874 { "DASHDOT", "Dash Dot ____ _ ____ _", 4, 2.0, " 49\n1.25\n 74\n0\n 49\n-0.25\n 74\n0\n"
875 " 49\n0.25\n 74\n0\n 49\n-0.25\n 74\n0\n" },
876 { "DASHED", "Dashed __ __ __ __ __", 2, 0.75, " 49\n0.50\n 74\n0\n 49\n-0.25\n 74\n0\n" },
877 { "DOTTED", "Dotted . . . .", 2, 0.2, " 49\n0.00\n 74\n0\n 49\n-0.20\n 74\n0\n" },
878 { "DIVIDE", "Divide ____ _ _ ____", 6, 2.5, " 49\n1.25\n 74\n0\n 49\n-0.25\n 74\n0\n"
879 " 49\n0.25\n 74\n0\n 49\n-0.25\n 74\n0\n"
880 " 49\n0.25\n 74\n0\n 49\n-0.25\n 74\n0\n" },
881 };
882
883 std::string ltypeTableHandle = emitSymbolTableHeader( "LTYPE", static_cast<int>( std::size( ltypes ) ) );
884
885 for( const LtypePattern& lt : ltypes )
886 {
887 fmt::print( m_outputFile,
888 " 0\n"
889 "LTYPE\n"
890 " 5\n{}\n"
891 "330\n{}\n"
892 "100\nAcDbSymbolTableRecord\n"
893 "100\nAcDbLinetypeTableRecord\n"
894 " 2\n{}\n"
895 " 70\n0\n"
896 " 3\n{}\n"
897 " 72\n65\n"
898 " 73\n{}\n"
899 " 40\n{}\n"
900 "{}",
901 nextHandle(),
902 ltypeTableHandle,
903 lt.name, lt.description, lt.elementCount, lt.patternLength, lt.dashes );
904 }
905
906 fmt::print( m_outputFile,
907 " 0\n"
908 "ENDTAB\n" );
909
910 // STYLE table - one entry per bold/italic combination.
911 std::string styleTableHandle = emitSymbolTableHeader( "STYLE", 4 );
912
913 static const char* style_name[4] = { "KICAD", "KICADB", "KICADI", "KICADBI" };
914
915 for( int i = 0; i < 4; i++ )
916 {
917 fmt::print( m_outputFile,
918 " 0\n"
919 "STYLE\n"
920 " 5\n{}\n"
921 "330\n{}\n"
922 "100\nAcDbSymbolTableRecord\n"
923 "100\nAcDbTextStyleTableRecord\n"
924 " 2\n{}\n"
925 " 70\n0\n"
926 " 40\n0\n"
927 " 41\n1\n"
928 " 42\n1\n"
929 " 50\n{:g}\n"
930 " 71\n0\n"
931 // The standard ISO font (when kicad is built with it the dxf text in
932 // acad matches *perfectly*)
933 " 3\nisocp.shx\n",
934 nextHandle(),
935 styleTableHandle,
936 style_name[i],
937 i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
938 }
939
940 fmt::print( m_outputFile,
941 " 0\n"
942 "ENDTAB\n" );
943
944 int numLayers = m_layersToExport.empty() ? static_cast<int>( DXF_COLOR_T::NBCOLORS )
945 : static_cast<int>( m_layersToExport.size() );
946
947 // If printing in monochrome, only output the black layer
948 if( !GetColorMode() && m_layersToExport.empty() )
949 numLayers = 1;
950
951 // Every LAYER record must carry a 390 hard-pointer to a PlotStyleName object.
952 // We share one ACDBPLACEHOLDER named "Normal" across every layer; allocate its
953 // handle (and that of its owning dictionary) now so the LAYER records can cite it.
954 // The +1 on the count is the default layer "0" emitted next.
955 std::string layerTableHandle = emitSymbolTableHeader( "LAYER", numLayers + 1 );
956
959
960 // Default layer "0" is required by name in the LAYER table. Entities may
961 // reference undeclared layers (spec page 230) but the "0" entry itself must
962 // exist. Color 7 / CONTINUOUS are the conventional defaults.
963 fmt::print( m_outputFile,
964 " 0\n"
965 "LAYER\n"
966 " 5\n{}\n"
967 "330\n{}\n"
968 "100\nAcDbSymbolTableRecord\n"
969 "100\nAcDbLayerTableRecord\n"
970 " 2\n0\n"
971 " 70\n0\n"
972 " 62\n7\n"
973 " 6\nCONTINUOUS\n"
974 "390\n{}\n",
975 nextHandle(), layerTableHandle, m_plotStyleNormalHandle );
976
977 /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
978
979 - The primary colors (1 - 9)
980 - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
981 - Greys (251 - 255)
982 */
983
984 wxString layerName;
985 int colorNumber;
986
987 bool hasActualColor = false;
988 COLOR4D actualColor;
989
990 for( int i = 0; i < numLayers; i++ )
991 {
992 if( !m_layersToExport.empty() )
993 {
996 m_layersToExport.at( i ).first );
997
998 auto it = std::find_if( std::begin( acad_dxf_color_names ), std::end( acad_dxf_color_names ),
999 [colorName](const auto& layer)
1000 {
1001 return std::strcmp( layer.name, colorName.ToStdString().c_str() ) == 0;
1002 });
1003
1004 if( it != std::end( acad_dxf_color_names ) )
1005 colorNumber = it->index;
1006 else
1007 colorNumber = 7; // Default to white/black
1008
1009 actualColor = RenderSettings()->GetLayerColor( m_layersToExport.at( i ).first );
1010 hasActualColor = true;
1011 }
1012 else
1013 {
1014 layerName = wxString( acad_dxf_color_names[i].name );
1016 }
1017
1018 fmt::print( m_outputFile,
1019 " 0\n"
1020 "LAYER\n"
1021 " 5\n{}\n"
1022 "330\n{}\n"
1023 "100\nAcDbSymbolTableRecord\n"
1024 "100\nAcDbLayerTableRecord\n"
1025 " 2\n{}\n"
1026 " 70\n0\n"
1027 " 62\n{}\n",
1028 nextHandle(),
1029 layerTableHandle,
1030 TO_UTF8( layerName ),
1031 colorNumber );
1032
1033 if( hasActualColor )
1034 {
1035 // Group 420 carries the 24-bit true color introduced in R2004; the legacy
1036 // 62 color index alone would otherwise lose the user-chosen layer colour.
1037 int r = static_cast<int>( actualColor.r * 255 );
1038 int g = static_cast<int>( actualColor.g * 255 );
1039 int b = static_cast<int>( actualColor.b * 255 );
1040
1041 int trueColorValue = ( r << 16 ) | ( g << 8 ) | b;
1042
1043 fmt::print( m_outputFile, "420\n{}\n", trueColorValue );
1044 }
1045
1046 // 6 (linetype) and 390 (plot style) are mandatory on every R2000+ LAYER.
1047 fmt::print( m_outputFile,
1048 " 6\nCONTINUOUS\n"
1049 "390\n{}\n",
1051 }
1052
1053 fmt::print( m_outputFile,
1054 " 0\n"
1055 "ENDTAB\n" );
1056
1057 // AutoCAD's R2000+ reader walks a fixed list of expected symbol tables and aborts
1058 // when one is absent. Empty headers satisfy the parser. Spec page 35 only
1059 // constrains table order via LTYPE preceding LAYER, which is already true above.
1060 for( const char* tableName : { "VPORT", "VIEW", "UCS" } )
1061 {
1062 emitSymbolTableHeader( tableName, 0 );
1063 fmt::print( m_outputFile, " 0\nENDTAB\n" );
1064 }
1065
1066 // DIMSTYLE is the only symbol table whose record handle uses group code 105
1067 // instead of 5, and whose header carries an extra AcDbDimStyleTable subclass
1068 // marker (spec page 35). AutoCAD requires the Standard entry.
1069 std::string dimstyleTableHandle = emitSymbolTableHeader( "DIMSTYLE", 1 );
1070
1071 fmt::print( m_outputFile,
1072 "100\nAcDbDimStyleTable\n"
1073 " 71\n0\n"
1074 " 0\n"
1075 "DIMSTYLE\n"
1076 "105\n{}\n"
1077 "330\n{}\n"
1078 "100\nAcDbSymbolTableRecord\n"
1079 "100\nAcDbDimStyleTableRecord\n"
1080 " 2\nStandard\n"
1081 " 70\n0\n"
1082 " 0\n"
1083 "ENDTAB\n",
1084 nextHandle(),
1085 dimstyleTableHandle );
1086
1087 // R2004 mandates three empty layout blocks (*Model_Space, *Paper_Space,
1088 // *Paper_Space0); each BLOCK_RECORD entry carries a 340 hard-pointer to its
1089 // associated LAYOUT object. Pre-allocate the LAYOUT handles now so EndPlot()
1090 // can emit the back-pointers when it writes OBJECTS.
1091 std::string blockRecordTableHandle = emitSymbolTableHeader( "BLOCK_RECORD", 3 );
1092
1094 std::string paperSpaceBR = nextHandle();
1095 std::string paperSpace0BR = nextHandle();
1096
1097 m_dxfLayouts.clear();
1098 m_dxfLayouts.reserve( 3 );
1099 m_dxfLayouts.push_back( { "Model", "*Model_Space", m_modelSpaceHandle, nextHandle(), false } );
1100 m_dxfLayouts.push_back( { "Layout1", "*Paper_Space", paperSpaceBR, nextHandle(), true } );
1101 m_dxfLayouts.push_back( { "Layout2", "*Paper_Space0", paperSpace0BR, nextHandle(), true } );
1102
1103 // The root Named Object Dictionary and ACAD_LAYOUT live in OBJECTS, but their
1104 // handles are referenced from LAYOUT objects via 330, so allocate them now.
1107
1108 for( const DXF_LAYOUT& l : m_dxfLayouts )
1109 {
1110 fmt::print( m_outputFile,
1111 " 0\n"
1112 "BLOCK_RECORD\n"
1113 " 5\n{}\n"
1114 "330\n{}\n"
1115 "100\nAcDbSymbolTableRecord\n"
1116 "100\nAcDbBlockTableRecord\n"
1117 " 2\n{}\n"
1118 "340\n{}\n"
1119 " 70\n0\n"
1120 "280\n1\n"
1121 "281\n0\n",
1122 l.blockRecordHandle,
1123 blockRecordTableHandle,
1124 l.blockName,
1125 l.layoutHandle );
1126 }
1127
1128 fmt::print( m_outputFile,
1129 " 0\n"
1130 "ENDTAB\n"
1131 " 0\n"
1132 "ENDSEC\n" );
1133
1134 // Three empty BLOCK/ENDBLK pairs back the three BLOCK_RECORD entries. Paperspace
1135 // blocks carry the 67/1 paperspace flag inside AcDbEntity.
1136 fmt::print( m_outputFile,
1137 " 0\n"
1138 "SECTION\n"
1139 " 2\n"
1140 "BLOCKS\n" );
1141
1142 for( const DXF_LAYOUT& l : m_dxfLayouts )
1143 {
1144 fmt::print( m_outputFile,
1145 " 0\n"
1146 "BLOCK\n"
1147 " 5\n{}\n"
1148 "330\n{}\n"
1149 "100\nAcDbEntity\n"
1150 "{}"
1151 " 8\n0\n"
1152 "100\nAcDbBlockBegin\n"
1153 " 2\n{}\n"
1154 " 70\n0\n"
1155 " 10\n0.0\n 20\n0.0\n 30\n0.0\n"
1156 " 3\n{}\n"
1157 " 1\n\n"
1158 " 0\n"
1159 "ENDBLK\n"
1160 " 5\n{}\n"
1161 "330\n{}\n"
1162 "100\nAcDbEntity\n"
1163 "{}"
1164 " 8\n0\n"
1165 "100\nAcDbBlockEnd\n",
1166 nextHandle(),
1167 l.blockRecordHandle,
1168 l.isPaperSpace ? " 67\n1\n" : "",
1169 l.blockName,
1170 l.blockName,
1171 nextHandle(),
1172 l.blockRecordHandle,
1173 l.isPaperSpace ? " 67\n1\n" : "" );
1174 }
1175
1176 fmt::print( m_outputFile,
1177 " 0\n"
1178 "ENDSEC\n" );
1179
1180 // Begin ENTITIES section
1181 fmt::print( m_outputFile,
1182 " 0\n"
1183 "SECTION\n"
1184 " 2\n"
1185 "ENTITIES\n" );
1186
1187 return true;
1188}
1189
1190
1192{
1193 // Root Named Object Dictionary. 281/1 marks dict elements as hard-owned, matching
1194 // AutoCAD. The empty ACAD_GROUP dict is required by the named-object root, and
1195 // the ACAD_PLOTSTYLENAME dict resolves the 390 plot-style references on LAYER
1196 // records.
1197 std::string acadGroupDictHandle = nextHandle();
1198
1199 fmt::print( m_outputFile,
1200 " 0\n"
1201 "SECTION\n"
1202 " 2\n"
1203 "OBJECTS\n"
1204 " 0\n"
1205 "DICTIONARY\n"
1206 " 5\n{}\n"
1207 "330\n0\n"
1208 "100\nAcDbDictionary\n"
1209 "281\n1\n"
1210 " 3\nACAD_GROUP\n"
1211 "350\n{}\n"
1212 " 3\nACAD_LAYOUT\n"
1213 "350\n{}\n"
1214 " 3\nACAD_PLOTSTYLENAME\n"
1215 "350\n{}\n",
1217 acadGroupDictHandle,
1220
1221 // ACAD_GROUP - empty, but the named-object root requires it.
1222 fmt::print( m_outputFile,
1223 " 0\n"
1224 "DICTIONARY\n"
1225 " 5\n{}\n"
1226 "330\n{}\n"
1227 "100\nAcDbDictionary\n"
1228 "281\n1\n",
1229 acadGroupDictHandle,
1231
1232 // ACDBDICTIONARYWDFLT is a dictionary with a default entry; the 340 points to the
1233 // same Normal placeholder as the dict's "Normal" entry, and every LAYER's 390
1234 // resolves through here.
1235 fmt::print( m_outputFile,
1236 " 0\n"
1237 "ACDBDICTIONARYWDFLT\n"
1238 " 5\n{}\n"
1239 "330\n{}\n"
1240 "100\nAcDbDictionary\n"
1241 "281\n1\n"
1242 " 3\nNormal\n"
1243 "350\n{}\n"
1244 "100\nAcDbDictionaryWithDefault\n"
1245 "340\n{}\n",
1250
1251 // ACDBPLACEHOLDER carries no payload; it just gives the "Normal" plot style a
1252 // handle that LAYER's 390 can resolve.
1253 fmt::print( m_outputFile,
1254 " 0\n"
1255 "ACDBPLACEHOLDER\n"
1256 " 5\n{}\n"
1257 "330\n{}\n",
1260
1261 // ACAD_LAYOUT names every LAYOUT object emitted below.
1262 fmt::print( m_outputFile,
1263 " 0\n"
1264 "DICTIONARY\n"
1265 " 5\n{}\n"
1266 "330\n{}\n"
1267 "100\nAcDbDictionary\n"
1268 "281\n1\n",
1271
1272 for( const DXF_LAYOUT& l : m_dxfLayouts )
1273 {
1274 fmt::print( m_outputFile,
1275 " 3\n{}\n"
1276 "350\n{}\n",
1277 l.name,
1278 l.layoutHandle );
1279 }
1280
1281 // The 4/<name> and 44/45 (width/height) fields are correlated and must change
1282 // together. ±1e+20 in extmin/extmax is the AcDbLayout sentinel for uninitialised
1283 // extents.
1284 static constexpr const char* PAPER_NAME = "A3";
1285 static constexpr double PAPER_WIDTH_MM = 420.0;
1286 static constexpr double PAPER_HEIGHT_MM = 297.0;
1287 static constexpr int PLOT_FLAG_MODELTYPE = 1024;
1288
1289 // Field set and ordering match what ODA File Converter writes for R2004. The
1290 // reader is order-sensitive here and rejects deviations like a missing group 2,
1291 // ModelType set on a paperspace layout, or 147 appearing before 76/77/78.
1292 for( std::size_t i = 0; i < m_dxfLayouts.size(); ++i )
1293 {
1294 const DXF_LAYOUT& l = m_dxfLayouts[i];
1295 int plotLayoutFlag = l.isPaperSpace ? 0 : PLOT_FLAG_MODELTYPE;
1296
1297 fmt::print( m_outputFile,
1298 " 0\n"
1299 "LAYOUT\n"
1300 " 5\n{}\n"
1301 "330\n{}\n"
1302 "100\nAcDbPlotSettings\n"
1303 " 1\n\n"
1304 " 2\nnone_device\n"
1305 " 4\n{}\n"
1306 " 6\n\n"
1307 " 40\n0.0\n 41\n0.0\n 42\n0.0\n 43\n0.0\n"
1308 " 44\n{:.1f}\n 45\n{:.1f}\n 46\n0.0\n 47\n0.0\n"
1309 " 48\n0.0\n 49\n0.0\n"
1310 "140\n0.0\n141\n0.0\n142\n1.0\n143\n1.0\n"
1311 " 70\n{}\n"
1312 " 72\n1\n 73\n0\n 74\n5\n"
1313 " 7\n\n"
1314 " 75\n16\n"
1315 " 76\n0\n 77\n2\n 78\n300\n"
1316 "147\n1.0\n"
1317 "148\n0.0\n149\n0.0\n"
1318 "100\nAcDbLayout\n"
1319 " 1\n{}\n"
1320 " 70\n1\n"
1321 " 71\n{}\n"
1322 " 10\n0.0\n 20\n0.0\n"
1323 " 11\n{:.1f}\n 21\n{:.1f}\n"
1324 " 12\n0.0\n 22\n0.0\n 32\n0.0\n"
1325 " 14\n1e+20\n 24\n1e+20\n 34\n1e+20\n"
1326 " 15\n-1e+20\n 25\n-1e+20\n 35\n-1e+20\n"
1327 "146\n0.0\n"
1328 " 13\n0.0\n 23\n0.0\n 33\n0.0\n"
1329 " 16\n1.0\n 26\n0.0\n 36\n0.0\n"
1330 " 17\n0.0\n 27\n1.0\n 37\n0.0\n"
1331 " 76\n0\n"
1332 "330\n{}\n",
1333 l.layoutHandle,
1335 PAPER_NAME, PAPER_WIDTH_MM, PAPER_HEIGHT_MM,
1336 plotLayoutFlag,
1337 l.name,
1338 static_cast<int>( i ),
1339 PAPER_WIDTH_MM, PAPER_HEIGHT_MM,
1341 }
1342
1343 fmt::print( m_outputFile,
1344 " 0\n"
1345 "ENDSEC\n" );
1346}
1347
1348
1350{
1351 wxASSERT( m_outputFile );
1352
1353 fmt::print( m_outputFile,
1354 " 0\n"
1355 "ENDSEC\n" );
1356
1358
1359 fmt::print( m_outputFile,
1360 " 0\n"
1361 "EOF\n" );
1362 fclose( m_outputFile );
1363 m_outputFile = nullptr;
1364
1365 return true;
1366}
1367
1368
1369void DXF_PLOTTER::SetColor( const COLOR4D& color )
1370{
1371 if( ( m_colorMode )
1372 || ( color == COLOR4D::BLACK )
1373 || ( color == COLOR4D::WHITE ) )
1374 {
1375 m_currentColor = color;
1376 }
1377 else
1378 {
1380 }
1381}
1382
1383
1384void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width, int aCornerRadius )
1385{
1386 wxASSERT( m_outputFile );
1387
1388 if( aCornerRadius > 0 )
1389 {
1390 BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
1391 box.Normalize();
1392 SHAPE_RECT rect( box );
1393 rect.SetRadius( aCornerRadius );
1394 PlotPoly( rect.Outline(), fill, width, nullptr );
1395 return;
1396 }
1397
1398 if( p1 != p2 )
1399 {
1400 MoveTo( p1 );
1401 LineTo( VECTOR2I( p1.x, p2.y ) );
1402 LineTo( VECTOR2I( p2.x, p2.y ) );
1403 LineTo( VECTOR2I( p2.x, p1.y ) );
1404 FinishTo( VECTOR2I( p1.x, p1.y ) );
1405 }
1406 else
1407 {
1408 // Draw as a point
1410
1411 VECTOR2D point_dev = userToDeviceCoordinates( p1 );
1412
1413 emitEntityHandle( "POINT", "AcDbPoint", TO_UTF8( cLayerName ) );
1414 fmt::print( m_outputFile, " 10\n{}\n 20\n{}\n 30\n0\n",
1415 formatCoord( point_dev.x ),
1416 formatCoord( point_dev.y ) );
1417 }
1418}
1419
1420
1421void DXF_PLOTTER::Circle( const VECTOR2I& centre, int diameter, FILL_T fill, int width )
1422{
1423 wxASSERT( m_outputFile );
1424 double radius = userToDeviceSize( diameter / 2.0 );
1425 VECTOR2D centre_dev = userToDeviceCoordinates( centre );
1426
1428 std::string layer = TO_UTF8( cLayerName );
1429
1430 if( radius > 0 )
1431 {
1432 if( fill == FILL_T::NO_FILL )
1433 {
1434 emitEntityHandle( "CIRCLE", "AcDbCircle", layer );
1435 fmt::print( m_outputFile, " 10\n{}\n 20\n{}\n 30\n0\n 40\n{}\n",
1436 formatCoord( centre_dev.x ),
1437 formatCoord( centre_dev.y ),
1438 formatCoord( radius ) );
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 std::string lineStyleName = getDXFLineType( 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
1668 emitEntityHandle( "ARC", "AcDbCircle", layer );
1669 fmt::print( m_outputFile,
1670 " 10\n{}\n 20\n{}\n 30\n0\n 40\n{}\n"
1671 "100\nAcDbArc\n"
1672 " 50\n{:.8f}\n 51\n{:.8f}\n",
1673 formatCoord( centre_device.x ),
1674 formatCoord( centre_device.y ),
1675 formatCoord( radius_device ),
1676 startAngle.AsDegrees(),
1677 endAngle.AsDegrees() );
1678}
1679
1680
1681void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth, void* aData )
1682{
1683 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1684
1685 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1686 {
1687 std::vector<VECTOR2I> cornerList;
1688 SHAPE_POLY_SET outlineBuffer;
1689 TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(), ERROR_INSIDE );
1690 const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );
1691
1692 cornerList.reserve( path.PointCount() );
1693
1694 for( int jj = 0; jj < path.PointCount(); jj++ )
1695 cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );
1696
1697 // Ensure the polygon is closed
1698 if( cornerList[0] != cornerList[cornerList.size() - 1] )
1699 cornerList.push_back( cornerList[0] );
1700
1701 PlotPoly( cornerList, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1702 }
1703 else
1704 {
1705 MoveTo( aStart );
1706 FinishTo( aEnd );
1707 }
1708}
1709
1710
1711void DXF_PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle,
1712 double aRadius, int aWidth, void* aData )
1713{
1714 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1715
1716 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1717 {
1718 Arc( centre, aStartAngle, aAngle, aRadius - aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1719 Arc( centre, aStartAngle, aAngle, aRadius + aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1720 }
1721 else
1722 {
1723 Arc( centre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1724 }
1725}
1726
1727
1728void DXF_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1729{
1730 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1731
1732 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1733 {
1734 VECTOR2I offsetp1( p1.x - width/2, p1.y - width/2 );
1735 VECTOR2I offsetp2( p2.x + width/2, p2.y + width/2 );
1736 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1737
1738 offsetp1.x += width;
1739 offsetp1.y += width;
1740 offsetp2.x -= width;
1741 offsetp2.y -= width;
1742 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1743 }
1744 else
1745 {
1746 Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1747 }
1748}
1749
1750
1751void DXF_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1752{
1753 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1754
1755 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1756 {
1757 Circle( pos, diametre - width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1758 Circle( pos, diametre + width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1759 }
1760 else
1761 {
1762 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1763 }
1764}
1765
1766
1767void DXF_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1768{
1769 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1770
1771 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1772 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1773 else
1774 Circle( pos, diametre, FILL_T::FILLED_SHAPE, 0 );
1775}
1776
1777
1778void DXF_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1779{
1780 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1781
1782 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1783 {
1784 SHAPE_POLY_SET outline = aPoly.CloneDropTriangulation();
1787
1788 outline = aPoly.CloneDropTriangulation();
1791 }
1792 else
1793 {
1794 PLOTTER::PlotPoly( aPoly.COutline( 0 ), FILL_T::NO_FILL, aWidth, aData );
1795 }
1796}
1797
1798
1799void DXF_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient, void* aData )
1800{
1801 wxASSERT( m_outputFile );
1802
1803 VECTOR2I size( aSize );
1804 EDA_ANGLE orient( aOrient );
1805
1806 /* The chip is reduced to an oval tablet with size.y > size.x
1807 * (Oval vertical orientation 0) */
1808 if( size.x > size.y )
1809 {
1810 std::swap( size.x, size.y );
1811 orient += ANGLE_90;
1812 }
1813
1814 ThickOval( aPos, size, orient, DXF_LINE_WIDTH, aData );
1815}
1816
1817
1818void DXF_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1819{
1820 wxASSERT( m_outputFile );
1821 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1822}
1823
1824
1825void DXF_PLOTTER::FlashPadRect( const VECTOR2I& aPos, const VECTOR2I& aPadSize, const EDA_ANGLE& aOrient,
1826 void* aData )
1827{
1828 wxASSERT( m_outputFile );
1829
1830 VECTOR2I size, start, end;
1831
1832 size.x = aPadSize.x / 2;
1833 size.y = aPadSize.y / 2;
1834
1835 if( size.x < 0 )
1836 size.x = 0;
1837
1838 if( size.y < 0 )
1839 size.y = 0;
1840
1841 // If a dimension is zero, the trace is reduced to 1 line
1842 if( size.x == 0 )
1843 {
1844 start = VECTOR2I( aPos.x, aPos.y - size.y );
1845 end = VECTOR2I( aPos.x, aPos.y + size.y );
1846 RotatePoint( start, aPos, aOrient );
1847 RotatePoint( end, aPos, aOrient );
1848 MoveTo( start );
1849 FinishTo( end );
1850 return;
1851 }
1852
1853 if( size.y == 0 )
1854 {
1855 start = VECTOR2I( aPos.x - size.x, aPos.y );
1856 end = VECTOR2I( aPos.x + size.x, aPos.y );
1857 RotatePoint( start, aPos, aOrient );
1858 RotatePoint( end, aPos, aOrient );
1859 MoveTo( start );
1860 FinishTo( end );
1861 return;
1862 }
1863
1864 start = VECTOR2I( aPos.x - size.x, aPos.y - size.y );
1865 RotatePoint( start, aPos, aOrient );
1866 MoveTo( start );
1867
1868 end = VECTOR2I( aPos.x - size.x, aPos.y + size.y );
1869 RotatePoint( end, aPos, aOrient );
1870 LineTo( end );
1871
1872 end = VECTOR2I( aPos.x + size.x, aPos.y + size.y );
1873 RotatePoint( end, aPos, aOrient );
1874 LineTo( end );
1875
1876 end = VECTOR2I( aPos.x + size.x, aPos.y - size.y );
1877 RotatePoint( end, aPos, aOrient );
1878 LineTo( end );
1879
1880 FinishTo( start );
1881}
1882
1883
1884void DXF_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize, int aCornerRadius,
1885 const EDA_ANGLE& aOrient, void* aData )
1886{
1887 SHAPE_POLY_SET outline;
1888 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0, 0, 0,
1890
1891 // TransformRoundRectToPolygon creates only one convex polygon
1892 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1893
1894 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1895
1896 for( int ii = 1; ii < poly.PointCount(); ++ii )
1897 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1898
1899 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1900}
1901
1902
1903void DXF_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient,
1904 SHAPE_POLY_SET* aPolygons, void* aData )
1905{
1906 for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
1907 {
1908 SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
1909
1910 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1911
1912 for( int ii = 1; ii < poly.PointCount(); ++ii )
1913 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1914
1915 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1916 }
1917}
1918
1919
1920void DXF_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners, const EDA_ANGLE& aPadOrient,
1921 void* aData )
1922{
1923 wxASSERT( m_outputFile );
1924 VECTOR2I coord[4]; /* coord actual corners of a trapezoidal trace */
1925
1926 for( int ii = 0; ii < 4; ii++ )
1927 {
1928 coord[ii] = aCorners[ii];
1929 RotatePoint( coord[ii], aPadOrient );
1930 coord[ii] += aPadPos;
1931 }
1932
1933 // Plot edge:
1934 MoveTo( coord[0] );
1935 LineTo( coord[1] );
1936 LineTo( coord[2] );
1937 LineTo( coord[3] );
1938 FinishTo( coord[0] );
1939}
1940
1941
1942void DXF_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aRadius, int aCornerCount,
1943 const EDA_ANGLE& aOrient, void* aData )
1944{
1945 // Do nothing
1946 wxASSERT( 0 );
1947}
1948
1949
1957bool containsNonAsciiChars( const wxString& string )
1958{
1959 for( unsigned i = 0; i < string.length(); i++ )
1960 {
1961 wchar_t ch = string[i];
1962
1963 if( ch > 255 )
1964 return true;
1965 }
1966 return false;
1967}
1968
1969
1970void DXF_PLOTTER::Text( const VECTOR2I& aPos,
1971 const COLOR4D& aColor,
1972 const wxString& aText,
1973 const EDA_ANGLE& aOrient,
1974 const VECTOR2I& aSize,
1975 enum GR_TEXT_H_ALIGN_T aH_justify,
1976 enum GR_TEXT_V_ALIGN_T aV_justify,
1977 int aWidth,
1978 bool aItalic,
1979 bool aBold,
1980 bool aMultilineAllowed,
1981 KIFONT::FONT* aFont,
1982 const KIFONT::METRICS& aFontMetrics,
1983 void* aData )
1984{
1985 // Fix me: see how to use DXF text mode for multiline texts
1986 if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
1987 aMultilineAllowed = false; // the text has only one line.
1988
1989 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
1990
1991 if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
1992 {
1993 // output text as graphics.
1994 // Perhaps multiline texts could be handled as DXF text entity
1995 // but I do not want spend time about this (JPC)
1996 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
1997 aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1998 }
1999 else
2000 {
2001 TEXT_ATTRIBUTES attrs;
2002 attrs.m_Halign = aH_justify;
2003 attrs.m_Valign = aV_justify;
2004 attrs.m_Size = aSize;
2005 attrs.m_StrokeWidth = aWidth;
2006 attrs.m_Angle = aOrient;
2007 attrs.m_Italic = aItalic;
2008 attrs.m_Bold = aBold;
2009 attrs.m_Mirrored = aSize.x < 0;
2010 attrs.m_Multiline = false;
2011 plotOneLineOfText( aPos, aColor, aText, attrs );
2012 }
2013}
2014
2015
2017 const COLOR4D& aColor,
2018 const wxString& aText,
2019 const TEXT_ATTRIBUTES& aAttributes,
2020 KIFONT::FONT* aFont,
2021 const KIFONT::METRICS& aFontMetrics,
2022 void* aData )
2023{
2024 TEXT_ATTRIBUTES attrs = aAttributes;
2025
2026 // Fix me: see how to use DXF text mode for multiline texts
2027 if( attrs.m_Multiline && !aText.Contains( wxT( "\n" ) ) )
2028 attrs.m_Multiline = false; // the text has only one line.
2029
2030 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
2031
2032 if( m_textAsLines || containsNonAsciiChars( aText ) || attrs.m_Multiline || processSuperSub )
2033 {
2034 // output text as graphics.
2035 // Perhaps multiline texts could be handled as DXF text entity
2036 // but I do not want spend time about that (JPC)
2037 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
2038 }
2039 else
2040 {
2041 plotOneLineOfText( aPos, aColor, aText, attrs );
2042 }
2043}
2044
2045
2046void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor,
2047 const wxString& aText, const TEXT_ATTRIBUTES& aAttributes )
2048{
2049 /* Emit text as a text entity. This loses formatting and shape but it's
2050 more useful as a CAD object */
2051 VECTOR2D origin_dev = userToDeviceCoordinates( aPos );
2052 SetColor( aColor );
2054
2055 VECTOR2D size_dev = userToDeviceSize( aAttributes.m_Size );
2056 int h_code = 0, v_code = 0;
2057
2058 switch( aAttributes.m_Halign )
2059 {
2060 case GR_TEXT_H_ALIGN_LEFT: h_code = 0; break;
2061 case GR_TEXT_H_ALIGN_CENTER: h_code = 1; break;
2062 case GR_TEXT_H_ALIGN_RIGHT: h_code = 2; break;
2063 case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
2064 }
2065
2066 switch( aAttributes.m_Valign )
2067 {
2068 case GR_TEXT_V_ALIGN_TOP: v_code = 3; break;
2069 case GR_TEXT_V_ALIGN_CENTER: v_code = 2; break;
2070 case GR_TEXT_V_ALIGN_BOTTOM: v_code = 1; break;
2071 case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
2072 }
2073
2074 std::string textStyle = "KICAD";
2075 if( aAttributes.m_Bold )
2076 {
2077 if( aAttributes.m_Italic )
2078 textStyle = "KICADBI";
2079 else
2080 textStyle = "KICADB";
2081 }
2082 else if( aAttributes.m_Italic )
2083 textStyle = "KICADI";
2084
2085 // The DXF spec requires two AcDbText subclass markers on TEXT, with group 73
2086 // (vertical alignment) scoped under the second one. The text string (group 1)
2087 // sits between height and rotation inside the first AcDbText scope.
2088 emitEntityHandle( "TEXT", "AcDbText", TO_UTF8( cLayerName ) );
2089
2090 fmt::print( m_outputFile,
2091 " 10\n{}\n 20\n{}\n 30\n0\n"
2092 " 40\n{}\n",
2093 formatCoord( origin_dev.x ),
2094 formatCoord( origin_dev.y ),
2095 formatCoord( size_dev.y ) );
2096
2097 /* There are two issue in emitting the text:
2098 - Our overline character (~) must be converted to the appropriate
2099 control sequence %%O or %%o
2100 - Text encoding in DXF is more or less unspecified since depends on
2101 the DXF declared version, the acad version reading it *and* some
2102 system variables to be put in the header handled only by newer acads
2103 Also before R15 unicode simply is not supported (you need to use
2104 bigfonts which are a massive PITA). Common denominator solution:
2105 use Latin1 (and however someone could choke on it, anyway). Sorry
2106 for the extended latin people. If somewant want to try fixing this
2107 recent version seems to use UTF-8 (and not UCS2 like the rest of
2108 Windows)
2109
2110 XXX Actually there is a *third* issue: older DXF formats are limited
2111 to 255 bytes records (it was later raised to 2048); since I'm lazy
2112 and text so long is not probable I just don't implement this rule.
2113 If someone is interested in fixing this, you have to emit the first
2114 partial lines with group code 3 (max 250 bytes each) and then finish
2115 with a group code 1 (less than 250 bytes). The DXF refs explains it
2116 in no more details...
2117 */
2118
2119 int braceNesting = 0;
2120 int overbarDepth = -1;
2121
2122 fmt::print( m_outputFile, " 1\n" );
2123
2124 for( unsigned int i = 0; i < aText.length(); i++ )
2125 {
2126 /* Here I do a bad thing: writing the output one byte at a time!
2127 but today I'm lazy and I have no idea on how to coerce a Unicode
2128 wxString to spit out latin1 encoded text ...
2129
2130 At least stdio is *supposed* to do output buffering, so there is
2131 hope is not too slow */
2132 wchar_t ch = aText[i];
2133
2134 if( ch > 255 )
2135 {
2136 // I can't encode this...
2137 putc( '?', m_outputFile );
2138 }
2139 else
2140 {
2141 if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
2142 {
2143 fmt::print( m_outputFile, "%%o" );
2144 overbarDepth = braceNesting;
2145
2146 // Skip the '{'
2147 i++;
2148 continue;
2149 }
2150 else if( aText[i] == '{' )
2151 {
2152 braceNesting++;
2153 }
2154 else if( aText[i] == '}' )
2155 {
2156 if( braceNesting > 0 )
2157 braceNesting--;
2158
2159 if( braceNesting == overbarDepth )
2160 {
2161 fmt::print( m_outputFile, "%%O" );
2162 overbarDepth = -1;
2163 continue;
2164 }
2165 }
2166
2167 putc( ch, m_outputFile );
2168 }
2169 }
2170
2171 fmt::print( m_outputFile, "\n" );
2172
2173 // Remaining AcDbText fields, plus the second AcDbText marker scoping vertical align.
2174 fmt::print( m_outputFile,
2175 " 50\n{:.8f}\n"
2176 " 41\n{}\n"
2177 " 51\n{:.8f}\n"
2178 " 7\n{}\n"
2179 " 71\n{}\n"
2180 " 72\n{}\n"
2181 " 11\n{}\n 21\n{}\n 31\n0\n"
2182 "100\nAcDbText\n"
2183 " 73\n{}\n",
2184 aAttributes.m_Angle.GetAngle().AsDegrees(),
2185 formatCoord( fabs( size_dev.x / size_dev.y ) ),
2186 aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0,
2187 textStyle,
2188 aAttributes.m_Mirrored ? 2 : 0,
2189 h_code,
2190 formatCoord( origin_dev.x ),
2191 formatCoord( origin_dev.y ),
2192 v_code );
2193}
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 std::string 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.
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
void FlashPadCustom(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, SHAPE_POLY_SET *aPolygons, void *aData) override
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.
void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the scale/position for the DXF plot.
void ThickArc(const VECTOR2D &aCentre, const EDA_ANGLE &aStAngle, const EDA_ANGLE &aAngle, double aRadius, int aWidth, void *aData) override
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.
void FlashPadOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
DXF oval pad: always done in sketch mode.
void Text(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const EDA_ANGLE &aOrient, const VECTOR2I &aSize, GR_TEXT_H_ALIGN_T aH_justify, 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.
std::string emitEntityHandle(const char *aEntityType, const char *aSubclass, const std::string &aLayerName, const std::string &aOwner="")
std::string m_plotStyleNameDictHandle
void FlashPadRect(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
DXF rectangular pad: always done in sketch mode.
void ThickRect(const VECTOR2I &p1, const VECTOR2I &p2, int width, void *aData) override
void FlashPadRoundRect(const VECTOR2I &aPadPos, const VECTOR2I &aSize, int aCornerRadius, const EDA_ANGLE &aOrient, void *aData) override
std::vector< DXF_LAYOUT > m_dxfLayouts
void ThickPoly(const SHAPE_POLY_SET &aPoly, int aWidth, void *aData) override
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.
void FlashPadTrapez(const VECTOR2I &aPadPos, const VECTOR2I *aCorners, const EDA_ANGLE &aPadOrient, void *aData) override
DXF trapezoidal pad: only sketch mode is supported.
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...
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...
void FilledCircle(const VECTOR2I &pos, int diametre, void *aData) override
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
void Rect(const VECTOR2I &p1, const VECTOR2I &p2, FILL_T fill, int width, int aCornerRadius=0) override
DXF rectangle: fill not supported.
bool EndPlot() override
void FlashRegularPolygon(const VECTOR2I &aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE &aOrient, void *aData) override
Flash a regular polygon.
void PenTo(const VECTOR2I &pos, char plume) override
Moveto/lineto primitive, moves the 'pen' to the specified direction.
void SetColor(const COLOR4D &color) override
The DXF exporter handles 'colors' as layers...
int FindNearestLegacyColor(int aR, int aG, int aB)
void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData) override
std::string m_plotStyleNormalHandle
double m_unitScalingFactor
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:248
std::vector< std::pair< PCB_LAYER_ID, wxString > > m_layersToExport
Definition plotter.h:764
bool m_plotMirror
Definition plotter.h:736
void MoveTo(const VECTOR2I &pos)
Definition plotter.h:325
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:389
void FinishTo(const VECTOR2I &pos)
Definition plotter.h:335
double m_iuPerDeviceUnit
Definition plotter.h:733
PCB_LAYER_ID m_layer
Definition plotter.h:766
RENDER_SETTINGS * RenderSettings()
Definition plotter.h:169
VECTOR2I m_plotOffset
Definition plotter.h:735
VECTOR2I m_penLastpos
Definition plotter.h:749
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:757
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:291
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition plotter.h:725
bool GetColorMode() const
Definition plotter.h:166
FILE * m_outputFile
Output file.
Definition plotter.h:742
void LineTo(const VECTOR2I &pos)
Definition plotter.h:330
void PenFinish()
Definition plotter.h:341
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:618
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:548
double m_IUsPerDecimil
Definition plotter.h:731
bool m_colorMode
Definition plotter.h:745
virtual DXF_OUTLINE_MODE GetDXFPlotMode() const
Definition plotter.h:111
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:53
DXF_LAYER_OUTPUT_MODE
Specifies the output mode for the DXF layer.
Definition plotter.h:124
@ SKETCH
Definition plotter.h:83
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