KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint_utils.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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
20#include "footprint_utils.h"
21
22#include <core/mirror.h>
23#include <trigo.h>
24
25#include <footprint.h>
26#include <item_realignment.h>
27#include <pad.h>
28
29
30bool ComputeFootprintShift( const FOOTPRINT& aExisting, const FOOTPRINT& aNew, VECTOR2I& aShift,
31 EDA_ANGLE& aAngleShift )
32{
33 // First, we will collect a list of "useful" corresponding points in the two footprints.
34 // To be useful, a point from a pad must have a unique pad number, as in the general case, we
35 // won't know which of multiple same-named pads in one footprint correspond to which in the
36 // other.
37 //
38 // Theoretically, we could use graphics items and other hints like pad shape matching
39 // but these would be hard to unambiguously match in the general case and pad numbers should
40 // cover 99% of cases.
41
42 const auto getUniquelyNumberedPads =
43 []( const FOOTPRINT& fp ) -> std::unordered_map<wxString, VECTOR2I>
44 {
45 std::unordered_map<wxString, VECTOR2I> result;
46 std::unordered_set<wxString> seenDuplicate;
47
48 for( PAD* pad : fp.Pads() )
49 {
50 const wxString& number = pad->GetNumber();
51
52 // Already seen a pad with this number, so not unique
53 // (and it won't be found in result)
54 if( seenDuplicate.find( number ) != seenDuplicate.end() )
55 continue;
56
57 // Already have a pad with this number, so not unique.
58 // Remove the previous entry from the result and mark this number as seen.
59 if( result.find( number ) != result.end() )
60 {
61 result.erase( number );
62 seenDuplicate.insert( number );
63 continue;
64 }
65
66 // Use the exact library-frame position. PAD::GetFPRelativePosition()
67 // applies the footprint transform in integer coordinates, which can
68 // cause rounding errors.
69 result[number] = pad->GetLibraryPosition();
70 }
71
72 return result;
73 };
74
75 std::unordered_map<wxString, VECTOR2I> existingPads = getUniquelyNumberedPads( aExisting );
76 std::unordered_map<wxString, VECTOR2I> newPads = getUniquelyNumberedPads( aNew );
77
78 // Pads of footprints on opposite sides are mirror images in the library frame, so they
79 // cannot be aligned by a rotation and translation. Mirror the new pads onto the existing
80 // footprint's side to match. FOOTPRINT::Flip() always mirrors children about the
81 // footprint's library origin, whatever the flip centre, so this mirror is about (0,0).
82 if( aExisting.IsFlipped() != aNew.IsFlipped() )
83 {
84 for( auto& [number, pos] : newPads )
85 MIRROR( pos.y, 0 );
86 }
87
88 std::vector<VECTOR2I> existingPoints;
89 std::vector<VECTOR2I> newPoints;
90
91 // The matching points are the ones with the same unique pad number in both footprints
92 // If a pad is missing in the new footprint, it won't be included.
93 for( const auto& [number, pos] : existingPads )
94 {
95 auto it = newPads.find( number );
96
97 if( it == newPads.end() )
98 continue;
99
100 existingPoints.push_back( pos );
101 newPoints.push_back( it->second );
102 }
103
104 ORTHO_ITEM_REALIGNER aligner;
105
106 std::optional<ITEM_REALIGNER_BASE::TRANSFORM> transform =
107 aligner.GetTransform( existingPoints, newPoints );
108
109 if( !transform.has_value() )
110 {
111 // Failed to compute a transform - perhaps there were no useful pads
112 aShift = VECTOR2I( 0, 0 );
113 aAngleShift = ANGLE_0;
114 return false;
115 }
116
117 aShift = GetRotated( transform->m_Translation, aExisting.GetOrientation() );
118 aAngleShift = transform->m_Rotation;
119
120 return true;
121}
EDA_ANGLE GetOrientation() const
Definition footprint.h:438
bool IsFlipped() const
Definition footprint.h:660
This is a relatively straight-forward implementation of the ITEM_REALIGNER_BASE that should handle mo...
std::optional< TRANSFORM > GetTransform(const std::vector< VECTOR2I > &aPtsA, const std::vector< VECTOR2I > &aPtsB) const override
Compute the best fit transform to align the two sets of points.
Definition pad.h:61
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:422
bool ComputeFootprintShift(const FOOTPRINT &aExisting, const FOOTPRINT &aNew, VECTOR2I &aShift, EDA_ANGLE &aAngleShift)
Compute position and angle shift between two footprints.
Collection of reusable/testable functions for footprint manipulation.
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:41
wxString result
Test unit parsing edge cases and error handling.
VECTOR2I GetRotated(const VECTOR2I &aVector, const EDA_ANGLE &aAngle)
Return a new VECTOR2I that is the result of rotating aVector by aAngle.
Definition trigo.h:73
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683