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