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, 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
25
26#include <board.h>
27#include <footprint.h>
28#include <pad.h>
29#include <padstack.h>
30
31#include <memory>
32
33// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23225
34// When two pads are swapped in the footprint editor, the user expects the visible copper
35// shapes to exchange positions. When a pad has a shape offset relative to its hole, the
36// previous implementation swapped hole/anchor positions instead, producing incorrect results.
37
38BOOST_AUTO_TEST_SUITE( PadSwapPositions )
39
40static std::unique_ptr<PAD> makeRoundPad( FOOTPRINT* aParent, const VECTOR2I& aPos,
41 const VECTOR2I& aOffset,
42 const EDA_ANGLE& aOrient = ANGLE_0 )
43{
44 auto pad = std::make_unique<PAD>( aParent );
45 pad->SetAttribute( PAD_ATTRIB::PTH );
47 pad->SetSize( PADSTACK::ALL_LAYERS, { pcbIUScale.mmToIU( 2.0 ), pcbIUScale.mmToIU( 2.0 ) } );
48 pad->SetDrillSize( { pcbIUScale.mmToIU( 0.8 ), pcbIUScale.mmToIU( 0.8 ) } );
49 pad->SetOrientation( aOrient );
50 pad->SetPosition( aPos );
51 pad->SetOffset( PADSTACK::ALL_LAYERS, aOffset );
52 return pad;
53}
54
55
56BOOST_AUTO_TEST_CASE( SwapPadsWithoutOffset )
57{
58 BOARD board;
60 auto footprint = std::make_unique<FOOTPRINT>( &board );
61
62 const VECTOR2I posA{ pcbIUScale.mmToIU( -1.5 ), 0 };
63 const VECTOR2I posB{ pcbIUScale.mmToIU( 0.0 ), 0 };
64
65 auto padA = makeRoundPad( footprint.get(), posA, { 0, 0 } );
66 auto padB = makeRoundPad( footprint.get(), posB, { 0, 0 } );
67
68 VECTOR2I originalShapeA = padA->ShapePos( PADSTACK::ALL_LAYERS );
69 VECTOR2I originalShapeB = padB->ShapePos( PADSTACK::ALL_LAYERS );
70
71 PAD::SwapShapePositions( padA.get(), padB.get() );
72
73 BOOST_CHECK_EQUAL( padA->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeB );
74 BOOST_CHECK_EQUAL( padB->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeA );
75}
76
77
78BOOST_AUTO_TEST_CASE( SwapPadsWhenOneHasShapeOffset )
79{
80 BOARD board;
82 auto footprint = std::make_unique<FOOTPRINT>( &board );
83
84 // Pad A holds a shape offset from its drill/anchor; pad B does not.
85 const VECTOR2I posA{ pcbIUScale.mmToIU( -1.5 ), pcbIUScale.mmToIU( 0.6 ) };
86 const VECTOR2I offsetA{ pcbIUScale.mmToIU( 0.6 ), 0 };
87 const VECTOR2I posB{ pcbIUScale.mmToIU( 0.0 ), 0 };
88
89 auto padA = makeRoundPad( footprint.get(), posA, offsetA, EDA_ANGLE( 90, DEGREES_T ) );
90 auto padB = makeRoundPad( footprint.get(), posB, { 0, 0 } );
91
92 VECTOR2I originalShapeA = padA->ShapePos( PADSTACK::ALL_LAYERS );
93 VECTOR2I originalShapeB = padB->ShapePos( PADSTACK::ALL_LAYERS );
94
95 PAD::SwapShapePositions( padA.get(), padB.get() );
96
97 // Visible shape centers must be exchanged; each pad keeps its own offset/orientation.
98 BOOST_CHECK_EQUAL( padA->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeB );
99 BOOST_CHECK_EQUAL( padB->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeA );
100 BOOST_CHECK_EQUAL( padA->GetOffset( PADSTACK::ALL_LAYERS ), offsetA );
101 BOOST_CHECK_EQUAL( padB->GetOffset( PADSTACK::ALL_LAYERS ), VECTOR2I( 0, 0 ) );
102}
103
104
105BOOST_AUTO_TEST_CASE( SwapPadsWithDifferentOffsets )
106{
107 BOARD board;
109 auto footprint = std::make_unique<FOOTPRINT>( &board );
110
111 const VECTOR2I posA{ pcbIUScale.mmToIU( -2.0 ), pcbIUScale.mmToIU( 0.5 ) };
112 const VECTOR2I offsetA{ pcbIUScale.mmToIU( 0.3 ), pcbIUScale.mmToIU( 0.1 ) };
113 const VECTOR2I posB{ pcbIUScale.mmToIU( 2.0 ), pcbIUScale.mmToIU( -0.5 ) };
114 const VECTOR2I offsetB{ pcbIUScale.mmToIU( -0.2 ), pcbIUScale.mmToIU( 0.4 ) };
115
116 auto padA = makeRoundPad( footprint.get(), posA, offsetA, EDA_ANGLE( 45, DEGREES_T ) );
117 auto padB = makeRoundPad( footprint.get(), posB, offsetB, EDA_ANGLE( -30, DEGREES_T ) );
118
119 VECTOR2I originalShapeA = padA->ShapePos( PADSTACK::ALL_LAYERS );
120 VECTOR2I originalShapeB = padB->ShapePos( PADSTACK::ALL_LAYERS );
121
122 PAD::SwapShapePositions( padA.get(), padB.get() );
123
124 BOOST_CHECK_EQUAL( padA->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeB );
125 BOOST_CHECK_EQUAL( padB->ShapePos( PADSTACK::ALL_LAYERS ), originalShapeA );
126 // Offsets and orientations must remain with each pad.
127 BOOST_CHECK_EQUAL( padA->GetOffset( PADSTACK::ALL_LAYERS ), offsetA );
128 BOOST_CHECK_EQUAL( padB->GetOffset( PADSTACK::ALL_LAYERS ), offsetB );
129}
130
131
132BOOST_AUTO_TEST_CASE( SwapIsInvolutiveForTwoPads )
133{
134 BOARD board;
136 auto footprint = std::make_unique<FOOTPRINT>( &board );
137
138 const VECTOR2I posA{ pcbIUScale.mmToIU( -1.5 ), pcbIUScale.mmToIU( 0.6 ) };
139 const VECTOR2I offsetA{ pcbIUScale.mmToIU( 0.6 ), 0 };
140 const VECTOR2I posB{ pcbIUScale.mmToIU( 0.0 ), 0 };
141
142 auto padA = makeRoundPad( footprint.get(), posA, offsetA, EDA_ANGLE( 90, DEGREES_T ) );
143 auto padB = makeRoundPad( footprint.get(), posB, { 0, 0 } );
144
145 VECTOR2I originalPosA = padA->GetPosition();
146 VECTOR2I originalPosB = padB->GetPosition();
147
148 PAD::SwapShapePositions( padA.get(), padB.get() );
149 PAD::SwapShapePositions( padA.get(), padB.get() );
150
151 BOOST_CHECK_EQUAL( padA->GetPosition(), originalPosA );
152 BOOST_CHECK_EQUAL( padB->GetPosition(), originalPosB );
153}
154
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
@ FPHOLDER
Definition board.h:315
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void SetBoardUse(BOARD_USE aUse)
Set what the board is going to be used for.
Definition board.h:335
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:55
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:1573
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:687