diff options
Diffstat (limited to 'pcbnew/plot_brditems_plotter.cpp')
-rw-r--r-- | pcbnew/plot_brditems_plotter.cpp | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp new file mode 100644 index 0000000..248eda7 --- /dev/null +++ b/pcbnew/plot_brditems_plotter.cpp @@ -0,0 +1,688 @@ +/** + * @file plot_brditems_plotter.cpp + * @brief basic plot functions to plot board items, or a group of board items. + */ + + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <fctsys.h> +#include <common.h> +#include <plot_common.h> +#include <base_struct.h> +#include <drawtxt.h> +#include <trigo.h> +#include <macros.h> +#include <wxBasePcbFrame.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> + +/* class BRDITEMS_PLOTTER is a helper class to plot board items + * and a group of board items + */ + + +EDA_COLOR_T BRDITEMS_PLOTTER::getColor( LAYER_NUM aLayer ) +{ + EDA_COLOR_T color = m_board->GetLayerColor( ToLAYER_ID( aLayer ) ); + if (color == WHITE) + color = LIGHTGRAY; + return color; +} + + +void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, EDA_COLOR_T aColor, EDA_DRAW_MODE_T aPlotMode ) +{ + wxPoint shape_pos = aPad->ShapePos(); + + // Set plot color (change WHITE to LIGHTGRAY because + // the white items are not seen on a white paper or screen + m_plotter->SetColor( aColor != WHITE ? aColor : LIGHTGRAY); + + switch( aPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + m_plotter->FlashPadCircle( shape_pos, aPad->GetSize().x, aPlotMode ); + break; + + case PAD_SHAPE_OVAL: + m_plotter->FlashPadOval( shape_pos, aPad->GetSize(), + aPad->GetOrientation(), aPlotMode ); + break; + + case PAD_SHAPE_TRAPEZOID: + { + wxPoint coord[4]; + aPad->BuildPadPolygon( coord, wxSize(0,0), 0 ); + m_plotter->FlashPadTrapez( shape_pos, coord, + aPad->GetOrientation(), aPlotMode ); + } + break; + + case PAD_SHAPE_RECT: + default: + m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), + aPad->GetOrientation(), aPlotMode ); + break; + } +} + + +bool BRDITEMS_PLOTTER::PlotAllTextsModule( MODULE* aModule ) +{ + // see if we want to plot VALUE and REF fields + bool trace_val = GetPlotValue(); + bool trace_ref = GetPlotReference(); + + TEXTE_MODULE* textModule = &aModule->Reference(); + LAYER_NUM textLayer = textModule->GetLayer(); + + if( textLayer >= LAYER_ID_COUNT ) // how will this ever be true? + return false; + + if( !m_layerMask[textLayer] ) + trace_ref = false; + + if( !textModule->IsVisible() && !GetPlotInvisibleText() ) + trace_ref = false; + + textModule = &aModule->Value(); + textLayer = textModule->GetLayer(); + + if( textLayer > LAYER_ID_COUNT ) // how will this ever be true? + return false; + + if( !m_layerMask[textLayer] ) + trace_val = false; + + if( !textModule->IsVisible() && !GetPlotInvisibleText() ) + trace_val = false; + + // Plot text fields, if allowed + if( trace_ref ) + { + PlotTextModule( &aModule->Reference(), getColor( textLayer ) ); + } + + if( trace_val ) + { + PlotTextModule( &aModule->Value(), getColor( textLayer ) ); + } + + for( BOARD_ITEM *item = aModule->GraphicalItems().GetFirst(); item; item = item->Next() ) + { + textModule = dyn_cast<TEXTE_MODULE*>( item ); + + if( !textModule ) + continue; + + if( !textModule->IsVisible() ) + continue; + + textLayer = textModule->GetLayer(); + + if( textLayer >= LAYER_ID_COUNT ) + return false; + + if( !m_layerMask[textLayer] ) + continue; + + PlotTextModule( textModule, getColor( textLayer ) ); + } + + return true; +} + + +// plot items like text and graphics, but not tracks and module +void BRDITEMS_PLOTTER::PlotBoardGraphicItems() +{ + for( BOARD_ITEM* item = m_board->m_Drawings; item; item = item->Next() ) + { + switch( item->Type() ) + { + case PCB_LINE_T: + PlotDrawSegment( (DRAWSEGMENT*) item); + break; + + case PCB_TEXT_T: + PlotTextePcb( (TEXTE_PCB*) item ); + break; + + case PCB_DIMENSION_T: + PlotDimension( (DIMENSION*) item ); + break; + + case PCB_TARGET_T: + PlotPcbTarget( (PCB_TARGET*) item ); + break; + + case PCB_MARKER_T: + default: + break; + } + } +} + +void BRDITEMS_PLOTTER::PlotTextModule( TEXTE_MODULE* pt_texte, EDA_COLOR_T aColor ) +{ + wxSize size; + wxPoint pos; + double orient; + int thickness; + + if( aColor == WHITE ) + aColor = LIGHTGRAY; + + m_plotter->SetColor( aColor ); + + // calculate some text parameters : + size = pt_texte->GetSize(); + pos = pt_texte->GetTextPosition(); + + orient = pt_texte->GetDrawRotation(); + + thickness = pt_texte->GetThickness(); + + if( pt_texte->IsMirrored() ) + size.x = -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->IsBold() || thickness; + + m_plotter->Text( pos, aColor, + pt_texte->GetShownText(), + orient, size, + pt_texte->GetHorizJustify(), pt_texte->GetVertJustify(), + thickness, pt_texte->IsItalic(), allow_bold ); +} + + +void BRDITEMS_PLOTTER::PlotDimension( DIMENSION* aDim ) +{ + if( !m_layerMask[aDim->GetLayer()] ) + return; + + DRAWSEGMENT draw; + + draw.SetWidth( aDim->GetWidth() ); + draw.SetLayer( aDim->GetLayer() ); + + EDA_COLOR_T color = getColor( aDim->GetLayer() ); + + // Set plot color (change WHITE to LIGHTGRAY because + // the white items are not seen on a white paper or screen + m_plotter->SetColor( color ); + + PlotTextePcb( &aDim->Text() ); + + draw.SetStart( aDim->m_crossBarO ); + draw.SetEnd( aDim->m_crossBarF ); + PlotDrawSegment( &draw ); + + draw.SetStart( aDim->m_featureLineGO); + draw.SetEnd( aDim->m_featureLineGF ); + PlotDrawSegment( &draw ); + + draw.SetStart( aDim->m_featureLineDO ); + draw.SetEnd( aDim->m_featureLineDF ); + PlotDrawSegment( &draw ); + + draw.SetStart( aDim->m_crossBarF ); + draw.SetEnd( aDim->m_arrowD1F ); + PlotDrawSegment( &draw ); + + draw.SetStart( aDim->m_crossBarF ); + draw.SetEnd( aDim->m_arrowD2F ); + PlotDrawSegment( &draw ); + + draw.SetStart( aDim->m_crossBarO ); + draw.SetEnd( aDim->m_arrowG1F ); + PlotDrawSegment( &draw ); + + draw.SetStart( aDim->m_crossBarO ); + draw.SetEnd( aDim->m_arrowG2F ); + PlotDrawSegment( &draw ); +} + + +void BRDITEMS_PLOTTER::PlotPcbTarget( PCB_TARGET* aMire ) +{ + int dx1, dx2, dy1, dy2, radius; + + if( !m_layerMask[aMire->GetLayer()] ) + return; + + m_plotter->SetColor( getColor( aMire->GetLayer() ) ); + + DRAWSEGMENT draw; + + draw.SetShape( S_CIRCLE ); + draw.SetWidth( aMire->GetWidth() ); + draw.SetLayer( aMire->GetLayer() ); + draw.SetStart( aMire->GetPosition() ); + radius = aMire->GetSize() / 3; + + if( aMire->GetShape() ) // shape X + radius = aMire->GetSize() / 2; + + // Draw the circle + draw.SetEnd( wxPoint( draw.GetStart().x + radius, draw.GetStart().y )); + + PlotDrawSegment( &draw ); + + draw.SetShape( S_SEGMENT ); + + radius = aMire->GetSize() / 2; + dx1 = radius; + dy1 = 0; + dx2 = 0; + dy2 = radius; + + if( aMire->GetShape() ) // Shape X + { + dx1 = dy1 = radius; + dx2 = dx1; + dy2 = -dy1; + } + + wxPoint mirePos( aMire->GetPosition() ); + + // Draw the X or + shape: + draw.SetStart( wxPoint( mirePos.x - dx1, mirePos.y - dy1 )); + draw.SetEnd( wxPoint( mirePos.x + dx1, mirePos.y + dy1 )); + PlotDrawSegment( &draw ); + + draw.SetStart( wxPoint( mirePos.x - dx2, mirePos.y - dy2 )); + draw.SetEnd( wxPoint( mirePos.x + dx2, mirePos.y + dy2 )); + PlotDrawSegment( &draw ); +} + + +// Plot footprints graphic items (outlines) +void BRDITEMS_PLOTTER::Plot_Edges_Modules() +{ + for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + for( BOARD_ITEM* item = module->GraphicalItems().GetFirst(); item; item = item->Next() ) + { + EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ); + + if( !edge || !m_layerMask[edge->GetLayer()] ) + continue; + + Plot_1_EdgeModule( edge ); + } + } +} + + +//* Plot a graphic item (outline) relative to a footprint +void BRDITEMS_PLOTTER::Plot_1_EdgeModule( EDGE_MODULE* aEdge ) +{ + int type_trace; // Type of item to plot. + int thickness; // Segment thickness. + int radius; // Circle radius. + + if( aEdge->Type() != PCB_MODULE_EDGE_T ) + return; + + m_plotter->SetColor( getColor( aEdge->GetLayer() ) ); + + type_trace = aEdge->GetShape(); + thickness = aEdge->GetWidth(); + + wxPoint pos( aEdge->GetStart() ); + wxPoint end( aEdge->GetEnd() ); + + switch( type_trace ) + { + case S_SEGMENT: + m_plotter->ThickSegment( pos, end, thickness, GetPlotMode() ); + break; + + case S_CIRCLE: + radius = KiROUND( GetLineLength( end, pos ) ); + m_plotter->ThickCircle( pos, radius * 2, thickness, GetPlotMode() ); + break; + + case S_ARC: + { + radius = KiROUND( GetLineLength( end, pos ) ); + double startAngle = ArcTangente( end.y - pos.y, end.x - pos.x ); + double endAngle = startAngle + aEdge->GetAngle(); + + m_plotter->ThickArc( pos, -endAngle, -startAngle, radius, thickness, GetPlotMode() ); + } + 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 ); + } + + m_plotter->PlotPoly( cornerList, FILLED_SHAPE, thickness ); + } + break; + } +} + + +// Plot a PCB Text, i;e. a text found on a copper or technical layer +void BRDITEMS_PLOTTER::PlotTextePcb( TEXTE_PCB* pt_texte ) +{ + double orient; + int thickness; + wxPoint pos; + wxSize size; + wxString shownText( pt_texte->GetShownText() ); + + if( shownText.IsEmpty() ) + return; + + if( !m_layerMask[pt_texte->GetLayer()] ) + return; + + EDA_COLOR_T color = getColor( pt_texte->GetLayer() ); + m_plotter->SetColor( color ); + + size = pt_texte->GetSize(); + pos = pt_texte->GetTextPosition(); + orient = pt_texte->GetOrientation(); + thickness = pt_texte->GetThickness(); + + if( pt_texte->IsMirrored() ) + 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->IsBold() || thickness; + + if( pt_texte->IsMultilineAllowed() ) + { + std::vector<wxPoint> positions; + wxArrayString strings_list; + wxStringSplit( shownText, strings_list, '\n' ); + positions.reserve( strings_list.Count() ); + + pt_texte->GetPositionsOfLinesOfMultilineText( positions, strings_list.Count() ); + + for( unsigned ii = 0; ii < strings_list.Count(); ii++ ) + { + wxString& txt = strings_list.Item( ii ); + m_plotter->Text( positions[ii], color, txt, orient, size, + pt_texte->GetHorizJustify(), pt_texte->GetVertJustify(), + thickness, pt_texte->IsItalic(), allow_bold ); + } + } + else + { + m_plotter->Text( pos, color, shownText, orient, size, + pt_texte->GetHorizJustify(), pt_texte->GetVertJustify(), + thickness, pt_texte->IsItalic(), allow_bold ); + } +} + + +/* Plot areas (given by .m_FilledPolysList member) in a zone + */ +void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone ) +{ + const SHAPE_POLY_SET& polysList = aZone->GetFilledPolysList(); + + if( polysList.IsEmpty() ) + return; + + // We need a buffer to store corners coordinates: + static std::vector< wxPoint > cornerList; + cornerList.clear(); + + m_plotter->SetColor( getColor( aZone->GetLayer() ) ); + + /* 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( SHAPE_POLY_SET::CONST_ITERATOR ic = polysList.CIterate(); ic; ++ic ) + { + wxPoint pos( ic->x, ic->y ); + cornerList.push_back( pos ); + + if( ic.IsEndContour() ) // 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( GetPlotMode() == FILLED ) + { + // Plot the filled area polygon. + // The area can be filled by segments or uses solid polygons + if( aZone->GetFillMode() == 0 ) // We are using solid polygons + { + m_plotter->PlotPoly( cornerList, FILLED_SHAPE, aZone->GetMinThickness() ); + } + else // We are using areas filled by segments: plot segments and outline + { + for( unsigned iseg = 0; iseg < aZone->FillSegments().size(); iseg++ ) + { + wxPoint start = aZone->FillSegments()[iseg].m_Start; + wxPoint end = aZone->FillSegments()[iseg].m_End; + m_plotter->ThickSegment( start, end, + aZone->GetMinThickness(), + GetPlotMode() ); + } + + // Plot the area outline only + if( aZone->GetMinThickness() > 0 ) + m_plotter->PlotPoly( cornerList, NO_FILL, aZone->GetMinThickness() ); + } + } + else + { + if( aZone->GetMinThickness() > 0 ) + { + for( unsigned jj = 1; jj<cornerList.size(); jj++ ) + m_plotter->ThickSegment( cornerList[jj -1], cornerList[jj], + aZone->GetMinThickness(), + GetPlotMode() ); + } + + m_plotter->SetCurrentLineWidth( -1 ); + } + + cornerList.clear(); + } + } +} + + +/* Plot items type DRAWSEGMENT on layers allowed by aLayerMask + */ +void BRDITEMS_PLOTTER::PlotDrawSegment( DRAWSEGMENT* aSeg ) +{ + if( !m_layerMask[aSeg->GetLayer()] ) + return; + + int radius = 0; + double StAngle = 0, EndAngle = 0; + int thickness = aSeg->GetWidth(); + + m_plotter->SetColor( getColor( aSeg->GetLayer() ) ); + + wxPoint start( aSeg->GetStart() ); + wxPoint end( aSeg->GetEnd() ); + + m_plotter->SetCurrentLineWidth( thickness ); + + switch( aSeg->GetShape() ) + { + case S_CIRCLE: + radius = KiROUND( GetLineLength( end, start ) ); + m_plotter->ThickCircle( start, radius * 2, thickness, GetPlotMode() ); + break; + + case S_ARC: + radius = KiROUND( GetLineLength( end, start ) ); + StAngle = ArcTangente( end.y - start.y, end.x - start.x ); + EndAngle = StAngle + aSeg->GetAngle(); + m_plotter->ThickArc( start, -EndAngle, -StAngle, radius, thickness, GetPlotMode() ); + break; + + case S_CURVE: + { + const std::vector<wxPoint>& bezierPoints = aSeg->GetBezierPoints(); + + for( unsigned i = 1; i < bezierPoints.size(); i++ ) + m_plotter->ThickSegment( bezierPoints[i - 1], + bezierPoints[i], + thickness, GetPlotMode() ); + } + break; + + default: + m_plotter->ThickSegment( start, end, thickness, GetPlotMode() ); + } +} + + +/** Helper function to plot a single drill mark. It compensate and clamp + * the drill mark size depending on the current plot options + */ +void BRDITEMS_PLOTTER::plotOneDrillMark( PAD_DRILL_SHAPE_T aDrillShape, + const wxPoint &aDrillPos, wxSize aDrillSize, + const wxSize &aPadSize, + double aOrientation, int aSmallDrill ) +{ + // Small drill marks have no significance when applied to slots + if( aSmallDrill && aDrillShape == PAD_DRILL_SHAPE_CIRCLE ) + aDrillSize.x = std::min( aSmallDrill, aDrillSize.x ); + + // Round holes only have x diameter, slots have both + aDrillSize.x -= getFineWidthAdj(); + aDrillSize.x = Clamp( 1, aDrillSize.x, aPadSize.x - 1 ); + + if( aDrillShape == PAD_DRILL_SHAPE_OBLONG ) + { + aDrillSize.y -= getFineWidthAdj(); + aDrillSize.y = Clamp( 1, aDrillSize.y, aPadSize.y - 1 ); + m_plotter->FlashPadOval( aDrillPos, aDrillSize, aOrientation, GetPlotMode() ); + } + else + m_plotter->FlashPadCircle( aDrillPos, aDrillSize.x, GetPlotMode() ); +} + + +void BRDITEMS_PLOTTER::PlotDrillMarks() +{ + /* If small drills marks were requested prepare a clamp value to pass + to the helper function */ + int small_drill = (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, SVG 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( GetPlotMode() == FILLED ) + m_plotter->SetColor( WHITE ); + + for( TRACK *pts = m_board->m_Track; pts != NULL; pts = pts->Next() ) + { + const VIA* via = dyn_cast<const VIA*>( pts ); + + if( via ) + plotOneDrillMark( PAD_DRILL_SHAPE_CIRCLE, via->GetStart(), + wxSize( via->GetDrillValue(), 0 ), + wxSize( via->GetWidth(), 0 ), 0, small_drill ); + } + + for( MODULE *Module = m_board->m_Modules; Module != NULL; Module = Module->Next() ) + { + for( D_PAD *pad = Module->Pads(); pad != NULL; pad = pad->Next() ) + { + if( pad->GetDrillSize().x == 0 ) + continue; + + plotOneDrillMark( pad->GetDrillShape(), + pad->GetPosition(), pad->GetDrillSize(), + pad->GetSize(), pad->GetOrientation(), + small_drill ); + } + } + + if( GetPlotMode() == FILLED ) + m_plotter->SetColor( GetColor() ); +} |