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>
33#include <pcb_tablecell.h>
35#include <widgets/msgpanel.h>
36#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
52
53
55 PCB_TABLE( aOther ),
56 m_filter( aOther.m_filter ),
57 m_columns( aOther.m_columns ),
58 m_units( aOther.m_units ),
59 m_precision( aOther.m_precision ),
60 m_showTotals( aOther.m_showTotals ),
61 m_rowShapes( aOther.m_rowShapes ),
62 m_rowKeys( aOther.m_rowKeys ),
65{
66 // PCB_TABLE's copy ctor propagates m_structType, so the clone reports PCB_DRILL_CHART_T
67 // and swapData's guard stays meaningful
68}
69
70
72{
73 m_columns = aTemplate.Columns();
74 m_units = aTemplate.GetUnits();
75 m_precision = aTemplate.GetPrecision();
76 m_showTotals = aTemplate.GetShowTotals();
77}
78
79
81{
82 wxCHECK_RET( aImage && aImage->Type() == Type(), wxT( "Cannot swap data with invalid chart." ) );
83
84 PCB_TABLE::swapData( aImage );
85
86 PCB_DRILL_CHART* other = static_cast<PCB_DRILL_CHART*>( aImage );
87
88 std::swap( m_filter, other->m_filter );
89 std::swap( m_columns, other->m_columns );
90 std::swap( m_units, other->m_units );
91 std::swap( m_precision, other->m_precision );
92 std::swap( m_showTotals, other->m_showTotals );
93 std::swap( m_builtGeneration, other->m_builtGeneration );
94 std::swap( m_rowShapes, other->m_rowShapes );
95 std::swap( m_rowKeys, other->m_rowKeys );
96 std::swap( m_symbolColumn, other->m_symbolColumn );
97}
98
99
100std::vector<DRILL_CHART_GROUP> PCB_DRILL_CHART::buildGroups( const BOARD& aBoard ) const
101{
103
105 spec.m_Filter = m_filter;
106
107 // Every span on the board. What shares a row is the profile's grouping to decide
108 DRILL_CHART_MODEL model( profile );
109 model.Build( aBoard, EnumerateDrillSpans( aBoard ), spec );
110
111 return model.Groups();
112}
113
114
115void PCB_DRILL_CHART::migrateRows( int aRows, int aCols, int aFirstDataRow,
116 const std::vector<std::string>& aNewRowKeys )
117{
118 const int oldCols = GetColCount();
119
120 // With no recorded keys a row has no identity beyond its position, which is what
121 // ResizeCells already preserves
122 if( oldCols <= 0 || aCols <= 0 || m_cells.empty() || m_rowKeys.empty() )
123 return;
124
125 const int oldRows = static_cast<int>( m_cells.size() ) / oldCols;
126
127 std::map<std::string, int> oldRowByKey;
128 int oldFirstData = oldRows;
129 int oldLastData = -1;
130
131 for( const auto& [oldRow, key] : m_rowKeys )
132 {
133 if( oldRow < 0 || oldRow >= oldRows )
134 continue;
135
136 oldRowByKey[key] = oldRow;
137 oldFirstData = std::min( oldFirstData, oldRow );
138 oldLastData = std::max( oldLastData, oldRow );
139 }
140
141 if( oldRowByKey.empty() )
142 return;
143
144 int newDataCount = 0;
145
146 for( const std::string& key : aNewRowKeys )
147 {
148 if( !key.empty() )
149 newDataCount++;
150 }
151
152 std::vector<int> sourceRow( aRows, -1 );
153
154 // The title and heading are matched from the end of their run, so the heading stays the
155 // heading when a title is added or removed
156 for( int ii = 0; ii < aFirstDataRow; ++ii )
157 {
158 const int oldIdx = oldFirstData - ( aFirstDataRow - ii );
159
160 if( oldIdx >= 0 )
161 sourceRow[ii] = oldIdx;
162 }
163
164 for( int ii = aFirstDataRow; ii < aRows; ++ii )
165 {
166 if( aNewRowKeys[ii].empty() )
167 continue;
168
169 const auto it = oldRowByKey.find( aNewRowKeys[ii] );
170
171 if( it != oldRowByKey.end() )
172 sourceRow[ii] = it->second;
173 }
174
175 const int trailingNewStart = aFirstDataRow + newDataCount;
176
177 for( int ii = trailingNewStart; ii < aRows; ++ii )
178 {
179 const int oldIdx = oldLastData + 1 + ( ii - trailingNewStart );
180
181 if( oldIdx < oldRows )
182 sourceRow[ii] = oldIdx;
183 }
184
185 std::vector<PCB_TABLECELL*> newCells( static_cast<size_t>( aRows ) * aCols, nullptr );
186 std::vector<bool> carried( m_cells.size(), false );
187 std::map<int, int> newRowHeights;
188
189 for( int ii = 0; ii < aRows; ++ii )
190 {
191 if( sourceRow[ii] < 0 )
192 continue;
193
194 // A column added since the last rebuild has no cell to carry, and one taken away
195 // leaves its cells behind to be deleted with the rest of the uncarried ones
196 for( int col = 0; col < std::min( oldCols, aCols ); ++col )
197 {
198 const size_t from = static_cast<size_t>( sourceRow[ii] ) * oldCols + col;
199
200 newCells[static_cast<size_t>( ii ) * aCols + col] = m_cells[from];
201 carried[from] = true;
202 }
203
204 const auto heightIt = m_rowHeights.find( sourceRow[ii] );
205
206 if( heightIt != m_rowHeights.end() )
207 newRowHeights[ii] = heightIt->second;
208 }
209
210 for( size_t ii = 0; ii < m_cells.size(); ++ii )
211 {
212 if( !carried[ii] )
213 delete m_cells[ii];
214 }
215
216 for( PCB_TABLECELL*& cell : newCells )
217 {
218 if( !cell )
219 {
220 cell = new PCB_TABLECELL( this );
221 cell->SetLayer( GetLayer() );
222 }
223 }
224
225 m_cells = std::move( newCells );
226 m_rowHeights = std::move( newRowHeights );
227}
228
229
230bool PCB_DRILL_CHART::IsDataRow( int aRow ) const
231{
232 if( !m_rowKeys.empty() )
233 return m_rowKeys.count( aRow ) > 0;
234
235 // A chart written before the keys were recorded still has to answer this, and its rows are
236 // laid out the way RebuildCells lays them out
237 const int firstDataRow = 1;
238 const int lastDataRow = GetRowCount() - 1 - ( m_showTotals ? 1 : 0 );
239
240 return aRow >= firstDataRow && aRow <= lastDataRow;
241}
242
243
245 const std::vector<KICAD_T>& aScanTypes )
246{
247 // A chart answers to both scan types, so reporting per matching type would hand it to the
248 // inspector twice and list it twice in the disambiguation menu
249 bool wantChart = false;
250 bool wantCells = false;
251
252 for( KICAD_T scanType : aScanTypes )
253 {
254 if( scanType == PCB_DRILL_CHART_T || scanType == PCB_TABLE_T )
255 wantChart = true;
256 else if( scanType == PCB_TABLECELL_T )
257 wantCells = true;
258 }
259
260 if( wantChart && INSPECT_RESULT::QUIT == aInspector( this, aTestData ) )
262
263 if( wantCells )
264 {
265 for( PCB_TABLECELL* cell : GetCells() )
266 {
267 if( INSPECT_RESULT::QUIT == aInspector( cell, aTestData ) )
269 }
270 }
271
273}
274
275
276wxString PCB_DRILL_CHART::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
277{
278 return wxString::Format( _( "Drill Chart (%d rows)" ), GetRowCount() );
279}
280
281
282void PCB_DRILL_CHART::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
283{
284 aList.emplace_back( _( "Drill Chart" ), wxEmptyString );
285 aList.emplace_back( _( "Rows" ), wxString::Format( wxT( "%d" ), GetRowCount() ) );
286 aList.emplace_back( _( "Layer" ), GetLayerName() );
287}
288
289
290double PCB_DRILL_CHART::Similarity( const BOARD_ITEM& aOther ) const
291{
292 if( aOther.Type() != Type() )
293 return 0.0;
294
295 return PCB_TABLE::Similarity( aOther );
296}
297
298
299bool PCB_DRILL_CHART::operator==( const BOARD_ITEM& aOther ) const
300{
301 if( aOther.Type() != Type() )
302 return false;
303
304 const PCB_DRILL_CHART& other = static_cast<const PCB_DRILL_CHART&>( aOther );
305
306 // Must cover everything swapData swaps. The git merge driver decides a change is a
307 // change from this, so an omitted member is a silently dropped edit
308 return m_filter == other.m_filter
309 && m_columns == other.m_columns && m_units == other.m_units
310 && m_precision == other.m_precision
311 && m_showTotals == other.m_showTotals
312 && m_rowShapes == other.m_rowShapes && m_rowKeys == other.m_rowKeys
313 && m_symbolColumn == other.m_symbolColumn && PCB_TABLE::operator==( aOther );
314}
315
316
317namespace
318{
319
320const wxString NO_VALUE( wxS( "\u2014" ) );
321
322
323wxString formatLength( int aValue, DRILL_CHART_UNITS aUnits, int aPrecision )
324{
325 const double mm = pcbIUScale.IUTomm( aValue );
326
327 switch( aUnits )
328 {
330 return wxString::Format( wxT( "%.*f\u2033" ), aPrecision + 1, mm / 25.4 );
331
333 default:
334 return wxString::Format( wxT( "%.*f" ), aPrecision, mm );
335 }
336}
337
338
339wxString spanText( const BOARD& aBoard, const DRILL_CHART_GROUP& aGroup )
340{
341 // The board's own layer names, so a chart matches the stackup the fabricator was given
342 return wxString::Format( wxT( "%s / %s" ), aBoard.GetLayerName( aGroup.m_TopLayer ),
343 aBoard.GetLayerName( aGroup.m_BottomLayer ) );
344}
345
346
347wxString operationText( const DRILL_CHART_GROUP& aGroup )
348{
350 return wxT( "Backdrill" );
351
352 if( aGroup.m_IsSlot )
353 return wxT( "Slot" );
354
355 return wxT( "Drill" );
356}
357
358
359wxString protectionText( const DRILL_CHART_GROUP& aGroup )
360{
361 wxArrayString parts;
362
363 if( aGroup.m_Filled )
364 parts.Add( wxT( "filled" ) );
365
366 if( aGroup.m_Capped )
367 parts.Add( wxT( "capped" ) );
368
369 if( aGroup.m_TopPlugged || aGroup.m_BottomPlugged )
370 parts.Add( wxT( "plugged" ) );
371
372 if( aGroup.m_TopCovered || aGroup.m_BottomCovered )
373 parts.Add( wxT( "covered" ) );
374
375 if( aGroup.m_TopTented || aGroup.m_BottomTented )
376 parts.Add( wxT( "tented" ) );
377
378 return parts.IsEmpty() ? NO_VALUE : wxJoin( parts, ',' );
379}
380
381
382wxString symbolText( const DRILL_CHART_GROUP& aGroup, DRILL_CHART_UNITS aUnits, int aPrecision )
383{
384 switch( aGroup.m_Symbol.m_MarkMode )
385 {
387 return aGroup.m_Symbol.m_Letter;
388
390 return formatLength( aGroup.m_Diameter, aUnits, aPrecision );
391
393 default:
394 // The shape itself is drawn by the painter. The cell carries no text
395 return wxEmptyString;
396 }
397}
398
399} // namespace
400
401
402void PCB_DRILL_CHART::RebuildCells( const BOARD& aBoard, DRILL_SYMBOL_PROFILE* aAssignedProfile )
403{
404 std::vector<DRILL_CHART_GROUP> groups = buildGroups( aBoard );
405
406 if( aAssignedProfile )
407 {
408 // The caller's copy, so a cancelled placement leaves nothing behind and a batch
409 // rebuild accumulates instead of keeping only the last chart's
410 AssignDrillSymbols( groups, *aAssignedProfile );
411 }
412 else
413 {
414 // Whole board, not this chart's filtered subset, or a filtered chart prints a
415 // different symbol from the map for the same hole
416 const std::map<std::string, DRILL_SYMBOL_ASSIGNMENT> resolved =
417 ResolveDrillSymbols( aBoard );
418
419 for( DRILL_CHART_GROUP& group : groups )
420 {
421 const auto it = resolved.find( group.m_SymbolKey );
422
423 if( it != resolved.end() )
424 group.m_Symbol = it->second;
425 }
426 }
427
428 const int cols = static_cast<int>( m_columns.size() );
429 const int totalRows = m_showTotals ? 1 : 0;
430
431 // The headings are the header row. A chart carries no caption of its own
432 const int rows = 1 + static_cast<int>( groups.size() ) + totalRows;
433
434 // A new cell carries a half-INT_MAX rectangle and Normalize() anchors on cell 0's centre,
435 // so without holding the old position a rebuild lands the chart half a metre off-board
436 const VECTOR2I anchor = GetCells().empty() ? VECTOR2I( 0, 0 ) : GetPosition();
437
438 std::vector<std::string> newRowKeys( rows );
439
440 // What a row reports, so a rebuild hands its formatting to the row still reporting the
441 // same holes rather than to whatever lands on its index
442 for( size_t ii = 0; ii < groups.size(); ++ii )
443 newRowKeys[1 + static_cast<int>( ii )] = groups[ii].m_Key;
444
445 migrateRows( rows, cols, 1, newRowKeys );
446
447 SetColCount( cols );
448 ResizeCells( rows, cols );
449
450 m_rowKeys.clear();
451
452 for( int ii = 0; ii < rows; ++ii )
453 {
454 if( !newRowKeys[ii].empty() )
455 m_rowKeys[ii] = newRowKeys[ii];
456 }
457
458 int row = 0;
459
460 auto setCell =
461 [&]( int aRow, int aCol, const wxString& aText )
462 {
463 PCB_TABLECELL* cell = GetCell( aRow, aCol );
464
465 if( !cell )
466 return;
467
468 cell->SetText( aText );
469
470 // The column's alignment, which was otherwise editable, serialized and
471 // ignored
472 switch( m_columns[aCol].m_Align )
473 {
476 break;
477
480 break;
481
484 break;
485 }
486 };
487
488 m_rowShapes.clear();
489
490 for( int col = 0; col < cols; ++col )
491 setCell( row, col, m_columns[col].m_Heading );
492
493 row++;
494
495 m_symbolColumn = -1;
496
497 for( int col = 0; col < cols; ++col )
498 {
500 m_symbolColumn = col;
501 }
502
503 for( const DRILL_CHART_GROUP& group : groups )
504 {
505 if( group.m_Symbol.m_MarkMode == DRILL_MARK_MODE::SHAPE )
506 m_rowShapes[row] = group.m_Symbol.m_ShapeIndex;
507
508 for( int col = 0; col < cols; ++col )
509 {
510 wxString text;
511
512 switch( m_columns[col].m_Id )
513 {
515 text = symbolText( group, m_units, m_precision );
516 break;
517
519 text = formatLength( group.m_Diameter, m_units, m_precision );
520 break;
521
523 text = group.m_IsSlot ? wxString::Format( wxT( "%s x %s" ),
524 formatLength( group.m_SizeXY.x, m_units, m_precision ),
525 formatLength( group.m_SizeXY.y, m_units, m_precision ) )
526 : NO_VALUE;
527 break;
528
530 text = group.m_NotPlated ? wxT( "No" ) : wxT( "Yes" );
531 break;
532
534 text = wxString::Format( wxT( "%d" ), group.m_OperationCount );
535 break;
536
538 text = wxString::Format( wxT( "%d" ), group.m_SiteCount );
539 break;
540
542 text = spanText( aBoard, group );
543 break;
544
546 text = operationText( group );
547 break;
548
550 text = protectionText( group );
551 break;
552
554 text = group.m_StubLength.has_value()
555 ? formatLength( *group.m_StubLength, m_units, m_precision )
556 : NO_VALUE;
557 break;
558
560 {
561 // Depth over diameter, the number a fabricator uses to judge plating
562 // difficulty. Needs a real stackup, so it stays an em dash without one
563 const int depth = aBoard.GetStackupOrDefault().GetLayerDistance(
564 group.m_TopLayer, group.m_BottomLayer );
565
566 const int diameter = group.m_Diameter;
567
568 text = depth > 0 && diameter > 0
569 ? wxString::Format( wxT( "%.1f:1" ), (double) depth / diameter )
570 : NO_VALUE;
571 break;
572 }
573
575 text = group.m_Symbol.m_Description;
576 break;
577 }
578
579 setCell( row, col, text );
580 }
581
582 row++;
583 }
584
585 if( totalRows )
586 {
587 int operations = 0;
588
589 // Sites are counted over distinct coordinates, not summed per group. A backdrilled
590 // via contributes several operations at one location and is still one site
591 std::set<std::pair<int, int>> sites;
592
593 for( const DRILL_CHART_GROUP& group : groups )
594 {
595 operations += group.m_OperationCount;
596
597 for( const VECTOR2I& site : group.m_Sites )
598 sites.emplace( site.x, site.y );
599 }
600
601 setCell( row, 0, wxString::Format( wxT( "%d OPS / %zu SITES" ), operations,
602 sites.size() ) );
603
604 for( int col = 1; col < cols; ++col )
605 setCell( row, col, wxEmptyString );
606 }
607
608 Autosize();
609
610 // After autosizing or the authored width never shows, and as a minimum so a width
611 // saved against a narrower board cannot clip its text
612 bool widened = false;
613
614 for( int col = 0; col < cols; ++col )
615 {
616 if( m_columns[col].m_Width > GetColWidth( col ) )
617 {
618 SetColWidth( col, m_columns[col].m_Width );
619 widened = true;
620 }
621 }
622
623 // The widths above are only a map until the cells are placed against them
624 if( widened )
625 Normalize();
626
627 Move( anchor - GetPosition() );
628
630
631}
632
633
634void PCB_DRILL_CHART::Serialize( google::protobuf::Any& aContainer ) const
635{
636 using namespace kiapi::board;
637 types::DrillChart chart;
638
639 google::protobuf::Any tableAny;
640 PCB_TABLE::Serialize( tableAny );
641 tableAny.UnpackTo( chart.mutable_table() );
642
643 types::DrillChartFilter* filter = chart.mutable_filter();
644 filter->set_plated( m_filter.m_Plated );
645 filter->set_non_plated( m_filter.m_NonPlated );
646 filter->set_vias( m_filter.m_Vias );
647 filter->set_slots( m_filter.m_Slots );
648 filter->set_backdrills( m_filter.m_Backdrills );
649 filter->set_castellated( m_filter.m_Castellated );
650
651 for( const DRILL_CHART_COLUMN& col : m_columns )
652 {
653 types::DrillChartColumn* proto = chart.add_columns();
654 proto->set_id( static_cast<types::DrillChartColumnId>( static_cast<int>( col.m_Id ) + 1 ) );
655 proto->set_heading( col.m_Heading.ToStdString() );
656 proto->set_align( static_cast<types::DrillChartAlign>( static_cast<int>( col.m_Align ) + 1 ) );
657 kiapi::common::PackDistance( *proto->mutable_width(), col.m_Width );
658 }
659
660 switch( m_units )
661 {
662 case DRILL_CHART_UNITS::MM: chart.set_units( kiapi::common::types::U_MM ); break;
663 case DRILL_CHART_UNITS::INCH: chart.set_units( kiapi::common::types::U_INCH ); break;
664 }
665
666 chart.set_precision( m_precision );
667 chart.set_show_totals( m_showTotals );
668
669 chart.set_symbol_column( m_symbolColumn );
670
671 for( const auto& [row, shapeIndex] : m_rowShapes )
672 ( *chart.mutable_row_shapes() )[row] = shapeIndex;
673
674 for( const auto& [row, key] : m_rowKeys )
675 ( *chart.mutable_row_keys() )[row] = key;
676
677 aContainer.PackFrom( chart );
678
679}
680
681
682bool PCB_DRILL_CHART::Deserialize( const google::protobuf::Any& aContainer )
683{
684 using namespace kiapi::board;
685 types::DrillChart chart;
686
687 if( !aContainer.UnpackTo( &chart ) )
688 {
689 return false;
690 }
691
692 google::protobuf::Any tableAny;
693 tableAny.PackFrom( chart.table() );
694
695 if( !PCB_TABLE::Deserialize( tableAny ) )
696 {
697 return false;
698 }
699
700 // The table carries the layer. A chart on a manufacturing layer would be plotted into a
701 // fabrication output rather than the documentation
702 if( !DrillDocumentationLayers().Contains( GetLayer() ) )
703 {
704 return false;
705 }
706
707 if( chart.has_filter() )
708 {
709 m_filter.m_Plated = chart.filter().plated();
710 m_filter.m_NonPlated = chart.filter().non_plated();
711 m_filter.m_Vias = chart.filter().vias();
712 m_filter.m_Slots = chart.filter().slots();
713 m_filter.m_Backdrills = chart.filter().backdrills();
714 m_filter.m_Castellated = chart.filter().castellated();
715 }
716
717 m_columns.clear();
718
719 for( const types::DrillChartColumn& proto : chart.columns() )
720 {
721 if( proto.id() == types::DCC_UNKNOWN )
722 continue;
723
724 if( proto.id() > types::DCC_DESCRIPTION )
725 {
726 return false;
727 }
728
730 col.m_Id = static_cast<DRILL_CHART_COLUMN_ID>( static_cast<int>( proto.id() ) - 1 );
731 col.m_Heading = wxString::FromUTF8( proto.heading() );
732
733 if( proto.align() < types::DCA_UNKNOWN || proto.align() > types::DCA_RIGHT )
734 {
735 return false;
736 }
737
738 if( proto.align() != types::DCA_UNKNOWN )
739 col.m_Align = static_cast<DRILL_CHART_ALIGN>( static_cast<int>( proto.align() ) - 1 );
740
741 kiapi::common::types::Distance width;
742 width.set_value_nm( std::clamp<int64_t>( proto.width().value_nm(), 0,
745 m_columns.push_back( col );
746 }
747
748 // No columns divides by zero the next time this is rebuilt or autosized. Repeats and
749 // implausible widths reach table geometry
751 {
752 return false;
753 }
754
755 // The shared enum also carries mils, metres and tenths. A chart has no rendering for
756 // those, so anything but inches reads back as millimetres rather than as a broken chart
757 switch( chart.units() )
758 {
759 case kiapi::common::types::U_INCH: m_units = DRILL_CHART_UNITS::INCH; break;
760 default: m_units = DRILL_CHART_UNITS::MM; break;
761 }
762
763 m_symbolColumn = chart.symbol_column();
764
765 m_rowShapes.clear();
766
767 for( const auto& [row, shapeIndex] : chart.row_shapes() )
768 m_rowShapes[row] = shapeIndex;
769
770 m_rowKeys.clear();
771
772 for( const auto& [row, key] : chart.row_keys() )
773 m_rowKeys[row] = key;
774
775 SetPrecision( chart.precision() );
776 m_showTotals = chart.show_totals();
777
778 return true;
779}
780
781
783{
784 const uint64_t generation = aBoard.GetDrillModelGeneration();
785
786 // Assignments are committed only once every chart has rebuilt, so a chart that throws
787 // cannot leave half a profile behind
789 int rebuilt = 0;
790
791 for( BOARD_ITEM* item : aBoard.Drawings() )
792 {
793 if( item->Type() != PCB_DRILL_CHART_T )
794 continue;
795
796 PCB_DRILL_CHART* chart = static_cast<PCB_DRILL_CHART*>( item );
797
798 if( chart->GetBuiltGeneration() == generation )
799 continue;
800
801 chart->RebuildCells( aBoard, &assigned );
802 rebuilt++;
803 }
804
805 if( !rebuilt )
806 return;
807
808 aBoard.GetDesignSettings().GetDrillSymbolProfile() = assigned;
809
810}
811
812
814{
816 {
818
819 if( unitsEnum.Choices().GetCount() == 0 )
820 {
821 unitsEnum.Map( DRILL_CHART_UNITS::MM, _HKI( "Millimeters" ) )
822 .Map( DRILL_CHART_UNITS::INCH, _HKI( "Inches" ) );
823 }
824
827
832
833 const wxString chartProps = _( "Drill Chart Properties" );
834
837 chartProps );
838
839 propMgr.AddProperty( new PROPERTY<PCB_DRILL_CHART, int>( _HKI( "Decimal Places" ),
842 chartProps );
843
844 propMgr.AddProperty( new PROPERTY<PCB_DRILL_CHART, bool>( _HKI( "Show Totals" ),
847 chartProps );
848 }
850
851
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
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
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:86
friend class BOARD
Definition board_item.h:578
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
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:409
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:3639
uint64_t GetDrillModelGeneration() const
Definition board.h:587
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:936
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
const DRAWINGS & Drawings() const
Definition board.h:465
The column set and formatting a new chart starts from.
DRILL_CHART_UNITS GetUnits() const
std::vector< DRILL_CHART_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
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:231
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:365
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:776
static ENUM_MAP< T > & Instance()
Definition property.h:770
wxPGChoices & Choices()
Definition property.h:821
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.
DRILL_CHART_UNITS GetUnits() const
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
std::map< int, std::string > m_rowKeys
void ApplyTemplate(const DRILL_CHART_TEMPLATE &aTemplate)
Copy a template's formatting.
void swapData(BOARD_ITEM *aImage) override
uint64_t GetBuiltGeneration() const
Board drill generation the cells were built at.
DRILL_CHART_UNITS m_units
std::map< int, int > m_rowShapes
void RebuildCells(const BOARD &aBoard, DRILL_SYMBOL_PROFILE *aAssignedProfile=nullptr)
Regenerate the cells from the board.
std::vector< DRILL_CHART_COLUMN > m_columns
uint64_t m_builtGeneration
void SetShowTotals(bool aShow)
std::vector< DRILL_CHART_GROUP > buildGroups(const BOARD &aBoard) const
The board data this chart reports on, as chart rows.
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.
int GetPrecision() const
void SetPrecision(int aPrecision)
Clamped.
void migrateRows(int aRows, int aCols, int aFirstDataRow, const std::vector< std::string > &aNewRowKeys)
Move each row's cells to the row that reports the same drill group.
INSPECT_RESULT Visit(INSPECTOR inspector, void *testData, const std::vector< KICAD_T > &aScanTypes) override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
bool GetShowTotals() const
bool IsDataRow(int aRow) const
True for a row that reports a drill group, false for the title, heading and totals.
void SetUnits(DRILL_CHART_UNITS aUnits)
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.
DRILL_CHART_FILTER m_filter
int GetRowCount() const
Definition pcb_table.h:123
std::vector< PCB_TABLECELL * > m_cells
Definition pcb_table.h:335
void SetColWidth(int aCol, int aWidth)
Definition pcb_table.h:130
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void ResizeCells(int aRows, int aCols)
Grow or shrink to aRows x aCols, keeping the cells that already exist.
PCB_TABLECELL * GetCell(int aRow, int aCol) const
Definition pcb_table.h:150
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:160
void Autosize()
bool operator==(const PCB_TABLE &aOther) const
int GetColCount() const
Definition pcb_table.h:121
virtual void swapData(BOARD_ITEM *aImage) override
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
void SetColCount(int aCount)
Definition pcb_table.h:120
int GetColWidth(int aCol) const
Definition pcb_table.h:132
VECTOR2I GetPosition() const override
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
PCB_TABLE(BOARD_ITEM *aParent, int aLineWidth)
Definition pcb_table.cpp:55
std::map< int, int > m_rowHeights
Definition pcb_table.h:334
Provide class metadata.Helper macro to map type hashes to names.
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.
static bool empty(const wxTextEntryBase *aCtrl)
std::vector< DRILL_SPAN > EnumerateDrillSpans(const BOARD &aBoard)
Every drill span present on the board, through-holes first.
bool ValidateDrillChartColumns(std::vector< DRILL_CHART_COLUMN > &aColumns)
Reject a column set that repeats an id or is implausibly wide.
LSET DrillDocumentationLayers()
Layers a chart or map may live on.
DRILL_CHART_ALIGN
constexpr int DRILL_CHART_MAX_COLUMN_WIDTH
A chart column can be no wider than this, and no chart wider than that in total.
DRILL_CHART_COLUMN_ID
DRILL_CHART_UNITS
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)
INSPECT_RESULT
Definition eda_item.h:44
const INSPECTOR_FUNC & INSPECTOR
std::function passed to nested users by ref, avoids copying std::function.
Definition eda_item.h:91
Some functions to handle hotkeys in KiCad.
Message panel definition file.
KICOMMON_API int UnpackDistance(const types::Distance &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackDistance(types::Distance &aOutput, int aInput, const EDA_IU_SCALE &aScale)
#define _HKI(x)
Definition page_info.cpp:40
#define NO_VALUE
static struct PCB_DRILL_CHART_DESC _PCB_DRILL_CHART_DESC
void RefreshDrillCharts(BOARD &aBoard)
Bring every chart on the board up to date.
#define TYPE_HASH(x)
Definition property.h:74
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition property.h:877
#define REGISTER_TYPE(x)
DRILL_CHART_COLUMN_ID m_Id
DRILL_CHART_ALIGN m_Align
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
KIBIS_MODEL * model
static const long long MM
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:87
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ 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:683