summaryrefslogtreecommitdiff
path: root/pcbnew/exporters/gen_drill_report_files.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/exporters/gen_drill_report_files.cpp')
-rw-r--r--pcbnew/exporters/gen_drill_report_files.cpp455
1 files changed, 455 insertions, 0 deletions
diff --git a/pcbnew/exporters/gen_drill_report_files.cpp b/pcbnew/exporters/gen_drill_report_files.cpp
new file mode 100644
index 0000000..94a6c75
--- /dev/null
+++ b/pcbnew/exporters/gen_drill_report_files.cpp
@@ -0,0 +1,455 @@
+/**
+ * @file gen_drill_report_files.cpp
+ * @brief Functions to create report and map files for EXCELLON drill files.
+ */
+
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 1992-2015 Jean_Pierre Charras <jp.charras at wanadoo.fr>
+ * Copyright (C) 1992-2015 KiCad Developers, see change_log.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 <confirm.h>
+#include <kicad_string.h>
+#include <macros.h>
+
+#include <class_board.h>
+
+#include <pcbnew.h>
+#include <pcbplot.h>
+#include <gendrill_Excellon_writer.h>
+
+/* Conversion utilities - these will be used often in there... */
+inline double diameter_in_inches( double ius )
+{
+ return ius * 0.001 / IU_PER_MILS;
+}
+
+
+inline double diameter_in_mm( double ius )
+{
+ return ius / IU_PER_MM;
+}
+
+
+bool EXCELLON_WRITER::GenDrillMapFile( const wxString& aFullFileName,
+ PlotFormat aFormat )
+{
+ double scale = 1.0;
+ wxPoint offset;
+ PLOTTER* plotter = NULL;
+ PAGE_INFO dummy( PAGE_INFO::A4, false );
+
+ PCB_PLOT_PARAMS plot_opts; // starts plotting with default options
+
+ LOCALE_IO toggle; // use standard C notation for float numbers
+
+ const PAGE_INFO& page_info = m_pageInfo ? *m_pageInfo : dummy;
+
+ // Calculate dimensions and center of PCB
+ EDA_RECT bbbox = m_pcb->ComputeBoundingBox( true );
+
+ // Calculate the scale for the format type, scale 1 in HPGL, drawing on
+ // an A4 sheet in PS, + text description of symbols
+ switch( aFormat )
+ {
+ case PLOT_FORMAT_GERBER:
+ offset = GetOffset();
+ plotter = new GERBER_PLOTTER();
+ plotter->SetViewport( offset, IU_PER_DECIMILS, scale, false );
+ plotter->SetGerberCoordinatesFormat( 5 ); // format x.5 unit = mm
+ break;
+
+ case PLOT_FORMAT_HPGL: // Scale for HPGL format.
+ {
+ HPGL_PLOTTER* hpgl_plotter = new HPGL_PLOTTER;
+ plotter = hpgl_plotter;
+ hpgl_plotter->SetPenNumber( plot_opts.GetHPGLPenNum() );
+ hpgl_plotter->SetPenSpeed( plot_opts.GetHPGLPenSpeed() );
+ hpgl_plotter->SetPenOverlap( 0 );
+ plotter->SetPageSettings( page_info );
+ plotter->SetViewport( offset, IU_PER_DECIMILS, scale, false );
+ }
+ break;
+
+
+ default:
+ wxASSERT( false );
+ // fall through
+ case PLOT_FORMAT_PDF:
+ case PLOT_FORMAT_POST:
+ {
+ PAGE_INFO pageA4( wxT( "A4" ) );
+ wxSize pageSizeIU = pageA4.GetSizeIU();
+
+ // Reserve a margin around the page.
+ int margin = KiROUND( 20 * IU_PER_MM );
+
+ // Calculate a scaling factor to print the board on the sheet
+ double Xscale = double( pageSizeIU.x - ( 2 * margin ) ) / bbbox.GetWidth();
+
+ // We should print the list of drill sizes, so reserve room for it
+ // 60% height for board 40% height for list
+ int ypagesize_for_board = KiROUND( pageSizeIU.y * 0.6 );
+ double Yscale = double( ypagesize_for_board - margin ) / bbbox.GetHeight();
+
+ scale = std::min( Xscale, Yscale );
+
+ // Experience shows the scale should not to large, because texts
+ // create problem (can be to big or too small).
+ // So the scale is clipped at 3.0;
+ scale = std::min( scale, 3.0 );
+
+ offset.x = KiROUND( double( bbbox.Centre().x ) -
+ ( pageSizeIU.x / 2.0 ) / scale );
+ offset.y = KiROUND( double( bbbox.Centre().y ) -
+ ( ypagesize_for_board / 2.0 ) / scale );
+
+ if( aFormat == PLOT_FORMAT_PDF )
+ plotter = new PDF_PLOTTER;
+ else
+ plotter = new PS_PLOTTER;
+
+ plotter->SetPageSettings( pageA4 );
+ plotter->SetViewport( offset, IU_PER_DECIMILS, scale, false );
+ }
+ break;
+
+ case PLOT_FORMAT_DXF:
+ {
+ DXF_PLOTTER* dxf_plotter = new DXF_PLOTTER;
+ plotter = dxf_plotter;
+ plotter->SetPageSettings( page_info );
+ plotter->SetViewport( offset, IU_PER_DECIMILS, scale, false );
+ }
+ break;
+
+ case PLOT_FORMAT_SVG:
+ {
+ SVG_PLOTTER* svg_plotter = new SVG_PLOTTER;
+ plotter = svg_plotter;
+ plotter->SetPageSettings( page_info );
+ plotter->SetViewport( offset, IU_PER_DECIMILS, scale, false );
+ }
+ break;
+ }
+
+ plotter->SetCreator( wxT( "PCBNEW" ) );
+ plotter->SetDefaultLineWidth( 5 * IU_PER_MILS );
+ plotter->SetColorMode( false );
+
+ if( ! plotter->OpenFile( aFullFileName ) )
+ {
+ delete plotter;
+ return false;
+ }
+
+ plotter->StartPlot();
+
+ // Draw items on edge layer (not all, only items useful for drill map
+ BRDITEMS_PLOTTER itemplotter( plotter, m_pcb, plot_opts );
+ itemplotter.SetLayerSet( Edge_Cuts );
+
+ for( EDA_ITEM* PtStruct = m_pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+ {
+ switch( PtStruct->Type() )
+ {
+ case PCB_LINE_T:
+ itemplotter.PlotDrawSegment( (DRAWSEGMENT*) PtStruct );
+ break;
+
+ case PCB_TEXT_T:
+ itemplotter.PlotTextePcb( (TEXTE_PCB*) PtStruct );
+ break;
+
+ case PCB_DIMENSION_T:
+ case PCB_TARGET_T:
+ case PCB_MARKER_T: // do not draw
+ default:
+ break;
+ }
+ }
+
+ int x, y;
+ int plotX, plotY, TextWidth;
+ int intervalle = 0;
+ char line[1024];
+ wxString msg;
+ int textmarginaftersymbol = KiROUND( 2 * IU_PER_MM );
+
+ // Set Drill Symbols width
+ plotter->SetDefaultLineWidth( 0.2 * IU_PER_MM / scale );
+ plotter->SetCurrentLineWidth( -1 );
+
+ // Plot board outlines and drill map
+ PlotDrillMarks( plotter );
+
+ // Print a list of symbols used.
+ int charSize = 3 * IU_PER_MM; // text size in IUs
+ double charScale = 1.0 / scale; // real scale will be 1/scale,
+ // because the global plot scale is scale
+ TextWidth = KiROUND( (charSize * charScale) / 10.0 ); // Set text width (thickness)
+ intervalle = KiROUND( charSize * charScale ) + TextWidth;
+
+ // Trace information.
+ plotX = KiROUND( bbbox.GetX() + textmarginaftersymbol * charScale );
+ plotY = bbbox.GetBottom() + intervalle;
+
+ // Plot title "Info"
+ wxString Text = wxT( "Drill Map:" );
+ plotter->Text( wxPoint( plotX, plotY ), UNSPECIFIED_COLOR, Text, 0,
+ wxSize( KiROUND( charSize * charScale ),
+ KiROUND( charSize * charScale ) ),
+ GR_TEXT_HJUSTIFY_LEFT, GR_TEXT_VJUSTIFY_CENTER,
+ TextWidth, false, false );
+
+ for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
+ {
+ DRILL_TOOL& tool = m_toolListBuffer[ii];
+
+ if( tool.m_TotalCount == 0 )
+ continue;
+
+ plotY += intervalle;
+
+ int plot_diam = KiROUND( tool.m_Diameter );
+ x = KiROUND( plotX - textmarginaftersymbol * charScale - plot_diam / 2.0 );
+ y = KiROUND( plotY + charSize * charScale );
+ plotter->Marker( wxPoint( x, y ), plot_diam, ii );
+
+ // List the diameter of each drill in mm and inches.
+ sprintf( line, "%2.2fmm / %2.3f\" ",
+ diameter_in_mm( tool.m_Diameter ),
+ diameter_in_inches( tool.m_Diameter ) );
+
+ msg = FROM_UTF8( line );
+
+ // Now list how many holes and ovals are associated with each drill.
+ if( ( tool.m_TotalCount == 1 )
+ && ( tool.m_OvalCount == 0 ) )
+ sprintf( line, "(1 hole)" );
+ else if( tool.m_TotalCount == 1 ) // && ( toolm_OvalCount == 1 )
+ sprintf( line, "(1 slot)" );
+ else if( tool.m_OvalCount == 0 )
+ sprintf( line, "(%d holes)", tool.m_TotalCount );
+ else if( tool.m_OvalCount == 1 )
+ sprintf( line, "(%d holes + 1 slot)", tool.m_TotalCount - 1 );
+ else // if ( toolm_OvalCount > 1 )
+ sprintf( line, "(%d holes + %d slots)",
+ tool.m_TotalCount - tool.m_OvalCount,
+ tool.m_OvalCount );
+
+ msg += FROM_UTF8( line );
+
+ if( tool.m_Hole_NotPlated )
+ msg += wxT( " (not plated)" );
+
+ plotter->Text( wxPoint( plotX, y ), UNSPECIFIED_COLOR, msg, 0,
+ wxSize( KiROUND( charSize * charScale ),
+ KiROUND( charSize * charScale ) ),
+ GR_TEXT_HJUSTIFY_LEFT, GR_TEXT_VJUSTIFY_CENTER,
+ TextWidth, false, false );
+
+ intervalle = KiROUND( ( ( charSize * charScale ) + TextWidth ) * 1.2 );
+
+ if( intervalle < ( plot_diam + ( 1 * IU_PER_MM / scale ) + TextWidth ) )
+ intervalle = plot_diam + ( 1 * IU_PER_MM / scale ) + TextWidth;
+ }
+
+ plotter->EndPlot();
+ delete plotter;
+
+ return true;
+}
+
+
+bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
+{
+ FILE_OUTPUTFORMATTER out( aFullFileName );
+
+ static const char separator[] =
+ " =============================================================\n";
+
+ wxASSERT( m_pcb );
+
+ unsigned totalHoleCount;
+ wxString brdFilename = m_pcb->GetFileName();
+
+ std::vector<LAYER_PAIR> hole_sets = getUniqueLayerPairs();
+
+ out.Print( 0, "Drill report for %s\n", TO_UTF8( brdFilename ) );
+ out.Print( 0, "Created on %s\n\n", TO_UTF8( DateAndTime() ) );
+
+ // Output the cu layer stackup, so layer name references make sense.
+ out.Print( 0, "Copper Layer Stackup:\n" );
+ out.Print( 0, separator );
+
+ LSET cu = m_pcb->GetEnabledLayers() & LSET::AllCuMask();
+
+ int conventional_layer_num = 1;
+ for( LSEQ seq = cu.Seq(); seq; ++seq, ++conventional_layer_num )
+ {
+ out.Print( 0, " L%-2d: %-25s %s\n",
+ conventional_layer_num,
+ TO_UTF8( m_pcb->GetLayerName( *seq ) ),
+ layerName( *seq ).c_str() // generic layer name
+ );
+ }
+
+ out.Print( 0, "\n\n" );
+
+ /* output hole lists:
+ * 1 - through holes
+ * 2 - for partial holes only: by layer starting and ending pair
+ * 3 - Non Plated through holes
+ */
+
+ bool buildNPTHlist = false;
+
+ // in this loop are plated only:
+ for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
+ {
+ LAYER_PAIR pair = hole_sets[pair_ndx];
+
+ BuildHolesList( pair, buildNPTHlist );
+
+ if( pair == LAYER_PAIR( F_Cu, B_Cu ) )
+ {
+ out.Print( 0, "Drill file '%s' contains\n",
+ TO_UTF8( drillFileName( pair, false ) ) );
+
+ out.Print( 0, " plated through holes:\n" );
+ out.Print( 0, separator );
+ totalHoleCount = printToolSummary( out, false );
+ out.Print( 0, " Total plated holes count %u\n", totalHoleCount );
+ }
+ else // blind/buried
+ {
+ out.Print( 0, "Drill file '%s' contains\n",
+ TO_UTF8( drillFileName( pair, false ) ) );
+
+ out.Print( 0, " holes connecting layer pair: '%s and %s' (%s vias):\n",
+ TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.first ) ) ),
+ TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.second ) ) ),
+ pair.first == F_Cu || pair.second == B_Cu ? "blind" : "buried"
+ );
+
+ out.Print( 0, separator );
+ totalHoleCount = printToolSummary( out, false );
+ out.Print( 0, " Total plated holes count %u\n", totalHoleCount );
+ }
+
+ out.Print( 0, "\n\n" );
+ }
+
+ // NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged,
+ // or only the NPTH list (which never has vias)
+ if( !m_merge_PTH_NPTH )
+ buildNPTHlist = true;
+
+ BuildHolesList( LAYER_PAIR( F_Cu, B_Cu ), buildNPTHlist );
+
+ // nothing wrong with an empty NPTH file in report.
+ if( m_merge_PTH_NPTH )
+ out.Print( 0, "Not plated through holes are merged with plated holes\n" );
+ else
+ out.Print( 0, "Drill file '%s' contains\n",
+ TO_UTF8( drillFileName( LAYER_PAIR( F_Cu, B_Cu ), true ) ) );
+
+ out.Print( 0, " unplated through holes:\n" );
+ out.Print( 0, separator );
+ totalHoleCount = printToolSummary( out, true );
+ out.Print( 0, " Total unplated holes count %u\n", totalHoleCount );
+
+ return true;
+}
+
+
+bool EXCELLON_WRITER::PlotDrillMarks( PLOTTER* aPlotter )
+{
+ // Plot the drill map:
+ wxPoint pos;
+
+ for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
+ {
+ const HOLE_INFO& hole = m_holeListBuffer[ii];
+ pos = hole.m_Hole_Pos;
+
+ // Always plot the drill symbol (for slots identifies the needed cutter!
+ aPlotter->Marker( pos, hole.m_Hole_Diameter, hole.m_Tool_Reference - 1 );
+
+ if( hole.m_Hole_Shape != 0 )
+ {
+ wxSize oblong_size = hole.m_Hole_Size;
+ aPlotter->FlashPadOval( pos, oblong_size, hole.m_Hole_Orient, SKETCH );
+ }
+ }
+
+ return true;
+}
+
+
+unsigned EXCELLON_WRITER::printToolSummary( OUTPUTFORMATTER& out, bool aSummaryNPTH ) const
+{
+ unsigned totalHoleCount = 0;
+
+ for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
+ {
+ const DRILL_TOOL& tool = m_toolListBuffer[ii];
+
+ if( aSummaryNPTH && !tool.m_Hole_NotPlated )
+ continue;
+
+ if( !aSummaryNPTH && tool.m_Hole_NotPlated )
+ continue;
+
+ // List the tool number assigned to each drill,
+ // in mm then in inches.
+ int tool_number = ii+1;
+ out.Print( 0, " T%d %2.2fmm %2.3f\" ", tool_number,
+ diameter_in_mm( tool.m_Diameter ),
+ diameter_in_inches( tool.m_Diameter ) );
+
+ // Now list how many holes and ovals are associated with each drill.
+ if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
+ out.Print( 0, "(1 hole)\n" );
+ else if( tool.m_TotalCount == 1 )
+ out.Print( 0, "(1 hole) (with 1 slot)\n" );
+ else if( tool.m_OvalCount == 0 )
+ out.Print( 0, "(%d holes)\n", tool.m_TotalCount );
+ else if( tool.m_OvalCount == 1 )
+ out.Print( 0, "(%d holes) (with 1 slot)\n", tool.m_TotalCount );
+ else // tool.m_OvalCount > 1
+ out.Print( 0, "(%d holes) (with %d slots)\n",
+ tool.m_TotalCount, tool.m_OvalCount );
+
+ totalHoleCount += tool.m_TotalCount;
+ }
+
+ out.Print( 0, "\n" );
+
+ return totalHoleCount;
+}