KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_board_statistics.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) 2019 Alexander Shuklin, [email protected]
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
27#include <board_statistics.h>
28
29#include <wx/filedlg.h>
30
31#include <kiplatform/ui.h>
32#include <confirm.h>
33#include <pad.h>
34#include <macros.h>
35#include <string_utils.h>
37#include <widgets/wx_grid.h>
39
40
41#define COL_LABEL 0
42#define COL_AMOUNT 1
43
44// Defines for components view
45#define ROW_LABEL 0
46#define COL_FRONT_SIDE 1
47#define COL_BOTTOM_SIDE 2
48#define COL_TOTAL 3
49
50// Defines for board view
51#define ROW_BOARD_DIMS 0
52#define ROW_BOARD_AREA 1
53#define ROW_FRONT_COPPER_AREA 2
54#define ROW_BACK_COPPER_AREA 3
55#define ROW_MIN_CLEARANCE 4
56#define ROW_MIN_TRACK_WIDTH 5
57#define ROW_MIN_DRILL_DIAMETER 6
58#define ROW_BOARD_THICKNESS 7
59
60
65{
73
74 // Flags to remember last checkboxes state
78
79 // Variables to save last report file name and folder
80 bool saveReportInitialized; // true after the 3 next string are initialized
81 wxString saveReportFolder; // last report folder
82 wxString saveReportName; // last report filename
83 wxString m_project; // name of the project used to create the last report
84 // used to reinit last state after a project change
85};
86
87
89
91 DIALOG_BOARD_STATISTICS_BASE( aParentFrame ),
92 m_frame( aParentFrame ),
93 m_boardWidth( 0 ),
94 m_boardHeight( 0 ),
95 m_boardArea( 0.0 ),
96 m_frontCopperArea( 0.0 ),
97 m_backCopperArea( 0.0 ),
98 m_minClearanceTrackToTrack( std::numeric_limits<int>::max() ),
99 m_minTrackWidth( std::numeric_limits<int>::max() ),
100 m_minDrillSize( std::numeric_limits<int>::max() ),
101 m_boardThickness( 0 ),
102 m_hasOutline( false ),
105{
106 m_gridDrills->Connect( wxEVT_GRID_COL_SORT,
107 wxGridEventHandler( DIALOG_BOARD_STATISTICS::drillGridSort ),
108 nullptr, this );
109
110 m_checkBoxExcludeComponentsNoPins->SetValue( s_savedDialogState.excludeNoPins );
111 m_checkBoxSubtractHoles->SetValue( s_savedDialogState.subtractHoles );
112 m_checkBoxSubtractHolesFromCopper->SetValue( s_savedDialogState.subtractHolesFromCopper );
113
114 wxFont labelFont = KIUI::GetSmallInfoFont( this );
115 m_componentsLabel->SetFont( labelFont );
116 m_boardLabel->SetFont( labelFont );
117 m_padsLabel->SetFont( labelFont );
118 m_viasLabel->SetFont( labelFont );
119
120 // Make labels for grids
121 m_gridComponents->SetCellValue( ROW_LABEL, COL_FRONT_SIDE, _( "Front Side" ) );
122 m_gridComponents->SetCellValue( ROW_LABEL, COL_BOTTOM_SIDE, _( "Back Side" ) );
123 m_gridComponents->SetCellValue( ROW_LABEL, COL_TOTAL, _( "Total" ) );
124
125 wxFont headingFont = KIUI::GetStatusFont( this );
126
127 for( int col = COL_AMOUNT; col < m_gridComponents->GetNumberCols(); col++ )
128 m_gridComponents->SetCellFont( ROW_LABEL, col, headingFont );
129
130 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_LABEL, _( "Dimensions:" ) );
131 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_LABEL, _( "Area:" ) );
132 m_gridBoard->SetCellValue( ROW_FRONT_COPPER_AREA, COL_LABEL, _( "Front copper area:" ) );
133 m_gridBoard->SetCellValue( ROW_BACK_COPPER_AREA, COL_LABEL, _( "Back copper area:" ) );
134 m_gridBoard->SetCellValue( ROW_MIN_CLEARANCE, COL_LABEL, _( "Min track clearance:" ) );
135 m_gridBoard->SetCellValue( ROW_MIN_TRACK_WIDTH, COL_LABEL, _( "Min track width:" ) );
136 m_gridBoard->SetCellValue( ROW_MIN_DRILL_DIAMETER,COL_LABEL, _( "Min drill diameter:" ) );
137 m_gridBoard->SetCellValue( ROW_BOARD_THICKNESS, COL_LABEL, _( "Board stackup thickness:" ) );
138
140 {
141 // Remove wxgrid's selection boxes
142 grid->SetCellHighlightPenWidth( 0 );
143 grid->SetColMinimalAcceptableWidth( 80 );
144
145 for( int row = 0; row < grid->GetNumberRows(); row++ )
146 {
147 grid->SetCellAlignment( row, COL_LABEL, wxALIGN_LEFT, wxALIGN_CENTRE );
148
149 for( int col = COL_AMOUNT; col < grid->GetNumberCols(); col++ )
150 grid->SetCellAlignment( row, col, wxALIGN_RIGHT, wxALIGN_CENTER );
151 }
152 }
153
154 wxFileName fn = m_frame->GetBoard()->GetFileName();
155
156 if( !s_savedDialogState.saveReportInitialized
157 || s_savedDialogState.m_project != Prj().GetProjectFullName() )
158 {
159 fn.SetName( fn.GetName() + wxT( "_report" ) );
160 fn.SetExt( wxT( "txt" ) );
161 s_savedDialogState.saveReportName = fn.GetFullName();
162 s_savedDialogState.saveReportFolder = wxPathOnly( Prj().GetProjectFullName() );
164 s_savedDialogState.saveReportInitialized = true;
165 }
166
167 // The wxStdDialogButtonSizer wxID_CANCLE button is in fact a close button
168 // Nothing to cancel:
169 m_sdbControlSizerCancel->SetLabel( _( "Close" ) );
170}
171
172
174{
175 m_fpTypes.clear();
176
177 // If you need some more types to be shown, simply add them to the corresponding list
178 m_fpTypes.push_back( FP_LINE_ITEM( FP_THROUGH_HOLE, FP_THROUGH_HOLE, _( "THT:" ) ) );
179 m_fpTypes.push_back( FP_LINE_ITEM( FP_SMD, FP_SMD, _( "SMD:" ) ) );
180 m_fpTypes.push_back( FP_LINE_ITEM( FP_THROUGH_HOLE|FP_SMD, 0, _( "Unspecified:" ) ) );
181
182 m_padTypes.clear();
183 m_padTypes.push_back( INFO_LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::PTH, _( "Through hole:" ) ) );
184 m_padTypes.push_back( INFO_LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::SMD, _( "SMD:" ) ) );
185 m_padTypes.push_back( INFO_LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::CONN, _( "Connector:" ) ) );
186 m_padTypes.push_back( INFO_LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::NPTH, _( "NPTH:" ) ) );
187
188 m_padFabProps.clear();
189 m_padFabProps.push_back( INFO_LINE_ITEM<PAD_PROP>( PAD_PROP::CASTELLATED, _( "Castellated pad:" ) ) );
190 m_padFabProps.push_back( INFO_LINE_ITEM<PAD_PROP>( PAD_PROP::PRESSFIT, _( "Press-fit pad:" ) ) );
191
192 m_viaTypes.clear();
193 m_viaTypes.push_back( INFO_LINE_ITEM<VIATYPE>( VIATYPE::THROUGH, _( "Through vias:" ) ) );
194 m_viaTypes.push_back( INFO_LINE_ITEM<VIATYPE>( VIATYPE::BLIND, _( "Blind vias:" ) ) );
195 m_viaTypes.push_back( INFO_LINE_ITEM<VIATYPE>( VIATYPE::BURIED, _( "Buried vias:" ) ) );
196 m_viaTypes.push_back( INFO_LINE_ITEM<VIATYPE>( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
197
198 // If there not enough rows in grids, append some
199 int appendRows = (int) m_fpTypes.size() + 2 - m_gridComponents->GetNumberRows();
200
201 if( appendRows > 0 )
202 m_gridComponents->AppendRows( appendRows );
203
204 appendRows = (int) m_padTypes.size() + 1 + (int)m_padFabProps.size() - m_gridPads->GetNumberRows();
205
206 if( appendRows > 0 )
207 m_gridPads->AppendRows( appendRows );
208
209 appendRows = (int) m_viaTypes.size() + 1 - m_gridVias->GetNumberRows();
210
211 if( appendRows )
212 m_gridVias->AppendRows( appendRows );
213}
214
215
217{
221
222 Layout();
223 m_drillsPanel->Layout();
224
225 m_gridDrills->AutoSizeColumns();
228
229 // Add space for the vertical scrollbar, so that it won't overlap with the cells.
230 m_gridDrills->SetMinSize( wxSize( m_gridDrills->GetEffectiveMinSize().x
231 + wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ),
232 60 ) );
233
235
237 return true;
238}
239
240
242{
243 BOARD* board = m_frame->GetBoard();
244 SHAPE_POLY_SET frontCopper;
245 SHAPE_POLY_SET backCopper;
246 SHAPE_POLY_SET frontHoles;
247 SHAPE_POLY_SET backHoles;
248
249 // Type list for track-related statistics gathering
250 static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T };
251
252 // Get footprints and pads count
253 for( FOOTPRINT* footprint : board->Footprints() )
254 {
255 // Do not proceed footprints with no pads if checkbox checked
256 if( m_checkBoxExcludeComponentsNoPins->GetValue() && ! footprint->Pads().size() )
257 continue;
258
259 // Go through components types list
260 for( FP_LINE_ITEM& line : m_fpTypes )
261 {
262 if( ( footprint->GetAttributes() & line.m_Attribute_mask ) == line.m_Attribute_value )
263 {
264 switch( footprint->GetSide() )
265 {
266 case F_Cu: line.m_FrontSideQty++; break;
267 case B_Cu: line.m_BackSideQty++; break;
268 default: /* unsided: user-layers only, etc. */ break;
269 }
270
271 break;
272 }
273 }
274
275 for( PAD* pad : footprint->Pads() )
276 {
277 // Go through pads types list
279 {
280 if( pad->GetAttribute() == line.m_Attribute )
281 {
282 line.m_Qty++;
283 break;
284 }
285 }
286
287 // Go through pads prop list
289 {
290 if( pad->GetProperty() == line.m_Attribute )
291 {
292 line.m_Qty++;
293 break;
294 }
295 }
296 }
297 }
298
299 // Get via counts
300 for( PCB_TRACK* track : board->Tracks() )
301 {
302 // Get min track width
303 if( track->Type() == PCB_TRACE_T )
304 m_minTrackWidth = std::min( m_minTrackWidth, track->GetWidth() );
305
306 if( !track->IsType( trackTypes ) )
307 continue;
308
309 // Get min clearance between tracks
310 auto layer = track->GetLayer();
311 auto trackShapeA = track->GetEffectiveShape( layer );
312
313 for( PCB_TRACK* otherTrack : board->Tracks() )
314 {
315 if( layer != otherTrack->GetLayer() )
316 continue;
317
318 if( track->GetNetCode() == otherTrack->GetNetCode() )
319 continue;
320
321 if( !otherTrack->IsType( trackTypes ) )
322 continue;
323
324 int actual = 0;
325 auto trackShapeB = otherTrack->GetEffectiveShape( layer );
326 bool collide = trackShapeA->Collide( trackShapeB.get(), m_minClearanceTrackToTrack, &actual );
327
328 if( collide )
330 }
331
332 if( track->Type() == PCB_VIA_T )
333 {
334 PCB_VIA* via = static_cast<PCB_VIA*>( track );
335
337 {
338 if( via->GetViaType() == line.m_Attribute )
339 {
340 line.m_Qty++;
341 break;
342 }
343 }
344 }
345 }
346
347 // Collect drill information
348 m_drillTypes.clear();
349 m_gridDrills->ClearRows();
350
351 std::vector<DRILL_LINE_ITEM> drills;
352 CollectDrillLineItems( board, drills );
353
354 for( const auto& d : drills )
355 {
356 m_drillTypes.push_back( d );
357 m_gridDrills->InsertRows();
358 }
359
360 sort( m_drillTypes.begin(), m_drillTypes.end(),
362
363 SHAPE_POLY_SET polySet;
364 m_hasOutline = board->GetBoardPolygonOutlines( polySet );
365
366 if( m_hasOutline )
367 {
368 m_boardArea = 0.0;
369
370 for( int i = 0; i < polySet.OutlineCount(); i++ )
371 {
372 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
373 m_boardArea += outline.Area();
374
375 // If checkbox "subtract holes" is checked
376 if( m_checkBoxSubtractHoles->GetValue() )
377 {
378 for( int j = 0; j < polySet.HoleCount( i ); j++ )
379 m_boardArea -= polySet.Hole( i, j ).Area();
380
381 for( FOOTPRINT* fp : board->Footprints() )
382 {
383 for( PAD* pad : fp->Pads() )
384 {
385 if( !pad->HasHole() )
386 continue;
387
388 auto hole = pad->GetEffectiveHoleShape();
389 const SEG& seg = hole->GetSeg();
390 double width = hole->GetWidth();
391 double area = seg.Length() * width;
392
393 // Each end of the hole is a half-circle, so together, we have one
394 // full circle. The area of a circle is pi * r^2, so the area of the
395 // hole is pi * (d/2)^2 = pi * 1/4 * d^2.
396 area += M_PI * 0.25 * width * width;
397 m_boardArea -= area;
398 }
399 }
400
401 for( PCB_TRACK* track : board->Tracks() )
402 {
403 if( track->Type() == PCB_VIA_T )
404 {
405 PCB_VIA* via = static_cast<PCB_VIA*>( track );
406 double drill = via->GetDrillValue();
407 m_boardArea -= M_PI * 0.25 * drill * drill;
408 }
409 }
410 }
411 }
412
413 // Compute the bounding box to get a rectangular size
414 // We use the polySet bounding box, not the board bounding box, because
415 // we do not want the thickness of graphic items defining the board outlines
416 // to be taken in account to calculate the physical board bbox
417 BOX2I bbox = polySet.BBox();
418
419 m_boardWidth = (int) bbox.GetWidth();
420 m_boardHeight = (int) bbox.GetHeight();
421 }
422
423 board->RunOnChildren(
424 [&]( BOARD_ITEM* child )
425 {
426 if( child->Type() == PCB_FOOTPRINT_T
427 || child->Type() == PCB_GROUP_T
428 || child->Type() == PCB_GENERATOR_T )
429 {
430 // Wait for recursion into children
431 return;
432 }
433
434 if( child->IsOnLayer( F_Cu ) )
435 child->TransformShapeToPolySet( frontCopper, F_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
436
437 if( child->IsOnLayer( B_Cu ) )
438 child->TransformShapeToPolySet( backCopper, B_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
439
440 if( child->Type() == PCB_PAD_T )
441 {
442 PAD* pad = static_cast<PAD*>( child );
443
444 if( pad->HasHole() )
445 {
446 pad->TransformHoleToPolygon( frontHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
447 pad->TransformHoleToPolygon( backHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
448 }
449 }
450 else if( child->Type() == PCB_VIA_T )
451 {
452 PCB_VIA* via = static_cast<PCB_VIA*>( child );
453 VECTOR2I center = via->GetPosition();
454 int R = via->GetDrillValue() / 2;
455
456 if( via->IsOnLayer( F_Cu ) )
458
459 if( via->IsOnLayer( B_Cu ) )
461 }
462 },
464
465 if( m_checkBoxSubtractHolesFromCopper->GetValue() )
466 {
467 frontCopper.BooleanSubtract( frontHoles );
468 backCopper.BooleanSubtract( backHoles );
469 }
470
471 m_frontCopperArea = frontCopper.Area();
472 m_backCopperArea = backCopper.Area();
473
475}
476
477
478static wxString formatCount( int aCount )
479{
480 return wxString::Format( wxT( "%i" ), aCount );
481};
482
483
485{
486 int totalPads = 0;
487 int row = 0;
488
489 for( const INFO_LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
490 {
491 m_gridPads->SetCellValue( row, COL_LABEL, line.m_Title );
492 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.m_Qty ) );
493 totalPads += line.m_Qty;
494 row++;
495 }
496
497 m_gridPads->SetCellValue( row, COL_LABEL, _( "Total:" ) );
498 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( totalPads ) );
499 row++;
500
501 for( const INFO_LINE_ITEM<PAD_PROP>& line : m_padFabProps )
502 {
503 m_gridPads->SetCellValue( row, COL_LABEL, line.m_Title );
504 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.m_Qty ) );
505 row++;
506 }
507
508 int totalVias = 0;
509 row = 0;
510
511 for( const INFO_LINE_ITEM<VIATYPE>& line : m_viaTypes )
512 {
513 m_gridVias->SetCellValue( row, COL_LABEL, line.m_Title );
514 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( line.m_Qty ) );
515 totalVias += line.m_Qty;
516 row++;
517 }
518
519 m_gridVias->SetCellValue( row, COL_LABEL, _( "Total:" ) );
520 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( totalVias ) );
521
522
523 int totalFront = 0;
524 int totalBack = 0;
525
526 // We don't use row 0, as there labels are
527 row = 1;
528
529 for( const FP_LINE_ITEM& line : m_fpTypes )
530 {
531 m_gridComponents->SetCellValue( row, COL_LABEL, line.m_Title );
532 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( line.m_FrontSideQty ) );
533 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( line.m_BackSideQty ) );
534 m_gridComponents->SetCellValue( row, 3, formatCount( line.m_FrontSideQty + line.m_BackSideQty ) );
535 totalFront += line.m_FrontSideQty;
536 totalBack += line.m_BackSideQty;
537 row++;
538 }
539
540 m_gridComponents->SetCellValue( row, COL_LABEL, _( "Total:" ) );
541 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( totalFront ) );
542 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( totalBack ) );
543 m_gridComponents->SetCellValue( row, COL_TOTAL, formatCount( totalFront + totalBack ) );
544
545 if( m_hasOutline )
546 {
547 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_AMOUNT,
548 wxString::Format( wxT( "%s x %s" ),
549 m_frame->MessageTextFromValue( m_boardWidth, false ),
550 m_frame->MessageTextFromValue( m_boardHeight, true ) ) );
551 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
552 m_frame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA ) );
553 }
554 else
555 {
556 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_AMOUNT, _( "unknown" ) );
557 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
558 }
559
561 m_frame->MessageTextFromValue( m_frontCopperArea, true, EDA_DATA_TYPE::AREA ) );
563 m_frame->MessageTextFromValue( m_backCopperArea, true, EDA_DATA_TYPE::AREA ) );
564
566 m_frame->MessageTextFromValue( m_minClearanceTrackToTrack, true, EDA_DATA_TYPE::DISTANCE ) );
567
569 m_frame->MessageTextFromValue( m_minTrackWidth, true, EDA_DATA_TYPE::DISTANCE ) );
570
572 m_frame->MessageTextFromValue( m_boardThickness, true, EDA_DATA_TYPE::DISTANCE ) );
573
575
577 m_frame->MessageTextFromValue( m_minDrillSize, true, EDA_DATA_TYPE::DISTANCE ) );
578
579 m_gridComponents->AutoSize();
580 m_gridPads->AutoSize();
581 m_gridBoard->AutoSize();
582 m_gridVias->AutoSize();
583
585}
586
587
589{
590 BOARD* board = m_frame->GetBoard();
591 int row = 0;
592
593 for( const DRILL_LINE_ITEM& line : m_drillTypes )
594 {
595 wxString shapeStr;
596 wxString startLayerStr;
597 wxString stopLayerStr;
598
599 switch( line.shape )
600 {
601 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
602 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
603 default: shapeStr = _( "???" ); break;
604 }
605
606 if( line.shape == PAD_DRILL_SHAPE::CIRCLE )
607 m_minDrillSize = std::min( m_minDrillSize, line.xSize );
608
609 if( line.startLayer == UNDEFINED_LAYER )
610 startLayerStr = _( "N/A" );
611 else
612 startLayerStr = board->GetLayerName( line.startLayer );
613
614 if( line.stopLayer == UNDEFINED_LAYER )
615 stopLayerStr = _( "N/A" );
616 else
617 stopLayerStr = board->GetLayerName( line.stopLayer );
618
619 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_COUNT, formatCount( line.m_Qty ) );
620 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_SHAPE, shapeStr );
621 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_X_SIZE, m_frame->MessageTextFromValue( line.xSize ) );
622 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_Y_SIZE, m_frame->MessageTextFromValue( line.ySize ) );
623 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_PLATED, line.isPlated ? _( "PTH" ) : _( "NPTH" ) );
624 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_VIA_PAD, line.isPad ? _( "Pad" ) : _( "Via" ) );
625 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_START_LAYER, startLayerStr );
626 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerStr );
627
628 row++;
629 }
630}
631
632
633void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
634 bool aUseColLabels,
635 bool aUseFirstColAsLabel )
636{
637 std::vector<int> widths( aGrid->GetNumberCols(), 0 );
638 int rowLabelsWidth = 0;
639
640 // Determine column widths.
641
642 if( aUseColLabels )
643 {
644 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
645 widths[col] = (int) aGrid->GetColLabelValue( col ).length();
646 }
647
648 for( int row = 0; row < aGrid->GetNumberRows(); row++ )
649 {
650 rowLabelsWidth = std::max( rowLabelsWidth, (int) aGrid->GetRowLabelValue( row ).length() );
651
652 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
653 widths[col] = std::max( widths[col], (int) aGrid->GetCellValue( row, col ).length() );
654 }
655
656 // Print the cells.
657
658 wxString tmp;
659
660 // Print column labels.
661
662 aStr << wxT( "|" );
663
664 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
665 {
666 if( aUseColLabels )
667 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetColLabelValue( col ) );
668 else
669 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( 0, col ) );
670
671 aStr << tmp;
672 }
673
674 aStr << wxT( "\n" );
675
676 // Print column label horizontal separators.
677
678 aStr << wxT( "|" );
679
680 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
681 {
682 aStr << wxT( "-" );
683 aStr.Append( '-', widths[col] );
684 aStr << wxT( "-|" );
685 }
686
687 aStr << wxT( "\n" );
688
689 // Print regular cells.
690
691 int firstRow = 0, firstCol = 0;
692
693 if( !aUseColLabels )
694 firstRow = 1;
695
696 if( aUseFirstColAsLabel )
697 firstCol = 1;
698
699 for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
700 {
701 if( aUseFirstColAsLabel )
702 tmp.Printf( wxT( "|%-*s |" ), widths[0], aGrid->GetCellValue( row, 0 ) );
703 else
704 tmp.Printf( wxT( "|" ) );
705
706 aStr << tmp;
707
708 for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
709 {
710 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( row, col ) );
711 aStr << tmp;
712 }
713
714 aStr << wxT( "\n" );
715 }
716}
717
718
720{
721 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_gridDrills );
722
723 m_gridDrills->EnsureColLabelsVisible();
724
725 double remainingWidth = KIPLATFORM::UI::GetUnobscuredSize( m_gridDrills ).x;
726
727 // Find the total current width
728 for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
729 {
731 remainingWidth -= m_gridDrills->GetColSize( i );
732 }
733
734 double scalingFactor = std::max( 1.0,
736 int startLayerColWidth = static_cast<int>( m_startLayerColInitialSize * scalingFactor );
737 int stopLayerColWidth = static_cast<int>( m_stopLayerColInitialSize * scalingFactor );
738
739 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_START_LAYER, startLayerColWidth );
740 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerColWidth );
741}
742
743
744void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
745{
746 s_savedDialogState.excludeNoPins = m_checkBoxExcludeComponentsNoPins->GetValue();
747 s_savedDialogState.subtractHoles = m_checkBoxSubtractHoles->GetValue();
748 s_savedDialogState.subtractHolesFromCopper = m_checkBoxSubtractHolesFromCopper->GetValue();
752 Layout();
753 m_drillsPanel->Layout();
754}
755
756
757void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
758{
759 FILE* outFile;
760 wxString msg;
761 wxString boardName;
762
763 wxFileName fn = m_frame->GetBoard()->GetFileName();
764 boardName = fn.GetName();
765 wxFileDialog dlg( this, _( "Save Report File" ), s_savedDialogState.saveReportFolder,
767 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
768
769 if( dlg.ShowModal() == wxID_CANCEL )
770 return;
771
772 s_savedDialogState.saveReportFolder = wxPathOnly( dlg.GetPath() );
773 s_savedDialogState.saveReportName = dlg.GetFilename();
774
775 outFile = wxFopen( dlg.GetPath(), wxT( "wt" ) );
776
777 if( outFile == nullptr )
778 {
779 msg.Printf( _( "Failed to create file '%s'." ), dlg.GetPath() );
780 DisplayErrorMessage( this, msg );
781 return;
782 }
783
784 msg << _( "PCB statistics report\n=====================" ) << wxT( "\n" );
785 msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxT( "\n" );
786 msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << wxT( "\n" );
787 msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << wxT( "\n" );
788
789 msg << wxT( "\n" );
790 msg << _( "Board" ) << wxT( "\n-----\n" );
791
792 if( m_hasOutline )
793 {
794 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " )
795 << m_frame->MessageTextFromValue( m_boardWidth ) << wxT( "\n" );
796 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )
797 << m_frame->MessageTextFromValue( m_boardHeight ) << wxT( "\n" );
798 msg << wxS( "- " ) << _( "Area" ) + wxS( ": " )
799 << m_frame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
800 }
801 else
802 {
803 msg << wxS( "- " ) << _( "Dimensions" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
804 msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
805 }
806
807 msg << wxS( "- " ) << _( "Front copper area" ) + wxS( ": " )
808 << m_frame->MessageTextFromValue( m_frontCopperArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
809 msg << wxS( "- " ) << _( "Back copper area" ) + wxS( ": " )
810 << m_frame->MessageTextFromValue( m_backCopperArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
811
812 msg << wxS( "- " ) << _( "Min track clearance" ) + wxS( ": " )
813 << m_frame->MessageTextFromValue( m_minClearanceTrackToTrack, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
814
815 msg << wxS( "- " ) << _( "Min track width" ) + wxS( ": " )
816 << m_frame->MessageTextFromValue( m_minTrackWidth, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
817
818 msg << wxS( "- " ) << _( "Min drill diameter" ) + wxS( ": " )
819 << m_frame->MessageTextFromValue( m_minDrillSize, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
820
821 msg << wxS( "- " ) << _( "Board stackup thickness" ) + wxS( ": " )
822 << m_frame->MessageTextFromValue( m_boardThickness, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
823
824 msg << wxT( "\n" );
825 msg << _( "Pads" ) << wxT( "\n----\n" );
826
827 for( auto& type : m_padTypes )
828 msg << wxT( "- " ) << type.m_Title << wxS( " " ) << type.m_Qty << wxT( "\n" );
829
830 msg << wxT( "\n" );
831 msg << _( "Vias" ) << wxT( "\n----\n" );
832
833 for( auto& type : m_viaTypes )
834 msg << wxT( "- " ) << type.m_Title << wxS( " " ) << type.m_Qty << wxT( "\n" );
835
836 // We will save data about components in the table.
837 // We have to calculate column widths
838 std::vector<int> widths;
839 std::vector<wxString> labels{ wxT( "" ), _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
840 wxString tmp;
841
842 widths.reserve( labels.size() );
843
844 for( const wxString& label : labels )
845 widths.push_back( (int) label.size() );
846
847 int frontTotal = 0;
848 int backTotal = 0;
849
850 for( const FP_LINE_ITEM& line : m_fpTypes )
851 {
852 // Get maximum width for left label column
853 widths[0] = std::max( (int) line.m_Title.size(), widths[0] );
854 frontTotal += line.m_FrontSideQty;
855 backTotal += line.m_BackSideQty;
856 }
857
858 // Get maximum width for other columns
859 tmp.Printf( wxT( "%i" ), frontTotal );
860 widths[1] = std::max( (int) tmp.size(), widths[1] );
861 tmp.Printf( wxT( "%i" ), backTotal );
862 widths[2] = std::max( (int) tmp.size(), widths[2] );
863 tmp.Printf( wxT( "%i" ), frontTotal + backTotal );
864 widths[3] = std::max( (int) tmp.size(), widths[3] );
865
866 //Write components amount to file
867 msg << wxT( "\n" );
868 msg << _( "Components" ) << wxT( "\n----------\n" );
869 msg << wxT( "\n" );
870
871 printGridToStringAsTable( m_gridComponents, msg, false, true );
872
873 msg << wxT( "\n" );
874 msg << _( "Drill holes" ) << wxT( "\n-----------\n" );
875 msg << wxT( "\n" );
876
877 printGridToStringAsTable( m_gridDrills, msg, true, false );
878
879 if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
880 {
881 msg.Printf( _( "Error writing file '%s'." ), dlg.GetPath() );
882 DisplayErrorMessage( this, msg );
883 }
884
885 fclose( outFile );
886}
887
888
889void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
890{
891 aEvent.Skip();
893}
894
895void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
896{
897 DRILL_LINE_ITEM::COL_ID colId = static_cast<DRILL_LINE_ITEM::COL_ID>( aEvent.GetCol() );
898 bool ascending = !( m_gridDrills->IsSortingBy( colId )
899 && m_gridDrills->IsSortOrderAscending() );
900
901 sort( m_drillTypes.begin(), m_drillTypes.end(), DRILL_LINE_ITEM::COMPARE( colId, ascending ) );
902
904}
905
906
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr int ARC_LOW_DEF
Definition base_units.h:128
void CollectDrillLineItems(BOARD *board, std::vector< DRILL_LINE_ITEM > &out)
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc) const
Convert the item shape to a polyset.
Definition board_item.h:425
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:314
int BuildBoardThicknessFromStackup() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:2642
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition board.cpp:2787
const FOOTPRINTS & Footprints() const
Definition board.h:363
const TRACKS & Tracks() const
Definition board.h:361
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:692
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Definition board.cpp:600
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
DIALOG_BOARD_STATISTICS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Board Statistics"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void checkboxClicked(wxCommandEvent &aEvent) override
Save board statistics to a file.
void printGridToStringAsTable(wxGrid *aGrid, wxString &aStr, bool aUseColLabels, bool aUseFirstColAsLabel)
std::deque< INFO_LINE_ITEM< PAD_PROP > > m_padFabProps
std::deque< INFO_LINE_ITEM< PAD_ATTRIB > > m_padTypes
std::deque< FP_LINE_ITEM > m_fpTypes
int m_startLayerColInitialSize
Width of the start layer column as calculated by the wxWidgets autosizing algorithm.
void updateWidgets()
Update drills grid.
void saveReportClicked(wxCommandEvent &aEvent) override
void updateDrillGrid()
Print grid to string in tabular format.
~DIALOG_BOARD_STATISTICS()
Get data from the PCB board and print it to dialog.
int m_stopLayerColInitialSize
Width of the stop layer column.
DIALOG_BOARD_STATISTICS(PCB_EDIT_FRAME *aParentFrame)
void drillGridSort(wxGridEvent &aEvent)
void refreshItemsTypes()
< Function to fill up all items types to be shown in the dialog.
void drillGridSize(wxSizeEvent &aEvent) override
bool m_hasOutline
Show if board outline properly defined.
std::deque< DRILL_LINE_ITEM > m_drillTypes
std::deque< INFO_LINE_ITEM< VIATYPE > > m_viaTypes
void getDataFromPCB()
Apply data to dialog widgets.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Definition pad.h:54
The main frame for Pcbnew.
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:156
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition project.cpp:174
Definition seg.h:42
int Length() const
Return the length (this).
Definition seg.h:343
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
double Area(bool aAbsolute=true) const
Return the area of this chain.
Represent a set of closed polygons.
double Area()
Return the area of this poly set.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:194
This file is part of the common library.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
static DIALOG_BOARD_STATISTICS_SAVED_STATE s_savedDialogState
#define ROW_BOARD_AREA
#define ROW_BOARD_THICKNESS
#define COL_AMOUNT
#define ROW_LABEL
#define COL_BOTTOM_SIDE
#define ROW_BACK_COPPER_AREA
#define ROW_MIN_TRACK_WIDTH
#define COL_LABEL
#define COL_TOTAL
#define ROW_MIN_DRILL_DIAMETER
#define ROW_FRONT_COPPER_AREA
#define ROW_BOARD_DIMS
static wxString formatCount(int aCount)
#define COL_FRONT_SIDE
#define ROW_MIN_CLEARANCE
#define _(s)
@ RECURSE
Definition eda_item.h:51
static const std::vector< KICAD_T > trackTypes
@ FP_SMD
Definition footprint.h:82
@ FP_THROUGH_HOLE
Definition footprint.h:81
static wxString TextFileWildcard()
@ B_Cu
Definition layer_ids.h:65
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition wxgtk/ui.cpp:264
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
STL namespace.
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:83
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:84
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:107
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:105
@ THROUGH
Definition pcb_track.h:68
@ MICROVIA
Definition pcb_track.h:71
bool collide(T aObject, U aAnotherObject, int aLayer, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
Definition shape_index.h:97
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Footprint attributes (such as SMD, THT, Virtual and so on), which will be shown in the dialog.
Type information, which will be shown in dialog.
VECTOR2I center
int actual
#define M_PI
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.