kicad/common/common_plotGERBER_functions...

517 lines
14 KiB
C++
Raw Normal View History

2008-12-23 07:37:39 +00:00
/******************************************/
/* Kicad: Common plot GERBER Routines */
/******************************************/
#include "fctsys.h"
#include "gr_basic.h"
#include "trigo.h"
#include "wxstruct.h"
#include "base_struct.h"
#include "common.h"
#include "plot_common.h"
#include "macros.h"
#include "kicad_string.h"
2008-12-23 07:37:39 +00:00
/***************************************************************************/
void Gerber_Plotter::set_viewport( wxPoint offset,
double aScale, int orient )
2008-12-23 07:37:39 +00:00
/***************************************************************************/
/** function set_viewport
2008-12-23 07:37:39 +00:00
* Set the plot offset for the current plotting
* @param aOffset = plot offset
* @param aScale = coordinate scale (scale coefficient for coordinates)
2008-12-23 07:37:39 +00:00
*/
{
wxASSERT(!output_file);
wxASSERT(orient == 0);
plot_orient_options = 0;
plot_offset = offset;
wxASSERT(aScale == 1);
plot_scale = 1;
device_scale = 1;
set_default_line_width(100); /* epaisseur du trait standard en 1/1000 pouce */
2008-12-23 07:37:39 +00:00
}
/******************************************************************/
void Gerber_Plotter::start_plot( FILE *aFile )
/*****************************************************************/
/** Function start_plot
2008-12-23 07:37:39 +00:00
* Write GERBER header to file
* initialize global variable g_Plot_PlotOutputFile
* @param aFile: an opened file to write to
*/
{
char Line[1024];
wxASSERT(!output_file);
final_file = aFile;
work_file = tmpfile();
output_file = work_file;
2008-12-23 07:37:39 +00:00
DateAndTime( Line );
wxString Title = creator + wxT( " " ) + GetBuildVersion();
fprintf( output_file, "G04 (created by %s) date %s*\n", CONV_TO_UTF8( Title ), Line );
2008-12-23 07:37:39 +00:00
// Specify linear interpol (G01), unit = INCH (G70), abs format (G90):
fputs( "G01*\nG70*\nG90*\n", output_file );
fputs( "%MOIN*%\n", output_file ); // set unites = INCHES
2008-12-23 07:37:39 +00:00
/* Set gerber format to 3.4 */
fputs( "G04 Gerber Fmt 3.4, Leading zero omitted, Abs format*\n%FSLAX34Y34*%\n",
output_file);
2008-12-23 07:37:39 +00:00
fputs( "G04 APERTURE LIST*\n", output_file );
/* Select the default aperture */
set_current_line_width(-1);
2008-12-23 07:37:39 +00:00
}
/******************************************************************/
void Gerber_Plotter::end_plot( )
/*****************************************************************/
{
char line[1024];
wxString msg;
wxASSERT(output_file);
/* Outfile is actually a temporary file! */
fputs( "M02*\n", output_file );
fflush(output_file);
rewind( work_file ); // work_file == output_file !!!
output_file = final_file;
2008-12-23 07:37:39 +00:00
// Placement des Apertures en RS274X
while( fgets( line, 1024, work_file ) )
{
fputs( line, output_file );
if( strcmp( strtok( line, "\n\r" ), "G04 APERTURE LIST*" ) == 0 )
{
write_aperture_list();
fputs( "G04 APERTURE END LIST*\n", output_file );
}
}
fclose( work_file );
fclose( final_file );
output_file = 0;
}
/*************************************************************************************/
void Gerber_Plotter::set_default_line_width( int width )
/*************************************************************************************/
/* Set the default line width (in 1/1000 inch) for the current plotting
*/
{
default_pen_width = width; // epaisseur du trait standard en 1/1000 pouce
current_aperture = apertures.end();
}
/***************************************/
void Gerber_Plotter::set_current_line_width( int width )
/***************************************/
/* Set the Current line width (in 1/1000 inch) for the next plot
2008-12-23 07:37:39 +00:00
*/
{
int pen_width;
if( width > 0 )
pen_width = width;
else
pen_width = default_pen_width;
select_aperture(wxSize(pen_width, pen_width), Aperture::Plotting);
current_pen_width = pen_width;
}
/******************************************************/
vector<Aperture>::iterator Gerber_Plotter::get_aperture(const wxSize &size,
Aperture::Aperture_Type type)
/******************************************************/
{
int last_D_code = 9;
// Search an existing aperture
vector<Aperture>::iterator tool = apertures.begin();
while (tool != apertures.end()) {
last_D_code = tool->D_code;
if ((tool->type == type)
&& (tool->size == size))
return tool;
tool++;
}
// Allocate a new aperture
Aperture new_tool;
new_tool.size = size;
new_tool.type = type;
new_tool.D_code = last_D_code+1;
apertures.push_back(new_tool);
return apertures.end()-1;
}
/******************************************************/
void Gerber_Plotter::select_aperture(const wxSize &size, Aperture::Aperture_Type type)
/******************************************************/
{
wxASSERT(output_file);
if ((current_aperture == apertures.end())
|| (current_aperture->type != type)
|| (current_aperture->size != size)) {
/* Pick an existing aperture or create a new one */
current_aperture = get_aperture(size, type);
fprintf( output_file, "G54D%d*\n", current_aperture->D_code );
}
}
/******************************************************/
void Gerber_Plotter::write_aperture_list( )
/******************************************************/
/* Genere la liste courante des D_CODES
* Retourne le nombre de D_Codes utilises
* Genere une sequence RS274X
*/
{
wxASSERT(output_file);
char cbuf[1024];
/* Init : */
for (vector<Aperture>::iterator tool=apertures.begin();
tool != apertures.end(); tool++)
{
const float fscale = 0.0001f * plot_scale; // For 3.4 format
char* text;
text = cbuf + sprintf( cbuf, "%%ADD%d", tool->D_code);
switch( tool->type )
{
case Aperture::Circle:
sprintf( text, "C,%f*%%\n", tool->size.x * fscale );
break;
case Aperture::Rect:
sprintf( text, "R,%fX%f*%%\n", tool->size.x * fscale,
tool->size.y * fscale );
break;
case Aperture::Plotting:
sprintf( text, "C,%f*%%\n", tool->size.x * fscale );
break;
case Aperture::Oval:
sprintf( text, "O,%fX%f*%%\n", tool->size.x * fscale,
tool->size.y * fscale );
break;
}
fputs( cbuf, output_file );
}
}
/**********************************************/
void Gerber_Plotter::pen_to( wxPoint aPos, char plume )
{
wxASSERT(output_file);
user_to_device_coordinates( aPos );
switch ( plume )
2008-12-23 07:37:39 +00:00
{
case 'Z':
break;
2008-12-23 07:37:39 +00:00
case 'U':
fprintf( output_file, "X%5.5dY%5.5dD02*\n", aPos.x, aPos.y );
2008-12-23 07:37:39 +00:00
break;
case 'D':
fprintf( output_file, "X%5.5dY%5.5dD01*\n", aPos.x, aPos.y );
2008-12-23 07:37:39 +00:00
}
pen_state = plume;
2008-12-23 07:37:39 +00:00
}
/************************************************************/
void Gerber_Plotter::rect( wxPoint p1, wxPoint p2, FILL_T fill, int width )
/************************************************************/
2008-12-23 07:37:39 +00:00
{
wxASSERT(output_file);
int coord[10] = {
p1.x, p1.y,
p1.x, p2.y,
p2.x, p2.y,
p2.x, p1.y,
p1.x, p1.y
};
poly( 5, coord, fill, width);
2008-12-23 07:37:39 +00:00
}
/********************************************************************/
void Gerber_Plotter::circle( wxPoint pos, int diametre, FILL_T fill, int width )
2008-12-23 07:37:39 +00:00
/********************************************************************/
/** Function PlotCircle_GERBER
* writes a non filled circle to output file
* Plot one circle as segments (6 to 16 depending on its radius
* @param aCentre = centre coordintes
* @param aRadius = radius of the circle
* @param aWidth = line width
2008-12-23 07:37:39 +00:00
*/
{
wxASSERT(output_file);
wxPoint start,end;
double aRadius = diametre / 2;
const int delta = 3600/32; /* increment (in 0.1 degrees) to draw circles */
2008-12-23 07:37:39 +00:00
start.x = pos.x + aRadius;
start.y = pos.y;
set_current_line_width(width);
move_to(start);
for(int ii = delta; ii < 3600; ii += delta )
2008-12-23 07:37:39 +00:00
{
end.x = pos.x + (int) (aRadius * fcosinus[ii]);
end.y = pos.y + (int) (aRadius * fsinus[ii]);
line_to(end);
2008-12-23 07:37:39 +00:00
}
finish_to( start );
2008-12-23 07:37:39 +00:00
}
/***************************************************************/
void Gerber_Plotter::poly( int nb_segm, int* coord, FILL_T fill, int width )
2008-12-23 07:37:39 +00:00
/***************************************************************/
/** Function PlotFilledPolygon_GERBER
* writes a filled polyline to output file
* @param aCornersCount = numer of corners
* @param aCoord = buffer of corners coordinates
*/
{
wxASSERT(output_file);
2008-12-23 07:37:39 +00:00
wxPoint pos, startpos;
set_current_line_width(width);
2008-12-23 07:37:39 +00:00
if (fill)
fputs( "G36*\n", output_file );
startpos.x = *coord++;
startpos.y = *coord++;
move_to(startpos);
for(int ii = 1; ii < nb_segm; ii++ )
2008-12-23 07:37:39 +00:00
{
pos.x = *coord++;
pos.y = *coord++;
line_to( pos );
}
if (fill)
{
finish_to(startpos);
fputs( "G37*\n", output_file );
}
else
{
pen_finish();
2008-12-23 07:37:39 +00:00
}
}
void Gerber_Plotter::flash_pad_circle(wxPoint pos, int diametre,
GRTraceMode trace_mode)
/* Plot a circular pad or via at the user position pos
*/
{
wxASSERT(output_file);
wxSize size( diametre, diametre );
2008-12-23 07:37:39 +00:00
switch (trace_mode)
{
case FILAIRE:
case SKETCH:
set_current_line_width(-1);
circle(pos, diametre-current_pen_width, NO_FILL);
break;
case FILLED:
user_to_device_coordinates( pos );
select_aperture(size, Aperture::Circle);
fprintf( output_file, "X%5.5dY%5.5dD03*\n", pos.x, pos.y );
break;
}
}
void Gerber_Plotter::flash_pad_oval(wxPoint pos, wxSize size, int orient,
GRTraceMode trace_mode)
/* Trace 1 pastille PAD_OVAL en position pos_X,Y:
* dimensions dx, dy,
* orientation orient
* Pour une orientation verticale ou horizontale, la forme est flashee
* Pour une orientation quelconque la forme est tracee comme un segment
*/
2008-12-23 07:37:39 +00:00
{
wxASSERT(output_file);
int x0, y0, x1, y1, delta;
/* Trace de la forme flashee */
if(( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
&& trace_mode == FILLED)
{
if( orient == 900 || orient == 2700 ) /* orient tournee de 90 deg */
EXCHG( size.x, size.y );
user_to_device_coordinates( pos );
select_aperture(size, Aperture::Oval);
fprintf( output_file, "X%5.5dY%5.5dD03*\n", pos.x, pos.y );
}
else /* Forme tracee comme un segment */
2008-12-23 07:37:39 +00:00
{
if( size.x > size.y )
{
EXCHG( size.x, size.y );
if( orient < 2700 )
orient += 900;
else
orient -= 2700;
}
if (trace_mode == FILLED)
{
/* la pastille est ramenee a une pastille ovale avec dy > dx */
delta = size.y - size.x;
x0 = 0;
y0 = -delta / 2;
x1 = 0;
y1 = delta / 2;
RotatePoint( &x0, &y0, orient );
RotatePoint( &x1, &y1, orient );
thick_segment( wxPoint( pos.x + x0, pos.y + y0 ),
wxPoint( pos.x + x1, pos.y + y1 ),
size.x, trace_mode );
} else
sketch_oval(pos, size, orient, -1);
2008-12-23 07:37:39 +00:00
}
}
void Gerber_Plotter::flash_pad_rect(wxPoint pos, wxSize size,
int orient, GRTraceMode trace_mode)
/* Plot 1 rectangular pad
* donne par son centre, ses dimensions, et son orientation
* For a vertical or horizontal shape, the shape is an aperture (Dcode) and it is flashed
* For others orientations the shape is plotted as a polygon
*/
{
wxASSERT(output_file);
/* Trace de la forme flashee */
switch( orient )
{
case 900:
case 2700: /* la rotation de 90 ou 270 degres revient a permutter des dimensions */
EXCHG( size.x, size.y );
2008-12-23 07:37:39 +00:00
// Pass through
2008-12-23 07:37:39 +00:00
case 0:
case 1800:
switch (trace_mode) {
case FILAIRE:
case SKETCH:
set_current_line_width(-1);
rect(wxPoint(pos.x-(size.x-current_pen_width)/2,
pos.y-(size.y-current_pen_width)/2),
wxPoint(pos.x+(size.x-current_pen_width)/2,
pos.y+(size.y-current_pen_width)/2),
NO_FILL);
break;
case FILLED:
user_to_device_coordinates( pos );
select_aperture(size, Aperture::Rect);
fprintf( output_file, "X%5.5dY%5.5dD03*\n", pos.x, pos.y );
break;
}
break;
default: /* plot pad shape as polygon */
flash_pad_trapez( pos, size, wxSize( 0, 0 ), orient, trace_mode );
break;
}
2008-12-23 07:37:39 +00:00
}
void Gerber_Plotter::flash_pad_trapez(wxPoint pos, wxSize size, wxSize delta,
int orient, GRTraceMode trace_mode)
/* Trace 1 pad trapezoidal donne par :
* son centre pos.x,pos.y
* ses dimensions size.x et size.y
* les variations delta.x et delta.y ( 1 des deux au moins doit etre nulle)
* son orientation orient en 0.1 degres
* le mode de trace (FILLED, SKETCH, FILAIRE)
*
* Le trace n'est fait que pour un trapeze, c.a.d que delta.x ou delta.y
* = 0.
*
* les notation des sommets sont ( vis a vis de la table tracante )
*
* " 0 ------------- 3 "
* " . . "
* " . O . "
* " . . "
* " 1 ---- 2 "
*
*
* exemple de Disposition pour delta.y > 0, delta.x = 0
* " 1 ---- 2 "
* " . . "
* " . O . "
* " . . "
* " 0 ------------- 3 "
*
*
* exemple de Disposition pour delta.y = 0, delta.x > 0
* " 0 "
* " . . "
* " . . "
* " . 3 "
* " . . "
* " . O . "
* " . . "
* " . 2 "
* " . . "
* " . . "
* " 1 "
*/
{
wxASSERT(output_file);
int ii, jj;
int dx, dy;
wxPoint polygon[4]; /* polygon corners */
int coord[10];
int ddx, ddy;
/* calcul des dimensions optimales du spot choisi = 1/4 plus petite dim */
dx = size.x - abs( delta.y );
dy = size.y - abs( delta.x );
dx = size.x / 2;
dy = size.y / 2;
ddx = delta.x / 2;
ddy = delta.y / 2;
polygon[0].x = -dx - ddy;
polygon[0].y = +dy + ddx;
polygon[1].x = -dx + ddy;
polygon[1].y = -dy - ddx;
polygon[2].x = +dx - ddy;
polygon[2].y = -dy + ddx;
polygon[3].x = +dx + ddy;
polygon[3].y = +dy - ddx;
/* Dessin du polygone et Remplissage eventuel de l'interieur */
for( ii = 0, jj = 0; ii < 4; ii++ )
{
RotatePoint( &polygon[ii].x, &polygon[ii].y, orient );
coord[jj] = polygon[ii].x += pos.x;
jj++;
coord[jj] = polygon[ii].y += pos.y;
jj++;
}
coord[8]=coord[0];
coord[9]=coord[1];
set_current_line_width(-1);
poly( 5, coord, trace_mode==FILLED?FILLED_SHAPE:NO_FILL );
}
2008-12-23 07:37:39 +00:00