269 std::vector<ITEM_CHANGE> children;
271 if( !aBefore || !aAfter )
277 std::vector<ITEM_DESCRIPTOR> beforeDesc;
278 std::vector<ITEM_DESCRIPTOR> afterDesc;
279 std::map<KIID_PATH, const BOARD_ITEM*> beforeMap;
280 std::map<KIID_PATH, const BOARD_ITEM*> afterMap;
291 d.
id.push_back( aChild->m_Uuid );
296 auto collect = [&](
const FOOTPRINT* aFp, std::vector<ITEM_DESCRIPTOR>& aOut,
297 std::map<KIID_PATH, const BOARD_ITEM*>& aMap )
328 collect( aBefore, beforeDesc, beforeMap );
329 collect( aAfter, afterDesc, afterMap );
337 for(
const auto& [idA, idB] : recon.
aToB )
339 auto itA = beforeMap.find( idA );
340 auto itB = afterMap.find( idB );
341 const BOARD_ITEM* a = itA == beforeMap.end() ? nullptr : itA->second;
342 const BOARD_ITEM* b = itB == afterMap.end() ? nullptr : itB->second;
347 std::vector<PROPERTY_DELTA> propDeltas;
352 if( propDeltas.empty() )
362 children.push_back( std::move( c ) );
365 auto isLibraryUuidNoise = [](
const BOARD_ITEM* aItem )
372 auto it = beforeMap.find( idA );
374 if( it == beforeMap.end() || !it->second )
379 if( isLibraryUuidNoise( a ) )
389 children.push_back( std::move( c ) );
394 auto it = afterMap.find( idB );
396 if( it == afterMap.end() || !it->second )
401 if( isLibraryUuidNoise( b ) )
411 children.push_back( std::move( c ) );
441 result.docType = wxS(
"kicad_pcb" );
450 std::vector<ITEM_DESCRIPTOR> beforeDesc;
451 std::vector<ITEM_DESCRIPTOR> afterDesc;
452 std::map<KIID, const BOARD_ITEM*> beforeMap;
453 std::map<KIID, const BOARD_ITEM*> afterMap;
455 beforeDesc.reserve( beforeSet.size() );
456 afterDesc.reserve( afterSet.size() );
464 beforeMap[item->m_Uuid] = item;
473 afterMap[item->m_Uuid] = item;
492 result.changes.push_back( std::move( c ) );
497 if( std::find_if(
result.changes.begin(),
result.changes.end(),
500 return aC.id == dup && aC.kind == CHANGE_KIND::DUPLICATE_UUID;
511 result.changes.push_back( std::move( c ) );
515 for(
const auto& [idA, idB] : recon.
aToB )
517 const KIID& uuidA = idA.back();
518 const KIID& uuidB = idB.back();
519 auto itA = beforeMap.find( uuidA );
520 auto itB = afterMap.find( uuidB );
521 const BOARD_ITEM* a = itA == beforeMap.end() ? nullptr : itA->second;
522 const BOARD_ITEM* b = itB == afterMap.end() ? nullptr : itB->second;
527 std::vector<PROPERTY_DELTA> propDeltas;
535 std::vector<ITEM_CHANGE> childChanges;
537 if(
auto fpA =
dynamic_cast<const FOOTPRINT*
>( a ) )
539 if(
auto fpB =
dynamic_cast<const FOOTPRINT*
>( b ) )
543 if( propDeltas.empty() && childChanges.empty() )
553 c.
children = std::move( childChanges );
554 result.changes.push_back( std::move( c ) );
562 auto isAutoGenerated = [](
const BOARD_ITEM* aItem )
570 if(
auto zone =
dynamic_cast<const ZONE*
>( aItem ) )
571 return zone->IsTeardropArea();
579 auto it = beforeMap.find( idA.back() );
581 if( it == beforeMap.end() || !it->second )
586 if( isAutoGenerated( a ) )
599 if(
auto fp =
dynamic_cast<const FOOTPRINT*
>( a ) )
601 std::vector<ITEM_CHANGE> dummyAfter;
608 result.changes.push_back( std::move( c ) );
614 auto it = afterMap.find( idB.back() );
616 if( it == afterMap.end() || !it->second )
621 if( isAutoGenerated( b ) )
632 if(
auto fp =
dynamic_cast<const FOOTPRINT*
>( b ) )
638 result.changes.push_back( std::move( c ) );
648 std::vector<PROPERTY_DELTA> docDeltas;
658 if( beforeThickness != afterThickness )
664 docDeltas.push_back( std::move( d ) );
676 auto summarizeStackup = [](
const BOARD_STACKUP& aStackup ) -> std::string
695 std::size_t h = std::hash<std::string>{}( aStackup.m_FinishType.ToStdString() );
696 h =
KiHashCombine( h, std::hash<bool>{}( aStackup.m_HasDielectricConstrains ) );
697 h =
KiHashCombine( h, std::hash<bool>{}( aStackup.m_HasThicknessConstrains ) );
698 h =
KiHashCombine( h, std::hash<bool>{}( aStackup.m_EdgePlating ) );
699 h =
KiHashCombine( h, std::hash<int>{}(
static_cast<int>( aStackup.m_EdgeConnectorConstraints ) ) );
706 h =
KiHashCombine( h, std::hash<int>{}(
static_cast<int>( item->GetType() ) ) );
707 h =
KiHashCombine( h, std::hash<int>{}(
static_cast<int>( item->GetBrdLayerId() ) ) );
708 h =
KiHashCombine( h, std::hash<std::string>{}( item->GetLayerName().ToStdString() ) );
709 h =
KiHashCombine( h, std::hash<bool>{}( item->IsEnabled() ) );
711 for(
int sub = 0; sub < item->GetSublayersCount(); ++sub )
713 h =
KiHashCombine( h, std::hash<int>{}( item->GetThickness( sub ) ) );
714 h =
KiHashCombine( h, std::hash<std::string>{}( item->GetMaterial( sub ).ToStdString() ) );
715 h =
KiHashCombine( h, std::hash<std::string>{}( item->GetColor( sub ).ToStdString() ) );
717 if( item->HasEpsilonRValue() )
718 h =
KiHashCombine( h, std::hash<double>{}( item->GetEpsilonR( sub ) ) );
720 if( item->HasLossTangentValue() )
721 h =
KiHashCombine( h, std::hash<double>{}( item->GetLossTangent( sub ) ) );
725 return wxString::Format( wxS(
"%d copper / %d dielectric layers (hash %zx)" ), copper, dielectric, h )
729 if( beforeStackup != afterStackup )
735 docDeltas.push_back( std::move( d ) );
744 if( beforeDRC != afterDRC )
750 docDeltas.push_back( std::move( d ) );
761 auto summarizeNetSettings = [](
const NET_SETTINGS& aSettings ) -> std::string
764 auto hashCombine = [&h]( std::size_t v )
769 auto hashNetclass = [&](
const NETCLASS* nc )
774 hashCombine( std::hash<std::string>{}( nc->GetName().ToStdString() ) );
775 hashCombine(
static_cast<std::size_t
>( nc->GetPriority() ) );
776 hashCombine(
static_cast<std::size_t
>( nc->GetClearanceOpt().value_or( -1 ) ) );
777 hashCombine(
static_cast<std::size_t
>( nc->GetTrackWidthOpt().value_or( -1 ) ) );
778 hashCombine(
static_cast<std::size_t
>( nc->GetViaDiameterOpt().value_or( -1 ) ) );
779 hashCombine(
static_cast<std::size_t
>( nc->GetViaDrillOpt().value_or( -1 ) ) );
780 hashCombine(
static_cast<std::size_t
>( nc->GetuViaDiameterOpt().value_or( -1 ) ) );
781 hashCombine(
static_cast<std::size_t
>( nc->GetuViaDrillOpt().value_or( -1 ) ) );
782 hashCombine(
static_cast<std::size_t
>( nc->GetDiffPairWidthOpt().value_or( -1 ) ) );
783 hashCombine(
static_cast<std::size_t
>( nc->GetDiffPairGapOpt().value_or( -1 ) ) );
784 hashCombine(
static_cast<std::size_t
>( nc->GetDiffPairViaGapOpt().value_or( -1 ) ) );
785 hashCombine(
static_cast<std::size_t
>( nc->GetWireWidthOpt().value_or( -1 ) ) );
786 hashCombine(
static_cast<std::size_t
>( nc->GetBusWidthOpt().value_or( -1 ) ) );
787 hashCombine(
static_cast<std::size_t
>( nc->GetLineStyleOpt().value_or( -1 ) ) );
788 hashCombine( std::hash<std::string>{}( nc->GetTuningProfile().ToStdString() ) );
792 hashCombine( std::hash<std::string>{}( nc->GetSchematicColor(
true ).ToCSSString().ToStdString() ) );
793 hashCombine( std::hash<std::string>{}( nc->GetPcbColor(
true ).ToCSSString().ToStdString() ) );
796 hashNetclass( aSettings.GetDefaultNetclass().get() );
798 for(
const auto& [
name, nc] : aSettings.GetNetclasses() )
799 hashNetclass( nc.get() );
801 for(
const auto& [netname, classes] : aSettings.GetNetclassLabelAssignments() )
803 hashCombine( std::hash<std::string>{}( netname.ToStdString() ) );
805 for(
const wxString& c : classes )
806 hashCombine( std::hash<std::string>{}( c.ToStdString() ) );
809 for(
const auto& [
chain, className] : aSettings.GetNetChainClasses() )
811 hashCombine( std::hash<std::string>{}(
chain.ToStdString() ) );
812 hashCombine( std::hash<std::string>{}( className.ToStdString() ) );
815 for(
const auto& [netname, color] : aSettings.GetNetColorAssignments() )
817 hashCombine( std::hash<std::string>{}( netname.ToStdString() ) );
818 hashCombine( std::hash<std::string>{}( color.ToCSSString().ToStdString() ) );
821 const std::size_t classCount = aSettings.GetNetclasses().size() + ( aSettings.GetDefaultNetclass() ? 1u : 0u );
823 return wxString::Format( wxS(
"%zu netclass(es) (hash %zx)" ), classCount, h ).ToStdString();
826 const std::shared_ptr<NET_SETTINGS>& beforeNet = beforeDS.
m_NetSettings;
827 const std::shared_ptr<NET_SETTINGS>& afterNet = afterDS.
m_NetSettings;
833 if( beforeNet && afterNet && *beforeNet != *afterNet )
839 docDeltas.push_back( std::move( d ) );
846 auto readSiblingDruContent = [](
const BOARD* aBoard ) -> wxString
849 return wxEmptyString;
851 wxString boardPath = aBoard->GetFileName();
853 if( boardPath.IsEmpty() )
854 return wxEmptyString;
856 wxFileName fn( boardPath );
859 if( !fn.FileExists() )
860 return wxEmptyString;
862 wxFile file( fn.GetFullPath() );
864 if( !file.IsOpened() )
865 return wxEmptyString;
868 file.ReadAll( &contents );
872 auto summarizeRules = [](
const wxString& aContents ) -> std::string
874 if( aContents.IsEmpty() )
875 return "(no custom rules)";
877 std::size_t h = std::hash<std::string>{}( aContents.ToStdString() );
878 return wxString::Format( wxS(
"%zu byte(s) (hash %zx)" ), aContents.size(), h ).ToStdString();
881 const wxString beforeRules = readSiblingDruContent(
m_before );
882 const wxString afterRules = readSiblingDruContent(
m_after );
884 if( beforeRules != afterRules )
890 docDeltas.push_back( std::move( d ) );
896 auto readProjectFileNamed = [](
const BOARD* aBoard,
const std::string& aFileName ) -> wxString
899 return wxEmptyString;
903 if( boardPath.IsEmpty() )
904 return wxEmptyString;
906 wxFileName fn( boardPath );
907 fn.SetFullName( wxString::FromUTF8( aFileName ) );
909 if( !fn.FileExists() )
910 return wxEmptyString;
912 wxFile file( fn.GetFullPath() );
914 if( !file.IsOpened() )
915 return wxEmptyString;
918 file.ReadAll( &contents );
922 auto summarizeTable = [](
const wxString& aContents,
const wxString& aLabel ) -> std::string
924 if( aContents.IsEmpty() )
925 return wxString::Format( wxS(
"(no %s)" ), aLabel ).ToStdString();
927 std::size_t h = std::hash<std::string>{}( aContents.ToStdString() );
928 return wxString::Format( wxS(
"%zu byte(s) (hash %zx)" ), aContents.size(), h ).ToStdString();
934 if( beforeFp != afterFp )
940 docDeltas.push_back( std::move( d ) );
946 if( beforeSym != afterSym )
952 docDeltas.push_back( std::move( d ) );
958 auto boardDrawingSheet = [](
const BOARD* aBoard ) -> wxString
961 return wxEmptyString;
966 const wxString beforeSheet = boardDrawingSheet(
m_before );
967 const wxString afterSheet = boardDrawingSheet(
m_after );
969 if( beforeSheet != afterSheet )
975 docDeltas.push_back( std::move( d ) );
978 if( !docDeltas.empty() )
986 result.changes.push_back( std::move( c ) );