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 = aLibOutline.OutlineCount() != bLibOutline.OutlineCount();
712
713 for( int poly = 0; !cornersDiffer && poly < aLibOutline.OutlineCount(); 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() != bPolygon.size() )
719 {
720 cornersDiffer = true;
721 break;
722 }
723
724 for( size_t contour = 0; contour < aPolygon.size() && !cornersDiffer; contour++ )
725 {
726 if( !aPolygon[contour].CompareGeometry( bPolygon[contour], true, EPSILON ) )
727 cornersDiffer = true;
728 }
729 }
730
731 if( cornersDiffer )
732 {
733 diff = true;
734
735 if( aReporter )
736 aReporter->Report( wxString::Format( _( "%s corners differ." ), ITEM_DESC( a ) ) );
737 }
738
739 return diff;
740}
741
742
748bool stackupNeedsUpdate( const FOOTPRINT& a, const FOOTPRINT& b, REPORTER* aReporter )
749{
750 bool diff = false;
751
753 wxString::Format( _( "Footprint stackup mode differs." ) ) );
754
755 const LSET& aLayers = a.GetLayerSet();
756 const LSET& bLayers = b.GetLayerSet();
757
758 TEST( aLayers, bLayers,
759 wxString::Format( _( "Footprint layers differ." ) ) );
760
761 return diff;
762}
763
764
773bool footprintVsBoardStackup( const FOOTPRINT& aFp, const BOARD& aBoard, REPORTER* aReporter )
774{
776 return false;
777
778 // Filter only layers that can differ between footprint and board
779 const LSET& fpLayers = aFp.GetStackupLayers();
780 const LSET& brdLayers = aBoard.GetEnabledLayers() & ( LSET::AllCuMask() | LSET::UserDefinedLayersMask() );
781
782 bool mismatch = false;
783
784 // Any layer in the FP and not on the board is flagged
785 const LSET onlyInFp = fpLayers & ~brdLayers;
786
787 if( onlyInFp.count() )
788 {
789 mismatch = true;
790 if( aReporter )
791 aReporter->Report( wxString::Format( _( "Footprint has %lu layers not on board: %s" ), onlyInFp.count(),
792 LAYER_UTILS::AccumulateNames( onlyInFp, &aBoard ) ) );
793 }
794
795 // Only look at copper layers here: user layers on the board and not in the FP is normal
796 const LSET cuOnlyInBoard = ( brdLayers & ~fpLayers ) & LSET::AllCuMask();
797
798 if( cuOnlyInBoard.count() )
799 {
800 mismatch = true;
801 if( aReporter )
802 aReporter->Report( wxString::Format( _( "Board has %lu copper layers not in footprint: %s" ),
803 cuOnlyInBoard.count(),
804 LAYER_UTILS::AccumulateNames( cuOnlyInBoard, &aBoard ) ) );
805 }
806
807 return mismatch;
808}
809
810
811bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags,
812 REPORTER* aReporter )
813{
814 wxASSERT( aLibFP );
815 bool diff = false;
816
817 std::unique_ptr<FOOTPRINT> temp( static_cast<FOOTPRINT*>( aLibFP->Clone() ) );
818
819 temp->SetParent( GetBoard() );
820
821 if( !( aCompareFlags & COMPARE_FLAGS::INSTANCE_TO_INSTANCE ) && IsFlipped() != temp->IsFlipped() )
822 temp->Flip( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
823
824 for( BOARD_ITEM* item : temp->GraphicalItems() )
825 item->NormalizeForCompare();
826
827 // This temporary footprint must not have a parent when it goes out of scope because it
828 // must not trigger the IncrementTimestamp call in ~FOOTPRINT.
829 temp->SetParent( nullptr );
830
831 aLibFP = temp.get();
832
833 // These checks don't set off errors, they're just informational
834 footprintVsBoardStackup( *this, *GetBoard(), aReporter );
835
836#define TEST_ATTR( a, b, attr, msg ) TEST( ( a & attr ), ( b & attr ), msg )
837
839 _( "Footprint types differ." ) );
840
842 wxString::Format( _( "'%s' settings differ." ),
843 _( "Allow bridged solder mask apertures between pads" ) ) );
844
845 if( !( aCompareFlags & COMPARE_FLAGS::DRC ) )
846 {
847 // These tests are skipped for DRC: they are presumed to relate to a given design.
849 wxString::Format( _( "'%s' settings differ." ),
850 _( "Not in schematic" ) ) );
851
853 wxString::Format( _( "'%s' settings differ." ),
854 _( "Exclude from position files" ) ) );
855
857 wxString::Format( _( "'%s' settings differ." ),
858 _( "Exclude from bill of materials" ) ) );
859
861 wxString::Format( _( "'%s' settings differ." ),
862 _( "Exclude from simulation" ) ) );
863
865 wxString::Format( _( "'%s' settings differ." ),
866 _( "Do not populate" ) ) );
867 }
868
869#define REPORT( msg ) { if( aReporter ) aReporter->Report( msg ); }
870#define CHECKPOINT { if( diff && !aReporter ) return diff; }
871
872 if( stackupNeedsUpdate( *this, *aLibFP, aReporter ) )
873 {
874 diff = true;
875 REPORT( _( "Footprint stackup differs." ) );
876 }
877
878 // Clearance and zone connection overrides are as likely to be set at the board level as in
879 // the library.
880 //
881 // If we ignore them and someone *does* change one of them in the library, then stale
882 // footprints won't be caught.
883 //
884 // On the other hand, if we report them then boards that override at the board level are
885 // going to be VERY noisy.
886 //
887 // For report them as different, but we DON'T generate DRC errors on them.
888 if( !( aCompareFlags & COMPARE_FLAGS::DRC ) )
889 {
890 if( GetLocalClearance().has_value() && GetLocalClearance() != aLibFP->GetLocalClearance() )
891 {
892 diff = true;
893 REPORT( _( "Pad clearance overridden." ) );
894 }
895
896 if( GetLocalSolderMaskMargin().has_value()
898 {
899 diff = true;
900 REPORT( _( "Solder mask expansion overridden." ) );
901 }
902
903
904 if( GetLocalSolderPasteMargin().has_value()
906 {
907 diff = true;
908 REPORT( _( "Solder paste absolute clearance overridden." ) );
909 }
910
913 {
914 diff = true;
915 REPORT( _( "Solder paste relative clearance overridden." ) );
916 }
917
920 {
921 diff = true;
922 REPORT( _( "Zone connection overridden." ) );
923 }
924 }
925
926 TEST( GetNetTiePadGroups().size(), aLibFP->GetNetTiePadGroups().size(),
927 _( "Net tie pad groups differ." ) );
928
929 for( size_t ii = 0; ii < GetNetTiePadGroups().size(); ++ii )
930 {
931 TEST( GetNetTiePadGroups()[ii], aLibFP->GetNetTiePadGroups()[ii],
932 _( "Net tie pad groups differ." ) );
933 }
934
935 // Text items are really problematic. We don't want to test the reference, but after that
936 // it gets messy.
937 //
938 // What about the value? Depends on whether or not it's a singleton part.
939 //
940 // And what about other texts? They might be added only to instances on the board, or even
941 // changed for instances on the board. Or they might want to be tested for equality.
942 //
943 // Currently we punt and ignore all the text items.
944
945 // Drawings and pads are also somewhat problematic as there's no guarantee that they'll be
946 // in the same order in the two footprints. Rather than building some sophisticated hashing
947 // algorithm we use the footprint sorting functions to attempt to sort them in the same
948 // order.
949
950 // However FOOTPRINT::cmp_drawings uses PCB_SHAPE coordinates and other infos, so we have
951 // already normalized graphic items in model footprint from library, so we need to normalize
952 // graphic items in the footprint to test (*this). So normalize them using a copy of this
953 FOOTPRINT dummy( *this );
954 dummy.SetParentGroup( nullptr );
955 dummy.SetParent( nullptr );
956
957 if( BOARD* board = GetBoard() )
958 board->UncacheItemSubtreeById( &dummy );
959
960 for( BOARD_ITEM* item : dummy.GraphicalItems() )
961 item->NormalizeForCompare();
962
963 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aShapes;
964 std::copy_if( dummy.GraphicalItems().begin(), dummy.GraphicalItems().end(),
965 std::inserter( aShapes, aShapes.begin() ),
966 []( BOARD_ITEM* item )
967 {
968 return item->Type() == PCB_SHAPE_T;
969 } );
970
971 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bShapes;
972 std::copy_if( aLibFP->GraphicalItems().begin(), aLibFP->GraphicalItems().end(),
973 std::inserter( bShapes, bShapes.begin() ),
974 []( BOARD_ITEM* item )
975 {
976 return item->Type() == PCB_SHAPE_T;
977 } );
978
979 if( aShapes.size() != bShapes.size() )
980 {
981 diff = true;
982 REPORT( _( "Graphic item count differs." ) );
983 }
984 else
985 {
986 for( auto aIt = aShapes.begin(), bIt = bShapes.begin(); aIt != aShapes.end(); aIt++, bIt++ )
987 {
988 // aShapes and bShapes are the tested footprint PCB_SHAPE and the model PCB_SHAPE.
989 // These shapes are already normalized.
990 PCB_SHAPE* curr_shape = static_cast<PCB_SHAPE*>( *aIt );
991 PCB_SHAPE* test_shape = static_cast<PCB_SHAPE*>( *bIt );
992
993 if( shapeNeedsUpdate( *curr_shape, *test_shape ) )
994 {
995 diff = true;
996 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
997 }
998 }
999 }
1000
1001 CHECKPOINT;
1002
1003 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aBarcodes;
1004 std::copy_if( dummy.GraphicalItems().begin(), dummy.GraphicalItems().end(),
1005 std::inserter( aBarcodes, aBarcodes.begin() ),
1006 []( BOARD_ITEM* item )
1007 {
1008 return item->Type() == PCB_BARCODE_T;
1009 } );
1010
1011 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bBarcodes;
1012 std::copy_if( aLibFP->GraphicalItems().begin(), aLibFP->GraphicalItems().end(),
1013 std::inserter( bBarcodes, bBarcodes.begin() ),
1014 []( BOARD_ITEM* item )
1015 {
1016 return item->Type() == PCB_BARCODE_T;
1017 } );
1018
1019 if( aBarcodes.size() != bBarcodes.size() )
1020 {
1021 diff = true;
1022 REPORT( _( "Barcode count differs." ) );
1023 }
1024 else
1025 {
1026 for( auto aIt = aBarcodes.begin(), bIt = bBarcodes.begin(); aIt != aBarcodes.end(); aIt++, bIt++ )
1027 {
1028 // aBarcodes and bBarcodes are the tested footprint PCB_BARCODE and the model PCB_BARCODE.
1029 // These shapes are already normalized.
1030 PCB_BARCODE* curr_barcode = static_cast<PCB_BARCODE*>( *aIt );
1031 PCB_BARCODE* test_barcode = static_cast<PCB_BARCODE*>( *bIt );
1032
1033 if( barcodeNeedsUpdate( *curr_barcode, *test_barcode ) )
1034 {
1035 diff = true;
1036 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
1037 }
1038 }
1039 }
1040
1041 CHECKPOINT;
1042
1043 std::set<PAD*, FOOTPRINT::cmp_pads> aPads( Pads().begin(), Pads().end() );
1044 std::set<PAD*, FOOTPRINT::cmp_pads> bLibPads( aLibFP->Pads().begin(), aLibFP->Pads().end() );
1045
1046 if( aPads.size() != bLibPads.size() )
1047 {
1048 diff = true;
1049 REPORT( _( "Pad count differs." ) );
1050 }
1051 else
1052 {
1053 for( auto aIt = aPads.begin(), bLibIt = bLibPads.begin(); aIt != aPads.end(); aIt++, bLibIt++ )
1054 {
1055 if( padNeedsUpdate( *aIt, *bLibIt, aReporter ) )
1056 diff = true;
1057 else if( aReporter && padHasOverrides( *aIt, *bLibIt, *aReporter ) )
1058 diff = true;
1059 }
1060 }
1061
1062 CHECKPOINT;
1063
1064 std::set<ZONE*, FOOTPRINT::cmp_zones> aZones( Zones().begin(), Zones().end() );
1065 std::set<ZONE*, FOOTPRINT::cmp_zones> bZones( aLibFP->Zones().begin(), aLibFP->Zones().end() );
1066
1067 if( aZones.size() != bZones.size() )
1068 {
1069 diff = true;
1070 REPORT( _( "Rule area count differs." ) );
1071 }
1072 else
1073 {
1074 for( auto aIt = aZones.begin(), bIt = bZones.begin(); aIt != aZones.end(); aIt++, bIt++ )
1075 diff |= zoneNeedsUpdate( *aIt, *bIt, aReporter );
1076 }
1077
1078 return diff;
1079}
1080
1081
1083{
1084 BOARD* board = m_drcEngine->GetBoard();
1085 PROJECT* project = board->GetProject();
1086
1087 if( !project )
1088 {
1089 REPORT_AUX( _( "No project loaded, skipping library parity tests." ) );
1090 return true; // Continue with other tests
1091 }
1092
1093 if( !reportPhase( _( "Loading footprint library table..." ) ) )
1094 return false; // DRC cancelled
1095
1096 std::map<LIB_ID, std::shared_ptr<FOOTPRINT>> libFootprintCache;
1097
1099 wxString msg;
1100 int ii = 0;
1101 const int progressDelta = 250;
1102
1103 if( !reportPhase( _( "Checking board footprints against library..." ) ) )
1104 return false;
1105
1106 for( FOOTPRINT* footprint : board->Footprints() )
1107 {
1108 if( m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES )
1109 && m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_MISMATCH ) )
1110 {
1111 return true; // Continue with other tests
1112 }
1113
1114 if( !reportProgress( ii++, (int) board->Footprints().size(), progressDelta ) )
1115 return false; // DRC cancelled
1116
1117 LIB_ID fpID = footprint->GetFPID();
1118 wxString libName = fpID.GetLibNickname();
1119 wxString fpName = fpID.GetLibItemName();
1120 LIBRARY_TABLE_ROW* libTableRow = nullptr;
1121
1122 if( libName.IsEmpty() )
1123 {
1124 // Not much we can do here
1125 continue;
1126 }
1127
1128 if( std::optional<LIBRARY_TABLE_ROW*> optRow = adapter->GetRow( libName ); optRow )
1129 libTableRow = *optRow;
1130
1131 if( !libTableRow )
1132 {
1133 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1134 {
1135 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1136 msg.Printf( _( "The current configuration does not include the footprint library '%s'" ),
1137 UnescapeString( libName ) );
1138 drcItem->SetErrorMessage( msg );
1139 drcItem->SetItems( footprint );
1140 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1141 }
1142
1143 continue;
1144 }
1145 else if( !adapter->HasLibrary( libName, true ) )
1146 {
1147 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1148 {
1149 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1150 msg.Printf( _( "The footprint library '%s' is not enabled in the current configuration" ),
1151 UnescapeString( libName ) );
1152 drcItem->SetErrorMessage( msg );
1153 drcItem->SetItems( footprint );
1154 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1155 }
1156
1157 continue;
1158 }
1159 else if( !adapter->IsLibraryLoaded( libName ) )
1160 {
1161 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1162 {
1163 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1164 msg.Printf( _( "The footprint library '%s' was not found at '%s'" ),
1165 UnescapeString( libName ),
1166 LIBRARY_MANAGER::GetFullURI( libTableRow, true ) );
1167 drcItem->SetErrorMessage( msg );
1168 drcItem->SetItems( footprint );
1169 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1170 }
1171
1172 continue;
1173 }
1174
1175 auto cacheIt = libFootprintCache.find( fpID );
1176 std::shared_ptr<FOOTPRINT> libFootprint;
1177
1178 if( cacheIt != libFootprintCache.end() )
1179 {
1180 libFootprint = cacheIt->second;
1181 }
1182 else
1183 {
1184 try
1185 {
1186 libFootprint.reset( adapter->LoadFootprint( libName, fpName, true ) );
1187
1188 if( libFootprint )
1189 libFootprintCache[ fpID ] = libFootprint;
1190 }
1191 catch( const IO_ERROR& )
1192 {
1193 }
1194 }
1195
1196 if( !libFootprint )
1197 {
1198 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) )
1199 {
1200 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
1201 msg.Printf( _( "Footprint '%s' not found in library '%s'" ),
1202 fpName,
1203 libName );
1204 drcItem->SetErrorMessage( msg );
1205 drcItem->SetItems( footprint );
1206 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1207 }
1208 }
1209 else if( footprint->FootprintNeedsUpdate( libFootprint.get(), BOARD_ITEM::COMPARE_FLAGS::DRC ) )
1210 {
1211 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_MISMATCH ) )
1212 {
1213 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_MISMATCH );
1214 msg.Printf( _( "Footprint '%s' does not match copy in library '%s'" ),
1215 fpName,
1216 libName );
1217 drcItem->SetErrorMessage( msg );
1218 drcItem->SetItems( footprint );
1219 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
1220 }
1221 }
1222 }
1223
1224 return true;
1225}
1226
1227
1228namespace detail
1229{
1231}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:86
friend class BOARD
Definition board_item.h:593
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:346
@ INSTANCE_TO_INSTANCE
Definition board_item.h:571
virtual bool IsOnCopperLayer() const
Definition board_item.h:189
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const FOOTPRINTS & Footprints() const
Definition board.h:464
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:943
PROJECT * GetProject() const
Definition board.h:775
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1185
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
constexpr const Vec & GetOrigin() const
Definition box2.h:207
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:444
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:228
double AsDegrees() const
Definition eda_angle.h:115
int GetEllipseMinorRadius() const
Definition eda_shape.h:395
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:377
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:423
FILL_T GetFillMode() const
Definition eda_shape.h:148
int GetEllipseMajorRadius() const
Definition eda_shape.h:386
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:404
SHAPE_T GetShape() const
Definition eda_shape.h:175
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:414
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:560
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:533
ZONES & Zones()
Definition footprint.h:411
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:526
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
std::optional< int > GetLocalClearance() const
Definition footprint.h:520
std::deque< PAD * > & Pads()
Definition footprint.h:405
int GetAttributes() const
Definition footprint.h:551
FOOTPRINT(BOARD *parent)
bool IsFlipped() const
Definition footprint.h:662
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:609
const LSET & GetStackupLayers() const
Definition footprint.h:549
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:529
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:523
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:542
DRAWINGS & GraphicalItems()
Definition footprint.h:408
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:563
bool GetRemoveUnconnected() const
Definition pad.h:874
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:375
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:600
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
const wxString & GetNumber() const
Definition pad.h:143
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:307
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:757
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:838
VECTOR2I GetDrillSize() const
Definition pad.h:320
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:811
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:205
bool GetKeepTopBottom() const
Definition pad.h:889
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:298
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:583
const PADSTACK & Padstack() const
Definition pad.h:331
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:434
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:852
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:593
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
EDA_ANGLE GetFPRelativeOrientation() const
Definition pad.cpp:1769
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:834
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:741
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:611
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:227
int GetThermalGap() const
Definition pad.h:773
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:2196
int GetPadToDieLength() const
Definition pad.h:578
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:230
VECTOR2I GetLibraryStart() const
Definition pcb_shape.h:229
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:63
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
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.
int OutlineCount() const
Return the number of outlines in the set.
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:832
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1062
bool GetDoNotAllowVias() const
Definition zone.h:843
bool GetDoNotAllowPads() const
Definition zone.h:845
bool GetDoNotAllowTracks() const
Definition zone.h:844
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:854
long long int GetMinIslandArea() const
Definition zone.h:857
const wxString & GetZoneName() const
Definition zone.h:160
int GetMinThickness() const
Definition zone.h:315
ZONE_SETTINGS::CORNER_SMOOTHING GetCornerSmoothingType() const
Definition zone.h:771
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:846
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:842
int GetHatchSmoothingLevel() const
Definition zone.h:334
unsigned int GetCornerRadius() const
Definition zone.h:775
SHAPE_POLY_SET GetLibraryOutline() const
Definition zone.cpp:910
int GetThermalReliefGap() const
Definition zone.h:248
unsigned GetAssignedPriority() const
Definition zone.h:122
@ DRCE_LIB_FOOTPRINT_ISSUES
Definition drc_item.h:85
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:86
#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:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
@ FP_SMD
Definition footprint.h:87
@ FP_DNP
Definition footprint.h:92
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:88
@ FP_BOARD_ONLY
Definition footprint.h:90
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:89
@ FP_EXCLUDE_FROM_SIM
Definition footprint.h:93
@ FP_THROUGH_HOLE
Definition footprint.h:86
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:161
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
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
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:59
@ ROUNDRECT
Definition padstack.h:56
BARCODE class definition.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
VECTOR2I end