KiCad PCB EDA Suite
class_plotter.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
40 #include <fctsys.h>
41 
42 #include <trigo.h>
43 #include <wxstruct.h>
44 #include <base_struct.h>
45 #include <common.h>
46 #include <class_plotter.h>
47 #include <macros.h>
48 #include <class_base_screen.h>
49 #include <drawtxt.h>
51 
52 
54 {
55  plotScale = 1;
56  defaultPenWidth = 0;
57  currentPenWidth = -1; // To-be-set marker
58  penState = 'Z'; // End-of-path idle
59  m_plotMirror = false; // Plot mirror option flag
60  m_mirrorIsHorizontal = true;
61  m_yaxisReversed = false;
62  outputFile = 0;
63  colorMode = false; // Starts as a BW plot
64  negativeMode = false;
65  // Temporary init to avoid not initialized vars, will be set later
66  m_IUsPerDecimil = 1; // will be set later to the actual value
67  iuPerDeviceUnit = 1; // will be set later to the actual value
68  m_dotMarkLength_mm = 0.1; // Dotted line parameter in mm: segment
69  // Dashed line parameter is 5 * dotted line mark
70  // Dashed line gap is 3 * dotted line mark
71 }
72 
74 {
75  // Emergency cleanup, but closing the file is
76  // usually made in EndPlot().
77  if( outputFile )
78  fclose( outputFile );
79 }
80 
81 
82 bool PLOTTER::OpenFile( const wxString& aFullFilename )
83 {
84  filename = aFullFilename;
85 
86  wxASSERT( !outputFile );
87 
88  // Open the file in text mode (not suitable for all plotters
89  // but only for most of them
90  outputFile = wxFopen( filename, wxT( "wt" ) );
91 
92  if( outputFile == NULL )
93  return false ;
94 
95  return true;
96 }
97 
98 
100 {
101  wxPoint pos = aCoordinate - plotOffset;
102 
103  double x = pos.x * plotScale;
104  double y = ( paperSize.y - pos.y * plotScale );
105 
106  if( m_plotMirror )
107  {
109  x = ( paperSize.x - pos.x * plotScale );
110  else
111  y = pos.y * plotScale;
112  }
113 
114  if( m_yaxisReversed )
115  y = paperSize.y - y;
116 
117  x *= iuPerDeviceUnit;
118  y *= iuPerDeviceUnit;
119 
120  return DPOINT( x, y );
121 }
122 
123 
124 DPOINT PLOTTER::userToDeviceSize( const wxSize& size )
125 {
126  return DPOINT( size.x * plotScale * iuPerDeviceUnit,
127  size.y * plotScale * iuPerDeviceUnit );
128 }
129 
130 
131 double PLOTTER::userToDeviceSize( double size ) const
132 {
133  return size * plotScale * iuPerDeviceUnit;
134 }
135 
136 
138 {
139  return userToDeviceSize( std::max( 1.0,
140  m_dotMarkLength_mm * 10000 / 25.4 * m_IUsPerDecimil - GetCurrentLineWidth() ) );
141 }
142 
143 
145 {
146  return std::max( GetDashGapLenIU(), 5.0 * GetDotMarkLenIU() );
147 }
148 
149 
151 {
152  return 3.0 * GetDotMarkLenIU() + userToDeviceSize( 2 * GetCurrentLineWidth() );
153 }
154 
155 void PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
156  FILL_T fill, int width )
157 {
158  wxPoint start, end;
159  const int delta = 50; // increment (in 0.1 degrees) to draw circles
160 
161  if( StAngle > EndAngle )
162  std::swap( StAngle, EndAngle );
163 
164  SetCurrentLineWidth( width );
165  /* Please NOTE the different sign due to Y-axis flip */
166  start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) );
167  start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) );
168  MoveTo( start );
169  for( int ii = StAngle + delta; ii < EndAngle; ii += delta )
170  {
171  end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) );
172  end.y = centre.y + KiROUND( sindecideg( radius, -ii ) );
173  LineTo( end );
174  }
175 
176  end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) );
177  end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) );
178  FinishTo( end );
179 }
180 
181 
182 void PLOTTER::PlotImage(const wxImage & aImage, const wxPoint& aPos, double aScaleFactor )
183 {
184  wxSize size( aImage.GetWidth() * aScaleFactor,
185  aImage.GetHeight() * aScaleFactor );
186 
187  wxPoint start = aPos;
188  start.x -= size.x / 2;
189  start.y -= size.y / 2;
190 
191  wxPoint end = start;
192  end.x += size.x;
193  end.y += size.y;
194 
195  Rect( start, end, NO_FILL );
196 }
197 
198 
199 void PLOTTER::markerSquare( const wxPoint& position, int radius )
200 {
201  double r = KiROUND( radius / 1.4142 );
202  std::vector< wxPoint > corner_list;
203  wxPoint corner;
204  corner.x = position.x + r;
205  corner.y = position.y + r;
206  corner_list.push_back( corner );
207  corner.x = position.x + r;
208  corner.y = position.y - r;
209  corner_list.push_back( corner );
210  corner.x = position.x - r;
211  corner.y = position.y - r;
212  corner_list.push_back( corner );
213  corner.x = position.x - r;
214  corner.y = position.y + r;
215  corner_list.push_back( corner );
216  corner.x = position.x + r;
217  corner.y = position.y + r;
218  corner_list.push_back( corner );
219 
220  PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() );
221 }
222 
223 
224 void PLOTTER::markerCircle( const wxPoint& position, int radius )
225 {
226  Circle( position, radius * 2, NO_FILL, GetCurrentLineWidth() );
227 }
228 
229 
230 void PLOTTER::markerLozenge( const wxPoint& position, int radius )
231 {
232  std::vector< wxPoint > corner_list;
233  wxPoint corner;
234  corner.x = position.x;
235  corner.y = position.y + radius;
236  corner_list.push_back( corner );
237  corner.x = position.x + radius;
238  corner.y = position.y,
239  corner_list.push_back( corner );
240  corner.x = position.x;
241  corner.y = position.y - radius;
242  corner_list.push_back( corner );
243  corner.x = position.x - radius;
244  corner.y = position.y;
245  corner_list.push_back( corner );
246  corner.x = position.x;
247  corner.y = position.y + radius;
248  corner_list.push_back( corner );
249 
250  PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() );
251 }
252 
253 
254 void PLOTTER::markerHBar( const wxPoint& pos, int radius )
255 {
256  MoveTo( wxPoint( pos.x - radius, pos.y ) );
257  FinishTo( wxPoint( pos.x + radius, pos.y ) );
258 }
259 
260 
261 void PLOTTER::markerSlash( const wxPoint& pos, int radius )
262 {
263  MoveTo( wxPoint( pos.x - radius, pos.y - radius ) );
264  FinishTo( wxPoint( pos.x + radius, pos.y + radius ) );
265 }
266 
267 
268 void PLOTTER::markerBackSlash( const wxPoint& pos, int radius )
269 {
270  MoveTo( wxPoint( pos.x + radius, pos.y - radius ) );
271  FinishTo( wxPoint( pos.x - radius, pos.y + radius ) );
272 }
273 
274 
275 void PLOTTER::markerVBar( const wxPoint& pos, int radius )
276 {
277  MoveTo( wxPoint( pos.x, pos.y - radius ) );
278  FinishTo( wxPoint( pos.x, pos.y + radius ) );
279 }
280 
281 
282 void PLOTTER::Marker( const wxPoint& position, int diametre, unsigned aShapeId )
283 {
284  int radius = diametre / 2;
285  /* Marker are composed by a series of 'parts' superimposed; not every
286  combination make sense, obviously. Since they are used in order I
287  tried to keep the uglier/more complex constructions at the end.
288  Also I avoided the |/ |\ -/ -\ construction because they're *very*
289  ugly... if needed they could be added anyway... I'd like to see
290  a board with more than 58 drilling/slotting tools!
291  If Visual C++ supported the 0b literals they would be optimally
292  and easily encoded as an integer array. We have to do with octal */
293  static const unsigned char marker_patterns[MARKER_COUNT] = {
294  // Bit order: O Square Lozenge - | \ /
295  // First choice: simple shapes
296  0003, // X
297  0100, // O
298  0014, // +
299  0040, // Sq
300  0020, // Lz
301  // Two simple shapes
302  0103, // X O
303  0017, // X +
304  0043, // X Sq
305  0023, // X Lz
306  0114, // O +
307  0140, // O Sq
308  0120, // O Lz
309  0054, // + Sq
310  0034, // + Lz
311  0060, // Sq Lz
312  // Three simple shapes
313  0117, // X O +
314  0143, // X O Sq
315  0123, // X O Lz
316  0057, // X + Sq
317  0037, // X + Lz
318  0063, // X Sq Lz
319  0154, // O + Sq
320  0134, // O + Lz
321  0074, // + Sq Lz
322  // Four simple shapes
323  0174, // O Sq Lz +
324  0163, // X O Sq Lz
325  0157, // X O Sq +
326  0137, // X O Lz +
327  0077, // X Sq Lz +
328  // This draws *everything *
329  0177, // X O Sq Lz +
330  // Here we use the single bars... so the cross is forbidden
331  0110, // O -
332  0104, // O |
333  0101, // O /
334  0050, // Sq -
335  0044, // Sq |
336  0041, // Sq /
337  0030, // Lz -
338  0024, // Lz |
339  0021, // Lz /
340  0150, // O Sq -
341  0144, // O Sq |
342  0141, // O Sq /
343  0130, // O Lz -
344  0124, // O Lz |
345  0121, // O Lz /
346  0070, // Sq Lz -
347  0064, // Sq Lz |
348  0061, // Sq Lz /
349  0170, // O Sq Lz -
350  0164, // O Sq Lz |
351  0161, // O Sq Lz /
352  // Last resort: the backlash component (easy to confound)
353  0102, // \ O
354  0042, // \ Sq
355  0022, // \ Lz
356  0142, // \ O Sq
357  0122, // \ O Lz
358  0062, // \ Sq Lz
359  0162 // \ O Sq Lz
360  };
361  if( aShapeId >= MARKER_COUNT )
362  {
363  // Fallback shape
364  markerCircle( position, radius );
365  }
366  else
367  {
368  // Decode the pattern and draw the corresponding parts
369  unsigned char pat = marker_patterns[aShapeId];
370  if( pat & 0001 )
371  markerSlash( position, radius );
372  if( pat & 0002 )
373  markerBackSlash( position, radius );
374  if( pat & 0004 )
375  markerVBar( position, radius );
376  if( pat & 0010 )
377  markerHBar( position, radius );
378  if( pat & 0020 )
379  markerLozenge( position, radius );
380  if( pat & 0040 )
381  markerSquare( position, radius );
382  if( pat & 0100 )
383  markerCircle( position, radius );
384  }
385 }
386 
387 
388 void PLOTTER::segmentAsOval( const wxPoint& start, const wxPoint& end, int width,
389  EDA_DRAW_MODE_T tracemode )
390 {
391  wxPoint center( (start.x + end.x) / 2, (start.y + end.y) / 2 );
392  wxSize size( end.x - start.x, end.y - start.y );
393  double orient;
394 
395  if( size.y == 0 )
396  orient = 0;
397  else if( size.x == 0 )
398  orient = 900;
399  else
400  orient = -ArcTangente( size.y, size.x );
401 
402  size.x = KiROUND( EuclideanNorm( size ) ) + width;
403  size.y = width;
404 
405  FlashPadOval( center, size, orient, tracemode, NULL );
406 }
407 
408 
409 void PLOTTER::sketchOval( const wxPoint& pos, const wxSize& aSize, double orient, int width )
410 {
411  SetCurrentLineWidth( width );
412  width = currentPenWidth;
413  int radius, deltaxy, cx, cy;
414  wxSize size( aSize );
415 
416  if( size.x > size.y )
417  {
418  std::swap( size.x, size.y );
419  orient = AddAngles( orient, 900 );
420  }
421 
422  deltaxy = size.y - size.x; /* distance between centers of the oval */
423  radius = ( size.x - width ) / 2;
424  cx = -radius;
425  cy = -deltaxy / 2;
426  RotatePoint( &cx, &cy, orient );
427  MoveTo( wxPoint( cx + pos.x, cy + pos.y ) );
428  cx = -radius;
429  cy = deltaxy / 2;
430  RotatePoint( &cx, &cy, orient );
431  FinishTo( wxPoint( cx + pos.x, cy + pos.y ) );
432 
433  cx = radius;
434  cy = -deltaxy / 2;
435  RotatePoint( &cx, &cy, orient );
436  MoveTo( wxPoint( cx + pos.x, cy + pos.y ) );
437  cx = radius;
438  cy = deltaxy / 2;
439  RotatePoint( &cx, &cy, orient );
440  FinishTo( wxPoint( cx + pos.x, cy + pos.y ) );
441 
442  cx = 0;
443  cy = deltaxy / 2;
444  RotatePoint( &cx, &cy, orient );
445  Arc( wxPoint( cx + pos.x, cy + pos.y ),
446  orient + 1800, orient + 3600,
447  radius, NO_FILL );
448  cx = 0;
449  cy = -deltaxy / 2;
450  RotatePoint( &cx, &cy, orient );
451  Arc( wxPoint( cx + pos.x, cy + pos.y ),
452  orient, orient + 1800,
453  radius, NO_FILL );
454 }
455 
456 
457 void PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
458  EDA_DRAW_MODE_T tracemode, void* aData )
459 {
460  if( tracemode == FILLED )
461  {
462  SetCurrentLineWidth( width );
463  MoveTo( start );
464  FinishTo( end );
465  }
466  else
467  {
468  SetCurrentLineWidth( -1 );
469  segmentAsOval( start, end, width, tracemode );
470  }
471 }
472 
473 
474 void PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
475  int radius, int width, EDA_DRAW_MODE_T tracemode, void* aData )
476 {
477  if( tracemode == FILLED )
478  Arc( centre, StAngle, EndAngle, radius, NO_FILL, width );
479  else
480  {
481  SetCurrentLineWidth( -1 );
482  Arc( centre, StAngle, EndAngle,
483  radius - ( width - currentPenWidth ) / 2, NO_FILL, -1 );
484  Arc( centre, StAngle, EndAngle,
485  radius + ( width - currentPenWidth ) / 2, NO_FILL, -1 );
486  }
487 }
488 
489 
490 void PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
491  EDA_DRAW_MODE_T tracemode, void* aData )
492 {
493  if( tracemode == FILLED )
494  Rect( p1, p2, NO_FILL, width );
495  else
496  {
497  SetCurrentLineWidth( -1 );
498  wxPoint offsetp1( p1.x - (width - currentPenWidth) / 2,
499  p1.y - (width - currentPenWidth) / 2 );
500  wxPoint offsetp2( p2.x + (width - currentPenWidth) / 2,
501  p2.y + (width - currentPenWidth) / 2 );
502  Rect( offsetp1, offsetp2, NO_FILL, -1 );
503  offsetp1.x += (width - currentPenWidth);
504  offsetp1.y += (width - currentPenWidth);
505  offsetp2.x -= (width - currentPenWidth);
506  offsetp2.y -= (width - currentPenWidth);
507  Rect( offsetp1, offsetp2, NO_FILL, -1 );
508  }
509 }
510 
511 
512 void PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
513  EDA_DRAW_MODE_T tracemode, void* aData )
514 {
515  if( tracemode == FILLED )
516  Circle( pos, diametre, NO_FILL, width );
517  else
518  {
519  SetCurrentLineWidth( -1 );
520  Circle( pos, diametre - width + currentPenWidth, NO_FILL, -1 );
521  Circle( pos, diametre + width - currentPenWidth, NO_FILL, -1 );
522  }
523 }
524 
525 
526 void PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill,
527  int aWidth, void * aData )
528 {
529  std::vector< wxPoint > cornerList;
530 
531  for( int ii = 0; ii < aCornerList.PointCount(); ii++ )
532  cornerList.push_back( wxPoint( aCornerList.CPoint( ii ) ) );
533 
534  PlotPoly( cornerList , aFill, aWidth, aData );
535 }
536 
537 
538 void PLOTTER::SetPageSettings( const PAGE_INFO& aPageSettings )
539 {
540  pageInfo = aPageSettings;
541 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:112
void FinishTo(const wxPoint &pos)
double m_dotMarkLength_mm
Dotted line parameter in mm: segment.
void markerHBar(const wxPoint &pos, int radius)
Plot a - bar centered on the position.
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:106
virtual void ThickCircle(const wxPoint &pos, int diametre, int width, EDA_DRAW_MODE_T tracemode, void *aData)
int PointCount() const
Function PointCount()
static const unsigned MARKER_COUNT
Draw a marker (used for the drill map)
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
double GetDotMarkLenIU() const
bool colorMode
virtual ~PLOTTER()
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
char penState
Current pen state: 'U', 'D' or 'Z' (see PenTo)
double m_IUsPerDecimil
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=NULL)=0
Function PlotPoly.
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
wxPoint plotOffset
Plot offset (in IUs)
static const int delta[8][2]
Definition: solve.cpp:112
PAGE_INFO pageInfo
virtual void ThickArc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, int width, EDA_DRAW_MODE_T tracemode, void *aData)
This file contains miscellaneous commonly used macros and functions.
virtual void ThickRect(const wxPoint &p1, const wxPoint &p2, int width, EDA_DRAW_MODE_T tracemode, void *aData)
bool m_yaxisReversed
true to mirror horizontally (else vertically)
void LineTo(const wxPoint &pos)
bool m_plotMirror
X axis orientation (SVG) and plot mirrored (only for PS, PDF HPGL and SVG)
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:271
T AddAngles(T a1, T2 a2)
Add two angles (keeping the result normalized). T2 is here.
Definition: trigo.h:267
Base window classes and related definitions.
EDA_DRAW_MODE_T
Definition: eda_text.h:62
Class PAGE_INFO describes the page size and margins of a paper page on which to eventually print or p...
double plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
virtual DPOINT userToDeviceSize(const wxSize &size)
Modifies size according to the plotter scale factors (wxSize version, returns a DPOINT) ...
void markerSlash(const wxPoint &pos, int radius)
Plot a / bar centered on the position.
void Marker(const wxPoint &position, int diametre, unsigned aShapeId)
Draw a pattern shape number aShapeId, to coord position.
void MoveTo(const wxPoint &pos)
virtual DPOINT userToDeviceCoordinates(const wxPoint &aCoordinate)
Modifies coordinates according to the orientation, scale factor, and offsets trace.
double cosdecideg(double r, double a)
Circle generation utility: computes r * cos(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:333
virtual void PlotImage(const wxImage &aImage, const wxPoint &aPos, double aScaleFactor)
Function PlotImage Only Postscript plotters can plot bitmaps for plotters that cannot plot a bitmap...
double sindecideg(double r, double a)
Circle generation utility: computes r * sin(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:324
BASE_SCREEN class implementation.
int currentPenWidth
virtual int GetCurrentLineWidth() const
#define max(a, b)
Definition: auxiliary.h:86
int defaultPenWidth
true to generate a negative image (PS mode mainly)
virtual void FlashPadOval(const wxPoint &aPadPos, const wxSize &aSize, double aPadOrient, EDA_DRAW_MODE_T aTraceMode, void *aData)=0
virtual function FlashPadOval
Class SHAPE_LINE_CHAIN.
virtual void ThickSegment(const wxPoint &start, const wxPoint &end, int width, EDA_DRAW_MODE_T tracemode, void *aData)
void markerLozenge(const wxPoint &position, int radius)
Plot a lozenge centered on the position.
FILE * outputFile
true if the Y axis is top to bottom (SVG)
double GetDashGapLenIU() const
The common library.
void markerCircle(const wxPoint &pos, int radius)
Plot a circle centered on the position.
virtual void Rect(const wxPoint &p1, const wxPoint &p2, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH)=0
virtual void Arc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH)
Generic fallback: arc rendered as a polyline.
FILL_T
Enum FILL_T is the set of fill types used in plotting or drawing enclosed areas.
Definition: base_struct.h:70
This file is part of the common libary.
Basic classes for most KiCad items.
double GetDashMarkLenIU() const
double iuPerDeviceUnit
Device scale (from IUs to plotter device units - usually decimils)
void segmentAsOval(const wxPoint &start, const wxPoint &end, int width, EDA_DRAW_MODE_T tracemode)
Cdonvert a thick segment and plot it as an oval.
VECTOR2< double > DPOINT
Definition: vector2d.h:594
wxString filename
void markerVBar(const wxPoint &pos, int radius)
Plot a | bar centered on the position.
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
void markerSquare(const wxPoint &position, int radius)
Plot a square centered on the position.
void markerBackSlash(const wxPoint &pos, int radius)
Plot a \ bar centered on the position.
void sketchOval(const wxPoint &pos, const wxSize &size, double orient, int width)
virtual void Circle(const wxPoint &pos, int diametre, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH)=0
wxSize paperSize
Paper size in IU - not in mils.
bool m_mirrorIsHorizontal
virtual void SetCurrentLineWidth(int width, void *aData=NULL)=0
Set the line width for the next drawing.
bool negativeMode
true to plot in color, false to plot in black and white