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