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 <ranges>
26#include <macros.h>
27#include <string_utils.h>
29#include <geometry/shape_rect.h>
30#include <trigo.h>
31#include <fmt/core.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,
1473 void* aData )
1474{
1475 if( aCornerList.size() <= 1 )
1476 return;
1477
1478 unsigned last = aCornerList.size() - 1;
1479
1480 // Plot outlines with lines (thickness = 0) to define the polygon
1481 if( aWidth <= 0 || aFill == FILL_T::NO_FILL )
1482 {
1483 MoveTo( aCornerList[0] );
1484
1485 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1486 LineTo( aCornerList[ii] );
1487
1488 // Close polygon if 'fill' requested
1489 if( aFill != FILL_T::NO_FILL )
1490 {
1491 if( aCornerList[last] != aCornerList[0] )
1492 LineTo( aCornerList[0] );
1493 }
1494
1495 PenFinish();
1496 return;
1497 }
1498
1499 // The polygon outline has thickness, and is filled
1500 // Build and plot the polygon which contains the initial
1501 // polygon and its thick outline
1502 SHAPE_POLY_SET bufferOutline;
1503 SHAPE_POLY_SET bufferPolybase;
1504
1505 bufferPolybase.NewOutline();
1506
1507 // enter outline as polygon:
1508 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1509 {
1510 TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
1512 }
1513
1514 // enter the initial polygon:
1515 for( const VECTOR2I& corner : aCornerList )
1516 bufferPolybase.Append( corner );
1517
1518 // Merge polygons to build the polygon which contains the initial
1519 // polygon and its thick outline
1520
1521 // create the outline which contains thick outline:
1522 bufferPolybase.BooleanAdd( bufferOutline );
1523 bufferPolybase.Fracture();
1524
1525 if( bufferPolybase.OutlineCount() < 1 ) // should not happen
1526 return;
1527
1528 const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
1529
1530 if( path.PointCount() < 2 ) // should not happen
1531 return;
1532
1533 // Now, output the final polygon to DXF file:
1534 last = path.PointCount() - 1;
1535 VECTOR2I point = path.CPoint( 0 );
1536
1537 VECTOR2I startPoint( point.x, point.y );
1538 MoveTo( startPoint );
1539
1540 for( int ii = 1; ii < path.PointCount(); ii++ )
1541 {
1542 point = path.CPoint( ii );
1543 LineTo( VECTOR2I( point.x, point.y ) );
1544 }
1545
1546 // Close polygon, if needed
1547 point = path.CPoint( last );
1548 VECTOR2I endPoint( point.x, point.y );
1549
1550 if( endPoint != startPoint )
1551 LineTo( startPoint );
1552
1553 PenFinish();
1554}
1555
1556
1557std::vector<VECTOR2I> arcPts( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
1558 const EDA_ANGLE& aAngle, double aRadius )
1559{
1560 std::vector<VECTOR2I> pts;
1561
1562 /*
1563 * Arcs are not so easily approximated by beziers (in the general case), so we approximate
1564 * them in the old way
1565 */
1566 EDA_ANGLE startAngle = -aStartAngle;
1567 EDA_ANGLE endAngle = startAngle - aAngle;
1568 VECTOR2I start;
1569 VECTOR2I end;
1570 const EDA_ANGLE delta( 5, DEGREES_T ); // increment to draw circles
1571
1572 if( startAngle > endAngle )
1573 std::swap( startAngle, endAngle );
1574
1575 // Usual trig arc plotting routine...
1576 start.x = KiROUND( aCenter.x + aRadius * ( -startAngle ).Cos() );
1577 start.y = KiROUND( aCenter.y + aRadius * ( -startAngle ).Sin() );
1578 pts.emplace_back( start );
1579
1580 for( EDA_ANGLE ii = startAngle + delta; ii < endAngle; ii += delta )
1581 {
1582 end.x = KiROUND( aCenter.x + aRadius * ( -ii ).Cos() );
1583 end.y = KiROUND( aCenter.y + aRadius * ( -ii ).Sin() );
1584 pts.emplace_back( end );
1585 }
1586
1587 end.x = KiROUND( aCenter.x + aRadius * ( -endAngle ).Cos() );
1588 end.y = KiROUND( aCenter.y + aRadius * ( -endAngle ).Sin() );
1589 pts.emplace_back( end );
1590
1591 return pts;
1592}
1593
1594
1595void DXF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aLineChain, FILL_T aFill, int aWidth, void* aData )
1596{
1597 std::set<size_t> handledArcs;
1598 std::vector<VECTOR2I> cornerList;
1599
1600 for( int ii = 0; ii < aLineChain.SegmentCount(); ++ii )
1601 {
1602 if( aLineChain.IsArcSegment( ii ) )
1603 {
1604 size_t arcIndex = aLineChain.ArcIndex( ii );
1605
1606 if( !handledArcs.contains( arcIndex ) )
1607 {
1608 handledArcs.insert( arcIndex );
1609 const SHAPE_ARC& arc( aLineChain.Arc( arcIndex ) );
1610 std::vector<VECTOR2I> pts = arcPts( arc.GetCenter(), arc.GetStartAngle(),
1611 arc.GetCentralAngle(), arc.GetRadius() );
1612
1613 for( const VECTOR2I& pt : std::ranges::reverse_view( pts ) )
1614 cornerList.emplace_back( pt );
1615 }
1616 }
1617 else
1618 {
1619 const SEG& seg( aLineChain.Segment( ii ) );
1620 cornerList.emplace_back( seg.A );
1621 cornerList.emplace_back( seg.B );
1622 }
1623 }
1624
1625 if( aLineChain.IsClosed() && cornerList.front() != cornerList.back() )
1626 cornerList.emplace_back( aLineChain.CPoint( 0 ) );
1627
1628 PlotPoly( cornerList, aFill, aWidth, aData );
1629}
1630
1631
1632void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
1633{
1634 wxASSERT( m_outputFile );
1635
1636 if( plume == 'Z' )
1637 {
1638 return;
1639 }
1640
1641 VECTOR2D pos_dev = userToDeviceCoordinates( pos );
1642 VECTOR2D pen_lastpos_dev = userToDeviceCoordinates( m_penLastpos );
1643
1644 if( m_penLastpos != pos && plume == 'D' )
1645 {
1648
1649 // DXF LINE
1651 std::string layer = TO_UTF8( cLayerName );
1652 const char* lname = getDXFLineType( static_cast<LINE_STYLE>( m_currentLineType ) );
1653
1654 // The linetype name (6) sits on the AcDbEntity side, before the AcDbLine
1655 // marker. Emitted inline so group 6 can interleave between the two markers.
1656 fmt::print( m_outputFile,
1657 " 0\nLINE\n"
1658 " 5\n{}\n"
1659 "330\n{}\n"
1660 "100\nAcDbEntity\n"
1661 " 8\n{}\n"
1662 " 6\n{}\n"
1663 "100\nAcDbLine\n"
1664 " 10\n{}\n 20\n{}\n 30\n0\n 11\n{}\n 21\n{}\n 31\n0\n",
1665 nextHandle(), m_modelSpaceHandle, layer, lname,
1666 formatCoord( pen_lastpos_dev.x ),
1667 formatCoord( pen_lastpos_dev.y ),
1668 formatCoord( pos_dev.x ),
1669 formatCoord( pos_dev.y ) );
1670 }
1671
1672 m_penLastpos = pos;
1673}
1674
1675
1676void DXF_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
1677{
1678 wxASSERT( aLineStyle >= LINE_STYLE::FIRST_TYPE
1679 && aLineStyle <= LINE_STYLE::LAST_TYPE );
1680
1681 m_currentLineType = aLineStyle;
1682}
1683
1684
1685void DXF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
1686 const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
1687{
1688 wxASSERT( m_outputFile );
1689
1690 if( aRadius <= 0 )
1691 return;
1692
1693 EDA_ANGLE startAngle = -aStartAngle;
1694 EDA_ANGLE endAngle = startAngle - aAngle;
1695
1696 // In DXF, arcs are drawn CCW.
1697 // If startAngle > endAngle, it is CW. So transform it to CCW
1698 if( endAngle < startAngle )
1699 std::swap( startAngle, endAngle );
1700
1701 VECTOR2D centre_device = userToDeviceCoordinates( aCenter );
1702 double radius_device = userToDeviceSize( aRadius );
1703
1704 // ARC carries two subclass markers, AcDbCircle (centre and radius) then AcDbArc
1705 // (angle pair). Reversing them trips AutoCAD's AcDb validator.
1707 std::string layer = TO_UTF8( cLayerName );
1708
1709 emitEntityHandle( "ARC", "AcDbCircle", layer );
1710 fmt::print( m_outputFile,
1711 " 10\n{}\n 20\n{}\n 30\n0\n 40\n{}\n"
1712 "100\nAcDbArc\n"
1713 " 50\n{:.8f}\n 51\n{:.8f}\n",
1714 formatCoord( centre_device.x ),
1715 formatCoord( centre_device.y ),
1716 formatCoord( radius_device ),
1717 startAngle.AsDegrees(),
1718 endAngle.AsDegrees() );
1719}
1720
1721
1722void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth, void* aData )
1723{
1724 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1725
1726 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1727 {
1728 std::vector<VECTOR2I> cornerList;
1729 SHAPE_POLY_SET outlineBuffer;
1730 TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(),
1731 ERROR_INSIDE );
1732 const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );
1733
1734 cornerList.reserve( path.PointCount() );
1735
1736 for( int jj = 0; jj < path.PointCount(); jj++ )
1737 cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );
1738
1739 // Ensure the polygon is closed
1740 if( cornerList[0] != cornerList[cornerList.size() - 1] )
1741 cornerList.push_back( cornerList[0] );
1742
1743 PlotPoly( cornerList, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1744 }
1745 else
1746 {
1747 MoveTo( aStart );
1748 FinishTo( aEnd );
1749 }
1750}
1751
1752
1753void DXF_PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle,
1754 const EDA_ANGLE& aAngle, double aRadius, int aWidth, void* aData )
1755{
1756 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1757
1758 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1759 {
1760 Arc( centre, aStartAngle, aAngle, aRadius - aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1761 Arc( centre, aStartAngle, aAngle, aRadius + aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1762 }
1763 else
1764 {
1765 Arc( centre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1766 }
1767}
1768
1769
1770void DXF_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1771{
1772 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1773
1774 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1775 {
1776 VECTOR2I offsetp1( p1.x - width/2, p1.y - width/2 );
1777 VECTOR2I offsetp2( p2.x + width/2, p2.y + width/2 );
1778 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1779
1780 offsetp1.x += width;
1781 offsetp1.y += width;
1782 offsetp2.x -= width;
1783 offsetp2.y -= width;
1784 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1785 }
1786 else
1787 {
1788 Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1789 }
1790}
1791
1792
1793void DXF_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1794{
1795 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1796
1797 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1798 {
1799 Circle( pos, diametre - width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1800 Circle( pos, diametre + width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1801 }
1802 else
1803 {
1804 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1805 }
1806}
1807
1808
1809void DXF_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1810{
1811 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1812
1813 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1814 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1815 else
1816 Circle( pos, diametre, FILL_T::FILLED_SHAPE, 0 );
1817}
1818
1819
1820void DXF_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1821{
1822 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1823
1824 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1825 {
1826 SHAPE_POLY_SET outline = aPoly.CloneDropTriangulation();
1829
1830 outline = aPoly.CloneDropTriangulation();
1833 }
1834 else
1835 {
1836 PLOTTER::PlotPoly( aPoly.COutline( 0 ), FILL_T::NO_FILL, aWidth, aData );
1837 }
1838}
1839
1840
1841void DXF_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1842 const EDA_ANGLE& aOrient, void* aData )
1843{
1844 wxASSERT( m_outputFile );
1845
1846 VECTOR2I size( aSize );
1847 EDA_ANGLE orient( aOrient );
1848
1849 /* The chip is reduced to an oval tablet with size.y > size.x
1850 * (Oval vertical orientation 0) */
1851 if( size.x > size.y )
1852 {
1853 std::swap( size.x, size.y );
1854 orient += ANGLE_90;
1855 }
1856
1857 ThickOval( aPos, size, orient, DXF_LINE_WIDTH, aData );
1858}
1859
1860
1861void DXF_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1862{
1863 wxASSERT( m_outputFile );
1864 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1865}
1866
1867
1868void DXF_PLOTTER::FlashPadRect( const VECTOR2I& aPos, const VECTOR2I& aPadSize,
1869 const EDA_ANGLE& aOrient, void* aData )
1870{
1871 wxASSERT( m_outputFile );
1872
1873 VECTOR2I size, start, end;
1874
1875 size.x = aPadSize.x / 2;
1876 size.y = aPadSize.y / 2;
1877
1878 if( size.x < 0 )
1879 size.x = 0;
1880
1881 if( size.y < 0 )
1882 size.y = 0;
1883
1884 // If a dimension is zero, the trace is reduced to 1 line
1885 if( size.x == 0 )
1886 {
1887 start = VECTOR2I( aPos.x, aPos.y - size.y );
1888 end = VECTOR2I( aPos.x, aPos.y + size.y );
1889 RotatePoint( start, aPos, aOrient );
1890 RotatePoint( end, aPos, aOrient );
1891 MoveTo( start );
1892 FinishTo( end );
1893 return;
1894 }
1895
1896 if( size.y == 0 )
1897 {
1898 start = VECTOR2I( aPos.x - size.x, aPos.y );
1899 end = VECTOR2I( aPos.x + size.x, aPos.y );
1900 RotatePoint( start, aPos, aOrient );
1901 RotatePoint( end, aPos, aOrient );
1902 MoveTo( start );
1903 FinishTo( end );
1904 return;
1905 }
1906
1907 start = VECTOR2I( aPos.x - size.x, aPos.y - size.y );
1908 RotatePoint( start, aPos, aOrient );
1909 MoveTo( start );
1910
1911 end = VECTOR2I( aPos.x - size.x, aPos.y + size.y );
1912 RotatePoint( end, aPos, aOrient );
1913 LineTo( end );
1914
1915 end = VECTOR2I( aPos.x + size.x, aPos.y + size.y );
1916 RotatePoint( end, aPos, aOrient );
1917 LineTo( end );
1918
1919 end = VECTOR2I( aPos.x + size.x, aPos.y - size.y );
1920 RotatePoint( end, aPos, aOrient );
1921 LineTo( end );
1922
1923 FinishTo( start );
1924}
1925
1926
1927void DXF_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1928 int aCornerRadius, const EDA_ANGLE& aOrient, void* aData )
1929{
1930 SHAPE_POLY_SET outline;
1931 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0, 0,
1933
1934 // TransformRoundRectToPolygon creates only one convex polygon
1935 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1936
1937 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1938
1939 for( int ii = 1; ii < poly.PointCount(); ++ii )
1940 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1941
1942 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1943}
1944
1945
1946void DXF_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1947 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1948 void* aData )
1949{
1950 for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
1951 {
1952 SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
1953
1954 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1955
1956 for( int ii = 1; ii < poly.PointCount(); ++ii )
1957 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1958
1959 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1960 }
1961}
1962
1963
1964void DXF_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1965 const EDA_ANGLE& aPadOrient, void* aData )
1966{
1967 wxASSERT( m_outputFile );
1968 VECTOR2I coord[4]; /* coord actual corners of a trapezoidal trace */
1969
1970 for( int ii = 0; ii < 4; ii++ )
1971 {
1972 coord[ii] = aCorners[ii];
1973 RotatePoint( coord[ii], aPadOrient );
1974 coord[ii] += aPadPos;
1975 }
1976
1977 // Plot edge:
1978 MoveTo( coord[0] );
1979 LineTo( coord[1] );
1980 LineTo( coord[2] );
1981 LineTo( coord[3] );
1982 FinishTo( coord[0] );
1983}
1984
1985
1986void DXF_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aRadius, int aCornerCount,
1987 const EDA_ANGLE& aOrient, void* aData )
1988{
1989 // Do nothing
1990 wxASSERT( 0 );
1991}
1992
1993
2001bool containsNonAsciiChars( const wxString& string )
2002{
2003 for( unsigned i = 0; i < string.length(); i++ )
2004 {
2005 wchar_t ch = string[i];
2006
2007 if( ch > 255 )
2008 return true;
2009 }
2010 return false;
2011}
2012
2013
2014void DXF_PLOTTER::Text( const VECTOR2I& aPos,
2015 const COLOR4D& aColor,
2016 const wxString& aText,
2017 const EDA_ANGLE& aOrient,
2018 const VECTOR2I& aSize,
2019 enum GR_TEXT_H_ALIGN_T aH_justify,
2020 enum GR_TEXT_V_ALIGN_T aV_justify,
2021 int aWidth,
2022 bool aItalic,
2023 bool aBold,
2024 bool aMultilineAllowed,
2025 KIFONT::FONT* aFont,
2026 const KIFONT::METRICS& aFontMetrics,
2027 void* aData )
2028{
2029 // Fix me: see how to use DXF text mode for multiline texts
2030 if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
2031 aMultilineAllowed = false; // the text has only one line.
2032
2033 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
2034
2035 if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
2036 {
2037 // output text as graphics.
2038 // Perhaps multiline texts could be handled as DXF text entity
2039 // but I do not want spend time about this (JPC)
2040 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
2041 aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
2042 }
2043 else
2044 {
2045 TEXT_ATTRIBUTES attrs;
2046 attrs.m_Halign = aH_justify;
2047 attrs.m_Valign =aV_justify;
2048 attrs.m_StrokeWidth = aWidth;
2049 attrs.m_Angle = aOrient;
2050 attrs.m_Italic = aItalic;
2051 attrs.m_Bold = aBold;
2052 attrs.m_Mirrored = aSize.x < 0;
2053 attrs.m_Multiline = false;
2054 plotOneLineOfText( aPos, aColor, aText, attrs );
2055 }
2056}
2057
2058
2060 const COLOR4D& aColor,
2061 const wxString& aText,
2062 const TEXT_ATTRIBUTES& aAttributes,
2063 KIFONT::FONT* aFont,
2064 const KIFONT::METRICS& aFontMetrics,
2065 void* aData )
2066{
2067 TEXT_ATTRIBUTES attrs = aAttributes;
2068
2069 // Fix me: see how to use DXF text mode for multiline texts
2070 if( attrs.m_Multiline && !aText.Contains( wxT( "\n" ) ) )
2071 attrs.m_Multiline = false; // the text has only one line.
2072
2073 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
2074
2075 if( m_textAsLines || containsNonAsciiChars( aText ) || attrs.m_Multiline || processSuperSub )
2076 {
2077 // output text as graphics.
2078 // Perhaps multiline texts could be handled as DXF text entity
2079 // but I do not want spend time about that (JPC)
2080 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
2081 }
2082 else
2083 {
2084 plotOneLineOfText( aPos, aColor, aText, attrs );
2085 }
2086}
2087
2088
2089void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor,
2090 const wxString& aText, const TEXT_ATTRIBUTES& aAttributes )
2091{
2092 /* Emit text as a text entity. This loses formatting and shape but it's
2093 more useful as a CAD object */
2094 VECTOR2D origin_dev = userToDeviceCoordinates( aPos );
2095 SetColor( aColor );
2097
2098 VECTOR2D size_dev = userToDeviceSize( aAttributes.m_Size );
2099 int h_code = 0, v_code = 0;
2100
2101 switch( aAttributes.m_Halign )
2102 {
2103 case GR_TEXT_H_ALIGN_LEFT: h_code = 0; break;
2104 case GR_TEXT_H_ALIGN_CENTER: h_code = 1; break;
2105 case GR_TEXT_H_ALIGN_RIGHT: h_code = 2; break;
2106 case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
2107 }
2108
2109 switch( aAttributes.m_Valign )
2110 {
2111 case GR_TEXT_V_ALIGN_TOP: v_code = 3; break;
2112 case GR_TEXT_V_ALIGN_CENTER: v_code = 2; break;
2113 case GR_TEXT_V_ALIGN_BOTTOM: v_code = 1; break;
2114 case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
2115 }
2116
2117 std::string textStyle = "KICAD";
2118 if( aAttributes.m_Bold )
2119 {
2120 if( aAttributes.m_Italic )
2121 textStyle = "KICADBI";
2122 else
2123 textStyle = "KICADB";
2124 }
2125 else if( aAttributes.m_Italic )
2126 textStyle = "KICADI";
2127
2128 // The DXF spec requires two AcDbText subclass markers on TEXT, with group 73
2129 // (vertical alignment) scoped under the second one. The text string (group 1)
2130 // sits between height and rotation inside the first AcDbText scope.
2131 emitEntityHandle( "TEXT", "AcDbText", TO_UTF8( cLayerName ) );
2132
2133 fmt::print( m_outputFile,
2134 " 10\n{}\n 20\n{}\n 30\n0\n"
2135 " 40\n{}\n",
2136 formatCoord( origin_dev.x ),
2137 formatCoord( origin_dev.y ),
2138 formatCoord( size_dev.y ) );
2139
2140 /* There are two issue in emitting the text:
2141 - Our overline character (~) must be converted to the appropriate
2142 control sequence %%O or %%o
2143 - Text encoding in DXF is more or less unspecified since depends on
2144 the DXF declared version, the acad version reading it *and* some
2145 system variables to be put in the header handled only by newer acads
2146 Also before R15 unicode simply is not supported (you need to use
2147 bigfonts which are a massive PITA). Common denominator solution:
2148 use Latin1 (and however someone could choke on it, anyway). Sorry
2149 for the extended latin people. If somewant want to try fixing this
2150 recent version seems to use UTF-8 (and not UCS2 like the rest of
2151 Windows)
2152
2153 XXX Actually there is a *third* issue: older DXF formats are limited
2154 to 255 bytes records (it was later raised to 2048); since I'm lazy
2155 and text so long is not probable I just don't implement this rule.
2156 If someone is interested in fixing this, you have to emit the first
2157 partial lines with group code 3 (max 250 bytes each) and then finish
2158 with a group code 1 (less than 250 bytes). The DXF refs explains it
2159 in no more details...
2160 */
2161
2162 int braceNesting = 0;
2163 int overbarDepth = -1;
2164
2165 fmt::print( m_outputFile, " 1\n" );
2166
2167 for( unsigned int i = 0; i < aText.length(); i++ )
2168 {
2169 /* Here I do a bad thing: writing the output one byte at a time!
2170 but today I'm lazy and I have no idea on how to coerce a Unicode
2171 wxString to spit out latin1 encoded text ...
2172
2173 At least stdio is *supposed* to do output buffering, so there is
2174 hope is not too slow */
2175 wchar_t ch = aText[i];
2176
2177 if( ch > 255 )
2178 {
2179 // I can't encode this...
2180 putc( '?', m_outputFile );
2181 }
2182 else
2183 {
2184 if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
2185 {
2186 fmt::print( m_outputFile, "%%o" );
2187 overbarDepth = braceNesting;
2188
2189 // Skip the '{'
2190 i++;
2191 continue;
2192 }
2193 else if( aText[i] == '{' )
2194 {
2195 braceNesting++;
2196 }
2197 else if( aText[i] == '}' )
2198 {
2199 if( braceNesting > 0 )
2200 braceNesting--;
2201
2202 if( braceNesting == overbarDepth )
2203 {
2204 fmt::print( m_outputFile, "%%O" );
2205 overbarDepth = -1;
2206 continue;
2207 }
2208 }
2209
2210 putc( ch, m_outputFile );
2211 }
2212 }
2213
2214 fmt::print( m_outputFile, "\n" );
2215
2216 // Remaining AcDbText fields, plus the second AcDbText marker scoping vertical align.
2217 fmt::print( m_outputFile,
2218 " 50\n{:.8f}\n"
2219 " 41\n{}\n"
2220 " 51\n{:.8f}\n"
2221 " 7\n{}\n"
2222 " 71\n{}\n"
2223 " 72\n{}\n"
2224 " 11\n{}\n 21\n{}\n 31\n0\n"
2225 "100\nAcDbText\n"
2226 " 73\n{}\n",
2227 aAttributes.m_Angle.AsDegrees(),
2228 formatCoord( fabs( size_dev.x / size_dev.y ) ),
2229 aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0,
2230 textStyle,
2231 aAttributes.m_Mirrored ? 2 : 0,
2232 h_code,
2233 formatCoord( origin_dev.x ),
2234 formatCoord( origin_dev.y ),
2235 v_code );
2236}
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
std::vector< VECTOR2I > arcPts(const VECTOR2D &aCenter, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius)
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:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
static const COLOR4D WHITE
Definition color4d.h:401
static const COLOR4D BLACK
Definition color4d.h:402
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:389
double g
Green component.
Definition color4d.h:390
double b
Blue component.
Definition color4d.h:391
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:240
std::vector< std::pair< PCB_LAYER_ID, wxString > > m_layersToExport
Definition plotter.h:725
bool m_plotMirror
Definition plotter.h:697
void MoveTo(const VECTOR2I &pos)
Definition plotter.h:305
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:483
void FinishTo(const VECTOR2I &pos)
Definition plotter.h:315
double m_iuPerDeviceUnit
Definition plotter.h:694
PCB_LAYER_ID m_layer
Definition plotter.h:727
RENDER_SETTINGS * RenderSettings()
Definition plotter.h:164
VECTOR2I m_plotOffset
Definition plotter.h:696
VECTOR2I m_penLastpos
Definition plotter.h:710
virtual VECTOR2D userToDeviceCoordinates(const VECTOR2I &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition plotter.cpp:89
VECTOR2I m_paperSize
Definition plotter.h:718
virtual VECTOR2D userToDeviceSize(const VECTOR2I &size)
Modify size according to the plotter scale factors (VECTOR2I version, returns a VECTOR2D).
Definition plotter.cpp:114
int GetPlotterArcHighDef() const
Definition plotter.h:271
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition plotter.h:686
bool GetColorMode() const
Definition plotter.h:161
FILE * m_outputFile
Output file.
Definition plotter.h:703
void LineTo(const VECTOR2I &pos)
Definition plotter.h:310
void PenFinish()
Definition plotter.h:321
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:712
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:642
double m_IUsPerDecimil
Definition plotter.h:692
bool m_colorMode
Definition plotter.h:706
virtual DXF_OUTLINE_MODE GetDXFPlotMode() const
Definition plotter.h:106
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
EDA_ANGLE GetCentralAngle() const
Get the "central angle" of the arc - this is the angle at the point of the "pie slice".
double GetRadius() const
EDA_ANGLE GetStartAngle() const
const VECTOR2I & GetCenter() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
bool IsArcSegment(size_t aSegment) const
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:413
@ DEGREES_T
Definition eda_angle.h:31
FILL_T
Definition eda_shape.h:59
@ NO_FILL
Definition eda_shape.h:60
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
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:48
DXF_LAYER_OUTPUT_MODE
Specifies the output mode for the DXF layer.
Definition plotter.h:119
@ SKETCH
Definition plotter.h:78
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
int delta
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