KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_drill_chart.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <set>
21
22#include <pcb_drill_chart.h>
23
24#include <board.h>
29#include <base_units.h>
30#include <string_utils.h>
31#include <eda_text.h>
32#include <i18n_utility.h>
34#include <widgets/msgpanel.h>
35#include <api/api_enums.h>
37#include <api/api_utils.h>
38#include <api/api_pcb_utils.h>
39#include <api/board/board_types.pb.h>
40
41
43 m_Profile( aBoard.GetDesignSettings().GetDrillSymbolProfile() )
44{
45}
46
47
52
53
62
63
65 PCB_GENERATED_TABLE( aOther ),
66 m_filter( aOther.m_filter ),
67 m_showTotals( aOther.m_showTotals ),
68 m_rowShapes( aOther.m_rowShapes ),
71{
72 // PCB_TABLE's copy ctor propagates m_structType, so the clone reports PCB_DRILL_CHART_T
73 // and swapData's guard stays meaningful
74}
75
76
78{
79 m_columns = aTemplate.Columns();
80 m_units = aTemplate.GetUnits();
81 m_precision = aTemplate.GetPrecision();
82 m_showTotals = aTemplate.GetShowTotals();
83}
84
85
87{
88 wxCHECK_RET( aImage && aImage->Type() == Type(), wxT( "Cannot swap data with invalid chart." ) );
89
91
92 PCB_DRILL_CHART* other = static_cast<PCB_DRILL_CHART*>( aImage );
93
94 std::swap( m_filter, other->m_filter );
95 std::swap( m_showTotals, other->m_showTotals );
96 std::swap( m_builtGeneration, other->m_builtGeneration );
97 std::swap( m_rowShapes, other->m_rowShapes );
98 std::swap( m_symbolColumn, other->m_symbolColumn );
99}
100
101
103{
104 return static_cast<DRILL_CHART_COLUMN_ID>( m_columns[aCol].m_Id );
105}
106
107
108std::vector<DRILL_CHART_GROUP> PCB_DRILL_CHART::buildGroups( const BOARD& aBoard,
109 const DRILL_SYMBOL_PROFILE& aProfile ) const
110{
112 spec.m_Filter = m_filter;
113
114 // Every span on the board. What shares a row is the profile's grouping to decide
115 DRILL_CHART_MODEL model( aProfile );
116 model.Build( aBoard, EnumerateDrillSpans( aBoard ), spec );
117
118 return model.Groups();
119}
120
121
122bool PCB_DRILL_CHART::IsStale( const BOARD& aBoard ) const
123{
125}
126
127
128bool PCB_DRILL_CHART::IsDataRow( int aRow ) const
129{
130 if( !m_rowKeys.empty() )
131 return PCB_GENERATED_TABLE::IsDataRow( aRow );
132
133 // A chart written before the keys were recorded still has to answer this, and its rows are
134 // laid out the way RebuildCells lays them out
135 const int firstDataRow = 1;
136 const int lastDataRow = GetRowCount() - 1 - ( m_showTotals ? 1 : 0 );
137
138 return aRow >= firstDataRow && aRow <= lastDataRow;
139}
140
141
142wxString PCB_DRILL_CHART::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
143{
144 return wxString::Format( _( "Drill Chart (%d rows)" ), GetRowCount() );
145}
146
147
148void PCB_DRILL_CHART::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
149{
150 aList.emplace_back( _( "Drill Chart" ), wxEmptyString );
151 aList.emplace_back( _( "Rows" ), wxString::Format( wxT( "%d" ), GetRowCount() ) );
152 aList.emplace_back( _( "Layer" ), GetLayerName() );
153}
154
155
156double PCB_DRILL_CHART::Similarity( const BOARD_ITEM& aOther ) const
157{
158 if( aOther.Type() != Type() )
159 return 0.0;
160
161 return PCB_TABLE::Similarity( aOther );
162}
163
164
165bool PCB_DRILL_CHART::operator==( const BOARD_ITEM& aOther ) const
166{
167 if( aOther.Type() != Type() )
168 return false;
169
170 const PCB_DRILL_CHART& other = static_cast<const PCB_DRILL_CHART&>( aOther );
171
172 // Must cover everything swapData swaps. The git merge driver decides a change is a
173 // change from this, so an omitted member is a silently dropped edit
174 return m_filter == other.m_filter && m_showTotals == other.m_showTotals && m_rowShapes == other.m_rowShapes
175 && m_symbolColumn == other.m_symbolColumn && generatedEquals( other );
176}
177
178
179namespace
180{
181
182const wxString NO_VALUE( wxS( "\u2014" ) );
183
184
185wxString spanText( const BOARD& aBoard, const DRILL_CHART_GROUP& aGroup )
186{
187 // The board's own layer names, so a chart matches the stackup the fabricator was given
188 return wxString::Format( wxT( "%s / %s" ), aBoard.GetLayerName( aGroup.m_TopLayer ),
189 aBoard.GetLayerName( aGroup.m_BottomLayer ) );
190}
191
192
193wxString operationText( const DRILL_CHART_GROUP& aGroup )
194{
196 return wxT( "Backdrill" );
197
198 if( aGroup.m_IsSlot )
199 return wxT( "Slot" );
200
201 return wxT( "Drill" );
202}
203
204
205wxString protectionText( const DRILL_CHART_GROUP& aGroup )
206{
207 wxArrayString parts;
208
209 if( aGroup.m_Filled )
210 parts.Add( wxT( "filled" ) );
211
212 if( aGroup.m_Capped )
213 parts.Add( wxT( "capped" ) );
214
215 if( aGroup.m_TopPlugged || aGroup.m_BottomPlugged )
216 parts.Add( wxT( "plugged" ) );
217
218 if( aGroup.m_TopCovered || aGroup.m_BottomCovered )
219 parts.Add( wxT( "covered" ) );
220
221 if( aGroup.m_TopTented || aGroup.m_BottomTented )
222 parts.Add( wxT( "tented" ) );
223
224 return parts.IsEmpty() ? NO_VALUE : wxJoin( parts, ',' );
225}
226
227
228wxString symbolText( const DRILL_CHART_GROUP& aGroup, GENERATED_TABLE_UNITS aUnits, int aPrecision )
229{
230 switch( aGroup.m_Symbol.m_MarkMode )
231 {
233 return aGroup.m_Symbol.m_Letter;
234
236 return FormatGeneratedTableLength( aGroup.m_Diameter, aUnits, aPrecision );
237
239 default:
240 // The shape itself is drawn by the painter. The cell carries no text
241 return wxEmptyString;
242 }
243}
244
245} // namespace
246
247
248std::unique_ptr<GENERATED_TABLE_CONTENT> PCB_DRILL_CHART::generate( const BOARD& aBoard,
249 GENERATED_TABLE_REFRESH* aRefresh ) const
250{
251 // A refresh can carry grouping the board has not been given yet, as the properties dialog does
252 std::vector<DRILL_CHART_GROUP> groups =
253 buildGroups( aBoard, aRefresh ? aRefresh->Pending<DRILL_PROFILE_PENDING>().m_Profile
255
256 if( aRefresh )
257 {
258 // The refresh's copy, so a cancelled placement leaves nothing behind and a batch
259 // rebuild accumulates instead of keeping only the last chart's
261 }
262 else
263 {
264 // Whole board, not this chart's filtered subset, or a filtered chart prints a
265 // different symbol from the map for the same hole
266 const std::map<std::string, DRILL_SYMBOL_ASSIGNMENT> resolved =
267 ResolveDrillSymbols( aBoard );
268
269 for( DRILL_CHART_GROUP& group : groups )
270 {
271 const auto it = resolved.find( group.m_SymbolKey );
272
273 if( it != resolved.end() )
274 group.m_Symbol = it->second;
275 }
276 }
277
278 auto content = std::make_unique<DRILL_CHART_CONTENT>();
279 content->m_Rows.reserve( groups.size() );
280 content->m_RowShapes.reserve( groups.size() );
281
282 const int cols = static_cast<int>( m_columns.size() );
283
284 for( int col = 0; col < cols; ++col )
285 {
287 content->m_SymbolColumn = col;
288 }
289
290 for( const DRILL_CHART_GROUP& group : groups )
291 {
292 GENERATED_TABLE_ROW& row = content->m_Rows.emplace_back();
293 row.m_Key = group.m_Key;
294 row.m_Cells.reserve( cols );
295
296 if( group.m_Symbol.m_MarkMode == DRILL_MARK_MODE::SHAPE )
297 content->m_RowShapes.emplace_back( group.m_Symbol.m_ShapeIndex );
298 else
299 content->m_RowShapes.emplace_back();
300
301 for( int col = 0; col < cols; ++col )
302 {
303 wxString text;
304
305 switch( columnId( col ) )
306 {
308 text = symbolText( group, m_units, m_precision );
309 break;
310
313 break;
314
316 text = group.m_IsSlot
317 ? wxString::Format( wxT( "%s x %s" ),
319 m_precision ),
321 m_precision ) )
322 : NO_VALUE;
323 break;
324
326 text = group.m_NotPlated ? wxT( "No" ) : wxT( "Yes" );
327 break;
328
330 text = wxString::Format( wxT( "%d" ), group.m_OperationCount );
331 break;
332
334 text = wxString::Format( wxT( "%d" ), group.m_SiteCount );
335 break;
336
338 text = spanText( aBoard, group );
339 break;
340
342 text = operationText( group );
343 break;
344
346 text = protectionText( group );
347 break;
348
350 text = group.m_StubLength.has_value()
352 : NO_VALUE;
353 break;
354
356 {
357 // Depth over diameter, the number a fabricator uses to judge plating
358 // difficulty. Needs a real stackup, so it stays an em dash without one
359 const int depth = aBoard.GetStackupOrDefault().GetLayerDistance(
360 group.m_TopLayer, group.m_BottomLayer );
361
362 const int diameter = group.m_Diameter;
363
364 text = depth > 0 && diameter > 0
365 ? wxString::Format( wxT( "%.1f:1" ), (double) depth / diameter )
366 : NO_VALUE;
367 break;
368 }
369
371 text = group.m_Symbol.m_Description;
372 break;
373 }
374
375 row.m_Cells.push_back( std::move( text ) );
376 }
377 }
378
379 if( m_showTotals )
380 {
381 int operations = 0;
382
383 // Sites are counted over distinct coordinates, not summed per group. A backdrilled
384 // via contributes several operations at one location and is still one site
385 std::set<std::pair<int, int>> sites;
386
387 for( const DRILL_CHART_GROUP& group : groups )
388 {
389 operations += group.m_OperationCount;
390
391 for( const VECTOR2I& site : group.m_Sites )
392 sites.emplace( site.x, site.y );
393 }
394
395 content->m_Trailer.push_back( { wxString::Format( wxT( "%d OPS / %zu SITES" ), operations, sites.size() ) } );
396 }
397
398 return content;
399}
400
401
402void PCB_DRILL_CHART::onRebuilt( const BOARD& aBoard, const GENERATED_TABLE_CONTENT& aContent )
403{
404 const DRILL_CHART_CONTENT& content = static_cast<const DRILL_CHART_CONTENT&>( aContent );
405
406 m_rowShapes.clear();
407
408 for( size_t ii = 0; ii < content.m_RowShapes.size(); ++ii )
409 {
410 if( content.m_RowShapes[ii] )
411 m_rowShapes[1 + static_cast<int>( ii )] = *content.m_RowShapes[ii];
412 }
413
416}
417
418
419void PCB_DRILL_CHART::Serialize( google::protobuf::Any& aContainer ) const
420{
421 using namespace kiapi::board;
422 types::DrillChart chart;
423
424 PackGeneratedTable( *this, chart );
425
426 types::DrillChartFilter* filter = chart.mutable_filter();
427 filter->set_plated( m_filter.m_Plated );
428 filter->set_non_plated( m_filter.m_NonPlated );
429 filter->set_vias( m_filter.m_Vias );
430 filter->set_slots( m_filter.m_Slots );
431 filter->set_backdrills( m_filter.m_Backdrills );
432 filter->set_castellated( m_filter.m_Castellated );
433
434 chart.set_show_totals( m_showTotals );
435
436 chart.set_symbol_column( m_symbolColumn );
437
438 for( const auto& [row, shapeIndex] : m_rowShapes )
439 ( *chart.mutable_row_shapes() )[row] = shapeIndex;
440
441 aContainer.PackFrom( chart );
442}
443
444
445bool PCB_DRILL_CHART::Deserialize( const google::protobuf::Any& aContainer )
446{
447 using namespace kiapi::board;
448 types::DrillChart chart;
449
450 if( !aContainer.UnpackTo( &chart ) )
451 {
452 return false;
453 }
454
455 if( !UnpackGeneratedTable( chart, *this ) )
456 {
457 return false;
458 }
459
460 if( chart.has_filter() )
461 {
462 m_filter.m_Plated = chart.filter().plated();
463 m_filter.m_NonPlated = chart.filter().non_plated();
464 m_filter.m_Vias = chart.filter().vias();
465 m_filter.m_Slots = chart.filter().slots();
466 m_filter.m_Backdrills = chart.filter().backdrills();
467 m_filter.m_Castellated = chart.filter().castellated();
468 }
469
470 m_symbolColumn = chart.symbol_column();
471
472 m_rowShapes.clear();
473
474 for( const auto& [row, shapeIndex] : chart.row_shapes() )
475 m_rowShapes[row] = shapeIndex;
476
477 m_showTotals = chart.show_totals();
478
479 return true;
480}
481
482
void PackGeneratedTable(const PCB_GENERATED_TABLE &aTable, MSG &aMsg)
Pack what every generated table shares into its message.
bool UnpackGeneratedTable(const MSG &aMsg, PCB_GENERATED_TABLE &aTable)
The reverse of PackGeneratedTable.
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
DRILL_SYMBOL_PROFILE & GetDrillSymbolProfile()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:3660
uint64_t GetDrillModelGeneration() const
Definition board.h:596
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:943
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
The column set and formatting a new chart starts from.
GENERATED_TABLE_UNITS GetUnits() const
std::vector< GENERATED_TABLE_COLUMN > & Columns()
static DRILL_CHART_TEMPLATE MakeDefault()
Grouping rules and symbol assignments, shared by reference so a chart and its map can never disagree ...
The base class for create windows for drawing purpose.
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
One pass of rebuilding tables.
T & Pending()
The pass's T, created from the board on first use.
A drill chart placed on the board, kept in step with the holes.
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
PCB_DRILL_CHART(BOARD_ITEM *aParent)
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
void ApplyTemplate(const DRILL_CHART_TEMPLATE &aTemplate)
Copy a template's formatting.
void swapData(BOARD_ITEM *aImage) override
void onRebuilt(const BOARD &aBoard, const GENERATED_TABLE_CONTENT &aContent) override
Called once the cells are laid out, with the content they were built from.
std::map< int, int > m_rowShapes
uint64_t m_builtGeneration
void SetShowTotals(bool aShow)
bool operator==(const BOARD_ITEM &aOther) const override
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
std::unique_ptr< GENERATED_TABLE_CONTENT > generate(const BOARD &aBoard, GENERATED_TABLE_REFRESH *aRefresh) const override
The rows this table reports.
std::vector< DRILL_CHART_GROUP > buildGroups(const BOARD &aBoard, const DRILL_SYMBOL_PROFILE &aProfile) const
The board data this chart reports on, as chart rows grouped the way aProfile says.
bool IsDataRow(int aRow) const override
True for a row that reports a drill group, false for the title, heading and totals.
bool GetShowTotals() const
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
bool IsStale(const BOARD &aBoard) const override
One integer comparison, so a board with nothing drill related changed pays nothing.
DRILL_CHART_COLUMN_ID columnId(int aCol) const
The typed id a column at this index holds, so generate's switch stays exhaustive even though a stored...
DRILL_CHART_FILTER m_filter
A table whose cell text is derived from the board and rebuilt whenever it goes stale.
std::vector< GENERATED_TABLE_COLUMN > m_columns
GENERATED_TABLE_UNITS m_units
virtual bool IsDataRow(int aRow) const
True for a row that reports on the board, false for the heading and trailer rows.
bool generatedEquals(const PCB_GENERATED_TABLE &aOther) const
Equality of the members this class owns and of the underlying PCB_TABLE.
void swapData(BOARD_ITEM *aImage) override
PCB_GENERATED_TABLE(BOARD_ITEM *aParent, KICAD_T aType, int aLineWidth)
std::map< int, std::string > m_rowKeys
int GetRowCount() const
Definition pcb_table.h:128
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
Provide class metadata.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
DRILL_CHART_COLUMN_ID
std::vector< DRILL_SPAN > EnumerateDrillSpans(const BOARD &aBoard, bool aPadBackdrills)
Every drill span present on the board, through-holes first.
std::map< std::string, DRILL_SYMBOL_ASSIGNMENT > ResolveDrillSymbols(const BOARD &aBoard)
The symbol every hole should carry, without touching the board.
void AssignDrillSymbols(std::vector< DRILL_CHART_GROUP > &aGroups, DRILL_SYMBOL_PROFILE &aProfile)
Give every group a mark, keeping the ones the profile already records.
#define _(s)
wxString FormatGeneratedTableLength(int aValue, GENERATED_TABLE_UNITS aUnits, int aPrecision)
Render a length the way a generated table's cell shows it.
GENERATED_TABLE_UNITS
Some functions to handle hotkeys in KiCad.
Message panel definition file.
#define _HKI(x)
Definition page_info.cpp:40
#define NO_VALUE
static struct PCB_DRILL_CHART_DESC _PCB_DRILL_CHART_DESC
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
#define REGISTER_TYPE(x)
Helper macro to map type hashes to names.
A drill chart's rows, with the symbol each one is drawn with.
std::vector< std::optional< int > > m_RowShapes
Per m_Rows entry, empty when the mark is not a shape.
PCB_LAYER_ID m_TopLayer
DRILL_SYMBOL_ASSIGNMENT m_Symbol
PCB_LAYER_ID m_BottomLayer
Turns the board's drill operations into chart rows for one symbol profile.
DRILL_CHART_FILTER m_Filter
The board's drill symbol profile as a refresh assigns new symbols into it.
DRILL_SYMBOL_PROFILE m_Profile
void Commit(BOARD &aBoard) override
DRILL_PROFILE_PENDING(BOARD &aBoard)
What a generated table reports, one entry per cell below the heading row.
One data row of a generated table, and the identity its formatting follows across rebuilds.
std::vector< wxString > m_Cells
KIBIS_MODEL * model
@ PCB_DRILL_CHART_T
class PCB_DRILL_CHART, a live drill chart derived from PCB_TABLE
Definition typeinfo.h:239
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708