109 const std::vector<T*>& aNew )
111 struct MATCH_CANDIDATE
120 for(
T* existing : aExisting )
122 for(
T* updated : aNew )
124 if( existing->Type() != updated->Type() )
127 double similarity = existing->Similarity( *updated );
129 if constexpr( std::is_same_v<T, PAD> )
131 if( existing->GetNumber() == updated->GetNumber() )
135 if( similarity <= 0.0 )
138 candidates.push_back( { existing, updated, similarity } );
143 [](
const MATCH_CANDIDATE& a,
const MATCH_CANDIDATE& b )
145 if( a.score != b.score )
146 return a.score > b.score;
148 if( a.existing != b.existing )
149 return a.existing < b.existing;
151 return a.updated < b.updated;
154 std::vector<std::pair<T*, T*>> matches;
157 std::unordered_set<T*> matchedExisting;
158 std::unordered_set<T*> matchedNew;
160 for(
const MATCH_CANDIDATE& candidate :
candidates )
162 if( matchedExisting.find( candidate.existing ) != matchedExisting.end() )
165 if( matchedNew.find( candidate.updated ) != matchedNew.end() )
168 matchedExisting.insert( candidate.existing );
169 matchedNew.insert( candidate.updated );
170 matches.emplace_back( candidate.existing, candidate.updated );
179 bool aMatchPadPositions,
180 bool aDeleteExtraTexts,
181 bool aResetTextLayers,
182 bool aResetTextEffects,
183 bool aResetTextPositions,
184 bool aResetTextContent,
185 bool aResetFabricationAttrs,
186 bool aResetClearanceOverrides,
188 bool aResetTransform,
189 bool* aUpdated,
bool* aShifted )
192 bool dummyBool =
false;
195 aUpdated = &dummyBool;
198 aShifted = &dummyBool;
217 if( aMatchPadPositions )
221 position += posShift;
222 orientation += angleShift;
237 if( !aResetTransform )
256 std::vector<PAD*> oldPads;
257 oldPads.reserve( aExisting->
Pads().size() );
260 oldPads.push_back(
pad );
262 std::vector<PAD*> newPads;
263 newPads.reserve( aNew->
Pads().size() );
266 newPads.push_back(
pad );
269 std::unordered_set<PAD*> matchedNewPads;
271 for(
const std::pair<PAD*, PAD*>& match : padMatches )
273 PAD* oldPad = match.first;
274 PAD* newPad = match.second;
276 matchedNewPads.insert( newPad );
288 for(
PAD* newPad : aNew->
Pads() )
290 if( matchedNewPads.find( newPad ) != matchedNewPads.end() )
297 std::vector<BOARD_ITEM*> oldDrawings;
301 oldDrawings.push_back( item );
303 std::vector<BOARD_ITEM*> newDrawings;
307 newDrawings.push_back( item );
309 std::vector<std::pair<BOARD_ITEM*, BOARD_ITEM*>> drawingMatches =
matchItemsBySimilarity( oldDrawings, newDrawings );
310 std::unordered_map<BOARD_ITEM*, BOARD_ITEM*> oldToNewDrawings;
311 std::unordered_set<BOARD_ITEM*> matchedNewDrawings;
313 for(
const std::pair<BOARD_ITEM*, BOARD_ITEM*>& match : drawingMatches )
318 oldToNewDrawings[ oldItem ] = newItem;
319 matchedNewDrawings.insert( newItem );
325 if( matchedNewDrawings.find( newItem ) == matchedNewDrawings.end() )
326 newItem->ResetUuid();
329 std::vector<ZONE*> oldZones;
330 oldZones.reserve( aExisting->
Zones().size() );
333 oldZones.push_back( zone );
335 std::vector<ZONE*> newZones;
336 newZones.reserve( aNew->
Zones().size() );
339 newZones.push_back( zone );
342 std::unordered_set<ZONE*> matchedNewZones;
344 for(
const std::pair<ZONE*, ZONE*>& match : zoneMatches )
346 ZONE* oldZone = match.first;
347 ZONE* newZone = match.second;
349 matchedNewZones.insert( newZone );
353 for(
ZONE* newZone : newZones )
355 if( matchedNewZones.find( newZone ) == matchedNewZones.end() )
356 newZone->ResetUuid();
359 std::vector<PCB_POINT*> oldPoints;
360 oldPoints.reserve( aExisting->
Points().size() );
363 oldPoints.push_back( point );
365 std::vector<PCB_POINT*> newPoints;
366 newPoints.reserve( aNew->
Points().size() );
369 newPoints.push_back( point );
371 std::vector<std::pair<PCB_POINT*, PCB_POINT*>> pointMatches =
matchItemsBySimilarity( oldPoints, newPoints );
372 std::unordered_set<PCB_POINT*> matchedNewPoints;
374 for(
const std::pair<PCB_POINT*, PCB_POINT*>& match : pointMatches )
379 matchedNewPoints.insert( newPoint );
385 if( matchedNewPoints.find( newPoint ) == matchedNewPoints.end() )
386 newPoint->ResetUuid();
389 std::vector<PCB_GROUP*> oldGroups;
390 oldGroups.reserve( aExisting->
Groups().size() );
393 oldGroups.push_back(
group );
395 std::vector<PCB_GROUP*> newGroups;
396 newGroups.reserve( aNew->
Groups().size() );
399 newGroups.push_back(
group );
401 std::vector<std::pair<PCB_GROUP*, PCB_GROUP*>> groupMatches =
matchItemsBySimilarity( oldGroups, newGroups );
402 std::unordered_set<PCB_GROUP*> matchedNewGroups;
404 for(
const std::pair<PCB_GROUP*, PCB_GROUP*>& match : groupMatches )
409 matchedNewGroups.insert( newGroup );
415 if( matchedNewGroups.find( newGroup ) == matchedNewGroups.end() )
416 newGroup->ResetUuid();
419 std::vector<PCB_FIELD*> oldFields;
420 std::vector<PCB_FIELD*> newFields;
422 oldFields.reserve( aExisting->
GetFields().size() );
426 wxCHECK2( field,
continue );
428 if( field->IsReference() || field->IsValue() )
431 oldFields.push_back( field );
434 newFields.reserve( aNew->
GetFields().size() );
438 wxCHECK2( field,
continue );
440 if( field->IsReference() || field->IsValue() )
443 newFields.push_back( field );
446 std::vector<std::pair<PCB_FIELD*, PCB_FIELD*>> fieldMatches =
matchItemsBySimilarity( oldFields, newFields );
447 std::unordered_map<PCB_FIELD*, PCB_FIELD*> oldToNewFields;
448 std::unordered_set<PCB_FIELD*> matchedNewFields;
450 for(
const std::pair<PCB_FIELD*, PCB_FIELD*>& match : fieldMatches )
455 oldToNewFields[ oldField ] = newField;
456 matchedNewFields.insert( newField );
462 if( matchedNewFields.find( newField ) == matchedNewFields.end() )
463 newField->ResetUuid();
466 std::unordered_map<PCB_TEXT*, PCB_TEXT*> oldToNewTexts;
468 for(
const std::pair<BOARD_ITEM*, BOARD_ITEM*>& match : drawingMatches )
473 if( oldText && newText )
474 oldToNewTexts[ oldText ] = newText;
477 std::set<PCB_TEXT*> handledTextItems;
491 auto textMatchIt = oldToNewTexts.find( oldTextItem );
493 if( textMatchIt != oldToNewTexts.end() )
494 newTextItem = textMatchIt->second;
498 handledTextItems.insert( newTextItem );
499 processTextItem( *oldTextItem, *newTextItem, posShift, angleShift, aResetTextContent,
500 aResetTextLayers, aResetTextEffects, aResetTextPositions, aUpdated );
502 else if( aDeleteExtraTexts )
508 newTextItem =
static_cast<PCB_TEXT*
>( oldTextItem->
Clone() );
509 handledTextItems.insert( newTextItem );
510 aNew->
Add( newTextItem );
526 if( !handledTextItems.contains( newTextItem ) )
536 aResetTextEffects, aResetTextPositions, aUpdated );
543 aResetTextLayers, aResetTextEffects, aResetTextPositions, aUpdated );
545 std::set<PCB_FIELD*> handledFields;
550 wxCHECK2( oldField,
continue );
553 if( oldField->IsReference() || oldField->IsValue() )
558 auto fieldMatchIt = oldToNewFields.find( oldField );
560 if( fieldMatchIt != oldToNewFields.end() )
561 newField = fieldMatchIt->second;
565 handledFields.insert( newField );
566 processTextItem( *oldField, *newField, posShift, angleShift, aResetTextContent, aResetTextLayers,
567 aResetTextEffects, aResetTextPositions, aUpdated );
569 else if( aDeleteExtraTexts )
576 handledFields.insert( newField );
577 aNew->
Add( newField );
584 wxCHECK2( newField,
continue );
587 if( newField->IsReference() || newField->IsValue() )
590 if( !handledFields.contains( newField ) )
597 if( aResetFabricationAttrs )
609 if( aResetClearanceOverrides )
637 if( aNew->
Models().size() != aExisting->
Models().size() )
643 for(
size_t ii = 0; ii < aNew->
Models().size(); ++ii )
679 if( *aUpdated ==
false )
686 aCommit.
Remove( aExisting );