Fix router clearance check for pads with different sizes on each layer

When using FRONT_INNER_BACK padstack mode on a 2-layer board, the router
would incorrectly report clearance violations. This happened because
syncPad() created an INNER_LAYERS SOLID with PNS_LAYER_RANGE(1, 0),
which got swapped to (0, 1) by the constructor. This caused the SOLID
to be indexed on both F_Cu and B_Cu, leading to incorrect collision
checks that used the wrong pad size.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/20355
This commit is contained in:
Seth Hillbrand
2026-01-25 20:27:47 -08:00
parent 61a2fd3f45
commit 7d1b5db8ac
2 changed files with 48 additions and 3 deletions
+10
View File
@@ -1245,6 +1245,16 @@ std::vector<std::unique_ptr<PNS::SOLID>> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPa
auto makeSolidFromPadLayer =
[&]( PCB_LAYER_ID aLayer )
{
// For FRONT_INNER_BACK mode, skip creating a SOLID for inner layers when there are
// no inner layers (2-layer board). Otherwise PNS_LAYER_RANGE(1, 0) would be swapped
// to (0, 1) and indexed on both F_Cu and B_Cu, causing incorrect collision checks.
if( aPad->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK
&& aLayer != F_Cu && aLayer != B_Cu
&& aPad->BoardCopperLayerCount() <= 2 )
{
return;
}
std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
+38 -3
View File
@@ -25,15 +25,15 @@
#include <settings/settings_manager.h>
#include <optional>
#include <pcbnew/board.h>
#include <pcbnew/pad.h>
#include <pcbnew/pcb_track.h>
#include <pcbnew/board.h>
#include <router/pns_item.h>
#include <router/pns_kicad_iface.h>
#include <router/pns_node.h>
#include <router/pns_router.h>
#include <router/pns_item.h>
#include <router/pns_via.h>
#include <router/pns_kicad_iface.h>
static bool isCopper( const PNS::ITEM* aItem )
{
@@ -505,3 +505,38 @@ BOOST_AUTO_TEST_CASE( PCBViaBackdrillCloneRetainsData )
checkVia( *viaClone );
}
/**
* Test that PNS_LAYER_RANGE(1, 0) is swapped to (0, 1).
*
* This is a minimal regression test for https://gitlab.com/kicad/code/kicad/-/issues/20355
* The actual fix is in pns_kicad_iface.cpp syncPad() which skips creating an
* INNER_LAYERS SOLID on 2-layer boards. This test verifies the layer range behavior
* that motivated the fix.
*/
BOOST_AUTO_TEST_CASE( PNSLayerRangeSwapBehavior )
{
// On a 2-layer board with FRONT_INNER_BACK mode, BoardCopperLayerCount() returns 2.
// The code would calculate PNS_LAYER_RANGE(1, 2 - 2) = PNS_LAYER_RANGE(1, 0)
// Since start > end, the constructor swaps them to (0, 1), which would span
// both F_Cu and B_Cu incorrectly.
PNS_LAYER_RANGE innerLayersRange2Layer( 1, 0 ); // What would happen on 2-layer board
// Verify the swap behavior that causes the bug
BOOST_CHECK_EQUAL( innerLayersRange2Layer.Start(), 0 );
BOOST_CHECK_EQUAL( innerLayersRange2Layer.End(), 1 );
BOOST_CHECK( innerLayersRange2Layer.Overlaps( 0 ) ); // F_Cu
BOOST_CHECK( innerLayersRange2Layer.Overlaps( 1 ) ); // B_Cu
// On a 4-layer board, inner layers are 1 and 2, so PNS_LAYER_RANGE(1, 4-2) = (1, 2)
PNS_LAYER_RANGE innerLayersRange4Layer( 1, 2 ); // Correct for 4-layer board
BOOST_CHECK_EQUAL( innerLayersRange4Layer.Start(), 1 );
BOOST_CHECK_EQUAL( innerLayersRange4Layer.End(), 2 );
BOOST_CHECK( !innerLayersRange4Layer.Overlaps( 0 ) ); // F_Cu - should not overlap
BOOST_CHECK( innerLayersRange4Layer.Overlaps( 1 ) ); // In1_Cu
BOOST_CHECK( innerLayersRange4Layer.Overlaps( 2 ) ); // In2_Cu
BOOST_CHECK( !innerLayersRange4Layer.Overlaps( 3 ) ); // B_Cu - should not overlap
}