/** * @file plot_rtn.cpp * @brief Common plot routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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& 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 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; jjThickSegment( 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& 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; }