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 (C) 2021-2023 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 <kiway.h>
25#include <macros.h>
27#include <fp_lib_table.h>
28#include <board.h>
29#include <pcb_shape.h>
30#include <zone.h>
31#include <footprint.h>
32#include <pad.h>
33#include <drc/drc_engine.h>
34#include <drc/drc_item.h>
36
37/*
38 Library parity test.
39
40 Errors generated:
41 - DRCE_LIB_FOOTPRINT_ISSUES
42 - DRCE_LIB_FOOTPRINT_MISMATCH
43*/
44
46{
47public:
49 {
50 m_isRuleDriven = false;
51 }
52
54 {
55 }
56
57 virtual bool Run() override;
58
59 virtual const wxString GetName() const override
60 {
61 return wxT( "library_parity" );
62 };
63
64 virtual const wxString GetDescription() const override
65 {
66 return wxT( "Performs board footprint vs library integity checks" );
67 }
68};
69
70
71//
72// The TEST*() macros have two modes:
73// In "Report" mode (aReporter != nullptr) all properties are checked and reported on.
74// In "DRC" mode (aReporter == nulltpr) properties are only checked until a difference is found.
75//
76#define TEST( a, b, msg ) \
77 do { \
78 if( a != b ) \
79 { \
80 diff = true; \
81 \
82 if( aReporter && wxString( msg ).length() ) \
83 aReporter->Report( msg ); \
84 } \
85 \
86 if( diff && !aReporter ) \
87 return diff; \
88 } while (0)
89
90#define EPSILON 0.000001
91#define TEST_D( a, b, msg ) \
92 do { \
93 if( abs( a - b ) > EPSILON ) \
94 { \
95 diff = true; \
96 \
97 if( aReporter && wxString( msg ).length() ) \
98 aReporter->Report( msg ); \
99 } \
100 \
101 if( diff && !aReporter ) \
102 return diff; \
103 } while (0)
104
105#define TEST_V3D( a, b, msg ) \
106 do { \
107 if( abs( a.x - b.x ) > EPSILON \
108 || abs( a.y - b.y ) > EPSILON \
109 || abs( a.z - b.z ) > EPSILON ) \
110 { \
111 diff = true; \
112 \
113 if( aReporter && wxString( msg ).length() ) \
114 aReporter->Report( msg ); \
115 } \
116 \
117 if( diff && !aReporter ) \
118 return diff; \
119 } while (0)
120
121#define ITEM_DESC( item ) ( item )->GetItemDescription( &g_unitsProvider )
122#define PAD_DESC( pad ) wxString::Format( _( "Pad %s" ), ( pad )->GetNumber() )
123
124
126
127
128bool primitiveNeedsUpdate( const std::shared_ptr<PCB_SHAPE>& a,
129 const std::shared_ptr<PCB_SHAPE>& b )
130{
131 REPORTER* aReporter = nullptr;
132 bool diff = false;
133
134 TEST( a->GetShape(), b->GetShape(), "" );
135
136 switch( a->GetShape() )
137 {
139 {
140 BOX2I aRect( a->GetStart(), a->GetEnd() - a->GetStart() );
141 BOX2I bRect( b->GetStart(), b->GetEnd() - b->GetStart() );
142
143 aRect.Normalize();
144 bRect.Normalize();
145
146 TEST( aRect.GetOrigin(), bRect.GetOrigin(), "" );
147 TEST( aRect.GetEnd(), bRect.GetEnd(), "" );
148 break;
149 }
150
151 case SHAPE_T::SEGMENT:
152 case SHAPE_T::CIRCLE:
153 TEST( a->GetStart(), b->GetStart(), "" );
154 TEST( a->GetEnd(), b->GetEnd(), "" );
155 break;
156
157 case SHAPE_T::ARC:
158 TEST( a->GetStart(), b->GetStart(), "" );
159 TEST( a->GetEnd(), b->GetEnd(), "" );
160 TEST( a->GetCenter(), b->GetCenter(), "" );
161 TEST_D( a->GetArcAngle().AsDegrees(), b->GetArcAngle().AsDegrees(), "" );
162 break;
163
164 case SHAPE_T::BEZIER:
165 TEST( a->GetStart(), b->GetStart(), "" );
166 TEST( a->GetEnd(), b->GetEnd(), "" );
167 TEST( a->GetBezierC1(), b->GetBezierC1(), "" );
168 TEST( a->GetBezierC2(), b->GetBezierC2(), "" );
169 break;
170
171 case SHAPE_T::POLY:
172 TEST( a->GetPolyShape().TotalVertices(), b->GetPolyShape().TotalVertices(), "" );
173
174 for( int ii = 0; ii < a->GetPolyShape().TotalVertices(); ++ii )
175 TEST( a->GetPolyShape().CVertex( ii ), b->GetPolyShape().CVertex( ii ), "" );
176
177 break;
178
179 default:
180 UNIMPLEMENTED_FOR( a->SHAPE_T_asString() );
181 }
182
183 TEST( a->GetStroke(), b->GetStroke(), "" );
184 TEST( a->IsFilled(), b->IsFilled(), "" );
185
186 return diff;
187}
188
189
190bool padHasOverrides( const PAD* a, const PAD* b, REPORTER* aReporter )
191{
192 bool diff = false;
193
195 wxString::Format( _( "%s has clearance override." ), PAD_DESC( a ) ) );
197 wxString::Format( _( "%s has solder mask expansion override." ), PAD_DESC( a ) ) );
199 wxString::Format( _( "%s has solder paste clearance override." ), PAD_DESC( a ) ) );
201 wxString::Format( _( "%s has solder paste clearance override." ), PAD_DESC( a ) ) );
202
204 wxString::Format( _( "%s has zone connection override." ), PAD_DESC( a ) ) );
205 TEST( a->GetThermalGap(), b->GetThermalGap(),
206 wxString::Format( _( "%s has thermal relief gap override." ), PAD_DESC( a ) ) );
208 wxString::Format( _( "%s has thermal relief spoke width override." ), PAD_DESC( a ) ) );
210 wxString::Format( _( "%s has thermal relief spoke angle override." ), PAD_DESC( a ) ) );
212 wxString::Format( _( "%s has zone knockout setting override." ), PAD_DESC( a ) ) );
213
214 return diff;
215}
216
217
218bool padNeedsUpdate( const PAD* a, const PAD* b, REPORTER* aReporter )
219{
220 bool diff = false;
221
223 wxString::Format( _( "%s pad to die length differs." ), PAD_DESC( a ) ) );
225 wxString::Format( _( "%s position differs." ), PAD_DESC( a ) ) );
226
227 TEST( a->GetNumber(), b->GetNumber(),
228 wxString::Format( _( "%s has different numbers." ), PAD_DESC( a ) ) );
229
230 // These are assigned from the schematic and not from the library
231 // TEST( a->GetPinFunction(), b->GetPinFunction() );
232 // TEST( a->GetPinType(), b->GetPinType() );
233
234 bool layerSettingsDiffer = a->GetRemoveUnconnected() != b->GetRemoveUnconnected();
235
236 // NB: KeepTopBottom is undefined if RemoveUnconnected is NOT set.
237 if( a->GetRemoveUnconnected() )
238 layerSettingsDiffer |= a->GetKeepTopBottom() != b->GetKeepTopBottom();
239
240 // Trim layersets to the current board before comparing
241 LSET enabledLayers = a->GetBoard()->GetEnabledLayers();
242 LSET aLayers = a->GetLayerSet() & enabledLayers;
243 LSET bLayers = b->GetLayerSet() & enabledLayers;
244
245 if( layerSettingsDiffer || aLayers != bLayers )
246 {
247 diff = true;
248
249 if( aReporter )
250 aReporter->Report( wxString::Format( _( "%s layers differ." ), PAD_DESC( a ) ) );
251 else
252 return true;
253 }
254
255 TEST( a->GetShape(), b->GetShape(),
256 wxString::Format( _( "%s pad shape type differs." ), PAD_DESC( a ) ) );
257
258 TEST( a->GetAttribute(), b->GetAttribute(),
259 wxString::Format( _( "%s pad type differs." ), PAD_DESC( a ) ) );
260 TEST( a->GetProperty(), b->GetProperty(),
261 wxString::Format( _( "%s fabrication property differs." ), PAD_DESC( a ) ) );
262
263 // The pad orientation, for historical reasons is the pad rotation + parent rotation.
264 TEST_D( ( a->GetOrientation() - a->GetParentFootprint()->GetOrientation() ).Normalize().AsDegrees(),
265 ( b->GetOrientation() - b->GetParentFootprint()->GetOrientation() ).Normalize().AsDegrees(),
266 wxString::Format( _( "%s orientation differs." ), PAD_DESC( a ) ) );
267
268 TEST( a->GetSize(), b->GetSize(),
269 wxString::Format( _( "%s size differs." ), PAD_DESC( a ) ) );
270 TEST( a->GetDelta(), b->GetDelta(),
271 wxString::Format( _( "%s trapezoid delta differs." ), PAD_DESC( a ) ) );
272
275 {
276 diff = true;
277
278 if( aReporter )
279 aReporter->Report( wxString::Format( _( "%s rounded corners differ." ), PAD_DESC( a ) ) );
280 else
281 return true;
282 }
283
286 {
287 diff = true;
288
289 if( aReporter )
290 aReporter->Report( wxString::Format( _( "%s chamfered corners differ." ), PAD_DESC( a ) ) );
291 else
292 return true;
293 }
294
295 TEST( a->GetOffset(), b->GetOffset(),
296 wxString::Format( _( "%s shape offset from hole differs." ), PAD_DESC( a ) ) );
297
298 TEST( a->GetDrillShape(), b->GetDrillShape(),
299 wxString::Format( _( "%s drill shape differs." ), PAD_DESC( a ) ) );
300 TEST( a->GetDrillSize(), b->GetDrillSize(),
301 wxString::Format( _( "%s drill size differs." ), PAD_DESC( a ) ) );
302
303 // Clearance and zone connection overrides are as likely to be set at the board level as in
304 // the library.
305 //
306 // If we ignore them and someone *does* change one of them in the library, then stale
307 // footprints won't be caught.
308 //
309 // On the other hand, if we report them then boards that override at the board level are
310 // going to be VERY noisy.
311 //
312 // So we just do it when we have a reporter.
313 if( aReporter && padHasOverrides( a, b, aReporter ) )
314 diff = true;
315
316 bool primitivesDiffer = false;
317
318 if( a->GetPrimitives().size() != b->GetPrimitives().size() )
319 {
320 primitivesDiffer = true;
321 }
322 else
323 {
324 for( size_t ii = 0; ii < a->GetPrimitives().size(); ++ii )
325 {
326 if( primitiveNeedsUpdate( a->GetPrimitives()[ii], b->GetPrimitives()[ii] ) )
327 {
328 primitivesDiffer = true;
329 break;
330 }
331 }
332 }
333
334 if( primitivesDiffer )
335 {
336 diff = true;
337
338 if( aReporter )
339 aReporter->Report( wxString::Format( _( "%s shape primitives differ." ), PAD_DESC( a ) ) );
340 else
341 return true;
342 }
343
344 return diff;
345}
346
347
348bool shapeNeedsUpdate( const PCB_SHAPE* a, const PCB_SHAPE* b )
349{
350 REPORTER* aReporter = nullptr;
351 bool diff = false;
352
353 TEST( a->GetShape(), b->GetShape(), "" );
354
355 switch( a->GetShape() )
356 {
358 {
359 BOX2I aRect( a->GetStart(), a->GetEnd() - a->GetStart() );
360 BOX2I bRect( b->GetStart(), b->GetEnd() - b->GetStart() );
361
362 aRect.Normalize();
363 bRect.Normalize();
364
365 TEST( aRect.GetOrigin(), bRect.GetOrigin(), "" );
366 TEST( aRect.GetEnd(), bRect.GetEnd(), "" );
367 break;
368 }
369
370 case SHAPE_T::SEGMENT:
371 case SHAPE_T::CIRCLE:
372 TEST( a->GetStart(), b->GetStart(), "" );
373 TEST( a->GetEnd(), b->GetEnd(), "" );
374 break;
375
376 case SHAPE_T::ARC:
377 TEST( a->GetStart(), b->GetStart(), "" );
378 TEST( a->GetEnd(), b->GetEnd(), "" );
379
380 // Arc center is calculated and so may have round-off errors when parents are
381 // differentially rotated.
382 if( ( a->GetCenter() - b->GetCenter() ).EuclideanNorm() > pcbIUScale.mmToIU( 0.0001 ) )
383 return true;
384
385 break;
386
387 case SHAPE_T::BEZIER:
388 TEST( a->GetStart(), b->GetStart(), "" );
389 TEST( a->GetEnd(), b->GetEnd(), "" );
390 TEST( a->GetBezierC1(), b->GetBezierC1(), "" );
391 TEST( a->GetBezierC2(), b->GetBezierC2(), "" );
392 break;
393
394 case SHAPE_T::POLY:
396
397 for( int ii = 0; ii < a->GetPolyShape().TotalVertices(); ++ii )
398 TEST( a->GetPolyShape().CVertex( ii ), b->GetPolyShape().CVertex( ii ), "" );
399
400 break;
401
402 default:
404 }
405
406 TEST( a->GetStroke(), b->GetStroke(), "" );
407 TEST( a->IsFilled(), b->IsFilled(), "" );
408
409 TEST( a->GetLayer(), b->GetLayer(), "" );
410
411 return diff;
412}
413
414
415bool textNeedsUpdate( const PCB_TEXT* a, const PCB_TEXT* b )
416{
417 REPORTER* aReporter = nullptr;
418 bool diff = false;
419
420 TEST( a->GetLayer(), b->GetLayer(), "" );
421 TEST( a->IsKeepUpright(), b->IsKeepUpright(), "" );
422
423 TEST( a->GetText(), b->GetText(), "" );
424
425 TEST( a->GetTextThickness(), b->GetTextThickness(), "" );
426 TEST( a->GetTextAngle(), b->GetTextAngle(), "" );
427 TEST( a->IsItalic(), b->IsItalic(), "" );
428 TEST( a->IsBold(), b->IsBold(), "" );
429 TEST( a->IsVisible(), b->IsVisible(), "" );
430 TEST( a->IsMirrored(), b->IsMirrored(), "" );
431
432 TEST( a->GetHorizJustify(), b->GetHorizJustify(), "" );
433 TEST( a->GetVertJustify(), b->GetVertJustify(), "" );
434
435 TEST( a->GetTextSize(), b->GetTextSize(), "" );
437
438 return diff;
439}
440
441
442bool zoneNeedsUpdate( const ZONE* a, const ZONE* b, REPORTER* aReporter )
443{
444 bool diff = false;
445
447 wxString::Format( _( "%s corner smoothing setting differs." ), ITEM_DESC( a ) ) );
449 wxString::Format( _( "%s corner smoothing radius differs." ), ITEM_DESC( a ) ) );
450 TEST( a->GetZoneName(), b->GetZoneName(),
451 wxString::Format( _( "%s name differs." ), ITEM_DESC( a ) ) );
453 wxString::Format( _( "%s priority differs." ), ITEM_DESC( a ) ) );
454
455 TEST( a->GetIsRuleArea(), b->GetIsRuleArea(),
456 wxString::Format( _( "%s keep-out property differs." ), ITEM_DESC( a ) ) );
458 wxString::Format( _( "%s keep out copper fill setting differs." ), ITEM_DESC( a ) ) );
460 wxString::Format( _( "%s keep out footprints setting differs." ), ITEM_DESC( a ) ) );
462 wxString::Format( _( "%s keep out pads setting differs." ), ITEM_DESC( a ) ) );
464 wxString::Format( _( "%s keep out tracks setting differs." ), ITEM_DESC( a ) ) );
466 wxString::Format( _( "%s keep out vias setting differs." ), ITEM_DESC( a ) ) );
467
468 TEST( a->GetLayerSet(), b->GetLayerSet(),
469 wxString::Format( _( "%s layers differ." ), ITEM_DESC( a ) ) );
470
472 wxString::Format( _( "%s pad connection property differs." ), ITEM_DESC( a ) ) );
474 wxString::Format( _( "%s local clearance differs." ), ITEM_DESC( a ) ) );
476 wxString::Format( _( "%s thermal relief gap differs." ), ITEM_DESC( a ) ) );
478 wxString::Format( _( "%s thermal relief spoke width differs." ), ITEM_DESC( a ) ) );
479
481 wxString::Format( _( "%s min thickness differs." ), ITEM_DESC( a ) ) );
482
484 wxString::Format( _( "%s remove islands setting differs." ), ITEM_DESC( a ) ) );
486 wxString::Format( _( "%s minimum island size setting differs." ), ITEM_DESC( a ) ) );
487
488 TEST( a->GetFillMode(), b->GetFillMode(),
489 wxString::Format( _( "%s fill type differs." ), ITEM_DESC( a ) ) );
491 wxString::Format( _( "%s hatch width differs." ), ITEM_DESC( a ) ) );
492 TEST( a->GetHatchGap(), b->GetHatchGap(),
493 wxString::Format( _( "%s hatch gap differs." ), ITEM_DESC( a ) ) );
495 wxString::Format( _( "%s hatch orientation differs." ), ITEM_DESC( a ) ) );
497 wxString::Format( _( "%s hatch smoothing level differs." ), ITEM_DESC( a ) ) );
499 wxString::Format( _( "%s hatch smoothing amount differs." ), ITEM_DESC( a ) ) );
501 wxString::Format( _( "%s minimum hatch hole setting differs." ), ITEM_DESC( a ) ) );
502
503 // This is just a display property
504 // TEST( a->GetHatchBorderAlgorithm(), b->GetHatchBorderAlgorithm() );
505
507 wxString::Format( _( "%s outline corner count differs." ), ITEM_DESC( a ) ) );
508
509 bool cornersDiffer = false;
510
511 for( int ii = 0; ii < a->Outline()->TotalVertices(); ++ii )
512 {
513 if( a->Outline()->CVertex( ii ) != b->Outline()->CVertex( ii ) )
514 {
515 diff = true;
516 cornersDiffer = true;
517 break;
518 }
519 }
520
521 if( cornersDiffer && aReporter )
522 aReporter->Report( wxString::Format( _( "%s corners differ." ), ITEM_DESC( a ) ) );
523
524 return diff;
525}
526
527
528bool modelNeedsUpdate( const FP_3DMODEL& a, const FP_3DMODEL& b, REPORTER* aReporter )
529{
530 bool diff = false;
531
532 TEST_V3D( a.m_Scale, b.m_Scale, _( "3D model scale doesn't match: " ) + a.m_Filename );
533 TEST_V3D( a.m_Rotation, b.m_Rotation, _( "3D model rotation doesn't match: " ) + a.m_Filename );
534 TEST_V3D( a.m_Offset, b.m_Offset, _( "3D model offset doesn't match: " ) + a.m_Filename );
535 TEST( a.m_Opacity, b.m_Opacity, _( "3D model opacity doesn't match: " ) + a.m_Filename );
536 TEST( a.m_Filename, b.m_Filename, _( "3D model doesn't match: " ) + a.m_Filename );
537 TEST( a.m_Show, b.m_Show, _( "3D model visibility doesn't match: " ) + a.m_Filename );
538
539 return diff;
540}
541
542
543bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFootprint, REPORTER* aReporter )
544{
545 UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
546
547 wxASSERT( aLibFootprint );
548 bool diff = false;
549
550 // To avoid issues when comparing the footprint on board and the footprint in library
551 // use a footprint not flipped, not rotated and at position 0,0.
552 // Otherwise one can see differences when comparing coordinates of some items
553 if( IsFlipped() || GetPosition() != VECTOR2I( 0, 0 ) || GetOrientation() != ANGLE_0 )
554 {
555 std::unique_ptr<FOOTPRINT> temp( static_cast<FOOTPRINT*>( Clone() ) );
556 temp->SetParentGroup( nullptr );
557
558 if( IsFlipped() )
559 temp->Flip( {0,0}, false );
560
561 if( GetPosition() != VECTOR2I( 0, 0 ) )
562 temp->SetPosition( { 0, 0 } );
563
564 if( GetOrientation() != ANGLE_0 )
565 temp->SetOrientation( ANGLE_0 );
566
567 for( BOARD_ITEM* item : temp->GraphicalItems() )
568 {
569 if( item->Type() == PCB_SHAPE_T )
570 static_cast<PCB_SHAPE*>( item )->NormalizeRect();
571 }
572
573 diff = temp->FootprintNeedsUpdate( aLibFootprint, aReporter );
574
575 // This temporary footprint must not have a parent when it goes out of scope because it must
576 // not trigger the IncrementTimestamp call in ~FOOTPRINT.
577 temp->SetParent( nullptr );
578 return diff;
579 }
580
581 TEST( GetLibDescription(), aLibFootprint->GetLibDescription(),
582 _( "Footprint descriptions differ." ) );
583 TEST( GetKeywords(), aLibFootprint->GetKeywords(),
584 _( "Footprint keywords differ." ) );
585
586#define TEST_ATTR( a, b, attr, msg ) TEST( ( a & attr ), ( b & attr ), msg )
587
589 _( "Footprint types differ." ) );
591 _( "Allow bridged solder mask apertures between pads settings differ." ) );
593 _( "Exempt from courtyard requirement settings differ." ) );
594
595 // Clearance and zone connection overrides are as likely to be set at the board level as in
596 // the library.
597 //
598 // If we ignore them and someone *does* change one of them in the library, then stale
599 // footprints won't be caught.
600 //
601 // On the other hand, if we report them then boards that override at the board level are
602 // going to be VERY noisy.
603 if( aReporter )
604 {
605 TEST( GetLocalClearance(), aLibFootprint->GetLocalClearance(),
606 _( "Pad clearance overridden." ) );
608 _( "Solder mask expansion overridden." ) );
610 _( "Solder paste absolute clearance overridden." ) );
612 _( "Solder paste relative clearance overridden." ) );
613
614 TEST( GetZoneConnection(), aLibFootprint->GetZoneConnection(),
615 _( "Zone connection overridden." ) );
616 }
617
618 TEST( GetNetTiePadGroups().size(), aLibFootprint->GetNetTiePadGroups().size(),
619 _( "Net tie pad groups differ." ) );
620
621 for( size_t ii = 0; ii < GetNetTiePadGroups().size(); ++ii )
622 {
623 TEST( GetNetTiePadGroups()[ii], aLibFootprint->GetNetTiePadGroups()[ii],
624 _( "Net tie pad groups differ." ) );
625 }
626
627#define REPORT( msg ) { if( aReporter ) aReporter->Report( msg ); }
628#define CHECKPOINT { if( diff && !aReporter ) return diff; }
629
630 // Text items are really problematic. We don't want to test the reference, but after that
631 // it gets messy.
632 //
633 // What about the value? Depends on whether or not it's a singleton part.
634 //
635 // And what about other texts? They might be added only to instances on the board, or even
636 // changed for instances on the board. Or they might want to be tested for equality.
637 //
638 // Currently we punt and ignore all the text items.
639
640 // Drawings and pads are also somewhat problematic as there's no guarantee that they'll be
641 // in the same order in the two footprints. Rather than building some sophisticated hashing
642 // algorithm we use the footprint sorting functions to attempt to sort them in the same
643 // order.
644
645 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aShapes;
646 std::copy_if( GraphicalItems().begin(), GraphicalItems().end(),
647 std::inserter( aShapes, aShapes.begin() ),
648 []( BOARD_ITEM* item )
649 {
650 return item->Type() == PCB_SHAPE_T;
651 } );
652 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bShapes;
653 std::copy_if( aLibFootprint->GraphicalItems().begin(), aLibFootprint->GraphicalItems().end(),
654 std::inserter( bShapes, bShapes.begin() ),
655 []( BOARD_ITEM* item )
656 {
657 return item->Type() == PCB_SHAPE_T;
658 } );
659
660 if( aShapes.size() != bShapes.size() )
661 {
662 diff = true;
663 REPORT( _( "Graphic item count differs." ) );
664 }
665 else
666 {
667 for( auto aIt = aShapes.begin(), bIt = bShapes.begin(); aIt != aShapes.end(); aIt++, bIt++ )
668 {
669 if( ( *aIt )->Type() == PCB_SHAPE_T )
670 {
671 if( shapeNeedsUpdate( static_cast<PCB_SHAPE*>( *aIt ), static_cast<PCB_SHAPE*>( *bIt ) ) )
672 {
673 diff = true;
674 REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
675 }
676 }
677 }
678 }
679
681
682 std::set<PAD*, FOOTPRINT::cmp_pads> aPads( Pads().begin(), Pads().end() );
683 std::set<PAD*, FOOTPRINT::cmp_pads> bPads( aLibFootprint->Pads().begin(), aLibFootprint->Pads().end() );
684
685 if( aPads.size() != bPads.size() )
686 {
687 diff = true;
688 REPORT( _( "Pad count differs." ) );
689 }
690 else
691 {
692 for( auto aIt = aPads.begin(), bIt = bPads.begin(); aIt != aPads.end(); aIt++, bIt++ )
693 {
694 if( padNeedsUpdate( *aIt, *bIt, aReporter ) )
695 diff = true;
696 else if( aReporter && padHasOverrides( *aIt, *bIt, aReporter ) )
697 diff = true;
698 }
699 }
700
702
703 if( Models().size() != aLibFootprint->Models().size() )
704 {
705 diff = true;
706 REPORT( _( "3D model count differs." ) );
707 }
708 else
709 {
710 for( size_t ii = 0; ii < Models().size(); ++ii )
711 diff |= modelNeedsUpdate( Models()[ii], aLibFootprint->Models()[ii], aReporter );
712 }
713
715
716 // Rotate/position a copy of libFootprint so that zones sort the same
717 std::unique_ptr<FOOTPRINT> libCopy( static_cast<FOOTPRINT*>( aLibFootprint->Clone() ) );
718
719 libCopy->SetOrientation( GetOrientation() );
720 libCopy->Move( GetPosition() );
721
722 std::set<ZONE*, FOOTPRINT::cmp_zones> aZones( Zones().begin(), Zones().end() );
723 std::set<ZONE*, FOOTPRINT::cmp_zones> bZones( libCopy->Zones().begin(), libCopy->Zones().end() );
724
725 if( aZones.size() != bZones.size() )
726 {
727 diff = true;
728 REPORT( _( "Rule area count differs." ) );
729 }
730 else
731 {
732 for( auto aIt = aZones.begin(), bIt = bZones.begin(); aIt != aZones.end(); aIt++, bIt++ )
733 diff |= zoneNeedsUpdate( *aIt, *bIt, aReporter );
734 }
735
736 return diff;
737}
738
739
741{
742 BOARD* board = m_drcEngine->GetBoard();
743 PROJECT* project = board->GetProject();
744
745 if( !project )
746 {
747 reportAux( _( "No project loaded, skipping library parity tests." ) );
748 return true; // Continue with other tests
749 }
750
751 if( !reportPhase( _( "Loading footprint library table..." ) ) )
752 return false; // DRC cancelled
753
754 std::map<LIB_ID, std::shared_ptr<FOOTPRINT>> libFootprintCache;
755
756 FP_LIB_TABLE* libTable = project->PcbFootprintLibs();
757 wxString msg;
758 int ii = 0;
759 const int progressDelta = 250;
760
761 if( !reportPhase( _( "Checking board footprints against library..." ) ) )
762 return false;
763
764 for( FOOTPRINT* footprint : board->Footprints() )
765 {
768 {
769 return true; // Continue with other tests
770 }
771
772 if( !reportProgress( ii++, (int) board->Footprints().size(), progressDelta ) )
773 return false; // DRC cancelled
774
775 LIB_ID fpID = footprint->GetFPID();
776 wxString libName = fpID.GetLibNickname();
777 wxString fpName = fpID.GetLibItemName();
778 const LIB_TABLE_ROW* libTableRow = nullptr;
779
780 try
781 {
782 libTableRow = libTable->FindRow( libName );
783 }
784 catch( const IO_ERROR& )
785 {
786 }
787
788 if( !libTableRow )
789 {
791 {
792 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
793 msg.Printf( _( "The current configuration does not include the library '%s'." ),
794 libName );
795 drcItem->SetErrorMessage( msg );
796 drcItem->SetItems( footprint );
797 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
798 }
799
800 continue;
801 }
802 else if( !libTable->HasLibrary( libName, true ) )
803 {
805 {
806 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
807 msg.Printf( _( "The library '%s' is not enabled in the current configuration." ),
808 libName );
809 drcItem->SetErrorMessage( msg );
810 drcItem->SetItems( footprint );
811 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
812 }
813
814 continue;
815 }
816
817 auto cacheIt = libFootprintCache.find( fpID );
818 std::shared_ptr<FOOTPRINT> libFootprint;
819
820 if( cacheIt != libFootprintCache.end() )
821 {
822 libFootprint = cacheIt->second;
823 }
824 else
825 {
826 try
827 {
828 libFootprint.reset( libTable->FootprintLoad( libName, fpName, true ) );
829
830 if( libFootprint )
831 libFootprintCache[ fpID ] = libFootprint;
832 }
833 catch( const IO_ERROR& )
834 {
835 }
836 }
837
838 if( !libFootprint )
839 {
841 {
842 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES );
843 msg.Printf( _( "Footprint '%s' not found in library '%s'." ),
844 fpName,
845 libName );
846 drcItem->SetErrorMessage( msg );
847 drcItem->SetItems( footprint );
848 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
849 }
850 }
851 else if( footprint->FootprintNeedsUpdate( libFootprint.get() ) )
852 {
854 {
855 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_MISMATCH );
856 msg.Printf( _( "Footprint '%s' does not match copy in library '%s'." ),
857 fpName,
858 libName );
859 drcItem->SetErrorMessage( msg );
860 drcItem->SetItems( footprint );
861 reportViolation( drcItem, footprint->GetCenter(), UNDEFINED_LAYER );
862 }
863 }
864 }
865
866 return true;
867}
868
869
870namespace detail
871{
873}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:204
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:45
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:247
VECTOR2I GetFPRelativePosition() const
Definition: board_item.cpp:261
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:271
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:614
FOOTPRINTS & Footprints()
Definition: board.h:313
PROJECT * GetProject() const
Definition: board.h:449
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:120
const Vec & GetOrigin() const
Definition: box2.h:184
const Vec GetEnd() const
Definition: box2.h:186
BOARD * GetBoard() const
Definition: drc_engine.h:89
bool IsErrorLimitExceeded(int error_code)
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:325
virtual const wxString GetDescription() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetName() const override
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out DRC_ITEM and posi...
virtual bool reportPhase(const wxString &aStageName)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
double AsDegrees() const
Definition: eda_angle.h:149
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:100
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:183
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:256
bool IsFilled() const
Definition: eda_shape.h:91
SHAPE_T GetShape() const
Definition: eda_shape.h:117
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:149
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:124
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:87
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:180
bool IsItalic() const
Definition: eda_text.h:141
const EDA_ANGLE & GetTextAngle() const
Definition: eda_text.h:131
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:95
bool IsKeepUpright() const
Definition: eda_text.h:166
virtual bool IsVisible() const
Definition: eda_text.h:147
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:160
bool IsMirrored() const
Definition: eda_text.h:150
bool IsBold() const
Definition: eda_text.h:144
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition: eda_text.h:163
int GetTextThickness() const
Definition: eda_text.h:123
VECTOR2I GetTextSize() const
Definition: eda_text.h:207
wxString GetLibDescription() const
Definition: footprint.h:236
EDA_ANGLE GetOrientation() const
Definition: footprint.h:209
ZONES & Zones()
Definition: footprint.h:194
int GetLocalClearance() const
Definition: footprint.h:254
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: footprint.cpp:1674
double GetLocalSolderPasteMarginRatio() const
Definition: footprint.h:268
int GetAttributes() const
Definition: footprint.h:274
bool IsFlipped() const
Definition: footprint.h:348
PADS & Pads()
Definition: footprint.h:188
int GetLocalSolderPasteMargin() const
Definition: footprint.h:265
const std::vector< wxString > & GetNetTiePadGroups() const
Definition: footprint.h:296
bool FootprintNeedsUpdate(const FOOTPRINT *aLibFootprint, REPORTER *aReporter=nullptr)
Return true if a board footprint differs from the library version.
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:202
ZONE_CONNECTION GetZoneConnection() const
Definition: footprint.h:272
wxString GetKeywords() const
Definition: footprint.h:239
VECTOR2I GetPosition() const override
Definition: footprint.h:206
DRAWINGS & GraphicalItems()
Definition: footprint.h:191
int GetLocalSolderMaskMargin() const
Definition: footprint.h:251
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:98
double m_Opacity
Definition: footprint.h:99
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:97
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:96
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:100
bool m_Show
Include model in rendering.
Definition: footprint.h:101
const FP_LIB_TABLE_ROW * FindRow(const wxString &aNickName, bool aCheckIfEnabled=false)
Return an FP_LIB_TABLE_ROW if aNickName is found in this table or in any chained fall back table frag...
FOOTPRINT * FootprintLoad(const wxString &aNickname, const wxString &aFootprintName, bool aKeepUUID=false)
Load a footprint having aFootprintName from the library given by aNickname.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
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
Hold a record identifying a library accessed by the appropriate plug in object in the LIB_TABLE.
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:552
Definition: pad.h:58
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: pad.cpp:817
PAD_PROP GetProperty() const
Definition: pad.h:375
bool GetRemoveUnconnected() const
Definition: pad.h:581
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: pad.h:355
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:369
const VECTOR2I & GetDrillSize() const
Definition: pad.h:253
PAD_ATTRIB GetAttribute() const
Definition: pad.h:372
ZONE_CONNECTION GetZoneConnection() const
Definition: pad.h:494
const wxString & GetNumber() const
Definition: pad.h:130
double GetLocalSolderPasteMarginRatio() const
Definition: pad.h:397
int GetRoundRectCornerRadius() const
Definition: pad.cpp:328
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives() const
Accessor to the basic shape list for custom-shaped pads.
Definition: pad.h:300
EDA_ANGLE GetThermalSpokeAngle() const
Definition: pad.h:513
const VECTOR2I & GetOffset() const
Definition: pad.h:260
int GetLocalSolderMaskMargin() const
Definition: pad.h:387
bool GetKeepTopBottom() const
Definition: pad.h:587
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
Definition: pad.h:207
const VECTOR2I & GetDelta() const
Definition: pad.h:250
PAD_SHAPE GetShape() const
Definition: pad.h:189
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:341
int GetThermalSpokeWidth() const
Definition: pad.h:503
int GetLocalSolderPasteMargin() const
Definition: pad.h:394
int GetChamferPositions() const
Definition: pad.h:566
double GetRoundRectRadiusRatio() const
Definition: pad.h:547
int GetThermalGap() const
Definition: pad.h:526
const VECTOR2I & GetSize() const
Definition: pad.h:243
double GetChamferRectRatio() const
Definition: pad.h:556
int GetPadToDieLength() const
Definition: pad.h:385
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:72
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:82
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:67
Container for project specific data.
Definition: project.h:62
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
int TotalVertices() const
Return total number of vertices stored in the set.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:710
bool GetDoNotAllowVias() const
Definition: zone.h:712
bool GetDoNotAllowPads() const
Definition: zone.h:714
bool GetDoNotAllowTracks() const
Definition: zone.h:713
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition: zone.h:724
SHAPE_POLY_SET * Outline()
Definition: zone.h:325
long long int GetMinIslandArea() const
Definition: zone.h:727
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: zone.cpp:501
const wxString & GetZoneName() const
Definition: zone.h:131
int GetMinThickness() const
Definition: zone.h:258
ZONE_CONNECTION GetPadConnection() const
Definition: zone.h:255
int GetHatchThickness() const
Definition: zone.h:273
double GetHatchHoleMinArea() const
Definition: zone.h:288
int GetThermalReliefSpokeWidth() const
Definition: zone.h:202
EDA_ANGLE GetHatchOrientation() const
Definition: zone.h:279
bool GetDoNotAllowFootprints() const
Definition: zone.h:715
ZONE_FILL_MODE GetFillMode() const
Definition: zone.h:181
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:129
bool GetDoNotAllowCopperPour() const
Definition: zone.h:711
int GetHatchGap() const
Definition: zone.h:276
double GetHatchSmoothingValue() const
Definition: zone.h:285
int GetHatchSmoothingLevel() const
Definition: zone.h:282
unsigned int GetCornerRadius() const
Definition: zone.h:665
int GetCornerSmoothingType() const
Definition: zone.h:661
int GetThermalReliefGap() const
Definition: zone.h:191
unsigned GetAssignedPriority() const
Definition: zone.h:119
@ DRCE_LIB_FOOTPRINT_ISSUES
Definition: drc_item.h:76
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition: drc_item.h:77
bool textNeedsUpdate(const PCB_TEXT *a, const PCB_TEXT *b)
bool padHasOverrides(const PAD *a, const PAD *b, REPORTER *aReporter)
#define TEST_V3D(a, b, msg)
#define PAD_DESC(pad)
UNITS_PROVIDER g_unitsProvider(pcbIUScale, EDA_UNITS::MILLIMETRES)
bool primitiveNeedsUpdate(const std::shared_ptr< PCB_SHAPE > &a, const std::shared_ptr< PCB_SHAPE > &b)
#define TEST(a, b, msg)
bool modelNeedsUpdate(const FP_3DMODEL &a, const FP_3DMODEL &b, REPORTER *aReporter)
bool zoneNeedsUpdate(const ZONE *a, const ZONE *b, REPORTER *aReporter)
#define TEST_ATTR(a, b, attr, msg)
#define TEST_D(a, b, msg)
bool padNeedsUpdate(const PAD *a, const PAD *b, REPORTER *aReporter)
bool shapeNeedsUpdate(const PCB_SHAPE *a, const PCB_SHAPE *b)
#define CHECKPOINT
#define ITEM_DESC(item)
#define _(s)
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:437
#define TEST(a, b)
@ ARC
use RECTANGLE instead of RECT to avoid collision in a Windows header
@ FP_SMD
Definition: footprint.h:73
@ FP_ALLOW_MISSING_COURTYARD
Definition: footprint.h:79
@ FP_THROUGH_HOLE
Definition: footprint.h:72
@ FP_ALLOW_SOLDERMASK_BRIDGES
Definition: footprint.h:78
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
#define REPORT(msg)
Definition: lib_symbol.cpp:255
#define ITEM_DESC(item)
Definition: lib_symbol.cpp:256
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588