KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_library_parity.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.
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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <layer_range.h>
25#include <layer_utils.h>
26#include <kiway.h>
27#include <macros.h>
30#include <board.h>
31#include <pcb_shape.h>
32#include <pcb_barcode.h>
33#include <zone.h>
34#include <footprint.h>
35#include <pad.h>
36#include <drc/drc_engine.h>
37#include <drc/drc_item.h>
39#include <project_pcb.h>
40#include <string_utils.h>
41
42
43/*
44 Library parity test.
45
46 Errors generated:
47 - DRCE_LIB_FOOTPRINT_ISSUES
48 - DRCE_LIB_FOOTPRINT_MISMATCH
49*/
50
52{
53public:
58
60
61 virtual bool Run() override;
62
63 virtual const wxString GetName() const override { return wxT( "library_parity" ); };
64};
65
66
67//
68// The TEST*() macros have two modes:
69// In "Report" mode (aReporter != nullptr) all properties are checked and reported on.
70// In "DRC" mode (aReporter == nulltpr) properties are only checked until a difference is found.
71//
72#define TEST( a, b, msg ) \
73 do { \
74 if( a != b ) \
75 { \
76 diff = true; \
77 \
78 if( aReporter && wxString( msg ).length() ) \
79 aReporter->Report( msg ); \
80 } \
81 \
82 if( diff && !aReporter ) \
83 return diff; \
84 } while (0)
85
86#define EPSILON 10
87#define TEST_PT( a, b, msg ) \
88 do { \
89 if( abs( a.x - b.x ) > EPSILON \
90 || abs( a.y - b.y ) > EPSILON ) \
91 { \
92 diff = true; \
93 \
94 if( aReporter && wxString( msg ).length() ) \
95 aReporter->Report( msg ); \
96 } \
97 \
98 if( diff && !aReporter ) \
99 return diff; \
100 } while (0)
101
102#define EPSILON_D 0.000010
103#define TEST_D( a, b, msg ) \
104 do { \
105 if( abs( a - b ) > EPSILON_D ) \
106 { \
107 diff = true; \
108 \
109 if( aReporter && wxString( msg ).length() ) \
110 aReporter->Report( msg ); \
111 } \
112 \
113 if( diff && !aReporter ) \
114 return diff; \
115 } while (0)
116
117#define ITEM_DESC( item ) ( item )->GetItemDescription( &g_unitsProvider, true )
118#define PAD_DESC( pad ) wxString::Format( _( "Pad %s" ), ( pad )->GetNumber() )
119
120
122
123
124LSET getBoardNormalizedLayerSet( const BOARD_ITEM* aLibItem, const BOARD* aBoard )
125{
126 LSET lset = aLibItem->GetLayerSet();
127
128 if( aBoard )
129 lset &= aBoard->GetEnabledLayers();
130
131 return lset;
132}
133
134
135static bool boardLayersMatchWithInnerLayerExpansion( const LSET& aItem, const LSET& bLib,
136 bool aAllowCuExpansion )
137{
138 if( !aAllowCuExpansion )
139 {
140 return aItem == bLib;
141 }
142
143 // first test non copper layers, these should exact match
144 const LSET nonCuMask = LSET::AllNonCuMask();
145 const LSET aNonCu = aItem & nonCuMask;
146 const LSET bNonCu = bLib & nonCuMask;
147
148 if( aNonCu != bNonCu )
149 return false;
150
151 // top and bottom copper must match exactly
152 if( ( aItem & LSET::ExternalCuMask() ) != ( bLib & LSET::ExternalCuMask() ) )
153 return false;
154
155 // technically we can ignore the inner layers entirely due to aAllowCuExpansion at this point
156 // since its assumed the layers got expanded elsewhere
157 // but it feels weird not to sanity this
158
159 // extract the inner layers to compare prescence
160 const LSET aInner = ( aItem & LSET::AllCuMask() ) & ~( LSET::ExternalCuMask() );
161 const LSET bInner = ( bLib & LSET::AllCuMask() ) & ~( LSET::ExternalCuMask() );
162
163 const LSET missingInnerInA = bInner & ~aInner;
164 if( missingInnerInA.count() )
165 return false;
166
167 return true;
168}
169
170
171bool primitiveNeedsUpdate( const std::shared_ptr<PCB_SHAPE>& a,
172 const std::shared_ptr<PCB_SHAPE>& b )
173{
174 REPORTER* aReporter = nullptr;
175 bool diff = false;
176
177 TEST( a->GetShape(), b->GetShape(), "" );
178
179 switch( a->GetShape() )
180 {
182 {
183 BOX2I aRect( a->GetStart(), a->GetEnd() - a->GetStart() );
184 BOX2I bRect( b->GetStart(), b->GetEnd() - b->GetStart() );
185
186 aRect.Normalize();
187 bRect.Normalize();
188
189 TEST_PT( aRect.GetOrigin(), bRect.GetOrigin(), "" );
190 TEST_PT( aRect.GetEnd(), bRect.GetEnd(), "" );
191 break;
192 }
193
194 case SHAPE_T::SEGMENT:
195 case SHAPE_T::CIRCLE:
196 TEST_PT( a->GetStart(), b->GetStart(), "" );
197 TEST_PT( a->GetEnd(), b->GetEnd(), "" );
198 break;
199
200 case SHAPE_T::ARC:
201 TEST_PT( a->GetStart(), b->GetStart(), "" );
202 TEST_PT( a->GetEnd(), b->GetEnd(), "" );
203
204 // Arc center is calculated and so may have round-off errors when parents are
205 // differentially rotated.
206 if( ( a->GetArcMid() - b->GetArcMid() ).EuclideanNorm() > pcbIUScale.mmToIU( 0.0005 ) )
207 return true;
208
209 break;
210
211 case SHAPE_T::BEZIER:
212 TEST_PT( a->GetStart(), b->GetStart(), "" );
213 TEST_PT( a->GetEnd(), b->GetEnd(), "" );
214 TEST_PT( a->GetBezierC1(), b->GetBezierC1(), "" );
215 TEST_PT( a->GetBezierC2(), b->GetBezierC2(), "" );
216 break;
217
218 case SHAPE_T::POLY:
219 {
220 TEST( a->GetPolyShape().TotalVertices(), b->GetPolyShape().TotalVertices(), "" );
221
222 for( int poly = 0; poly < static_cast<int>( a->GetPolyShape().CPolygons().size() ); poly++ )
223 {
224 const SHAPE_POLY_SET::POLYGON aPolygon = a->GetPolyShape().CPolygon( poly );
225 const SHAPE_POLY_SET::POLYGON bPolygon = b->GetPolyShape().CPolygon( poly );
226
227 if( aPolygon.size() == 0 || bPolygon.size() == 0
228 || !aPolygon[0].CompareGeometry( bPolygon[0], true, EPSILON ) )
229 {
230 diff = true;
231 return diff;
232 }
233 }
234
235 break;
236 }
237
238 default:
239 UNIMPLEMENTED_FOR( a->SHAPE_T_asString() );
240 }
241
242 TEST( a->GetStroke(), b->GetStroke(), "" );
243 TEST( a->GetFillMode(), b->GetFillMode(), "" );
244
245 return diff;
246}
247
248
249bool padHasOverrides( const PAD* a, const PAD* b, REPORTER& aReporter )
250{
251 bool diff = false;
252
253#define REPORT_MSG( s, p ) aReporter.Report( wxString::Format( s, p ) )
254
255 if( a->GetLocalClearance().has_value() && a->GetLocalClearance() != b->GetLocalClearance() )
256 {
257 diff = true;
258 REPORT_MSG( _( "%s has clearance override." ), PAD_DESC( a ) );
259 }
260
261 if( a->GetLocalSolderMaskMargin().has_value()
263 {
264 diff = true;
265 REPORT_MSG( _( "%s has solder mask expansion override." ), PAD_DESC( a ) );
266 }
267
268
269 if( a->GetLocalSolderPasteMargin().has_value()
271 {
272 diff = true;
273 REPORT_MSG( _( "%s has solder paste clearance override." ), PAD_DESC( a ) );
274 }
275
278 {
279 diff = true;
280 REPORT_MSG( _( "%s has solder paste clearance override." ), PAD_DESC( a ) );
281 }
282
285 {
286 diff = true;
287 REPORT_MSG( _( "%s has zone connection override." ), PAD_DESC( a ) );
288 }
289
290 if( a->GetLocalThermalGapOverride().has_value()
291 && a->GetThermalGap() != b->GetThermalGap() )
292 {
293 diff = true;
294 REPORT_MSG( _( "%s has thermal relief gap override." ), PAD_DESC( a ) );
295 }
296
297 if( a->GetLocalThermalSpokeWidthOverride().has_value()
299 {
300 diff = true;
301 REPORT_MSG( _( "%s has thermal relief spoke width override." ), PAD_DESC( a ) );
302 }
303
305 {
306 diff = true;
307 REPORT_MSG( _( "%s has thermal relief spoke angle override." ), PAD_DESC( a ) );
308 }
309
311 {
312 diff = true;
313 REPORT_MSG( _( "%s has zone knockout setting override." ), PAD_DESC( a ) );
314 }
315
316 return diff;
317}
318
319
320bool padNeedsUpdate( const PAD* a, const PAD* bLib, REPORTER* aReporter )
321{
322 bool diff = false;
323
325 wxString::Format( _( "%s pad to die length differs." ), PAD_DESC( a ) ) );
327 wxString::Format( _( "%s position differs." ), PAD_DESC( a ) ) );
328
329 TEST( a->GetNumber(), bLib->GetNumber(),
330 wxString::Format( _( "%s has different numbers." ), PAD_DESC( a ) ) );
331
332 // These are assigned from the schematic and not from the library
333 // TEST( a->GetPinFunction(), b->GetPinFunction() );
334 // TEST( a->GetPinType(), b->GetPinType() );
335
336 bool layerSettingsDiffer = a->GetRemoveUnconnected() != bLib->GetRemoveUnconnected();
337
338 // NB: KeepTopBottom is undefined if RemoveUnconnected is NOT set.
339 if( a->GetRemoveUnconnected() )
340 layerSettingsDiffer |= a->GetKeepTopBottom() != bLib->GetKeepTopBottom();
341
342 bool allowExpansion = false;
343 if( const FOOTPRINT* fp = a->GetParentFootprint() )
344 allowExpansion = ( fp->GetStackupMode() == FOOTPRINT_STACKUP::EXPAND_INNER_LAYERS );
345
346 if( layerSettingsDiffer
349 allowExpansion ) )
350 {
351 diff = true;
352
353 if( aReporter )
354 aReporter->Report( wxString::Format( _( "%s layers differ." ), PAD_DESC( a ) ) );
355 else
356 return true;
357 }
358
359 TEST( a->GetAttribute(), bLib->GetAttribute(),
360 wxString::Format( _( "%s pad type differs." ), PAD_DESC( a ) ) );
361 TEST( a->GetProperty(), bLib->GetProperty(),
362 wxString::Format( _( "%s fabrication property differs." ), PAD_DESC( a ) ) );
363
364 // The pad orientation, for historical reasons is the pad rotation + parent rotation.
367 wxString::Format( _( "%s orientation differs." ), PAD_DESC( a ) ) );
368
369 std::vector<PCB_LAYER_ID> layers = a->Padstack().UniqueLayers();
370 const BOARD* board = a->GetBoard();
371 wxString layerName;
372
373 for( PCB_LAYER_ID layer : layers )
374 {
375 layerName = board ? board->GetLayerName( layer ) : LayerName( layer );
376
377 TEST( a->GetShape( layer ), bLib->GetShape( layer ),
378 wxString::Format( _( "%s pad shape type differs on layer %s." ),
379 PAD_DESC( a ),
380 layerName ) );
381
382 TEST( a->GetSize( layer ), bLib->GetSize( layer ),
383 wxString::Format( _( "%s size differs on layer %s." ),
384 PAD_DESC( a ),
385 layerName ) );
386
387 TEST( a->GetDelta( layer ), bLib->GetDelta( layer ),
388 wxString::Format( _( "%s trapezoid delta differs on layer %s." ),
389 PAD_DESC( a ),
390 layerName ) );
391
392 if( a->GetShape( layer ) == PAD_SHAPE::ROUNDRECT || a->GetShape( layer ) == PAD_SHAPE::CHAMFERED_RECT)
393 {
394 TEST_D( a->GetRoundRectRadiusRatio( layer ),
395 bLib->GetRoundRectRadiusRatio( layer ),
396 wxString::Format( _( "%s rounded corners differ on layer %s." ),
397 PAD_DESC( a ),
398 layerName ) );
399 }
400
401 if( a->GetShape( layer ) == PAD_SHAPE::CHAMFERED_RECT)
402 {
403 TEST_D( a->GetChamferRectRatio( layer ),
404 bLib->GetChamferRectRatio( layer ),
405 wxString::Format( _( "%s chamfered corner sizes differ on layer %s." ),
406 PAD_DESC( a ),
407 layerName ) );
408
409 TEST( a->GetChamferPositions( layer ),
410 bLib->GetChamferPositions( layer ),
411 wxString::Format( _( "%s chamfered corners differ on layer %s." ),
412 PAD_DESC( a ),
413 layerName ) );
414 }
415
416 TEST_PT( a->GetOffset( layer ), bLib->GetOffset( layer ),
417 wxString::Format( _( "%s shape offset from hole differs on layer %s." ),
418 PAD_DESC( a ),
419 layerName ) );
420 }
421
422 TEST( a->GetDrillShape(), bLib->GetDrillShape(),
423 wxString::Format( _( "%s drill shape differs." ), PAD_DESC( a ) ) );
424 TEST( a->GetDrillSize(), bLib->GetDrillSize(),
425 wxString::Format( _( "%s drill size differs." ), PAD_DESC( a ) ) );
426
427 // Clearance and zone connection overrides are as likely to be set at the board level as in
428 // the library.
429 //
430 // If we ignore them and someone *does* change one of them in the library, then stale
431 // footprints won't be caught.
432 //
433 // On the other hand, if we report them then boards that override at the board level are
434 // going to be VERY noisy.
435 //
436 // So we just do it when we have a reporter.
437 if( aReporter && padHasOverrides( a, bLib, *aReporter ) )
438 diff = true;
439
440 bool primitivesDiffer = false;
441 PCB_LAYER_ID firstDifferingLayer = UNDEFINED_LAYER;
442
444 [&]( PCB_LAYER_ID aLayer )
445 {
446 if( a->GetPrimitives( aLayer ).size() != bLib->GetPrimitives( aLayer ).size() )
447 {
448 primitivesDiffer = true;
449 }
450 else
451 {
452 for( size_t ii = 0; ii < a->GetPrimitives( aLayer ).size(); ++ii )
453 {
454 if( primitiveNeedsUpdate( a->GetPrimitives( aLayer )[ii],
455 bLib->GetPrimitives( aLayer )[ii] ) )
456 {
457 primitivesDiffer = true;
458 break;
459 }
460 }
461 }
462
463 if( primitivesDiffer && firstDifferingLayer == UNDEFINED_LAYER )
464 firstDifferingLayer = aLayer;
465 } );
466
467
468 if( primitivesDiffer )
469 {
470 diff = true;
471 layerName = board ? board->GetLayerName( firstDifferingLayer )
472 : LayerName( firstDifferingLayer );
473
474 if( aReporter )
475 {
476 aReporter->Report( wxString::Format( _( "%s shape primitives differ on layer %s." ),
477 PAD_DESC( a ),
478 layerName ) );
479 }
480 else
481 {
482 return true;
483 }
484 }
485
486 return diff;
487}
488
489
490bool barcodeNeedsUpdate( const PCB_BARCODE& curr_barcode, const PCB_BARCODE& ref_barcode )
491{
492 REPORTER* aReporter = nullptr;
493 bool diff = false;
494
495 TEST( curr_barcode.GetText(), ref_barcode.GetText(),
496 wxString::Format( _( "%s text differs." ), ITEM_DESC( &curr_barcode ) ) );
497
498 TEST_PT( curr_barcode.GetPosition(), ref_barcode.GetPosition(),
499 wxString::Format( _( "%s position differs." ), ITEM_DESC( &curr_barcode ) ) );
500
501 TEST( curr_barcode.GetWidth(), ref_barcode.GetWidth(),
502 wxString::Format( _( "%s width differs." ), ITEM_DESC( &curr_barcode ) ) );
503 TEST( curr_barcode.GetHeight(), ref_barcode.GetHeight(),
504 wxString::Format( _( "%s height differs." ), ITEM_DESC( &curr_barcode ) ) );
505
506 TEST( curr_barcode.GetTextSize(), ref_barcode.GetTextSize(),
507 wxString::Format( _( "%s text size differs." ), ITEM_DESC( &curr_barcode ) ) );
508
509 TEST( (int) curr_barcode.GetKind(), (int) ref_barcode.GetKind(),
510 wxString::Format( _( "%s code differs." ), ITEM_DESC( &curr_barcode ) ) );
511 TEST( (int) curr_barcode.GetErrorCorrection(), (int) ref_barcode.GetErrorCorrection(),
512 wxString::Format( _( "%s error correction level differs." ), ITEM_DESC( &curr_barcode ) ) );
513
514 return diff;
515}
516
517
518bool shapeNeedsUpdate( const PCB_SHAPE& curr_shape, const PCB_SHAPE& ref_shape )
519{
520 // curr_shape and ref_shape are expected to be normalized, for a more reliable test.
521 REPORTER* aReporter = nullptr;
522 bool diff = false;
523
524 TEST( curr_shape.GetShape(), ref_shape.GetShape(), "" );
525
526 switch( curr_shape.GetShape() )
527 {
529 {
530 BOX2I aRect( curr_shape.GetStart(), curr_shape.GetEnd() - curr_shape.GetStart() );
531 BOX2I bRect( ref_shape.GetStart(), ref_shape.GetEnd() - ref_shape.GetStart() );
532
533 aRect.Normalize();
534 bRect.Normalize();
535
536 TEST_PT( aRect.GetOrigin(), bRect.GetOrigin(), "" );
537 TEST_PT( aRect.GetEnd(), bRect.GetEnd(), "" );
538 break;
539 }
540
541 case SHAPE_T::SEGMENT:
542 case SHAPE_T::CIRCLE:
543 TEST_PT( curr_shape.GetStart(), ref_shape.GetStart(), "" );
544 TEST_PT( curr_shape.GetEnd(), ref_shape.GetEnd(), "" );
545 break;
546
547 case SHAPE_T::ARC:
548 TEST_PT( curr_shape.GetStart(), ref_shape.GetStart(), "" );
549 TEST_PT( curr_shape.GetEnd(), ref_shape.GetEnd(), "" );
550
551 // Arc center is calculated and so may have round-off errors when parents are
552 // differentially rotated.
553 if( ( curr_shape.GetArcMid() - ref_shape.GetArcMid() ).EuclideanNorm() > pcbIUScale.mmToIU( 0.0005 ) )
554 return true;
555
556 break;
557
558 case SHAPE_T::BEZIER:
559 TEST_PT( curr_shape.GetStart(), ref_shape.GetStart(), "" );
560 TEST_PT( curr_shape.GetEnd(), ref_shape.GetEnd(), "" );
561 TEST_PT( curr_shape.GetBezierC1(), ref_shape.GetBezierC1(), "" );
562 TEST_PT( curr_shape.GetBezierC2(), ref_shape.GetBezierC2(), "" );
563 break;
564
565 case SHAPE_T::POLY:
566 {
567 TEST( curr_shape.GetPolyShape().TotalVertices(), ref_shape.GetPolyShape().TotalVertices(), "" );
568
569 for( int poly = 0; poly < static_cast<int>( curr_shape.GetPolyShape().CPolygons().size() ); poly++ )
570 {
571 const SHAPE_POLY_SET::POLYGON curr_polygon = curr_shape.GetPolyShape().CPolygon( poly );
572 const SHAPE_POLY_SET::POLYGON ref_polygon = ref_shape.GetPolyShape().CPolygon( poly );
573 if( curr_polygon.size() == 0 || ref_polygon.size() == 0
574 || !curr_polygon[0].CompareGeometry( ref_polygon[0], true, EPSILON ) )
575 {
576 diff = true;
577 return diff;
578 }
579 }
580 break;
581 }
582
583 default:
584 UNIMPLEMENTED_FOR( curr_shape.SHAPE_T_asString() );
585 }
586
587 if( curr_shape.IsOnCopperLayer() )
588 TEST( curr_shape.GetStroke(), ref_shape.GetStroke(), "" );
589
590 TEST( curr_shape.GetFillMode(), ref_shape.GetFillMode(), "" );
591
592 TEST( curr_shape.GetLayer(), ref_shape.GetLayer(), "" );
593
594 return diff;
595}
596
597
598bool zoneNeedsUpdate( const ZONE* a, const ZONE* b, REPORTER* aReporter )
599{
600 bool diff = false;
601
603 wxString::Format( _( "%s corner smoothing setting differs." ), ITEM_DESC( a ) ) );
605 wxString::Format( _( "%s corner smoothing radius differs." ), ITEM_DESC( a ) ) );
606 TEST( a->GetZoneName(), b->GetZoneName(),
607 wxString::Format( _( "%s name differs." ), ITEM_DESC( a ) ) );
609 wxString::Format( _( "%s priority differs." ), ITEM_DESC( a ) ) );
610
611 TEST( a->GetIsRuleArea(), b->GetIsRuleArea(),
612 wxString::Format( _( "%s keep-out property differs." ), ITEM_DESC( a ) ) );
614 wxString::Format( _( "%s keep out zone fill setting differs." ), ITEM_DESC( a ) ) );
616 wxString::Format( _( "%s keep out footprints setting differs." ), ITEM_DESC( a ) ) );
618 wxString::Format( _( "%s keep out pads setting differs." ), ITEM_DESC( a ) ) );
620 wxString::Format( _( "%s keep out tracks setting differs." ), ITEM_DESC( a ) ) );
622 wxString::Format( _( "%s keep out vias setting differs." ), ITEM_DESC( a ) ) );
623
624 // In1_Cu is used to indicate whether inner layer expansion is allowed on rulesets
625 // Kind of annoying footprint pads use both in1_cu and have an stackup mode setting
626 bool innerLayerExpansionAllowed = b->GetLayerSet().Contains( In1_Cu );
627
630 innerLayerExpansionAllowed ) )
631 {
632 diff = true;
633
634 if( aReporter )
635 {
636 aReporter->Report( wxString::Format( _( "%s layers differ." ), ITEM_DESC( a ) ) );
637 }
638 else
639 {
640 return true;
641 }
642 }
643
645 wxString::Format( _( "%s pad connection property differs." ), ITEM_DESC( a ) ) );
647 wxString::Format( _( "%s local clearance differs." ), ITEM_DESC( a ) ) );
649 wxString::Format( _( "%s thermal relief gap differs." ), ITEM_DESC( a ) ) );
651 wxString::Format( _( "%s thermal relief spoke width differs." ), ITEM_DESC( a ) ) );
652
654 wxString::Format( _( "%s min thickness differs." ), ITEM_DESC( a ) ) );
655
657 wxString::Format( _( "%s remove islands setting differs." ), ITEM_DESC( a ) ) );
659 wxString::Format( _( "%s minimum island size setting differs." ), ITEM_DESC( a ) ) );
660
661 TEST( a->GetFillMode(), b->GetFillMode(),
662 wxString::Format( _( "%s fill type differs." ), ITEM_DESC( a ) ) );
664 wxString::Format( _( "%s hatch width differs." ), ITEM_DESC( a ) ) );
665 TEST( a->GetHatchGap(), b->GetHatchGap(),
666 wxString::Format( _( "%s hatch gap differs." ), ITEM_DESC( a ) ) );
668 wxString::Format( _( "%s hatch orientation differs." ), ITEM_DESC( a ) ) );
670 wxString::Format( _( "%s hatch smoothing level differs." ), ITEM_DESC( a ) ) );
672 wxString::Format( _( "%s hatch smoothing amount differs." ), ITEM_DESC( a ) ) );
674 wxString::Format( _( "%s minimum hatch hole setting differs." ), ITEM_DESC( a ) ) );
675
676 // This is just a display property
677 // TEST( a->GetHatchBorderAlgorithm(), b->GetHatchBorderAlgorithm() );
678
680 wxString::Format( _( "%s outline corner count differs." ), ITEM_DESC( a ) ) );
681
682 bool cornersDiffer = false;
683
684 for( int poly = 0; poly < static_cast<int>( a->Outline()->CPolygons().size() ); poly++ )
685 {
686 const SHAPE_POLY_SET::POLYGON aPolygon = a->Outline()->CPolygon( poly );
687 const SHAPE_POLY_SET::POLYGON bPolygon = b->Outline()->CPolygon( poly );
688
689 if( aPolygon.size() == 0 || bPolygon.size() == 0
690 || !aPolygon[0].CompareGeometry( bPolygon[0], true, EPSILON ) )
691 {
692 diff = true;
693 cornersDiffer = true;
694 break;
695 }
696 }
697
698 if( cornersDiffer && aReporter )
699 aReporter->Report( wxString::Format( _( "%s corners differ." ), ITEM_DESC( a ) ) );
700
701 return diff;
702}
703
704
710bool stackupNeedsUpdate( const FOOTPRINT& a, const FOOTPRINT& b, REPORTER* aReporter )
711{
712 bool diff = false;
713
715 wxString::Format( _( "Footprint stackup mode differs." ) ) );
716
717 const LSET& aLayers = a.GetLayerSet();
718 const LSET& bLayers = b.GetLayerSet();
719
720 TEST( aLayers, bLayers,
721 wxString::Format( _( "Footprint layers differ." ) ) );
722
723 return diff;
724}
725
726
735bool footprintVsBoardStackup( const FOOTPRINT& aFp, const BOARD& aBoard, REPORTER* aReporter )
736{
738 return false;
739
740 // Filter only layers that can differ between footprint and board
741 const LSET& fpLayers = aFp.GetStackupLayers();
742 const LSET& brdLayers = aBoard.GetEnabledLayers() & ( LSET::AllCuMask() | LSET::UserDefinedLayersMask() );
743
744 bool mismatch = false;
745
746 // Any layer in the FP and not on the board is flagged
747 const LSET onlyInFp = fpLayers & ~brdLayers;
748
749 if( onlyInFp.count() )
750 {
751 mismatch = true;
752 if( aReporter )
753 aReporter->Report( wxString::Format( _( "Footprint has %lu layers not on board: %s" ), onlyInFp.count(),
754 LAYER_UTILS::AccumulateNames( onlyInFp, &aBoard ) ) );
755 }
756
757 // Only look at copper layers here: user layers on the board and not in the FP is normal
758 const LSET cuOnlyInBoard = ( brdLayers & ~fpLayers ) & LSET::AllCuMask();
759
760 if( cuOnlyInBoard.count() )
761 {
762 mismatch = true;
763 if( aReporter )
764 aReporter->Report( wxString::Format( _( "Board has %lu copper layers not in footprint: %s" ),
765 cuOnlyInBoard.count(),
766 LAYER_UTILS::AccumulateNames( cuOnlyInBoard, &aBoard ) ) );
767 }
768
769 return mismatch;
770}
771
772
773bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags,
774 REPORTER* aReporter )
775{
776 wxASSERT( aLibFP );
777 bool diff = false;
778
779 // To avoid issues when comparing the footprint on board and the footprint in library
780 // use the footprint from lib flipped, rotated and at same position as this.
781 // And using the footprint from lib with same changes as this minimize the issues
782 // due to rounding and shape modifications
783
784 std::unique_ptr<FOOTPRINT> temp( static_cast<FOOTPRINT*>( aLibFP->Clone() ) );
785
786 temp->SetParent( GetBoard() ); // Needed to know the copper layer count;
787
788 if( !( aCompareFlags & COMPARE_FLAGS::INSTANCE_TO_INSTANCE ) )
789 {
790 if( IsFlipped() != temp->IsFlipped() )
791 temp->Flip( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
792
793 // Set position first before rotating to minimize rounding errors.
794 if( GetPosition() != temp->GetPosition() )
795 temp->SetPosition( GetPosition() );
796
797 if( GetOrientation() != temp->GetOrientation() )
798 temp->SetOrientation( GetOrientation() );
799 }
800
801 for( BOARD_ITEM* item : temp->GraphicalItems() )
802 item->NormalizeForCompare();
803
804 // This temporary footprint must not have a parent when it goes out of scope because it
805 // must not trigger the IncrementTimestamp call in ~FOOTPRINT.
806 temp->SetParent( nullptr );
807
808 aLibFP = temp.get();
809
810 // These checks don't set off errors, they're just informational
811 footprintVsBoardStackup( *this, *GetBoard(), aReporter );
812
813#define TEST_ATTR( a, b, attr, msg ) TEST( ( a & attr ), ( b & attr ), msg )
814
816 _( "Footprint types differ." ) );
817
819 wxString::Format( _( "'%s' settings differ." ),
820 _( "Allow bridged solder mask apertures between pads" ) ) );
821
822 if( !( aCompareFlags & COMPARE_FLAGS::DRC ) )
823 {
824 // These tests are skipped for DRC: they are presumed to relate to a given design.
826 wxString::Format( _( "'%s' settings differ." ),
827 _( "Not in schematic" ) ) );
828
830 wxString::Format( _( "'%s' settings differ." ),
831 _( "Exclude from position files" ) ) );
832
834 wxString::Format( _( "'%s' settings differ." ),
835 _( "Exclude from bill of materials" ) ) );
836
838 wxString::Format( _( "'%s' settings differ." ),
839 _( "Do not populate" ) ) );
840 }
841
842#define REPORT( msg ) { if( aReporter ) aReporter->Report( msg ); }
843#define CHECKPOINT { if( diff && !aReporter ) return diff; }
844
845 if( stackupNeedsUpdate( *this, *aLibFP, aReporter ) )
846 {
847 diff = true;
848 REPORT( _( "Footprint stackup differs." ) );
849 }
850
851 // Clearance and zone connection overrides are as likely to be set at the board level as in
852 // the library.
853 //
854 // If we ignore them and someone *does* change one of them in the library, then stale
855 // footprints won't be caught.
856 //
857 // On the other hand, if we report them then boards that override at the board level are
858 // going to be VERY noisy.
859 //
860 // For report them as different, but we DON'T generate DRC errors on them.
861 if( !( aCompareFlags & COMPARE_FLAGS::DRC ) )
862 {
863 if( GetLocalClearance().has_value() && GetLocalClearance() != aLibFP->GetLocalClearance() )
864 {
865 diff = true;
866 REPORT( _( "Pad clearance overridden." ) );
867 }
868
869 if( GetLocalSolderMaskMargin().has_value()
871 {
872 diff = true;
873 REPORT( _( "Solder mask expansion overridden." ) );
874 }
875
876
877 if( GetLocalSolderPasteMargin().has_value()
879 {
880 diff = true;
881 REPORT( _( "Solder paste absolute clearance overridden." ) );
882 }
883
886 {
887 diff = true;
888 REPORT( _( "Solder paste relative clearance overridden." ) );
889 }
890
893 {
894 diff = true;
895 REPORT( _( "Zone connection overridden." ) );
896 }
897 }
898
899 TEST( GetNetTiePadGroups().size(), aLibFP->GetNetTiePadGroups().size(),
900 _( "Net tie pad groups differ." ) );
901
902 for( size_t ii = 0; ii < GetNetTiePadGroups().size(); ++ii )
903 {
904 TEST( GetNetTiePadGroups()[ii], aLibFP->GetNetTiePadGroups()[ii],
905 _( "Net tie pad groups differ." ) );
906 }
907
908 // Text items are really problematic. We don't want to test the reference, but after that
909 // it gets messy.
910 //
911 // What about the value? Depends on whether or not it's a singleton part.
912 //
913 // And what about other texts? They might be added only to instances on the board, or even
914 // changed for instances on the board. Or they might want to be tested for equality.
915 //
916 // Currently we punt and ignore all the text items.
917
918 // Drawings and pads are also somewhat problematic as there's no guarantee that they'll be
919 // in the same order in the two footprints. Rather than building some sophisticated hashing
920 // algorithm we use the footprint sorting functions to attempt to sort them in the same
921 // order.
922
923 // However FOOTPRINT::cmp_drawings uses PCB_SHAPE coordinates and other infos, so we have
924 // already normalized graphic items in model footprint from library, so we need to normalize
925 // graphic items in the footprint to test (*this). So normalize them using a copy of this
926 FOOTPRINT dummy( *this );
927 dummy.SetParentGroup( nullptr );
928 dummy.SetParent( nullptr );
929
930 if( BOARD* board = GetBoard() )
931 board->UncacheItemSubtreeById( &dummy );
932
933 for( BOARD_ITEM* item : dummy.GraphicalItems() )
934 item->NormalizeForCompare();
935
936 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aShapes;
937 std::copy_if( dummy.GraphicalItems().begin(), dummy.GraphicalItems().end(),
938 std::inserter( aShapes, aShapes.begin() ),
939 []( BOARD_ITEM* item )
940 {
941 return item->Type() == PCB_SHAPE_T;
942 } );
943
944 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bShapes;
945 std::copy_if( aLibFP->GraphicalItems().begin(), aLibFP->GraphicalItems().end(),
946 std::inserter( bShapes, bShapes.begin() ),
947 []( BOARD_ITEM* item )
948 {
949 return item->Type() == PCB_SHAPE_T;
950 } );
951
952 if( aShapes.size() != bShapes.size() )
953 {
954 diff = true;
955 REPORT( _( "Graphic item count differs." ) );
956 }
957 else
958 {
959 for( auto aIt = aShapes.begin(), bIt = bShapes.begin(); aIt != aShapes.end(); aIt++, bIt++ )
960 {
961 // aShapes and bShapes are the tested footprint PCB_SHAPE and the model PCB_SHAPE.
962 // These shapes are already normalized.
963 PCB_SHAPE* curr_shape = static_cast<PCB_SHAPE*>( *aIt );
964 PCB_SHAPE* test_shape = static_cast<PCB_SHAPE*>( *bIt );
965
966 if( shapeNeedsUpdate( *curr_shape, *test_shape ) )
967 {
968 diff = true;
969 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
970 }
971 }
972 }
973
975
976 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aBarcodes;
977 std::copy_if( dummy.GraphicalItems().begin(), dummy.GraphicalItems().end(),
978 std::inserter( aBarcodes, aBarcodes.begin() ),
979 []( BOARD_ITEM* item )
980 {
981 return item->Type() == PCB_BARCODE_T;
982 } );
983
984 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bBarcodes;
985 std::copy_if( aLibFP->GraphicalItems().begin(), aLibFP->GraphicalItems().end(),
986 std::inserter( bBarcodes, bBarcodes.begin() ),
987 []( BOARD_ITEM* item )
988 {
989 return item->Type() == PCB_BARCODE_T;
990 } );
991
992 if( aBarcodes.size() != bBarcodes.size() )
993 {
994 diff = true;
995 REPORT( _( "Barcode count differs." ) );
996 }
997 else
998 {
999 for( auto aIt = aBarcodes.begin(), bIt = bBarcodes.begin(); aIt != aBarcodes.end(); aIt++, bIt++ )
1000 {
1001 // aBarcodes and bBarcodes are the tested footprint PCB_BARCODE and the model PCB_BARCODE.
1002 // These shapes are already normalized.
1003 PCB_BARCODE* curr_barcode = static_cast<PCB_BARCODE*>( *aIt );
1004 PCB_BARCODE* test_barcode = static_cast<PCB_BARCODE*>( *bIt );
1005
1006 if( barcodeNeedsUpdate( *curr_barcode, *test_barcode ) )
1007 {
1008 diff = true;
1009 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
1010 }
1011 }
1012 }
1013
1014 CHECKPOINT;
1015
1016 std::set<PAD*, FOOTPRINT::cmp_pads> aPads( Pads().begin(), Pads().end() );
1017 std::set<PAD*, FOOTPRINT::cmp_pads> bLibPads( aLibFP->Pads().begin(), aLibFP->Pads().end() );
1018
1019 if( aPads.size() != bLibPads.size() )
1020 {
1021 diff = true;
1022 REPORT( _( "Pad count differs." ) );
1023 }
1024 else
1025 {
1026 for( auto aIt = aPads.begin(), bLibIt = bLibPads.begin(); aIt != aPads.end(); aIt++, bLibIt++ )
1027 {
1028 if( padNeedsUpdate( *aIt, *bLibIt, aReporter ) )
1029 diff = true;
1030 else if( aReporter && padHasOverrides( *aIt, *bLibIt, *aReporter ) )
1031 diff = true;
1032 }
1033 }
1034
1035 CHECKPOINT;
1036
1037 std::set<ZONE*, FOOTPRINT::cmp_zones> aZones( Zones().begin(), Zones().end() );
1038 std::set<ZONE*, FOOTPRINT::cmp_zones> bZones( aLibFP->Zones().begin(), aLibFP->Zones().end() );
1039
1040 if( aZones.size() != bZones.size() )
1041 {
1042 diff = true;
1043 REPORT( _( "Rule area count differs." ) );
1044 }
1045 else
1046 {
1047 for( auto aIt = aZones.begin(), bIt = bZones.begin(); aIt != aZones.end(); aIt++, bIt++ )
1048 diff |= zoneNeedsUpdate( *aIt, *bIt, aReporter );
1049 }
1050
1051 return diff;
1052}
1053
1054
1056{
1057 BOARD* board = m_drcEngine->GetBoard();
1058 PROJECT* project = board->GetProject();
1059
1060 if( !project )
1061 {
1062 REPORT_AUX( _( "No project loaded, skipping library parity tests." ) );
1063 return true; // Continue with other tests
1064 }
1065
1066 if( !reportPhase( _( "Loading footprint library table..." ) ) )
1067 return false; // DRC cancelled
1068
1069 std::map<LIB_ID, std::shared_ptr<FOOTPRINT>> libFootprintCache;
1070
1072 wxString msg;
1073 int ii = 0;
1074 const int progressDelta = 250;
1075
1076 if( !reportPhase( _( "Checking board footprints against library..." ) ) )
1077 return false;
1078
1079 for( FOOTPRINT* footprint : board->Footprints() )
1080 {
1081 if( m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES )
1082 && m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_MISMATCH ) )
1083 {
1084 return true; // Continue with other tests
1085 }
1086
1087 if( !reportProgress( ii++, (int) board->Footprints().size(), progressDelta ) )
1088 return false; // DRC cancelled
1089
1090 LIB_ID fpID = footprint->GetFPID();
1091 wxString libName = fpID.GetLibNickname();
1092 wxString fpName = fpID.GetLibItemName();
1093 LIBRARY_TABLE_ROW* libTableRow = nullptr;
1094
1095 if( libName.IsEmpty() )
1096 {
1097 // Not much we can do here
1098 continue;
1099 }
1100
1101 if( std::optional<LIBRARY_TABLE_ROW*> optRow = adapter->GetRow( libName ); optRow )
1102 libTableRow = *optRow;
1103
1104 if( !libTableRow )
1105 {
1106 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1107 {
1108 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1109 msg.Printf( _( "The current configuration does not include the footprint library '%s'" ),
1110 UnescapeString( libName ) );
1111 drcItem->SetErrorMessage( msg );
1112 drcItem->SetItems( footprint );
1113 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1114 }
1115
1116 continue;
1117 }
1118 else if( !adapter->HasLibrary( libName, true ) )
1119 {
1120 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1121 {
1122 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1123 msg.Printf( _( "The footprint library '%s' is not enabled in the current configuration" ),
1124 UnescapeString( libName ) );
1125 drcItem->SetErrorMessage( msg );
1126 drcItem->SetItems( footprint );
1127 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1128 }
1129
1130 continue;
1131 }
1132 else if( !adapter->IsLibraryLoaded( libName ) )
1133 {
1134 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1135 {
1136 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1137 msg.Printf( _( "The footprint library '%s' was not found at '%s'" ),
1138 UnescapeString( libName ),
1139 LIBRARY_MANAGER::GetFullURI( libTableRow, true ) );
1140 drcItem->SetErrorMessage( msg );
1141 drcItem->SetItems( footprint );
1142 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1143 }
1144
1145 continue;
1146 }
1147
1148 auto cacheIt = libFootprintCache.find( fpID );
1149 std::shared_ptr<FOOTPRINT> libFootprint;
1150
1151 if( cacheIt != libFootprintCache.end() )
1152 {
1153 libFootprint = cacheIt->second;
1154 }
1155 else
1156 {
1157 try
1158 {
1159 libFootprint.reset( adapter->LoadFootprint( libName, fpName, true ) );
1160
1161 if( libFootprint )
1162 libFootprintCache[ fpID ] = libFootprint;
1163 }
1164 catch( const IO_ERROR& )
1165 {
1166 }
1167 }
1168
1169 if( !libFootprint )
1170 {
1171 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1172 {
1173 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1174 msg.Printf( _( "Footprint '%s' not found in library '%s'" ),
1175 fpName,
1176 libName );
1177 drcItem->SetErrorMessage( msg );
1178 drcItem->SetItems( footprint );
1179 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1180 }
1181 }
1182 else if( footprint->FootprintNeedsUpdate( libFootprint.get(), BOARD_ITEM::COMPARE_FLAGS::DRC ) )
1183 {
1184 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_MISMATCH ) )
1185 {
1186 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_MISMATCH );
1187 msg.Printf( _( "Footprint '%s' does not match copy in library '%s'" ),
1188 fpName,
1189 libName );
1190 drcItem->SetErrorMessage( msg );
1191 drcItem->SetItems( footprint );
1192 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1193 }
1194 }
1195 }
1196
1197 return true;
1198}
1199
1200
1201namespace detail
1202{
1204}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
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: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:494
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
VECTOR2I GetFPRelativePosition() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:288
@ INSTANCE_TO_INSTANCE
Definition board_item.h:478
virtual bool IsOnCopperLayer() const
Definition board_item.h:175
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const FOOTPRINTS & Footprints() const
Definition board.h:364
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:737
PROJECT * GetProject() const
Definition board.h:587
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:976
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
constexpr const Vec & GetOrigin() const
Definition box2.h:210
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:407
virtual ~DRC_TEST_PROVIDER_LIBRARY_PARITY()=default
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetName() const override
virtual bool reportPhase(const wxString &aStageName)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
EDA_ANGLE Normalize()
Definition eda_angle.h:229
double AsDegrees() const
Definition eda_angle.h:116
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:271
FILL_T GetFillMode() const
Definition eda_shape.h:154
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:181
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:228
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:186
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:268
VECTOR2I GetArcMid() const
An interface to the global shared library manager that is schematic-specific and linked to one projec...
FOOTPRINT * LoadFootprint(const wxString &aNickname, const wxString &aName, bool aKeepUUID)
Load a FOOTPRINT having aName from the library given by aNickname.
bool AllowSolderMaskBridges() const
Definition footprint.h:443
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:419
EDA_ANGLE GetOrientation() const
Definition footprint.h:350
ZONES & Zones()
Definition footprint.h:332
bool FootprintNeedsUpdate(const FOOTPRINT *aLibFP, int aCompareFlags=0, REPORTER *aReporter=nullptr)
Return true if a board footprint differs from the library version.
std::optional< int > GetLocalSolderPasteMargin() const
Definition footprint.h:412
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::optional< int > GetLocalClearance() const
Definition footprint.h:406
std::deque< PAD * > & Pads()
Definition footprint.h:326
int GetAttributes() const
Definition footprint.h:437
FOOTPRINT(BOARD *parent)
Definition footprint.cpp:84
bool IsFlipped() const
Definition footprint.h:544
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:492
const LSET & GetStackupLayers() const
Definition footprint.h:435
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:415
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:409
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:428
VECTOR2I GetPosition() const override
Definition footprint.h:347
DRAWINGS & GraphicalItems()
Definition footprint.h:329
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
bool IsLibraryLoaded(const wxString &aNickname)
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false)
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:627
static const LSET & ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition lset.cpp:634
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:704
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
std::vector< PCB_LAYER_ID > UniqueLayers() const
Definition pad.h:55
PAD_PROP GetProperty() const
Definition pad.h:566
bool GetRemoveUnconnected() const
Definition pad.h:858
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:377
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:600
const VECTOR2I & GetDrillSize() const
Definition pad.h:317
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
const wxString & GetNumber() const
Definition pad.h:137
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:304
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:750
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:796
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
bool GetKeepTopBottom() const
Definition pad.h:873
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:583
const PADSTACK & Padstack() const
Definition pad.h:333
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.h:329
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:437
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:836
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:593
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
EDA_ANGLE GetFPRelativeOrientation() const
Definition pad.cpp:1467
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:819
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:734
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:611
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:222
int GetThermalGap() const
Definition pad.h:766
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:1859
int GetPadToDieLength() const
Definition pad.h:578
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
VECTOR2I GetPosition() const override
Get the position (center) of the barcode in internal units.
wxString GetText() const
int GetTextSize() const
int GetHeight() const
Get the barcode height (in internal units).
BARCODE_ECC_T GetErrorCorrection() const
BARCODE_T GetKind() const
Returns the type of the barcode (QR, CODE_39, etc.).
int GetWidth() const
Get the barcode width (in internal units).
STROKE_PARAMS GetStroke() const override
Definition pcb_shape.h:91
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:71
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:66
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
int TotalVertices() const
Return total number of vertices stored in the set.
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
const POLYGON & CPolygon(int aIndex) const
const std::vector< POLYGON > & CPolygons() const
Handle a list of polygons defining a copper zone.
Definition zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:716
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:846
bool GetDoNotAllowVias() const
Definition zone.h:727
bool GetDoNotAllowPads() const
Definition zone.h:729
bool GetDoNotAllowTracks() const
Definition zone.h:728
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:738
SHAPE_POLY_SET * Outline()
Definition zone.h:336
long long int GetMinIslandArea() const
Definition zone.h:741
const wxString & GetZoneName() const
Definition zone.h:164
int GetMinThickness() const
Definition zone.h:302
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:299
int GetHatchThickness() const
Definition zone.h:311
double GetHatchHoleMinArea() const
Definition zone.h:326
int GetThermalReliefSpokeWidth() const
Definition zone.h:246
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:317
bool GetDoNotAllowFootprints() const
Definition zone.h:730
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:225
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
int GetHatchGap() const
Definition zone.h:314
double GetHatchSmoothingValue() const
Definition zone.h:323
bool GetDoNotAllowZoneFills() const
Definition zone.h:726
int GetHatchSmoothingLevel() const
Definition zone.h:320
unsigned int GetCornerRadius() const
Definition zone.h:661
int GetCornerSmoothingType() const
Definition zone.h:657
int GetThermalReliefGap() const
Definition zone.h:235
unsigned GetAssignedPriority() const
Definition zone.h:126
@ DRCE_LIB_FOOTPRINT_ISSUES
Definition drc_item.h:83
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:84
#define REPORT_AUX(s)
UNITS_PROVIDER g_unitsProvider(pcbIUScale, EDA_UNITS::MM)
bool footprintVsBoardStackup(const FOOTPRINT &aFp, const BOARD &aBoard, REPORTER *aReporter)
Report board->footprint stackup differences.
#define TEST_PT(a, b, msg)
bool padNeedsUpdate(const PAD *a, const PAD *bLib, REPORTER *aReporter)
#define PAD_DESC(pad)
bool primitiveNeedsUpdate(const std::shared_ptr< PCB_SHAPE > &a, const std::shared_ptr< PCB_SHAPE > &b)
static bool boardLayersMatchWithInnerLayerExpansion(const LSET &aItem, const LSET &bLib, bool aAllowCuExpansion)
#define TEST(a, b, msg)
bool padHasOverrides(const PAD *a, const PAD *b, REPORTER &aReporter)
bool shapeNeedsUpdate(const PCB_SHAPE &curr_shape, const PCB_SHAPE &ref_shape)
bool zoneNeedsUpdate(const ZONE *a, const ZONE *b, REPORTER *aReporter)
bool barcodeNeedsUpdate(const PCB_BARCODE &curr_barcode, const PCB_BARCODE &ref_barcode)
#define TEST_ATTR(a, b, attr, msg)
#define TEST_D(a, b, msg)
bool stackupNeedsUpdate(const FOOTPRINT &a, const FOOTPRINT &b, REPORTER *aReporter)
Compare the stackup related settings of two footprints.
#define CHECKPOINT
LSET getBoardNormalizedLayerSet(const BOARD_ITEM *aLibItem, const BOARD *aBoard)
#define REPORT_MSG(s, p)
#define ITEM_DESC(item)
#define _(s)
#define EPSILON
#define TEST(a, b)
@ SEGMENT
Definition eda_shape.h:47
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:48
@ FP_SMD
Definition footprint.h:85
@ FP_DNP
Definition footprint.h:90
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:86
@ FP_BOARD_ONLY
Definition footprint.h:88
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:87
@ FP_THROUGH_HOLE
Definition footprint.h:84
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:99
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ In1_Cu
Definition layer_ids.h:66
#define REPORT(msg)
#define ITEM_DESC(item)
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
wxString AccumulateNames(const LSEQ &aLayers, const BOARD *aBoard)
Accumulate layer names from a layer set into a comma separated string.
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
BARCODE class definition.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
VECTOR2I end