108 const std::vector<T*>& aNew )
110 struct MATCH_CANDIDATE
117 std::vector<MATCH_CANDIDATE> candidates;
119 for(
T* existing : aExisting )
121 for(
T* updated : aNew )
123 if( existing->Type() != updated->Type() )
126 double similarity = existing->Similarity( *updated );
128 if constexpr( std::is_same_v<T, PAD> )
130 if( existing->GetNumber() == updated->GetNumber() )
134 if( similarity <= 0.0 )
137 candidates.push_back( { existing, updated, similarity } );
141 std::sort( candidates.begin(), candidates.end(),
142 [](
const MATCH_CANDIDATE& a,
const MATCH_CANDIDATE& b )
144 if( a.score != b.score )
145 return a.score > b.score;
147 if( a.existing != b.existing )
148 return a.existing < b.existing;
150 return a.updated < b.updated;
153 std::vector<std::pair<T*, T*>> matches;
154 matches.reserve( candidates.size() );
156 std::unordered_set<T*> matchedExisting;
157 std::unordered_set<T*> matchedNew;
159 for(
const MATCH_CANDIDATE& candidate : candidates )
161 if( matchedExisting.find( candidate.existing ) != matchedExisting.end() )
164 if( matchedNew.find( candidate.updated ) != matchedNew.end() )
167 matchedExisting.insert( candidate.existing );
168 matchedNew.insert( candidate.updated );
169 matches.emplace_back( candidate.existing, candidate.updated );
177 bool aMatchPadPositions,
178 bool aDeleteExtraTexts,
179 bool aResetTextLayers,
180 bool aResetTextEffects,
181 bool aResetTextPositions,
182 bool aResetTextContent,
183 bool aResetFabricationAttrs,
184 bool aResetClearanceOverrides,
186 bool aResetTransform,
187 bool* aUpdated,
bool* aShifted )
190 bool dummyBool =
false;
193 aUpdated = &dummyBool;
196 aShifted = &dummyBool;
215 if( aMatchPadPositions )
219 position += posShift;
220 orientation += angleShift;
235 if( !aResetTransform )
253 std::vector<PAD*> oldPads;
254 oldPads.reserve( aExisting->
Pads().size() );
257 oldPads.push_back(
pad );
259 std::vector<PAD*> newPads;
260 newPads.reserve( aNew->
Pads().size() );
263 newPads.push_back(
pad );
266 std::unordered_set<PAD*> matchedNewPads;
268 for(
const std::pair<PAD*, PAD*>& match : padMatches )
270 PAD* oldPad = match.first;
271 PAD* newPad = match.second;
273 matchedNewPads.insert( newPad );
285 for(
PAD* newPad : aNew->
Pads() )
287 if( matchedNewPads.find( newPad ) != matchedNewPads.end() )
294 std::vector<BOARD_ITEM*> oldDrawings;
298 oldDrawings.push_back( item );
300 std::vector<BOARD_ITEM*> newDrawings;
304 newDrawings.push_back( item );
306 std::vector<std::pair<BOARD_ITEM*, BOARD_ITEM*>> drawingMatches =
matchItemsBySimilarity( oldDrawings, newDrawings );
307 std::unordered_map<BOARD_ITEM*, BOARD_ITEM*> oldToNewDrawings;
308 std::unordered_set<BOARD_ITEM*> matchedNewDrawings;
310 for(
const std::pair<BOARD_ITEM*, BOARD_ITEM*>& match : drawingMatches )
315 oldToNewDrawings[ oldItem ] = newItem;
316 matchedNewDrawings.insert( newItem );
322 if( matchedNewDrawings.find( newItem ) == matchedNewDrawings.end() )
323 newItem->ResetUuid();
326 std::vector<ZONE*> oldZones;
327 oldZones.reserve( aExisting->
Zones().size() );
330 oldZones.push_back( zone );
332 std::vector<ZONE*> newZones;
333 newZones.reserve( aNew->
Zones().size() );
336 newZones.push_back( zone );
339 std::unordered_set<ZONE*> matchedNewZones;
341 for(
const std::pair<ZONE*, ZONE*>& match : zoneMatches )
343 ZONE* oldZone = match.first;
344 ZONE* newZone = match.second;
346 matchedNewZones.insert( newZone );
350 for(
ZONE* newZone : newZones )
352 if( matchedNewZones.find( newZone ) == matchedNewZones.end() )
353 newZone->ResetUuid();
356 std::vector<PCB_POINT*> oldPoints;
357 oldPoints.reserve( aExisting->
Points().size() );
360 oldPoints.push_back( point );
362 std::vector<PCB_POINT*> newPoints;
363 newPoints.reserve( aNew->
Points().size() );
366 newPoints.push_back( point );
368 std::vector<std::pair<PCB_POINT*, PCB_POINT*>> pointMatches =
matchItemsBySimilarity( oldPoints, newPoints );
369 std::unordered_set<PCB_POINT*> matchedNewPoints;
371 for(
const std::pair<PCB_POINT*, PCB_POINT*>& match : pointMatches )
376 matchedNewPoints.insert( newPoint );
382 if( matchedNewPoints.find( newPoint ) == matchedNewPoints.end() )
383 newPoint->ResetUuid();
386 std::vector<PCB_GROUP*> oldGroups;
387 oldGroups.reserve( aExisting->
Groups().size() );
390 oldGroups.push_back(
group );
392 std::vector<PCB_GROUP*> newGroups;
393 newGroups.reserve( aNew->
Groups().size() );
396 newGroups.push_back(
group );
398 std::vector<std::pair<PCB_GROUP*, PCB_GROUP*>> groupMatches =
matchItemsBySimilarity( oldGroups, newGroups );
399 std::unordered_set<PCB_GROUP*> matchedNewGroups;
401 for(
const std::pair<PCB_GROUP*, PCB_GROUP*>& match : groupMatches )
406 matchedNewGroups.insert( newGroup );
412 if( matchedNewGroups.find( newGroup ) == matchedNewGroups.end() )
413 newGroup->ResetUuid();
416 std::vector<PCB_FIELD*> oldFields;
417 std::vector<PCB_FIELD*> newFields;
419 oldFields.reserve( aExisting->
GetFields().size() );
423 wxCHECK2( field,
continue );
425 if( field->IsReference() || field->IsValue() )
428 oldFields.push_back( field );
431 newFields.reserve( aNew->
GetFields().size() );
435 wxCHECK2( field,
continue );
437 if( field->IsReference() || field->IsValue() )
440 newFields.push_back( field );
443 std::vector<std::pair<PCB_FIELD*, PCB_FIELD*>> fieldMatches =
matchItemsBySimilarity( oldFields, newFields );
444 std::unordered_map<PCB_FIELD*, PCB_FIELD*> oldToNewFields;
445 std::unordered_set<PCB_FIELD*> matchedNewFields;
447 for(
const std::pair<PCB_FIELD*, PCB_FIELD*>& match : fieldMatches )
452 oldToNewFields[ oldField ] = newField;
453 matchedNewFields.insert( newField );
459 if( matchedNewFields.find( newField ) == matchedNewFields.end() )
460 newField->ResetUuid();
463 std::unordered_map<PCB_TEXT*, PCB_TEXT*> oldToNewTexts;
465 for(
const std::pair<BOARD_ITEM*, BOARD_ITEM*>& match : drawingMatches )
470 if( oldText && newText )
471 oldToNewTexts[ oldText ] = newText;
474 std::set<PCB_TEXT*> handledTextItems;
488 auto textMatchIt = oldToNewTexts.find( oldTextItem );
490 if( textMatchIt != oldToNewTexts.end() )
491 newTextItem = textMatchIt->second;
495 handledTextItems.insert( newTextItem );
496 processTextItem( *oldTextItem, *newTextItem, posShift, angleShift, aResetTextContent,
497 aResetTextLayers, aResetTextEffects, aResetTextPositions, aUpdated );
499 else if( aDeleteExtraTexts )
505 newTextItem =
static_cast<PCB_TEXT*
>( oldTextItem->
Clone() );
506 handledTextItems.insert( newTextItem );
507 aNew->
Add( newTextItem );
523 if( !handledTextItems.contains( newTextItem ) )
533 aResetTextEffects, aResetTextPositions, aUpdated );
540 aResetTextLayers, aResetTextEffects, aResetTextPositions, aUpdated );
542 std::set<PCB_FIELD*> handledFields;
547 wxCHECK2( oldField,
continue );
550 if( oldField->IsReference() || oldField->IsValue() )
555 auto fieldMatchIt = oldToNewFields.find( oldField );
557 if( fieldMatchIt != oldToNewFields.end() )
558 newField = fieldMatchIt->second;
562 handledFields.insert( newField );
563 processTextItem( *oldField, *newField, posShift, angleShift, aResetTextContent, aResetTextLayers,
564 aResetTextEffects, aResetTextPositions, aUpdated );
566 else if( aDeleteExtraTexts )
573 handledFields.insert( newField );
574 aNew->
Add( newField );
581 wxCHECK2( newField,
continue );
584 if( newField->IsReference() || newField->IsValue() )
587 if( !handledFields.contains( newField ) )
594 if( aResetFabricationAttrs )
606 if( aResetClearanceOverrides )
634 if( aNew->
Models().size() != aExisting->
Models().size() )
640 for(
size_t ii = 0; ii < aNew->
Models().size(); ++ii )
678 if( *aUpdated ==
false )
685 aCommit.
Remove( aExisting );