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