KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sym_item_diff.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 3
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/gpl-3.0.html
19 */
20
23
24#include <lib_symbol.h>
25#include <sch_field.h>
26#include <sch_item.h>
27#include <sch_pin.h>
28
29#include <map>
30#include <set>
31
32
33namespace KICAD_DIFF
34{
35
36namespace
37{
38 wxString graphicTypeName( const SCH_ITEM* aItem )
39 {
40 switch( aItem->Type() )
41 {
42 case SCH_SHAPE_T: return _( "Graphic" );
43 case SCH_TEXT_T: return _( "Text" );
44 case SCH_TEXTBOX_T: return _( "Text Box" );
45 default: return _( "Item" );
46 }
47 }
48
49
50 void diffKeyed( std::vector<SYM_ELEMENT>& aOut, const wxString& aTypeName,
51 const std::map<wxString, const EDA_ITEM*>& aBefore,
52 const std::map<wxString, const EDA_ITEM*>& aAfter )
53 {
54 std::set<wxString> keys;
55
56 for( const auto& [key, item] : aBefore )
57 keys.insert( key );
58
59 for( const auto& [key, item] : aAfter )
60 keys.insert( key );
61
62 for( const wxString& key : keys )
63 {
64 auto beforeIt = aBefore.find( key );
65 auto afterIt = aAfter.find( key );
66 const EDA_ITEM* before = beforeIt != aBefore.end() ? beforeIt->second : nullptr;
67 const EDA_ITEM* after = afterIt != aAfter.end() ? afterIt->second : nullptr;
68
69 if( before && after )
70 {
71 std::vector<PROPERTY_DELTA> deltas = DiffItemProperties( before, after );
72
73 if( !deltas.empty() )
74 aOut.push_back( { after, aTypeName, key, CHANGE_KIND::MODIFIED, std::move( deltas ) } );
75 }
76 else if( before )
77 {
78 aOut.push_back( { before, aTypeName, key, CHANGE_KIND::REMOVED, ItemProperties( before, false ) } );
79 }
80 else
81 {
82 aOut.push_back( { after, aTypeName, key, CHANGE_KIND::ADDED, ItemProperties( after, true ) } );
83 }
84 }
85 }
86
87
88 std::map<wxString, const EDA_ITEM*> pinsByNumber( const LIB_SYMBOL* aSym )
89 {
90 std::map<wxString, const EDA_ITEM*> out;
91
92 if( aSym )
93 {
94 for( SCH_PIN* pin : aSym->GetPins() )
95 {
96 if( pin )
97 out[pin->GetNumber()] = pin;
98 }
99 }
100
101 return out;
102 }
103
104
105 std::map<wxString, const EDA_ITEM*> fieldsByName( const LIB_SYMBOL* aSym )
106 {
107 std::map<wxString, const EDA_ITEM*> out;
108
109 if( aSym )
110 {
111 std::vector<SCH_FIELD*> fields;
112 aSym->GetFields( fields );
113
114 for( SCH_FIELD* field : fields )
115 {
116 if( field )
117 out[field->GetName()] = field;
118 }
119 }
120
121 return out;
122 }
123
124
125 std::vector<const SCH_ITEM*> graphics( const LIB_SYMBOL* aSym )
126 {
127 std::vector<const SCH_ITEM*> out;
128
129 if( aSym )
130 {
131 for( const SCH_ITEM& item : aSym->GetDrawItems() )
132 {
133 if( item.Type() != SCH_PIN_T && item.Type() != SCH_FIELD_T )
134 out.push_back( &item );
135 }
136 }
137
138 return out;
139 }
140} // namespace
141
142
143std::vector<SYM_ELEMENT> DiffSymbolElements( const LIB_SYMBOL* aBefore, const LIB_SYMBOL* aAfter )
144{
145 std::vector<SYM_ELEMENT> out;
146
147 diffKeyed( out, _( "Pin" ), pinsByNumber( aBefore ), pinsByNumber( aAfter ) );
148 diffKeyed( out, _( "Field" ), fieldsByName( aBefore ), fieldsByName( aAfter ) );
149
150 std::vector<const SCH_ITEM*> beforeGraphics = graphics( aBefore );
151 std::vector<const SCH_ITEM*> afterGraphics = graphics( aAfter );
152 std::set<const SCH_ITEM*> matchedBefore;
153 std::set<const SCH_ITEM*> matchedAfter;
154
155 // Pass 1: consume graphics the property diff sees as identical so unchanged
156 // geometry never shows up.
157 for( const SCH_ITEM* after : afterGraphics )
158 {
159 for( const SCH_ITEM* before : beforeGraphics )
160 {
161 if( matchedBefore.count( before ) || before->Type() != after->Type() )
162 continue;
163
164 if( DiffItemProperties( before, after ).empty() )
165 {
166 matchedBefore.insert( before );
167 matchedAfter.insert( after );
168 break;
169 }
170 }
171 }
172
173 // Pass 2: pair each leftover after-graphic with its nearest same-type
174 // leftover before-graphic (a modified shape), else it is newly added.
175 for( const SCH_ITEM* after : afterGraphics )
176 {
177 if( matchedAfter.count( after ) )
178 continue;
179
180 const SCH_ITEM* best = nullptr;
181 double bestDist = 0.0;
182
183 for( const SCH_ITEM* before : beforeGraphics )
184 {
185 if( matchedBefore.count( before ) || before->Type() != after->Type() )
186 continue;
187
188 VECTOR2I delta = before->GetPosition() - after->GetPosition();
189 double dist = (double) delta.x * delta.x + (double) delta.y * delta.y;
190
191 if( !best || dist < bestDist )
192 {
193 best = before;
194 bestDist = dist;
195 }
196 }
197
198 if( best )
199 {
200 matchedBefore.insert( best );
201
202 std::vector<PROPERTY_DELTA> deltas = DiffItemProperties( best, after );
203
204 if( !deltas.empty() )
205 out.push_back( { after, graphicTypeName( after ), wxEmptyString, CHANGE_KIND::MODIFIED,
206 std::move( deltas ) } );
207 }
208 else
209 {
210 out.push_back( { after, graphicTypeName( after ), wxEmptyString, CHANGE_KIND::ADDED,
211 ItemProperties( after, true ) } );
212 }
213 }
214
215 for( const SCH_ITEM* before : beforeGraphics )
216 {
217 if( !matchedBefore.count( before ) )
218 out.push_back( { before, graphicTypeName( before ), wxEmptyString, CHANGE_KIND::REMOVED,
219 ItemProperties( before, false ) } );
220 }
221
222 return out;
223}
224
225} // namespace KICAD_DIFF
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
Define a library symbol object.
Definition lib_symbol.h:79
void GetFields(std::vector< SCH_FIELD * > &aList, bool aVisibleOnly=false) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:709
std::vector< SCH_PIN * > GetPins() const override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
std::vector< SYM_ELEMENT > DiffSymbolElements(const LIB_SYMBOL *aBefore, const LIB_SYMBOL *aAfter)
Compares the elements of two library symbols.
std::vector< PROPERTY_DELTA > DiffItemProperties(const INSPECTABLE *aBefore, const INSPECTABLE *aAfter)
Enumerate the property deltas between two items of the same dynamic type.
std::vector< PROPERTY_DELTA > ItemProperties(const INSPECTABLE *aItem, bool aAsAfter)
List one item's properties as one-sided deltas for an added or removed item.
KIBIS_PIN * pin
int delta
@ SCH_FIELD_T
Definition typeinfo.h:147
@ SCH_SHAPE_T
Definition typeinfo.h:146
@ SCH_TEXT_T
Definition typeinfo.h:148
@ SCH_TEXTBOX_T
Definition typeinfo.h:149
@ SCH_PIN_T
Definition typeinfo.h:150
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683