From e95ae05dcf78ffc74eb2696512ec69083e1509ad Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Wed, 4 Jun 2025 20:30:48 -0400 Subject: [PATCH] Move CleanUp to SCHEMATIC --- eeschema/bus-wire-junction.cpp | 184 --------------------- eeschema/sch_base_frame.h | 2 +- eeschema/sch_edit_frame.cpp | 4 +- eeschema/sch_edit_frame.h | 9 - eeschema/schematic.cpp | 193 ++++++++++++++++++++++ eeschema/schematic.h | 9 + eeschema/schematic_holder.h | 5 + eeschema/tools/sch_drawing_tools.cpp | 2 +- eeschema/tools/sch_edit_tool.cpp | 6 +- eeschema/tools/sch_line_wire_bus_tool.cpp | 2 +- eeschema/tools/sch_move_tool.cpp | 6 +- 11 files changed, 218 insertions(+), 204 deletions(-) diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp index f5c69e1272..229d56e905 100644 --- a/eeschema/bus-wire-junction.cpp +++ b/eeschema/bus-wire-junction.cpp @@ -112,190 +112,6 @@ bool SCH_EDIT_FRAME::TrimWire( SCH_COMMIT* aCommit, const VECTOR2I& aStart, cons } -void SCH_EDIT_FRAME::SchematicCleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen ) -{ - SCH_SELECTION_TOOL* selectionTool = m_toolManager->GetTool(); - std::vector lines; - std::vector junctions; - std::vector ncs; - std::vector items_to_remove; - bool changed = true; - - if( aScreen == nullptr ) - aScreen = GetScreen(); - - auto remove_item = [&]( SCH_ITEM* aItem ) -> void - { - changed = true; - - if( !( aItem->GetFlags() & STRUCT_DELETED ) ) - { - aItem->SetFlags( STRUCT_DELETED ); - - if( aItem->IsSelected() ) - selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ ); - - RemoveFromScreen( aItem, aScreen ); - aCommit->Removed( aItem, aScreen ); - } - }; - - Schematic().BreakSegmentsOnJunctions( aCommit, aScreen ); - - for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) ) - { - if( !aScreen->IsExplicitJunction( item->GetPosition() ) ) - items_to_remove.push_back( item ); - else - junctions.push_back( static_cast( item ) ); - } - - for( SCH_ITEM* item : items_to_remove ) - remove_item( item ); - - for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) ) - ncs.push_back( static_cast( item ) ); - - alg::for_all_pairs( junctions.begin(), junctions.end(), - [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond ) - { - if( ( aFirst->GetEditFlags() & STRUCT_DELETED ) - || ( aSecond->GetEditFlags() & STRUCT_DELETED ) ) - { - return; - } - - if( aFirst->GetPosition() == aSecond->GetPosition() ) - remove_item( aSecond ); - } ); - - alg::for_all_pairs( ncs.begin(), ncs.end(), - [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond ) - { - if( ( aFirst->GetEditFlags() & STRUCT_DELETED ) - || ( aSecond->GetEditFlags() & STRUCT_DELETED ) ) - { - return; - } - - if( aFirst->GetPosition() == aSecond->GetPosition() ) - remove_item( aSecond ); - } ); - - - auto minX = []( const SCH_LINE* l ) - { - return std::min( l->GetStartPoint().x, l->GetEndPoint().x ); - }; - - auto maxX = []( const SCH_LINE* l ) - { - return std::max( l->GetStartPoint().x, l->GetEndPoint().x ); - }; - - auto minY = []( const SCH_LINE* l ) - { - return std::min( l->GetStartPoint().y, l->GetEndPoint().y ); - }; - - auto maxY = []( const SCH_LINE* l ) - { - return std::max( l->GetStartPoint().y, l->GetEndPoint().y ); - }; - - // Would be nice to put lines in a canonical form here by swapping - // start <-> end as needed but I don't know what swapping breaks. - while( changed ) - { - changed = false; - lines.clear(); - - for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) ) - { - if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS ) - lines.push_back( static_cast( item ) ); - } - - // Sort by minimum X position - std::sort( lines.begin(), lines.end(), - [&]( const SCH_LINE* a, const SCH_LINE* b ) - { - return minX( a ) < minX( b ); - } ); - - for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 ) - { - SCH_LINE* firstLine = *it1; - - if( firstLine->GetEditFlags() & STRUCT_DELETED ) - continue; - - if( firstLine->IsNull() ) - { - remove_item( firstLine ); - continue; - } - - int firstRightXEdge = maxX( firstLine ); - auto it2 = it1; - - for( ++it2; it2 != lines.end(); ++it2 ) - { - SCH_LINE* secondLine = *it2; - int secondLeftXEdge = minX( secondLine ); - - // impossible to overlap remaining lines - if( secondLeftXEdge > firstRightXEdge ) - break; - - // No Y axis overlap - if( !( std::max( minY( firstLine ), minY( secondLine ) ) - <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) ) - { - continue; - } - - if( secondLine->GetFlags() & STRUCT_DELETED ) - continue; - - if( !secondLine->IsParallel( firstLine ) - || !secondLine->IsStrokeEquivalent( firstLine ) - || secondLine->GetLayer() != firstLine->GetLayer() ) - { - continue; - } - - // Remove identical lines - if( firstLine->IsEndPoint( secondLine->GetStartPoint() ) - && firstLine->IsEndPoint( secondLine->GetEndPoint() ) ) - { - remove_item( secondLine ); - continue; - } - - // See if we can merge an overlap (or two colinear touching segments with - // no junction where they meet). - SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true ); - - if( mergedLine != nullptr ) - { - remove_item( firstLine ); - remove_item( secondLine ); - - AddToScreen( mergedLine, aScreen ); - aCommit->Added( mergedLine, aScreen ); - - if( firstLine->IsSelected() || secondLine->IsSelected() ) - selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ ); - - break; - } - } - } - } -} - - void SCH_EDIT_FRAME::DeleteJunction( SCH_COMMIT* aCommit, SCH_ITEM* aJunction ) { SCH_SCREEN* screen = GetScreen(); diff --git a/eeschema/sch_base_frame.h b/eeschema/sch_base_frame.h index 3fcdeb7317..e00af25ea1 100644 --- a/eeschema/sch_base_frame.h +++ b/eeschema/sch_base_frame.h @@ -216,7 +216,7 @@ public: * Remove an item from the screen (and view) * aScreen is the screen the item is located on, if not the current screen */ - void RemoveFromScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen ); + void RemoveFromScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen ) override; /** * Mark an item for refresh. diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 1230cdd984..d0162a03ed 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -1780,12 +1780,12 @@ void SCH_EDIT_FRAME::RecalculateConnections( SCH_COMMIT* aCommit, SCH_CLEANUP_FL // Ensure schematic graph is accurate if( aCleanupFlags == LOCAL_CLEANUP ) { - SchematicCleanUp( aCommit, GetScreen() ); + Schematic().CleanUp( aCommit, GetScreen() ); } else if( aCleanupFlags == GLOBAL_CLEANUP ) { for( const SCH_SHEET_PATH& sheet : list ) - SchematicCleanUp( aCommit, sheet.LastScreen() ); + Schematic().CleanUp( aCommit, sheet.LastScreen() ); } timer.Stop(); diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index d9d8e0653a..24da30768c 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -481,15 +481,6 @@ public: SCH_JUNCTION* AddJunction( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen, const VECTOR2I& aPos ); - /** - * Perform routine schematic cleaning including breaking wire and buses and deleting - * identical objects superimposed on top of each other. - * - * @param aCommit Transaction container used to record changes for undo/redo - * @param aScreen is the screen to examine, or nullptr to examine the current screen - */ - void SchematicCleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen = nullptr ); - /** * If any single wire passes through _both points_, remove the portion between the two points, * potentially splitting the wire into two. diff --git a/eeschema/schematic.cpp b/eeschema/schematic.cpp index 2733b57b30..20a96c4786 100644 --- a/eeschema/schematic.cpp +++ b/eeschema/schematic.cpp @@ -36,7 +36,9 @@ #include #include #include +#include #include +#include #include #include @@ -1062,4 +1064,195 @@ bool SCHEMATIC::BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScre } return brokenSegments; +} + + +void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen ) +{ + SCH_SELECTION_TOOL* selectionTool = m_schematicHolder->GetSelectionTool(); + std::vector lines; + std::vector junctions; + std::vector ncs; + std::vector items_to_remove; + bool changed = true; + + if( aScreen == nullptr ) + aScreen = GetCurrentScreen(); + + auto remove_item = [&]( SCH_ITEM* aItem ) -> void + { + changed = true; + + if( !( aItem->GetFlags() & STRUCT_DELETED ) ) + { + aItem->SetFlags( STRUCT_DELETED ); + + if( aItem->IsSelected() && selectionTool ) + selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ ); + + if( m_schematicHolder ) + { + m_schematicHolder->RemoveFromScreen( aItem, aScreen ); + } + aCommit->Removed( aItem, aScreen ); + } + }; + + BreakSegmentsOnJunctions( aCommit, aScreen ); + + for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) ) + { + if( !aScreen->IsExplicitJunction( item->GetPosition() ) ) + items_to_remove.push_back( item ); + else + junctions.push_back( static_cast( item ) ); + } + + for( SCH_ITEM* item : items_to_remove ) + remove_item( item ); + + for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) ) + ncs.push_back( static_cast( item ) ); + + alg::for_all_pairs( junctions.begin(), junctions.end(), + [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond ) + { + if( ( aFirst->GetEditFlags() & STRUCT_DELETED ) + || ( aSecond->GetEditFlags() & STRUCT_DELETED ) ) + { + return; + } + + if( aFirst->GetPosition() == aSecond->GetPosition() ) + remove_item( aSecond ); + } ); + + alg::for_all_pairs( ncs.begin(), ncs.end(), + [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond ) + { + if( ( aFirst->GetEditFlags() & STRUCT_DELETED ) + || ( aSecond->GetEditFlags() & STRUCT_DELETED ) ) + { + return; + } + + if( aFirst->GetPosition() == aSecond->GetPosition() ) + remove_item( aSecond ); + } ); + + + auto minX = []( const SCH_LINE* l ) + { + return std::min( l->GetStartPoint().x, l->GetEndPoint().x ); + }; + + auto maxX = []( const SCH_LINE* l ) + { + return std::max( l->GetStartPoint().x, l->GetEndPoint().x ); + }; + + auto minY = []( const SCH_LINE* l ) + { + return std::min( l->GetStartPoint().y, l->GetEndPoint().y ); + }; + + auto maxY = []( const SCH_LINE* l ) + { + return std::max( l->GetStartPoint().y, l->GetEndPoint().y ); + }; + + // Would be nice to put lines in a canonical form here by swapping + // start <-> end as needed but I don't know what swapping breaks. + while( changed ) + { + changed = false; + lines.clear(); + + for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) ) + { + if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS ) + lines.push_back( static_cast( item ) ); + } + + // Sort by minimum X position + std::sort( lines.begin(), lines.end(), + [&]( const SCH_LINE* a, const SCH_LINE* b ) + { + return minX( a ) < minX( b ); + } ); + + for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 ) + { + SCH_LINE* firstLine = *it1; + + if( firstLine->GetEditFlags() & STRUCT_DELETED ) + continue; + + if( firstLine->IsNull() ) + { + remove_item( firstLine ); + continue; + } + + int firstRightXEdge = maxX( firstLine ); + auto it2 = it1; + + for( ++it2; it2 != lines.end(); ++it2 ) + { + SCH_LINE* secondLine = *it2; + int secondLeftXEdge = minX( secondLine ); + + // impossible to overlap remaining lines + if( secondLeftXEdge > firstRightXEdge ) + break; + + // No Y axis overlap + if( !( std::max( minY( firstLine ), minY( secondLine ) ) + <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) ) + { + continue; + } + + if( secondLine->GetFlags() & STRUCT_DELETED ) + continue; + + if( !secondLine->IsParallel( firstLine ) + || !secondLine->IsStrokeEquivalent( firstLine ) + || secondLine->GetLayer() != firstLine->GetLayer() ) + { + continue; + } + + // Remove identical lines + if( firstLine->IsEndPoint( secondLine->GetStartPoint() ) + && firstLine->IsEndPoint( secondLine->GetEndPoint() ) ) + { + remove_item( secondLine ); + continue; + } + + // See if we can merge an overlap (or two colinear touching segments with + // no junction where they meet). + SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true ); + + if( mergedLine != nullptr ) + { + remove_item( firstLine ); + remove_item( secondLine ); + + if( m_schematicHolder ) + { + m_schematicHolder->AddToScreen( mergedLine, aScreen ); + } + + aCommit->Added( mergedLine, aScreen ); + + if( firstLine->IsSelected() || secondLine->IsSelected() ) + selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ ); + + break; + } + } + } + } } \ No newline at end of file diff --git a/eeschema/schematic.h b/eeschema/schematic.h index a0aeb65dc9..dce4cae1c4 100644 --- a/eeschema/schematic.h +++ b/eeschema/schematic.h @@ -388,6 +388,15 @@ public: void SetSchematicHolder( SCHEMATIC_HOLDER* aHolder ) { m_schematicHolder = aHolder; } + /** + * Perform routine schematic cleaning including breaking wire and buses and deleting + * identical objects superimposed on top of each other. + * + * @param aCommit Transaction container used to record changes for undo/redo + * @param aScreen is the screen to examine, or nullptr to examine the current screen + */ + void CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen = nullptr ); + /** * True if a SCHEMATIC exists, false if not */ diff --git a/eeschema/schematic_holder.h b/eeschema/schematic_holder.h index bea141caef..2f869024c7 100644 --- a/eeschema/schematic_holder.h +++ b/eeschema/schematic_holder.h @@ -21,6 +21,7 @@ class EDA_ITEM; class SCH_SCREEN; +class SCH_SELECTION_TOOL; /** * This is a bridge class to help the schematic be able to affect SCH_EDIT_FRAME @@ -37,4 +38,8 @@ public: * aScreen is the screen the item is located on, if not the current screen */ virtual void AddToScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen = nullptr ) = 0; + + virtual SCH_SELECTION_TOOL* GetSelectionTool() { return nullptr; } + + virtual void RemoveFromScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen ) = 0; }; \ No newline at end of file diff --git a/eeschema/tools/sch_drawing_tools.cpp b/eeschema/tools/sch_drawing_tools.cpp index 4f56d21c71..6ead5f06d2 100644 --- a/eeschema/tools/sch_drawing_tools.cpp +++ b/eeschema/tools/sch_drawing_tools.cpp @@ -1466,7 +1466,7 @@ int SCH_DRAWING_TOOLS::SingleClickPlace( const TOOL_EVENT& aEvent ) SCH_COMMIT commit( m_toolMgr ); commit.Added( newItem, screen ); - m_frame->SchematicCleanUp( &commit ); + m_frame->Schematic().CleanUp( &commit ); commit.Push( description ); } diff --git a/eeschema/tools/sch_edit_tool.cpp b/eeschema/tools/sch_edit_tool.cpp index 4a08fd11bb..e7e24733d2 100644 --- a/eeschema/tools/sch_edit_tool.cpp +++ b/eeschema/tools/sch_edit_tool.cpp @@ -1046,7 +1046,7 @@ int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent ) lwbTool->TrimOverLappingWires( commit, &selectionCopy ); lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy ); - m_frame->SchematicCleanUp( commit ); + m_frame->Schematic().CleanUp( commit ); if( !localCommit.Empty() ) localCommit.Push( _( "Rotate" ) ); @@ -1243,7 +1243,7 @@ int SCH_EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent ) lwbTool->TrimOverLappingWires( commit, &selectionCopy ); lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy ); - m_frame->SchematicCleanUp( commit ); + m_frame->Schematic().CleanUp( commit ); } if( !localCommit.Empty() ) @@ -1592,7 +1592,7 @@ int SCH_EDIT_TOOL::RepeatDrawItem( const TOOL_EVENT& aEvent ) lwbTool->TrimOverLappingWires( &commit, &newItems ); lwbTool->AddJunctionsIfNeeded( &commit, &newItems ); - m_frame->SchematicCleanUp( &commit ); + m_frame->Schematic().CleanUp( &commit ); commit.Push( _( "Repeat Item" ) ); } diff --git a/eeschema/tools/sch_line_wire_bus_tool.cpp b/eeschema/tools/sch_line_wire_bus_tool.cpp index 0c2bfbc8e7..8ba586950e 100644 --- a/eeschema/tools/sch_line_wire_bus_tool.cpp +++ b/eeschema/tools/sch_line_wire_bus_tool.cpp @@ -1238,7 +1238,7 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments() getViewControls()->SetAutoPan( false ); // Correct and remove segments that need to be merged. - m_frame->SchematicCleanUp( &commit ); + m_frame->Schematic().CleanUp( &commit ); std::vector symbols; diff --git a/eeschema/tools/sch_move_tool.cpp b/eeschema/tools/sch_move_tool.cpp index 1d17620fc7..313dbdafbe 100644 --- a/eeschema/tools/sch_move_tool.cpp +++ b/eeschema/tools/sch_move_tool.cpp @@ -1024,7 +1024,7 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm for( EDA_ITEM* item : selection ) m_frame->AutoRotateItem( m_frame->GetScreen(), static_cast( item ) ); - m_frame->SchematicCleanUp( aCommit ); + m_frame->Schematic().CleanUp( aCommit ); } for( EDA_ITEM* item : m_frame->GetScreen()->Items() ) @@ -1052,7 +1052,7 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm void SCH_MOVE_TOOL::trimDanglingLines( SCH_COMMIT* aCommit ) { // Need a local cleanup first to ensure we remove unneeded junctions - m_frame->SchematicCleanUp( aCommit, m_frame->GetScreen() ); + m_frame->Schematic().CleanUp( aCommit, m_frame->GetScreen() ); std::set danglers; @@ -1840,7 +1840,7 @@ int SCH_MOVE_TOOL::AlignToGrid( const TOOL_EVENT& aEvent ) m_toolMgr->PostEvent( EVENTS::SelectedItemsMoved ); - m_frame->SchematicCleanUp( &commit ); + m_frame->Schematic().CleanUp( &commit ); commit.Push( _( "Align Items to Grid" ) ); return 0; }