From 0a609dd48d5b31adc2b5c64725633189893ca36d Mon Sep 17 00:00:00 2001 From: Jeff Young <jeff@rokeby.ie> Date: Sun, 11 Jul 2021 12:49:36 +0100 Subject: [PATCH] Add footprint library checking to DRC. Fixes https://gitlab.com/kicad/code/kicad/issues/6821 --- pcbnew/CMakeLists.txt | 3 +- .../dialogs/dialog_footprint_properties.cpp | 2 +- .../dialog_footprint_properties_fp_editor.cpp | 2 +- pcbnew/drc/drc_item.cpp | 5 + pcbnew/drc/drc_item.h | 2 + pcbnew/drc/drc_test_provider.h | 7 - .../drc/drc_test_provider_annular_width.cpp | 8 - pcbnew/drc/drc_test_provider_connectivity.cpp | 8 - .../drc_test_provider_copper_clearance.cpp | 8 - .../drc_test_provider_courtyard_clearance.cpp | 8 - .../drc_test_provider_diff_pair_coupling.cpp | 5 - pcbnew/drc/drc_test_provider_disallow.cpp | 8 - .../drc/drc_test_provider_edge_clearance.cpp | 8 - pcbnew/drc/drc_test_provider_hole_size.cpp | 8 - pcbnew/drc/drc_test_provider_hole_to_hole.cpp | 8 - .../drc/drc_test_provider_library_parity.cpp | 519 ++++++++++++++++++ .../drc/drc_test_provider_matched_length.cpp | 5 - pcbnew/drc/drc_test_provider_misc.cpp | 8 - ...=> drc_test_provider_schematic_parity.cpp} | 34 +- .../drc/drc_test_provider_silk_clearance.cpp | 5 - pcbnew/drc/drc_test_provider_silk_to_mask.cpp | 5 - pcbnew/drc/drc_test_provider_track_width.cpp | 8 - pcbnew/drc/drc_test_provider_via_diameter.cpp | 8 - pcbnew/footprint.cpp | 76 ++- pcbnew/footprint.h | 16 +- pcbnew/plugins/kicad/pcb_plugin.cpp | 4 +- qa/data/issue1358.kicad_pro | 1 + qa/data/issue2528.kicad_pro | 1 + qa/data/issue4774.kicad_pro | 1 + qa/data/issue5854.kicad_pro | 1 + qa/data/issue5978.kicad_pro | 1 + qa/data/issue5990.kicad_pro | 1 + qa/data/issue6879.kicad_pro | 1 + qa/data/issue6945.kicad_pro | 1 + qa/data/issue7267.kicad_pro | 1 + qa/data/issue7325.kicad_pro | 1 + qa/data/issue7975.kicad_pro | 1 + qa/data/issue8003.kicad_pro | 28 +- qa/data/issue8407.kicad_pro | 1 + qa/data/issue9081.kicad_pro | 1 + qa/drc_proto/CMakeLists.txt | 2 +- qa/pns/CMakeLists.txt | 2 +- 42 files changed, 657 insertions(+), 166 deletions(-) create mode 100644 pcbnew/drc/drc_test_provider_library_parity.cpp rename pcbnew/drc/{drc_test_provider_lvs.cpp => drc_test_provider_schematic_parity.cpp} (90%) diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 0ae24f0bd5..b551222a45 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -238,7 +238,8 @@ set( PCBNEW_DRC_SRCS drc/drc_test_provider_edge_clearance.cpp drc/drc_test_provider_hole_to_hole.cpp drc/drc_test_provider_hole_size.cpp - drc/drc_test_provider_lvs.cpp + drc/drc_test_provider_library_parity.cpp + drc/drc_test_provider_schematic_parity.cpp drc/drc_test_provider_misc.cpp drc/drc_test_provider_track_width.cpp drc/drc_test_provider_via_diameter.cpp diff --git a/pcbnew/dialogs/dialog_footprint_properties.cpp b/pcbnew/dialogs/dialog_footprint_properties.cpp index a64885fd3a..1a4a16f9e5 100644 --- a/pcbnew/dialogs/dialog_footprint_properties.cpp +++ b/pcbnew/dialogs/dialog_footprint_properties.cpp @@ -508,7 +508,7 @@ bool DIALOG_FOOTPRINT_PROPERTIES::TransferDataFromWindow() // Copy the models from the panel to the footprint std::vector<FP_3DMODEL>& panelList = m_3dPanel->GetModelList(); - std::list<FP_3DMODEL>* fpList = &m_footprint->Models(); + std::vector<FP_3DMODEL>* fpList = &m_footprint->Models(); fpList->clear(); fpList->insert( fpList->end(), panelList.begin(), panelList.end() ); diff --git a/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp b/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp index 36749fa09e..b2f4973555 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp +++ b/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp @@ -443,7 +443,7 @@ bool DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::TransferDataFromWindow() // Copy the models from the panel to the footprint std::vector<FP_3DMODEL>& panelList = m_3dPanel->GetModelList(); - std::list<FP_3DMODEL>* fpList = &m_footprint->Models(); + std::vector<FP_3DMODEL>* fpList = &m_footprint->Models(); fpList->clear(); fpList->insert( fpList->end(), panelList.begin(), panelList.end() ); diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index 8af220c15b..27d9649815 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -170,6 +170,10 @@ DRC_ITEM DRC_ITEM::netConflict( DRCE_NET_CONFLICT, _( "Pad net doesn't match schematic" ), wxT( "net_conflict" ) ); +DRC_ITEM DRC_ITEM::libFootprintIssues( DRCE_LIB_FOOTPRINT_ISSUES, + _( "Library footprint issue" ), + wxT( "lib_footprint_issues" ) ); + DRC_ITEM DRC_ITEM::unresolvedVariable( DRCE_UNRESOLVED_VARIABLE, _( "Unresolved text variable" ), wxT( "unresolved_variable" ) ); @@ -298,6 +302,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode ) case DRCE_DUPLICATE_FOOTPRINT: return std::make_shared<DRC_ITEM>( duplicateFootprints ); case DRCE_NET_CONFLICT: return std::make_shared<DRC_ITEM>( netConflict ); case DRCE_EXTRA_FOOTPRINT: return std::make_shared<DRC_ITEM>( extraFootprint ); + case DRCE_LIB_FOOTPRINT_ISSUES: return std::make_shared<DRC_ITEM>( libFootprintIssues ); case DRCE_UNRESOLVED_VARIABLE: return std::make_shared<DRC_ITEM>( unresolvedVariable ); case DRCE_OVERLAPPING_SILK: return std::make_shared<DRC_ITEM>( silkOverlaps ); case DRCE_SILK_MASK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkMaskClearance ); diff --git a/pcbnew/drc/drc_item.h b/pcbnew/drc/drc_item.h index 2202b8d9bc..b5c29b7fd7 100644 --- a/pcbnew/drc/drc_item.h +++ b/pcbnew/drc/drc_item.h @@ -68,6 +68,7 @@ enum PCB_DRC_CODE { DRCE_NET_CONFLICT, // pad net doesn't match netlist DRCE_FOOTPRINT_TYPE_MISMATCH, // footprint attribute does not match actual pads + DRCE_LIB_FOOTPRINT_ISSUES, // footprint does not match the current library DRCE_PAD_TH_WITH_NO_HOLE, // footprint has Plated Through-Hole with no hole DRCE_UNRESOLVED_VARIABLE, @@ -164,6 +165,7 @@ private: static DRC_ITEM missingFootprint; static DRC_ITEM extraFootprint; static DRC_ITEM netConflict; + static DRC_ITEM libFootprintIssues; static DRC_ITEM unresolvedVariable; static DRC_ITEM silkMaskClearance; static DRC_ITEM silkOverlaps; diff --git a/pcbnew/drc/drc_test_provider.h b/pcbnew/drc/drc_test_provider.h index c410aeb41a..ef2ecdaf60 100644 --- a/pcbnew/drc/drc_test_provider.h +++ b/pcbnew/drc/drc_test_provider.h @@ -91,13 +91,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const = 0; - virtual int GetNumPhases() const = 0; - - virtual bool IsRuleDriven() const - { - return m_isRuleDriven; - } - bool IsEnabled() const { return m_enabled; diff --git a/pcbnew/drc/drc_test_provider_annular_width.cpp b/pcbnew/drc/drc_test_provider_annular_width.cpp index 0bde18c48e..38ad04faf8 100644 --- a/pcbnew/drc/drc_test_provider_annular_width.cpp +++ b/pcbnew/drc/drc_test_provider_annular_width.cpp @@ -63,8 +63,6 @@ public: } virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - - int GetNumPhases() const override; }; @@ -164,12 +162,6 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() } -int DRC_TEST_PROVIDER_ANNULAR_WIDTH::GetNumPhases() const -{ - return 1; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_ANNULAR_WIDTH::GetConstraintTypes() const { return { ANNULAR_WIDTH_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_connectivity.cpp b/pcbnew/drc/drc_test_provider_connectivity.cpp index 4acf134e31..6be0b52e8a 100644 --- a/pcbnew/drc/drc_test_provider_connectivity.cpp +++ b/pcbnew/drc/drc_test_provider_connectivity.cpp @@ -65,8 +65,6 @@ public: } virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - - int GetNumPhases() const override; }; @@ -175,12 +173,6 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run() } -int DRC_TEST_PROVIDER_CONNECTIVITY::GetNumPhases() const -{ - return 3; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_CONNECTIVITY::GetConstraintTypes() const { return {}; diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index 977467e1ea..2abc25894b 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -80,8 +80,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: bool testTrackAgainstItem( PCB_TRACK* track, SHAPE* trackShape, PCB_LAYER_ID layer, BOARD_ITEM* other ); @@ -1006,12 +1004,6 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() } -int DRC_TEST_PROVIDER_COPPER_CLEARANCE::GetNumPhases() const -{ - return 4; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_COPPER_CLEARANCE::GetConstraintTypes() const { return { CLEARANCE_CONSTRAINT, HOLE_CLEARANCE_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp index 39cb51f830..677fed73da 100644 --- a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp @@ -69,8 +69,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: bool testFootprintCourtyardDefinitions(); @@ -319,12 +317,6 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::Run() } -int DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::GetNumPhases() const -{ - return 2; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::GetConstraintTypes() const { return { COURTYARD_CLEARANCE_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp b/pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp index 634cf7cafa..bd03165d83 100644 --- a/pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp +++ b/pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp @@ -74,11 +74,6 @@ public: return "Tests differential pair coupling"; } - virtual int GetNumPhases() const override - { - return 1; - } - virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; private: diff --git a/pcbnew/drc/drc_test_provider_disallow.cpp b/pcbnew/drc/drc_test_provider_disallow.cpp index 52a56f7692..b84a0f609b 100644 --- a/pcbnew/drc/drc_test_provider_disallow.cpp +++ b/pcbnew/drc/drc_test_provider_disallow.cpp @@ -60,8 +60,6 @@ public: } virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - - int GetNumPhases() const override; }; @@ -169,12 +167,6 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run() } -int DRC_TEST_PROVIDER_DISALLOW::GetNumPhases() const -{ - return 1; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_DISALLOW::GetConstraintTypes() const { return { DISALLOW_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_edge_clearance.cpp b/pcbnew/drc/drc_test_provider_edge_clearance.cpp index 040ce53d8a..6ef6b42e6d 100644 --- a/pcbnew/drc/drc_test_provider_edge_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_edge_clearance.cpp @@ -70,8 +70,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: bool testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape, BOARD_ITEM* other, DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode ); @@ -282,12 +280,6 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run() } -int DRC_TEST_PROVIDER_EDGE_CLEARANCE::GetNumPhases() const -{ - return 1; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_EDGE_CLEARANCE::GetConstraintTypes() const { return { EDGE_CLEARANCE_CONSTRAINT, SILK_CLEARANCE_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_hole_size.cpp b/pcbnew/drc/drc_test_provider_hole_size.cpp index 83a0e6d0d7..707f27868e 100644 --- a/pcbnew/drc/drc_test_provider_hole_size.cpp +++ b/pcbnew/drc/drc_test_provider_hole_size.cpp @@ -62,8 +62,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: void checkVia( PCB_VIA* via, bool aExceedMicro, bool aExceedStd ); void checkPad( PAD* aPad ); @@ -251,12 +249,6 @@ void DRC_TEST_PROVIDER_HOLE_SIZE::checkVia( PCB_VIA* via, bool aExceedMicro, boo } -int DRC_TEST_PROVIDER_HOLE_SIZE::GetNumPhases() const -{ - return 2; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_HOLE_SIZE::GetConstraintTypes() const { return { HOLE_SIZE_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_hole_to_hole.cpp b/pcbnew/drc/drc_test_provider_hole_to_hole.cpp index fa9800369b..e357052f26 100644 --- a/pcbnew/drc/drc_test_provider_hole_to_hole.cpp +++ b/pcbnew/drc/drc_test_provider_hole_to_hole.cpp @@ -70,8 +70,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: bool testHoleAgainstHole( BOARD_ITEM* aItem, SHAPE_CIRCLE* aHole, BOARD_ITEM* aOther ); @@ -327,12 +325,6 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::testHoleAgainstHole( BOARD_ITEM* aItem, SHA } -int DRC_TEST_PROVIDER_HOLE_TO_HOLE::GetNumPhases() const -{ - return 1; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_HOLE_TO_HOLE::GetConstraintTypes() const { return { HOLE_TO_HOLE_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_library_parity.cpp b/pcbnew/drc/drc_test_provider_library_parity.cpp new file mode 100644 index 0000000000..424339c3d3 --- /dev/null +++ b/pcbnew/drc/drc_test_provider_library_parity.cpp @@ -0,0 +1,519 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 KiCad Developers. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kiway.h> +#include <netlist_reader/pcb_netlist.h> +#include <fp_lib_table.h> +#include <board.h> +#include <fp_shape.h> +#include <fp_text.h> +#include <zone.h> +#include <footprint.h> +#include <pad.h> +#include <drc/drc_engine.h> +#include <drc/drc_item.h> +#include <drc/drc_test_provider.h> +#include <macros.h> + +/* + Library parity test. + + Errors generated: + - DRCE_LIB_FOOTPRINT_ISSUES +*/ + +class DRC_TEST_PROVIDER_LIBRARY_PARITY : public DRC_TEST_PROVIDER +{ +public: + DRC_TEST_PROVIDER_LIBRARY_PARITY() + { + m_isRuleDriven = false; + } + + virtual ~DRC_TEST_PROVIDER_LIBRARY_PARITY() + { + } + + virtual bool Run() override; + + virtual const wxString GetName() const override + { + return "library_parity"; + }; + + virtual const wxString GetDescription() const override + { + return "Performs board footprint vs library integity checks"; + } + + virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; +}; + + +#define TEST( a, b ) { if( a != b ) return true; } +#define TEST_PADS( a, b ) { if( padsNeedUpdate( a, b ) ) return true; } +#define TEST_SHAPES( a, b ) { if( shapesNeedUpdate( a, b ) ) return true; } +#define TEST_PRIMITIVES( a, b ) { if( primitivesNeedUpdate( a, b ) ) return true; } +#define TEST_ZONES( a, b ) { if( zonesNeedUpdate( a, b ) ) return true; } +#define TEST_MODELS( a, b ) { if( modelsNeedUpdate( a, b ) ) return true; } + + +bool primitivesNeedUpdate( const std::shared_ptr<PCB_SHAPE>& a, + const std::shared_ptr<PCB_SHAPE>& b ) +{ + TEST( a->GetShape(), b->GetShape() ); + + switch( a->GetShape() ) + { + case SHAPE_T::SEGMENT: + case SHAPE_T::RECT: + case SHAPE_T::CIRCLE: + TEST( a->GetStart(), b->GetStart() ); + TEST( a->GetEnd(), b->GetEnd() ); + break; + + case SHAPE_T::ARC: + TEST( a->GetStart(), b->GetStart() ); + TEST( a->GetEnd(), b->GetEnd() ); + TEST( a->GetCenter(), b->GetCenter() ); + TEST( a->GetArcAngle(), b->GetArcAngle() ); + break; + + case SHAPE_T::BEZIER: + TEST( a->GetStart(), b->GetStart() ); + TEST( a->GetEnd(), b->GetEnd() ); + TEST( a->GetBezierC1(), b->GetBezierC1() ); + TEST( a->GetBezierC2(), b->GetBezierC2() ); + break; + + case SHAPE_T::POLY: + TEST( a->GetPolyShape().TotalVertices(), b->GetPolyShape().TotalVertices() ); + + for( int ii = 0; ii < a->GetPolyShape().TotalVertices(); ++ii ) + TEST( a->GetPolyShape().CVertex( ii ), b->GetPolyShape().CVertex( ii ) ); + + break; + + default: + UNIMPLEMENTED_FOR( a->SHAPE_T_asString() ); + } + + TEST( a->GetWidth(), b->GetWidth() ); + TEST( a->IsFilled(), b->IsFilled() ); + + return false; +} + + +bool padsNeedUpdate( const PAD* a, const PAD* b ) +{ + TEST( a->GetPadToDieLength(), b->GetPadToDieLength() ); + TEST( a->GetPos0(), b->GetPos0() ); + + TEST( a->GetNumber(), b->GetNumber() ); + + // These are assigned from the schematic and not from the library + // TEST( a->GetPinFunction(), b->GetPinFunction() ); + // TEST( a->GetPinType(), b->GetPinType() ); + + TEST( a->GetRemoveUnconnected(), b->GetRemoveUnconnected() ); + + // NB: KeepTopBottom is undefined if RemoveUnconnected is NOT set. + if( a->GetRemoveUnconnected() ) + TEST( a->GetKeepTopBottom(), b->GetKeepTopBottom() ); + + TEST( a->GetShape(), b->GetShape() ); + TEST( a->GetLayerSet(), b->GetLayerSet() ); + TEST( a->GetAttribute(), b->GetAttribute() ); + TEST( a->GetProperty(), b->GetProperty() ); + + // The pad orientation, for historical reasons is the pad rotation + parent rotation. + TEST( NormalizeAnglePos( a->GetOrientation() - a->GetParent()->GetOrientation() ), + NormalizeAnglePos( b->GetOrientation() - b->GetParent()->GetOrientation() ) ); + + TEST( a->GetSize(), b->GetSize() ); + TEST( a->GetDelta(), b->GetDelta() ); + TEST( a->GetRoundRectCornerRadius(), b->GetRoundRectCornerRadius() ); + TEST( a->GetRoundRectRadiusRatio(), b->GetRoundRectRadiusRatio() ); + TEST( a->GetChamferRectRatio(), b->GetChamferRectRatio() ); + TEST( a->GetChamferPositions(), b->GetChamferPositions() ); + TEST( a->GetOffset(), b->GetOffset() ); + + TEST( a->GetDrillShape(), b->GetDrillShape() ); + TEST( a->GetDrillSize(), b->GetDrillSize() ); + + TEST( a->GetLocalClearance(), b->GetLocalClearance() ); + TEST( a->GetLocalSolderMaskMargin(), b->GetLocalSolderMaskMargin() ); + TEST( a->GetLocalSolderPasteMargin(), b->GetLocalSolderPasteMargin() ); + TEST( a->GetLocalSolderPasteMarginRatio(), b->GetLocalSolderPasteMarginRatio() ); + + TEST( a->GetZoneConnection(), b->GetZoneConnection() ); + TEST( a->GetThermalGap(), b->GetThermalGap() ); + TEST( a->GetThermalSpokeWidth(), b->GetThermalSpokeWidth() ); + TEST( a->GetCustomShapeInZoneOpt(), b->GetCustomShapeInZoneOpt() ); + + TEST( a->GetPrimitives().size(), b->GetPrimitives().size() ); + + for( size_t ii = 0; ii < a->GetPrimitives().size(); ++ii ) + TEST_PRIMITIVES( a->GetPrimitives()[ii], b->GetPrimitives()[ii] ); + + return false; +} + + +bool shapesNeedUpdate( const FP_SHAPE* a, const FP_SHAPE* b ) +{ + TEST( a->GetShape(), b->GetShape() ); + + switch( a->GetShape() ) + { + case SHAPE_T::SEGMENT: + case SHAPE_T::RECT: + case SHAPE_T::CIRCLE: + TEST( a->GetStart0(), b->GetStart0() ); + TEST( a->GetEnd0(), b->GetEnd0() ); + break; + + case SHAPE_T::ARC: + TEST( a->GetStart0(), b->GetStart0() ); + TEST( a->GetEnd0(), b->GetEnd0() ); + TEST( a->GetCenter0(), b->GetCenter0() ); + TEST( a->GetArcAngle(), b->GetArcAngle() ); + break; + + case SHAPE_T::BEZIER: + TEST( a->GetStart0(), b->GetStart0() ); + TEST( a->GetEnd0(), b->GetEnd0() ); + TEST( a->GetBezierC1_0(), b->GetBezierC1_0() ); + TEST( a->GetBezierC2_0(), b->GetBezierC2_0() ); + break; + + case SHAPE_T::POLY: + TEST( a->GetPolyShape().TotalVertices(), b->GetPolyShape().TotalVertices() ); + + for( int ii = 0; ii < a->GetPolyShape().TotalVertices(); ++ii ) + TEST( a->GetPolyShape().CVertex( ii ), b->GetPolyShape().CVertex( ii ) ); + + break; + + default: + UNIMPLEMENTED_FOR( a->SHAPE_T_asString() ); + } + + TEST( a->GetWidth(), b->GetWidth() ); + TEST( a->IsFilled(), b->IsFilled() ); + + TEST( a->GetLayer(), b->GetLayer() ); + + return false; +} + + +bool textsNeedUpdate( const FP_TEXT* a, const FP_TEXT* b ) +{ + TEST( a->GetLayer(), b->GetLayer() ); + TEST( a->IsKeepUpright(), b->IsKeepUpright() ); + + TEST( a->GetText(), b->GetText() ); + + TEST( a->GetTextThickness(), b->GetTextThickness() ); + TEST( a->GetTextAngle(), b->GetTextAngle() ); + TEST( a->IsItalic(), b->IsItalic() ); + TEST( a->IsBold(), b->IsBold() ); + TEST( a->IsVisible(), b->IsVisible() ); + TEST( a->IsMirrored(), b->IsMirrored() ); + + TEST( a->GetHorizJustify(), b->GetHorizJustify() ); + TEST( a->GetVertJustify(), b->GetVertJustify() ); + + TEST( a->GetTextSize(), b->GetTextSize() ); + TEST( a->GetPos0(), b->GetPos0() ); + + return false; +} + + +bool zonesNeedUpdate( const FP_ZONE* a, const FP_ZONE* b ) +{ + TEST( a->GetCornerSmoothingType(), b->GetCornerSmoothingType() ); + TEST( a->GetCornerRadius(), b->GetCornerRadius() ); + TEST( a->GetZoneName(), b->GetZoneName() ); + TEST( a->GetPriority(), b->GetPriority() ); + + TEST( a->GetIsRuleArea(), b->GetIsRuleArea() ); + TEST( a->GetDoNotAllowCopperPour(), b->GetDoNotAllowCopperPour() ); + TEST( a->GetDoNotAllowFootprints(), b->GetDoNotAllowFootprints() ); + TEST( a->GetDoNotAllowPads(), b->GetDoNotAllowPads() ); + TEST( a->GetDoNotAllowTracks(), b->GetDoNotAllowTracks() ); + TEST( a->GetDoNotAllowVias(), b->GetDoNotAllowVias() ); + + TEST( a->GetLayerSet(), b->GetLayerSet() ); + + TEST( a->GetPadConnection(), b->GetPadConnection() ); + TEST( a->GetLocalClearance(), b->GetLocalClearance() ); + TEST( a->GetThermalReliefGap(), b->GetThermalReliefGap() ); + TEST( a->GetThermalReliefSpokeWidth(), b->GetThermalReliefSpokeWidth() ); + + TEST( a->GetMinThickness(), b->GetMinThickness() ); + + TEST( a->GetFillVersion(), b->GetFillVersion() ); + TEST( a->GetIslandRemovalMode(), b->GetIslandRemovalMode() ); + TEST( a->GetMinIslandArea(), b->GetMinIslandArea() ); + + TEST( a->GetFillMode(), b->GetFillMode() ); + TEST( a->GetHatchThickness(), b->GetHatchThickness() ); + TEST( a->GetHatchGap(), b->GetHatchGap() ); + TEST( a->GetHatchOrientation(), b->GetHatchOrientation() ); + TEST( a->GetHatchSmoothingLevel(), b->GetHatchSmoothingLevel() ); + TEST( a->GetHatchSmoothingValue(), b->GetHatchSmoothingValue() ); + TEST( a->GetHatchBorderAlgorithm(), b->GetHatchBorderAlgorithm() ); + TEST( a->GetHatchHoleMinArea(), b->GetHatchHoleMinArea() ); + + TEST( a->Outline()->TotalVertices(), b->Outline()->TotalVertices() ); + + for( int ii = 0; ii < a->Outline()->TotalVertices(); ++ii ) + { + TEST( a->Outline()->CVertex( ii ) - a->GetParent()->GetPosition(), + b->Outline()->CVertex( ii ) - b->GetParent()->GetPosition() ); + } + + return false; +} + + +bool modelsNeedUpdate( const FP_3DMODEL& a, const FP_3DMODEL& b ) +{ +#define EPSILON 0.000001 +#define TEST_V3D( a, b ) { if( abs( a.x - b.x ) > EPSILON \ + || abs( a.y - b.y ) > EPSILON \ + || abs( a.z - b.z ) > EPSILON ) \ + return true; } + + TEST_V3D( a.m_Scale, b.m_Scale ); + TEST_V3D( a.m_Rotation, b.m_Rotation ); + TEST_V3D( a.m_Offset, b.m_Offset ); + TEST( a.m_Opacity, b.m_Opacity ); + TEST( a.m_Filename, b.m_Filename ); + TEST( a.m_Show, b.m_Show ); + + return false; +} + + +bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFootprint ) +{ + TEST( GetDescription(), aLibFootprint->GetDescription() ); + TEST( GetKeywords(), aLibFootprint->GetKeywords() ); + TEST( GetAttributes(), aLibFootprint->GetAttributes() ); + + TEST( GetPlacementCost90(), aLibFootprint->GetPlacementCost90() ); + TEST( GetPlacementCost180(), aLibFootprint->GetPlacementCost180() ); + + TEST( GetLocalClearance(), aLibFootprint->GetLocalClearance() ); + TEST( GetLocalSolderMaskMargin(), aLibFootprint->GetLocalSolderMaskMargin() ); + TEST( GetLocalSolderPasteMargin(), aLibFootprint->GetLocalSolderPasteMargin() ); + TEST( GetLocalSolderPasteMarginRatio(), aLibFootprint->GetLocalSolderPasteMarginRatio() ); + + TEST( GetZoneConnection(), aLibFootprint->GetZoneConnection() ); + + // Text items are really problematic. We don't want to test the reference, but after that + // it gets messy. What about the value? Depends on whether or not it's a singleton part. + // And what about other texts? They might be added only to instances on the board, or even + // changed for instances on the board. Or they might want to be tested for equality. + // Currently we punt and ignore all the text items. + + // Drawings and pads are also somewhat problematic as there's no gaurantee that they'll be + // in the same order in the two footprints. Rather than builds some sophisticated hashing + // algorithm we use the footprint sorting functions to attempt to sort them in the same order. + + std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> aShapes; + std::copy_if( GraphicalItems().begin(), GraphicalItems().end(), + std::inserter( aShapes, aShapes.begin() ), + []( BOARD_ITEM* item ) + { + return item->Type() == PCB_FP_SHAPE_T; + } ); + std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> bShapes; + std::copy_if( aLibFootprint->GraphicalItems().begin(), aLibFootprint->GraphicalItems().end(), + std::inserter( bShapes, bShapes.begin() ), + []( BOARD_ITEM* item ) + { + return item->Type() == PCB_FP_SHAPE_T; + } ); + + std::set<PAD*, FOOTPRINT::cmp_pads> aPads( Pads().begin(), Pads().end() ); + std::set<PAD*, FOOTPRINT::cmp_pads> bPads( aLibFootprint->Pads().begin(), aLibFootprint->Pads().end() ); + + std::set<FP_ZONE*, FOOTPRINT::cmp_zones> aZones( Zones().begin(), Zones().end() ); + std::set<FP_ZONE*, FOOTPRINT::cmp_zones> bZones( aLibFootprint->Zones().begin(), aLibFootprint->Zones().end() ); + + TEST( aPads.size(), bPads.size() ); + TEST( aZones.size(), bZones.size() ); + TEST( aShapes.size(), bShapes.size() ); + + for( auto aIt = aPads.begin(), bIt = bPads.begin(); aIt != aPads.end(); aIt++, bIt++ ) + TEST_PADS( *aIt, *bIt ); + + for( auto aIt = aShapes.begin(), bIt = bShapes.begin(); aIt != aShapes.end(); aIt++, bIt++ ) + { + if( ( *aIt )->Type() == PCB_FP_SHAPE_T ) + TEST_SHAPES( static_cast<FP_SHAPE*>( *aIt ), static_cast<FP_SHAPE*>( *bIt ) ); + } + + for( auto aIt = aZones.begin(), bIt = bZones.begin(); aIt != aZones.end(); aIt++, bIt++ ) + TEST_ZONES( *aIt, *bIt ); + + TEST( Models().size(), aLibFootprint->Models().size() ); + + for( size_t ii = 0; ii < Models().size(); ++ii ) + TEST_MODELS( Models()[ii], aLibFootprint->Models()[ii] ); + + return false; +} + + +bool DRC_TEST_PROVIDER_LIBRARY_PARITY::Run() +{ + BOARD* board = m_drcEngine->GetBoard(); + PROJECT* project = board->GetProject(); + + if( !project ) + { + reportAux( _( "No project loaded, skipping library parity tests." ) ); + return true; // Continue with other tests + } + + if( !reportPhase( _( "Loading footprint library table..." ) ) ) + return false; // DRC cancelled + + std::map<LIB_ID, std::shared_ptr<FOOTPRINT>> libFootprintCache; + + FP_LIB_TABLE* libTable = project->PcbFootprintLibs(); + wxString msg; + int ii = 0; + const int delta = 50; // Number of tests between calls to progress bar + + if( !reportPhase( _( "Checking board footprints against library..." ) ) ) + return false; + + for( FOOTPRINT* footprint : board->Footprints() ) + { + if( m_drcEngine->IsErrorLimitExceeded( DRCE_LIB_FOOTPRINT_ISSUES ) ) + return true; // Continue with other tests + + if( !reportProgress( ii++, board->Footprints().size(), delta ) ) + return false; // DRC cancelled + + LIB_ID fpID = footprint->GetFPID(); + wxString libName = fpID.GetLibNickname(); + wxString fpName = fpID.GetLibItemName(); + const LIB_TABLE_ROW* libTableRow = nullptr; + + try + { + libTableRow = libTable->FindRow( libName ); + } + catch( const IO_ERROR& ) + { + } + + if( !libTableRow ) + { + std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES ); + msg.Printf( _( "The current configuration does not include the library '%s'." ), + libName ); + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( footprint ); + reportViolation( drcItem, footprint->GetCenter() ); + + continue; + } + else if( !libTable->HasLibrary( libName, true ) ) + { + std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES ); + msg.Printf( _( "The library '%s' is not enabled in the current configuration." ), + libName ); + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( footprint ); + reportViolation( drcItem, footprint->GetCenter() ); + + continue; + } + + auto cacheIt = libFootprintCache.find( fpID ); + std::shared_ptr<FOOTPRINT> libFootprint; + + if( cacheIt != libFootprintCache.end() ) + { + libFootprint = cacheIt->second; + } + else + { + try + { + libFootprint.reset( libTable->FootprintLoad( libName, fpName, true ) ); + + if( libFootprint ) + libFootprintCache[ fpID ] = libFootprint; + } + catch( const IO_ERROR& ) + { + } + } + + if( !libFootprint ) + { + std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES ); + msg.Printf( "Footprint '%s' not found in library '%s'.", + fpName, + libName ); + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( footprint ); + reportViolation( drcItem, footprint->GetCenter() ); + } + else if( footprint->FootprintNeedsUpdate( libFootprint.get() ) ) + { + std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LIB_FOOTPRINT_ISSUES ); + msg.Printf( "Footprint '%s' does not match copy in library '%s'.", + fpName, + libName ); + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( footprint ); + reportViolation( drcItem, footprint->GetCenter() ); + } + } + + return true; +} + + +std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_LIBRARY_PARITY::GetConstraintTypes() const +{ + return {}; +} + + +namespace detail +{ +static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_LIBRARY_PARITY> dummy; +} diff --git a/pcbnew/drc/drc_test_provider_matched_length.cpp b/pcbnew/drc/drc_test_provider_matched_length.cpp index 81639ebda7..951cb63bb3 100644 --- a/pcbnew/drc/drc_test_provider_matched_length.cpp +++ b/pcbnew/drc/drc_test_provider_matched_length.cpp @@ -66,11 +66,6 @@ public: return "Tests matched track lengths."; } - virtual int GetNumPhases() const override - { - return 1; - } - virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; DRC_LENGTH_REPORT BuildLengthReport() const; diff --git a/pcbnew/drc/drc_test_provider_misc.cpp b/pcbnew/drc/drc_test_provider_misc.cpp index 79dd8f49db..078242c519 100644 --- a/pcbnew/drc/drc_test_provider_misc.cpp +++ b/pcbnew/drc/drc_test_provider_misc.cpp @@ -70,8 +70,6 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: void testOutline(); void testDisabledLayers(); @@ -284,12 +282,6 @@ bool DRC_TEST_PROVIDER_MISC::Run() } -int DRC_TEST_PROVIDER_MISC::GetNumPhases() const -{ - return 3; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_MISC::GetConstraintTypes() const { return {}; diff --git a/pcbnew/drc/drc_test_provider_lvs.cpp b/pcbnew/drc/drc_test_provider_schematic_parity.cpp similarity index 90% rename from pcbnew/drc/drc_test_provider_lvs.cpp rename to pcbnew/drc/drc_test_provider_schematic_parity.cpp index 8d85cdf7a9..c1368e5b07 100644 --- a/pcbnew/drc/drc_test_provider_lvs.cpp +++ b/pcbnew/drc/drc_test_provider_schematic_parity.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2020 KiCad Developers. + * Copyright (C) 2004-2021 KiCad Developers. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ #include <netlist_reader/pcb_netlist.h> /* - Layout-versus-schematic (LVS) test. + Schematic parity test. Errors generated: - DRCE_MISSING_FOOTPRINT @@ -44,15 +44,15 @@ - cross-check PCB fields against SCH fields */ -class DRC_TEST_PROVIDER_LVS : public DRC_TEST_PROVIDER +class DRC_TEST_PROVIDER_SCHEMATIC_PARITY : public DRC_TEST_PROVIDER { public: - DRC_TEST_PROVIDER_LVS() + DRC_TEST_PROVIDER_SCHEMATIC_PARITY() { m_isRuleDriven = false; } - virtual ~DRC_TEST_PROVIDER_LVS() + virtual ~DRC_TEST_PROVIDER_SCHEMATIC_PARITY() { } @@ -60,7 +60,7 @@ public: virtual const wxString GetName() const override { - return "LVS"; + return "schematic_parity"; }; virtual const wxString GetDescription() const override @@ -70,14 +70,12 @@ public: virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - int GetNumPhases() const override; - private: - void testFootprints( NETLIST& aNetlist ); + void testNetlist( NETLIST& aNetlist ); }; -void DRC_TEST_PROVIDER_LVS::testFootprints( NETLIST& aNetlist ) +void DRC_TEST_PROVIDER_SCHEMATIC_PARITY::testNetlist( NETLIST& aNetlist ) { BOARD* board = m_drcEngine->GetBoard(); @@ -211,7 +209,7 @@ void DRC_TEST_PROVIDER_LVS::testFootprints( NETLIST& aNetlist ) } -bool DRC_TEST_PROVIDER_LVS::Run() +bool DRC_TEST_PROVIDER_SCHEMATIC_PARITY::Run() { if( m_drcEngine->GetTestFootprints() ) { @@ -222,11 +220,11 @@ bool DRC_TEST_PROVIDER_LVS::Run() if( !netlist ) { - reportAux( _("No netlist provided, skipping LVS.") ); + reportAux( _( "No netlist provided, skipping schematic parity tests." ) ); return true; } - testFootprints( *netlist ); + testNetlist( *netlist ); reportRuleStatistics(); } @@ -235,13 +233,7 @@ bool DRC_TEST_PROVIDER_LVS::Run() } -int DRC_TEST_PROVIDER_LVS::GetNumPhases() const -{ - return m_drcEngine->GetTestFootprints() ? 1 : 0; -} - - -std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_LVS::GetConstraintTypes() const +std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_SCHEMATIC_PARITY::GetConstraintTypes() const { return {}; } @@ -249,5 +241,5 @@ std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_LVS::GetConstraintTypes() const namespace detail { -static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_LVS> dummy; +static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_SCHEMATIC_PARITY> dummy; } diff --git a/pcbnew/drc/drc_test_provider_silk_clearance.cpp b/pcbnew/drc/drc_test_provider_silk_clearance.cpp index 301a51b2a1..7da92e80d1 100644 --- a/pcbnew/drc/drc_test_provider_silk_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_silk_clearance.cpp @@ -68,11 +68,6 @@ public: return "Tests for overlapping silkscreen features."; } - virtual int GetNumPhases() const override - { - return 1; - } - virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; private: diff --git a/pcbnew/drc/drc_test_provider_silk_to_mask.cpp b/pcbnew/drc/drc_test_provider_silk_to_mask.cpp index 5298e3da97..82e97d284d 100644 --- a/pcbnew/drc/drc_test_provider_silk_to_mask.cpp +++ b/pcbnew/drc/drc_test_provider_silk_to_mask.cpp @@ -65,11 +65,6 @@ public: return "Tests for silkscreen being clipped by solder mask"; } - virtual int GetNumPhases() const override - { - return 1; - } - virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; private: diff --git a/pcbnew/drc/drc_test_provider_track_width.cpp b/pcbnew/drc/drc_test_provider_track_width.cpp index 19c1be6c0c..8287e44ab1 100644 --- a/pcbnew/drc/drc_test_provider_track_width.cpp +++ b/pcbnew/drc/drc_test_provider_track_width.cpp @@ -59,8 +59,6 @@ public: } virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - - int GetNumPhases() const override; }; @@ -171,12 +169,6 @@ bool DRC_TEST_PROVIDER_TRACK_WIDTH::Run() } -int DRC_TEST_PROVIDER_TRACK_WIDTH::GetNumPhases() const -{ - return 1; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_TRACK_WIDTH::GetConstraintTypes() const { return { TRACK_WIDTH_CONSTRAINT }; diff --git a/pcbnew/drc/drc_test_provider_via_diameter.cpp b/pcbnew/drc/drc_test_provider_via_diameter.cpp index c4a884894b..8286961e2f 100644 --- a/pcbnew/drc/drc_test_provider_via_diameter.cpp +++ b/pcbnew/drc/drc_test_provider_via_diameter.cpp @@ -58,8 +58,6 @@ public: } virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override; - - int GetNumPhases() const override; }; @@ -160,12 +158,6 @@ bool DRC_TEST_PROVIDER_VIA_DIAMETER::Run() } -int DRC_TEST_PROVIDER_VIA_DIAMETER::GetNumPhases() const -{ - return 1; -} - - std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_VIA_DIAMETER::GetConstraintTypes() const { return { VIA_DIAMETER_CONSTRAINT }; diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index 3a7801c724..834c94db10 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -2168,28 +2168,48 @@ bool FOOTPRINT::HasThroughHolePads() const } -bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* aFirst, - const BOARD_ITEM* aSecond ) const +#define TEST( a, b ) { if( a != b ) return a < b; } +#define TEST_PT( a, b ) { if( a.x != b.x ) return a.x < b.x; if( a.y != b.y ) return a.y < b.y; } + + +bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* itemA, const BOARD_ITEM* itemB ) const { - if( aFirst->Type() != aSecond->Type() ) - return aFirst->Type() < aSecond->Type(); + TEST( itemA->Type(), itemB->Type() ); + TEST( itemA->GetLayer(), itemB->GetLayer() ); - if( aFirst->GetLayer() != aSecond->GetLayer() ) - return aFirst->GetLayer() < aSecond->GetLayer(); - - if( aFirst->Type() == PCB_FP_SHAPE_T ) + if( itemA->Type() == PCB_FP_SHAPE_T ) { - const FP_SHAPE* dwgA = static_cast<const FP_SHAPE*>( aFirst ); - const FP_SHAPE* dwgB = static_cast<const FP_SHAPE*>( aSecond ); + const FP_SHAPE* dwgA = static_cast<const FP_SHAPE*>( itemA ); + const FP_SHAPE* dwgB = static_cast<const FP_SHAPE*>( itemB ); - if( dwgA->GetShape() != dwgB->GetShape() ) - return dwgA->GetShape() < dwgB->GetShape(); + TEST( dwgA->GetShape(), dwgB->GetShape() ); + + TEST_PT( dwgA->GetStart0(), dwgB->GetStart0() ); + TEST_PT( dwgA->GetEnd0(), dwgB->GetEnd0() ); + + if( dwgA->GetShape() == SHAPE_T::ARC ) + { + TEST_PT( dwgA->GetCenter0(), dwgB->GetCenter0() ); + } + else if( dwgA->GetShape() == SHAPE_T::BEZIER ) + { + TEST_PT( dwgA->GetBezierC1_0(), dwgB->GetBezierC1_0() ); + TEST_PT( dwgA->GetBezierC2_0(), dwgB->GetBezierC2_0() ); + } + else if( dwgA->GetShape() == SHAPE_T::POLY ) + { + TEST( dwgA->GetPolyShape().TotalVertices(), dwgB->GetPolyShape().TotalVertices() ); + + for( int ii = 0; ii < dwgA->GetPolyShape().TotalVertices(); ++ii ) + TEST_PT( dwgA->GetPolyShape().CVertex( ii ), dwgB->GetPolyShape().CVertex( ii ) ); + } + + TEST( dwgA->GetWidth(), dwgB->GetWidth() ); } - if( aFirst->m_Uuid != aSecond->m_Uuid ) // shopuld be always the case foer valid boards - return aFirst->m_Uuid < aSecond->m_Uuid; + TEST( itemA->m_Uuid, itemB->m_Uuid ); // should be always the case for valid boards - return aFirst < aSecond; + return itemA < itemB; } @@ -2198,13 +2218,35 @@ bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) co if( aFirst->GetNumber() != aSecond->GetNumber() ) return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0; - if( aFirst->m_Uuid != aSecond->m_Uuid ) // shopuld be always the case foer valid boards - return aFirst->m_Uuid < aSecond->m_Uuid; + TEST_PT( aFirst->GetPos0(), aSecond->GetPos0() ); + TEST_PT( aFirst->GetSize(), aSecond->GetSize() ); + TEST( aFirst->GetShape(), aSecond->GetShape() ); + + TEST( aFirst->m_Uuid, aSecond->m_Uuid ); // should be always the case for valid boards return aFirst < aSecond; } +bool FOOTPRINT::cmp_zones::operator()( const FP_ZONE* aFirst, const FP_ZONE* aSecond ) const +{ + TEST( aFirst->GetPriority(), aSecond->GetPriority() ); + TEST( aFirst->GetLayerSet().Seq(), aSecond->GetLayerSet().Seq() ); + + TEST( aFirst->Outline()->TotalVertices(), aSecond->Outline()->TotalVertices() ); + + for( int ii = 0; ii < aFirst->Outline()->TotalVertices(); ++ii ) + TEST_PT( aFirst->Outline()->CVertex( ii ), aSecond->Outline()->CVertex( ii ) ); + + TEST( aFirst->m_Uuid, aSecond->m_Uuid ); // should be always the case for valid boards + + return aFirst < aSecond; +} + + +#undef TEST + + void FOOTPRINT::TransformPadsWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index d1d57ca6aa..04567eed5d 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -180,8 +180,8 @@ public: bool HasThroughHolePads() const; - std::list<FP_3DMODEL>& Models() { return m_3D_Drawings; } - const std::list<FP_3DMODEL>& Models() const { return m_3D_Drawings; } + std::vector<FP_3DMODEL>& Models() { return m_3D_Drawings; } + const std::vector<FP_3DMODEL>& Models() const { return m_3D_Drawings; } void SetPosition( const wxPoint& aPos ) override; wxPoint GetPosition() const override { return m_pos; } @@ -655,6 +655,11 @@ public: */ static const wxChar* StringLibNameInvalidChars( bool aUserReadable ); + /** + * Return true if a board footprint differs from the library version. + */ + bool FootprintNeedsUpdate( const FOOTPRINT* aLibFootprint ); + /** * Take ownership of caller's heap allocated aInitialComments block. * @@ -723,6 +728,11 @@ public: bool operator()( const PAD* aFirst, const PAD* aSecond ) const; }; + struct cmp_zones + { + bool operator()( const FP_ZONE* aFirst, const FP_ZONE* aSecond ) const; + }; + #if defined(DEBUG) virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } @@ -778,7 +788,7 @@ private: int m_rot90Cost; // Horizontal automatic placement cost ( 0..10 ). int m_rot180Cost; // Vertical automatic placement cost ( 0..10 ). - std::list<FP_3DMODEL> m_3D_Drawings; // Linked list of 3D models. + std::vector<FP_3DMODEL> m_3D_Drawings; // 3D models. std::map<wxString, wxString> m_properties; wxArrayString* m_initial_comments; // s-expression comments in the footprint, // lazily allocated only if needed for speed diff --git a/pcbnew/plugins/kicad/pcb_plugin.cpp b/pcbnew/plugins/kicad/pcb_plugin.cpp index 05cfb07d9e..08a01f16cc 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.cpp +++ b/pcbnew/plugins/kicad/pcb_plugin.cpp @@ -1225,8 +1225,8 @@ void PCB_PLUGIN::format( const FOOTPRINT* aFootprint, int aNestLevel ) const std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> sorted_drawings( aFootprint->GraphicalItems().begin(), aFootprint->GraphicalItems().end() ); - std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aFootprint->Zones().begin(), - aFootprint->Zones().end() ); + std::set<FP_ZONE*, FOOTPRINT::cmp_zones> sorted_zones( aFootprint->Zones().begin(), + aFootprint->Zones().end() ); std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aFootprint->Groups().begin(), aFootprint->Groups().end() ); diff --git a/qa/data/issue1358.kicad_pro b/qa/data/issue1358.kicad_pro index 9a19e8294c..7ed001781b 100755 --- a/qa/data/issue1358.kicad_pro +++ b/qa/data/issue1358.kicad_pro @@ -70,6 +70,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue2528.kicad_pro b/qa/data/issue2528.kicad_pro index c6cdf3e39a..9dac24766f 100644 --- a/qa/data/issue2528.kicad_pro +++ b/qa/data/issue2528.kicad_pro @@ -70,6 +70,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue4774.kicad_pro b/qa/data/issue4774.kicad_pro index d7147fec91..33c06aae03 100644 --- a/qa/data/issue4774.kicad_pro +++ b/qa/data/issue4774.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue5854.kicad_pro b/qa/data/issue5854.kicad_pro index 80c3d5d718..ccf29b8c6b 100644 --- a/qa/data/issue5854.kicad_pro +++ b/qa/data/issue5854.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue5978.kicad_pro b/qa/data/issue5978.kicad_pro index a3bcd1c0ca..7ca859b753 100644 --- a/qa/data/issue5978.kicad_pro +++ b/qa/data/issue5978.kicad_pro @@ -76,6 +76,7 @@ "items_not_allowed": "error", "keepout": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_too_small": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue5990.kicad_pro b/qa/data/issue5990.kicad_pro index 3f6614108b..86c625ec89 100755 --- a/qa/data/issue5990.kicad_pro +++ b/qa/data/issue5990.kicad_pro @@ -76,6 +76,7 @@ "items_not_allowed": "error", "keepout": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_too_small": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue6879.kicad_pro b/qa/data/issue6879.kicad_pro index f5ecbf0b78..8e19f3b9ee 100755 --- a/qa/data/issue6879.kicad_pro +++ b/qa/data/issue6879.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue6945.kicad_pro b/qa/data/issue6945.kicad_pro index b91a40bf0c..df42218c6f 100644 --- a/qa/data/issue6945.kicad_pro +++ b/qa/data/issue6945.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue7267.kicad_pro b/qa/data/issue7267.kicad_pro index c97a4a1d67..daa0e7ee74 100755 --- a/qa/data/issue7267.kicad_pro +++ b/qa/data/issue7267.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue7325.kicad_pro b/qa/data/issue7325.kicad_pro index adc515328a..8e95a6b65a 100755 --- a/qa/data/issue7325.kicad_pro +++ b/qa/data/issue7325.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue7975.kicad_pro b/qa/data/issue7975.kicad_pro index b8f1357531..279e58a00c 100644 --- a/qa/data/issue7975.kicad_pro +++ b/qa/data/issue7975.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue8003.kicad_pro b/qa/data/issue8003.kicad_pro index b50cada7ef..5b527e88fd 100644 --- a/qa/data/issue8003.kicad_pro +++ b/qa/data/issue8003.kicad_pro @@ -48,13 +48,20 @@ "min_clearance": 0.508 } }, - "diff_pair_dimensions": [], + "diff_pair_dimensions": [ + { + "gap": 0.0, + "via_gap": 0.0, + "width": 0.0 + } + ], "drc_exclusions": [], "meta": { "version": 2 }, "rule_severities": { "annular_width": "error", + "aperture_clearance": "error", "clearance": "error", "copper_edge_clearance": "error", "courtyards_overlap": "error", @@ -69,6 +76,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", @@ -81,6 +89,9 @@ "silk_over_copper": "warning", "silk_overlap": "warning", "skew_out_of_range": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", @@ -95,21 +106,32 @@ "allow_blind_buried_vias": false, "allow_microvias": false, "max_error": 0.005, + "min_aperture_clearance": 0.049999999999999996, "min_clearance": 0.0, "min_copper_edge_clearance": 0.01, "min_hole_clearance": 0.0, "min_hole_to_hole": 0.25, "min_microvia_diameter": 0.19999999999999998, "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.12, "min_through_hole_diameter": 0.3, "min_track_width": 0.19999999999999998, "min_via_annular_width": 0.049999999999999996, "min_via_diameter": 0.39999999999999997, "use_height_for_length_calcs": true }, - "track_widths": [], - "via_dimensions": [], + "track_widths": [ + 0.0 + ], + "via_dimensions": [ + { + "diameter": 0.0, + "drill": 0.0 + } + ], "zones_allow_external_fillets": false, "zones_use_no_outline": true }, diff --git a/qa/data/issue8407.kicad_pro b/qa/data/issue8407.kicad_pro index 7221d043b0..31ef7631e7 100644 --- a/qa/data/issue8407.kicad_pro +++ b/qa/data/issue8407.kicad_pro @@ -83,6 +83,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/data/issue9081.kicad_pro b/qa/data/issue9081.kicad_pro index 9c75d2361e..9b51fe1860 100644 --- a/qa/data/issue9081.kicad_pro +++ b/qa/data/issue9081.kicad_pro @@ -75,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "ignore", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", diff --git a/qa/drc_proto/CMakeLists.txt b/qa/drc_proto/CMakeLists.txt index d3dea5d406..3dfc688f93 100644 --- a/qa/drc_proto/CMakeLists.txt +++ b/qa/drc_proto/CMakeLists.txt @@ -44,7 +44,7 @@ add_executable( drc_proto ../../pcbnew/drc/drc_test_provider_connectivity.cpp ../../pcbnew/drc/drc_test_provider_courtyard_clearance.cpp ../../pcbnew/drc/drc_test_provider_via_diameter.cpp - ../../pcbnew/drc/drc_test_provider_lvs.cpp + ../../pcbnew/drc/drc_test_provider_schematic_parity.cpp ../../pcbnew/drc/drc_test_provider_misc.cpp ../../pcbnew/drc/drc_test_provider_silk_to_mask.cpp ../../pcbnew/drc/drc_test_provider_silk_clearance.cpp diff --git a/qa/pns/CMakeLists.txt b/qa/pns/CMakeLists.txt index 2502d71f6c..5ab10c9a52 100644 --- a/qa/pns/CMakeLists.txt +++ b/qa/pns/CMakeLists.txt @@ -43,7 +43,7 @@ add_executable( test_pns ../../pcbnew/drc/drc_test_provider_connectivity.cpp ../../pcbnew/drc/drc_test_provider_courtyard_clearance.cpp ../../pcbnew/drc/drc_test_provider_via_diameter.cpp - ../../pcbnew/drc/drc_test_provider_lvs.cpp + ../../pcbnew/drc/drc_test_provider_schematic_parity.cpp ../../pcbnew/drc/drc_test_provider_misc.cpp ../../pcbnew/drc/drc_test_provider_silk_to_mask.cpp ../../pcbnew/drc/drc_test_provider_silk_clearance.cpp