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 for( BOARD_ITEM* item : dummy.GraphicalItems() )
931 item->NormalizeForCompare();
932
933 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aShapes;
934 std::copy_if( dummy.GraphicalItems().begin(), dummy.GraphicalItems().end(),
935 std::inserter( aShapes, aShapes.begin() ),
936 []( BOARD_ITEM* item )
937 {
938 return item->Type() == PCB_SHAPE_T;
939 } );
940
941 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bShapes;
942 std::copy_if( aLibFP->GraphicalItems().begin(), aLibFP->GraphicalItems().end(),
943 std::inserter( bShapes, bShapes.begin() ),
944 []( BOARD_ITEM* item )
945 {
946 return item->Type() == PCB_SHAPE_T;
947 } );
948
949 if( aShapes.size() != bShapes.size() )
950 {
951 diff = true;
952 REPORT( _( "Graphic item count differs." ) );
953 }
954 else
955 {
956 for( auto aIt = aShapes.begin(), bIt = bShapes.begin(); aIt != aShapes.end(); aIt++, bIt++ )
957 {
958 // aShapes and bShapes are the tested footprint PCB_SHAPE and the model PCB_SHAPE.
959 // These shapes are already normalized.
960 PCB_SHAPE* curr_shape = static_cast<PCB_SHAPE*>( *aIt );
961 PCB_SHAPE* test_shape = static_cast<PCB_SHAPE*>( *bIt );
962
963 if( shapeNeedsUpdate( *curr_shape, *test_shape ) )
964 {
965 diff = true;
966 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
967 }
968 }
969 }
970
972
973 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aBarcodes;
974 std::copy_if( dummy.GraphicalItems().begin(), dummy.GraphicalItems().end(),
975 std::inserter( aBarcodes, aBarcodes.begin() ),
976 []( BOARD_ITEM* item )
977 {
978 return item->Type() == PCB_BARCODE_T;
979 } );
980
981 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bBarcodes;
982 std::copy_if( aLibFP->GraphicalItems().begin(), aLibFP->GraphicalItems().end(),
983 std::inserter( bBarcodes, bBarcodes.begin() ),
984 []( BOARD_ITEM* item )
985 {
986 return item->Type() == PCB_BARCODE_T;
987 } );
988
989 if( aBarcodes.size() != bBarcodes.size() )
990 {
991 diff = true;
992 REPORT( _( "Barcode count differs." ) );
993 }
994 else
995 {
996 for( auto aIt = aBarcodes.begin(), bIt = bBarcodes.begin(); aIt != aBarcodes.end(); aIt++, bIt++ )
997 {
998 // aBarcodes and bBarcodes are the tested footprint PCB_BARCODE and the model PCB_BARCODE.
999 // These shapes are already normalized.
1000 PCB_BARCODE* curr_barcode = static_cast<PCB_BARCODE*>( *aIt );
1001 PCB_BARCODE* test_barcode = static_cast<PCB_BARCODE*>( *bIt );
1002
1003 if( barcodeNeedsUpdate( *curr_barcode, *test_barcode ) )
1004 {
1005 diff = true;
1006 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
1007 }
1008 }
1009 }
1010
1011 CHECKPOINT;
1012
1013 std::set<PAD*, FOOTPRINT::cmp_pads> aPads( Pads().begin(), Pads().end() );
1014 std::set<PAD*, FOOTPRINT::cmp_pads> bLibPads( aLibFP->Pads().begin(), aLibFP->Pads().end() );
1015
1016 if( aPads.size() != bLibPads.size() )
1017 {
1018 diff = true;
1019 REPORT( _( "Pad count differs." ) );
1020 }
1021 else
1022 {
1023 for( auto aIt = aPads.begin(), bLibIt = bLibPads.begin(); aIt != aPads.end(); aIt++, bLibIt++ )
1024 {
1025 if( padNeedsUpdate( *aIt, *bLibIt, aReporter ) )
1026 diff = true;
1027 else if( aReporter && padHasOverrides( *aIt, *bLibIt, *aReporter ) )
1028 diff = true;
1029 }
1030 }
1031
1032 CHECKPOINT;
1033
1034 std::set<ZONE*, FOOTPRINT::cmp_zones> aZones( Zones().begin(), Zones().end() );
1035 std::set<ZONE*, FOOTPRINT::cmp_zones> bZones( aLibFP->Zones().begin(), aLibFP->Zones().end() );
1036
1037 if( aZones.size() != bZones.size() )
1038 {
1039 diff = true;
1040 REPORT( _( "Rule area count differs." ) );
1041 }
1042 else
1043 {
1044 for( auto aIt = aZones.begin(), bIt = bZones.begin(); aIt != aZones.end(); aIt++, bIt++ )
1045 diff |= zoneNeedsUpdate( *aIt, *bIt, aReporter );
1046 }
1047
1048 return diff;
1049}
1050
1051
1053{
1054 BOARD* board = m_drcEngine->GetBoard();
1055 PROJECT* project = board->GetProject();
1056
1057 if( !project )
1058 {
1059 REPORT_AUX( _( "No project loaded, skipping library parity tests." ) );
1060 return true; // Continue with other tests
1061 }
1062
1063 if( !reportPhase( _( "Loading footprint library table..." ) ) )
1064 return false; // DRC cancelled
1065
1066 std::map<LIB_ID, std::shared_ptr<FOOTPRINT>> libFootprintCache;
1067
1069 wxString msg;
1070 int ii = 0;
1071 const int progressDelta = 250;
1072
1073 if( !reportPhase( _( "Checking board footprints against library..." ) ) )
1074 return false;
1075
1076 for( FOOTPRINT* footprint : board->Footprints() )
1077 {
1078 if( m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES )
1079 && m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_MISMATCH ) )
1080 {
1081 return true; // Continue with other tests
1082 }
1083
1084 if( !reportProgress( ii++, (int) board->Footprints().size(), progressDelta ) )
1085 return false; // DRC cancelled
1086
1087 LIB_ID fpID = footprint->GetFPID();
1088 wxString libName = fpID.GetLibNickname();
1089 wxString fpName = fpID.GetLibItemName();
1090 LIBRARY_TABLE_ROW* libTableRow = nullptr;
1091
1092 if( libName.IsEmpty() )
1093 {
1094 // Not much we can do here
1095 continue;
1096 }
1097
1098 if( std::optional<LIBRARY_TABLE_ROW*> optRow = adapter->GetRow( libName ); optRow )
1099 libTableRow = *optRow;
1100
1101 if( !libTableRow )
1102 {
1103 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1104 {
1105 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1106 msg.Printf( _( "The current configuration does not include the footprint library '%s'" ),
1107 UnescapeString( libName ) );
1108 drcItem->SetErrorMessage( msg );
1109 drcItem->SetItems( footprint );
1110 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1111 }
1112
1113 continue;
1114 }
1115 else if( !adapter->HasLibrary( libName, true ) )
1116 {
1117 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1118 {
1119 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1120 msg.Printf( _( "The footprint library '%s' is not enabled in the current configuration" ),
1121 UnescapeString( libName ) );
1122 drcItem->SetErrorMessage( msg );
1123 drcItem->SetItems( footprint );
1124 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1125 }
1126
1127 continue;
1128 }
1129 else if( !adapter->IsLibraryLoaded( libName ) )
1130 {
1131 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1132 {
1133 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1134 msg.Printf( _( "The footprint library '%s' was not found at '%s'" ),
1135 UnescapeString( libName ),
1136 LIBRARY_MANAGER::GetFullURI( libTableRow, true ) );
1137 drcItem->SetErrorMessage( msg );
1138 drcItem->SetItems( footprint );
1139 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1140 }
1141
1142 continue;
1143 }
1144
1145 auto cacheIt = libFootprintCache.find( fpID );
1146 std::shared_ptr<FOOTPRINT> libFootprint;
1147
1148 if( cacheIt != libFootprintCache.end() )
1149 {
1150 libFootprint = cacheIt->second;
1151 }
1152 else
1153 {
1154 try
1155 {
1156 libFootprint.reset( adapter->LoadFootprint( libName, fpName, true ) );
1157
1158 if( libFootprint )
1159 libFootprintCache[ fpID ] = libFootprint;
1160 }
1161 catch( const IO_ERROR& )
1162 {
1163 }
1164 }
1165
1166 if( !libFootprint )
1167 {
1168 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1169 {
1170 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1171 msg.Printf( _( "Footprint '%s' not found in library '%s'" ),
1172 fpName,
1173 libName );
1174 drcItem->SetErrorMessage( msg );
1175 drcItem->SetItems( footprint );
1176 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1177 }
1178 }
1179 else if( footprint->FootprintNeedsUpdate( libFootprint.get(), BOARD_ITEM::COMPARE_FLAGS::DRC ) )
1180 {
1181 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_MISMATCH ) )
1182 {
1183 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_MISMATCH );
1184 msg.Printf( _( "Footprint '%s' does not match copy in library '%s'" ),
1185 fpName,
1186 libName );
1187 drcItem->SetErrorMessage( msg );
1188 drcItem->SetItems( footprint );
1189 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1190 }
1191 }
1192 }
1193
1194 return true;
1195}
1196
1197
1198namespace detail
1199{
1201}
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:83
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:85
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:256
@ INSTANCE_TO_INSTANCE
Definition board_item.h:446
virtual bool IsOnCopperLayer() const
Definition board_item.h:155
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
const FOOTPRINTS & Footprints() const
Definition board.h:363
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:727
PROJECT * GetProject() const
Definition board.h:579
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:966
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:400
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:258
FILL_T GetFillMode() const
Definition eda_shape.h:142
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:336
SHAPE_T GetShape() const
Definition eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:173
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:255
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:413
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:389
EDA_ANGLE GetOrientation() const
Definition footprint.h:328
ZONES & Zones()
Definition footprint.h:310
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:382
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::optional< int > GetLocalClearance() const
Definition footprint.h:376
std::deque< PAD * > & Pads()
Definition footprint.h:304
int GetAttributes() const
Definition footprint.h:407
FOOTPRINT(BOARD *parent)
Definition footprint.cpp:77
bool IsFlipped() const
Definition footprint.h:514
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:462
const LSET & GetStackupLayers() const
Definition footprint.h:405
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:385
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:379
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:398
VECTOR2I GetPosition() const override
Definition footprint.h:325
DRAWINGS & GraphicalItems()
Definition footprint.h:307
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) const
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 LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:610
static const LSET & ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition lset.cpp:617
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:582
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:687
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:591
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:856
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:598
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:748
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:794
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
bool GetKeepTopBottom() const
Definition pad.h:871
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:581
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:834
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:591
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:584
EDA_ANGLE GetFPRelativeOrientation() const
Definition pad.cpp:1407
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:817
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:732
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:609
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:222
int GetThermalGap() const
Definition pad.h:764
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:1766
int GetPadToDieLength() const
Definition pad.h:576
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:65
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:701
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:806
bool GetDoNotAllowVias() const
Definition zone.h:712
bool GetDoNotAllowPads() const
Definition zone.h:714
bool GetDoNotAllowTracks() const
Definition zone.h:713
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:723
SHAPE_POLY_SET * Outline()
Definition zone.h:331
long long int GetMinIslandArea() const
Definition zone.h:726
const wxString & GetZoneName() const
Definition zone.h:159
int GetMinThickness() const
Definition zone.h:297
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:294
int GetHatchThickness() const
Definition zone.h:306
double GetHatchHoleMinArea() const
Definition zone.h:321
int GetThermalReliefSpokeWidth() const
Definition zone.h:241
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:312
bool GetDoNotAllowFootprints() const
Definition zone.h:715
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:220
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:136
int GetHatchGap() const
Definition zone.h:309
double GetHatchSmoothingValue() const
Definition zone.h:318
bool GetDoNotAllowZoneFills() const
Definition zone.h:711
int GetHatchSmoothingLevel() const
Definition zone.h:315
unsigned int GetCornerRadius() const
Definition zone.h:646
int GetCornerSmoothingType() const
Definition zone.h:642
int GetThermalReliefGap() const
Definition zone.h:230
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:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
@ FP_SMD
Definition footprint.h:83
@ FP_DNP
Definition footprint.h:88
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:84
@ FP_BOARD_ONLY
Definition footprint.h:86
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:85
@ FP_THROUGH_HOLE
Definition footprint.h:82
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:97
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