1291 lines
43 KiB
C++
1291 lines
43 KiB
C++
/**
|
|
* @file plot_rtn.cpp
|
|
* @brief Common plot routines.
|
|
*/
|
|
|
|
#include <fctsys.h>
|
|
#include <common.h>
|
|
#include <plot_common.h>
|
|
#include <base_struct.h>
|
|
#include <drawtxt.h>
|
|
#include <confirm.h>
|
|
#include <trigo.h>
|
|
#include <wxBasePcbFrame.h>
|
|
#include <pcbcommon.h>
|
|
#include <macros.h>
|
|
|
|
#include <class_board.h>
|
|
#include <class_module.h>
|
|
#include <class_track.h>
|
|
#include <class_edge_mod.h>
|
|
#include <class_pcb_text.h>
|
|
#include <class_zone.h>
|
|
#include <class_drawsegment.h>
|
|
#include <class_mire.h>
|
|
#include <class_dimension.h>
|
|
|
|
#include <pcbnew.h>
|
|
#include <pcbplot.h>
|
|
|
|
static void Plot_Edges_Modules( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts,
|
|
BOARD* pcb, int aLayerMask, EDA_DRAW_MODE_T trace_mode );
|
|
|
|
static void PlotTextModule( PLOTTER* aPlotter, TEXTE_MODULE* pt_texte,
|
|
EDA_DRAW_MODE_T trace_mode, EDA_COLOR_T aColor );
|
|
|
|
|
|
/* Creates the plot for silkscreen layers
|
|
*/
|
|
void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, long aLayerMask,
|
|
const PCB_PLOT_PARAMS& aPlotOpt )
|
|
{
|
|
TEXTE_MODULE* pt_texte;
|
|
|
|
EDA_DRAW_MODE_T trace_mode = aPlotOpt.GetMode();
|
|
|
|
// Plot edge layer and graphic items
|
|
|
|
for( EDA_ITEM* item = aBoard->m_Drawings; item; item = item->Next() )
|
|
{
|
|
switch( item->Type() )
|
|
{
|
|
case PCB_LINE_T:
|
|
PlotDrawSegment( aPlotter, aPlotOpt, (DRAWSEGMENT*) item, aLayerMask, trace_mode );
|
|
break;
|
|
|
|
case PCB_TEXT_T:
|
|
PlotTextePcb( aPlotter, aPlotOpt, (TEXTE_PCB*) item, aLayerMask, trace_mode );
|
|
break;
|
|
|
|
case PCB_DIMENSION_T:
|
|
PlotDimension( aPlotter, aPlotOpt, (DIMENSION*) item, aLayerMask, trace_mode );
|
|
break;
|
|
|
|
case PCB_TARGET_T:
|
|
PlotPcbTarget( aPlotter, aPlotOpt, (PCB_TARGET*) item, aLayerMask, trace_mode );
|
|
break;
|
|
|
|
case PCB_MARKER_T:
|
|
break;
|
|
|
|
default:
|
|
DisplayError( NULL, wxT( "PlotSilkScreen() error: unexpected Type()" ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Plot footprint outlines :
|
|
Plot_Edges_Modules( aPlotter, aPlotOpt, aBoard, aLayerMask, trace_mode );
|
|
|
|
// Plot pads (creates pads outlines, for pads on silkscreen layers)
|
|
int layersmask_plotpads = aLayerMask;
|
|
// Calculate the mask layers of allowed layers for pads
|
|
|
|
if( !aPlotOpt.GetPlotPadsOnSilkLayer() ) // Do not plot pads on silk screen layers
|
|
layersmask_plotpads &= ~(SILKSCREEN_LAYER_BACK | SILKSCREEN_LAYER_FRONT );
|
|
|
|
if( layersmask_plotpads )
|
|
{
|
|
for( MODULE* Module = aBoard->m_Modules; Module; Module = Module->Next() )
|
|
{
|
|
for( D_PAD * pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
|
|
{
|
|
// See if the pad is on this layer
|
|
if( (pad->GetLayerMask() & layersmask_plotpads) == 0 )
|
|
continue;
|
|
|
|
wxPoint shape_pos = pad->ReturnShapePos();
|
|
|
|
switch( pad->GetShape() )
|
|
{
|
|
case PAD_CIRCLE:
|
|
aPlotter->FlashPadCircle( shape_pos, pad->GetSize().x, LINE );
|
|
break;
|
|
|
|
case PAD_OVAL:
|
|
aPlotter->FlashPadOval( shape_pos, pad->GetSize(),
|
|
pad->GetOrientation(), LINE );
|
|
break;
|
|
|
|
case PAD_TRAPEZOID:
|
|
{
|
|
wxPoint coord[4];
|
|
pad->BuildPadPolygon( coord, wxSize(0,0), 0 );
|
|
aPlotter->FlashPadTrapez( shape_pos, coord,
|
|
pad->GetOrientation(), LINE );
|
|
}
|
|
break;
|
|
|
|
case PAD_RECT:
|
|
default:
|
|
aPlotter->FlashPadRect( shape_pos, pad->GetSize(),
|
|
pad->GetOrientation(), LINE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Plot footprints fields (ref, value ...)
|
|
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
|
|
{
|
|
// see if we want to plot VALUE and REF fields
|
|
bool trace_val = aPlotOpt.GetPlotValue();
|
|
bool trace_ref = aPlotOpt.GetPlotReference();
|
|
|
|
TEXTE_MODULE* text = module->m_Reference;
|
|
unsigned textLayer = text->GetLayer();
|
|
|
|
if( textLayer >= 32 )
|
|
{
|
|
wxString errMsg;
|
|
|
|
errMsg.Printf( _( "Your BOARD has a bad layer number of %u for \
|
|
module\n %s's \"reference\" text." ),
|
|
textLayer, GetChars( module->GetReference() ) );
|
|
DisplayError( NULL, errMsg );
|
|
return;
|
|
}
|
|
|
|
if( ( ( 1 << textLayer ) & aLayerMask ) == 0 )
|
|
trace_ref = false;
|
|
|
|
if( !text->IsVisible() && !aPlotOpt.GetPlotInvisibleText() )
|
|
trace_ref = false;
|
|
|
|
text = module->m_Value;
|
|
textLayer = text->GetLayer();
|
|
|
|
if( textLayer > 32 )
|
|
{
|
|
wxString errMsg;
|
|
|
|
errMsg.Printf( _( "Your BOARD has a bad layer number of %u for \
|
|
module\n %s's \"value\" text." ),
|
|
textLayer, GetChars( module->GetReference() ) );
|
|
DisplayError( NULL, errMsg );
|
|
return;
|
|
}
|
|
|
|
if( ( (1 << textLayer) & aLayerMask ) == 0 )
|
|
trace_val = false;
|
|
|
|
if( !text->IsVisible() && !aPlotOpt.GetPlotInvisibleText() )
|
|
trace_val = false;
|
|
|
|
// Plot text fields, if allowed
|
|
if( trace_ref )
|
|
PlotTextModule( aPlotter, module->m_Reference,
|
|
trace_mode, aPlotOpt.GetReferenceColor() );
|
|
|
|
if( trace_val )
|
|
PlotTextModule( aPlotter, module->m_Value,
|
|
trace_mode, aPlotOpt.GetValueColor() );
|
|
|
|
for( pt_texte = (TEXTE_MODULE*) module->m_Drawings.GetFirst();
|
|
pt_texte != NULL;
|
|
pt_texte = pt_texte->Next() )
|
|
{
|
|
if( pt_texte->Type() != PCB_MODULE_TEXT_T )
|
|
continue;
|
|
|
|
if( !aPlotOpt.GetPlotOtherText() )
|
|
continue;
|
|
|
|
if( !pt_texte->IsVisible() && !aPlotOpt.GetPlotInvisibleText() )
|
|
continue;
|
|
|
|
textLayer = pt_texte->GetLayer();
|
|
|
|
if( textLayer >= 32 )
|
|
{
|
|
wxString errMsg;
|
|
|
|
errMsg.Printf( _( "Your BOARD has a bad layer number of %u \
|
|
for module\n %s's \"module text\" text of %s." ),
|
|
textLayer, GetChars( module->GetReference() ),
|
|
GetChars( pt_texte->m_Text ) );
|
|
DisplayError( NULL, errMsg );
|
|
return;
|
|
}
|
|
|
|
if( !( ( 1 << textLayer ) & aLayerMask ) )
|
|
continue;
|
|
|
|
PlotTextModule( aPlotter, pt_texte,
|
|
trace_mode, aPlotOpt.GetColor() );
|
|
}
|
|
}
|
|
|
|
// Plot filled areas
|
|
for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
|
|
{
|
|
ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii );
|
|
|
|
if( ( ( 1 << edge_zone->GetLayer() ) & aLayerMask ) == 0 )
|
|
continue;
|
|
|
|
PlotFilledAreas( aPlotter, aPlotOpt, edge_zone, trace_mode );
|
|
}
|
|
|
|
// Plot segments used to fill zone areas (outdated, but here for old boards
|
|
// compatibility):
|
|
for( SEGZONE* seg = aBoard->m_Zone; seg != NULL; seg = seg->Next() )
|
|
{
|
|
if( ( ( 1 << seg->GetLayer() ) & aLayerMask ) == 0 )
|
|
continue;
|
|
|
|
aPlotter->ThickSegment( seg->m_Start, seg->m_End, seg->m_Width, trace_mode );
|
|
}
|
|
}
|
|
|
|
|
|
static void PlotTextModule( PLOTTER* aPlotter, TEXTE_MODULE* pt_texte,
|
|
EDA_DRAW_MODE_T trace_mode, EDA_COLOR_T aColor )
|
|
{
|
|
wxSize size;
|
|
wxPoint pos;
|
|
int orient, thickness;
|
|
|
|
// calculate some text parameters :
|
|
size = pt_texte->m_Size;
|
|
pos = pt_texte->m_Pos;
|
|
|
|
orient = pt_texte->GetDrawRotation();
|
|
|
|
thickness = pt_texte->m_Thickness;
|
|
|
|
if( trace_mode == LINE )
|
|
thickness = -1;
|
|
|
|
if( pt_texte->m_Mirror )
|
|
NEGATE( size.x ); // Text is mirrored
|
|
|
|
// Non bold texts thickness is clamped at 1/6 char size by the low level draw function.
|
|
// but in Pcbnew we do not manage bold texts and thickness up to 1/4 char size
|
|
// (like bold text) and we manage the thickness.
|
|
// So we set bold flag to true
|
|
bool allow_bold = pt_texte->m_Bold || thickness;
|
|
|
|
aPlotter->Text( pos, aColor,
|
|
pt_texte->m_Text,
|
|
orient, size,
|
|
pt_texte->m_HJustify, pt_texte->m_VJustify,
|
|
thickness, pt_texte->m_Italic, allow_bold );
|
|
}
|
|
|
|
|
|
void PlotDimension( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts,
|
|
DIMENSION* aDim, int aLayerMask,
|
|
EDA_DRAW_MODE_T trace_mode )
|
|
{
|
|
if( (GetLayerMask( aDim->GetLayer() ) & aLayerMask) == 0 )
|
|
return;
|
|
|
|
DRAWSEGMENT draw;
|
|
|
|
draw.SetWidth( (trace_mode==LINE) ? -1 : aDim->GetWidth() );
|
|
draw.SetLayer( aDim->GetLayer() );
|
|
|
|
PlotTextePcb( aPlotter, aPlotOpts, &aDim->m_Text, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_crossBarOx, aDim->m_crossBarOy ));
|
|
draw.SetEnd( wxPoint( aDim->m_crossBarFx, aDim->m_crossBarFy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_featureLineGOx, aDim->m_featureLineGOy ));
|
|
draw.SetEnd( wxPoint( aDim->m_featureLineGFx, aDim->m_featureLineGFy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_featureLineDOx, aDim->m_featureLineDOy ));
|
|
draw.SetEnd( wxPoint( aDim->m_featureLineDFx, aDim->m_featureLineDFy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_arrowD1Ox, aDim->m_arrowD1Oy ));
|
|
draw.SetEnd( wxPoint( aDim->m_arrowD1Fx, aDim->m_arrowD1Fy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_arrowD2Ox, aDim->m_arrowD2Oy ));
|
|
draw.SetEnd( wxPoint( aDim->m_arrowD2Fx, aDim->m_arrowD2Fy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_arrowG1Ox, aDim->m_arrowG1Oy ));
|
|
draw.SetEnd( wxPoint( aDim->m_arrowG1Fx, aDim->m_arrowG1Fy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( aDim->m_arrowG2Ox, aDim->m_arrowG2Oy ));
|
|
draw.SetEnd( wxPoint( aDim->m_arrowG2Fx, aDim->m_arrowG2Fy ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
}
|
|
|
|
|
|
void PlotPcbTarget( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts,
|
|
PCB_TARGET* aMire, int aLayerMask,
|
|
EDA_DRAW_MODE_T trace_mode )
|
|
{
|
|
int dx1, dx2, dy1, dy2, radius;
|
|
|
|
if( (GetLayerMask( aMire->GetLayer() ) & aLayerMask) == 0 )
|
|
return;
|
|
|
|
DRAWSEGMENT draw;
|
|
|
|
draw.SetShape( S_CIRCLE );
|
|
draw.SetWidth( ( trace_mode == LINE ) ? -1 : aMire->GetWidth() );
|
|
draw.SetLayer( aMire->GetLayer() );
|
|
|
|
draw.SetStart( aMire->GetPosition() );
|
|
draw.SetEnd( wxPoint( draw.GetStart().x + ( aMire->GetSize() / 4 ), draw.GetStart().y ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetShape( S_SEGMENT );
|
|
|
|
radius = aMire->GetSize() / 2;
|
|
dx1 = radius;
|
|
dy1 = 0;
|
|
dx2 = 0;
|
|
dy2 = radius;
|
|
|
|
if( aMire->GetShape() ) // Shape X
|
|
{
|
|
dx1 = dy1 = ( radius * 7 ) / 5;
|
|
dx2 = dx1;
|
|
dy2 = -dy1;
|
|
}
|
|
|
|
wxPoint mirePos( aMire->GetPosition() );
|
|
|
|
draw.SetStart( wxPoint( mirePos.x - dx1, mirePos.y - dy1 ));
|
|
draw.SetEnd( wxPoint( mirePos.x + dx1, mirePos.y + dy1 ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
|
|
draw.SetStart( wxPoint( mirePos.x - dx2, mirePos.y - dy2 ));
|
|
draw.SetEnd( wxPoint( mirePos.x + dx2, mirePos.y + dy2 ));
|
|
PlotDrawSegment( aPlotter, aPlotOpts, &draw, aLayerMask, trace_mode );
|
|
}
|
|
|
|
|
|
// Plot footprints graphic items (outlines)
|
|
static void Plot_Edges_Modules( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts,
|
|
BOARD* aPcb, int aLayerMask,
|
|
EDA_DRAW_MODE_T trace_mode )
|
|
{
|
|
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
|
{
|
|
for( EDGE_MODULE* edge = (EDGE_MODULE*) module->m_Drawings.GetFirst();
|
|
edge;
|
|
edge = edge->Next() )
|
|
{
|
|
if( edge->Type() != PCB_MODULE_EDGE_T )
|
|
continue;
|
|
|
|
if( ( GetLayerMask( edge->GetLayer() ) & aLayerMask ) == 0 )
|
|
continue;
|
|
|
|
Plot_1_EdgeModule( aPlotter, aPlotOpts, edge, trace_mode, aLayerMask );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//* Plot a graphic item (outline) relative to a footprint
|
|
void Plot_1_EdgeModule( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts,
|
|
EDGE_MODULE* aEdge, EDA_DRAW_MODE_T trace_mode,
|
|
int masque_layer )
|
|
{
|
|
int type_trace; // Type of item to plot.
|
|
int thickness; // Segment thickness.
|
|
int radius; // Circle radius.
|
|
|
|
if( aEdge->Type() != PCB_MODULE_EDGE_T )
|
|
return;
|
|
|
|
type_trace = aEdge->GetShape();
|
|
thickness = aEdge->GetWidth();
|
|
|
|
wxPoint pos( aEdge->GetStart() );
|
|
wxPoint end( aEdge->GetEnd() );
|
|
|
|
switch( type_trace )
|
|
{
|
|
case S_SEGMENT:
|
|
aPlotter->ThickSegment( pos, end, thickness, trace_mode );
|
|
break;
|
|
|
|
case S_CIRCLE:
|
|
radius = (int) hypot( (double) ( end.x - pos.x ),
|
|
(double) ( end.y - pos.y ) );
|
|
aPlotter->ThickCircle( pos, radius * 2, thickness, trace_mode );
|
|
break;
|
|
|
|
case S_ARC:
|
|
{
|
|
radius = (int) hypot( (double) ( end.x - pos.x ),
|
|
(double) ( end.y - pos.y ) );
|
|
|
|
double startAngle = ArcTangente( end.y - pos.y, end.x - pos.x );
|
|
|
|
double endAngle = startAngle + aEdge->GetAngle();
|
|
|
|
if ( ( aPlotOpts.GetFormat() == PLOT_FORMAT_DXF ) &&
|
|
( masque_layer & ( SILKSCREEN_LAYER_BACK | DRAW_LAYER | COMMENT_LAYER ) ) )
|
|
aPlotter->ThickArc( pos, -startAngle, -endAngle, radius,
|
|
thickness, trace_mode );
|
|
else
|
|
aPlotter->ThickArc( pos, -endAngle, -startAngle, radius,
|
|
thickness, trace_mode );
|
|
}
|
|
break;
|
|
|
|
case S_POLYGON:
|
|
{
|
|
const std::vector<wxPoint>& polyPoints = aEdge->GetPolyPoints();
|
|
|
|
if( polyPoints.size() <= 1 ) // Malformed polygon
|
|
break;
|
|
|
|
// We must compute true coordinates from m_PolyList
|
|
// which are relative to module position, orientation 0
|
|
MODULE* module = aEdge->GetParentModule();
|
|
|
|
std::vector< wxPoint > cornerList;
|
|
|
|
cornerList.reserve( polyPoints.size() );
|
|
|
|
for( unsigned ii = 0; ii < polyPoints.size(); ii++ )
|
|
{
|
|
wxPoint corner = polyPoints[ii];
|
|
|
|
if( module )
|
|
{
|
|
RotatePoint( &corner, module->GetOrientation() );
|
|
corner += module->GetPosition();
|
|
}
|
|
|
|
cornerList.push_back( corner );
|
|
}
|
|
|
|
aPlotter->PlotPoly( cornerList, FILLED_SHAPE, thickness );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Plot a PCB Text, i;e. a text found on a copper or technical layer
|
|
void PlotTextePcb( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts, TEXTE_PCB* pt_texte, int aLayerMask,
|
|
EDA_DRAW_MODE_T trace_mode )
|
|
{
|
|
int orient, thickness;
|
|
wxPoint pos;
|
|
wxSize size;
|
|
|
|
if( pt_texte->m_Text.IsEmpty() )
|
|
return;
|
|
|
|
if( ( GetLayerMask( pt_texte->GetLayer() ) & aLayerMask ) == 0 )
|
|
return;
|
|
|
|
size = pt_texte->m_Size;
|
|
pos = pt_texte->m_Pos;
|
|
orient = pt_texte->m_Orient;
|
|
thickness = ( trace_mode==LINE ) ? -1 : pt_texte->m_Thickness;
|
|
|
|
if( pt_texte->m_Mirror )
|
|
size.x = -size.x;
|
|
|
|
// Non bold texts thickness is clamped at 1/6 char size by the low level draw function.
|
|
// but in Pcbnew we do not manage bold texts and thickness up to 1/4 char size
|
|
// (like bold text) and we manage the thickness.
|
|
// So we set bold flag to true
|
|
bool allow_bold = pt_texte->m_Bold || thickness;
|
|
|
|
if( pt_texte->m_MultilineAllowed )
|
|
{
|
|
wxArrayString* list = wxStringSplit( pt_texte->m_Text, '\n' );
|
|
wxPoint offset;
|
|
|
|
offset.y = pt_texte->GetInterline();
|
|
|
|
RotatePoint( &offset, orient );
|
|
|
|
for( unsigned i = 0; i < list->Count(); i++ )
|
|
{
|
|
wxString txt = list->Item( i );
|
|
aPlotter->Text( pos, UNSPECIFIED_COLOR, txt, orient, size,
|
|
pt_texte->m_HJustify, pt_texte->m_VJustify,
|
|
thickness, pt_texte->m_Italic, allow_bold );
|
|
pos += offset;
|
|
}
|
|
|
|
delete list;
|
|
}
|
|
else
|
|
{
|
|
aPlotter->Text( pos, UNSPECIFIED_COLOR, pt_texte->m_Text, orient, size,
|
|
pt_texte->m_HJustify, pt_texte->m_VJustify,
|
|
thickness, pt_texte->m_Italic, allow_bold );
|
|
}
|
|
}
|
|
|
|
|
|
/* Plot areas (given by .m_FilledPolysList member) in a zone
|
|
*/
|
|
void PlotFilledAreas( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts, ZONE_CONTAINER* aZone, EDA_DRAW_MODE_T trace_mode )
|
|
{
|
|
std::vector<CPolyPt> polysList = aZone->GetFilledPolysList();
|
|
unsigned imax = polysList.size();
|
|
|
|
if( imax == 0 ) // Nothing to draw
|
|
return;
|
|
|
|
// We need a buffer to store corners coordinates:
|
|
static std::vector< wxPoint > cornerList;
|
|
cornerList.clear();
|
|
|
|
/* Plot all filled areas: filled areas have a filled area and a thick
|
|
* outline we must plot the filled area itself ( as a filled polygon
|
|
* OR a set of segments ) and plot the thick outline itself
|
|
*
|
|
* in non filled mode the outline is plotted, but not the filling items
|
|
*/
|
|
for( unsigned ic = 0; ic < imax; ic++ )
|
|
{
|
|
CPolyPt* corner = &polysList[ic];
|
|
cornerList.push_back( wxPoint( corner->x, corner->y) );
|
|
|
|
if( corner->end_contour ) // Plot the current filled area outline
|
|
{
|
|
// First, close the outline
|
|
if( cornerList[0] != cornerList[cornerList.size() - 1] )
|
|
{
|
|
cornerList.push_back( cornerList[0] );
|
|
}
|
|
|
|
// Plot the current filled area and its outline
|
|
if( trace_mode == FILLED )
|
|
{
|
|
// Plot the current filled area polygon
|
|
if( aZone->m_FillMode == 0 ) // We are using solid polygons
|
|
{ // (if != 0: using segments )
|
|
aPlotter->PlotPoly( cornerList, FILLED_SHAPE );
|
|
}
|
|
else // We are using areas filled by
|
|
{ // segments: plot them )
|
|
for( unsigned iseg = 0; iseg < aZone->m_FillSegmList.size(); iseg++ )
|
|
{
|
|
wxPoint start = aZone->m_FillSegmList[iseg].m_Start;
|
|
wxPoint end = aZone->m_FillSegmList[iseg].m_End;
|
|
aPlotter->ThickSegment( start, end,
|
|
aZone->m_ZoneMinThickness,
|
|
trace_mode );
|
|
}
|
|
}
|
|
|
|
// Plot the current filled area outline
|
|
if( aZone->m_ZoneMinThickness > 0 )
|
|
aPlotter->PlotPoly( cornerList, NO_FILL, aZone->m_ZoneMinThickness );
|
|
}
|
|
else
|
|
{
|
|
if( aZone->m_ZoneMinThickness > 0 )
|
|
{
|
|
for( unsigned jj = 1; jj<cornerList.size(); jj++ )
|
|
aPlotter->ThickSegment( cornerList[jj -1], cornerList[jj],
|
|
( trace_mode == LINE ) ? -1 : aZone->m_ZoneMinThickness,
|
|
trace_mode );
|
|
}
|
|
|
|
aPlotter->SetCurrentLineWidth( -1 );
|
|
}
|
|
|
|
cornerList.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Plot items type DRAWSEGMENT on layers allowed by aLayerMask
|
|
*/
|
|
void PlotDrawSegment( PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpts,
|
|
DRAWSEGMENT* aSeg, int aLayerMask,
|
|
EDA_DRAW_MODE_T trace_mode )
|
|
{
|
|
int thickness;
|
|
int radius = 0, StAngle = 0, EndAngle = 0;
|
|
|
|
if( (GetLayerMask( aSeg->GetLayer() ) & aLayerMask) == 0 )
|
|
return;
|
|
|
|
if( trace_mode == LINE )
|
|
thickness = aPlotOpts.GetLineWidth();
|
|
else
|
|
thickness = aSeg->GetWidth();
|
|
|
|
wxPoint start( aSeg->GetStart() );
|
|
wxPoint end( aSeg->GetEnd() );
|
|
|
|
aPlotter->SetCurrentLineWidth( thickness );
|
|
|
|
switch( aSeg->GetShape() )
|
|
{
|
|
case S_CIRCLE:
|
|
radius = (int) hypot( (double) ( end.x - start.x ),
|
|
(double) ( end.y - start.y ) );
|
|
aPlotter->ThickCircle( start, radius * 2, thickness, trace_mode );
|
|
break;
|
|
|
|
case S_ARC:
|
|
radius = (int) hypot( (double) ( end.x - start.x ),
|
|
(double) ( end.y - start.y ) );
|
|
StAngle = ArcTangente( end.y - start.y, end.x - start.x );
|
|
EndAngle = StAngle + aSeg->GetAngle();
|
|
aPlotter->ThickArc( start, -EndAngle, -StAngle, radius, thickness, trace_mode );
|
|
break;
|
|
|
|
case S_CURVE:
|
|
{
|
|
const std::vector<wxPoint>& bezierPoints = aSeg->GetBezierPoints();
|
|
|
|
for( unsigned i = 1; i < bezierPoints.size(); i++ )
|
|
aPlotter->ThickSegment( bezierPoints[i - 1],
|
|
bezierPoints[i],
|
|
thickness,
|
|
trace_mode );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
aPlotter->ThickSegment( start, end, thickness, trace_mode );
|
|
}
|
|
}
|
|
|
|
|
|
void PlotBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, int aLayer,
|
|
const PCB_PLOT_PARAMS& aPlotOpt )
|
|
{
|
|
// Set the color and the text mode for this layer
|
|
aPlotter->SetColor( aPlotOpt.GetColor() );
|
|
aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
|
|
|
|
// Specify that the contents of the "Edges Pcb" layer are to be plotted
|
|
// in addition to the contents of the currently specified layer.
|
|
int layer_mask = GetLayerMask( aLayer );
|
|
|
|
if( !aPlotOpt.GetExcludeEdgeLayer() )
|
|
layer_mask |= EDGE_LAYER;
|
|
|
|
switch( aLayer )
|
|
{
|
|
case FIRST_COPPER_LAYER:
|
|
case LAYER_N_2:
|
|
case LAYER_N_3:
|
|
case LAYER_N_4:
|
|
case LAYER_N_5:
|
|
case LAYER_N_6:
|
|
case LAYER_N_7:
|
|
case LAYER_N_8:
|
|
case LAYER_N_9:
|
|
case LAYER_N_10:
|
|
case LAYER_N_11:
|
|
case LAYER_N_12:
|
|
case LAYER_N_13:
|
|
case LAYER_N_14:
|
|
case LAYER_N_15:
|
|
case LAST_COPPER_LAYER:
|
|
// Skip NPTH pads on copper layers ( only if hole size == pad size ):
|
|
PlotStandardLayer( aBoard, aPlotter, layer_mask, aPlotOpt, true, true );
|
|
|
|
// Adding drill marks, if required and if the plotter is able to plot them:
|
|
if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
|
|
PlotDrillMarks( aBoard, aPlotter, aPlotOpt );
|
|
|
|
break;
|
|
|
|
case SOLDERMASK_N_BACK:
|
|
case SOLDERMASK_N_FRONT:
|
|
PlotStandardLayer( aBoard, aPlotter, layer_mask, aPlotOpt,
|
|
aPlotOpt.GetPlotViaOnMaskLayer(), false );
|
|
break;
|
|
|
|
case SOLDERPASTE_N_BACK:
|
|
case SOLDERPASTE_N_FRONT:
|
|
PlotStandardLayer( aBoard, aPlotter, layer_mask, aPlotOpt,
|
|
false, false );
|
|
break;
|
|
|
|
case SILKSCREEN_N_FRONT:
|
|
case SILKSCREEN_N_BACK:
|
|
PlotSilkScreen( aBoard, aPlotter, layer_mask, aPlotOpt );
|
|
|
|
// Gerber: Subtract soldermask from silkscreen if enabled
|
|
if( aPlotter->GetPlotterType() == PLOT_FORMAT_GERBER
|
|
&& aPlotOpt.GetSubtractMaskFromSilk() )
|
|
{
|
|
if( aLayer == SILKSCREEN_N_FRONT )
|
|
layer_mask = GetLayerMask( SOLDERMASK_N_FRONT );
|
|
else
|
|
layer_mask = GetLayerMask( SOLDERMASK_N_BACK );
|
|
|
|
// Set layer polarity to negative
|
|
aPlotter->SetLayerPolarity( false );
|
|
PlotStandardLayer( aBoard, aPlotter, layer_mask, aPlotOpt,
|
|
aPlotOpt.GetPlotViaOnMaskLayer(), false );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
PlotSilkScreen( aBoard, aPlotter, layer_mask, aPlotOpt );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Plot a copper layer or mask.
|
|
* Silk screen layers are not plotted here.
|
|
*/
|
|
void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|
long aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
|
|
bool aPlotVia, bool aSkipNPTH_Pads )
|
|
{
|
|
wxPoint pos;
|
|
wxSize size;
|
|
wxString msg;
|
|
|
|
EDA_DRAW_MODE_T aPlotMode = aPlotOpt.GetMode();
|
|
|
|
// Plot pcb draw items.
|
|
for( BOARD_ITEM* item = aBoard->m_Drawings; item; item = item->Next() )
|
|
{
|
|
switch( item->Type() )
|
|
{
|
|
case PCB_LINE_T:
|
|
PlotDrawSegment( aPlotter, aPlotOpt, (DRAWSEGMENT*) item, aLayerMask, aPlotMode );
|
|
break;
|
|
|
|
case PCB_TEXT_T:
|
|
PlotTextePcb( aPlotter, aPlotOpt, (TEXTE_PCB*) item, aLayerMask, aPlotMode );
|
|
break;
|
|
|
|
case PCB_DIMENSION_T:
|
|
PlotDimension( aPlotter, aPlotOpt, (DIMENSION*) item, aLayerMask, aPlotMode );
|
|
break;
|
|
|
|
case PCB_TARGET_T:
|
|
PlotPcbTarget( aPlotter, aPlotOpt, (PCB_TARGET*) item, aLayerMask, aPlotMode );
|
|
break;
|
|
|
|
case PCB_MARKER_T:
|
|
break;
|
|
|
|
default:
|
|
DisplayError( NULL, wxT( "Plot_Standard_Layer() error : Unexpected Draw Type" ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw footprint shapes without pads (pads will plotted later)
|
|
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
|
|
{
|
|
for( BOARD_ITEM* item = module->m_Drawings; item; item = item->Next() )
|
|
{
|
|
switch( item->Type() )
|
|
{
|
|
case PCB_MODULE_EDGE_T:
|
|
if( aLayerMask & GetLayerMask( item->GetLayer() ) )
|
|
Plot_1_EdgeModule( aPlotter, aPlotOpt, (EDGE_MODULE*) item, aPlotMode, aLayerMask );
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Plot footprint pads
|
|
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
|
|
{
|
|
for( D_PAD* pad = module->m_Pads; pad; pad = pad->Next() )
|
|
{
|
|
if( (pad->GetLayerMask() & aLayerMask) == 0 )
|
|
continue;
|
|
|
|
wxPoint shape_pos = pad->ReturnShapePos();
|
|
|
|
pos = shape_pos;
|
|
|
|
wxSize margin;
|
|
double width_adj = 0;
|
|
|
|
if( aLayerMask & ALL_CU_LAYERS )
|
|
{
|
|
width_adj = aPlotter->GetPlotWidthAdj();
|
|
}
|
|
|
|
switch( aLayerMask &
|
|
( SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT |
|
|
SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT ) )
|
|
{
|
|
case SOLDERMASK_LAYER_FRONT:
|
|
case SOLDERMASK_LAYER_BACK:
|
|
margin.x = margin.y = pad->GetSolderMaskMargin();
|
|
break;
|
|
|
|
case SOLDERPASTE_LAYER_FRONT:
|
|
case SOLDERPASTE_LAYER_BACK:
|
|
margin = pad->GetSolderPasteMargin();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
size.x = pad->GetSize().x + ( 2 * margin.x ) + width_adj;
|
|
size.y = pad->GetSize().y + ( 2 * margin.y ) + width_adj;
|
|
|
|
// Don't draw a null size item :
|
|
if( size.x <= 0 || size.y <= 0 )
|
|
continue;
|
|
|
|
switch( pad->GetShape() )
|
|
{
|
|
case PAD_CIRCLE:
|
|
if( aSkipNPTH_Pads &&
|
|
(pad->GetSize() == pad->GetDrillSize()) &&
|
|
(pad->GetAttribute() == PAD_HOLE_NOT_PLATED) )
|
|
break;
|
|
|
|
aPlotter->FlashPadCircle( pos, size.x, aPlotMode );
|
|
break;
|
|
|
|
case PAD_OVAL:
|
|
if( aSkipNPTH_Pads &&
|
|
(pad->GetSize() == pad->GetDrillSize()) &&
|
|
(pad->GetAttribute() == PAD_HOLE_NOT_PLATED) )
|
|
break;
|
|
|
|
aPlotter->FlashPadOval( pos, size, pad->GetOrientation(), aPlotMode );
|
|
break;
|
|
|
|
case PAD_TRAPEZOID:
|
|
{
|
|
wxPoint coord[4];
|
|
pad->BuildPadPolygon( coord, margin, 0 );
|
|
aPlotter->FlashPadTrapez( pos, coord, pad->GetOrientation(), aPlotMode );
|
|
}
|
|
break;
|
|
|
|
case PAD_RECT:
|
|
default:
|
|
aPlotter->FlashPadRect( pos, size, pad->GetOrientation(), aPlotMode );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Plot vias :
|
|
if( aPlotVia )
|
|
{
|
|
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
|
|
{
|
|
if( track->Type() != PCB_VIA_T )
|
|
continue;
|
|
|
|
SEGVIA* Via = (SEGVIA*) track;
|
|
|
|
// vias are not plotted if not on selected layer, but if layer
|
|
// is SOLDERMASK_LAYER_BACK or SOLDERMASK_LAYER_FRONT,vias are drawn,
|
|
// if they are on an external copper layer
|
|
int via_mask_layer = Via->ReturnMaskLayer();
|
|
|
|
if( via_mask_layer & LAYER_BACK )
|
|
via_mask_layer |= SOLDERMASK_LAYER_BACK;
|
|
|
|
if( via_mask_layer & LAYER_FRONT )
|
|
via_mask_layer |= SOLDERMASK_LAYER_FRONT;
|
|
|
|
if( ( via_mask_layer & aLayerMask ) == 0 )
|
|
continue;
|
|
|
|
int via_margin = 0;
|
|
double width_adj = 0;
|
|
|
|
// If the current layer is a solder mask, use the global mask
|
|
// clearance for vias
|
|
if( ( aLayerMask & ( SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT ) ) )
|
|
via_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
|
|
|
if( aLayerMask & ALL_CU_LAYERS )
|
|
{
|
|
width_adj = aPlotter->GetPlotWidthAdj();
|
|
}
|
|
|
|
pos = Via->m_Start;
|
|
size.x = size.y = Via->m_Width + 2 * via_margin + width_adj;
|
|
|
|
// Don't draw a null size item :
|
|
if( size.x <= 0 )
|
|
continue;
|
|
|
|
aPlotter->FlashPadCircle( pos, size.x, aPlotMode );
|
|
}
|
|
}
|
|
|
|
// Plot tracks (not vias) :
|
|
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
|
|
{
|
|
wxPoint end;
|
|
|
|
if( track->Type() == PCB_VIA_T )
|
|
continue;
|
|
|
|
if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 )
|
|
continue;
|
|
|
|
size.x = size.y = track->m_Width + aPlotter->GetPlotWidthAdj();
|
|
pos = track->m_Start;
|
|
end = track->m_End;
|
|
|
|
aPlotter->ThickSegment( pos, end, size.x, aPlotMode );
|
|
}
|
|
|
|
// Plot zones (outdated, for old boards compatibility):
|
|
for( TRACK* track = aBoard->m_Zone; track; track = track->Next() )
|
|
{
|
|
wxPoint end;
|
|
|
|
if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 )
|
|
continue;
|
|
|
|
size.x = size.y = track->m_Width + aPlotter->GetPlotWidthAdj();
|
|
pos = track->m_Start;
|
|
end = track->m_End;
|
|
|
|
aPlotter->ThickSegment( pos, end, size.x, aPlotMode );
|
|
}
|
|
|
|
// Plot filled ares
|
|
for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
|
|
{
|
|
ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii );
|
|
|
|
if( ( ( 1 << edge_zone->GetLayer() ) & aLayerMask ) == 0 )
|
|
continue;
|
|
|
|
PlotFilledAreas( aPlotter, aPlotOpt, edge_zone, aPlotMode );
|
|
}
|
|
}
|
|
|
|
/** Helper function to plot a single drill mark. It compensate and clamp
|
|
the drill mark size depending on the current plot options */
|
|
static void PlotDrillMark( PLOTTER *aPlotter, PAD_SHAPE_T aDrillShape,
|
|
const wxPoint &aDrillPos, wxSize aDrillSize,
|
|
const wxSize &aPadSize,
|
|
double aOrientation, int aSmallDrill,
|
|
EDA_DRAW_MODE_T aTraceMode )
|
|
{
|
|
// Small drill marks have no significance when applied to slots
|
|
if( aSmallDrill && aDrillShape == PAD_ROUND )
|
|
aDrillSize.x = std::min( aSmallDrill, aDrillSize.x );
|
|
|
|
// Round holes only have x diameter, slots have both
|
|
aDrillSize.x -= aPlotter->GetPlotWidthAdj();
|
|
aDrillSize.x = Clamp( 1, aDrillSize.x, aPadSize.x - 1 );
|
|
if( aDrillShape == PAD_OVAL )
|
|
{
|
|
aDrillSize.y -= aPlotter->GetPlotWidthAdj();
|
|
aDrillSize.y = Clamp( 1, aDrillSize.y, aPadSize.y - 1 );
|
|
aPlotter->FlashPadOval( aDrillPos, aDrillSize, aOrientation, aTraceMode );
|
|
}
|
|
else
|
|
aPlotter->FlashPadCircle( aDrillPos, aDrillSize.x, aTraceMode );
|
|
}
|
|
|
|
/* Function PlotDrillMarks
|
|
* Draw a drill mark for pads and vias.
|
|
* Must be called after all drawings, because it
|
|
* redraw the drill mark on a pad or via, as a negative (i.e. white) shape in
|
|
* FILLED plot mode (for PS and PDF outputs)
|
|
*/
|
|
void PlotDrillMarks( BOARD *aBoard, PLOTTER* aPlotter,
|
|
const PCB_PLOT_PARAMS& aPlotOpts )
|
|
{
|
|
EDA_DRAW_MODE_T trace_mode = aPlotOpts.GetMode();
|
|
|
|
/* If small drills marks were requested prepare a clamp value to pass
|
|
to the helper function */
|
|
int small_drill = (aPlotOpts.GetDrillMarksType() == PCB_PLOT_PARAMS::SMALL_DRILL_SHAPE) ?
|
|
SMALL_DRILL : 0;
|
|
|
|
/* In the filled trace mode drill marks are drawn white-on-black to scrape
|
|
the underlying pad. This works only for drivers supporting color change,
|
|
obviously... it means that:
|
|
- PS and PDF output is correct (i.e. you have a 'donut' pad)
|
|
- In HPGL you can't see them
|
|
- In gerbers you can't see them, too. This is arguably the right thing to
|
|
do since having drill marks and high speed drill stations is a sure
|
|
recipe for broken tools and angry manufacturers. If you *really* want them
|
|
you could start a layer with negative polarity to scrape the film.
|
|
- In DXF they go into the 'WHITE' layer. This could be useful.
|
|
*/
|
|
if( trace_mode == FILLED )
|
|
{
|
|
aPlotter->SetColor( WHITE );
|
|
}
|
|
|
|
for( TRACK *pts = aBoard->m_Track; pts != NULL; pts = pts->Next() )
|
|
{
|
|
if( pts->Type() != PCB_VIA_T )
|
|
continue;
|
|
|
|
PlotDrillMark( aPlotter, PAD_CIRCLE,
|
|
pts->m_Start, wxSize( pts->GetDrillValue(), 0 ),
|
|
wxSize( pts->m_Width, 0 ), 0, small_drill,
|
|
trace_mode );
|
|
}
|
|
|
|
for( MODULE *Module = aBoard->m_Modules; Module != NULL; Module = Module->Next() )
|
|
{
|
|
for( D_PAD *pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
|
|
{
|
|
if( pad->GetDrillSize().x == 0 )
|
|
continue;
|
|
|
|
PlotDrillMark( aPlotter, pad->GetDrillShape(),
|
|
pad->GetPosition(), pad->GetDrillSize(),
|
|
pad->GetSize(), pad->GetOrientation(),
|
|
small_drill, trace_mode );
|
|
}
|
|
}
|
|
|
|
if( trace_mode == FILLED )
|
|
{
|
|
aPlotter->SetColor( aPlotOpts.GetColor() );
|
|
}
|
|
}
|
|
|
|
/** Set up most plot options for plotting a board (especially the viewport)
|
|
* Important thing:
|
|
* page size is the 'drawing' page size,
|
|
* paper size is the physical page size
|
|
*/
|
|
static void PlotSetupPlotter( PLOTTER *aPlotter, PCB_PLOT_PARAMS *aPlotOpts,
|
|
const PAGE_INFO &aPageInfo,
|
|
const EDA_RECT &aBbbox,
|
|
const wxPoint &aOrigin,
|
|
const wxString &aFilename )
|
|
{
|
|
PAGE_INFO pageA4( wxT( "A4" ) );
|
|
const PAGE_INFO* sheet_info;
|
|
double paperscale; // Page-to-paper ratio
|
|
wxSize paperSizeIU;
|
|
wxSize pageSizeIU( aPageInfo.GetSizeIU() );
|
|
bool autocenter = false;
|
|
|
|
/* Special options: to fit the sheet to an A4 sheet replace
|
|
the paper size. However there is a difference between
|
|
the autoscale and the a4paper option:
|
|
- Autoscale fits the board to the paper size
|
|
- A4paper fits the original paper size to an A4 sheet
|
|
- Both of them fit the board to an A4 sheet
|
|
*/
|
|
if( aPlotOpts->GetA4Output() ) // Fit paper to A4
|
|
{
|
|
sheet_info = &pageA4;
|
|
paperSizeIU = pageA4.GetSizeIU();
|
|
paperscale = (double) paperSizeIU.x / pageSizeIU.x;
|
|
autocenter = true;
|
|
}
|
|
else
|
|
{
|
|
sheet_info = &aPageInfo;
|
|
paperSizeIU = pageSizeIU;
|
|
paperscale = 1;
|
|
|
|
// Need autocentering only if scale is not 1:1
|
|
autocenter = (aPlotOpts->GetScale() != 1.0);
|
|
}
|
|
|
|
wxPoint boardCenter = aBbbox.Centre();
|
|
|
|
double compound_scale;
|
|
wxSize boardSize = aBbbox.GetSize();
|
|
|
|
/* Fit to 80% of the page if asked; it could be that the board is empty,
|
|
* in this case regress to 1:1 scale */
|
|
if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
|
|
{
|
|
double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
|
|
double yscale = (paperSizeIU.y * 0.8) / boardSize.y;
|
|
|
|
compound_scale = std::min( xscale, yscale ) * paperscale;
|
|
}
|
|
else
|
|
compound_scale = aPlotOpts->GetScale() * paperscale;
|
|
|
|
|
|
/* For the plot offset we have to keep in mind the auxiliary origin
|
|
too: if autoscaling is off we check that plot option (i.e. autoscaling
|
|
overrides auxiliary origin) */
|
|
wxPoint offset( 0, 0);
|
|
|
|
if( autocenter )
|
|
{
|
|
offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
|
|
offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
|
|
}
|
|
else
|
|
{
|
|
if( aPlotOpts->GetUseAuxOrigin() )
|
|
offset = aOrigin;
|
|
}
|
|
|
|
/* Configure the plotter object with all the stuff computed and
|
|
most of that taken from the options */
|
|
aPlotter->SetPageSettings( *sheet_info );
|
|
aPlotter->SetPlotWidthAdj( aPlotOpts->GetWidthAdjust() );
|
|
aPlotter->SetViewport( offset, IU_PER_DECIMILS, compound_scale,
|
|
aPlotOpts->GetMirror() );
|
|
aPlotter->SetDefaultLineWidth( aPlotOpts->GetLineWidth() );
|
|
aPlotter->SetCreator( wxT( "PCBNEW" ) );
|
|
aPlotter->SetColorMode( true );
|
|
aPlotter->SetFilename( aFilename );
|
|
aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
|
|
}
|
|
|
|
/** Prefill in black an area a little bigger than the board to prepare for the
|
|
* negative plot */
|
|
static void FillNegativeKnockout(PLOTTER *aPlotter, const EDA_RECT &aBbbox )
|
|
{
|
|
static const int margin = 500; // Add a 0.5 inch margin around the board
|
|
aPlotter->SetNegative( true );
|
|
aPlotter->SetColor( WHITE ); // Which will be plotted as black
|
|
aPlotter->Rect( wxPoint( aBbbox.GetX() - margin, aBbbox.GetY() - margin ),
|
|
wxPoint( aBbbox.GetRight() + margin, aBbbox.GetBottom() + margin ),
|
|
FILLED_SHAPE );
|
|
aPlotter->SetColor( BLACK );
|
|
}
|
|
|
|
/** Calculate the effective size of HPGL pens and set them in the
|
|
* plotter object */
|
|
static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter,
|
|
PCB_PLOT_PARAMS *aPlotOpts )
|
|
{
|
|
/* Compute pen_dim (the value is given in mils) in pcb units,
|
|
with plot scale (if Scale is 2, pen diameter value is always m_HPGLPenDiam
|
|
so apparent pen diam is real pen diam / Scale */
|
|
int pen_diam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * IU_PER_MILS /
|
|
aPlotOpts->GetScale() );
|
|
|
|
// compute pen_overlay (value comes in mils) in pcb units with plot scale
|
|
if( aPlotOpts->GetHPGLPenOverlay() < 0 )
|
|
aPlotOpts->SetHPGLPenOverlay( 0 );
|
|
|
|
if( aPlotOpts->GetHPGLPenOverlay() >= aPlotOpts->GetHPGLPenDiameter() )
|
|
aPlotOpts->SetHPGLPenOverlay( aPlotOpts->GetHPGLPenDiameter() - 1 );
|
|
|
|
int pen_overlay = KiROUND( aPlotOpts->GetHPGLPenOverlay() * IU_PER_MILS /
|
|
aPlotOpts->GetScale() );
|
|
|
|
// Set HPGL-specific options and start
|
|
aPlotter->SetPenSpeed( aPlotOpts->GetHPGLPenSpeed() );
|
|
aPlotter->SetPenNumber( aPlotOpts->GetHPGLPenNum() );
|
|
aPlotter->SetPenOverlap( pen_overlay );
|
|
aPlotter->SetPenDiameter( pen_diam );
|
|
}
|
|
|
|
/** Open a new plotfile using the options (and especially the format)
|
|
* specified in the options and prepare the page for plotting.
|
|
* Return the plotter object if OK, NULL if the file is not created
|
|
* (or has a problem)
|
|
*/
|
|
PLOTTER *StartPlotBoard( BOARD *aBoard,
|
|
PCB_PLOT_PARAMS *aPlotOpts,
|
|
const wxString& aFullFileName,
|
|
const wxString& aSheetDesc )
|
|
{
|
|
const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
|
|
EDA_RECT bbbox = aBoard->ComputeBoundingBox();
|
|
wxPoint auxOrigin( aBoard->GetOriginAxisPosition() );
|
|
FILE* output_file = wxFopen( aFullFileName, wxT( "wt" ) );
|
|
|
|
if( output_file == NULL )
|
|
return NULL;
|
|
|
|
// Create the plotter driver and set the few plotter specific
|
|
// options
|
|
PLOTTER *the_plotter = NULL;
|
|
switch( aPlotOpts->GetFormat() )
|
|
{
|
|
case PLOT_FORMAT_DXF:
|
|
the_plotter = new DXF_PLOTTER();
|
|
break;
|
|
|
|
case PLOT_FORMAT_POST:
|
|
PS_PLOTTER* PS_plotter;
|
|
PS_plotter = new PS_PLOTTER();
|
|
PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
|
|
aPlotOpts->GetFineScaleAdjustY() );
|
|
the_plotter = PS_plotter;
|
|
break;
|
|
|
|
case PLOT_FORMAT_PDF:
|
|
the_plotter = new PDF_PLOTTER();
|
|
break;
|
|
|
|
case PLOT_FORMAT_HPGL:
|
|
HPGL_PLOTTER* HPGL_plotter;
|
|
HPGL_plotter = new HPGL_PLOTTER();
|
|
|
|
/* HPGL options are a little more convoluted to compute, so
|
|
they're split in an other function */
|
|
ConfigureHPGLPenSizes( HPGL_plotter, aPlotOpts );
|
|
the_plotter = HPGL_plotter;
|
|
break;
|
|
|
|
case PLOT_FORMAT_GERBER:
|
|
the_plotter = new GERBER_PLOTTER();
|
|
break;
|
|
|
|
case PLOT_FORMAT_SVG:
|
|
the_plotter = new SVG_PLOTTER();
|
|
break;
|
|
|
|
default:
|
|
wxASSERT( false );
|
|
}
|
|
|
|
if( the_plotter )
|
|
{
|
|
// Compute the viewport and set the other options
|
|
PlotSetupPlotter( the_plotter, aPlotOpts, pageInfo, bbbox, auxOrigin,
|
|
aFullFileName );
|
|
|
|
if( the_plotter->StartPlot( output_file ) )
|
|
{
|
|
/* When plotting a negative board: draw a black rectangle
|
|
* (background for plot board in white) and switch the current
|
|
* color to WHITE; note the the color inversion is actually done
|
|
* in the driver (if supported) */
|
|
if( aPlotOpts->GetNegative() )
|
|
FillNegativeKnockout( the_plotter, bbbox );
|
|
|
|
// Plot the frame reference if requested
|
|
if( aPlotOpts->GetPlotFrameRef() )
|
|
PlotWorkSheet( the_plotter, aBoard->GetTitleBlock(),
|
|
aBoard->GetPageSettings(),
|
|
1, 1, // Only one page
|
|
aSheetDesc,
|
|
aBoard->GetFileName() );
|
|
|
|
return the_plotter;
|
|
}
|
|
}
|
|
|
|
// error in start_plot( ) or before
|
|
DisplayError( NULL, _("Error creating plot file"));
|
|
delete the_plotter;
|
|
return NULL;
|
|
}
|