1 /**
2 * @file DXF_plotter.cpp
3 * @brief Kicad: specialized plotter for DXF files format
4 */
5 /*
6 * This program source code file is part of KiCad, a free EDA CAD application.
7 *
8 * Copyright (C) 2017-2021 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 <plotters/plotter_dxf.h>
29 #include <macros.h>
30 #include <string_utils.h>
31 #include <convert_basic_shapes_to_polygon.h>
32 #include <trigo.h>
33
34 /**
35 * Oblique angle for DXF native text
36 * (I don't remember if 15 degrees is the ISO value... it looks nice anyway)
37 */
38 static const double DXF_OBLIQUE_ANGLE = 15;
39
40 /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
41
42 - The primary colors (1 - 9)
43 - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
44 - Greys (251 - 255)
45
46 There is *no* black... the white does it on paper, usually, and
47 anyway it depends on the plotter configuration, since DXF colors
48 are meant to be logical only (they represent *both* line color and
49 width); later version with plot styles only complicate the matter!
50
51 As usual, brown and magenta/purple are difficult to place since
52 they are actually variations of other colors.
53 */
54 static const struct
55 {
56 const char *name;
57 int color;
58 } dxf_layer[NBCOLORS] =
59 {
60 { "BLACK", 7 }, // In DXF, color 7 is *both* white and black!
61 { "GRAY1", 251 },
62 { "GRAY2", 8 },
63 { "GRAY3", 9 },
64 { "WHITE", 7 },
65 { "LYELLOW", 51 },
66 { "BLUE1", 178 },
67 { "GREEN1", 98 },
68 { "CYAN1", 138 },
69 { "RED1", 18 },
70 { "MAGENTA1", 228 },
71 { "BROWN1", 58 },
72 { "BLUE2", 5 },
73 { "GREEN2", 3 },
74 { "CYAN2", 4 },
75 { "RED2", 1 },
76 { "MAGENTA2", 6 },
77 { "BROWN2", 54 },
78 { "BLUE3", 171 },
79 { "GREEN3", 91 },
80 { "CYAN3", 131 },
81 { "RED3", 11 },
82 { "MAGENTA3", 221 },
83 { "YELLOW3", 2 },
84 { "BLUE4", 5 },
85 { "GREEN4", 3 },
86 { "CYAN4", 4 },
87 { "RED4", 1 },
88 { "MAGENTA4", 6 },
89 { "YELLOW4", 2 }
90 };
91
92
getDXFLineType(PLOT_DASH_TYPE aType)93 static const char* getDXFLineType( PLOT_DASH_TYPE aType )
94 {
95 switch( aType )
96 {
97 case PLOT_DASH_TYPE::DEFAULT:
98 case PLOT_DASH_TYPE::SOLID:
99 return "CONTINUOUS";
100 case PLOT_DASH_TYPE::DASH:
101 return "DASHED";
102 case PLOT_DASH_TYPE::DOT:
103 return "DOTTED";
104 case PLOT_DASH_TYPE::DASHDOT:
105 return "DASHDOT";
106 default:
107 wxFAIL_MSG( "Unhandled PLOT_DASH_TYPE" );
108 return "CONTINUOUS";
109 }
110 }
111
112
113 // A helper function to create a color name acceptable in DXF files
114 // DXF files do not use a RGB definition
getDXFColorName(const COLOR4D & aColor)115 static wxString getDXFColorName( const COLOR4D& aColor )
116 {
117 EDA_COLOR_T color = COLOR4D::FindNearestLegacyColor( int( aColor.r * 255 ),
118 int( aColor.g * 255 ),
119 int( aColor.b * 255 ) );
120 wxString cname( dxf_layer[color].name );
121 return cname;
122 }
123
124
SetUnits(DXF_UNITS aUnit)125 void DXF_PLOTTER::SetUnits( DXF_UNITS aUnit )
126 {
127 m_plotUnits = aUnit;
128
129 switch( aUnit )
130 {
131 case DXF_UNITS::MILLIMETERS:
132 m_unitScalingFactor = 0.00254;
133 m_measurementDirective = 1;
134 break;
135
136 case DXF_UNITS::INCHES:
137 default:
138 m_unitScalingFactor = 0.0001;
139 m_measurementDirective = 0;
140 }
141 }
142
143
SetViewport(const wxPoint & aOffset,double aIusPerDecimil,double aScale,bool aMirror)144 void DXF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
145 double aScale, bool aMirror )
146 {
147 m_plotOffset = aOffset;
148 m_plotScale = aScale;
149
150 /* DXF paper is 'virtual' so there is no need of a paper size.
151 Also this way we can handle the aux origin which can be useful
152 (for example when aligning to a mechanical drawing) */
153 m_paperSize.x = 0;
154 m_paperSize.y = 0;
155
156 /* Like paper size DXF units are abstract too. Anyway there is a
157 * system variable (MEASUREMENT) which will be set to 0 to indicate
158 * english units */
159 m_IUsPerDecimil = aIusPerDecimil;
160 m_iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils
161 m_iuPerDeviceUnit *= GetUnitScaling(); // Get the scaling factor for the current units
162
163 m_plotMirror = false; // No mirroring on DXF
164 m_currentColor = COLOR4D::BLACK;
165 }
166
167
StartPlot()168 bool DXF_PLOTTER::StartPlot()
169 {
170 wxASSERT( m_outputFile );
171
172 // DXF HEADER - Boilerplate
173 // Defines the minimum for drawing i.e. the angle system and the
174 // 4 linetypes (CONTINUOUS, DOTDASH, DASHED and DOTTED)
175 fprintf( m_outputFile,
176 " 0\n"
177 "SECTION\n"
178 " 2\n"
179 "HEADER\n"
180 " 9\n"
181 "$ANGBASE\n"
182 " 50\n"
183 "0.0\n"
184 " 9\n"
185 "$ANGDIR\n"
186 " 70\n"
187 "1\n"
188 " 9\n"
189 "$MEASUREMENT\n"
190 " 70\n"
191 "%u\n"
192 " 0\n"
193 "ENDSEC\n"
194 " 0\n"
195 "SECTION\n"
196 " 2\n"
197 "TABLES\n"
198 " 0\n"
199 "TABLE\n"
200 " 2\n"
201 "LTYPE\n"
202 " 70\n"
203 "4\n"
204 " 0\n"
205 "LTYPE\n"
206 " 5\n"
207 "40F\n"
208 " 2\n"
209 "CONTINUOUS\n"
210 " 70\n"
211 "0\n"
212 " 3\n"
213 "Solid line\n"
214 " 72\n"
215 "65\n"
216 " 73\n"
217 "0\n"
218 " 40\n"
219 "0.0\n"
220 " 0\n"
221 "LTYPE\n"
222 " 5\n"
223 "410\n"
224 " 2\n"
225 "DASHDOT\n"
226 " 70\n"
227 "0\n"
228 " 3\n"
229 "Dash Dot ____ _ ____ _\n"
230 " 72\n"
231 "65\n"
232 " 73\n"
233 "4\n"
234 " 40\n"
235 "2.0\n"
236 " 49\n"
237 "1.25\n"
238 " 49\n"
239 "-0.25\n"
240 " 49\n"
241 "0.25\n"
242 " 49\n"
243 "-0.25\n"
244 " 0\n"
245 "LTYPE\n"
246 " 5\n"
247 "411\n"
248 " 2\n"
249 "DASHED\n"
250 " 70\n"
251 "0\n"
252 " 3\n"
253 "Dashed __ __ __ __ __\n"
254 " 72\n"
255 "65\n"
256 " 73\n"
257 "2\n"
258 " 40\n"
259 "0.75\n"
260 " 49\n"
261 "0.5\n"
262 " 49\n"
263 "-0.25\n"
264 " 0\n"
265 "LTYPE\n"
266 " 5\n"
267 "43B\n"
268 " 2\n"
269 "DOTTED\n"
270 " 70\n"
271 "0\n"
272 " 3\n"
273 "Dotted . . . .\n"
274 " 72\n"
275 "65\n"
276 " 73\n"
277 "2\n"
278 " 40\n"
279 "0.2\n"
280 " 49\n"
281 "0.0\n"
282 " 49\n"
283 "-0.2\n"
284 " 0\n"
285 "ENDTAB\n",
286 GetMeasurementDirective() );
287
288 // Text styles table
289 // Defines 4 text styles, one for each bold/italic combination
290 fputs( " 0\n"
291 "TABLE\n"
292 " 2\n"
293 "STYLE\n"
294 " 70\n"
295 "4\n", m_outputFile );
296
297 static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"};
298 for(int i = 0; i < 4; i++ )
299 {
300 fprintf( m_outputFile,
301 " 0\n"
302 "STYLE\n"
303 " 2\n"
304 "%s\n" // Style name
305 " 70\n"
306 "0\n" // Standard flags
307 " 40\n"
308 "0\n" // Non-fixed height text
309 " 41\n"
310 "1\n" // Width factor (base)
311 " 42\n"
312 "1\n" // Last height (mandatory)
313 " 50\n"
314 "%g\n" // Oblique angle
315 " 71\n"
316 "0\n" // Generation flags (default)
317 " 3\n"
318 // The standard ISO font (when kicad is build with it
319 // the dxf text in acad matches *perfectly*)
320 "isocp.shx\n", // Font name (when not bigfont)
321 // Apply a 15 degree angle to italic text
322 style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
323 }
324
325 EDA_COLOR_T numLayers = NBCOLORS;
326
327 // If printing in monochrome, only output the black layer
328 if( !GetColorMode() )
329 numLayers = static_cast<EDA_COLOR_T>( 1 );
330
331 // Layer table - one layer per color
332 fprintf( m_outputFile,
333 " 0\n"
334 "ENDTAB\n"
335 " 0\n"
336 "TABLE\n"
337 " 2\n"
338 "LAYER\n"
339 " 70\n"
340 "%d\n", numLayers );
341
342 /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
343
344 - The primary colors (1 - 9)
345 - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
346 - Greys (251 - 255)
347 */
348
349 wxASSERT( numLayers <= NBCOLORS );
350
351 for( EDA_COLOR_T i = BLACK; i < numLayers; i = static_cast<EDA_COLOR_T>( int( i ) + 1 ) )
352 {
353 fprintf( m_outputFile,
354 " 0\n"
355 "LAYER\n"
356 " 2\n"
357 "%s\n" // Layer name
358 " 70\n"
359 "0\n" // Standard flags
360 " 62\n"
361 "%d\n" // Color number
362 " 6\n"
363 "CONTINUOUS\n",// Linetype name
364 dxf_layer[i].name, dxf_layer[i].color );
365 }
366
367 // End of layer table, begin entities
368 fputs( " 0\n"
369 "ENDTAB\n"
370 " 0\n"
371 "ENDSEC\n"
372 " 0\n"
373 "SECTION\n"
374 " 2\n"
375 "ENTITIES\n", m_outputFile );
376
377 return true;
378 }
379
380
EndPlot()381 bool DXF_PLOTTER::EndPlot()
382 {
383 wxASSERT( m_outputFile );
384
385 // DXF FOOTER
386 fputs( " 0\n"
387 "ENDSEC\n"
388 " 0\n"
389 "EOF\n", m_outputFile );
390 fclose( m_outputFile );
391 m_outputFile = nullptr;
392
393 return true;
394 }
395
396
SetColor(const COLOR4D & color)397 void DXF_PLOTTER::SetColor( const COLOR4D& color )
398 {
399 if( ( m_colorMode )
400 || ( color == COLOR4D::BLACK )
401 || ( color == COLOR4D::WHITE ) )
402 {
403 m_currentColor = color;
404 }
405 else
406 {
407 m_currentColor = COLOR4D::BLACK;
408 }
409 }
410
411
Rect(const wxPoint & p1,const wxPoint & p2,FILL_T fill,int width)412 void DXF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
413 {
414 wxASSERT( m_outputFile );
415 MoveTo( p1 );
416 LineTo( wxPoint( p1.x, p2.y ) );
417 LineTo( wxPoint( p2.x, p2.y ) );
418 LineTo( wxPoint( p2.x, p1.y ) );
419 FinishTo( wxPoint( p1.x, p1.y ) );
420 }
421
422
Circle(const wxPoint & centre,int diameter,FILL_T fill,int width)423 void DXF_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, int width )
424 {
425 wxASSERT( m_outputFile );
426 double radius = userToDeviceSize( diameter / 2 );
427 DPOINT centre_dev = userToDeviceCoordinates( centre );
428
429 if( radius > 0 )
430 {
431 wxString cname = getDXFColorName( m_currentColor );
432
433 if( fill == FILL_T::NO_FILL )
434 {
435 fprintf( m_outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n",
436 TO_UTF8( cname ),
437 centre_dev.x, centre_dev.y, radius );
438 }
439 else if( fill == FILL_T::FILLED_SHAPE )
440 {
441 double r = radius*0.5;
442 fprintf( m_outputFile, "0\nPOLYLINE\n" );
443 fprintf( m_outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname ) );
444 fprintf( m_outputFile, "40\n%g\n41\n%g\n", radius, radius);
445 fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) );
446 fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
447 centre_dev.x-r, centre_dev.y );
448 fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) );
449 fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
450 centre_dev.x+r, centre_dev.y );
451 fprintf( m_outputFile, "0\nSEQEND\n");
452 }
453 }
454 }
455
456
PlotPoly(const std::vector<wxPoint> & aCornerList,FILL_T aFill,int aWidth,void * aData)457 void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, FILL_T aFill, int aWidth,
458 void* aData )
459 {
460 if( aCornerList.size() <= 1 )
461 return;
462
463 unsigned last = aCornerList.size() - 1;
464
465 // Plot outlines with lines (thickness = 0) to define the polygon
466 if( aWidth <= 0 )
467 {
468 MoveTo( aCornerList[0] );
469
470 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
471 LineTo( aCornerList[ii] );
472
473 // Close polygon if 'fill' requested
474 if( aFill != FILL_T::NO_FILL )
475 {
476 if( aCornerList[last] != aCornerList[0] )
477 LineTo( aCornerList[0] );
478 }
479
480 PenFinish();
481
482 return;
483 }
484
485 // if the polygon outline has thickness, and is not filled
486 // (i.e. is a polyline) plot outlines with thick segments
487 if( aWidth > 0 && aFill == FILL_T::NO_FILL )
488 {
489 MoveTo( aCornerList[0] );
490
491 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
492 ThickSegment( aCornerList[ii-1], aCornerList[ii], aWidth, FILLED, nullptr );
493
494 return;
495 }
496
497 // The polygon outline has thickness, and is filled
498 // Build and plot the polygon which contains the initial
499 // polygon and its thick outline
500 SHAPE_POLY_SET bufferOutline;
501 SHAPE_POLY_SET bufferPolybase;
502
503 bufferPolybase.NewOutline();
504
505 // enter outline as polygon:
506 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
507 {
508 TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
509 aWidth, GetPlotterArcHighDef(), ERROR_INSIDE );
510 }
511
512 // enter the initial polygon:
513 for( unsigned ii = 0; ii < aCornerList.size(); ii++ )
514 {
515 bufferPolybase.Append( aCornerList[ii] );
516 }
517
518 // Merge polygons to build the polygon which contains the initial
519 // polygon and its thick outline
520
521 // create the outline which contains thick outline:
522 bufferPolybase.BooleanAdd( bufferOutline, SHAPE_POLY_SET::PM_FAST );
523 bufferPolybase.Fracture( SHAPE_POLY_SET::PM_FAST );
524
525 if( bufferPolybase.OutlineCount() < 1 ) // should not happen
526 return;
527
528 const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
529
530 if( path.PointCount() < 2 ) // should not happen
531 return;
532
533 // Now, output the final polygon to DXF file:
534 last = path.PointCount() - 1;
535 VECTOR2I point = path.CPoint( 0 );
536
537 wxPoint startPoint( point.x, point.y );
538 MoveTo( startPoint );
539
540 for( int ii = 1; ii < path.PointCount(); ii++ )
541 {
542 point = path.CPoint( ii );
543 LineTo( wxPoint( point.x, point.y ) );
544 }
545
546 // Close polygon, if needed
547 point = path.CPoint( last );
548 wxPoint endPoint( point.x, point.y );
549
550 if( endPoint != startPoint )
551 LineTo( startPoint );
552
553 PenFinish();
554 }
555
556
PenTo(const wxPoint & pos,char plume)557 void DXF_PLOTTER::PenTo( const wxPoint& pos, char plume )
558 {
559 wxASSERT( m_outputFile );
560
561 if( plume == 'Z' )
562 {
563 return;
564 }
565
566 DPOINT pos_dev = userToDeviceCoordinates( pos );
567 DPOINT pen_lastpos_dev = userToDeviceCoordinates( m_penLastpos );
568
569 if( m_penLastpos != pos && plume == 'D' )
570 {
571 wxASSERT( m_currentLineType >= PLOT_DASH_TYPE::FIRST_TYPE
572 && m_currentLineType <= PLOT_DASH_TYPE::LAST_TYPE );
573 // DXF LINE
574 wxString cname = getDXFColorName( m_currentColor );
575 const char* lname = getDXFLineType( static_cast<PLOT_DASH_TYPE>( m_currentLineType ) );
576 fprintf( m_outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n",
577 TO_UTF8( cname ), lname,
578 pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y );
579 }
580
581 m_penLastpos = pos;
582 }
583
584
SetDash(PLOT_DASH_TYPE aDashed)585 void DXF_PLOTTER::SetDash( PLOT_DASH_TYPE aDashed )
586 {
587 wxASSERT( aDashed >= PLOT_DASH_TYPE::FIRST_TYPE && aDashed <= PLOT_DASH_TYPE::LAST_TYPE );
588 m_currentLineType = aDashed;
589 }
590
591
ThickSegment(const wxPoint & aStart,const wxPoint & aEnd,int aWidth,OUTLINE_MODE aPlotMode,void * aData)592 void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth,
593 OUTLINE_MODE aPlotMode, void* aData )
594 {
595 if( aPlotMode == SKETCH )
596 {
597 std::vector<wxPoint> cornerList;
598 SHAPE_POLY_SET outlineBuffer;
599 TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(),
600 ERROR_INSIDE );
601 const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );
602
603 cornerList.reserve( path.PointCount() );
604
605 for( int jj = 0; jj < path.PointCount(); jj++ )
606 cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );
607
608 // Ensure the polygon is closed
609 if( cornerList[0] != cornerList[cornerList.size() - 1] )
610 cornerList.push_back( cornerList[0] );
611
612 PlotPoly( cornerList, FILL_T::NO_FILL );
613 }
614 else
615 {
616 MoveTo( aStart );
617 FinishTo( aEnd );
618 }
619 }
620
621
Arc(const wxPoint & centre,double StAngle,double EndAngle,int radius,FILL_T fill,int width)622 void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
623 FILL_T fill, int width )
624 {
625 wxASSERT( m_outputFile );
626
627 if( radius <= 0 )
628 return;
629
630 // In DXF, arcs are drawn CCW.
631 // In Kicad, arcs are CW or CCW
632 // If StAngle > EndAngle, it is CW. So transform it to CCW
633 if( StAngle > EndAngle )
634 {
635 std::swap( StAngle, EndAngle );
636 }
637
638 DPOINT centre_dev = userToDeviceCoordinates( centre );
639 double radius_dev = userToDeviceSize( radius );
640
641 // Emit a DXF ARC entity
642 wxString cname = getDXFColorName( m_currentColor );
643 fprintf( m_outputFile,
644 "0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n",
645 TO_UTF8( cname ),
646 centre_dev.x, centre_dev.y, radius_dev,
647 StAngle / 10.0, EndAngle / 10.0 );
648 }
649
650
FlashPadOval(const wxPoint & pos,const wxSize & aSize,double orient,OUTLINE_MODE trace_mode,void * aData)651 void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
652 OUTLINE_MODE trace_mode, void* aData )
653 {
654 wxASSERT( m_outputFile );
655 wxSize size( aSize );
656
657 /* The chip is reduced to an oval tablet with size.y > size.x
658 * (Oval vertical orientation 0) */
659 if( size.x > size.y )
660 {
661 std::swap( size.x, size.y );
662 orient = AddAngles( orient, 900 );
663 }
664
665 sketchOval( pos, size, orient, -1 );
666 }
667
668
FlashPadCircle(const wxPoint & pos,int diametre,OUTLINE_MODE trace_mode,void * aData)669 void DXF_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre,
670 OUTLINE_MODE trace_mode, void* aData )
671 {
672 wxASSERT( m_outputFile );
673 Circle( pos, diametre, FILL_T::NO_FILL );
674 }
675
676
FlashPadRect(const wxPoint & pos,const wxSize & padsize,double orient,OUTLINE_MODE trace_mode,void * aData)677 void DXF_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize,
678 double orient, OUTLINE_MODE trace_mode, void* aData )
679 {
680 wxASSERT( m_outputFile );
681 wxSize size;
682 int ox, oy, fx, fy;
683
684 size.x = padsize.x / 2;
685 size.y = padsize.y / 2;
686
687 if( size.x < 0 )
688 size.x = 0;
689
690 if( size.y < 0 )
691 size.y = 0;
692
693 // If a dimension is zero, the trace is reduced to 1 line
694 if( size.x == 0 )
695 {
696 ox = pos.x;
697 oy = pos.y - size.y;
698 RotatePoint( &ox, &oy, pos.x, pos.y, orient );
699 fx = pos.x;
700 fy = pos.y + size.y;
701 RotatePoint( &fx, &fy, pos.x, pos.y, orient );
702 MoveTo( wxPoint( ox, oy ) );
703 FinishTo( wxPoint( fx, fy ) );
704 return;
705 }
706
707 if( size.y == 0 )
708 {
709 ox = pos.x - size.x;
710 oy = pos.y;
711 RotatePoint( &ox, &oy, pos.x, pos.y, orient );
712 fx = pos.x + size.x;
713 fy = pos.y;
714 RotatePoint( &fx, &fy, pos.x, pos.y, orient );
715 MoveTo( wxPoint( ox, oy ) );
716 FinishTo( wxPoint( fx, fy ) );
717 return;
718 }
719
720 ox = pos.x - size.x;
721 oy = pos.y - size.y;
722 RotatePoint( &ox, &oy, pos.x, pos.y, orient );
723 MoveTo( wxPoint( ox, oy ) );
724
725 fx = pos.x - size.x;
726 fy = pos.y + size.y;
727 RotatePoint( &fx, &fy, pos.x, pos.y, orient );
728 LineTo( wxPoint( fx, fy ) );
729
730 fx = pos.x + size.x;
731 fy = pos.y + size.y;
732 RotatePoint( &fx, &fy, pos.x, pos.y, orient );
733 LineTo( wxPoint( fx, fy ) );
734
735 fx = pos.x + size.x;
736 fy = pos.y - size.y;
737 RotatePoint( &fx, &fy, pos.x, pos.y, orient );
738 LineTo( wxPoint( fx, fy ) );
739
740 FinishTo( wxPoint( ox, oy ) );
741 }
742
743
FlashPadRoundRect(const wxPoint & aPadPos,const wxSize & aSize,int aCornerRadius,double aOrient,OUTLINE_MODE aTraceMode,void * aData)744 void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
745 int aCornerRadius, double aOrient,
746 OUTLINE_MODE aTraceMode, void* aData )
747 {
748 SHAPE_POLY_SET outline;
749 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
750 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
751
752 // TransformRoundRectToPolygon creates only one convex polygon
753 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
754
755 MoveTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
756
757 for( int ii = 1; ii < poly.PointCount(); ++ii )
758 LineTo( wxPoint( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
759
760 FinishTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
761 }
762
FlashPadCustom(const wxPoint & aPadPos,const wxSize & aSize,double aOrient,SHAPE_POLY_SET * aPolygons,OUTLINE_MODE aTraceMode,void * aData)763 void DXF_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
764 double aOrient, SHAPE_POLY_SET* aPolygons,
765 OUTLINE_MODE aTraceMode, void* aData )
766 {
767 for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
768 {
769 SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
770
771 MoveTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
772
773 for( int ii = 1; ii < poly.PointCount(); ++ii )
774 LineTo( wxPoint( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
775
776 FinishTo( wxPoint( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
777 }
778 }
779
780
FlashPadTrapez(const wxPoint & aPadPos,const wxPoint * aCorners,double aPadOrient,OUTLINE_MODE aTrace_Mode,void * aData)781 void DXF_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners,
782 double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
783 {
784 wxASSERT( m_outputFile );
785 wxPoint coord[4]; /* coord actual corners of a trapezoidal trace */
786
787 for( int ii = 0; ii < 4; ii++ )
788 {
789 coord[ii] = aCorners[ii];
790 RotatePoint( &coord[ii], aPadOrient );
791 coord[ii] += aPadPos;
792 }
793
794 // Plot edge:
795 MoveTo( coord[0] );
796 LineTo( coord[1] );
797 LineTo( coord[2] );
798 LineTo( coord[3] );
799 FinishTo( coord[0] );
800 }
801
802
FlashRegularPolygon(const wxPoint & aShapePos,int aRadius,int aCornerCount,double aOrient,OUTLINE_MODE aTraceMode,void * aData)803 void DXF_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, int aRadius, int aCornerCount,
804 double aOrient, OUTLINE_MODE aTraceMode, void* aData )
805 {
806 // Do nothing
807 wxASSERT( 0 );
808 }
809
810
811 /**
812 * Check if a given string contains non-ASCII characters.
813 *
814 * @fixme The performance of this code is really poor, but in this case it can be
815 * acceptable because the plot operation is not called very often.
816 *
817 * @param string String to check.
818 * @return true if it contains some non-ASCII character, false if all characters are
819 * inside ASCII range (<=255).
820 */
containsNonAsciiChars(const wxString & string)821 bool containsNonAsciiChars( const wxString& string )
822 {
823 for( unsigned i = 0; i < string.length(); i++ )
824 {
825 wchar_t ch = string[i];
826 if( ch > 255 )
827 return true;
828 }
829 return false;
830 }
831
832
Text(const wxPoint & aPos,const COLOR4D & aColor,const wxString & aText,double aOrient,const wxSize & aSize,enum EDA_TEXT_HJUSTIFY_T aH_justify,enum EDA_TEXT_VJUSTIFY_T aV_justify,int aWidth,bool aItalic,bool aBold,bool aMultilineAllowed,void * aData)833 void DXF_PLOTTER::Text( const wxPoint& aPos,
834 const COLOR4D& aColor,
835 const wxString& aText,
836 double aOrient,
837 const wxSize& aSize,
838 enum EDA_TEXT_HJUSTIFY_T aH_justify,
839 enum EDA_TEXT_VJUSTIFY_T aV_justify,
840 int aWidth,
841 bool aItalic,
842 bool aBold,
843 bool aMultilineAllowed,
844 void* aData )
845 {
846 // Fix me: see how to use DXF text mode for multiline texts
847 if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
848 aMultilineAllowed = false; // the text has only one line.
849
850 bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );
851
852 if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
853 {
854 // output text as graphics.
855 // Perhaps multiline texts could be handled as DXF text entity
856 // but I do not want spend time about this (JPC)
857 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
858 aWidth, aItalic, aBold, aMultilineAllowed );
859 }
860 else
861 {
862 /* Emit text as a text entity. This loses formatting and shape but it's
863 more useful as a CAD object */
864 DPOINT origin_dev = userToDeviceCoordinates( aPos );
865 SetColor( aColor );
866 wxString cname = getDXFColorName( m_currentColor );
867 DPOINT size_dev = userToDeviceSize( aSize );
868 int h_code = 0, v_code = 0;
869
870 switch( aH_justify )
871 {
872 case GR_TEXT_HJUSTIFY_LEFT:
873 h_code = 0;
874 break;
875 case GR_TEXT_HJUSTIFY_CENTER:
876 h_code = 1;
877 break;
878 case GR_TEXT_HJUSTIFY_RIGHT:
879 h_code = 2;
880 break;
881 }
882
883 switch( aV_justify )
884 {
885 case GR_TEXT_VJUSTIFY_TOP:
886 v_code = 3;
887 break;
888 case GR_TEXT_VJUSTIFY_CENTER:
889 v_code = 2;
890 break;
891 case GR_TEXT_VJUSTIFY_BOTTOM:
892 v_code = 1;
893 break;
894 }
895
896 // Position, size, rotation and alignment
897 // The two alignment point usages is somewhat idiot (see the DXF ref)
898 // Anyway since we don't use the fit/aligned options, they're the same
899 fprintf( m_outputFile,
900 " 0\n"
901 "TEXT\n"
902 " 7\n"
903 "%s\n" // Text style
904 " 8\n"
905 "%s\n" // Layer name
906 " 10\n"
907 "%g\n" // First point X
908 " 11\n"
909 "%g\n" // Second point X
910 " 20\n"
911 "%g\n" // First point Y
912 " 21\n"
913 "%g\n" // Second point Y
914 " 40\n"
915 "%g\n" // Text height
916 " 41\n"
917 "%g\n" // Width factor
918 " 50\n"
919 "%g\n" // Rotation
920 " 51\n"
921 "%g\n" // Oblique angle
922 " 71\n"
923 "%d\n" // Mirror flags
924 " 72\n"
925 "%d\n" // H alignment
926 " 73\n"
927 "%d\n", // V alignment
928 aBold ? (aItalic ? "KICADBI" : "KICADB")
929 : (aItalic ? "KICADI" : "KICAD"),
930 TO_UTF8( cname ),
931 origin_dev.x, origin_dev.x,
932 origin_dev.y, origin_dev.y,
933 size_dev.y, fabs( size_dev.x / size_dev.y ),
934 aOrient / 10.0,
935 aItalic ? DXF_OBLIQUE_ANGLE : 0,
936 size_dev.x < 0 ? 2 : 0, // X mirror flag
937 h_code, v_code );
938
939 /* There are two issue in emitting the text:
940 - Our overline character (~) must be converted to the appropriate
941 control sequence %%O or %%o
942 - Text encoding in DXF is more or less unspecified since depends on
943 the DXF declared version, the acad version reading it *and* some
944 system variables to be put in the header handled only by newer acads
945 Also before R15 unicode simply is not supported (you need to use
946 bigfonts which are a massive PITA). Common denominator solution:
947 use Latin1 (and however someone could choke on it, anyway). Sorry
948 for the extended latin people. If somewant want to try fixing this
949 recent version seems to use UTF-8 (and not UCS2 like the rest of
950 Windows)
951
952 XXX Actually there is a *third* issue: older DXF formats are limited
953 to 255 bytes records (it was later raised to 2048); since I'm lazy
954 and text so long is not probable I just don't implement this rule.
955 If someone is interested in fixing this, you have to emit the first
956 partial lines with group code 3 (max 250 bytes each) and then finish
957 with a group code 1 (less than 250 bytes). The DXF refs explains it
958 in no more details...
959 */
960
961 int braceNesting = 0;
962 int overbarDepth = -1;
963
964 fputs( " 1\n", m_outputFile );
965
966 for( unsigned int i = 0; i < aText.length(); i++ )
967 {
968 /* Here I do a bad thing: writing the output one byte at a time!
969 but today I'm lazy and I have no idea on how to coerce a Unicode
970 wxString to spit out latin1 encoded text ...
971
972 At least stdio is *supposed* to do output buffering, so there is
973 hope is not too slow */
974 wchar_t ch = aText[i];
975
976 if( ch > 255 )
977 {
978 // I can't encode this...
979 putc( '?', m_outputFile );
980 }
981 else
982 {
983 if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
984 {
985 fputs( "%%o", m_outputFile );
986 overbarDepth = braceNesting;
987
988 // Skip the '{'
989 i++;
990 continue;
991 }
992 else if( aText[i] == '{' )
993 {
994 braceNesting++;
995 }
996 else if( aText[i] == '}' )
997 {
998 if( braceNesting > 0 )
999 braceNesting--;
1000
1001 if( braceNesting == overbarDepth )
1002 {
1003 fputs( "%%O", m_outputFile );
1004 overbarDepth = -1;
1005 continue;
1006 }
1007 }
1008
1009 putc( ch, m_outputFile );
1010 }
1011 }
1012
1013 putc( '\n', m_outputFile );
1014 }
1015 }
1016
1017