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