From 281aa74f30351525b9cc542be742bd6644487415 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Wed, 12 Feb 2025 12:13:52 +0000
Subject: [PATCH] Performance improvements for teardrop regeneration.

Avoid O(n^2) behaviour when deleting many
zones from the board zones list.
---
 pcbnew/board.cpp             | 17 +++++++++++++++++
 pcbnew/board.h               |  6 ++++++
 pcbnew/teardrop/teardrop.cpp | 29 ++++++-----------------------
 3 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp
index b26bf6d080..3fe27cfdf9 100644
--- a/pcbnew/board.cpp
+++ b/pcbnew/board.cpp
@@ -1187,6 +1187,23 @@ void BOARD::FinalizeBulkRemove( std::vector<BOARD_ITEM*>& aRemovedItems )
 }
 
 
+void BOARD::BulkRemoveStaleTeardrops( BOARD_COMMIT& aCommit )
+{
+    for( int ii = (int) m_zones.size() - 1; ii > 0; --ii )
+    {
+        ZONE* zone = m_zones[ii];
+
+        if( zone->IsTeardropArea() && zone->HasFlag( STRUCT_DELETED ) )
+        {
+            m_itemByIdCache.erase( zone->m_Uuid );
+            m_zones.erase( m_zones.begin() + ii );
+            m_connectivity->Remove( zone );
+            aCommit.Removed( zone );
+        }
+    }
+}
+
+
 void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
 {
     // find these calls and fix them!  Don't send me no stinking' nullptr.
diff --git a/pcbnew/board.h b/pcbnew/board.h
index 08c0f5f621..6f304e8ca8 100644
--- a/pcbnew/board.h
+++ b/pcbnew/board.h
@@ -424,6 +424,12 @@ public:
                                                               PCB_GENERATOR_T, PCB_FOOTPRINT_T,
                                                               PCB_TRACE_T, PCB_SHAPE_T } );
 
+    /**
+     * Remove all teardrop zones with the STRUCT_DELETED flag set.  This avoids O(n^2) traversal
+     * over the zone list.
+     */
+    void BulkRemoveStaleTeardrops( BOARD_COMMIT& aCommit );
+
     /**
      * Must be used if Add() is used using a BULK_x ADD_MODE to generate a change event for
      * listeners.
diff --git a/pcbnew/teardrop/teardrop.cpp b/pcbnew/teardrop/teardrop.cpp
index e2daf9d0a8..594f5b725a 100644
--- a/pcbnew/teardrop/teardrop.cpp
+++ b/pcbnew/teardrop/teardrop.cpp
@@ -101,7 +101,6 @@ void TEARDROP_MANAGER::RemoveTeardrops( BOARD_COMMIT& aCommit,
                                         const std::set<PCB_TRACK*>* dirtyTracks )
 {
     std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
-    std::vector<ZONE*>                 stale_teardrops;
 
     auto isStale =
             [&]( ZONE* zone )
@@ -135,14 +134,10 @@ void TEARDROP_MANAGER::RemoveTeardrops( BOARD_COMMIT& aCommit,
     for( ZONE* zone : m_board->Zones() )
     {
         if( zone->IsTeardropArea() && isStale( zone ) )
-            stale_teardrops.push_back( zone );
+            zone->SetFlags( STRUCT_DELETED );
     }
 
-    for( ZONE* td : stale_teardrops )
-    {
-        m_board->Remove( td, REMOVE_MODE::BULK );
-        aCommit.Removed( td );
-    }
+    m_board->BulkRemoveStaleTeardrops( aCommit );
 }
 
 
@@ -162,19 +157,13 @@ void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
     // Old teardrops must be removed, to ensure a clean teardrop rebuild
     if( aForceFullUpdate )
     {
-        std::vector<ZONE*> teardrops;
-
         for( ZONE* zone : m_board->Zones() )
         {
             if( zone->IsTeardropArea() )
-                teardrops.push_back( zone );
+                zone->SetFlags( STRUCT_DELETED );
         }
 
-        for( ZONE* td : teardrops )
-        {
-            m_board->Remove( td, REMOVE_MODE::BULK );
-            aCommit.Removed( td );
-        }
+        m_board->BulkRemoveStaleTeardrops( aCommit );
     }
 
     std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
@@ -283,19 +272,13 @@ void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
 
 void TEARDROP_MANAGER::DeleteTrackToTrackTeardrops( BOARD_COMMIT& aCommit )
 {
-    std::vector<ZONE*> stale_teardrops;
-
     for( ZONE* zone : m_board->Zones() )
     {
         if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
-            stale_teardrops.push_back( zone );
+            zone->SetFlags( STRUCT_DELETED );
     }
 
-    for( ZONE* td : stale_teardrops )
-    {
-        m_board->Remove( td, REMOVE_MODE::BULK );
-        aCommit.Removed( td );
-    }
+    m_board->BulkRemoveStaleTeardrops( aCommit );
 }