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
760bool DXF_PLOTTER::StartPlot( const wxString& aPageNumber )
761{
762 wxASSERT( m_outputFile );
763
764 // DXF HEADER - Boilerplate
765 // Defines the minimum for drawing i.e. the angle system and the
766 // 4 linetypes (CONTINUOUS, DOTDASH, DASHED and DOTTED)
767 fmt::print( m_outputFile,
768 " 0\n"
769 "SECTION\n"
770 " 2\n"
771 "HEADER\n"
772 " 9\n"
773 "$ANGBASE\n"
774 " 50\n"
775 "0.0\n"
776 " 9\n"
777 "$ANGDIR\n"
778 " 70\n"
779 "1\n"
780 " 9\n"
781 "$MEASUREMENT\n"
782 " 70\n"
783 "{}\n"
784 " 9\n"
785 "$INSUNITS\n"
786 " 70\n"
787 "{}\n"
788 " 0\n"
789 "ENDSEC\n"
790 " 0\n"
791 "SECTION\n"
792 " 2\n"
793 "TABLES\n"
794 " 0\n"
795 "TABLE\n"
796 " 2\n"
797 "LTYPE\n"
798 " 70\n"
799 "4\n"
800 " 0\n"
801 "LTYPE\n"
802 " 5\n"
803 "40F\n"
804 " 2\n"
805 "CONTINUOUS\n"
806 " 70\n"
807 "0\n"
808 " 3\n"
809 "Solid line\n"
810 " 72\n"
811 "65\n"
812 " 73\n"
813 "0\n"
814 " 40\n"
815 "0.0\n"
816 " 0\n"
817 "LTYPE\n"
818 " 5\n"
819 "410\n"
820 " 2\n"
821 "DASHDOT\n"
822 " 70\n"
823 "0\n"
824 " 3\n"
825 "Dash Dot ____ _ ____ _\n"
826 " 72\n"
827 "65\n"
828 " 73\n"
829 "4\n"
830 " 40\n"
831 "2.0\n"
832 " 49\n"
833 "1.25\n"
834 " 49\n"
835 "-0.25\n"
836 " 49\n"
837 "0.25\n"
838 " 49\n"
839 "-0.25\n"
840 " 0\n"
841 "LTYPE\n"
842 " 5\n"
843 "411\n"
844 " 2\n"
845 "DASHED\n"
846 " 70\n"
847 "0\n"
848 " 3\n"
849 "Dashed __ __ __ __ __\n"
850 " 72\n"
851 "65\n"
852 " 73\n"
853 "2\n"
854 " 40\n"
855 "0.75\n"
856 " 49\n"
857 "0.5\n"
858 " 49\n"
859 "-0.25\n"
860 " 0\n"
861 "LTYPE\n"
862 " 5\n"
863 "43B\n"
864 " 2\n"
865 "DOTTED\n"
866 " 70\n"
867 "0\n"
868 " 3\n"
869 "Dotted . . . .\n"
870 " 72\n"
871 "65\n"
872 " 73\n"
873 "2\n"
874 " 40\n"
875 "0.2\n"
876 " 49\n"
877 "0.0\n"
878 " 49\n"
879 "-0.2\n"
880 " 0\n"
881 "ENDTAB\n",
883
884 // Text styles table
885 // Defines 4 text styles, one for each bold/italic combination
886 fmt::print( m_outputFile,
887 " 0\n"
888 "TABLE\n"
889 " 2\n"
890 "STYLE\n"
891 " 70\n"
892 "4\n" );
893
894 static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"};
895
896 for(int i = 0; i < 4; i++ )
897 {
898 fmt::print( m_outputFile,
899 " 0\n"
900 "STYLE\n"
901 " 2\n"
902 "{}\n" // Style name
903 " 70\n"
904 "0\n" // Standard flags
905 " 40\n"
906 "0\n" // Non-fixed height text
907 " 41\n"
908 "1\n" // Width factor (base)
909 " 42\n"
910 "1\n" // Last height (mandatory)
911 " 50\n"
912 "{:g}\n" // Oblique angle
913 " 71\n"
914 "0\n" // Generation flags (default)
915 " 3\n"
916 // The standard ISO font (when kicad is build with it
917 // the dxf text in acad matches *perfectly*)
918 "isocp.shx\n", // Font name (when not bigfont)
919 // Apply a 15 degree angle to italic text
920 style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
921 }
922
923 int numLayers = static_cast<int>( !m_layersToExport.empty() ? m_layersToExport.size() : static_cast<int>(DXF_COLOR_T::NBCOLORS) );
924
925 // If printing in monochrome, only output the black layer
926 if( !GetColorMode() && m_layersToExport.empty() )
927 numLayers = 1;
928
929
930 // Layer table - one layer per color
931 fmt::print( m_outputFile,
932 " 0\n"
933 "ENDTAB\n"
934 " 0\n"
935 "TABLE\n"
936 " 2\n"
937 "LAYER\n"
938 " 70\n"
939 "{}\n", (int)numLayers );
940
941 /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
942
943 - The primary colors (1 - 9)
944 - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
945 - Greys (251 - 255)
946 */
947
948 wxString layerName;
949 int colorNumber;
950
951 bool hasActualColor = false;
952 COLOR4D actualColor;
953
954 for( int i = 0; i < numLayers; i++ )
955 {
956 if( !m_layersToExport.empty() )
957 {
960
961 auto it = std::find_if( std::begin( acad_dxf_color_names ), std::end( acad_dxf_color_names ),
962 [colorName](const auto& layer)
963 {
964 return std::strcmp( layer.name, colorName.ToStdString().c_str() ) == 0;
965 });
966
967 if( it != std::end( acad_dxf_color_names ) )
968 colorNumber = it->index;
969 else
970 colorNumber = 7; // Default to white/black
971
972 actualColor = RenderSettings()->GetLayerColor( m_layersToExport.at( i ).first );
973 hasActualColor = true;
974 }
975 else
976 {
977 layerName = wxString( acad_dxf_color_names[i].name );
979 }
980
981 fmt::print( m_outputFile,
982 " 0\n"
983 "LAYER\n"
984 " 2\n"
985 "{}\n" // Layer name
986 " 70\n"
987 "0\n" // Standard flags
988 " 62\n"
989 "{}\n", // Color number
990 TO_UTF8( layerName ),
991 colorNumber );
992
993 if( hasActualColor )
994 {
995 // Add the true color value as an extended data entry
996 int r = static_cast<int>( actualColor.r * 255 );
997 int g = static_cast<int>( actualColor.g * 255 );
998 int b = static_cast<int>( actualColor.b * 255 );
999
1000 int trueColorValue = ( r << 16 ) | ( g << 8 ) | b;
1001
1002 fmt::print( m_outputFile,
1003 " 420\n"
1004 "{}\n",
1005 trueColorValue );
1006 }
1007
1008 fmt::print( m_outputFile,
1009 " 6\n"
1010 "CONTINUOUS\n");// Linetype name
1011 }
1012
1013 // End of layer table, begin entities
1014 fmt::print( m_outputFile,
1015 " 0\n"
1016 "ENDTAB\n"
1017 " 0\n"
1018 "ENDSEC\n"
1019 " 0\n"
1020 "SECTION\n"
1021 " 2\n"
1022 "ENTITIES\n" );
1023
1024 return true;
1025}
1026
1027
1029{
1030 wxASSERT( m_outputFile );
1031
1032 // DXF FOOTER
1033 fmt::print( m_outputFile,
1034 " 0\n"
1035 "ENDSEC\n"
1036 " 0\n"
1037 "EOF\n" );
1038 fclose( m_outputFile );
1039 m_outputFile = nullptr;
1040
1041 return true;
1042}
1043
1044
1045void DXF_PLOTTER::SetColor( const COLOR4D& color )
1046{
1047 if( ( m_colorMode )
1048 || ( color == COLOR4D::BLACK )
1049 || ( color == COLOR4D::WHITE ) )
1050 {
1051 m_currentColor = color;
1052 }
1053 else
1054 {
1056 }
1057}
1058
1059
1060void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
1061 int aCornerRadius )
1062{
1063 wxASSERT( m_outputFile );
1064
1065 if( aCornerRadius > 0 )
1066 {
1067 BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
1068 box.Normalize();
1069 SHAPE_RECT rect( box );
1070 rect.SetRadius( aCornerRadius );
1071 PlotPoly( rect.Outline(), fill, width, nullptr );
1072 return;
1073 }
1074
1075 if( p1 != p2 )
1076 {
1077 MoveTo( p1 );
1078 LineTo( VECTOR2I( p1.x, p2.y ) );
1079 LineTo( VECTOR2I( p2.x, p2.y ) );
1080 LineTo( VECTOR2I( p2.x, p1.y ) );
1081 FinishTo( VECTOR2I( p1.x, p1.y ) );
1082 }
1083 else
1084 {
1085 // Draw as a point
1087
1088 VECTOR2D point_dev = userToDeviceCoordinates( p1 );
1089
1090 fmt::print( m_outputFile, "0\nPOINT\n8\n{}\n10\n{}\n20\n",
1091 TO_UTF8( cLayerName ),
1092 formatCoord( point_dev.x ),
1093 formatCoord( point_dev.y ) );
1094 }
1095}
1096
1097
1098void DXF_PLOTTER::Circle( const VECTOR2I& centre, int diameter, FILL_T fill, int width )
1099{
1100 wxASSERT( m_outputFile );
1101 double radius = userToDeviceSize( diameter / 2 );
1102 VECTOR2D centre_dev = userToDeviceCoordinates( centre );
1103
1105
1106 if( radius > 0 )
1107 {
1108 if( fill == FILL_T::NO_FILL )
1109 {
1110 fmt::print( m_outputFile, "0\nCIRCLE\n8\n{}\n10\n{}\n20\n{}\n40\n{}\n",
1111 TO_UTF8( cLayerName ),
1112 formatCoord( centre_dev.x ),
1113 formatCoord( centre_dev.y ),
1114 formatCoord( radius ) );
1115 }
1116 else if( fill == FILL_T::FILLED_SHAPE )
1117 {
1118 double r = radius * 0.5;
1119 fmt::print( m_outputFile, "0\nPOLYLINE\n" );
1120 fmt::print( m_outputFile, "8\n{}\n66\n1\n70\n1\n", TO_UTF8( cLayerName ) );
1121 fmt::print( m_outputFile, "40\n{}\n41\n{}\n",
1123 formatCoord( radius ) );
1124 fmt::print( m_outputFile, "0\nVERTEX\n8\n{}\n", TO_UTF8( cLayerName ) );
1125 fmt::print( m_outputFile, "10\n{}\n 20\n{}\n42\n1.0\n",
1126 formatCoord( centre_dev.x-r ),
1127 formatCoord( centre_dev.y ) );
1128 fmt::print( m_outputFile, "0\nVERTEX\n8\n{}\n", TO_UTF8( cLayerName ) );
1129 fmt::print( m_outputFile, "10\n{}\n 20\n{}\n42\n1.0\n",
1130 formatCoord( centre_dev.x+r ),
1131 formatCoord( centre_dev.y ) );
1132 fmt::print( m_outputFile, "0\nSEQEND\n" );
1133 }
1134 }
1135 else
1136 {
1137 // Draw as a point
1138 fmt::print( m_outputFile, "0\nPOINT\n8\n{}\n10\n{}\n20\n{}\n", TO_UTF8( cLayerName ),
1139 formatCoord( centre_dev.x ),
1140 formatCoord( centre_dev.y ) );
1141 }
1142}
1143
1144
1145void DXF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth,
1146 void* aData )
1147{
1148 if( aCornerList.size() <= 1 )
1149 return;
1150
1151 unsigned last = aCornerList.size() - 1;
1152
1153 // Plot outlines with lines (thickness = 0) to define the polygon
1154 if( aWidth <= 0 || aFill == FILL_T::NO_FILL )
1155 {
1156 MoveTo( aCornerList[0] );
1157
1158 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1159 LineTo( aCornerList[ii] );
1160
1161 // Close polygon if 'fill' requested
1162 if( aFill != FILL_T::NO_FILL )
1163 {
1164 if( aCornerList[last] != aCornerList[0] )
1165 LineTo( aCornerList[0] );
1166 }
1167
1168 PenFinish();
1169 return;
1170 }
1171
1172 // The polygon outline has thickness, and is filled
1173 // Build and plot the polygon which contains the initial
1174 // polygon and its thick outline
1175 SHAPE_POLY_SET bufferOutline;
1176 SHAPE_POLY_SET bufferPolybase;
1177
1178 bufferPolybase.NewOutline();
1179
1180 // enter outline as polygon:
1181 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1182 {
1183 TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
1185 }
1186
1187 // enter the initial polygon:
1188 for( const VECTOR2I& corner : aCornerList )
1189 bufferPolybase.Append( corner );
1190
1191 // Merge polygons to build the polygon which contains the initial
1192 // polygon and its thick outline
1193
1194 // create the outline which contains thick outline:
1195 bufferPolybase.BooleanAdd( bufferOutline );
1196 bufferPolybase.Fracture();
1197
1198 if( bufferPolybase.OutlineCount() < 1 ) // should not happen
1199 return;
1200
1201 const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
1202
1203 if( path.PointCount() < 2 ) // should not happen
1204 return;
1205
1206 // Now, output the final polygon to DXF file:
1207 last = path.PointCount() - 1;
1208 VECTOR2I point = path.CPoint( 0 );
1209
1210 VECTOR2I startPoint( point.x, point.y );
1211 MoveTo( startPoint );
1212
1213 for( int ii = 1; ii < path.PointCount(); ii++ )
1214 {
1215 point = path.CPoint( ii );
1216 LineTo( VECTOR2I( point.x, point.y ) );
1217 }
1218
1219 // Close polygon, if needed
1220 point = path.CPoint( last );
1221 VECTOR2I endPoint( point.x, point.y );
1222
1223 if( endPoint != startPoint )
1224 LineTo( startPoint );
1225
1226 PenFinish();
1227}
1228
1229
1230std::vector<VECTOR2I> arcPts( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
1231 const EDA_ANGLE& aAngle, double aRadius )
1232{
1233 std::vector<VECTOR2I> pts;
1234
1235 /*
1236 * Arcs are not so easily approximated by beziers (in the general case), so we approximate
1237 * them in the old way
1238 */
1239 EDA_ANGLE startAngle = -aStartAngle;
1240 EDA_ANGLE endAngle = startAngle - aAngle;
1241 VECTOR2I start;
1242 VECTOR2I end;
1243 const EDA_ANGLE delta( 5, DEGREES_T ); // increment to draw circles
1244
1245 if( startAngle > endAngle )
1246 std::swap( startAngle, endAngle );
1247
1248 // Usual trig arc plotting routine...
1249 start.x = KiROUND( aCenter.x + aRadius * ( -startAngle ).Cos() );
1250 start.y = KiROUND( aCenter.y + aRadius * ( -startAngle ).Sin() );
1251 pts.emplace_back( start );
1252
1253 for( EDA_ANGLE ii = startAngle + delta; ii < endAngle; ii += delta )
1254 {
1255 end.x = KiROUND( aCenter.x + aRadius * ( -ii ).Cos() );
1256 end.y = KiROUND( aCenter.y + aRadius * ( -ii ).Sin() );
1257 pts.emplace_back( end );
1258 }
1259
1260 end.x = KiROUND( aCenter.x + aRadius * ( -endAngle ).Cos() );
1261 end.y = KiROUND( aCenter.y + aRadius * ( -endAngle ).Sin() );
1262 pts.emplace_back( end );
1263
1264 return pts;
1265}
1266
1267
1268void DXF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aLineChain, FILL_T aFill, int aWidth, void* aData )
1269{
1270 std::set<size_t> handledArcs;
1271 std::vector<VECTOR2I> cornerList;
1272
1273 for( int ii = 0; ii < aLineChain.SegmentCount(); ++ii )
1274 {
1275 if( aLineChain.IsArcSegment( ii ) )
1276 {
1277 size_t arcIndex = aLineChain.ArcIndex( ii );
1278
1279 if( !handledArcs.contains( arcIndex ) )
1280 {
1281 handledArcs.insert( arcIndex );
1282 const SHAPE_ARC& arc( aLineChain.Arc( arcIndex ) );
1283 std::vector<VECTOR2I> pts = arcPts( arc.GetCenter(), arc.GetStartAngle(),
1284 arc.GetCentralAngle(), arc.GetRadius() );
1285
1286 for( const VECTOR2I& pt : std::ranges::reverse_view( pts ) )
1287 cornerList.emplace_back( pt );
1288 }
1289 }
1290 else
1291 {
1292 const SEG& seg( aLineChain.Segment( ii ) );
1293 cornerList.emplace_back( seg.A );
1294 cornerList.emplace_back( seg.B );
1295 }
1296 }
1297
1298 if( aLineChain.IsClosed() && cornerList.front() != cornerList.back() )
1299 cornerList.emplace_back( aLineChain.CPoint( 0 ) );
1300
1301 PlotPoly( cornerList, aFill, aWidth, aData );
1302}
1303
1304
1305void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
1306{
1307 wxASSERT( m_outputFile );
1308
1309 if( plume == 'Z' )
1310 {
1311 return;
1312 }
1313
1314 VECTOR2D pos_dev = userToDeviceCoordinates( pos );
1315 VECTOR2D pen_lastpos_dev = userToDeviceCoordinates( m_penLastpos );
1316
1317 if( m_penLastpos != pos && plume == 'D' )
1318 {
1321
1322 // DXF LINE
1324
1325 const char* lname = getDXFLineType( static_cast<LINE_STYLE>( m_currentLineType ) );
1326 fmt::print( m_outputFile, "0\nLINE\n8\n{}\n6\n{}\n10\n{}\n20\n{}\n11\n{}\n21\n{}\n",
1327 TO_UTF8( cLayerName ), lname,
1328 formatCoord( pen_lastpos_dev.x ),
1329 formatCoord( pen_lastpos_dev.y ),
1330 formatCoord( pos_dev.x ),
1331 formatCoord( pos_dev.y ) );
1332 }
1333
1334 m_penLastpos = pos;
1335}
1336
1337
1338void DXF_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
1339{
1340 wxASSERT( aLineStyle >= LINE_STYLE::FIRST_TYPE
1341 && aLineStyle <= LINE_STYLE::LAST_TYPE );
1342
1343 m_currentLineType = aLineStyle;
1344}
1345
1346
1347void DXF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
1348 const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
1349{
1350 wxASSERT( m_outputFile );
1351
1352 if( aRadius <= 0 )
1353 return;
1354
1355 EDA_ANGLE startAngle = -aStartAngle;
1356 EDA_ANGLE endAngle = startAngle - aAngle;
1357
1358 // In DXF, arcs are drawn CCW.
1359 // If startAngle > endAngle, it is CW. So transform it to CCW
1360 if( endAngle < startAngle )
1361 std::swap( startAngle, endAngle );
1362
1363 VECTOR2D centre_device = userToDeviceCoordinates( aCenter );
1364 double radius_device = userToDeviceSize( aRadius );
1365
1366 // Emit a DXF ARC entity
1368 fmt::print( m_outputFile,
1369 "0\nARC\n8\n{}\n10\n{}\n20\n{}\n40\n{}\n50\n{:.8f}\n51\n{:.8f}\n",
1370 TO_UTF8( cLayerName ),
1371 formatCoord( centre_device.x ),
1372 formatCoord( centre_device.y ),
1373 formatCoord( radius_device ),
1374 startAngle.AsDegrees(),
1375 endAngle.AsDegrees() );
1376}
1377
1378
1379void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth, void* aData )
1380{
1381 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1382
1383 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1384 {
1385 std::vector<VECTOR2I> cornerList;
1386 SHAPE_POLY_SET outlineBuffer;
1387 TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(),
1388 ERROR_INSIDE );
1389 const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );
1390
1391 cornerList.reserve( path.PointCount() );
1392
1393 for( int jj = 0; jj < path.PointCount(); jj++ )
1394 cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );
1395
1396 // Ensure the polygon is closed
1397 if( cornerList[0] != cornerList[cornerList.size() - 1] )
1398 cornerList.push_back( cornerList[0] );
1399
1400 PlotPoly( cornerList, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1401 }
1402 else
1403 {
1404 MoveTo( aStart );
1405 FinishTo( aEnd );
1406 }
1407}
1408
1409
1410void DXF_PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle,
1411 const EDA_ANGLE& aAngle, double aRadius, int aWidth, void* aData )
1412{
1413 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1414
1415 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1416 {
1417 Arc( centre, aStartAngle, aAngle, aRadius - aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1418 Arc( centre, aStartAngle, aAngle, aRadius + aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1419 }
1420 else
1421 {
1422 Arc( centre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1423 }
1424}
1425
1426
1427void DXF_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1428{
1429 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1430
1431 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1432 {
1433 VECTOR2I offsetp1( p1.x - width/2, p1.y - width/2 );
1434 VECTOR2I offsetp2( p2.x + width/2, p2.y + width/2 );
1435 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1436
1437 offsetp1.x += width;
1438 offsetp1.y += width;
1439 offsetp2.x -= width;
1440 offsetp2.y -= width;
1441 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1442 }
1443 else
1444 {
1445 Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
1446 }
1447}
1448
1449
1450void DXF_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1451{
1452 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1453
1454 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1455 {
1456 Circle( pos, diametre - width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1457 Circle( pos, diametre + width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1458 }
1459 else
1460 {
1461 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1462 }
1463}
1464
1465
1466void DXF_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1467{
1468 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1469
1470 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1471 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1472 else
1473 Circle( pos, diametre, FILL_T::FILLED_SHAPE, 0 );
1474}
1475
1476
1477void DXF_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1478{
1479 const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );
1480
1481 if( cfg && cfg->GetDXFPlotMode() == SKETCH )
1482 {
1483 SHAPE_POLY_SET outline = aPoly.CloneDropTriangulation();
1486
1487 outline = aPoly.CloneDropTriangulation();
1490 }
1491 else
1492 {
1493 PLOTTER::PlotPoly( aPoly.COutline( 0 ), FILL_T::NO_FILL, aWidth, aData );
1494 }
1495}
1496
1497
1498void DXF_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1499 const EDA_ANGLE& aOrient, void* aData )
1500{
1501 wxASSERT( m_outputFile );
1502
1503 VECTOR2I size( aSize );
1504 EDA_ANGLE orient( aOrient );
1505
1506 /* The chip is reduced to an oval tablet with size.y > size.x
1507 * (Oval vertical orientation 0) */
1508 if( size.x > size.y )
1509 {
1510 std::swap( size.x, size.y );
1511 orient += ANGLE_90;
1512 }
1513
1514 ThickOval( aPos, size, orient, DXF_LINE_WIDTH, aData );
1515}
1516
1517
1518void DXF_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1519{
1520 wxASSERT( m_outputFile );
1521 Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
1522}
1523
1524
1525void DXF_PLOTTER::FlashPadRect( const VECTOR2I& aPos, const VECTOR2I& aPadSize,
1526 const EDA_ANGLE& aOrient, void* aData )
1527{
1528 wxASSERT( m_outputFile );
1529
1530 VECTOR2I size, start, end;
1531
1532 size.x = aPadSize.x / 2;
1533 size.y = aPadSize.y / 2;
1534
1535 if( size.x < 0 )
1536 size.x = 0;
1537
1538 if( size.y < 0 )
1539 size.y = 0;
1540
1541 // If a dimension is zero, the trace is reduced to 1 line
1542 if( size.x == 0 )
1543 {
1544 start = VECTOR2I( aPos.x, aPos.y - size.y );
1545 end = VECTOR2I( aPos.x, aPos.y + size.y );
1546 RotatePoint( start, aPos, aOrient );
1547 RotatePoint( end, aPos, aOrient );
1548 MoveTo( start );
1549 FinishTo( end );
1550 return;
1551 }
1552
1553 if( size.y == 0 )
1554 {
1555 start = VECTOR2I( aPos.x - size.x, aPos.y );
1556 end = VECTOR2I( aPos.x + size.x, aPos.y );
1557 RotatePoint( start, aPos, aOrient );
1558 RotatePoint( end, aPos, aOrient );
1559 MoveTo( start );
1560 FinishTo( end );
1561 return;
1562 }
1563
1564 start = VECTOR2I( aPos.x - size.x, aPos.y - size.y );
1565 RotatePoint( start, aPos, aOrient );
1566 MoveTo( start );
1567
1568 end = VECTOR2I( aPos.x - size.x, aPos.y + size.y );
1569 RotatePoint( end, aPos, aOrient );
1570 LineTo( end );
1571
1572 end = VECTOR2I( aPos.x + size.x, aPos.y + size.y );
1573 RotatePoint( end, aPos, aOrient );
1574 LineTo( end );
1575
1576 end = VECTOR2I( aPos.x + size.x, aPos.y - size.y );
1577 RotatePoint( end, aPos, aOrient );
1578 LineTo( end );
1579
1580 FinishTo( start );
1581}
1582
1583
1584void DXF_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1585 int aCornerRadius, const EDA_ANGLE& aOrient, void* aData )
1586{
1587 SHAPE_POLY_SET outline;
1588 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0, 0,
1590
1591 // TransformRoundRectToPolygon creates only one convex polygon
1592 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1593
1594 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1595
1596 for( int ii = 1; ii < poly.PointCount(); ++ii )
1597 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1598
1599 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1600}
1601
1602
1603void DXF_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1604 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1605 void* aData )
1606{
1607 for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
1608 {
1609 SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
1610
1611 MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1612
1613 for( int ii = 1; ii < poly.PointCount(); ++ii )
1614 LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
1615
1616 FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
1617 }
1618}
1619
1620
1621void DXF_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1622 const EDA_ANGLE& aPadOrient, void* aData )
1623{
1624 wxASSERT( m_outputFile );
1625 VECTOR2I coord[4]; /* coord actual corners of a trapezoidal trace */
1626
1627 for( int ii = 0; ii < 4; ii++ )
1628 {
1629 coord[ii] = aCorners[ii];
1630 RotatePoint( coord[ii], aPadOrient );
1631 coord[ii] += aPadPos;
1632 }
1633
1634 // Plot edge:
1635 MoveTo( coord[0] );
1636 LineTo( coord[1] );
1637 LineTo( coord[2] );
1638 LineTo( coord[3] );
1639 FinishTo( coord[0] );
1640}
1641
1642
1643void DXF_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aRadius, int aCornerCount,
1644 const EDA_ANGLE& aOrient, void* aData )
1645{
1646 // Do nothing
1647 wxASSERT( 0 );
1648}
1649
1650
1658bool containsNonAsciiChars( const wxString& string )
1659{
1660 for( unsigned i = 0; i < string.length(); i++ )
1661 {
1662 wchar_t ch = string[i];
1663
1664 if( ch > 255 )
1665 return true;
1666 }
1667 return false;
1668}
1669
1670
1671void DXF_PLOTTER::Text( const VECTOR2I& aPos,
1672 const COLOR4D& aColor,
1673 const wxString& aText,
1674 const EDA_ANGLE& aOrient,
1675 const VECTOR2I& aSize,
1676 enum GR_TEXT_H_ALIGN_T aH_justify,
1677 enum GR_TEXT_V_ALIGN_T aV_justify,
1678 int aWidth,
1679 bool aItalic,
1680 bool aBold,
1681 bool aMultilineAllowed,
1682 KIFONT::FONT* aFont,
1683 const KIFONT::METRICS& aFontMetrics,
1684 void* aData )
1685{
1686 // Fix me: see how to use DXF text mode for multiline texts
1687 if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
1688 aMultilineAllowed = false; // the text has only one line.
1689
1690 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
1691
1692 if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
1693 {
1694 // output text as graphics.
1695 // Perhaps multiline texts could be handled as DXF text entity
1696 // but I do not want spend time about this (JPC)
1697 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
1698 aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1699 }
1700 else
1701 {
1702 TEXT_ATTRIBUTES attrs;
1703 attrs.m_Halign = aH_justify;
1704 attrs.m_Valign =aV_justify;
1705 attrs.m_StrokeWidth = aWidth;
1706 attrs.m_Angle = aOrient;
1707 attrs.m_Italic = aItalic;
1708 attrs.m_Bold = aBold;
1709 attrs.m_Mirrored = aSize.x < 0;
1710 attrs.m_Multiline = false;
1711 plotOneLineOfText( aPos, aColor, aText, attrs );
1712 }
1713}
1714
1715
1717 const COLOR4D& aColor,
1718 const wxString& aText,
1719 const TEXT_ATTRIBUTES& aAttributes,
1720 KIFONT::FONT* aFont,
1721 const KIFONT::METRICS& aFontMetrics,
1722 void* aData )
1723{
1724 TEXT_ATTRIBUTES attrs = aAttributes;
1725
1726 // Fix me: see how to use DXF text mode for multiline texts
1727 if( attrs.m_Multiline && !aText.Contains( wxT( "\n" ) ) )
1728 attrs.m_Multiline = false; // the text has only one line.
1729
1730 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
1731
1732 if( m_textAsLines || containsNonAsciiChars( aText ) || attrs.m_Multiline || processSuperSub )
1733 {
1734 // output text as graphics.
1735 // Perhaps multiline texts could be handled as DXF text entity
1736 // but I do not want spend time about that (JPC)
1737 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
1738 }
1739 else
1740 {
1741 plotOneLineOfText( aPos, aColor, aText, attrs );
1742 }
1743}
1744
1745
1746void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor,
1747 const wxString& aText, const TEXT_ATTRIBUTES& aAttributes )
1748{
1749 /* Emit text as a text entity. This loses formatting and shape but it's
1750 more useful as a CAD object */
1751 VECTOR2D origin_dev = userToDeviceCoordinates( aPos );
1752 SetColor( aColor );
1754
1755 VECTOR2D size_dev = userToDeviceSize( aAttributes.m_Size );
1756 int h_code = 0, v_code = 0;
1757
1758 switch( aAttributes.m_Halign )
1759 {
1760 case GR_TEXT_H_ALIGN_LEFT: h_code = 0; break;
1761 case GR_TEXT_H_ALIGN_CENTER: h_code = 1; break;
1762 case GR_TEXT_H_ALIGN_RIGHT: h_code = 2; break;
1763 case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
1764 }
1765
1766 switch( aAttributes.m_Valign )
1767 {
1768 case GR_TEXT_V_ALIGN_TOP: v_code = 3; break;
1769 case GR_TEXT_V_ALIGN_CENTER: v_code = 2; break;
1770 case GR_TEXT_V_ALIGN_BOTTOM: v_code = 1; break;
1771 case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
1772 }
1773
1774 std::string textStyle = "KICAD";
1775 if( aAttributes.m_Bold )
1776 {
1777 if( aAttributes.m_Italic )
1778 textStyle = "KICADBI";
1779 else
1780 textStyle = "KICADB";
1781 }
1782 else if( aAttributes.m_Italic )
1783 textStyle = "KICADI";
1784
1785 // Position, size, rotation and alignment
1786 // The two alignment point usages is somewhat idiot (see the DXF ref)
1787 // Anyway since we don't use the fit/aligned options, they're the same
1788 fmt::print( m_outputFile,
1789 " 0\n"
1790 "TEXT\n"
1791 " 7\n"
1792 "{}\n" // Text style
1793 " 8\n"
1794 "{}\n" // Layer name
1795 " 10\n"
1796 "{}\n" // First point X
1797 " 11\n"
1798 "{}\n" // Second point X
1799 " 20\n"
1800 "{}\n" // First point Y
1801 " 21\n"
1802 "{}\n" // Second point Y
1803 " 40\n"
1804 "{}\n" // Text height
1805 " 41\n"
1806 "{}\n" // Width factor
1807 " 50\n"
1808 "{:.8f}\n" // Rotation
1809 " 51\n"
1810 "{:.8f}\n" // Oblique angle
1811 " 71\n"
1812 "{}\n" // Mirror flags
1813 " 72\n"
1814 "{}\n" // H alignment
1815 " 73\n"
1816 "{}\n", // V alignment
1817 aAttributes.m_Bold ? ( aAttributes.m_Italic ? "KICADBI" : "KICADB" )
1818 : ( aAttributes.m_Italic ? "KICADI" : "KICAD" ),
1819 TO_UTF8( cLayerName ),
1820 formatCoord( origin_dev.x ),
1821 formatCoord( origin_dev.x ),
1822 formatCoord( origin_dev.y ),
1823 formatCoord( origin_dev.y ),
1824 formatCoord( size_dev.y ),
1825 formatCoord( fabs( size_dev.x / size_dev.y ) ),
1826 aAttributes.m_Angle.AsDegrees(),
1827 aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0,
1828 aAttributes.m_Mirrored ? 2 : 0, // X mirror flag
1829 h_code, v_code );
1830
1831 /* There are two issue in emitting the text:
1832 - Our overline character (~) must be converted to the appropriate
1833 control sequence %%O or %%o
1834 - Text encoding in DXF is more or less unspecified since depends on
1835 the DXF declared version, the acad version reading it *and* some
1836 system variables to be put in the header handled only by newer acads
1837 Also before R15 unicode simply is not supported (you need to use
1838 bigfonts which are a massive PITA). Common denominator solution:
1839 use Latin1 (and however someone could choke on it, anyway). Sorry
1840 for the extended latin people. If somewant want to try fixing this
1841 recent version seems to use UTF-8 (and not UCS2 like the rest of
1842 Windows)
1843
1844 XXX Actually there is a *third* issue: older DXF formats are limited
1845 to 255 bytes records (it was later raised to 2048); since I'm lazy
1846 and text so long is not probable I just don't implement this rule.
1847 If someone is interested in fixing this, you have to emit the first
1848 partial lines with group code 3 (max 250 bytes each) and then finish
1849 with a group code 1 (less than 250 bytes). The DXF refs explains it
1850 in no more details...
1851 */
1852
1853 int braceNesting = 0;
1854 int overbarDepth = -1;
1855
1856 fmt::print( m_outputFile, " 1\n" );
1857
1858 for( unsigned int i = 0; i < aText.length(); i++ )
1859 {
1860 /* Here I do a bad thing: writing the output one byte at a time!
1861 but today I'm lazy and I have no idea on how to coerce a Unicode
1862 wxString to spit out latin1 encoded text ...
1863
1864 At least stdio is *supposed* to do output buffering, so there is
1865 hope is not too slow */
1866 wchar_t ch = aText[i];
1867
1868 if( ch > 255 )
1869 {
1870 // I can't encode this...
1871 putc( '?', m_outputFile );
1872 }
1873 else
1874 {
1875 if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
1876 {
1877 fmt::print( m_outputFile, "%%o" );
1878 overbarDepth = braceNesting;
1879
1880 // Skip the '{'
1881 i++;
1882 continue;
1883 }
1884 else if( aText[i] == '{' )
1885 {
1886 braceNesting++;
1887 }
1888 else if( aText[i] == '}' )
1889 {
1890 if( braceNesting > 0 )
1891 braceNesting--;
1892
1893 if( braceNesting == overbarDepth )
1894 {
1895 fmt::print( m_outputFile, "%%O" );
1896 overbarDepth = -1;
1897 continue;
1898 }
1899 }
1900
1901 putc( ch, m_outputFile );
1902 }
1903 }
1904
1905 fmt::print( m_outputFile, "\n" );
1906}
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
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
void SetUnits(DXF_UNITS aUnit)
Set the units to use for plotting the DXF file.
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.
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.
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.
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
double m_unitScalingFactor
virtual void SetDash(int aLineWidth, LINE_STYLE aLineStyle) override
COLOR4D m_currentColor
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:243
std::vector< std::pair< PCB_LAYER_ID, wxString > > m_layersToExport
Definition plotter.h:728
bool m_plotMirror
Definition plotter.h:700
void MoveTo(const VECTOR2I &pos)
Definition plotter.h:308
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:487
void FinishTo(const VECTOR2I &pos)
Definition plotter.h:318
double m_iuPerDeviceUnit
Definition plotter.h:697
PCB_LAYER_ID m_layer
Definition plotter.h:730
RENDER_SETTINGS * RenderSettings()
Definition plotter.h:167
VECTOR2I m_plotOffset
Definition plotter.h:699
VECTOR2I m_penLastpos
Definition plotter.h:713
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:721
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:274
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition plotter.h:689
bool GetColorMode() const
Definition plotter.h:164
FILE * m_outputFile
Output file.
Definition plotter.h:706
void LineTo(const VECTOR2I &pos)
Definition plotter.h:313
void PenFinish()
Definition plotter.h:324
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont=nullptr, const KIFONT::METRICS &aFontMetrics=KIFONT::METRICS::Default(), void *aData=nullptr)
Definition plotter.cpp:696
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:626
double m_IUsPerDecimil
Definition plotter.h:695
bool m_colorMode
Definition plotter.h:709
virtual DXF_OUTLINE_MODE GetDXFPlotMode() const
Definition plotter.h:109
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:56
@ NO_FILL
Definition eda_shape.h:57
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
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:122
@ SKETCH
Definition plotter.h:81
DXF_COLOR_T
Legacy colors for DXF file.
Definition plotter_dxf.h:28
@ 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 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:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694