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_BURIED, _( "Blind/buried:" ) ) );
195 m_viaTypes.push_back( INFO_LINE_ITEM<VIATYPE>( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
196
197 // If there not enough rows in grids, append some
198 int appendRows = (int) m_fpTypes.size() + 2 - m_gridComponents->GetNumberRows();
199
200 if( appendRows > 0 )
201 m_gridComponents->AppendRows( appendRows );
202
203 appendRows = (int) m_padTypes.size() + 1 + (int)m_padFabProps.size() - m_gridPads->GetNumberRows();
204
205 if( appendRows > 0 )
206 m_gridPads->AppendRows( appendRows );
207
208 appendRows = (int) m_viaTypes.size() + 1 - m_gridVias->GetNumberRows();
209
210 if( appendRows )
211 m_gridVias->AppendRows( appendRows );
212}
213
214
216{
220
221 Layout();
222 m_drillsPanel->Layout();
223
224 m_gridDrills->AutoSizeColumns();
227
228 // Add space for the vertical scrollbar, so that it won't overlap with the cells.
229 m_gridDrills->SetMinSize( wxSize( m_gridDrills->GetEffectiveMinSize().x
230 + wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ),
231 60 ) );
232
234
236 return true;
237}
238
239
241{
242 BOARD* board = m_frame->GetBoard();
243 SHAPE_POLY_SET frontCopper;
244 SHAPE_POLY_SET backCopper;
245 SHAPE_POLY_SET frontHoles;
246 SHAPE_POLY_SET backHoles;
247
248 // Type list for track-related statistics gathering
249 static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T };
250
251 // Get footprints and pads count
252 for( FOOTPRINT* footprint : board->Footprints() )
253 {
254 // Do not proceed footprints with no pads if checkbox checked
255 if( m_checkBoxExcludeComponentsNoPins->GetValue() && ! footprint->Pads().size() )
256 continue;
257
258 // Go through components types list
259 for( FP_LINE_ITEM& line : m_fpTypes )
260 {
261 if( ( footprint->GetAttributes() & line.m_Attribute_mask ) == line.m_Attribute_value )
262 {
263 switch( footprint->GetSide() )
264 {
265 case F_Cu: line.m_FrontSideQty++; break;
266 case B_Cu: line.m_BackSideQty++; break;
267 default: /* unsided: user-layers only, etc. */ break;
268 }
269
270 break;
271 }
272 }
273
274 for( PAD* pad : footprint->Pads() )
275 {
276 // Go through pads types list
278 {
279 if( pad->GetAttribute() == line.m_Attribute )
280 {
281 line.m_Qty++;
282 break;
283 }
284 }
285
286 // Go through pads prop list
288 {
289 if( pad->GetProperty() == line.m_Attribute )
290 {
291 line.m_Qty++;
292 break;
293 }
294 }
295 }
296 }
297
298 // Get via counts
299 for( PCB_TRACK* track : board->Tracks() )
300 {
301 // Get min track width
302 if( track->Type() == PCB_TRACE_T )
303 m_minTrackWidth = std::min( m_minTrackWidth, track->GetWidth() );
304
305 if( !track->IsType( trackTypes ) )
306 continue;
307
308 // Get min clearance between tracks
309 auto layer = track->GetLayer();
310 auto trackShapeA = track->GetEffectiveShape( layer );
311
312 for( PCB_TRACK* otherTrack : board->Tracks() )
313 {
314 if( layer != otherTrack->GetLayer() )
315 continue;
316
317 if( track->GetNetCode() == otherTrack->GetNetCode() )
318 continue;
319
320 if( !otherTrack->IsType( trackTypes ) )
321 continue;
322
323 int actual = 0;
324 auto trackShapeB = otherTrack->GetEffectiveShape( layer );
325 bool collide = trackShapeA->Collide( trackShapeB.get(), m_minClearanceTrackToTrack, &actual );
326
327 if( collide )
329 }
330
331 if( track->Type() == PCB_VIA_T )
332 {
333 PCB_VIA* via = static_cast<PCB_VIA*>( track );
334
336 {
337 if( via->GetViaType() == line.m_Attribute )
338 {
339 line.m_Qty++;
340 break;
341 }
342 }
343 }
344 }
345
346 // Collect drill information
347 m_drillTypes.clear();
348 m_gridDrills->ClearRows();
349
350 std::vector<DRILL_LINE_ITEM> drills;
351 CollectDrillLineItems( board, drills );
352
353 for( const auto& d : drills )
354 {
355 m_drillTypes.push_back( d );
356 m_gridDrills->InsertRows();
357 }
358
359 sort( m_drillTypes.begin(), m_drillTypes.end(),
361
362 SHAPE_POLY_SET polySet;
363 m_hasOutline = board->GetBoardPolygonOutlines( polySet );
364
365 if( m_hasOutline )
366 {
367 m_boardArea = 0.0;
368
369 for( int i = 0; i < polySet.OutlineCount(); i++ )
370 {
371 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
372 m_boardArea += outline.Area();
373
374 // If checkbox "subtract holes" is checked
375 if( m_checkBoxSubtractHoles->GetValue() )
376 {
377 for( int j = 0; j < polySet.HoleCount( i ); j++ )
378 m_boardArea -= polySet.Hole( i, j ).Area();
379
380 for( FOOTPRINT* fp : board->Footprints() )
381 {
382 for( PAD* pad : fp->Pads() )
383 {
384 if( !pad->HasHole() )
385 continue;
386
387 auto hole = pad->GetEffectiveHoleShape();
388 const SEG& seg = hole->GetSeg();
389 double width = hole->GetWidth();
390 double area = seg.Length() * width;
391
392 // Each end of the hole is a half-circle, so together, we have one
393 // full circle. The area of a circle is pi * r^2, so the area of the
394 // hole is pi * (d/2)^2 = pi * 1/4 * d^2.
395 area += M_PI * 0.25 * width * width;
396 m_boardArea -= area;
397 }
398 }
399
400 for( PCB_TRACK* track : board->Tracks() )
401 {
402 if( track->Type() == PCB_VIA_T )
403 {
404 PCB_VIA* via = static_cast<PCB_VIA*>( track );
405 double drill = via->GetDrillValue();
406 m_boardArea -= M_PI * 0.25 * drill * drill;
407 }
408 }
409 }
410 }
411
412 // Compute the bounding box to get a rectangular size
413 // We use the polySet bounding box, not the board bounding box, because
414 // we do not want the thickness of graphic items defining the board outlines
415 // to be taken in account to calculate the physical board bbox
416 BOX2I bbox = polySet.BBox();
417
418 m_boardWidth = (int) bbox.GetWidth();
419 m_boardHeight = (int) bbox.GetHeight();
420 }
421
422 board->RunOnChildren(
423 [&]( BOARD_ITEM* child )
424 {
425 if( child->Type() == PCB_FOOTPRINT_T
426 || child->Type() == PCB_GROUP_T
427 || child->Type() == PCB_GENERATOR_T )
428 {
429 // Wait for recursion into children
430 return;
431 }
432
433 if( child->IsOnLayer( F_Cu ) )
434 child->TransformShapeToPolySet( frontCopper, F_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
435
436 if( child->IsOnLayer( B_Cu ) )
437 child->TransformShapeToPolySet( backCopper, B_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
438
439 if( child->Type() == PCB_PAD_T )
440 {
441 PAD* pad = static_cast<PAD*>( child );
442
443 if( pad->HasHole() )
444 {
445 pad->TransformHoleToPolygon( frontHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
446 pad->TransformHoleToPolygon( backHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
447 }
448 }
449 else if( child->Type() == PCB_VIA_T )
450 {
451 PCB_VIA* via = static_cast<PCB_VIA*>( child );
452 VECTOR2I center = via->GetPosition();
453 int R = via->GetDrillValue() / 2;
454
455 if( via->IsOnLayer( F_Cu ) )
457
458 if( via->IsOnLayer( B_Cu ) )
460 }
461 },
463
464 if( m_checkBoxSubtractHolesFromCopper->GetValue() )
465 {
466 frontCopper.BooleanSubtract( frontHoles );
467 backCopper.BooleanSubtract( backHoles );
468 }
469
470 m_frontCopperArea = frontCopper.Area();
471 m_backCopperArea = backCopper.Area();
472
474}
475
476
477static wxString formatCount( int aCount )
478{
479 return wxString::Format( wxT( "%i" ), aCount );
480};
481
482
484{
485 int totalPads = 0;
486 int row = 0;
487
488 for( const INFO_LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
489 {
490 m_gridPads->SetCellValue( row, COL_LABEL, line.m_Title );
491 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.m_Qty ) );
492 totalPads += line.m_Qty;
493 row++;
494 }
495
496 m_gridPads->SetCellValue( row, COL_LABEL, _( "Total:" ) );
497 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( totalPads ) );
498 row++;
499
500 for( const INFO_LINE_ITEM<PAD_PROP>& line : m_padFabProps )
501 {
502 m_gridPads->SetCellValue( row, COL_LABEL, line.m_Title );
503 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.m_Qty ) );
504 row++;
505 }
506
507 int totalVias = 0;
508 row = 0;
509
510 for( const INFO_LINE_ITEM<VIATYPE>& line : m_viaTypes )
511 {
512 m_gridVias->SetCellValue( row, COL_LABEL, line.m_Title );
513 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( line.m_Qty ) );
514 totalVias += line.m_Qty;
515 row++;
516 }
517
518 m_gridVias->SetCellValue( row, COL_LABEL, _( "Total:" ) );
519 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( totalVias ) );
520
521
522 int totalFront = 0;
523 int totalBack = 0;
524
525 // We don't use row 0, as there labels are
526 row = 1;
527
528 for( const FP_LINE_ITEM& line : m_fpTypes )
529 {
530 m_gridComponents->SetCellValue( row, COL_LABEL, line.m_Title );
531 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( line.m_FrontSideQty ) );
532 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( line.m_BackSideQty ) );
533 m_gridComponents->SetCellValue( row, 3, formatCount( line.m_FrontSideQty + line.m_BackSideQty ) );
534 totalFront += line.m_FrontSideQty;
535 totalBack += line.m_BackSideQty;
536 row++;
537 }
538
539 m_gridComponents->SetCellValue( row, COL_LABEL, _( "Total:" ) );
540 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( totalFront ) );
541 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( totalBack ) );
542 m_gridComponents->SetCellValue( row, COL_TOTAL, formatCount( totalFront + totalBack ) );
543
544 if( m_hasOutline )
545 {
546 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_AMOUNT,
547 wxString::Format( wxT( "%s x %s" ),
548 m_frame->MessageTextFromValue( m_boardWidth, false ),
549 m_frame->MessageTextFromValue( m_boardHeight, true ) ) );
550 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
551 m_frame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA ) );
552 }
553 else
554 {
555 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_AMOUNT, _( "unknown" ) );
556 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
557 }
558
560 m_frame->MessageTextFromValue( m_frontCopperArea, true, EDA_DATA_TYPE::AREA ) );
562 m_frame->MessageTextFromValue( m_backCopperArea, true, EDA_DATA_TYPE::AREA ) );
563
565 m_frame->MessageTextFromValue( m_minClearanceTrackToTrack, true, EDA_DATA_TYPE::DISTANCE ) );
566
568 m_frame->MessageTextFromValue( m_minTrackWidth, true, EDA_DATA_TYPE::DISTANCE ) );
569
571 m_frame->MessageTextFromValue( m_boardThickness, true, EDA_DATA_TYPE::DISTANCE ) );
572
574
576 m_frame->MessageTextFromValue( m_minDrillSize, true, EDA_DATA_TYPE::DISTANCE ) );
577
578 m_gridComponents->AutoSize();
579 m_gridPads->AutoSize();
580 m_gridBoard->AutoSize();
581 m_gridVias->AutoSize();
582
584}
585
586
588{
589 BOARD* board = m_frame->GetBoard();
590 int row = 0;
591
592 for( const DRILL_LINE_ITEM& line : m_drillTypes )
593 {
594 wxString shapeStr;
595 wxString startLayerStr;
596 wxString stopLayerStr;
597
598 switch( line.shape )
599 {
600 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
601 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
602 default: shapeStr = _( "???" ); break;
603 }
604
605 if( line.shape == PAD_DRILL_SHAPE::CIRCLE )
606 m_minDrillSize = std::min( m_minDrillSize, line.xSize );
607
608 if( line.startLayer == UNDEFINED_LAYER )
609 startLayerStr = _( "N/A" );
610 else
611 startLayerStr = board->GetLayerName( line.startLayer );
612
613 if( line.stopLayer == UNDEFINED_LAYER )
614 stopLayerStr = _( "N/A" );
615 else
616 stopLayerStr = board->GetLayerName( line.stopLayer );
617
618 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_COUNT, formatCount( line.m_Qty ) );
619 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_SHAPE, shapeStr );
620 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_X_SIZE, m_frame->MessageTextFromValue( line.xSize ) );
621 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_Y_SIZE, m_frame->MessageTextFromValue( line.ySize ) );
622 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_PLATED, line.isPlated ? _( "PTH" ) : _( "NPTH" ) );
623 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_VIA_PAD, line.isPad ? _( "Pad" ) : _( "Via" ) );
624 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_START_LAYER, startLayerStr );
625 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerStr );
626
627 row++;
628 }
629}
630
631
632void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
633 bool aUseColLabels,
634 bool aUseFirstColAsLabel )
635{
636 std::vector<int> widths( aGrid->GetNumberCols(), 0 );
637 int rowLabelsWidth = 0;
638
639 // Determine column widths.
640
641 if( aUseColLabels )
642 {
643 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
644 widths[col] = (int) aGrid->GetColLabelValue( col ).length();
645 }
646
647 for( int row = 0; row < aGrid->GetNumberRows(); row++ )
648 {
649 rowLabelsWidth = std::max( rowLabelsWidth, (int) aGrid->GetRowLabelValue( row ).length() );
650
651 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
652 widths[col] = std::max( widths[col], (int) aGrid->GetCellValue( row, col ).length() );
653 }
654
655 // Print the cells.
656
657 wxString tmp;
658
659 // Print column labels.
660
661 aStr << wxT( "|" );
662
663 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
664 {
665 if( aUseColLabels )
666 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetColLabelValue( col ) );
667 else
668 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( 0, col ) );
669
670 aStr << tmp;
671 }
672
673 aStr << wxT( "\n" );
674
675 // Print column label horizontal separators.
676
677 aStr << wxT( "|" );
678
679 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
680 {
681 aStr << wxT( "-" );
682 aStr.Append( '-', widths[col] );
683 aStr << wxT( "-|" );
684 }
685
686 aStr << wxT( "\n" );
687
688 // Print regular cells.
689
690 int firstRow = 0, firstCol = 0;
691
692 if( !aUseColLabels )
693 firstRow = 1;
694
695 if( aUseFirstColAsLabel )
696 firstCol = 1;
697
698 for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
699 {
700 if( aUseFirstColAsLabel )
701 tmp.Printf( wxT( "|%-*s |" ), widths[0], aGrid->GetCellValue( row, 0 ) );
702 else
703 tmp.Printf( wxT( "|" ) );
704
705 aStr << tmp;
706
707 for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
708 {
709 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( row, col ) );
710 aStr << tmp;
711 }
712
713 aStr << wxT( "\n" );
714 }
715}
716
717
719{
720 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_gridDrills );
721
722 m_gridDrills->EnsureColLabelsVisible();
723
724 double remainingWidth = KIPLATFORM::UI::GetUnobscuredSize( m_gridDrills ).x;
725
726 // Find the total current width
727 for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
728 {
730 remainingWidth -= m_gridDrills->GetColSize( i );
731 }
732
733 double scalingFactor = std::max( 1.0,
735 int startLayerColWidth = static_cast<int>( m_startLayerColInitialSize * scalingFactor );
736 int stopLayerColWidth = static_cast<int>( m_stopLayerColInitialSize * scalingFactor );
737
738 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_START_LAYER, startLayerColWidth );
739 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerColWidth );
740}
741
742
743void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
744{
745 s_savedDialogState.excludeNoPins = m_checkBoxExcludeComponentsNoPins->GetValue();
746 s_savedDialogState.subtractHoles = m_checkBoxSubtractHoles->GetValue();
747 s_savedDialogState.subtractHolesFromCopper = m_checkBoxSubtractHolesFromCopper->GetValue();
751 Layout();
752 m_drillsPanel->Layout();
753}
754
755
756void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
757{
758 FILE* outFile;
759 wxString msg;
760 wxString boardName;
761
762 wxFileName fn = m_frame->GetBoard()->GetFileName();
763 boardName = fn.GetName();
764 wxFileDialog dlg( this, _( "Save Report File" ), s_savedDialogState.saveReportFolder,
766 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
767
768 if( dlg.ShowModal() == wxID_CANCEL )
769 return;
770
771 s_savedDialogState.saveReportFolder = wxPathOnly( dlg.GetPath() );
772 s_savedDialogState.saveReportName = dlg.GetFilename();
773
774 outFile = wxFopen( dlg.GetPath(), wxT( "wt" ) );
775
776 if( outFile == nullptr )
777 {
778 msg.Printf( _( "Failed to create file '%s'." ), dlg.GetPath() );
779 DisplayErrorMessage( this, msg );
780 return;
781 }
782
783 msg << _( "PCB statistics report\n=====================" ) << wxT( "\n" );
784 msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxT( "\n" );
785 msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << wxT( "\n" );
786 msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << wxT( "\n" );
787
788 msg << wxT( "\n" );
789 msg << _( "Board" ) << wxT( "\n-----\n" );
790
791 if( m_hasOutline )
792 {
793 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " )
794 << m_frame->MessageTextFromValue( m_boardWidth ) << wxT( "\n" );
795 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )
796 << m_frame->MessageTextFromValue( m_boardHeight ) << wxT( "\n" );
797 msg << wxS( "- " ) << _( "Area" ) + wxS( ": " )
798 << m_frame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
799 }
800 else
801 {
802 msg << wxS( "- " ) << _( "Dimensions" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
803 msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
804 }
805
806 msg << wxS( "- " ) << _( "Front copper area" ) + wxS( ": " )
807 << m_frame->MessageTextFromValue( m_frontCopperArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
808 msg << wxS( "- " ) << _( "Back copper area" ) + wxS( ": " )
809 << m_frame->MessageTextFromValue( m_backCopperArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
810
811 msg << wxS( "- " ) << _( "Min track clearance" ) + wxS( ": " )
812 << m_frame->MessageTextFromValue( m_minClearanceTrackToTrack, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
813
814 msg << wxS( "- " ) << _( "Min track width" ) + wxS( ": " )
815 << m_frame->MessageTextFromValue( m_minTrackWidth, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
816
817 msg << wxS( "- " ) << _( "Min drill diameter" ) + wxS( ": " )
818 << m_frame->MessageTextFromValue( m_minDrillSize, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
819
820 msg << wxS( "- " ) << _( "Board stackup thickness" ) + wxS( ": " )
821 << m_frame->MessageTextFromValue( m_boardThickness, true, EDA_DATA_TYPE::DISTANCE ) << wxT( "\n" );
822
823 msg << wxT( "\n" );
824 msg << _( "Pads" ) << wxT( "\n----\n" );
825
826 for( auto& type : m_padTypes )
827 msg << wxT( "- " ) << type.m_Title << wxS( " " ) << type.m_Qty << wxT( "\n" );
828
829 msg << wxT( "\n" );
830 msg << _( "Vias" ) << wxT( "\n----\n" );
831
832 for( auto& type : m_viaTypes )
833 msg << wxT( "- " ) << type.m_Title << wxS( " " ) << type.m_Qty << wxT( "\n" );
834
835 // We will save data about components in the table.
836 // We have to calculate column widths
837 std::vector<int> widths;
838 std::vector<wxString> labels{ wxT( "" ), _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
839 wxString tmp;
840
841 widths.reserve( labels.size() );
842
843 for( const wxString& label : labels )
844 widths.push_back( (int) label.size() );
845
846 int frontTotal = 0;
847 int backTotal = 0;
848
849 for( const FP_LINE_ITEM& line : m_fpTypes )
850 {
851 // Get maximum width for left label column
852 widths[0] = std::max( (int) line.m_Title.size(), widths[0] );
853 frontTotal += line.m_FrontSideQty;
854 backTotal += line.m_BackSideQty;
855 }
856
857 // Get maximum width for other columns
858 tmp.Printf( wxT( "%i" ), frontTotal );
859 widths[1] = std::max( (int) tmp.size(), widths[1] );
860 tmp.Printf( wxT( "%i" ), backTotal );
861 widths[2] = std::max( (int) tmp.size(), widths[2] );
862 tmp.Printf( wxT( "%i" ), frontTotal + backTotal );
863 widths[3] = std::max( (int) tmp.size(), widths[3] );
864
865 //Write components amount to file
866 msg << wxT( "\n" );
867 msg << _( "Components" ) << wxT( "\n----------\n" );
868 msg << wxT( "\n" );
869
870 printGridToStringAsTable( m_gridComponents, msg, false, true );
871
872 msg << wxT( "\n" );
873 msg << _( "Drill holes" ) << wxT( "\n-----------\n" );
874 msg << wxT( "\n" );
875
876 printGridToStringAsTable( m_gridDrills, msg, true, false );
877
878 if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
879 {
880 msg.Printf( _( "Error writing file '%s'." ), dlg.GetPath() );
881 DisplayErrorMessage( this, msg );
882 }
883
884 fclose( outFile );
885}
886
887
888void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
889{
890 aEvent.Skip();
892}
893
894void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
895{
896 DRILL_LINE_ITEM::COL_ID colId = static_cast<DRILL_LINE_ITEM::COL_ID>( aEvent.GetCol() );
897 bool ascending = !( m_gridDrills->IsSortingBy( colId )
898 && m_gridDrills->IsSortOrderAscending() );
899
900 sort( m_drillTypes.begin(), m_drillTypes.end(), DRILL_LINE_ITEM::COMPARE( colId, ascending ) );
901
903}
904
905
@ 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:317
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:2512
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:2657
const FOOTPRINTS & Footprints() const
Definition board.h:358
const TRACKS & Tracks() const
Definition board.h:356
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:691
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Definition board.cpp:599
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
Definition edit_tool.cpp:86
@ 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:67
@ BLIND_BURIED
Definition pcb_track.h:68
@ MICROVIA
Definition pcb_track.h:69
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:110
@ 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.