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