KiCad PCB EDA Suite
gendrill_file_writer_base.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5  * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 #include <fctsys.h>
26 
27 #include <class_board.h>
28 #include <class_module.h>
29 #include <collectors.h>
30 #include <reporter.h>
31 
33 
34 
35 /* Helper function for sorting hole list.
36  * Compare function used for sorting holes type type (plated then not plated)
37  * then by increasing diameter value and X value
38  */
39 static bool CmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b )
40 {
42  return b.m_Hole_NotPlated;
43 
44  if( a.m_Hole_Diameter != b.m_Hole_Diameter )
45  return a.m_Hole_Diameter < b.m_Hole_Diameter;
46 
47  // group by components when possible
48  const D_PAD* pada = dyn_cast<const D_PAD*>( a.m_ItemParent );
49  const D_PAD* padb = dyn_cast<const D_PAD*>( b.m_ItemParent );
50 
51  if( pada && padb )
52  {
53  // cmp == 0 means the pads have the same parent, therfore the same reference
54  int cmp = pada->GetParent() - padb->GetParent();
55 
56  if( cmp )
57  return cmp < 0;
58  }
59  else if( pada || padb ) // in this case, other item is a via. Sort via first
60  {
61  return padb != nullptr;
62  }
63 
64  // At this point, sort by position, as last sort criteria
65  if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
66  return a.m_Hole_Pos.x < b.m_Hole_Pos.x;
67 
68  return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
69 }
70 
71 
73  bool aGenerateNPTH_list )
74 {
75  HOLE_INFO new_hole;
76 
77  m_holeListBuffer.clear();
78  m_toolListBuffer.clear();
79 
80  wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller
81 
82  // build hole list for vias
83  if( ! aGenerateNPTH_list ) // vias are always plated !
84  {
85  for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) )
86  {
87  int hole_sz = via->GetDrillValue();
88 
89  if( hole_sz == 0 ) // Should not occur.
90  continue;
91 
92  new_hole.m_ItemParent = via;
93  new_hole.m_Tool_Reference = -1; // Flag value for Not initialized
94  new_hole.m_Hole_Orient = 0;
95  new_hole.m_Hole_Diameter = hole_sz;
96  new_hole.m_Hole_NotPlated = false;
97  new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
98 
99  new_hole.m_Hole_Shape = 0; // hole shape: round
100  new_hole.m_Hole_Pos = via->GetStart();
101 
102  via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );
103 
104  // LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
105  // Remember: top layer = 0 and bottom layer = 31 for through hole vias
106  // Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
107  if( new_hole.m_Hole_Top_Layer != aLayerPair.first ||
108  new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
109  continue;
110 
111  m_holeListBuffer.push_back( new_hole );
112  }
113  }
114 
115  if( aLayerPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
116  {
117  // add holes for thru hole pads
118  for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() )
119  {
120  for( auto& pad : module->Pads() )
121  {
122  if( !m_merge_PTH_NPTH )
123  {
124  if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
125  continue;
126 
127  if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
128  continue;
129  }
130 
131  if( pad->GetDrillSize().x == 0 )
132  continue;
133 
134  new_hole.m_ItemParent = pad;
135  new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
136  new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
137  new_hole.m_Hole_Orient = pad->GetOrientation();
138  new_hole.m_Hole_Shape = 0; // hole shape: round
139  new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
140  new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
141 
142  if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
143  new_hole.m_Hole_Shape = 1; // oval flag set
144 
145  new_hole.m_Hole_Size = pad->GetDrillSize();
146  new_hole.m_Hole_Pos = pad->GetPosition(); // hole position
147  new_hole.m_Hole_Bottom_Layer = B_Cu;
148  new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes
149  m_holeListBuffer.push_back( new_hole );
150  }
151  }
152  }
153 
154  // Sort holes per increasing diameter value
155  sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSorting );
156 
157  // build the tool list
158  int last_hole = -1; // Set to not initialized (this is a value not used
159  // for m_holeListBuffer[ii].m_Hole_Diameter)
160  bool last_notplated_opt = false;
161 
162  DRILL_TOOL new_tool( 0, false );
163  unsigned jj;
164 
165  for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
166  {
167  if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole ||
168  m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt )
169  {
170  new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
171  new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
172  m_toolListBuffer.push_back( new_tool );
173  last_hole = new_tool.m_Diameter;
174  last_notplated_opt = new_tool.m_Hole_NotPlated;
175  }
176 
177  jj = m_toolListBuffer.size();
178 
179  if( jj == 0 )
180  continue; // Should not occurs
181 
182  m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
183 
184  m_toolListBuffer.back().m_TotalCount++;
185 
186  if( m_holeListBuffer[ii].m_Hole_Shape )
187  m_toolListBuffer.back().m_OvalCount++;
188  }
189 }
190 
191 
192 std::vector<DRILL_LAYER_PAIR> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
193 {
194  wxASSERT( m_pcb );
195 
196  static const KICAD_T interesting_stuff_to_collect[] = {
197  PCB_VIA_T,
198  EOT
199  };
200 
201  PCB_TYPE_COLLECTOR vias;
202 
203  vias.Collect( m_pcb, interesting_stuff_to_collect );
204 
205  std::set< DRILL_LAYER_PAIR > unique;
206 
207  DRILL_LAYER_PAIR layer_pair;
208 
209  for( int i = 0; i < vias.GetCount(); ++i )
210  {
211  VIA* v = (VIA*) vias[i];
212 
213  v->LayerPair( &layer_pair.first, &layer_pair.second );
214 
215  // only make note of blind buried.
216  // thru hole is placed unconditionally as first in fetched list.
217  if( layer_pair != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
218  {
219  unique.insert( layer_pair );
220  }
221  }
222 
223  std::vector<DRILL_LAYER_PAIR> ret;
224 
225  ret.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) ); // always first in returned list
226 
227  for( std::set< DRILL_LAYER_PAIR >::const_iterator it = unique.begin(); it != unique.end(); ++it )
228  ret.push_back( *it );
229 
230  return ret;
231 }
232 
233 
234 const std::string GENDRILL_WRITER_BASE::layerName( PCB_LAYER_ID aLayer ) const
235 {
236  // Generic names here.
237  switch( aLayer )
238  {
239  case F_Cu:
240  return "front";
241  case B_Cu:
242  return "back";
243  default:
244  return StrPrintf( "in%d", aLayer );
245  }
246 }
247 
248 
250 {
251  std::string ret = layerName( aPair.first );
252  ret += '-';
253  ret += layerName( aPair.second );
254 
255  return ret;
256 }
257 
258 
260  bool aMerge_PTH_NPTH ) const
261 {
262  wxASSERT( m_pcb );
263 
264  wxString extend;
265 
266  if( aNPTH )
267  extend = "-NPTH";
268  else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
269  {
270  if( !aMerge_PTH_NPTH )
271  extend = "-PTH";
272  // if merged, extend with nothing
273  }
274  else
275  {
276  extend += '-';
277  extend += layerPairName( aPair );
278  }
279 
280  wxFileName fn = m_pcb->GetFileName();
281 
282  fn.SetName( fn.GetName() + extend );
283  fn.SetExt( m_drillFileExtension );
284 
285  wxString ret = fn.GetFullName();
286 
287  return ret;
288 }
289 
290 void GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory,
291  REPORTER * aReporter )
292 {
293  wxFileName fn;
294  wxString msg;
295 
296  std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
297 
298  // append a pair representing the NPTH set of holes, for separate drill files.
299  if( !m_merge_PTH_NPTH )
300  hole_sets.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) );
301 
302  for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
303  it != hole_sets.end(); ++it )
304  {
305  DRILL_LAYER_PAIR pair = *it;
306  // For separate drill files, the last layer pair is the NPTH drill file.
307  bool doing_npth = m_merge_PTH_NPTH ? false : ( it == hole_sets.end() - 1 );
308 
309  buildHolesList( pair, doing_npth );
310 
311  // The file is created if it has holes, or if it is the non plated drill file
312  // to be sure the NPTH file is up to date in separate files mode.
313  if( getHolesCount() > 0 || doing_npth )
314  {
315  fn = getDrillFileName( pair, doing_npth, m_merge_PTH_NPTH );
316  fn.SetPath( aPlotDirectory );
317 
318  fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
319  wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
320  fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
321 
322  bool success = genDrillMapFile( fullfilename, m_mapFileFmt );
323 
324  if( ! success )
325  {
326  if( aReporter )
327  {
328  msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullfilename ) );
329  aReporter->Report( msg );
330  }
331 
332  return;
333  }
334  else
335  {
336  if( aReporter )
337  {
338  msg.Printf( _( "Create file %s\n" ), GetChars( fullfilename ) );
339  aReporter->Report( msg );
340  }
341  }
342  }
343  }
344 }
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:114
BOARD_ITEM_CONTAINER * GetParent() const
BOARD_ITEM * m_ItemParent
bool genDrillMapFile(const wxString &aFullFileName, PlotFormat aFormat)
Function GenDrillMapFile Plot a map of drill marks for holes.
static bool CmpHoleSorting(const HOLE_INFO &a, const HOLE_INFO &b)
int StrPrintf(std::string *aResult, const char *aFormat,...)
Function StrPrintf is like sprintf() but the output is appended to a std::string instead of to a char...
Definition: richio.cpp:74
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:65
Class BOARD to handle a board.
virtual const wxString getDrillFileName(DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH) const
wxString GetDefaultPlotExtension(PlotFormat aFormat)
Returns the default plot extension for a format.
MODULE * Next() const
Definition: class_module.h:123
MODULE * GetParent() const
Definition: class_pad.h:162
const std::string layerName(PCB_LAYER_ID aLayer) const
minor helper function.
Class REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:61
void CreateMapFilesSet(const wxString &aPlotDirectory, REPORTER *aReporter=NULL)
Function CreateMapFilesSet Creates the full set of map files for the board, in PS, PDF ...
Casted dyn_cast(From aObject)
Function dyn_cast()
Definition: typeinfo.h:61
search types array terminator (End Of Types)
Definition: typeinfo.h:82
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
std::vector< DRILL_TOOL > m_toolListBuffer
PCB_LAYER_ID
A quick note on layer IDs:
std::vector< DRILL_LAYER_PAIR > getUniqueLayerPairs() const
Get unique layer pairs by examining the micro and blind_buried vias.
const wxString & GetFileName() const
Definition: class_board.h:237
helper classes to handle hole info for drill files generators.
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > DRILL_LAYER_PAIR
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Collect BOARD_ITEM objects using this class&#39;s Inspector method, which does the collection.
Definition: collectors.cpp:522
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
void buildHolesList(DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list)
Function BuildHolesList Create the list of holes and tools for a given board The list is sorted by in...
PCB_LAYER_ID m_Hole_Bottom_Layer
PCB_LAYER_ID m_Hole_Top_Layer
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
DLIST< MODULE > m_Modules
Definition: class_board.h:248
size_t i
Definition: json11.cpp:597
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
DLIST< TRACK > m_Track
Definition: class_board.h:249
Module description (excepted pads)
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:627
const std::string layerPairName(DRILL_LAYER_PAIR aPair) const
minor helper function.
std::vector< HOLE_INFO > m_holeListBuffer
#define min(a, b)
Definition: auxiliary.h:85
VIA * GetFirstVia(TRACK *aTrk, const TRACK *aStopPoint=NULL)
Scan a track list for the first VIA o NULL if not found (or NULL passed)
Definition: class_track.h:510