void DIALOG_GEN_MODULE_POSITION::OnInitDialog( wxInitDialogEvent& event ) { // Output directory m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() ); m_radioBoxUnits->SetSelection( m_unitsOpt ); m_radioBoxFilesCount->SetSelection( m_fileOpt ); m_sdbSizerButtonsOK->SetDefault(); GetSizer()->SetSizeHints(this); Centre(); }
void DIALOG_GEN_MODULE_POSITION::initDialog() { m_config = Kiface().KifaceSettings(); m_config->Read( PLACEFILE_UNITS_KEY, &m_unitsOpt, 1 ); m_config->Read( PLACEFILE_OPT_KEY, &m_fileOpt, 0 ); // Output directory m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() ); m_radioBoxUnits->SetSelection( m_unitsOpt ); m_radioBoxFilesCount->SetSelection( m_fileOpt ); m_sdbSizerButtonsOK->SetDefault(); }
void DIALOG_GEN_MODULE_POSITION::OnOKButton( wxCommandEvent& event ) { m_unitsOpt = m_radioBoxUnits->GetSelection(); m_fileOpt = m_radioBoxFilesCount->GetSelection(); // Set output directory and replace backslashes with forward ones // (Keep unix convention in cfg files) wxString dirStr; dirStr = m_outputDirectoryName->GetValue(); dirStr.Replace( wxT( "\\" ), wxT( "/" ) ); m_plotOpts.SetOutputDirectory( dirStr ); m_parent->SetPlotSettings( m_plotOpts ); CreateFiles(); }
void DIALOG_GEN_MODULE_POSITION::OnOKButton( wxCommandEvent& event ) { m_unitsOpt = m_radioBoxUnits->GetSelection(); m_fileOpt = m_radioBoxFilesCount->GetSelection(); m_fileFormat = m_rbFormat->GetSelection(); m_config->Write( PLACEFILE_UNITS_KEY, m_unitsOpt ); m_config->Write( PLACEFILE_OPT_KEY, m_fileOpt ); m_config->Write( PLACEFILE_FORMAT_KEY, m_fileFormat ); // Set output directory and replace backslashes with forward ones // (Keep unix convention in cfg files) wxString dirStr; dirStr = m_outputDirectoryName->GetValue(); dirStr.Replace( wxT( "\\" ), wxT( "/" ) ); m_plotOpts.SetOutputDirectory( dirStr ); m_parent->SetPlotSettings( m_plotOpts ); CreateFiles(); // the dialog is not closed here. }
void DIALOG_PLOT::applyPlotSettings() { REPORTER& reporter = m_messagesPanel->Reporter(); PCB_PLOT_PARAMS tempOptions; tempOptions.SetExcludeEdgeLayer( m_excludeEdgeLayerOpt->GetValue() ); tempOptions.SetSubtractMaskFromSilk( m_subtractMaskFromSilk->GetValue() ); tempOptions.SetPlotFrameRef( m_plotSheetRef->GetValue() ); tempOptions.SetPlotPadsOnSilkLayer( m_plotPads_on_Silkscreen->GetValue() ); tempOptions.SetUseAuxOrigin( m_useAuxOriginCheckBox->GetValue() ); tempOptions.SetPlotValue( m_plotModuleValueOpt->GetValue() ); tempOptions.SetPlotReference( m_plotModuleRefOpt->GetValue() ); tempOptions.SetPlotInvisibleText( m_plotInvisibleText->GetValue() ); tempOptions.SetScaleSelection( m_scaleOpt->GetSelection() ); tempOptions.SetDrillMarksType( static_cast<PCB_PLOT_PARAMS::DrillMarksType> ( m_drillShapeOpt->GetSelection() ) ); tempOptions.SetMirror( m_plotMirrorOpt->GetValue() ); tempOptions.SetPlotMode( m_plotModeOpt->GetSelection() == 1 ? SKETCH : FILLED ); tempOptions.SetDXFPlotPolygonMode( m_DXF_plotModeOpt->GetValue() ); tempOptions.SetPlotViaOnMaskLayer( m_plotNoViaOnMaskOpt->GetValue() ); if( !m_DXF_plotTextStrokeFontOpt->IsEnabled() ) // Currently, only DXF supports this option tempOptions.SetTextMode( PLOTTEXTMODE_DEFAULT ); else tempOptions.SetTextMode( m_DXF_plotTextStrokeFontOpt->GetValue() ? PLOTTEXTMODE_DEFAULT : PLOTTEXTMODE_NATIVE ); // Update settings from text fields. Rewrite values back to the fields, // since the values may have been constrained by the setters. // read HPLG pen size (this param is stored in mils) wxString msg = m_HPGLPenSizeOpt->GetValue(); int tmp = ValueFromString( g_UserUnit, msg ) / IU_PER_MILS; if( !tempOptions.SetHPGLPenDiameter( tmp ) ) { msg = StringFromValue( g_UserUnit, tempOptions.GetHPGLPenDiameter() * IU_PER_MILS ); m_HPGLPenSizeOpt->SetValue( msg ); msg.Printf( _( "HPGL pen size constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } // Default linewidth msg = m_linesWidth->GetValue(); tmp = ValueFromString( g_UserUnit, msg ); if( !tempOptions.SetLineWidth( tmp ) ) { msg = StringFromValue( g_UserUnit, tempOptions.GetLineWidth() ); m_linesWidth->SetValue( msg ); msg.Printf( _( "Default line width constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } // X scale double tmpDouble; msg = m_fineAdjustXscaleOpt->GetValue(); msg.ToDouble( &tmpDouble ); if( !setDouble( &m_XScaleAdjust, tmpDouble, PLOT_MIN_SCALE, PLOT_MAX_SCALE ) ) { msg.Printf( wxT( "%f" ), m_XScaleAdjust ); m_fineAdjustXscaleOpt->SetValue( msg ); msg.Printf( _( "X scale constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } ConfigBaseWriteDouble( m_config, OPTKEY_PLOT_X_FINESCALE_ADJ, m_XScaleAdjust ); // Y scale msg = m_fineAdjustYscaleOpt->GetValue(); msg.ToDouble( &tmpDouble ); if( !setDouble( &m_YScaleAdjust, tmpDouble, PLOT_MIN_SCALE, PLOT_MAX_SCALE ) ) { msg.Printf( wxT( "%f" ), m_YScaleAdjust ); m_fineAdjustYscaleOpt->SetValue( msg ); msg.Printf( _( "Y scale constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } ConfigBaseWriteDouble( m_config, OPTKEY_PLOT_Y_FINESCALE_ADJ, m_YScaleAdjust ); // PS Width correction msg = m_PSFineAdjustWidthOpt->GetValue(); int itmp = ValueFromString( g_UserUnit, msg ); if( !setInt( &m_PSWidthAdjust, itmp, m_widthAdjustMinValue, m_widthAdjustMaxValue ) ) { msg = StringFromValue( g_UserUnit, m_PSWidthAdjust ); m_PSFineAdjustWidthOpt->SetValue( msg ); msg.Printf( _( "Width correction constrained. " "The reasonable width correction value must be in a range of " " [%+f; %+f] (%s) for current design rules. " ), To_User_Unit( g_UserUnit, m_widthAdjustMinValue ), To_User_Unit( g_UserUnit, m_widthAdjustMaxValue ), ( g_UserUnit == INCHES ) ? wxT( "\"" ) : wxT( "mm" ) ); reporter.Report( msg, REPORTER::RPT_WARNING ); } // Store m_PSWidthAdjust in mm in user config ConfigBaseWriteDouble( m_config, CONFIG_PS_FINEWIDTH_ADJ, (double)m_PSWidthAdjust / IU_PER_MM ); tempOptions.SetFormat( getPlotFormat() ); tempOptions.SetUseGerberProtelExtensions( m_useGerberExtensions->GetValue() ); tempOptions.SetUseGerberAttributes( m_useGerberX2Attributes->GetValue() ); tempOptions.SetIncludeGerberNetlistInfo( m_useGerberNetAttributes->GetValue() ); tempOptions.SetCreateGerberJobFile( m_generateGerberJobFile->GetValue() ); tempOptions.SetGerberPrecision( m_rbGerberFormat->GetSelection() == 0 ? 5 : 6 ); LSET selectedLayers; for( unsigned i = 0; i < m_layerList.size(); i++ ) { if( m_layerCheckListBox->IsChecked( i ) ) selectedLayers.set( m_layerList[i] ); } // Get a list of copper layers that aren't being used by inverting enabled layers. LSET disabledCopperLayers = LSET::AllCuMask() & ~m_board->GetEnabledLayers(); // Enable all of the disabled copper layers. // If someone enables more copper layers they will be selected by default. selectedLayers = selectedLayers | disabledCopperLayers; tempOptions.SetLayerSelection( selectedLayers ); tempOptions.SetNegative( m_plotPSNegativeOpt->GetValue() ); tempOptions.SetA4Output( m_forcePSA4OutputOpt->GetValue() ); // Set output directory and replace backslashes with forward ones wxString dirStr; dirStr = m_outputDirectoryName->GetValue(); dirStr.Replace( wxT( "\\" ), wxT( "/" ) ); tempOptions.SetOutputDirectory( dirStr ); if( !m_plotOpts.IsSameAs( tempOptions, false ) ) { // First, mark board as modified only for parameters saved in file if( !m_plotOpts.IsSameAs( tempOptions, true ) ) m_parent->OnModify(); // Now, save any change, for the session m_parent->SetPlotSettings( tempOptions ); m_plotOpts = tempOptions; } }
void DIALOG_PLOT::applyPlotSettings() { REPORTER& reporter = m_messagesPanel->Reporter(); PCB_PLOT_PARAMS tempOptions; tempOptions.SetExcludeEdgeLayer( m_excludeEdgeLayerOpt->GetValue() ); tempOptions.SetSubtractMaskFromSilk( m_subtractMaskFromSilk->GetValue() ); tempOptions.SetPlotFrameRef( m_plotSheetRef->GetValue() ); tempOptions.SetPlotPadsOnSilkLayer( m_plotPads_on_Silkscreen->GetValue() ); tempOptions.SetUseAuxOrigin( m_useAuxOriginCheckBox->GetValue() ); tempOptions.SetPlotValue( m_plotModuleValueOpt->GetValue() ); tempOptions.SetPlotReference( m_plotModuleRefOpt->GetValue() ); tempOptions.SetPlotInvisibleText( m_plotInvisibleText->GetValue() ); tempOptions.SetScaleSelection( m_scaleOpt->GetSelection() ); tempOptions.SetDrillMarksType( static_cast<PCB_PLOT_PARAMS::DrillMarksType> ( m_drillShapeOpt->GetSelection() ) ); tempOptions.SetMirror( m_plotMirrorOpt->GetValue() ); tempOptions.SetPlotMode( m_plotModeOpt->GetSelection() == 1 ? SKETCH : FILLED ); tempOptions.SetPlotViaOnMaskLayer( m_plotNoViaOnMaskOpt->GetValue() ); // Update settings from text fields. Rewrite values back to the fields, // since the values may have been constrained by the setters. // read HPLG pen size (this param is stored in mils) wxString msg = m_HPGLPenSizeOpt->GetValue(); int tmp = ValueFromString( g_UserUnit, msg ) / IU_PER_MILS; if( !tempOptions.SetHPGLPenDiameter( tmp ) ) { msg = StringFromValue( g_UserUnit, tempOptions.GetHPGLPenDiameter() * IU_PER_MILS ); m_HPGLPenSizeOpt->SetValue( msg ); msg.Printf( _( "HPGL pen size constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } // Read HPGL pen overlay (this param is stored in mils) msg = m_HPGLPenOverlayOpt->GetValue(); tmp = ValueFromString( g_UserUnit, msg ) / IU_PER_MILS; if( !tempOptions.SetHPGLPenOverlay( tmp ) ) { msg = StringFromValue( g_UserUnit, tempOptions.GetHPGLPenOverlay() * IU_PER_MILS ); m_HPGLPenOverlayOpt->SetValue( msg ); msg.Printf( _( "HPGL pen overlay constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } // Default linewidth msg = m_linesWidth->GetValue(); tmp = ValueFromString( g_UserUnit, msg ); if( !tempOptions.SetLineWidth( tmp ) ) { msg = StringFromValue( g_UserUnit, tempOptions.GetLineWidth() ); m_linesWidth->SetValue( msg ); msg.Printf( _( "Default line width constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } // X scale double tmpDouble; msg = m_fineAdjustXscaleOpt->GetValue(); msg.ToDouble( &tmpDouble ); if( !setDouble( &m_XScaleAdjust, tmpDouble, PLOT_MIN_SCALE, PLOT_MAX_SCALE ) ) { msg.Printf( wxT( "%f" ), m_XScaleAdjust ); m_fineAdjustXscaleOpt->SetValue( msg ); msg.Printf( _( "X scale constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } ConfigBaseWriteDouble( m_config, OPTKEY_PLOT_X_FINESCALE_ADJ, m_XScaleAdjust ); // Y scale msg = m_fineAdjustYscaleOpt->GetValue(); msg.ToDouble( &tmpDouble ); if( !setDouble( &m_YScaleAdjust, tmpDouble, PLOT_MIN_SCALE, PLOT_MAX_SCALE ) ) { msg.Printf( wxT( "%f" ), m_YScaleAdjust ); m_fineAdjustYscaleOpt->SetValue( msg ); msg.Printf( _( "Y scale constrained." ) ); reporter.Report( msg, REPORTER::RPT_INFO ); } ConfigBaseWriteDouble( m_config, OPTKEY_PLOT_Y_FINESCALE_ADJ, m_YScaleAdjust ); // PS Width correction msg = m_PSFineAdjustWidthOpt->GetValue(); int itmp = ValueFromString( g_UserUnit, msg ); if( !setInt( &m_PSWidthAdjust, itmp, m_widthAdjustMinValue, m_widthAdjustMaxValue ) ) { msg = StringFromValue( g_UserUnit, m_PSWidthAdjust ); m_PSFineAdjustWidthOpt->SetValue( msg ); msg.Printf( _( "Width correction constrained. " "The reasonable width correction value must be in a range of " " [%+f; %+f] (%s) for current design rules. " ), To_User_Unit( g_UserUnit, m_widthAdjustMinValue ), To_User_Unit( g_UserUnit, m_widthAdjustMaxValue ), ( g_UserUnit == INCHES ) ? wxT( "\"" ) : wxT( "mm" ) ); reporter.Report( msg, REPORTER::RPT_WARNING ); } // Store m_PSWidthAdjust in mm in user config ConfigBaseWriteDouble( m_config, CONFIG_PS_FINEWIDTH_ADJ, (double)m_PSWidthAdjust / IU_PER_MM ); tempOptions.SetFormat( getPlotFormat() ); tempOptions.SetUseGerberExtensions( m_useGerberExtensions->GetValue() ); tempOptions.SetUseGerberAttributes( m_useGerberAttributes->GetValue() ); tempOptions.SetGerberPrecision( m_rbGerberFormat->GetSelection() == 0 ? 5 : 6 ); LSET selectedLayers; for( unsigned i = 0; i < m_layerList.size(); i++ ) { if( m_layerCheckListBox->IsChecked( i ) ) selectedLayers.set( m_layerList[i] ); } tempOptions.SetLayerSelection( selectedLayers ); tempOptions.SetNegative( m_plotPSNegativeOpt->GetValue() ); tempOptions.SetA4Output( m_forcePSA4OutputOpt->GetValue() ); // Set output directory and replace backslashes with forward ones wxString dirStr; dirStr = m_outputDirectoryName->GetValue(); dirStr.Replace( wxT( "\\" ), wxT( "/" ) ); tempOptions.SetOutputDirectory( dirStr ); if( m_plotOpts != tempOptions ) { m_parent->SetPlotSettings( tempOptions ); m_plotOpts = tempOptions; m_parent->OnModify(); } }
bool DIALOG_GEN_MODULE_POSITION::CreateFiles() { BOARD * brd = m_parent->GetBoard(); wxFileName fn; wxString msg; bool singleFile = OneFileOnly(); int fullcount = 0; // Count the footprints to place, do not yet create a file int fpcount = m_parent->DoGenFootprintsPositionFile( wxEmptyString, UnitsMM(), ForceAllSmd(), 2 ); if( fpcount == 0) { wxMessageBox( _( "No modules for automated placement." ) ); return false; } // Create output directory if it does not exist (also transform it in // absolute form). Bail if it fails wxFileName outputDir = wxFileName::DirName( m_plotOpts.GetOutputDirectory() ); wxString boardFilename = m_parent->GetBoard()->GetFileName(); m_reporter = &m_messagesPanel->Reporter(); if( !EnsureFileDirectoryExists( &outputDir, boardFilename, m_reporter ) ) { msg.Printf( _( "Could not write plot files to folder \"%s\"." ), GetChars( outputDir.GetPath() ) ); DisplayError( this, msg ); return false; } fn = m_parent->GetBoard()->GetFileName(); fn.SetPath( outputDir.GetPath() ); // Create the the Front or Top side placement file, // or the single file int side = 1; if( singleFile ) { side = 2; fn.SetName( fn.GetName() + wxT( "-" ) + wxT("all") ); } else fn.SetName( fn.GetName() + wxT( "-" ) + frontSideName ); fn.SetExt( FootprintPlaceFileExtension ); fpcount = m_parent->DoGenFootprintsPositionFile( fn.GetFullPath(), UnitsMM(), ForceAllSmd(), side ); if( fpcount < 0 ) { msg.Printf( _( "Unable to create '%s'." ), GetChars( fn.GetFullPath() ) ); wxMessageBox( msg ); m_reporter->Report( msg, REPORTER::RPT_ERROR ); return false; } if( singleFile ) msg.Printf( _( "Place file: '%s'." ), GetChars( fn.GetFullPath() ) ); else msg.Printf( _( "Front side (top side) place file: '%s'." ), GetChars( fn.GetFullPath() ) ); m_reporter->Report( msg, REPORTER::RPT_INFO ); msg.Printf( _( "Component count: %d." ), fpcount ); m_reporter->Report( msg, REPORTER::RPT_INFO ); if( singleFile ) { m_reporter->Report( _( "Componment Placement File generation OK." ), REPORTER::RPT_ACTION ); return true; } // Create the Back or Bottom side placement file fullcount = fpcount; side = 0; fn = brd->GetFileName(); fn.SetPath( outputDir.GetPath() ); fn.SetName( fn.GetName() + wxT( "-" ) + backSideName ); fn.SetExt( wxT( "pos" ) ); fpcount = m_parent->DoGenFootprintsPositionFile( fn.GetFullPath(), UnitsMM(), ForceAllSmd(), side ); if( fpcount < 0 ) { msg.Printf( _( "Unable to create file '%s'." ), GetChars( fn.GetFullPath() ) ); m_reporter->Report( msg, REPORTER::RPT_ERROR ); wxMessageBox( msg ); return false; } // Display results if( !singleFile ) { msg.Printf( _( "Back side (bottom side) place file: '%s'." ), GetChars( fn.GetFullPath() ) ); m_reporter->Report( msg, REPORTER::RPT_INFO ); msg.Printf( _( "Component count: %d." ), fpcount ); m_reporter->Report( msg, REPORTER::RPT_INFO ); } if( !singleFile ) { fullcount += fpcount; msg.Printf( _( "Full component count: %d\n" ), fullcount ); m_reporter->Report( msg, REPORTER::RPT_INFO ); } m_reporter->Report( _( "Componment Placement File generation OK." ), REPORTER::RPT_ACTION ); return true; }
/** 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 ) { // Create the plotter driver and set the few plotter specific // options PLOTTER* plotter = NULL; switch( aPlotOpts->GetFormat() ) { case PLOT_FORMAT_DXF: 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() ); plotter = PS_plotter; break; case PLOT_FORMAT_PDF: 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 ); plotter = HPGL_plotter; break; case PLOT_FORMAT_GERBER: plotter = new GERBER_PLOTTER(); break; case PLOT_FORMAT_SVG: plotter = new SVG_PLOTTER(); break; default: wxASSERT( false ); return NULL; } // Compute the viewport and set the other options // page layout is not mirrored, so temporary change mirror option // just to plot the page layout PCB_PLOT_PARAMS plotOpts = *aPlotOpts; if( plotOpts.GetPlotFrameRef() && plotOpts.GetMirror() ) plotOpts.SetMirror( false ); initializePlotter( plotter, aBoard, &plotOpts ); if( plotter->OpenFile( aFullFileName ) ) { plotter->StartPlot(); // Plot the frame reference if requested if( aPlotOpts->GetPlotFrameRef() ) { PlotWorkSheet( plotter, aBoard->GetTitleBlock(), aBoard->GetPageSettings(), 1, 1, // Only one page aSheetDesc, aBoard->GetFileName() ); if( aPlotOpts->GetMirror() ) initializePlotter( plotter, aBoard, aPlotOpts ); } /* When plotting a negative board: draw a black rectangle * (background for plot board in white) and switch the current * color to WHITE; note the color inversion is actually done * in the driver (if supported) */ if( aPlotOpts->GetNegative() ) { EDA_RECT bbox = aBoard->ComputeBoundingBox(); FillNegativeKnockout( plotter, bbox ); } return plotter; } delete plotter; return NULL; }
/* Creates the plot for silkscreen layers * Silkscreen layers have specific requirement for pads (not filled) and texts * (with option to remove them from some copper areas (pads...) */ void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); // Plot edge layer and graphic items itemplotter.PlotBoardGraphicItems(); // Plot footprint outlines : itemplotter.Plot_Edges_Modules(); // 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->Pads(); pad != NULL; pad = pad->Next() ) { // See if the pad is on this layer LAYER_MSK masklayer = pad->GetLayerMask(); if( (masklayer & layersmask_plotpads) == 0 ) continue; EDA_COLOR_T color = ColorFromInt(0); if( (layersmask_plotpads & SILKSCREEN_LAYER_BACK) ) color = aBoard->GetLayerColor( SILKSCREEN_N_BACK ); if((layersmask_plotpads & SILKSCREEN_LAYER_FRONT ) ) color = ColorFromInt( color | aBoard->GetLayerColor( SILKSCREEN_N_FRONT ) ); itemplotter.PlotPad( pad, color, LINE ); } } } // Plot footprints fields (ref, value ...) for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { if( ! itemplotter.PlotAllTextsModule( module ) ) { wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ), GetChars( module->GetReference() ) ); } } // Plot filled areas for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii ); if( ( GetLayerMask( edge_zone->GetLayer() ) & aLayerMask ) == 0 ) continue; itemplotter.PlotFilledAreas( edge_zone ); } // 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( ( GetLayerMask( seg->GetLayer() ) & aLayerMask ) == 0 ) continue; aPlotter->ThickSegment( seg->GetStart(), seg->GetEnd(), seg->GetWidth(), itemplotter.GetMode() ); } }
/* Plot a solder mask layer. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers, * unless the minimum thickness is 0. * Currently the algo is: * 1 - build all pad shapes as polygons with a size inflated by * mask clearance + (min width solder mask /2) * 2 - Merge shapes * 3 - deflate result by (min width solder mask /2) * 4 - oring result by all pad shapes as polygons with a size inflated by * mask clearance only (because deflate sometimes creates shape artifacts) * 5 - draw result as polygons * * TODO: * make this calculation only for shapes with clearance near than (min width solder mask) * (using DRC algo) * plot all other shapes by flashing the basing shape * (shapes will be better, and calculations faster) */ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) { LAYER_NUM layer = ( aLayerMask & SOLDERMASK_LAYER_BACK ) ? SOLDERMASK_N_BACK : SOLDERMASK_N_FRONT; int inflate = aMinThickness/2; BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); // Plot edge layer and graphic items itemplotter.PlotBoardGraphicItems(); for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( layer != item->GetLayer() ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Build polygons for each pad shape. // the size of the shape on solder mask should be: // size of pad + clearance around the pad. // clearance = solder mask clearance + extra margin // extra margin is half the min width for solder mask // This extra margin is used to merge too close shapes // (distance < aMinThickness), and will be removed when creating // the actual shapes CPOLYGONS_LIST bufferPolys; // Contains shapes to plot CPOLYGONS_LIST initialPolys; // Contains exact shapes to plot /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount ) */ int circleToSegmentsCount = 32; double correction = 1.0 / cos( M_PI / circleToSegmentsCount ); // Plot pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { // add shapes with exact size module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0, circleToSegmentsCount, correction ); // add shapes inflated by aMinThickness/2 module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, inflate, circleToSegmentsCount, correction ); } // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, if( aPlotOpt.GetPlotViaOnMaskLayer() ) { // The current layer is a solder mask, // use the global mask clearance for vias int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; int via_margin = via_clearance + inflate; for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dynamic_cast<const VIA*>( track ); if( !via ) continue; // vias are plotted only if they are on the corresponding // external copper layer LAYER_MSK via_mask_layer = via->GetLayerMask(); 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; via->TransformShapeWithClearanceToPolygon( bufferPolys, via_margin, circleToSegmentsCount, correction ); via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance, circleToSegmentsCount, correction ); } } // Add filled zone areas for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( zone->GetLayer() != layer ) continue; zone->TransformOutlinesShapeWithClearanceToPolygon( bufferPolys, inflate, true ); } // Now: // 1 - merge areas which are intersecting, i.e. remove gaps // having a thickness < aMinThickness // 2 - deflate resulting areas by aMinThickness/2 KI_POLYGON_SET areasToMerge; bufferPolys.ExportTo( areasToMerge ); KI_POLYGON_SET initialAreas; initialPolys.ExportTo( initialAreas ); // Merge polygons: because each shape was created with an extra margin // = aMinThickness/2, shapes too close ( dist < aMinThickness ) // will be merged, because they are overlapping KI_POLYGON_SET areas; areas |= areasToMerge; // Deflate: remove the extra margin, to create the actual shapes // Here I am using polygon:resize, because this function creates better shapes // than deflate algo. // Use here deflate with arc creation and 18 segments per circle to create arcs // In boost polygon (at least v 1.54 and previous) in very rare cases resize crashes // with 16 segments (perhaps related to 45 degrees pads). So using 18 segments // is a workaround to try to avoid these crashes areas = resize( areas, -inflate , true, 18 ); // Resize slightly changes shapes. So *ensure* initial shapes are kept areas |= initialAreas; // To avoid a lot of code, use a ZONE_CONTAINER // to plot polygons, because they are exactly like // filled areas in zones ZONE_CONTAINER zone( aBoard ); zone.SetArcSegmentCount( 32 ); zone.SetMinThickness( 0 ); // trace polygons only zone.SetLayer ( layer ); zone.CopyPolygonsFromKiPolygonListToFilledPolysList( areas ); itemplotter.PlotFilledAreas( &zone ); }
/* Plot a copper layer or mask. * Silk screen layers are not plotted here. */ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); EDA_DRAW_MODE_T plotMode = aPlotOpt.GetMode(); // Plot edge layer and graphic items itemplotter.PlotBoardGraphicItems(); // Draw footprint shapes without pads (pads will plotted later) // We plot here module texts, but they are usually on silkscreen layer, // so they are not plot here but plot by PlotSilkScreen() // Plot footprints fields (ref, value ...) for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { if( ! itemplotter.PlotAllTextsModule( module ) ) { wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ), GetChars( module->GetReference() ) ); } } for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( ! (aLayerMask & GetLayerMask( item->GetLayer() ) ) ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Plot footprint pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( (pad->GetLayerMask() & aLayerMask) == 0 ) continue; wxSize margin; double width_adj = 0; if( aLayerMask & ALL_CU_LAYERS ) width_adj = itemplotter.getFineWidthAdj(); 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; } wxSize padPlotsSize; padPlotsSize.x = pad->GetSize().x + ( 2 * margin.x ) + width_adj; padPlotsSize.y = pad->GetSize().y + ( 2 * margin.y ) + width_adj; // Don't draw a null size item : if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) continue; EDA_COLOR_T color = BLACK; if( (pad->GetLayerMask() & LAYER_BACK) ) color = aBoard->GetVisibleElementColor( PAD_BK_VISIBLE ); if((pad->GetLayerMask() & LAYER_FRONT ) ) color = ColorFromInt( color | aBoard->GetVisibleElementColor( PAD_FR_VISIBLE ) ); // Temporary set the pad size to the required plot size: wxSize tmppadsize = pad->GetSize(); pad->SetSize( padPlotsSize ); switch( pad->GetShape() ) { case PAD_CIRCLE: case PAD_OVAL: if( aPlotOpt.GetSkipPlotNPTH_Pads() && (pad->GetSize() == pad->GetDrillSize()) && (pad->GetAttribute() == PAD_HOLE_NOT_PLATED) ) break; // Fall through: case PAD_TRAPEZOID: case PAD_RECT: default: itemplotter.PlotPad( pad, color, plotMode ); break; } pad->SetSize( tmppadsize ); // Restore the pad size } } // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true, // plot them on solder mask for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* Via = dynamic_cast<const VIA*>( track ); if( !Via ) continue; // vias are not plotted if not on selected layer, but if layer // is SOLDERMASK_LAYER_BACK or SOLDERMASK_LAYER_FRONT,vias are drawn, // only if they are on the corresponding external copper layer int via_mask_layer = Via->GetLayerMask(); if( aPlotOpt.GetPlotViaOnMaskLayer() ) { 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 = itemplotter.getFineWidthAdj(); int diameter = Via->GetWidth() + 2 * via_margin + width_adj; // Don't draw a null size item : if( diameter <= 0 ) continue; EDA_COLOR_T color = aBoard->GetVisibleElementColor(VIAS_VISIBLE + Via->GetViaType()); // Set plot color (change WHITE to LIGHTGRAY because // the white items are not seen on a white paper or screen aPlotter->SetColor( color != WHITE ? color : LIGHTGRAY); aPlotter->FlashPadCircle( Via->GetStart(), diameter, plotMode ); } // Plot tracks (not vias) : for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { if( track->Type() == PCB_VIA_T ) continue; if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 ) continue; int width = track->GetWidth() + itemplotter.getFineWidthAdj(); aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) ); aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode ); } // Plot zones (outdated, for old boards compatibility): for( TRACK* track = aBoard->m_Zone; track; track = track->Next() ) { if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 ) continue; int width = track->GetWidth() + itemplotter.getFineWidthAdj(); aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) ); aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode ); } // Plot filled ares for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( ( GetLayerMask(zone->GetLayer() ) & aLayerMask ) == 0 ) continue; itemplotter.PlotFilledAreas( zone ); } // Adding drill marks, if required and if the plotter is able to plot them: if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) itemplotter.PlotDrillMarks(); }
void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_NUM aLayer, const PCB_PLOT_PARAMS& aPlotOpt ) { PCB_PLOT_PARAMS plotOpt = aPlotOpt; int soldermask_min_thickness = aBoard->GetDesignSettings().m_SolderMaskMinWidth; // Set a default 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. LAYER_MSK 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 ): plotOpt.SetSkipPlotNPTH_Pads( true ); // Drill mark will be plotted, // if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); break; case SOLDERMASK_N_BACK: case SOLDERMASK_N_FRONT: plotOpt.SetSkipPlotNPTH_Pads( false ); // Disable plot pad holes plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); // Plot solder mask: if( soldermask_min_thickness == 0 ) PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); else PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt, soldermask_min_thickness ); break; case SOLDERPASTE_N_BACK: case SOLDERPASTE_N_FRONT: plotOpt.SetSkipPlotNPTH_Pads( false ); // Disable plot pad holes plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); break; case SILKSCREEN_N_FRONT: case SILKSCREEN_N_BACK: PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt ); // Gerber: Subtract soldermask from silkscreen if enabled if( aPlotter->GetPlotterType() == PLOT_FORMAT_GERBER && plotOpt.GetSubtractMaskFromSilk() ) { if( aLayer == SILKSCREEN_N_FRONT ) layer_mask = GetLayerMask( SOLDERMASK_N_FRONT ); else layer_mask = GetLayerMask( SOLDERMASK_N_BACK ); // Create the mask to subtract by creating a negative layer polarity aPlotter->SetLayerPolarity( false ); // Disable plot pad holes plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); // Plot the mask PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); } break; default: PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt ); break; } }
/* Plot a solder mask layer. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers, * unless the minimum thickness is 0. * Currently the algo is: * 1 - build all pad shapes as polygons with a size inflated by * mask clearance + (min width solder mask /2) * 2 - Merge shapes * 3 - deflate result by (min width solder mask /2) * 4 - oring result by all pad shapes as polygons with a size inflated by * mask clearance only (because deflate sometimes creates shape artifacts) * 5 - draw result as polygons * * TODO: * make this calculation only for shapes with clearance near than (min width solder mask) * (using DRC algo) * plot all other shapes by flashing the basing shape * (shapes will be better, and calculations faster) */ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) { LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask; int inflate = aMinThickness/2; BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); // Plot edge layer and graphic items // They do not have a solder Mask margin, because they are only graphic items // on this layer (like logos), not actually areas around pads. itemplotter.PlotBoardGraphicItems(); for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( layer != item->GetLayer() ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Build polygons for each pad shape. // the size of the shape on solder mask should be: // size of pad + clearance around the pad. // clearance = solder mask clearance + extra margin // extra margin is half the min width for solder mask // This extra margin is used to merge too close shapes // (distance < aMinThickness), and will be removed when creating // the actual shapes SHAPE_POLY_SET areas; // Contains shapes to plot SHAPE_POLY_SET initialPolys; // Contains exact shapes to plot /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount ) */ int circleToSegmentsCount = 32; double correction = 1.0 / cos( M_PI / circleToSegmentsCount ); // Plot pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { // add shapes with exact size module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0, circleToSegmentsCount, correction ); // add shapes inflated by aMinThickness/2 module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate, circleToSegmentsCount, correction ); } // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, if( aPlotOpt.GetPlotViaOnMaskLayer() ) { // The current layer is a solder mask, // use the global mask clearance for vias int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; int via_margin = via_clearance + inflate; for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( !via ) continue; // vias are plotted only if they are on the corresponding // external copper layer LSET via_set = via->GetLayerSet(); if( via_set[B_Cu] ) via_set.set( B_Mask ); if( via_set[F_Cu] ) via_set.set( F_Mask ); if( !( via_set & aLayerMask ).any() ) continue; via->TransformShapeWithClearanceToPolygon( areas, via_margin, circleToSegmentsCount, correction ); via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance, circleToSegmentsCount, correction ); } } // Add filled zone areas. #if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin; #else int zone_margin = 0; #endif for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( zone->GetLayer() != layer ) continue; zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate+zone_margin, false ); zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin, false ); } // To avoid a lot of code, use a ZONE_CONTAINER // to handle and plot polygons, because our polygons look exactly like // filled areas in zones // Note, also this code is not optimized: it creates a lot of copy/duplicate data // However it is not complex, and fast enough for plot purposes (copy/convert data // is only a very small calculation time for these calculations) ZONE_CONTAINER zone( aBoard ); zone.SetArcSegmentCount( 32 ); zone.SetMinThickness( 0 ); // trace polygons only zone.SetLayer ( layer ); areas.BooleanAdd( initialPolys ); areas.Inflate( -inflate, circleToSegmentsCount ); // Combine the current areas to initial areas. This is mandatory because // inflate/deflate transform is not perfect, and we want the initial areas perfectly kept areas.BooleanAdd( initialPolys ); areas.Fracture(); zone.AddFilledPolysList( areas ); itemplotter.PlotFilledAreas( &zone ); }
/* Plot outlines of copper, for copper layer */ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); SHAPE_POLY_SET outlines; for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) ); seq; ++seq ) { LAYER_ID layer = *seq; outlines.RemoveAllContours(); aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines ); outlines.Simplify(); // Plot outlines std::vector< wxPoint > cornerList; // Now we have one or more basic polygons: plot each polygon for( int ii = 0; ii < outlines.OutlineCount(); ii++ ) { for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ ) { cornerList.clear(); const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 ); for( int jj = 0; jj < path.PointCount(); jj++ ) cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) ); // Ensure the polygon is closed if( cornerList[0] != cornerList[cornerList.size() - 1] ) cornerList.push_back( cornerList[0] ); aPlotter->PlotPoly( cornerList, NO_FILL ); } } // Plot pad holes if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) { for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { wxSize hole = pad->GetDrillSize(); if( hole.x == 0 || hole.y == 0 ) continue; if( hole.x == hole.y ) aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL ); else { wxPoint drl_start, drl_end; int width; pad->GetOblongDrillGeometry( drl_start, drl_end, width ); aPlotter->ThickSegment( pad->GetPosition() + drl_start, pad->GetPosition() + drl_end, width, SKETCH ); } } } } // Plot vias holes for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes { aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL ); } } } }
void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_ID aLayer, const PCB_PLOT_PARAMS& aPlotOpt ) { PCB_PLOT_PARAMS plotOpt = aPlotOpt; int soldermask_min_thickness = aBoard->GetDesignSettings().m_SolderMaskMinWidth; // Set a default 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. LSET layer_mask( aLayer ); if( !aPlotOpt.GetExcludeEdgeLayer() ) layer_mask.set( Edge_Cuts ); if( IsCopperLayer( aLayer ) ) { // Skip NPTH pads on copper layers ( only if hole size == pad size ): // Drill mark will be plotted, // if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE if( plotOpt.GetFormat() == PLOT_FORMAT_DXF ) { plotOpt.SetSkipPlotNPTH_Pads( false ); PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt ); } else { plotOpt.SetSkipPlotNPTH_Pads( true ); PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); } } else { switch( aLayer ) { case B_Mask: case F_Mask: plotOpt.SetSkipPlotNPTH_Pads( false ); // Disable plot pad holes plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); // Plot solder mask: if( soldermask_min_thickness == 0 ) { if( plotOpt.GetFormat() == PLOT_FORMAT_DXF ) PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt ); else PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); } else PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt, soldermask_min_thickness ); break; case B_Adhes: case F_Adhes: case B_Paste: case F_Paste: plotOpt.SetSkipPlotNPTH_Pads( false ); // Disable plot pad holes plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); if( plotOpt.GetFormat() == PLOT_FORMAT_DXF ) PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt ); else PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); break; case F_SilkS: case B_SilkS: if( plotOpt.GetFormat() == PLOT_FORMAT_DXF ) PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt ); else PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt ); // Gerber: Subtract soldermask from silkscreen if enabled if( aPlotter->GetPlotterType() == PLOT_FORMAT_GERBER && plotOpt.GetSubtractMaskFromSilk() ) { if( aLayer == F_SilkS ) layer_mask = LSET( F_Mask ); else layer_mask = LSET( B_Mask ); // Create the mask to subtract by creating a negative layer polarity aPlotter->SetLayerPolarity( false ); // Disable plot pad holes plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); // Plot the mask PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); } break; // These layers are plotted like silk screen layers. // Mainly, pads on these layers are not filled. // This is not necessary the best choice. case Dwgs_User: case Cmts_User: case Eco1_User: case Eco2_User: case Edge_Cuts: case Margin: case F_CrtYd: case B_CrtYd: case F_Fab: case B_Fab: plotOpt.SetSkipPlotNPTH_Pads( false ); plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); if( plotOpt.GetFormat() == PLOT_FORMAT_DXF ) PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt ); else PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt ); break; default: plotOpt.SetSkipPlotNPTH_Pads( false ); plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE ); if( plotOpt.GetFormat() == PLOT_FORMAT_DXF ) PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt ); else PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt ); break; } } }
void PlotLayerOutlines( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); CPOLYGONS_LIST outlines; for( LAYER_NUM layer = FIRST_LAYER; layer < NB_PCB_LAYERS; layer++ ) { LAYER_MSK layer_mask = GetLayerMask( layer ); if( (aLayerMask & layer_mask ) == 0 ) continue; outlines.RemoveAllContours(); aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines ); // Merge all overlapping polygons. KI_POLYGON_SET kpolygons; KI_POLYGON_SET ktmp; outlines.ExportTo( ktmp ); kpolygons += ktmp; // Plot outlines std::vector< wxPoint > cornerList; for( unsigned ii = 0; ii < kpolygons.size(); ii++ ) { KI_POLYGON polygon = kpolygons[ii]; // polygon contains only one polygon, but it can have holes linked by // overlapping segments. // To plot clean outlines, we have to break this polygon into more polygons with // no overlapping segments, using Clipper, because boost::polygon // does not allow that ClipperLib::Path raw_polygon; ClipperLib::Paths normalized_polygons; for( unsigned ic = 0; ic < polygon.size(); ic++ ) { KI_POLY_POINT corner = *(polygon.begin() + ic); raw_polygon.push_back( ClipperLib::IntPoint( corner.x(), corner.y() ) ); } ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons ); // Now we have one or more basic polygons: plot each polygon for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ ) { ClipperLib::Path& polygon = normalized_polygons[ii]; cornerList.clear(); for( unsigned jj = 0; jj < polygon.size(); jj++ ) cornerList.push_back( wxPoint( polygon[jj].X , polygon[jj].Y ) ); // Ensure the polygon is closed if( cornerList[0] != cornerList[cornerList.size()-1] ) cornerList.push_back( cornerList[0] ); aPlotter->PlotPoly( cornerList, NO_FILL ); } } // Plot pad holes if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) { for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { wxSize hole = pad->GetDrillSize(); if( hole.x == 0 || hole.y == 0 ) continue; if( hole.x == hole.y ) aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL ); else { wxPoint drl_start, drl_end; int width; pad->GetOblongDrillGeometry( drl_start, drl_end, width ); aPlotter->ThickSegment( pad->GetPosition() + drl_start, pad->GetPosition() + drl_end, width, SKETCH ); } } } } // Plot vias holes for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes { aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL ); } } } }