KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pad_swap_positions.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <board.h>
23#include <footprint.h>
24#include <pad.h>
25#include <padstack.h>
26
27#include <memory>
28
29// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23225
30// When two pads are swapped in the footprint editor, the user expects the visible copper
31// shapes to exchange positions. When a pad has a shape offset relative to its hole, the
32// previous implementation swapped hole/anchor positions instead, producing incorrect results.
33
34BOOST_AUTO_TEST_SUITE( PadSwapPositions )
35
36static std::unique_ptr<PAD> makeRoundPad( FOOTPRINT* aParent, const VECTOR2I& aPos,
37 const VECTOR2I& aOffset,
38 const EDA_ANGLE& aOrient = ANGLE_0 )
39{
40 auto pad = std::make_unique<PAD>( aParent );
41 pad->SetAttribute( PAD_ATTRIB::PTH );
43 pad->SetSize( PADSTACK::ALL_LAYERS, { pcbIUScale.mmToIU( 2.0 ), pcbIUScale.mmToIU( 2.0 ) } );
44 pad->SetDrillSize( { pcbIUScale.mmToIU( 0.8 ), pcbIUScale.mmToIU( 0.8 ) } );
45 pad->SetOrientation( aOrient );
46 pad->SetPosition( aPos );
47 pad->SetOffset( PADSTACK::ALL_LAYERS, aOffset );
48 return pad;
49}
50
51
52BOOST_AUTO_TEST_CASE( SwapPadsWithoutOffset )
53{
54 BOARD board;
56 auto footprint = std::make_unique<FOOTPRINT>( &board );
57
58 const VECTOR2I posA{ pcbIUScale.mmToIU( -1.5 ), 0 };
59 const VECTOR2I posB{ pcbIUScale.mmToIU( 0.0 ), 0 };
60
61 auto padA = makeRoundPad( footprint.get(), posA, { 0, 0 } );
62 auto padB = makeRoundPad( footprint.get(), posB, { 0, 0 } );
63
64 VECTOR2I originalShapeA = padA->ShapePos( PADSTACK::ALL_LAYERS );
65 VECTOR2I originalShapeB = padB->ShapePos( PADSTACK::ALL_LAYERS );
66
67 PAD::SwapShapePositions( padA.get(), padB.get() );
68
69 BOOST_CHECK_EQUAL( padA->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeB );
70 BOOST_CHECK_EQUAL( padB->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeA );
71}
72
73
74BOOST_AUTO_TEST_CASE( SwapPadsWhenOneHasShapeOffset )
75{
76 BOARD board;
78 auto footprint = std::make_unique<FOOTPRINT>( &board );
79
80 // Pad A holds a shape offset from its drill/anchor; pad B does not.
81 const VECTOR2I posA{ pcbIUScale.mmToIU( -1.5 ), pcbIUScale.mmToIU( 0.6 ) };
82 const VECTOR2I offsetA{ pcbIUScale.mmToIU( 0.6 ), 0 };
83 const VECTOR2I posB{ pcbIUScale.mmToIU( 0.0 ), 0 };
84
85 auto padA = makeRoundPad( footprint.get(), posA, offsetA, EDA_ANGLE( 90, DEGREES_T ) );
86 auto padB = makeRoundPad( footprint.get(), posB, { 0, 0 } );
87
88 VECTOR2I originalShapeA = padA->ShapePos( PADSTACK::ALL_LAYERS );
89 VECTOR2I originalShapeB = padB->ShapePos( PADSTACK::ALL_LAYERS );
90
91 PAD::SwapShapePositions( padA.get(), padB.get() );
92
93 // Visible shape centers must be exchanged; each pad keeps its own offset/orientation.
94 BOOST_CHECK_EQUAL( padA->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeB );
95 BOOST_CHECK_EQUAL( padB->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeA );
96 BOOST_CHECK_EQUAL( padA->GetOffset( PADSTACK::ALL_LAYERS ), offsetA );
97 BOOST_CHECK_EQUAL( padB->GetOffset( PADSTACK::ALL_LAYERS ), VECTOR2I( 0, 0 ) );
98}
99
100
101BOOST_AUTO_TEST_CASE( SwapPadsWithDifferentOffsets )
102{
103 BOARD board;
105 auto footprint = std::make_unique<FOOTPRINT>( &board );
106
107 const VECTOR2I posA{ pcbIUScale.mmToIU( -2.0 ), pcbIUScale.mmToIU( 0.5 ) };
108 const VECTOR2I offsetA{ pcbIUScale.mmToIU( 0.3 ), pcbIUScale.mmToIU( 0.1 ) };
109 const VECTOR2I posB{ pcbIUScale.mmToIU( 2.0 ), pcbIUScale.mmToIU( -0.5 ) };
110 const VECTOR2I offsetB{ pcbIUScale.mmToIU( -0.2 ), pcbIUScale.mmToIU( 0.4 ) };
111
112 auto padA = makeRoundPad( footprint.get(), posA, offsetA, EDA_ANGLE( 45, DEGREES_T ) );
113 auto padB = makeRoundPad( footprint.get(), posB, offsetB, EDA_ANGLE( -30, DEGREES_T ) );
114
115 VECTOR2I originalShapeA = padA->ShapePos( PADSTACK::ALL_LAYERS );
116 VECTOR2I originalShapeB = padB->ShapePos( PADSTACK::ALL_LAYERS );
117
118 PAD::SwapShapePositions( padA.get(), padB.get() );
119
120 BOOST_CHECK_EQUAL( padA->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeB );
121 BOOST_CHECK_EQUAL( padB->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeA );
122 // Offsets and orientations must remain with each pad.
123 BOOST_CHECK_EQUAL( padA->GetOffset( PADSTACK::ALL_LAYERS ), offsetA );
124 BOOST_CHECK_EQUAL( padB->GetOffset( PADSTACK::ALL_LAYERS ), offsetB );
125}
126
127
128BOOST_AUTO_TEST_CASE( SwapIsInvolutiveForTwoPads )
129{
130 BOARD board;
132 auto footprint = std::make_unique<FOOTPRINT>( &board );
133
134 const VECTOR2I posA{ pcbIUScale.mmToIU( -1.5 ), pcbIUScale.mmToIU( 0.6 ) };
135 const VECTOR2I offsetA{ pcbIUScale.mmToIU( 0.6 ), 0 };
136 const VECTOR2I posB{ pcbIUScale.mmToIU( 0.0 ), 0 };
137
138 auto padA = makeRoundPad( footprint.get(), posA, offsetA, EDA_ANGLE( 90, DEGREES_T ) );
139 auto padB = makeRoundPad( footprint.get(), posB, { 0, 0 } );
140
141 VECTOR2I originalPosA = padA->GetPosition();
142 VECTOR2I originalPosB = padB->GetPosition();
143
144 PAD::SwapShapePositions( padA.get(), padB.get() );
145 PAD::SwapShapePositions( padA.get(), padB.get() );
146
147 BOOST_CHECK_EQUAL( padA->GetPosition(), originalPosA );
148 BOOST_CHECK_EQUAL( padB->GetPosition(), originalPosB );
149}
150
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
@ FPHOLDER
Definition board.h:364
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
void SetBoardUse(BOARD_USE aUse)
Set what the board is going to be used for.
Definition board.h:384
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:61
static void SwapShapePositions(PAD *aLhs, PAD *aRhs)
Swap the visible shape positions of two pads, preserving each pad's own shape offset.
Definition pad.cpp:1845
STL class.
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
@ DEGREES_T
Definition eda_angle.h:31
STL namespace.
@ PTH
Plated through hole pad.
Definition padstack.h:98
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(SwapPadsWithoutOffset)
static std::unique_ptr< PAD > makeRoundPad(FOOTPRINT *aParent, const VECTOR2I &aPos, const VECTOR2I &aOffset, const EDA_ANGLE &aOrient=ANGLE_0)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683