diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 9f6c0c6508..aef6ab339b 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -62,6 +62,11 @@ namespace AC_STACK namespace AC_KEYS { +/** + * Should the schematic use the new incremental connectivity algorithm + */ +static const wxChar IncrementalConnectivity[] = wxT( "IncrementalConnectivity" ); + /** * Decide whether to attempt usage of the 3DConnexion mouse */ @@ -326,6 +331,7 @@ ADVANCED_CFG::ADVANCED_CFG() m_UseClipper2 = true; m_Use3DConnexionDriver = false; + m_IncrementalConnectivity = false; loadFromConfigFile(); } @@ -472,6 +478,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::Use3DConnexionDriver, &m_Use3DConnexionDriver, m_Use3DConnexionDriver ) ); + configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::IncrementalConnectivity, + &m_IncrementalConnectivity, m_IncrementalConnectivity ) ); + // Special case for trace mask setting...we just grab them and set them immediately diff --git a/eeschema/connection_graph.cpp b/eeschema/connection_graph.cpp index 75d08b87b9..a57492f3c4 100644 --- a/eeschema/connection_graph.cpp +++ b/eeschema/connection_graph.cpp @@ -228,6 +228,24 @@ bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers ) } +void CONNECTION_SUBGRAPH::getAllConnectedItems( std::set>& aItems, std::set& aSubgraphs ) +{ + CONNECTION_SUBGRAPH* sg = this; + + while( sg->m_absorbed_by ) + sg = sg->m_absorbed_by; + + aSubgraphs.insert( sg ); + aSubgraphs.insert( sg->m_absorbed_subgraphs.begin(), sg->m_absorbed_subgraphs.end() ); + + for( SCH_ITEM* item : sg->m_items ) + aItems.emplace( m_sheet, item ); + + for( CONNECTION_SUBGRAPH* child_sg : sg->m_hier_children ) + child_sg->getAllConnectedItems( aItems, aSubgraphs ); +} + + wxString CONNECTION_SUBGRAPH::GetNetName() const { if( !m_driver || m_dirty ) @@ -377,6 +395,10 @@ void CONNECTION_SUBGRAPH::Absorb( CONNECTION_SUBGRAPH* aOther ) AddItem( item ); } + m_absorbed_subgraphs.insert( aOther ); + m_absorbed_subgraphs.insert( aOther->m_absorbed_subgraphs.begin(), + aOther->m_absorbed_subgraphs.end() ); + m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() ); m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() ); @@ -457,10 +479,84 @@ CONNECTION_SUBGRAPH::PRIORITY CONNECTION_SUBGRAPH::GetDriverPriority( SCH_ITEM* } +void CONNECTION_GRAPH::Merge( CONNECTION_GRAPH& aGraph ) +{ + std::copy( aGraph.m_items.begin(), aGraph.m_items.end(), + std::back_inserter( m_items ) ); + + for( SCH_ITEM* item : aGraph.m_items ) + item->SetConnectionGraph( this ); + + std::copy( aGraph.m_subgraphs.begin(), aGraph.m_subgraphs.end(), + std::back_inserter( m_subgraphs ) ); + + for( CONNECTION_SUBGRAPH* sg : aGraph.m_subgraphs ) + sg->m_graph = this; + + std::copy( aGraph.m_driver_subgraphs.begin(), + aGraph.m_driver_subgraphs.end(), + std::back_inserter( m_driver_subgraphs ) ); + + std::copy( aGraph.m_sheet_to_subgraphs_map.begin(), + aGraph.m_sheet_to_subgraphs_map.end(), + std::inserter( m_sheet_to_subgraphs_map, + m_sheet_to_subgraphs_map.begin() ) ); + + std::copy( aGraph.m_global_power_pins.begin(), + aGraph.m_global_power_pins.end(), + std::back_inserter( m_global_power_pins ) ); + + std::copy( aGraph.m_net_name_to_code_map.begin(), + aGraph.m_net_name_to_code_map.end(), + std::inserter( m_net_name_to_code_map, + m_net_name_to_code_map.begin() ) ); + + std::copy( aGraph.m_bus_name_to_code_map.begin(), + aGraph.m_bus_name_to_code_map.end(), + std::inserter( m_bus_name_to_code_map, + m_net_name_to_code_map.begin() ) ); + + std::copy( aGraph.m_net_code_to_subgraphs_map.begin(), + aGraph.m_net_code_to_subgraphs_map.end(), + std::inserter( m_net_code_to_subgraphs_map, + m_net_code_to_subgraphs_map.begin() ) ); + + std::copy( aGraph.m_net_name_to_subgraphs_map.begin(), + aGraph.m_net_name_to_subgraphs_map.end(), + std::inserter( m_net_name_to_subgraphs_map, + m_net_name_to_subgraphs_map.begin() ) ); + + std::copy( aGraph.m_item_to_subgraph_map.begin(), + aGraph.m_item_to_subgraph_map.end(), + std::inserter( m_item_to_subgraph_map, + m_item_to_subgraph_map.begin() ) ); + + std::copy( aGraph.m_global_power_pins.begin(), + aGraph.m_global_power_pins.end(), + std::back_inserter( m_global_power_pins ) ); + + std::copy( aGraph.m_local_label_cache.begin(), + aGraph.m_local_label_cache.end(), + std::inserter( m_local_label_cache, m_local_label_cache.begin() ) ); + + std::copy( aGraph.m_global_label_cache.begin(), + aGraph.m_global_label_cache.end(), + std::inserter( m_global_label_cache, m_global_label_cache.begin() ) ); + + m_last_bus_code = std::max( m_last_bus_code, aGraph.m_last_bus_code ); + m_last_net_code = std::max( m_last_net_code, aGraph.m_last_net_code ); + m_last_subgraph_code = std::max( m_last_subgraph_code, aGraph.m_last_subgraph_code ); +} + + void CONNECTION_GRAPH::Reset() { for( auto& subgraph : m_subgraphs ) - delete subgraph; + { + /// Only delete subgraphs of which we are the owner + if( subgraph->m_graph == this ) + delete subgraph; + } m_items.clear(); m_subgraphs.clear(); @@ -552,6 +648,169 @@ void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnco recalc_time.Show(); } +std::set> CONNECTION_GRAPH::ExtractAffectedItems( + const std::set &aItems ) +{ + std::set> retvals; + std::set subgraphs; + + auto traverse_subgraph = [&retvals, &subgraphs]( CONNECTION_SUBGRAPH* aSubgraph ) + { + // Find the primary subgraph on this sheet + while( aSubgraph->m_absorbed_by ) + aSubgraph = aSubgraph->m_absorbed_by; + + // Find the top most connected subgraph on all sheets + while( aSubgraph->m_hier_parent ) + aSubgraph = aSubgraph->m_hier_parent; + + // Recurse through all subsheets to collect connected items + aSubgraph->getAllConnectedItems( retvals, subgraphs ); + }; + + for( SCH_ITEM* item : aItems ) + { + auto it = m_item_to_subgraph_map.find( item ); + + if( it == m_item_to_subgraph_map.end() ) + continue; + + CONNECTION_SUBGRAPH* sg = it->second; + + traverse_subgraph( sg ); + + for( auto& bus_it : sg->m_bus_neighbors ) + { + for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second ) + traverse_subgraph( bus_sg ); + } + + for( auto& bus_it : sg->m_bus_parents ) + { + for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second ) + traverse_subgraph( bus_sg ); + } + } + + removeSubgraphs( subgraphs ); + + return retvals; +} + + +void CONNECTION_GRAPH::removeSubgraphs( std::set& aSubgraphs ) +{ + std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() ); + std::sort( m_subgraphs.begin(), m_subgraphs.end() ); + std::set codes_to_remove; + + for( auto el : m_sheet_to_subgraphs_map ) + { + std::sort( el.second.begin(), el.second.end() ); + } + + for( CONNECTION_SUBGRAPH* sg : aSubgraphs ) + { + { + auto it = std::lower_bound( m_driver_subgraphs.begin(), m_driver_subgraphs.end(), sg ); + + if( it != m_driver_subgraphs.end() ) + m_driver_subgraphs.erase( it ); + } + + { + auto it = std::lower_bound( m_subgraphs.begin(), m_subgraphs.end(), sg ); + + if( it != m_subgraphs.end() ) + m_subgraphs.erase( it ); + } + + for( auto el : m_sheet_to_subgraphs_map ) + { + auto it = std::lower_bound( el.second.begin(), el.second.end(), sg ); + + if( it != el.second.end() ) + el.second.erase( it ); + } + + auto remove_sg = [sg]( auto it ) -> bool + { + for( const CONNECTION_SUBGRAPH* test_sg : it->second ) + { + if( sg == test_sg ) + return true; + } + + return false; + }; + + for( auto it = m_global_label_cache.begin(); it != m_global_label_cache.end(); ) + { + if( remove_sg( it ) ) + it = m_global_label_cache.erase( it ); + else + ++it; + } + + for( auto it = m_local_label_cache.begin(); it != m_local_label_cache.end(); ) + { + if( remove_sg( it ) ) + it = m_local_label_cache.erase( it ); + else + ++it; + } + + for( auto it = m_net_code_to_subgraphs_map.begin(); it != m_net_code_to_subgraphs_map.end(); ) + { + if( remove_sg( it ) ) + { + codes_to_remove.insert( it->first.Netcode ); + it = m_net_code_to_subgraphs_map.erase( it ); + } + else + ++it; + } + + for( auto it = m_net_name_to_subgraphs_map.begin(); it != m_net_name_to_subgraphs_map.end(); ) + { + if( remove_sg( it ) ) + it = m_net_name_to_subgraphs_map.erase( it ); + else + ++it; + } + + for( auto it = m_item_to_subgraph_map.begin(); it != m_item_to_subgraph_map.end(); ) + { + if( it->second == sg ) + it = m_item_to_subgraph_map.erase( it ); + else + ++it; + } + } + + for( auto it = m_net_name_to_code_map.begin(); it != m_net_name_to_code_map.end(); ) + { + if( codes_to_remove.find( it->second ) != codes_to_remove.end() ) + it = m_net_name_to_code_map.erase( it ); + else + ++it; + } + + for( auto it = m_bus_name_to_code_map.begin(); it != m_bus_name_to_code_map.end(); ) + { + if( codes_to_remove.find( it->second ) != codes_to_remove.end() ) + it = m_bus_name_to_code_map.erase( it ); + else + ++it; + } + + for( CONNECTION_SUBGRAPH* sg : aSubgraphs ) + { + sg->m_code = -1; + delete sg; + } +} + void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet, const std::vector& aItemList ) @@ -1364,7 +1623,7 @@ void CONNECTION_GRAPH::processSubGraphs() for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs ) { - if( candidate->m_absorbed ) + if( candidate->m_absorbed || candidate == subgraph ) continue; bool match = false; @@ -1922,7 +2181,6 @@ void CONNECTION_GRAPH::propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph, boo candidate->m_code, candidate->m_driver_connection->Name() ); candidate->m_hier_parent = aParent; - search_list.push_back( candidate ); break; } @@ -1969,6 +2227,7 @@ void CONNECTION_GRAPH::propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph, boo aParent->m_code, candidate->m_code, candidate->m_driver_connection->Name() ); + aParent->m_hier_children.insert( candidate ); search_list.push_back( candidate ); break; } diff --git a/eeschema/connection_graph.h b/eeschema/connection_graph.h index 5737e9ceaa..80e00c8b49 100644 --- a/eeschema/connection_graph.h +++ b/eeschema/connection_graph.h @@ -137,6 +137,9 @@ public: return m_items; } + /// Finds all items in the subgraph as well as child subgraphs recursively + void getAllConnectedItems( std::set>& aItems, std::set& aSubgraphs ); + /** * Return the priority (higher is more important) of a candidate driver * @@ -210,6 +213,9 @@ private: /// If this subgraph is absorbed, points to the absorbing (and valid) subgraph CONNECTION_SUBGRAPH* m_absorbed_by; + /// Set of subgraphs that have been absorbed by this subgraph + std::set m_absorbed_subgraphs; + long m_code; /** @@ -236,7 +242,7 @@ private: * * For example, if this subgraph is a bus D[7..0], and on the same sheet there is * a net with label D7, this map will contain an entry for the D7 bus member, and - * the vector will contain a pointer to the D7 net subgraph. + * the set will contain a pointer to the D7 net subgraph. */ std::unordered_map< std::shared_ptr, std::unordered_set > m_bus_neighbors; @@ -258,6 +264,9 @@ private: // If not null, this indicates the subgraph on a higher level sheet that is linked to this one CONNECTION_SUBGRAPH* m_hier_parent; + // If not null, this indicates the subgraph(s) on a lower level sheet that are linked to this one + std::unordered_set m_hier_children; + /// A cache of escaped netnames from schematic items mutable std::unordered_map m_driver_name_cache; @@ -339,6 +348,13 @@ public: m_schematic = aSchematic; } + void SetLastCodes( const CONNECTION_GRAPH* aOther ) + { + m_last_net_code = aOther->m_last_net_code; + m_last_bus_code = aOther->m_last_bus_code; + m_last_subgraph_code = aOther->m_last_subgraph_code; + } + /** * Updates the connection graph for the given list of sheets. * @@ -403,6 +419,24 @@ public: */ wxString GetResolvedSubgraphName( const CONNECTION_SUBGRAPH* aSubGraph ) const; + /** + * For a set of items, this will remove the connected items and their + * associated data including subgraphs and generated codes from the connection graph. + * + * @param aItems A vector of items whose presence should be removed from the graph + * @return The full set of all items associated with the input items that were removed + */ + std::set> ExtractAffectedItems( + const std::set &aItems ); + + /** + * Combines the input graph contents into the current graph. After merging, the + * original graph is invalid. + * + * @param aGraph Input graph reference to add to the current graph + */ + void Merge( CONNECTION_GRAPH& aGraph ); + private: /** * Updates the graphical connectivity between items (i.e. where they touch) @@ -512,6 +546,14 @@ private: */ void propagateToNeighbors( CONNECTION_SUBGRAPH* aSubgraph, bool aForce ); + /** + * Removes references to the given subgraphs from all structures in the + * connection graph. + * + * @param aSubgraphs set of unique subgraphs to find/remove + */ + void removeSubgraphs( std::set& aSubgraphs ); + /** * Search for a matching bus member inside a bus connection * diff --git a/eeschema/dialogs/dialog_label_properties.cpp b/eeschema/dialogs/dialog_label_properties.cpp index 2d3b3f5dc5..39737a23da 100644 --- a/eeschema/dialogs/dialog_label_properties.cpp +++ b/eeschema/dialogs/dialog_label_properties.cpp @@ -595,7 +595,6 @@ bool DIALOG_LABEL_PROPERTIES::TransferDataFromWindow() m_Parent->UpdateItem( m_currentLabel ); m_Parent->GetCanvas()->Refresh(); - m_Parent->OnModify(); return true; } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index f8685dd7e8..fe4cb335c8 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -1671,7 +1671,73 @@ void SCH_EDIT_FRAME::RecalculateConnections( SCH_CLEANUP_FLAGS aCleanupFlags ) GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT ); }; - Schematic().ConnectionGraph()->Recalculate( list, true, &changeHandler ); + if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP + || m_undoList.m_CommandsList.empty() ) + { + Schematic().ConnectionGraph()->Recalculate( list, true, &changeHandler ); + } + else + { + auto& changed_list = m_undoList.m_CommandsList.back(); + std::set changed_items; + std::vector pts; + + for( unsigned ii = 0; ii < changed_list->GetCount(); ++ii ) + { + SCH_ITEM* item = static_cast( changed_list->GetPickedItem( ii ) ); + + std::vector tmp_pts = item->GetConnectionPoints(); + pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() ); + changed_items.insert( item ); + } + + for( VECTOR2I& pt: pts ) + { + for( SCH_ITEM* item : GetScreen()->Items().Overlapping(pt ) ) + { + if( !item->IsConnectable() ) + continue; + + if( SCH_LINE* line = dyn_cast( item ) ) + { + if( line->HitTest( pt ) ) + { + changed_items.insert( item ); + continue; + } + } + + if( item->IsConnected( pt ) ) + changed_items.insert( item ); + } + } + + std::set> all_items = + Schematic().ConnectionGraph()->ExtractAffectedItems( + changed_items ); + + CONNECTION_GRAPH new_graph( &Schematic() ); + + new_graph.SetLastCodes( Schematic().ConnectionGraph() ); + + for( auto&[ path, item ] : all_items ) + { + switch( item->Type() ) + { + case SCH_FIELD_T: + case SCH_PIN_T: + case SCH_SHEET_PIN_T: + static_cast( item->GetParent() )->SetConnectivityDirty(); + break; + + default: + item->SetConnectivityDirty(); + } + } + + new_graph.Recalculate( list, false, &changeHandler ); + Schematic().ConnectionGraph()->Merge( new_graph ); + } GetCanvas()->GetView()->UpdateAllItemsConditionally( [&]( KIGFX::VIEW_ITEM* aItem ) -> int diff --git a/eeschema/sch_item.cpp b/eeschema/sch_item.cpp index 41fad0064f..bb8aea1262 100644 --- a/eeschema/sch_item.cpp +++ b/eeschema/sch_item.cpp @@ -26,13 +26,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -163,6 +164,13 @@ SCH_CONNECTION* SCH_ITEM::Connection( const SCH_SHEET_PATH* aSheet ) const } +void SCH_ITEM::SetConnectionGraph( CONNECTION_GRAPH* aGraph ) +{ + for( auto& [path, conn] : m_connection_map ) + conn->SetGraph( aGraph ); +} + + std::shared_ptr SCH_ITEM::GetEffectiveNetClass( const SCH_SHEET_PATH* aSheet ) const { static std::shared_ptr nullNetclass = std::make_shared( wxEmptyString ); diff --git a/eeschema/sch_item.h b/eeschema/sch_item.h index acf42bd70a..4211190b8d 100644 --- a/eeschema/sch_item.h +++ b/eeschema/sch_item.h @@ -417,6 +417,9 @@ public: void SetConnectivityDirty( bool aDirty = true ) { m_connectivity_dirty = aDirty; } + /// Updates the connection graph for all connections in this item + void SetConnectionGraph( CONNECTION_GRAPH* aGraph ); + virtual void SetLastResolvedState( const SCH_ITEM* aItem ) { } std::shared_ptr GetEffectiveNetClass( const SCH_SHEET_PATH* aSheet = nullptr ) const; diff --git a/include/advanced_config.h b/include/advanced_config.h index 37506ddae7..97d3cd83a3 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -252,6 +252,11 @@ public: */ bool m_Use3DConnexionDriver; + /** + * Use the new incremental netlister for realtime jobs + */ + bool m_IncrementalConnectivity; + ///@}