diff --git a/pcbnew/exporters/gendrill_Excellon_writer.cpp b/pcbnew/exporters/gendrill_Excellon_writer.cpp index 539d347212..6be76b9396 100644 --- a/pcbnew/exporters/gendrill_Excellon_writer.cpp +++ b/pcbnew/exporters/gendrill_Excellon_writer.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2018 Jean_Pierre Charras * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2021 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 @@ -51,11 +51,6 @@ #include #include -// Comment/uncomment this to write or not a comment -// in drill file when PTH and NPTH are merged to flag -// tools used for PTH and tools used for NPTH -// #define WRITE_PTH_NPTH_COMMENT - // Oblong holes can be drilled by a "canned slot" command (G85) or a routing command // a linear routing command (G01) is perhaps more usual for drill files // @@ -141,6 +136,47 @@ void EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, } +void EXCELLON_WRITER::writeHoleAttribute( HOLE_ATTRIBUTE aAttribute, bool aToolAttr ) +{ + // Hole attributes are comments (lines starting by ';') in the drill files + // For tools (file header), they are similar to X2 apertures attributes. + // for attributes added in coordinate list, they are just comments. + if( !m_minimalHeader ) + { + switch( aAttribute ) + { + case HOLE_ATTRIBUTE::HOLE_VIA: + if( aToolAttr ) + fprintf( m_file, "; #@! TA.AperFunction,ViaDrill\n" ); + else + fprintf( m_file, "; via hole\n" ); + break; + + case HOLE_ATTRIBUTE::HOLE_PAD: + if( aToolAttr ) + fprintf( m_file, "; #@! TA.AperFunction,ComponentDrill\n" ); + else + fprintf( m_file, "; pad hole\n" ); + break; + + case HOLE_ATTRIBUTE::HOLE_MECHANICAL: + if( aToolAttr ) + fprintf( m_file, "; #@! TA.AperFunction,MechanicalDrill\n" ); + else + fprintf( m_file, "; mechanical\n" ); + break; + + case HOLE_ATTRIBUTE::HOLE_UNKNOWN: + if( aToolAttr ) + fprintf( m_file, "; #@! TD\n" ); + else + fprintf( m_file, "; unknown\n" ); + break; + } + } +} + + int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list ) { @@ -157,35 +193,19 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, holes_count = 0; -#ifdef WRITE_PTH_NPTH_COMMENT - // if PTH_ and NPTH are merged write a comment in drill file at the - // beginning of NPTH section - bool writePTHcomment = m_merge_PTH_NPTH; - bool writeNPTHcomment = m_merge_PTH_NPTH; -#endif - /* Write the tool list */ for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ ) { DRILL_TOOL& tool_descr = m_toolListBuffer[ii]; -#ifdef WRITE_PTH_NPTH_COMMENT - if( writePTHcomment && !tool_descr.m_Hole_NotPlated ) - { - writePTHcomment = false; - fprintf( m_file, ";TYPE=PLATED\n" ); - } - - if( writeNPTHcomment && tool_descr.m_Hole_NotPlated ) - { - writeNPTHcomment = false; - fprintf( m_file, ";TYPE=NON_PLATED\n" ); - } +#if USE_ATTRIB_FOR_HOLES + writeHoleAttribute( tool_descr.m_HoleAttribute, true ); #endif - - if( m_unitsMetric ) // if units are mm, the resolution is 0.001 mm (3 digits in mantissa) + // if units are mm, the resolution is 0.001 mm (3 digits in mantissa) + // if units are inches, the resolution is 0.1 mil (4 digits in mantissa) + if( m_unitsMetric ) fprintf( m_file, "T%dC%.3f\n", ii + 1, tool_descr.m_Diameter * m_conversionUnits ); - else // if units are inches, the resolution is 0.1 mil (4 digits in mantissa) + else fprintf( m_file, "T%dC%.4f\n", ii + 1, tool_descr.m_Diameter * m_conversionUnits ); } @@ -193,10 +213,12 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, fputs( "G90\n", m_file ); // Absolute mode fputs( "G05\n", m_file ); // Drill mode - /* Read the hole file and generate lines for normal holes (oblong + /* Read the hole list and generate data for normal holes (oblong * holes will be created later) */ int tool_reference = -2; + HOLE_ATTRIBUTE last_item_type = HOLE_ATTRIBUTE::HOLE_UNKNOWN; + for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { HOLE_INFO& hole_descr = m_holeListBuffer[ii]; @@ -210,6 +232,13 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, fprintf( m_file, "T%d\n", tool_reference ); } + HOLE_ATTRIBUTE curr_item_type = hole_descr.m_HoleAttribute; + + if( curr_item_type != last_item_type ) + writeHoleAttribute( curr_item_type, false ); + + last_item_type = curr_item_type; + x0 = hole_descr.m_Hole_Pos.x - m_offset.x; y0 = hole_descr.m_Hole_Pos.y - m_offset.y; @@ -224,10 +253,12 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, holes_count++; } - /* Read the hole file and generate lines for normal holes (oblong holes - * will be created later) */ + /* Read the hole list and generate data for oblong holes + */ tool_reference = -2; // set to a value not used for // m_holeListBuffer[ii].m_Tool_Reference + last_item_type = HOLE_ATTRIBUTE::HOLE_UNKNOWN; + for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { HOLE_INFO& hole_descr = m_holeListBuffer[ii]; @@ -241,6 +272,13 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, fprintf( m_file, "T%d\n", tool_reference ); } + HOLE_ATTRIBUTE curr_item_type = hole_descr.m_HoleAttribute; + + if( curr_item_type != last_item_type ) + writeHoleAttribute( curr_item_type, false ); + + last_item_type = curr_item_type; + diam = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y ); if( diam == 0 ) diff --git a/pcbnew/exporters/gendrill_Excellon_writer.h b/pcbnew/exporters/gendrill_Excellon_writer.h index a0a85fb2ea..ea683a2f5a 100644 --- a/pcbnew/exporters/gendrill_Excellon_writer.h +++ b/pcbnew/exporters/gendrill_Excellon_writer.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2017 Jean_Pierre Charras - * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-2021 KiCad Developers, see AUTHOR.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 @@ -37,6 +37,7 @@ class BOARD; class PLOTTER; class OUTPUTFORMATTER; + /** * EXCELLON_WRITER is a class mainly used to create Excellon drill files * However, this class is also used to create drill maps and drill report @@ -127,7 +128,7 @@ private: int createDrillFile( FILE * aFile, DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list ); - /* Print the DRILL file header. The full header is somethink like: + /** Print the DRILL file header. The full header is somethink like: * M48 * ;DRILL file {PCBNEW (2007-11-29-b)} date 17/1/2008-21:02:35 * ;FORMAT={ / absolute / / } @@ -141,11 +142,20 @@ private: void writeEXCELLONEndOfFile(); - /* Created a line like: + /** Created a line like: * X48000Y19500 * According to the selected format */ void writeCoordinates( char* aLine, double aCoordX, double aCoordY ); + + /** + * write a comment string giving the hole attribute like + * "; #@! TO.P,viatype\n" + * @param aAttribute is the hole attribute + * @param aToolAttr = true if the comment is for a tool, + * false for a hole in coord list + */ + void writeHoleAttribute( HOLE_ATTRIBUTE aAttribute, bool aToolAttr ); }; #endif // #ifndef _GENDRILL_EXCELLON_WRITER_ diff --git a/pcbnew/exporters/gendrill_file_writer_base.cpp b/pcbnew/exporters/gendrill_file_writer_base.cpp index bd1da98ffe..264834c263 100644 --- a/pcbnew/exporters/gendrill_file_writer_base.cpp +++ b/pcbnew/exporters/gendrill_file_writer_base.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2017 Jean_Pierre Charras * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2021 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 @@ -33,10 +33,13 @@ /* Helper function for sorting hole list. - * Compare function used for sorting holes type type (plated then not plated) - * then by increasing diameter value and X then Y position + * Compare function used for sorting holes type type: + * plated then not plated + * then by increasing diameter value + * then by attribute type (vias, pad, mechanical) + * then by X then Y position */ -static bool CmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b ) +static bool cmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b ) { if( a.m_Hole_NotPlated != b.m_Hole_NotPlated ) return b.m_Hole_NotPlated; @@ -44,7 +47,12 @@ static bool CmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b ) if( a.m_Hole_Diameter != b.m_Hole_Diameter ) return a.m_Hole_Diameter < b.m_Hole_Diameter; - // At this point (same diameter), sort by X then Y position. + // At this point (same diameter, same plated type), group by attribute + // type (via, pad, mechanical, although currently only not plated pads are mechanical) + if( a.m_HoleAttribute != b.m_HoleAttribute ) + return a.m_HoleAttribute < b.m_HoleAttribute; + + // At this point (same diameter, same type), sort by X then Y position. // This is optimal for drilling and make the file reproducible as long as holes // have not changed, even if the data order has changed. if( a.m_Hole_Pos.x != b.m_Hole_Pos.x ) @@ -79,6 +87,7 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, continue; new_hole.m_ItemParent = via; + new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_VIA; new_hole.m_Tool_Reference = -1; // Flag value for Not initialized new_hole.m_Hole_Orient = 0; new_hole.m_Hole_Diameter = hole_sz; @@ -122,6 +131,9 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, new_hole.m_ItemParent = pad; new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_NPTH); + new_hole.m_HoleAttribute = new_hole.m_Hole_NotPlated + ? HOLE_ATTRIBUTE::HOLE_MECHANICAL + : HOLE_ATTRIBUTE::HOLE_PAD; new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Shape = 0; // hole shape: round @@ -141,26 +153,33 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, } // Sort holes per increasing diameter value (and for each dimater, by position) - sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSorting ); + sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), cmpHoleSorting ); // build the tool list int last_hole = -1; // Set to not initialized (this is a value not used // for m_holeListBuffer[ii].m_Hole_Diameter) bool last_notplated_opt = false; + HOLE_ATTRIBUTE last_attribute = HOLE_ATTRIBUTE::HOLE_UNKNOWN; DRILL_TOOL new_tool( 0, false ); unsigned jj; for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { - if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole || - m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt ) + if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole + || m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt +#if USE_ATTRIB_FOR_HOLES + || m_holeListBuffer[ii].m_HoleAttribute != last_attribute +#endif + ) { new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter; new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated; + new_tool.m_HoleAttribute = m_holeListBuffer[ii].m_HoleAttribute; m_toolListBuffer.push_back( new_tool ); last_hole = new_tool.m_Diameter; last_notplated_opt = new_tool.m_Hole_NotPlated; + last_attribute = new_tool.m_HoleAttribute; } jj = m_toolListBuffer.size(); diff --git a/pcbnew/exporters/gendrill_file_writer_base.h b/pcbnew/exporters/gendrill_file_writer_base.h index 5190d95b86..ffd8d3ff10 100644 --- a/pcbnew/exporters/gendrill_file_writer_base.h +++ b/pcbnew/exporters/gendrill_file_writer_base.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2017 Jean_Pierre Charras - * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-2021 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 @@ -29,19 +29,37 @@ #ifndef GENDRILL_FILE_WRITER_BASE_H #define GENDRILL_FILE_WRITER_BASE_H +// holes can have an attribute in Excellon drill files, similar to attributes +// in Gerber X2 format +// They are only comments for a better identification of holes (vias, pads...) +// Set to 1 to add these comments and 0 to not use these comments +#define USE_ATTRIB_FOR_HOLES 1 + #include class BOARD_ITEM; +// hole attribute, mainly to identify vias and pads and add this info as comment +// in NC drill files +enum class HOLE_ATTRIBUTE +{ + HOLE_UNKNOWN, // uninitialized type + HOLE_VIA, // a via hole + HOLE_PAD, // a plated pad hole + HOLE_MECHANICAL // a mechanical (not plated) pad hole +}; + // the DRILL_TOOL class handles tools used in the excellon drill file: class DRILL_TOOL { public: - int m_Diameter; // the diameter of the used tool (for oblong, the smaller size) - int m_TotalCount; // how many times it is used (round and oblong) - int m_OvalCount; // oblong count - bool m_Hole_NotPlated; // Is the hole plated or not plated + int m_Diameter; // the diameter of the used tool + // (for oblong, the smaller size) + int m_TotalCount; // how many times it is used (round and oblong) + int m_OvalCount; // oblong count + bool m_Hole_NotPlated; // Is the hole plated or not plated + HOLE_ATTRIBUTE m_HoleAttribute; // Attribute (used in Excellon drill file) public: DRILL_TOOL( int aDiameter, bool a_NotPlated ) @@ -50,6 +68,7 @@ public: m_OvalCount = 0; m_Diameter = aDiameter; m_Hole_NotPlated = a_NotPlated; + m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_UNKNOWN; } }; @@ -60,6 +79,7 @@ public: * Not plated holes are always through holes, and must be output on a specific drill file * because they are drilled after the Pcb process is finished. */ + class HOLE_INFO { public: @@ -74,6 +94,8 @@ public: PCB_LAYER_ID m_Hole_Top_Layer; // hole starting layer (usually front layer): // m_Hole_Top_Layer < m_Hole_Bottom_Layer bool m_Hole_NotPlated; // hole not plated. Must be in a specific drill file or section + HOLE_ATTRIBUTE m_HoleAttribute; // Attribute, used in Excellon drill file and to sort holes + // by type. public: HOLE_INFO() @@ -86,6 +108,7 @@ public: m_Hole_Shape = 0; m_Hole_Bottom_Layer = B_Cu; m_Hole_Top_Layer = F_Cu; + m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_UNKNOWN; } };