KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_library_parity.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <layer_range.h>
25#include <layer_utils.h>
26#include <kiway.h>
27#include <macros.h>
30#include <board.h>
31#include <pcb_shape.h>
32#include <pcb_barcode.h>
33#include <zone.h>
34#include <footprint.h>
35#include <pad.h>
36#include <drc/drc_engine.h>
37#include <drc/drc_item.h>
39#include <project_pcb.h>
40#include <string_utils.h>
41
42
43/*
44 Library parity test.
45
46 Errors generated:
47 - DRCE_LIB_FOOTPRINT_ISSUES
48 - DRCE_LIB_FOOTPRINT_MISMATCH
49*/
50
52{
53public:
58
60
61 virtual bool Run() override;
62
63 virtual const wxString GetName() const override { return wxT( "library_parity" ); };
64};
65
66
67//
68// The TEST*() macros have two modes:
69// In "Report" mode (aReporter != nullptr) all properties are checked and reported on.
70// In "DRC" mode (aReporter == nulltpr) properties are only checked until a difference is found.
71//
72#define TEST( a, b, msg ) \
73 do { \
74 if( a != b ) \
75 { \
76 diff = true; \
77 \
78 if( aReporter && wxString( msg ).length() ) \
79 aReporter->Report( msg ); \
80 } \
81 \
82 if( diff && !aReporter ) \
83 return diff; \
84 } while (0)
85
86#define EPSILON 10
87#define TEST_PT( a, b, msg ) \
88 do { \
89 if( abs( a.x - b.x ) > EPSILON \
90 || abs( a.y - b.y ) > EPSILON ) \
91 { \
92 diff = true; \
93 \
94 if( aReporter && wxString( msg ).length() ) \
95 aReporter->Report( msg ); \
96 } \
97 \
98 if( diff && !aReporter ) \
99 return diff; \
100 } while (0)
101
102#define EPSILON_D 0.000010
103#define TEST_D( a, b, msg ) \
104 do { \
105 if( abs( a - b ) > EPSILON_D ) \
106 { \
107 diff = true; \
108 \
109 if( aReporter && wxString( msg ).length() ) \
110 aReporter->Report( msg ); \
111 } \
112 \
113 if( diff && !aReporter ) \
114 return diff; \
115 } while (0)
116
117#define ITEM_DESC( item ) ( item )->GetItemDescription( &g_unitsProvider, true )
118#define PAD_DESC( pad ) wxString::Format( _( "Pad %s" ), ( pad )->GetNumber() )
119
120
122
123
124LSET getBoardNormalizedLayerSet( const BOARD_ITEM* aLibItem, const BOARD* aBoard )
125{
126 LSET lset = aLibItem->GetLayerSet();
127
128 if( aBoard )
129 lset &= aBoard->GetEnabledLayers();
130
131 return lset;
132}
133
134
135static bool boardLayersMatchWithInnerLayerExpansion( const LSET& aItem, const LSET& bLib,
136 bool aAllowCuExpansion )
137{
138 if( !aAllowCuExpansion )
139 {
140 return aItem == bLib;
141 }
142
143 // first test non copper layers, these should exact match
144 const LSET nonCuMask = LSET::AllNonCuMask();
145 const LSET aNonCu = aItem & nonCuMask;
146 const LSET bNonCu = bLib & nonCuMask;
147
148 if( aNonCu != bNonCu )
149 return false;
150
151 // top and bottom copper must match exactly
152 if( ( aItem & LSET::ExternalCuMask() ) != ( bLib & LSET::ExternalCuMask() ) )
153 return false;
154
155 // technically we can ignore the inner layers entirely due to aAllowCuExpansion at this point
156 // since its assumed the layers got expanded elsewhere
157 // but it feels weird not to sanity this
158
159 // extract the inner layers to compare prescence
160 const LSET aInner = ( aItem & LSET::AllCuMask() ) & ~( LSET::ExternalCuMask() );
161 const LSET bInner = ( bLib & LSET::AllCuMask() ) & ~( LSET::ExternalCuMask() );
162
163 const LSET missingInnerInA = bInner & ~aInner;
164 if( missingInnerInA.count() )
165 return false;
166
167 return true;
168}
169
170
171bool primitiveNeedsUpdate( const std::shared_ptr<PCB_SHAPE>& a,
172 const std::shared_ptr<PCB_SHAPE>& b )
173{
174 REPORTER* aReporter = nullptr;
175 bool diff = false;
176
177 TEST( a->GetShape(), b->GetShape(), "" );
178
179 switch( a->GetShape() )
180 {
182 {
183 BOX2I aRect( a->GetStart(), a->GetEnd() - a->GetStart() );
184 BOX2I bRect( b->GetStart(), b->GetEnd() - b->GetStart() );
185
186 aRect.Normalize();
187 bRect.Normalize();
188
189 TEST_PT( aRect.GetOrigin(), bRect.GetOrigin(), "" );
190 TEST_PT( aRect.GetEnd(), bRect.GetEnd(), "" );
191 break;
192 }
193
194 case SHAPE_T::SEGMENT:
195 case SHAPE_T::CIRCLE:
196 TEST_PT( a->GetStart(), b->GetStart(), "" );
197 TEST_PT( a->GetEnd(), b->GetEnd(), "" );
198 break;
199
200 case SHAPE_T::ARC:
201 TEST_PT( a->GetStart(), b->GetStart(), "" );
202 TEST_PT( a->GetEnd(), b->GetEnd(), "" );
203
204 // Arc center is calculated and so may have round-off errors when parents are
205 // differentially rotated.
206 if( ( a->GetArcMid() - b->GetArcMid() ).EuclideanNorm() > pcbIUScale.mmToIU( 0.0005 ) )
207 return true;
208
209 break;
210
211 case SHAPE_T::BEZIER:
212 TEST_PT( a->GetStart(), b->GetStart(), "" );
213 TEST_PT( a->GetEnd(), b->GetEnd(), "" );
214 TEST_PT( a->GetBezierC1(), b->GetBezierC1(), "" );
215 TEST_PT( a->GetBezierC2(), b->GetBezierC2(), "" );
216 break;
217
218 case SHAPE_T::ELLIPSE:
220 TEST_PT( a->GetEllipseCenter(), b->GetEllipseCenter(), "" );
221 TEST( a->GetEllipseMajorRadius(), b->GetEllipseMajorRadius(), "" );
222 TEST( a->GetEllipseMinorRadius(), b->GetEllipseMinorRadius(), "" );
223 TEST( a->GetEllipseRotation(), b->GetEllipseRotation(), "" );
224
225 if( a->GetShape() == SHAPE_T::ELLIPSE_ARC )
226 {
227 TEST( a->GetEllipseStartAngle(), b->GetEllipseStartAngle(), "" );
228 TEST( a->GetEllipseEndAngle(), b->GetEllipseEndAngle(), "" );
229 }
230 break;
231
232 case SHAPE_T::POLY:
233 {
234 TEST( a->GetPolyShape().TotalVertices(), b->GetPolyShape().TotalVertices(), "" );
235
236 for( int poly = 0; poly < static_cast<int>( a->GetPolyShape().CPolygons().size() ); poly++ )
237 {
238 const SHAPE_POLY_SET::POLYGON aPolygon = a->GetPolyShape().CPolygon( poly );
239 const SHAPE_POLY_SET::POLYGON bPolygon = b->GetPolyShape().CPolygon( poly );
240
241 if( aPolygon.size() == 0 || bPolygon.size() == 0
242 || !aPolygon[0].CompareGeometry( bPolygon[0], true, EPSILON ) )
243 {
244 diff = true;
245 return diff;
246 }
247 }
248
249 break;
250 }
251
252 default:
253 UNIMPLEMENTED_FOR( a->SHAPE_T_asString() );
254 }
255
256 TEST( a->GetStroke(), b->GetStroke(), "" );
257 TEST( a->GetFillMode(), b->GetFillMode(), "" );
258
259 return diff;
260}
261
262
263bool padHasOverrides( const PAD* a, const PAD* b, REPORTER& aReporter )
264{
265 bool diff = false;
266
267#define REPORT_MSG( s, p ) aReporter.Report( wxString::Format( s, p ) )
268
269 if( a->GetLocalClearance().has_value() && a->GetLocalClearance() != b->GetLocalClearance() )
270 {
271 diff = true;
272 REPORT_MSG( _( "%s has clearance override." ), PAD_DESC( a ) );
273 }
274
275 if( a->GetLocalSolderMaskMargin().has_value()
277 {
278 diff = true;
279 REPORT_MSG( _( "%s has solder mask expansion override." ), PAD_DESC( a ) );
280 }
281
282
283 if( a->GetLocalSolderPasteMargin().has_value()
285 {
286 diff = true;
287 REPORT_MSG( _( "%s has solder paste clearance override." ), PAD_DESC( a ) );
288 }
289
292 {
293 diff = true;
294 REPORT_MSG( _( "%s has solder paste clearance override." ), PAD_DESC( a ) );
295 }
296
299 {
300 diff = true;
301 REPORT_MSG( _( "%s has zone connection override." ), PAD_DESC( a ) );
302 }
303
304 if( a->GetLocalThermalGapOverride().has_value()
305 && a->GetThermalGap() != b->GetThermalGap() )
306 {
307 diff = true;
308 REPORT_MSG( _( "%s has thermal relief gap override." ), PAD_DESC( a ) );
309 }
310
311 if( a->GetLocalThermalSpokeWidthOverride().has_value()
313 {
314 diff = true;
315 REPORT_MSG( _( "%s has thermal relief spoke width override." ), PAD_DESC( a ) );
316 }
317
319 {
320 diff = true;
321 REPORT_MSG( _( "%s has thermal relief spoke angle override." ), PAD_DESC( a ) );
322 }
323
325 {
326 diff = true;
327 REPORT_MSG( _( "%s has zone knockout setting override." ), PAD_DESC( a ) );
328 }
329
330 return diff;
331}
332
333
334bool padNeedsUpdate( const PAD* a, const PAD* bLib, REPORTER* aReporter )
335{
336 bool diff = false;
337
339 wxString::Format( _( "%s pad to die length differs." ), PAD_DESC( a ) ) );
341 wxString::Format( _( "%s position differs." ), PAD_DESC( a ) ) );
342
343 TEST( a->GetNumber(), bLib->GetNumber(),
344 wxString::Format( _( "%s has different numbers." ), PAD_DESC( a ) ) );
345
346 // These are assigned from the schematic and not from the library
347 // TEST( a->GetPinFunction(), b->GetPinFunction() );
348 // TEST( a->GetPinType(), b->GetPinType() );
349
350 bool layerSettingsDiffer = a->GetRemoveUnconnected() != bLib->GetRemoveUnconnected();
351
352 // NB: KeepTopBottom is undefined if RemoveUnconnected is NOT set.
353 if( a->GetRemoveUnconnected() )
354 layerSettingsDiffer |= a->GetKeepTopBottom() != bLib->GetKeepTopBottom();
355
356 bool allowExpansion = false;
357 if( const FOOTPRINT* fp = a->GetParentFootprint() )
358 allowExpansion = ( fp->GetStackupMode() == FOOTPRINT_STACKUP::EXPAND_INNER_LAYERS );
359
360 if( layerSettingsDiffer
363 allowExpansion ) )
364 {
365 diff = true;
366
367 if( aReporter )
368 aReporter->Report( wxString::Format( _( "%s layers differ." ), PAD_DESC( a ) ) );
369 else
370 return true;
371 }
372
373 TEST( a->GetAttribute(), bLib->GetAttribute(),
374 wxString::Format( _( "%s pad type differs." ), PAD_DESC( a ) ) );
375 TEST( a->GetProperty(), bLib->GetProperty(),
376 wxString::Format( _( "%s fabrication property differs." ), PAD_DESC( a ) ) );
377
378 // The pad orientation, for historical reasons is the pad rotation + parent rotation.
381 wxString::Format( _( "%s orientation differs." ), PAD_DESC( a ) ) );
382
383 std::vector<PCB_LAYER_ID> layers = a->Padstack().UniqueLayers();
384 const BOARD* board = a->GetBoard();
385 wxString layerName;
386
387 for( PCB_LAYER_ID layer : layers )
388 {
389 layerName = board ? board->GetLayerName( layer ) : LayerName( layer );
390
391 TEST( a->GetShape( layer ), bLib->GetShape( layer ),
392 wxString::Format( _( "%s pad shape type differs on layer %s." ),
393 PAD_DESC( a ),
394 layerName ) );
395
396 TEST( a->GetSize( layer ), bLib->GetSize( layer ),
397 wxString::Format( _( "%s size differs on layer %s." ),
398 PAD_DESC( a ),
399 layerName ) );
400
401 TEST( a->GetDelta( layer ), bLib->GetDelta( layer ),
402 wxString::Format( _( "%s trapezoid delta differs on layer %s." ),
403 PAD_DESC( a ),
404 layerName ) );
405
406 if( a->GetShape( layer ) == PAD_SHAPE::ROUNDRECT || a->GetShape( layer ) == PAD_SHAPE::CHAMFERED_RECT)
407 {
408 TEST_D( a->GetRoundRectRadiusRatio( layer ),
409 bLib->GetRoundRectRadiusRatio( layer ),
410 wxString::Format( _( "%s rounded corners differ on layer %s." ),
411 PAD_DESC( a ),
412 layerName ) );
413 }
414
415 if( a->GetShape( layer ) == PAD_SHAPE::CHAMFERED_RECT)
416 {
417 TEST_D( a->GetChamferRectRatio( layer ),
418 bLib->GetChamferRectRatio( layer ),
419 wxString::Format( _( "%s chamfered corner sizes differ on layer %s." ),
420 PAD_DESC( a ),
421 layerName ) );
422
423 TEST( a->GetChamferPositions( layer ),
424 bLib->GetChamferPositions( layer ),
425 wxString::Format( _( "%s chamfered corners differ on layer %s." ),
426 PAD_DESC( a ),
427 layerName ) );
428 }
429
430 TEST_PT( a->GetOffset( layer ), bLib->GetOffset( layer ),
431 wxString::Format( _( "%s shape offset from hole differs on layer %s." ),
432 PAD_DESC( a ),
433 layerName ) );
434 }
435
436 TEST( a->GetDrillShape(), bLib->GetDrillShape(),
437 wxString::Format( _( "%s drill shape differs." ), PAD_DESC( a ) ) );
438 TEST( a->GetDrillSize(), bLib->GetDrillSize(),
439 wxString::Format( _( "%s drill size differs." ), PAD_DESC( a ) ) );
440
441 // Clearance and zone connection overrides are as likely to be set at the board level as in
442 // the library.
443 //
444 // If we ignore them and someone *does* change one of them in the library, then stale
445 // footprints won't be caught.
446 //
447 // On the other hand, if we report them then boards that override at the board level are
448 // going to be VERY noisy.
449 //
450 // So we just do it when we have a reporter.
451 if( aReporter && padHasOverrides( a, bLib, *aReporter ) )
452 diff = true;
453
454 bool primitivesDiffer = false;
455 PCB_LAYER_ID firstDifferingLayer = UNDEFINED_LAYER;
456
458 [&]( PCB_LAYER_ID aLayer )
459 {
460 if( a->GetPrimitives( aLayer ).size() != bLib->GetPrimitives( aLayer ).size() )
461 {
462 primitivesDiffer = true;
463 }
464 else
465 {
466 for( size_t ii = 0; ii < a->GetPrimitives( aLayer ).size(); ++ii )
467 {
468 if( primitiveNeedsUpdate( a->GetPrimitives( aLayer )[ii],
469 bLib->GetPrimitives( aLayer )[ii] ) )
470 {
471 primitivesDiffer = true;
472 break;
473 }
474 }
475 }
476
477 if( primitivesDiffer && firstDifferingLayer == UNDEFINED_LAYER )
478 firstDifferingLayer = aLayer;
479 } );
480
481
482 if( primitivesDiffer )
483 {
484 diff = true;
485 layerName = board ? board->GetLayerName( firstDifferingLayer )
486 : LayerName( firstDifferingLayer );
487
488 if( aReporter )
489 {
490 aReporter->Report( wxString::Format( _( "%s shape primitives differ on layer %s." ),
491 PAD_DESC( a ),
492 layerName ) );
493 }
494 else
495 {
496 return true;
497 }
498 }
499
500 return diff;
501}
502
503
504bool barcodeNeedsUpdate( const PCB_BARCODE& curr_barcode, const PCB_BARCODE& ref_barcode )
505{
506 REPORTER* aReporter = nullptr;
507 bool diff = false;
508
509 TEST( curr_barcode.GetText(), ref_barcode.GetText(),
510 wxString::Format( _( "%s text differs." ), ITEM_DESC( &curr_barcode ) ) );
511
512 TEST_PT( curr_barcode.GetPosition(), ref_barcode.GetPosition(),
513 wxString::Format( _( "%s position differs." ), ITEM_DESC( &curr_barcode ) ) );
514
515 TEST( curr_barcode.GetWidth(), ref_barcode.GetWidth(),
516 wxString::Format( _( "%s width differs." ), ITEM_DESC( &curr_barcode ) ) );
517 TEST( curr_barcode.GetHeight(), ref_barcode.GetHeight(),
518 wxString::Format( _( "%s height differs." ), ITEM_DESC( &curr_barcode ) ) );
519
520 TEST( curr_barcode.GetTextSize(), ref_barcode.GetTextSize(),
521 wxString::Format( _( "%s text size differs." ), ITEM_DESC( &curr_barcode ) ) );
522
523 TEST( (int) curr_barcode.GetKind(), (int) ref_barcode.GetKind(),
524 wxString::Format( _( "%s code differs." ), ITEM_DESC( &curr_barcode ) ) );
525 TEST( (int) curr_barcode.GetErrorCorrection(), (int) ref_barcode.GetErrorCorrection(),
526 wxString::Format( _( "%s error correction level differs." ), ITEM_DESC( &curr_barcode ) ) );
527
528 return diff;
529}
530
531
532bool shapeNeedsUpdate( const PCB_SHAPE& curr_shape, const PCB_SHAPE& ref_shape )
533{
534 // curr_shape and ref_shape are expected to be normalized, for a more reliable test.
535 REPORTER* aReporter = nullptr;
536 bool diff = false;
537
538 TEST( curr_shape.GetShape(), ref_shape.GetShape(), "" );
539
540 switch( curr_shape.GetShape() )
541 {
543 {
544 BOX2I aRect( curr_shape.GetStart(), curr_shape.GetEnd() - curr_shape.GetStart() );
545 BOX2I bRect( ref_shape.GetStart(), ref_shape.GetEnd() - ref_shape.GetStart() );
546
547 aRect.Normalize();
548 bRect.Normalize();
549
550 TEST_PT( aRect.GetOrigin(), bRect.GetOrigin(), "" );
551 TEST_PT( aRect.GetEnd(), bRect.GetEnd(), "" );
552 break;
553 }
554
555 case SHAPE_T::SEGMENT:
556 case SHAPE_T::CIRCLE:
557 TEST_PT( curr_shape.GetStart(), ref_shape.GetStart(), "" );
558 TEST_PT( curr_shape.GetEnd(), ref_shape.GetEnd(), "" );
559 break;
560
561 case SHAPE_T::ARC:
562 TEST_PT( curr_shape.GetStart(), ref_shape.GetStart(), "" );
563 TEST_PT( curr_shape.GetEnd(), ref_shape.GetEnd(), "" );
564
565 // Arc center is calculated and so may have round-off errors when parents are
566 // differentially rotated.
567 if( ( curr_shape.GetArcMid() - ref_shape.GetArcMid() ).EuclideanNorm() > pcbIUScale.mmToIU( 0.0005 ) )
568 return true;
569
570 break;
571
572 case SHAPE_T::BEZIER:
573 TEST_PT( curr_shape.GetStart(), ref_shape.GetStart(), "" );
574 TEST_PT( curr_shape.GetEnd(), ref_shape.GetEnd(), "" );
575 TEST_PT( curr_shape.GetBezierC1(), ref_shape.GetBezierC1(), "" );
576 TEST_PT( curr_shape.GetBezierC2(), ref_shape.GetBezierC2(), "" );
577 break;
578
579 case SHAPE_T::ELLIPSE:
581 TEST_PT( curr_shape.GetEllipseCenter(), ref_shape.GetEllipseCenter(), "" );
582 TEST( curr_shape.GetEllipseMajorRadius(), ref_shape.GetEllipseMajorRadius(), "" );
583 TEST( curr_shape.GetEllipseMinorRadius(), ref_shape.GetEllipseMinorRadius(), "" );
584 TEST( curr_shape.GetEllipseRotation(), ref_shape.GetEllipseRotation(), "" );
585
586 if( curr_shape.GetShape() == SHAPE_T::ELLIPSE_ARC )
587 {
588 TEST( curr_shape.GetEllipseStartAngle(), ref_shape.GetEllipseStartAngle(), "" );
589 TEST( curr_shape.GetEllipseEndAngle(), ref_shape.GetEllipseEndAngle(), "" );
590 }
591 break;
592
593 case SHAPE_T::POLY:
594 {
595 TEST( curr_shape.GetPolyShape().TotalVertices(), ref_shape.GetPolyShape().TotalVertices(), "" );
596
597 for( int poly = 0; poly < static_cast<int>( curr_shape.GetPolyShape().CPolygons().size() ); poly++ )
598 {
599 const SHAPE_POLY_SET::POLYGON curr_polygon = curr_shape.GetPolyShape().CPolygon( poly );
600 const SHAPE_POLY_SET::POLYGON ref_polygon = ref_shape.GetPolyShape().CPolygon( poly );
601 if( curr_polygon.size() == 0 || ref_polygon.size() == 0
602 || !curr_polygon[0].CompareGeometry( ref_polygon[0], true, EPSILON ) )
603 {
604 diff = true;
605 return diff;
606 }
607 }
608 break;
609 }
610
611 default:
612 UNIMPLEMENTED_FOR( curr_shape.SHAPE_T_asString() );
613 }
614
615 if( curr_shape.IsOnCopperLayer() )
616 TEST( curr_shape.GetStroke(), ref_shape.GetStroke(), "" );
617
618 TEST( curr_shape.GetFillMode(), ref_shape.GetFillMode(), "" );
619
620 TEST( curr_shape.GetLayer(), ref_shape.GetLayer(), "" );
621
622 return diff;
623}
624
625
626bool zoneNeedsUpdate( const ZONE* a, const ZONE* b, REPORTER* aReporter )
627{
628 bool diff = false;
629
631 wxString::Format( _( "%s corner smoothing setting differs." ), ITEM_DESC( a ) ) );
633 wxString::Format( _( "%s corner smoothing radius differs." ), ITEM_DESC( a ) ) );
634 TEST( a->GetZoneName(), b->GetZoneName(),
635 wxString::Format( _( "%s name differs." ), ITEM_DESC( a ) ) );
637 wxString::Format( _( "%s priority differs." ), ITEM_DESC( a ) ) );
638
639 TEST( a->GetIsRuleArea(), b->GetIsRuleArea(),
640 wxString::Format( _( "%s keep-out property differs." ), ITEM_DESC( a ) ) );
642 wxString::Format( _( "%s keep out zone fill setting differs." ), ITEM_DESC( a ) ) );
644 wxString::Format( _( "%s keep out footprints setting differs." ), ITEM_DESC( a ) ) );
646 wxString::Format( _( "%s keep out pads setting differs." ), ITEM_DESC( a ) ) );
648 wxString::Format( _( "%s keep out tracks setting differs." ), ITEM_DESC( a ) ) );
650 wxString::Format( _( "%s keep out vias setting differs." ), ITEM_DESC( a ) ) );
651
652 // In1_Cu is used to indicate whether inner layer expansion is allowed on rulesets
653 // Kind of annoying footprint pads use both in1_cu and have an stackup mode setting
654 bool innerLayerExpansionAllowed = b->GetLayerSet().Contains( In1_Cu );
655
657 innerLayerExpansionAllowed ) )
658 {
659 diff = true;
660
661 if( aReporter )
662 {
663 aReporter->Report( wxString::Format( _( "%s layers differ." ), ITEM_DESC( a ) ) );
664 }
665 else
666 {
667 return true;
668 }
669 }
670
672 wxString::Format( _( "%s pad connection property differs." ), ITEM_DESC( a ) ) );
674 wxString::Format( _( "%s local clearance differs." ), ITEM_DESC( a ) ) );
676 wxString::Format( _( "%s thermal relief gap differs." ), ITEM_DESC( a ) ) );
678 wxString::Format( _( "%s thermal relief spoke width differs." ), ITEM_DESC( a ) ) );
679
681 wxString::Format( _( "%s min thickness differs." ), ITEM_DESC( a ) ) );
682
684 wxString::Format( _( "%s remove islands setting differs." ), ITEM_DESC( a ) ) );
686 wxString::Format( _( "%s minimum island size setting differs." ), ITEM_DESC( a ) ) );
687
688 TEST( a->GetFillMode(), b->GetFillMode(),
689 wxString::Format( _( "%s fill type differs." ), ITEM_DESC( a ) ) );
691 wxString::Format( _( "%s hatch width differs." ), ITEM_DESC( a ) ) );
692 TEST( a->GetHatchGap(), b->GetHatchGap(),
693 wxString::Format( _( "%s hatch gap differs." ), ITEM_DESC( a ) ) );
695 wxString::Format( _( "%s hatch orientation differs." ), ITEM_DESC( a ) ) );
697 wxString::Format( _( "%s hatch smoothing level differs." ), ITEM_DESC( a ) ) );
699 wxString::Format( _( "%s hatch smoothing amount differs." ), ITEM_DESC( a ) ) );
701 wxString::Format( _( "%s minimum hatch hole setting differs." ), ITEM_DESC( a ) ) );
702
703 // This is just a display property
704 // TEST( a->GetHatchBorderAlgorithm(), b->GetHatchBorderAlgorithm() );
705
707 wxString::Format( _( "%s outline corner count differs." ), ITEM_DESC( a ) ) );
708
709 bool cornersDiffer = false;
710
711 for( int poly = 0; poly < static_cast<int>( a->Outline()->CPolygons().size() ); poly++ )
712 {
713 const SHAPE_POLY_SET::POLYGON aPolygon = a->Outline()->CPolygon( poly );
714 const SHAPE_POLY_SET::POLYGON bPolygon = b->Outline()->CPolygon( poly );
715
716 if( aPolygon.size() == 0 || bPolygon.size() == 0
717 || !aPolygon[0].CompareGeometry( bPolygon[0], true, EPSILON ) )
718 {
719 diff = true;
720 cornersDiffer = true;
721 break;
722 }
723 }
724
725 if( cornersDiffer && aReporter )
726 aReporter->Report( wxString::Format( _( "%s corners differ." ), ITEM_DESC( a ) ) );
727
728 return diff;
729}
730
731
737bool stackupNeedsUpdate( const FOOTPRINT& a, const FOOTPRINT& b, REPORTER* aReporter )
738{
739 bool diff = false;
740
742 wxString::Format( _( "Footprint stackup mode differs." ) ) );
743
744 const LSET& aLayers = a.GetLayerSet();
745 const LSET& bLayers = b.GetLayerSet();
746
747 TEST( aLayers, bLayers,
748 wxString::Format( _( "Footprint layers differ." ) ) );
749
750 return diff;
751}
752
753
762bool footprintVsBoardStackup( const FOOTPRINT& aFp, const BOARD& aBoard, REPORTER* aReporter )
763{
765 return false;
766
767 // Filter only layers that can differ between footprint and board
768 const LSET& fpLayers = aFp.GetStackupLayers();
769 const LSET& brdLayers = aBoard.GetEnabledLayers() & ( LSET::AllCuMask() | LSET::UserDefinedLayersMask() );
770
771 bool mismatch = false;
772
773 // Any layer in the FP and not on the board is flagged
774 const LSET onlyInFp = fpLayers & ~brdLayers;
775
776 if( onlyInFp.count() )
777 {
778 mismatch = true;
779 if( aReporter )
780 aReporter->Report( wxString::Format( _( "Footprint has %lu layers not on board: %s" ), onlyInFp.count(),
781 LAYER_UTILS::AccumulateNames( onlyInFp, &aBoard ) ) );
782 }
783
784 // Only look at copper layers here: user layers on the board and not in the FP is normal
785 const LSET cuOnlyInBoard = ( brdLayers & ~fpLayers ) & LSET::AllCuMask();
786
787 if( cuOnlyInBoard.count() )
788 {
789 mismatch = true;
790 if( aReporter )
791 aReporter->Report( wxString::Format( _( "Board has %lu copper layers not in footprint: %s" ),
792 cuOnlyInBoard.count(),
793 LAYER_UTILS::AccumulateNames( cuOnlyInBoard, &aBoard ) ) );
794 }
795
796 return mismatch;
797}
798
799
800bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags,
801 REPORTER* aReporter )
802{
803 wxASSERT( aLibFP );
804 bool diff = false;
805
806 // To avoid issues when comparing the footprint on board and the footprint in library
807 // use the footprint from lib flipped, rotated and at same position as this.
808 // And using the footprint from lib with same changes as this minimize the issues
809 // due to rounding and shape modifications
810
811 std::unique_ptr<FOOTPRINT> temp( static_cast<FOOTPRINT*>( aLibFP->Clone() ) );
812
813 temp->SetParent( GetBoard() ); // Needed to know the copper layer count;
814
815 if( !( aCompareFlags & COMPARE_FLAGS::INSTANCE_TO_INSTANCE ) )
816 {
817 if( IsFlipped() != temp->IsFlipped() )
818 temp->Flip( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
819
820 // Set position first before rotating to minimize rounding errors.
821 if( GetPosition() != temp->GetPosition() )
822 temp->SetPosition( GetPosition() );
823
824 if( GetOrientation() != temp->GetOrientation() )
825 temp->SetOrientation( GetOrientation() );
826 }
827
828 for( BOARD_ITEM* item : temp->GraphicalItems() )
829 item->NormalizeForCompare();
830
831 // This temporary footprint must not have a parent when it goes out of scope because it
832 // must not trigger the IncrementTimestamp call in ~FOOTPRINT.
833 temp->SetParent( nullptr );
834
835 aLibFP = temp.get();
836
837 // These checks don't set off errors, they're just informational
838 footprintVsBoardStackup( *this, *GetBoard(), aReporter );
839
840#define TEST_ATTR( a, b, attr, msg ) TEST( ( a & attr ), ( b & attr ), msg )
841
843 _( "Footprint types differ." ) );
844
846 wxString::Format( _( "'%s' settings differ." ),
847 _( "Allow bridged solder mask apertures between pads" ) ) );
848
849 if( !( aCompareFlags & COMPARE_FLAGS::DRC ) )
850 {
851 // These tests are skipped for DRC: they are presumed to relate to a given design.
853 wxString::Format( _( "'%s' settings differ." ),
854 _( "Not in schematic" ) ) );
855
857 wxString::Format( _( "'%s' settings differ." ),
858 _( "Exclude from position files" ) ) );
859
861 wxString::Format( _( "'%s' settings differ." ),
862 _( "Exclude from bill of materials" ) ) );
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:125
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:86
friend class BOARD
Definition board_item.h:494
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
VECTOR2I GetFPRelativePosition() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:288
@ INSTANCE_TO_INSTANCE
Definition board_item.h:478
virtual bool IsOnCopperLayer() const
Definition board_item.h:175
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const FOOTPRINTS & Footprints() const
Definition board.h:364
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:745
PROJECT * GetProject() const
Definition board.h:587
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:986
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
constexpr const Vec & GetOrigin() const
Definition box2.h:210
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp: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:306
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:279
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:288
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:334
FILL_T GetFillMode() const
Definition eda_shape.h:162
int GetEllipseMajorRadius() const
Definition eda_shape.h:297
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:315
SHAPE_T GetShape() const
Definition eda_shape.h:189
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:194
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:325
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:276
VECTOR2I GetArcMid() const
An interface to the global shared library manager that is schematic-specific and linked to one projec...
FOOTPRINT * LoadFootprint(const wxString &aNickname, const wxString &aName, bool aKeepUUID)
Load a FOOTPRINT having aName from the library given by aNickname.
bool AllowSolderMaskBridges() const
Definition footprint.h:501
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:477
EDA_ANGLE GetOrientation() const
Definition footprint.h:408
ZONES & Zones()
Definition footprint.h:383
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:470
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::optional< int > GetLocalClearance() const
Definition footprint.h:464
std::deque< PAD * > & Pads()
Definition footprint.h:377
int GetAttributes() const
Definition footprint.h:495
FOOTPRINT(BOARD *parent)
Definition footprint.cpp:84
bool IsFlipped() const
Definition footprint.h:602
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:550
const LSET & GetStackupLayers() const
Definition footprint.h:493
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:473
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:467
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:486
VECTOR2I GetPosition() const override
Definition footprint.h:405
DRAWINGS & GraphicalItems()
Definition footprint.h:380
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
bool IsLibraryLoaded(const wxString &aNickname)
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false)
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:627
static const LSET & ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition lset.cpp:634
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:704
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
std::vector< PCB_LAYER_ID > UniqueLayers() const
Definition pad.h:55
PAD_PROP GetProperty() const
Definition pad.h:566
bool GetRemoveUnconnected() const
Definition pad.h:867
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:377
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:600
const VECTOR2I & GetDrillSize() const
Definition pad.h:317
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
const wxString & GetNumber() const
Definition pad.h:137
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:304
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:750
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:805
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
bool GetKeepTopBottom() const
Definition pad.h:882
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:583
const PADSTACK & Padstack() const
Definition pad.h:333
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.h:329
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:437
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:845
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:593
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
EDA_ANGLE GetFPRelativeOrientation() const
Definition pad.cpp:1467
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:828
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:734
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:611
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:222
int GetThermalGap() const
Definition pad.h:766
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:1877
int GetPadToDieLength() const
Definition pad.h:578
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
VECTOR2I GetPosition() const override
Get the position (center) of the barcode in internal units.
wxString GetText() const
int GetTextSize() const
int GetHeight() const
Get the barcode height (in internal units).
BARCODE_ECC_T GetErrorCorrection() const
BARCODE_T GetKind() const
Returns the type of the barcode (QR, CODE_39, etc.).
int GetWidth() const
Get the barcode width (in internal units).
STROKE_PARAMS GetStroke() const override
Definition pcb_shape.h:97
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:71
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:66
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:75
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:104
int TotalVertices() const
Return total number of vertices stored in the set.
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
const POLYGON & CPolygon(int aIndex) const
const std::vector< POLYGON > & CPolygons() const
Handle a list of polygons defining a copper zone.
Definition zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:802
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:936
bool GetDoNotAllowVias() const
Definition zone.h:813
bool GetDoNotAllowPads() const
Definition zone.h:815
bool GetDoNotAllowTracks() const
Definition zone.h:814
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:824
SHAPE_POLY_SET * Outline()
Definition zone.h:422
long long int GetMinIslandArea() const
Definition zone.h:827
const wxString & GetZoneName() const
Definition zone.h:164
int GetMinThickness() const
Definition zone.h:319
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:316
int GetHatchThickness() const
Definition zone.h:329
double GetHatchHoleMinArea() const
Definition zone.h:344
int GetThermalReliefSpokeWidth() const
Definition zone.h:263
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:335
bool GetDoNotAllowFootprints() const
Definition zone.h:816
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:242
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
int GetHatchGap() const
Definition zone.h:332
double GetHatchSmoothingValue() const
Definition zone.h:341
bool GetDoNotAllowZoneFills() const
Definition zone.h:812
int GetHatchSmoothingLevel() const
Definition zone.h:338
unsigned int GetCornerRadius() const
Definition zone.h:747
int GetCornerSmoothingType() const
Definition zone.h:743
int GetThermalReliefGap() const
Definition zone.h:252
unsigned GetAssignedPriority() const
Definition zone.h:126
@ DRCE_LIB_FOOTPRINT_ISSUES
Definition drc_item.h:83
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:84
#define REPORT_AUX(s)
UNITS_PROVIDER g_unitsProvider(pcbIUScale, EDA_UNITS::MM)
bool footprintVsBoardStackup(const FOOTPRINT &aFp, const BOARD &aBoard, REPORTER *aReporter)
Report board->footprint stackup differences.
#define TEST_PT(a, b, msg)
bool padNeedsUpdate(const PAD *a, const PAD *bLib, REPORTER *aReporter)
#define PAD_DESC(pad)
bool primitiveNeedsUpdate(const std::shared_ptr< PCB_SHAPE > &a, const std::shared_ptr< PCB_SHAPE > &b)
static bool boardLayersMatchWithInnerLayerExpansion(const LSET &aItem, const LSET &bLib, bool aAllowCuExpansion)
#define TEST(a, b, msg)
bool padHasOverrides(const PAD *a, const PAD *b, REPORTER &aReporter)
bool shapeNeedsUpdate(const PCB_SHAPE &curr_shape, const PCB_SHAPE &ref_shape)
bool zoneNeedsUpdate(const ZONE *a, const ZONE *b, REPORTER *aReporter)
bool barcodeNeedsUpdate(const PCB_BARCODE &curr_barcode, const PCB_BARCODE &ref_barcode)
#define TEST_ATTR(a, b, attr, msg)
#define TEST_D(a, b, msg)
bool stackupNeedsUpdate(const FOOTPRINT &a, const FOOTPRINT &b, REPORTER *aReporter)
Compare the stackup related settings of two footprints.
#define CHECKPOINT
LSET getBoardNormalizedLayerSet(const BOARD_ITEM *aLibItem, const BOARD *aBoard)
#define REPORT_MSG(s, p)
#define ITEM_DESC(item)
#define _(s)
#define EPSILON
#define TEST(a, b)
@ ELLIPSE
Definition eda_shape.h:56
@ SEGMENT
Definition eda_shape.h:50
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:51
@ ELLIPSE_ARC
Definition eda_shape.h:57
@ FP_SMD
Definition footprint.h:86
@ FP_DNP
Definition footprint.h:91
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:87
@ FP_BOARD_ONLY
Definition footprint.h:89
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:88
@ FP_THROUGH_HOLE
Definition footprint.h:85
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:150
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ In1_Cu
Definition layer_ids.h:66
#define REPORT(msg)
#define ITEM_DESC(item)
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
wxString AccumulateNames(const LSEQ &aLayers, const BOARD *aBoard)
Accumulate layer names from a layer set into a comma separated string.
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
BARCODE class definition.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
VECTOR2I end