From 3bbcd30396439dadfa1443729ecef73bdfd6a013 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Fri, 7 Sep 2012 21:29:44 +0200 Subject: [PATCH] Pcbnew: cleanup functions: now, cleanup uses same algorithm as connectivity calculations to detect pads connections, and is faster. therefore tracks which have a end point inside a pad, but not necessaryexactly to the pad position are seen as connected, and are no more removed. Side effect: reconnect to pads option is removed, because it is useless. TODO: use this algorithm in drag functions. Minor other fixes --- common/drawtxt.cpp | 30 +- eeschema/sch_text.cpp | 19 +- pcbnew/clean.cpp | 846 ++++++++---------- pcbnew/connect.cpp | 207 +---- pcbnew/connect.h | 258 ++++++ pcbnew/dialogs/dialog_cleaning_options.cpp | 31 +- pcbnew/dialogs/dialog_cleaning_options.h | 2 - .../dialogs/dialog_cleaning_options_base.cpp | 10 +- .../dialogs/dialog_cleaning_options_base.fbp | 206 +++-- pcbnew/dialogs/dialog_cleaning_options_base.h | 15 +- 10 files changed, 823 insertions(+), 801 deletions(-) create mode 100644 pcbnew/connect.h diff --git a/common/drawtxt.cpp b/common/drawtxt.cpp index 16e6bfbd94..0386ce3f4c 100644 --- a/common/drawtxt.cpp +++ b/common/drawtxt.cpp @@ -55,6 +55,13 @@ double s_HerscheyScaleFactor = HERSHEY_SCALE_FACTOR; +/* Helper function for texts with over bar + */ +int OverbarPositionY( int size_v, int thickness ) +{ + return KiROUND( ( (double) size_v * 1.1 ) + ( (double) thickness * 1.5 ) ); +} + /** * Function GetPensizeForBold * @return the "best" value for a pen size to draw/plot a bold text @@ -68,7 +75,7 @@ int GetPenSizeForBold( int aTextSize ) /** * Function Clamp_Text_PenSize - *As a rule, pen width should not be >1/4em, otherwise the character + * As a rule, pen width should not be >1/4em, otherwise the character * will be cluttered up in its own fatness * so pen width max is aSize/4 for bold text, and aSize/6 for normal text * The "best" pen width is aSize/5 for bold texts, @@ -219,14 +226,6 @@ static void DrawGraphicTextPline( } -/* Helper function for texts with over bar - */ -static int overbar_position( int size_v, int thickness ) -{ - return KiROUND( ( (double) size_v * 26 * s_HerscheyScaleFactor ) + ( (double) thickness * 1.5 ) ); -} - - /** * Function DrawGraphicText * Draw a graphic text (like module texts) @@ -271,7 +270,6 @@ void DrawGraphicText( EDA_DRAW_PANEL* aPanel, int dx, dy; // Draw coordinate for segments to draw. also used in some other calculation wxPoint current_char_pos; // Draw coordinates for the current char wxPoint overbar_pos; // Start point for the current overbar - int overbars; // Number of ~ seen int overbar_italic_comp; // Italic compensation for overbar EDA_RECT* clipBox; // Clip box used in basic draw functions @@ -400,7 +398,7 @@ void DrawGraphicText( EDA_DRAW_PANEL* aPanel, if( aItalic ) { - overbar_italic_comp = overbar_position( size_v, aWidth ) / 8; + overbar_italic_comp = OverbarPositionY( size_v, aWidth ) / 8; if( italic_reverse ) { overbar_italic_comp = -overbar_italic_comp; @@ -411,7 +409,7 @@ void DrawGraphicText( EDA_DRAW_PANEL* aPanel, overbar_italic_comp = 0; }; - overbars = 0; + int overbars = 0; // Number of ~ seen ptr = 0; /* ptr = text index */ while( ptr < char_count ) { @@ -420,12 +418,12 @@ void DrawGraphicText( EDA_DRAW_PANEL* aPanel, /* Found an overbar, adjust the pointers */ overbars++; - if( overbars % 2 ) + if( overbars & 1 ) // odd overbars count { /* Starting the overbar */ overbar_pos = current_char_pos; overbar_pos.x += overbar_italic_comp; - overbar_pos.y -= overbar_position( size_v, aWidth ); + overbar_pos.y -= OverbarPositionY( size_v, aWidth ); RotatePoint( &overbar_pos, aPos, aOrient ); } else @@ -434,7 +432,7 @@ void DrawGraphicText( EDA_DRAW_PANEL* aPanel, coord[0] = overbar_pos; overbar_pos = current_char_pos; overbar_pos.x += overbar_italic_comp; - overbar_pos.y -= overbar_position( size_v, aWidth ); + overbar_pos.y -= OverbarPositionY( size_v, aWidth ); RotatePoint( &overbar_pos, aPos, aOrient ); coord[1] = overbar_pos; /* Plot the overbar segment */ @@ -516,7 +514,7 @@ void DrawGraphicText( EDA_DRAW_PANEL* aPanel, /* Close the last overbar */ coord[0] = overbar_pos; overbar_pos = current_char_pos; - overbar_pos.y -= overbar_position( size_v, aWidth ); + overbar_pos.y -= OverbarPositionY( size_v, aWidth ); RotatePoint( &overbar_pos, aPos, aOrient ); coord[1] = overbar_pos; /* Plot the overbar segment */ diff --git a/eeschema/sch_text.cpp b/eeschema/sch_text.cpp index c431abf135..0d648f913d 100644 --- a/eeschema/sch_text.cpp +++ b/eeschema/sch_text.cpp @@ -44,6 +44,7 @@ extern void IncrementLabelMember( wxString& name ); +extern int OverbarPositionY( int size_v, int thickness ); /* Names of sheet label types. */ @@ -273,19 +274,19 @@ void SCH_TEXT::Rotate( wxPoint aPosition ) switch( GetOrientation() ) { - case 0: /* horizontal text */ + case 0: // horizontal text dy = m_Size.y; break; - case 1: /* Vert Orientation UP */ + case 1: // Vert Orientation UP dy = 0; break; - case 2: /* invert horizontal text*/ + case 2: // invert horizontal text dy = m_Size.y; break; - case 3: /* Vert Orientation BOTTOM */ + case 3: // Vert Orientation BOTTOM dy = 0; break; @@ -1241,8 +1242,14 @@ void SCH_GLOBALLABEL::CreateGraphicShape( std::vector & aPoints, const // Create outline shape : 6 points int x = symb_len + linewidth + 3; - // 50% more for negation bar - int y = KiROUND( (double) HalfSize * 1.5 + (double) linewidth + 3.0 ); + // Use negation bar Y position to calculate full vertical size + #define Y_CORRECTION 1.22 + // Note: this factor is due to the fact the negation bar Y position + // does not give exactly the full Y size of text + // and is experimentally set to this value + int y = KiROUND( OverbarPositionY( HalfSize, linewidth ) * Y_CORRECTION ); + // add room for line thickness and space between top of text and graphic shape + y += linewidth; // Starting point(anchor) aPoints.push_back( wxPoint( 0, 0 ) ); diff --git a/pcbnew/clean.cpp b/pcbnew/clean.cpp index b93b88081f..58af530889 100644 --- a/pcbnew/clean.cpp +++ b/pcbnew/clean.cpp @@ -36,37 +36,98 @@ #include #include #include - -// local functions : -static void clean_segments( PCB_EDIT_FRAME* aFrame ); -static void clean_vias( BOARD* aPcb ); -static void DeleteUnconnectedTracks( PCB_EDIT_FRAME* aFrame ); -static TRACK* MergeColinearSegmentIfPossible( BOARD* aPcb, TRACK* aTrackRef, - TRACK* aCandidate, int aEndType ); -static void CleanupTracks( PCB_EDIT_FRAME* aFrame, - bool aCleanVias, bool aMergeSegments, - bool aDeleteUnconnectedSegm, bool aConnectToPads ); - +#include #include -#define CONN2PAD_ENBL +// Helper class used to clean tracks and vias +class TRACKS_CLEANER: CONNECTIONS +{ +private: + BOARD * m_Brd; + bool m_deleteUnconnectedTracks; + bool m_mergeSegments; + bool m_cleanVias; -#ifdef CONN2PAD_ENBL -static void ConnectDanglingEndToPad( PCB_EDIT_FRAME* frame ); -static void ConnectDanglingEndToVia( BOARD* pcb ); -#endif +public: + TRACKS_CLEANER( BOARD * aPcb ); + /** + * the cleanup function. + * return true if some item was modified + */ + bool CleanupBoard(); + + void SetdeleteUnconnectedTracksOpt( bool aDelete ) + { + m_deleteUnconnectedTracks = aDelete; + } + + void SetMergeSegmentsOpt( bool aMerge ) + { + m_mergeSegments = aMerge; + } + + void SetCleanViasOpt( bool aClean ) + { + m_cleanVias = aClean; + } + +private: + + /** + * Removes redundant vias like vias at same location + * or on pad through + */ + bool clean_vias(); + + /** + * Removes dangling tracks + */ + bool deleteUnconnectedTracks(); + + /** + * Merge colinear segments and remove null len segments + */ + bool clean_segments(); + + /** + * helper function + * Rebuild list of tracks, and connected tracks + * this info must be rebuilt when tracks are erased + */ + void buildTrackConnectionInfo(); + + /** + * helper function + * merge aTrackRef and aCandidate, when possible, + * i.e. when they are colinear, same width, and obviously same layer + */ + TRACK* mergeColinearSegmentIfPossible( TRACK* aTrackRef, + TRACK* aCandidate, int aEndType ); +}; /* Install the track operation dialog frame */ void PCB_EDIT_FRAME::Clean_Pcb( wxDC* DC ) { - DIALOG_CLEANING_OPTIONS::connectToPads = false; DIALOG_CLEANING_OPTIONS dlg( this ); if( dlg.ShowModal() == wxID_OK ) - CleanupTracks( this, dlg.cleanVias, dlg.mergeSegments, - dlg.deleteUnconnectedSegm, dlg.connectToPads ); + { + wxBusyCursor( dummy ); + TRACKS_CLEANER cleaner( GetBoard() ); + cleaner.SetdeleteUnconnectedTracksOpt( dlg.deleteUnconnectedSegm ); + cleaner.SetMergeSegmentsOpt( dlg.mergeSegments ); + cleaner.SetCleanViasOpt( dlg.cleanVias ); + if( cleaner.CleanupBoard() ) + { + // Clear undo and redo lists to avoid inconsistencies between lists + GetScreen()->ClearUndoRedoList(); + SetCurItem( NULL ); + Compile_Ratsnest( NULL, true ); + OnModify(); + } + } m_canvas->Refresh( true ); } @@ -80,73 +141,87 @@ void PCB_EDIT_FRAME::Clean_Pcb( wxDC* DC ) * Create segments when track ends are incorrectly connected: * i.e. when a track end covers a pad or a via but is not exactly on the pad or the via center */ -void CleanupTracks( PCB_EDIT_FRAME* aFrame, - bool aCleanVias, bool aMergeSegments, - bool aDeleteUnconnectedSegm, bool aConnectToPads ) +bool TRACKS_CLEANER::CleanupBoard() { - wxBusyCursor( dummy ); + bool modified = false; - aFrame->ClearMsgPanel(); - aFrame->GetBoard()->GetNumSegmTrack(); // update the count - - // Clear undo and redo lists to avoid inconsistencies between lists - aFrame->GetScreen()->ClearUndoRedoList(); - aFrame->SetCurItem( NULL ); - - // Rebuild the pad infos (pad list and netcodes) to ensure an up to date info - aFrame->GetBoard()->m_Status_Pcb = 0; - aFrame->GetBoard()->BuildListOfNets(); - - if( aCleanVias ) // delete redundant vias - { - aFrame->SetStatusText( _( "Clean vias" ) ); - clean_vias( aFrame->GetBoard() ); - } - -#ifdef CONN2PAD_ENBL - /* Create missing segments when a track end covers a pad or a via, - * but is not on the pad or the via center */ - if( aConnectToPads ) - { - aFrame->SetStatusText( _( "Reconnect pads" ) ); - - // Create missing segments when a track end covers a pad, but is not on the pad center - ConnectDanglingEndToPad( aFrame ); - - // Create missing segments when a track end covers a via, but is not on the via center - ConnectDanglingEndToVia( aFrame->GetBoard() ); - } -#endif + // delete redundant vias + if( m_cleanVias && clean_vias() ) + modified = true; // Remove null segments and intermediate points on aligned segments - if( aMergeSegments ) - { - aFrame->SetStatusText( _( "Merge track segments" ) ); - clean_segments( aFrame ); - } + if( m_mergeSegments && clean_segments() ) + modified = true; // Delete dangling tracks - if( aDeleteUnconnectedSegm ) - { - aFrame->SetStatusText( _( "Delete unconnected tracks" ) ); - DeleteUnconnectedTracks( aFrame ); - } + if( m_deleteUnconnectedTracks && deleteUnconnectedTracks() ) + modified = true; - aFrame->SetStatusText( _( "Cleanup finished" ) ); - - aFrame->Compile_Ratsnest( NULL, true ); - - aFrame->OnModify(); + return modified; } - -void clean_vias( BOARD * aPcb ) +TRACKS_CLEANER::TRACKS_CLEANER( BOARD * aPcb ): CONNECTIONS( aPcb ) { - TRACK* track; - TRACK* next_track; + m_Brd = aPcb; + m_deleteUnconnectedTracks = false; + m_mergeSegments = false; - for( track = aPcb->m_Track; track; track = track->Next() ) + // Build connections info + BuildPadsList(); + buildTrackConnectionInfo(); +} + +void TRACKS_CLEANER::buildTrackConnectionInfo() +{ + BuildTracksCandidatesList( m_Brd->m_Track, NULL); + + // clear flags and variables used in cleanup + for( TRACK * track = m_Brd->m_Track; track; track = track->Next() ) { + track->start = NULL; + track->end = NULL; + track->m_PadsConnected.clear(); + track->SetState( START_ON_PAD|END_ON_PAD|BUSY, OFF ); + } + + // Build connections info tracks to pads + SearchTracksConnectedToPads(); + for( TRACK * track = m_Brd->m_Track; track; track = track->Next() ) + { + // Mark track if connected to pads + for( unsigned jj = 0; jj < track->m_PadsConnected.size(); jj++ ) + { + D_PAD * pad = track->m_PadsConnected[jj]; + + if( pad->HitTest( track->GetStart() ) ) + { + track->start = pad; + track->SetState( START_ON_PAD, ON ); + } + + if( pad->HitTest( track->GetEnd() ) ) + { + track->end = pad; + track->SetState( END_ON_PAD, ON ); + } + } + } +} + +bool TRACKS_CLEANER::clean_vias() +{ + TRACK* next_track; + bool modified = false; + + for( TRACK* track = m_Brd->m_Track; track; track = track->Next() ) + { + // Correct via m_End defects (if any) + if( track->Type() == PCB_VIA_T ) + { + if( track->m_Start != track->m_End ) + track->m_End = track->m_Start; + } + if( track->GetShape() != VIA_THROUGH ) continue; @@ -166,26 +241,36 @@ void clean_vias( BOARD * aPcb ) // delete via alt_track->UnLink(); delete alt_track; + modified = true; } } // Delete Via on pads at same location - for( track = aPcb->m_Track; track != NULL; track = next_track ) + for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track ) { next_track = track->Next(); if( track->m_Shape != VIA_THROUGH ) continue; - D_PAD* pad = aPcb->GetPadFast( track->m_Start, ALL_CU_LAYERS ); - - if( pad && (pad->GetLayerMask() & EXTERNAL_LAYERS) == EXTERNAL_LAYERS ) // redundant Via + // Examine the list of connected pads: + // if one pad through is found, the via can be removed + for( unsigned ii = 0; ii < track->m_PadsConnected.size(); ii++ ) { - // delete via - track->UnLink(); - delete track; + D_PAD * pad = track->m_PadsConnected[ii]; + + if( (pad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS ) + { + // redundant: via delete it + track->UnLink(); + delete track; + modified = true; + break; + } } } + + return modified; } @@ -194,245 +279,189 @@ void clean_vias( BOARD * aPcb ) * Vias: * If a via is only connected to a dangling track, it also will be removed */ -static void DeleteUnconnectedTracks( PCB_EDIT_FRAME* aFrame ) +bool TRACKS_CLEANER::deleteUnconnectedTracks() { - TRACK* segment; - TRACK* other; - TRACK* startNetcode; - TRACK* next; - ZONE_CONTAINER* zone; - int masklayer, oldnetcode; - int type_end, flag_erase; + if( m_Brd->m_Track == NULL ) + return false; - if( aFrame->GetBoard()->m_Track == NULL ) - return; - - aFrame->GetCanvas()->SetAbortRequest( false ); - - // correct via m_End defects - for( segment = aFrame->GetBoard()->m_Track; segment; segment = next ) + bool modified = false; + bool item_erased = true; + while( item_erased ) // Iterate when at least one track is deleted { - next = segment->Next(); - - if( segment->Type() == PCB_VIA_T ) + item_erased = false; + TRACK* next_track; + for( TRACK * track = m_Brd->m_Track; track ; track = next_track ) { - if( segment->m_Start != segment->m_End ) - segment->m_End = segment->m_Start; + next_track = track->Next(); - continue; + int flag_erase = 0; //Not connected indicator + int type_end = 0; + + if( track->GetState( START_ON_PAD ) ) + type_end |= START_ON_PAD; + + if( track->GetState( END_ON_PAD ) ) + type_end |= END_ON_PAD; + + // if the track start point is not connected to a pad, + // test if this track start point is connected to another track + // For via test, an enhancement could be to test if connected + // to 2 items on different layers. + // Currently a via must be connected to 2 items, that can be on the same layer + int top_layer, bottom_layer; + ZONE_CONTAINER* zone; + + if( (type_end & START_ON_PAD ) == 0 ) + { + TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_START ); + + if( other == NULL ) // Test a connection to zones + { + if( track->Type() != PCB_VIA_T ) + { + zone = m_Brd->HitTestForAnyFilledArea( track->m_Start, + track->GetLayer() ); + } + else + { + ((SEGVIA*)track)->ReturnLayerPair( &top_layer, &bottom_layer ); + zone = m_Brd->HitTestForAnyFilledArea( track->m_Start, + top_layer, bottom_layer ); + } + } + + if( (other == NULL) && (zone == NULL) ) + { + flag_erase |= 1; + } + else // segment, via or zone connected to this end + { + track->start = other; + // If a via is connected to this end, + // test if this via has a second item connected. + // If no, remove it with the current segment + + if( other && other->Type() == PCB_VIA_T ) + { + // search for another segment following the via + track->SetState( BUSY, ON ); + + SEGVIA* via = (SEGVIA*) other; + other = via->GetTrace( m_Brd->m_Track, NULL, FLG_START ); + + if( other == NULL ) + { + via->ReturnLayerPair( &top_layer, &bottom_layer ); + zone = m_Brd->HitTestForAnyFilledArea( via->m_Start, + bottom_layer, top_layer ); + } + + if( (other == NULL) && (zone == NULL) ) + flag_erase |= 2; + + track->SetState( BUSY, OFF ); + } + } + } + + // if track end point is not connected to a pad, + // test if this track end point is connected to an other track + if( (type_end & END_ON_PAD ) == 0 ) + { + TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_END ); + + if( other == NULL ) // Test a connection to zones + { + if( track->Type() != PCB_VIA_T ) + { + zone = m_Brd->HitTestForAnyFilledArea( track->m_End, + track->GetLayer() ); + } + else + { + ((SEGVIA*)track)->ReturnLayerPair( &top_layer, &bottom_layer ); + zone = m_Brd->HitTestForAnyFilledArea( track->m_End, + top_layer, bottom_layer ); + } + } + + if ( (other == NULL) && (zone == NULL) ) + { + flag_erase |= 0x10; + } + else // segment, via or zone connected to this end + { + track->end = other; + + // If a via is connected to this end, test if this via has a second item connected + // if no, remove it with the current segment + + if( other && other->Type() == PCB_VIA_T ) + { + // search for another segment following the via + + track->SetState( BUSY, ON ); + + SEGVIA* via = (SEGVIA*) other; + other = via->GetTrace( m_Brd->m_Track, NULL, FLG_END ); + + if( other == NULL ) + { + via->ReturnLayerPair( &top_layer, &bottom_layer ); + zone = m_Brd->HitTestForAnyFilledArea( via->m_End, + bottom_layer, top_layer ); + } + + if( (other == NULL) && (zone == NULL) ) + flag_erase |= 0x20; + + track->SetState( BUSY, OFF ); + } + } + } + + if( flag_erase ) + { + // remove segment from board + track->DeleteStructure(); + // iterate, because a track connected to the deleted track + // is now perhaps now not connected and should be deleted + item_erased = true; + modified = true; + } } } - // removal of unconnected tracks - segment = startNetcode = aFrame->GetBoard()->m_Track; - oldnetcode = segment->GetNet(); - - for( int ii = 0; segment ; segment = next, ii++ ) - { - next = segment->Next(); - - if( aFrame->GetCanvas()->GetAbortRequest() ) - break; - - if( segment->GetNet() != oldnetcode ) - { - startNetcode = segment; - oldnetcode = segment->GetNet(); - } - - flag_erase = 0; //Not connected indicator - type_end = 0; - - // Is a pad found on a track end ? - - masklayer = segment->ReturnMaskLayer(); - - D_PAD* pad; - - pad = aFrame->GetBoard()->GetPadFast( segment->m_Start, masklayer ); - - if( pad != NULL ) - { - segment->start = pad; - type_end |= START_ON_PAD; - } - - pad = aFrame->GetBoard()->GetPadFast( segment->m_End, masklayer ); - - if( pad != NULL ) - { - segment->end = pad; - type_end |= END_ON_PAD; - } - - // if not connected to a pad, test if segment's START is connected to another track - // For via tests, an enhancement could to test if connected to 2 items on different layers. - // Currently a via must be connected to 2 items, that can be on the same layer - int top_layer, bottom_layer; - - if( (type_end & START_ON_PAD ) == 0 ) - { - other = segment->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_START ); - - if( other == NULL ) // Test a connection to zones - { - if( segment->Type() != PCB_VIA_T ) - { - zone = aFrame->GetBoard()->HitTestForAnyFilledArea( segment->m_Start, - segment->GetLayer() ); - } - else - { - ((SEGVIA*)segment)->ReturnLayerPair( &top_layer, &bottom_layer ); - zone = aFrame->GetBoard()->HitTestForAnyFilledArea( segment->m_Start, - top_layer, bottom_layer ); - } - } - - if( (other == NULL) && (zone == NULL) ) - { - flag_erase |= 1; - } - else // segment, via or zone connected to this end - { - segment->start = other; - // If a via is connected to this end, test if this via has a second item connected - // if no, remove it with the current segment - - if( other && other->Type() == PCB_VIA_T ) - { - // search for another segment following the via - - segment->SetState( BUSY, ON ); - - SEGVIA* via = (SEGVIA*) other; - other = via->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_START ); - - if( other == NULL ) - { - via->ReturnLayerPair( &top_layer, &bottom_layer ); - zone = aFrame->GetBoard()->HitTestForAnyFilledArea( via->m_Start, - bottom_layer, - top_layer ); - } - - if( (other == NULL) && (zone == NULL) ) - flag_erase |= 2; - - segment->SetState( BUSY, OFF ); - } - } - } - - // if not connected to a pad, test if segment's END is connected to another track - if( (type_end & END_ON_PAD ) == 0 ) - { - other = segment->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_END ); - - if( other == NULL ) // Test a connection to zones - { - if( segment->Type() != PCB_VIA_T ) - { - zone = aFrame->GetBoard()->HitTestForAnyFilledArea( segment->m_End, - segment->GetLayer() ); - } - else - { - ((SEGVIA*)segment)->ReturnLayerPair( &top_layer, &bottom_layer ); - zone = aFrame->GetBoard()->HitTestForAnyFilledArea( segment->m_End, - top_layer, bottom_layer ); - } - } - - if ( (other == NULL) && (zone == NULL) ) - { - flag_erase |= 0x10; - } - else // segment, via or zone connected to this end - { - segment->end = other; - - // If a via is connected to this end, test if this via has a second item connected - // if no, remove it with the current segment - - if( other && other->Type() == PCB_VIA_T ) - { - // search for another segment following the via - - segment->SetState( BUSY, ON ); - - SEGVIA* via = (SEGVIA*) other; - other = via->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_END ); - - if( other == NULL ) - { - via->ReturnLayerPair( &top_layer, &bottom_layer ); - zone = aFrame->GetBoard()->HitTestForAnyFilledArea( via->m_End, - bottom_layer, - top_layer ); - } - - if( (other == NULL) && (zone == NULL) ) - flag_erase |= 0x20; - - segment->SetState( BUSY, OFF ); - } - } - } - - if( flag_erase ) - { - // update the pointer to start of the contiguous netcode group - if( segment == startNetcode ) - { - next = segment->Next(); - startNetcode = next; - } - else - { - next = startNetcode; - } - - // remove segment from board - segment->DeleteStructure(); - - if( next == NULL ) - break; - } - } + return modified; } // Delete null length segments, and intermediate points .. -static void clean_segments( PCB_EDIT_FRAME* aFrame ) +bool TRACKS_CLEANER::clean_segments() { + bool modified = false; TRACK* segment, * nextsegment; TRACK* other; - int ii; int flag, no_inc; - wxString msg; - aFrame->GetCanvas()->SetAbortRequest( false ); // Delete null segments - for( segment = aFrame->GetBoard()->m_Track; segment; segment = nextsegment ) + for( segment = m_Brd->m_Track; segment; segment = nextsegment ) { nextsegment = segment->Next(); - if( !segment->IsNull() ) - continue; - - // Length segment = 0; delete it - segment->DeleteStructure(); + if( segment->IsNull() ) // Length segment = 0; delete it + segment->DeleteStructure(); } - // Delete redundant segments - for( segment = aFrame->GetBoard()->m_Track, ii = 0; segment; segment = segment->Next(), ii++ ) + // Delete redundant segments, i.e. segments having the same end points + // and layers + for( segment = m_Brd->m_Track; segment; segment = segment->Next() ) { for( other = segment->Next(); other; other = nextsegment ) { nextsegment = other->Next(); - int erase = 0; + bool erase = false; if( segment->Type() != other->Type() ) continue; @@ -443,31 +472,25 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) if( segment->GetNet() != other->GetNet() ) break; - if( segment->m_Start == other->m_Start ) - { - if( segment->m_End == other->m_End ) - erase = 1; - } + if( ( segment->m_Start == other->m_Start ) && + ( segment->m_End == other->m_End ) ) + erase = true; - if( segment->m_Start == other->m_End ) - { - if( segment->m_End == other->m_Start ) - erase = 1; - } + if( ( segment->m_Start == other->m_End ) && + ( segment->m_End == other->m_Start ) ) + erase = true; // Delete redundant point if( erase ) { - ii--; other->DeleteStructure(); + modified = true; } } } - // delete intermediate points - ii = 0; - - for( segment = aFrame->GetBoard()->m_Track; segment; segment = nextsegment ) + // delete intermediate points (merging colinear segments) + for( segment = m_Brd->m_Track; segment; segment = nextsegment ) { TRACK* segStart; TRACK* segEnd; @@ -475,15 +498,12 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) nextsegment = segment->Next(); - if( aFrame->GetCanvas()->GetAbortRequest() ) - return; - if( segment->Type() != PCB_TRACE_T ) continue; flag = no_inc = 0; - // search for a possible point that connects on the START point of the segment + // search for a possible point connected to the START point of the current segment for( segStart = segment->Next(); ; ) { segStart = segment->GetTrace( segStart, NULL, FLG_START ); @@ -500,7 +520,7 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) // We must have only one segment connected segStart->SetState( BUSY, ON ); - other = segment->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_START ); + other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_START ); segStart->SetState( BUSY, OFF ); if( other == NULL ) @@ -513,17 +533,17 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) if( flag ) // We have the starting point of the segment is connected to an other segment { - segDelete = MergeColinearSegmentIfPossible( aFrame->GetBoard(), segment, segStart, - FLG_START ); + segDelete = mergeColinearSegmentIfPossible( segment, segStart, FLG_START ); if( segDelete ) { no_inc = 1; segDelete->DeleteStructure(); + modified = true; } } - // search for a possible point that connects on the END point of the segment: + // search for a possible point connected to the END point of the current segment: for( segEnd = segment->Next(); ; ) { segEnd = segment->GetTrace( segEnd, NULL, FLG_END ); @@ -538,7 +558,7 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) // We must have only one segment connected segEnd->SetState( BUSY, ON ); - other = segment->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_END ); + other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_END ); segEnd->SetState( BUSY, OFF ); if( other == NULL ) @@ -554,13 +574,13 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) if( flag & 2 ) // We have the ending point of the segment is connected to an other segment { - segDelete = MergeColinearSegmentIfPossible( aFrame->GetBoard(), - segment, segEnd, FLG_END ); + segDelete = mergeColinearSegmentIfPossible( segment, segEnd, FLG_END ); if( segDelete ) { no_inc = 1; segDelete->DeleteStructure(); + modified = true; } } @@ -568,7 +588,7 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) nextsegment = segment->Next(); } - return; + return modified; } @@ -577,13 +597,14 @@ static void clean_segments( PCB_EDIT_FRAME* aFrame ) * and see if the common point is not on a pad (i.e. if this common point can be removed). * the ending point of pt_ref is the start point (aEndType == START) * or the end point (aEndType != START) + * flags START_ON_PAD and END_ON_PAD must be set before calling this function * if the common point can be deleted, this function * change the common point coordinate of the aTrackRef segm * (and therefore connect the 2 other ending points) * and return aCandidate (which can be deleted). * else return NULL */ -TRACK* MergeColinearSegmentIfPossible( BOARD* aPcb, TRACK* aTrackRef, TRACK* aCandidate, +TRACK* TRACKS_CLEANER::mergeColinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate, int aEndType ) { if( aTrackRef->m_Width != aCandidate->m_Width ) @@ -594,13 +615,13 @@ TRACK* MergeColinearSegmentIfPossible( BOARD* aPcb, TRACK* aTrackRef, TRACK* aCa // Trivial case: superimposed tracks ( tracks, not vias ): if( aTrackRef->Type() == PCB_TRACE_T && aCandidate->Type() == PCB_TRACE_T ) { - if( aTrackRef->m_Start == aCandidate->m_Start ) - if( aTrackRef->m_End == aCandidate->m_End ) - return aCandidate; + if( ( aTrackRef->m_Start == aCandidate->m_Start ) && + ( aTrackRef->m_End == aCandidate->m_End ) ) + return aCandidate; - if( aTrackRef->m_Start == aCandidate->m_End ) - if( aTrackRef->m_End == aCandidate->m_Start ) - return aCandidate; + if( ( aTrackRef->m_Start == aCandidate->m_End ) && + ( aTrackRef->m_End == aCandidate->m_Start ) ) + return aCandidate; } int refdx = aTrackRef->m_End.x - aTrackRef->m_Start.x; @@ -646,8 +667,8 @@ TRACK* MergeColinearSegmentIfPossible( BOARD* aPcb, TRACK* aTrackRef, TRACK* aCa */ if( aEndType == FLG_START ) { - // We must not have a pad, which is a always terminal point for a track - if( aPcb->GetPadFast( aTrackRef->m_Start, aTrackRef->ReturnMaskLayer() ) ) + // We do not have a pad, which is a always terminal point for a track + if( aTrackRef->GetState( START_ON_PAD) ) return NULL; /* change the common point coordinate of pt_segm to use the other point @@ -655,18 +676,22 @@ TRACK* MergeColinearSegmentIfPossible( BOARD* aPcb, TRACK* aTrackRef, TRACK* aCa if( aTrackRef->m_Start == aCandidate->m_Start ) { aTrackRef->m_Start = aCandidate->m_End; + aTrackRef->start = aCandidate->end; + aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( END_ON_PAD) ); return aCandidate; } else { aTrackRef->m_Start = aCandidate->m_Start; + aTrackRef->start = aCandidate->start; + aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( START_ON_PAD) ); return aCandidate; } } else // aEndType == END { - // We must not have a pad, which is a always terminal point for a track - if( aPcb->GetPadFast( aTrackRef->m_End, aTrackRef->ReturnMaskLayer() ) ) + // We do not have a pad, which is a always terminal point for a track + if( aTrackRef->GetState( END_ON_PAD) ) return NULL; /* change the common point coordinate of pt_segm to use the other point @@ -674,11 +699,15 @@ TRACK* MergeColinearSegmentIfPossible( BOARD* aPcb, TRACK* aTrackRef, TRACK* aCa if( aTrackRef->m_End == aCandidate->m_Start ) { aTrackRef->m_End = aCandidate->m_End; + aTrackRef->end = aCandidate->end; + aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( END_ON_PAD) ); return aCandidate; } else { aTrackRef->m_End = aCandidate->m_Start; + aTrackRef->end = aCandidate->start; + aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( START_ON_PAD) ); return aCandidate; } } @@ -706,7 +735,7 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks() // find the netcode for segment using anything connected to the "start" of "segment" net_code_s = -1; - if( segment->start && segment->start->Type()==PCB_PAD_T ) + if( segment->start && segment->start->Type()==PCB_PAD_T ) { // get the netcode of the pad to propagate. net_code_s = ((D_PAD*)(segment->start))->GetNet(); @@ -725,7 +754,7 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks() // find the netcode for segment using anything connected to the "end" of "segment" net_code_e = -1; - if( segment->end && segment->end->Type()==PCB_PAD_T ) + if( segment->end && segment->end->Type()==PCB_PAD_T ) { net_code_e = ((D_PAD*)(segment->end))->GetNet(); } @@ -748,7 +777,7 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks() } // Remove tracks having a flagged segment - for( segment = GetBoard()->m_Track; segment; segment = next ) + for( segment = GetBoard()->m_Track; segment; segment = next ) { next = (TRACK*) segment->Next(); @@ -768,154 +797,3 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks() return isModified; } - - -#if defined(CONN2PAD_ENBL) - -/** - * Function ConnectDanglingEndToPad - * looks for vias which have no netcode and which are in electrical contact - * with a track to the degree that the track's end point falls on the via. - * Note that this is not a rigorous electrical check, but is better than - * testing for the track endpoint equaling the via center. When such a via - * is found, then add a small track to bridge from the overlapping track to - * the via and change the via's netcode so that subsequent continuity checks - * can be done with the faster equality algorithm. - */ -static void ConnectDanglingEndToVia( BOARD* pcb ) -{ - for( TRACK* track = pcb->m_Track; track; track = track->Next() ) - { - SEGVIA* via; - - if( track->Type()!=PCB_VIA_T || (via = (SEGVIA*)track)->GetNet()!=0 ) - continue; - - for( TRACK* other = pcb->m_Track; other; other = other->Next() ) - { - if( other == track ) - continue; - - if( !via->IsOnLayer( other->GetLayer() ) ) - continue; - - // if the other track's m_End does not match the via position, and the track's - // m_Start is within the bounds of the via, and the other track has no start - if( other->m_End != via->GetPosition() && via->HitTest( other->m_Start ) - && !other->start ) - { - TRACK* newTrack = (TRACK*)other->Clone(); - - pcb->m_Track.Insert( newTrack, other->Next() ); - - newTrack->m_End = via->GetPosition(); - - newTrack->start = other; - newTrack->end = via; - other->start = newTrack; - - via->SetNet( other->GetNet() ); - - if( !via->start ) - via->start = other; - - if( !via->end ) - via->end = other; - } - - // if the other track's m_Start does not match the via position, and the track's - // m_End is within the bounds of the via, and the other track has no end - else if( other->m_Start != via->GetPosition() && via->HitTest( other->m_End ) - && !other->end ) - { - TRACK* newTrack = (TRACK*)other->Clone(); - - pcb->m_Track.Insert( newTrack, other->Next() ); - - newTrack->m_Start = via->GetPosition(); - - newTrack->start = via; - newTrack->end = other; - other->end = newTrack; - - via->SetNet( other->GetNet() ); - - if( !via->start ) - via->start = other; - - if( !via->end ) - via->end = other; - } - } - } -} - - -/** - * Function ConnectDanglingEndToPad - * possibly adds a segment to the end of any and all tracks if their end is not exactly - * connected into the center of the pad. This allows faster control of - * connections. - */ -void ConnectDanglingEndToPad( PCB_EDIT_FRAME* aFrame ) -{ - TRACK* segment; - int nb_new_trace = 0; - wxString msg; - - aFrame->GetCanvas()->SetAbortRequest( false ); - - for( segment = aFrame->GetBoard()->m_Track; segment; segment = segment->Next() ) - { - D_PAD* pad; - - if( aFrame->GetCanvas()->GetAbortRequest() ) - return; - - pad = aFrame->GetBoard()->GetPad( segment, FLG_START ); - - if( pad ) - { - // test if the track start point is not exactly starting on the pad - if( segment->m_Start != pad->GetPosition() ) - { - if( segment->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_START ) == NULL ) - { - TRACK* newTrack = (TRACK*) segment->Clone(); - - aFrame->GetBoard()->m_Track.Insert( newTrack, segment->Next() ); - - newTrack->m_End = pad->GetPosition(); - newTrack->start = segment; - newTrack->end = pad; - - nb_new_trace++; - } - } - } - - pad = aFrame->GetBoard()->GetPad( segment, FLG_END ); - - if( pad ) - { - // test if the track end point is not exactly on the pad - if( segment->m_End != pad->GetPosition() ) - { - if( segment->GetTrace( aFrame->GetBoard()->m_Track, NULL, FLG_END ) == NULL ) - { - TRACK* newTrack = (TRACK*)segment->Clone(); - - aFrame->GetBoard()->m_Track.Insert( newTrack, segment->Next() ); - - newTrack->m_Start = pad->GetPosition(); - - newTrack->start = pad; - newTrack->end = segment; - nb_new_trace++; - } - } - } - } -} - -#endif diff --git a/pcbnew/connect.cpp b/pcbnew/connect.cpp index c7afdbfb95..0639007a20 100644 --- a/pcbnew/connect.cpp +++ b/pcbnew/connect.cpp @@ -34,11 +34,10 @@ #include #include -#include -#include - #include +// Helper classes to handle connection points +#include extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb ); extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode ); @@ -47,204 +46,6 @@ extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode ); static void RebuildTrackChain( BOARD* pcb ); -// A helper class to handle connection points (i.e. candidates) for tracks -class CONNECTED_POINT -{ -private: - BOARD_CONNECTED_ITEM * m_item; // a link to the parent item (track, via or pad) - wxPoint m_point; // the connection point - -public: - CONNECTED_POINT( TRACK * aTrack, const wxPoint & aPoint) - { - m_item = aTrack; - m_point = aPoint; - } - - CONNECTED_POINT( D_PAD * aPad, const wxPoint & aPoint) - { - m_item = aPad; - m_point = aPoint; - } - - TRACK * GetTrack() const - { - return m_item->Type() != PCB_PAD_T ? (TRACK*) m_item : NULL ; - } - - D_PAD * GetPad() const - { - return m_item->Type() == PCB_PAD_T ? (D_PAD*) m_item : NULL; - } - - const wxPoint & GetPoint() const { return m_point; } -}; - -// A helper class to handle connections calculations: -class CONNECTIONS -{ -private: - std::vector m_connected; // List of connected tracks/vias - // to a given track or via - std::vector m_candidates; // List of points to test - // (end points of tracks or vias location ) - BOARD * m_brd; // the master board. - const TRACK * m_firstTrack; // The first track used to build m_Candidates - const TRACK * m_lastTrack; // The last track used to build m_Candidates - std::vector m_sortedPads; // list of sorted pads by X (then Y) coordinate - -public: - CONNECTIONS( BOARD * aBrd ); - ~CONNECTIONS() {}; - - /** Function BuildPadsList - * Fills m_sortedPads with all pads that be connected to tracks - * pads are sorted by > then Y coordinates to allow fast binary search in list - * @param aNetcode = net code to use to filter pads - * if aNetcode < 0, all pads will be put in list (default) - */ - void BuildPadsList( int aNetcode = -1 ); - - /** - * @return the pads list used in connections calculations - */ - std::vector& GetPadsList() { return m_sortedPads; } - - /** - * Function Build_CurrNet_SubNets_Connections - * should be called after a track change (delete or add a track): - * Connections to pads and to tracks are recalculated - * If a track is deleted, the other pointers to pads do not change. - * When a new track is added in track list, its pointers to pads are already initialized - * Builds the subnets inside a net (tracks from aFirstTrack to aFirstTrack). - * subnets are clusters of pads and tracks that are connected together. - * When all tracks are created relative to the net, there is only a cluster - * when not tracks there are a cluster per pad - * @param aFirstTrack = first track of the given net - * @param aLastTrack = last track of the given net - * @param aNetcode = the netcode of the given net - */ - void Build_CurrNet_SubNets_Connections( TRACK* aFirstTrack, TRACK* aLastTrack, int aNetcode ); - - /** - * Function BuildTracksCandidatesList - * Fills m_Candidates with all connecting points (track ends or via location) - * with tracks from aBegin to aEnd. - * if aEnd == NULL, uses all tracks from aBegin - */ - void BuildTracksCandidatesList( TRACK * aBegin, TRACK * aEnd = NULL); - - /** - * Function BuildPadsCandidatesList - * Fills m_Candidates with all pads connecting points (pads position) - * m_sortedPads must be built - */ - void BuildPadsCandidatesList(); - - /** - * function SearchConnectedTracks - * Fills m_Connected with tracks/vias connected to aTrack - * @param aTrack = track or via to use as reference - */ - int SearchConnectedTracks( const TRACK * aTrack ); - - /** - * Function GetConnectedTracks - * Copy m_Connected that contains the list of tracks connected - * calculated by SearchConnectedTracks - * in aTrack->m_TracksConnected - * @param aTrack = track or via to fill with connected tracks - */ - void GetConnectedTracks(TRACK * aTrack) - { - aTrack->m_TracksConnected = m_connected; - } - - /** - * function SearchConnectionsPadsToIntersectingPads - * Explores the list of pads and adds to m_PadsConnected member - * of each pad pads connected to - * Here, connections are due to intersecting pads, not tracks - * m_sortedPads must be initialized - */ - void SearchConnectionsPadsToIntersectingPads(); - - /** - * function SearchTracksConnectedToPads - * Explores the list of pads. - * Adds to m_PadsConnected member of each track the pad(s) connected to - * Adds to m_TracksConnected member of each pad the track(s) connected to - * D_PAD::m_TracksConnected is cleared before adding items - * TRACK::m_PadsConnected is not cleared - */ - void SearchTracksConnectedToPads(); - - /** - * function CollectItemsNearTo - * Used by SearchTracksConnectedToPads - * Fills aList with pads near to aPosition - * near means aPosition to pad position <= aDistMax - * @param aList = list to fill - * @param aPosition = aPosition to use as reference - * @param aDistMax = dist max from aPosition to a candidate to select it - */ - void CollectItemsNearTo( std::vector& aList, - const wxPoint& aPosition, int aDistMax ); - - /** - * Function Propagate_SubNets - * Test a list of tracks, to create or propagate a sub netcode to pads and - * segments connected together. - * The track list must be sorted by nets, and all segments - * from m_firstTrack to m_lastTrack have the same net. - * When 2 items are connected (a track to a pad, or a track to an other track), - * they are grouped in a cluster. - * For pads, this is the .m_physical_connexion member which is a cluster identifier - * For tracks, this is the .m_Subnet member which is a cluster identifier - * For a given net, if all tracks are created, there is only one cluster. - * but if not all tracks are created, there are more than one cluster, - * and some ratsnests will be left active. - */ - void Propagate_SubNets(); - -private: - /** - * function searchEntryPointInCandidatesList - * Search an item in m_Connected connected to aPoint - * note m_Connected containts usually more than one candidate - * and searchEntryPointInCandidatesList returns an index to one of these candidates - * Others are neightbor of the indexed item. - * @param aPoint is the reference coordinates - * @return the index of item found or -1 if no candidate - */ - int searchEntryPointInCandidatesList( const wxPoint & aPoint); - - /** - * Function Merge_SubNets - * Change a subnet old value to a new value, for tracks and pads which are connected to - * tracks from m_firstTrack to m_lastTrack and their connected pads. - * and modify the subnet parameter (change the old value to the new value). - * After that, 2 cluster (or subnets) are merged into only one. - * Note: the resulting sub net value is the smallest between aOldSubNet and aNewSubNet - * @return modification count - * @param aOldSubNet = subnet value to modify - * @param aNewSubNet = new subnet value for each item which have old_val as subnet value - */ - int Merge_SubNets( int aOldSubNet, int aNewSubNet ); - - /** - * Function Merge_PadsSubNets - * Change a subnet value to a new value, in m_sortedPads pad list - * After that, 2 cluster (or subnets) are merged into only one. - * Note: the resulting subnet value is the smallest between aOldSubNet et aNewSubNet - * @return modification count - * @param aOldSubNet = subnet value to modify - * @param aNewSubNet = new subnet value for each item which have old_val as subnet value - */ - int Merge_PadsSubNets( int aOldSubNet, int aNewSubNet ); -}; - - CONNECTIONS::CONNECTIONS( BOARD * aBrd ) { m_brd = aBrd; @@ -441,10 +242,6 @@ static bool sortConnectedPointByXthenYCoordinates( const CONNECTED_POINT & aRef, void CONNECTIONS::BuildTracksCandidatesList( TRACK * aBegin, TRACK * aEnd) { m_candidates.clear(); - -// if( aBegin == NULL ) -// aBegin = m_brd->m_Track; - m_firstTrack = m_lastTrack = aBegin; unsigned ii = 0; diff --git a/pcbnew/connect.h b/pcbnew/connect.h new file mode 100644 index 0000000000..ba0629c967 --- /dev/null +++ b/pcbnew/connect.h @@ -0,0 +1,258 @@ +/** + * @file connect.h + * @brief helper classes to find track to track and track to pad connections. + */ +#ifndef CONNECT_H +#define CONNECT_H + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + + +// Helper classes to handle connection points (i.e. candidates) for tracks + +/* class CONNECTED_POINT describes a coordinate having a track or pad parent. + * when a pad is the parent, the coordinate is (obviously) the connection point + * when a track is the parent, the coordinate is the staring point + * or the ending point. + * therefore when building a list of CONNECTED_POINT, a pad or via generates one item, + * and a track generates 2 items. + */ +class CONNECTED_POINT +{ +private: + BOARD_CONNECTED_ITEM * m_item; // a link to the parent item (track, via or pad) + wxPoint m_point; // the connection point (coordinate of this point) + +public: + // ctor to build a CONNECTED_POINT instance, when the parent is a track or via + CONNECTED_POINT( TRACK * aTrack, const wxPoint & aPoint) + { + m_item = aTrack; + m_point = aPoint; + } + + // ctor to build a CONNECTED_POINT instance, when the parent is a pad + CONNECTED_POINT( D_PAD * aPad, const wxPoint & aPoint) + { + m_item = aPad; + m_point = aPoint; + } + + /** + * Function GetTrack + * @return the parent track or via of this connected point, + * or null if the parent is a pad + */ + TRACK * GetTrack() const + { + return m_item->Type() != PCB_PAD_T ? (TRACK*) m_item : NULL ; + } + + /** + * Function GetPad + * @return the parent pad of this connected point, + * or null if the parent is a track or via + */ + D_PAD * GetPad() const + { + return m_item->Type() == PCB_PAD_T ? (D_PAD*) m_item : NULL; + } + + const wxPoint & GetPoint() const { return m_point; } +}; + +// A helper class to handle connections calculations: +class CONNECTIONS +{ +private: + std::vector m_connected; // List of connected tracks/vias + // to a given track or via + std::vector m_candidates; // List of points to test + // (end points of tracks or vias location ) + BOARD * m_brd; // the master board. + const TRACK * m_firstTrack; // The first track used to build m_Candidates + const TRACK * m_lastTrack; // The last track used to build m_Candidates + std::vector m_sortedPads; // list of sorted pads by X (then Y) coordinate + +public: + CONNECTIONS( BOARD * aBrd ); + ~CONNECTIONS() {}; + + /** + * Function BuildPadsList + * Fills m_sortedPads with all pads that be connected to tracks + * pads are sorted by > then Y coordinates to allow fast binary search in list + * @param aNetcode = net code to use to filter pads + * if aNetcode < 0, all pads will be put in list (default) + */ + void BuildPadsList( int aNetcode = -1 ); + + /** + * Function GetPadsList + * @return the pads list used in connections calculations + */ + std::vector& GetPadsList() { return m_sortedPads; } + + /** + * Function Build_CurrNet_SubNets_Connections + * should be called after a track change (delete or add a track): + * Connections to pads and to tracks are recalculated + * If a track is deleted, the other pointers to pads do not change. + * When a new track is added in track list, its pointers to pads are already initialized + * Builds the subnets inside a net (tracks from aFirstTrack to aFirstTrack). + * subnets are clusters of pads and tracks that are connected together. + * When all tracks are created relative to the net, there is only a cluster + * when not tracks there are a cluster per pad + * @param aFirstTrack = first track of the given net + * @param aLastTrack = last track of the given net + * @param aNetcode = the netcode of the given net + */ + void Build_CurrNet_SubNets_Connections( TRACK* aFirstTrack, TRACK* aLastTrack, int aNetcode ); + + /** + * Function BuildTracksCandidatesList + * Fills m_Candidates with all connecting points (track ends or via location) + * with tracks from aBegin to aEnd. + * @param aBegin = first track to store in list (should not be NULL) + * @param aEnd = last track to store in list + * if aEnd == NULL, uses all tracks from aBegin + */ + void BuildTracksCandidatesList( TRACK * aBegin, TRACK * aEnd = NULL); + + /** + * Function BuildPadsCandidatesList + * Fills m_Candidates with all pads connecting points (pads position) + * m_sortedPads must be built + */ + void BuildPadsCandidatesList(); + + /** + * function SearchConnectedTracks + * Fills m_Connected with tracks/vias connected to aTrack + * @param aTrack = track or via to use as reference + */ + int SearchConnectedTracks( const TRACK * aTrack ); + + /** + * Function GetConnectedTracks + * Copy m_Connected that contains the list of tracks connected + * calculated by SearchConnectedTracks + * in aTrack->m_TracksConnected + * @param aTrack = track or via to fill with connected tracks + */ + void GetConnectedTracks(TRACK * aTrack) + { + aTrack->m_TracksConnected = m_connected; + } + + /** + * function SearchConnectionsPadsToIntersectingPads + * Explores the list of pads and adds to m_PadsConnected member + * of each pad pads connected to + * Here, connections are due to intersecting pads, not tracks + * m_sortedPads must be initialized + */ + void SearchConnectionsPadsToIntersectingPads(); + + /** + * function SearchTracksConnectedToPads + * Explores the list of pads. + * Adds to m_PadsConnected member of each track the pad(s) connected to + * Adds to m_TracksConnected member of each pad the track(s) connected to + * D_PAD::m_TracksConnected is cleared before adding items + * TRACK::m_PadsConnected is not cleared + */ + void SearchTracksConnectedToPads(); + + /** + * function CollectItemsNearTo + * Used by SearchTracksConnectedToPads + * Fills aList with pads near to aPosition + * near means aPosition to pad position <= aDistMax + * @param aList = list to fill + * @param aPosition = aPosition to use as reference + * @param aDistMax = dist max from aPosition to a candidate to select it + */ + void CollectItemsNearTo( std::vector& aList, + const wxPoint& aPosition, int aDistMax ); + + /** + * Function Propagate_SubNets + * Test a list of tracks, to create or propagate a sub netcode to pads and + * segments connected together. + * The track list must be sorted by nets, and all segments + * from m_firstTrack to m_lastTrack have the same net. + * When 2 items are connected (a track to a pad, or a track to an other track), + * they are grouped in a cluster. + * For pads, this is the .m_physical_connexion member which is a cluster identifier + * For tracks, this is the .m_Subnet member which is a cluster identifier + * For a given net, if all tracks are created, there is only one cluster. + * but if not all tracks are created, there are more than one cluster, + * and some ratsnests will be left active. + */ + void Propagate_SubNets(); + +private: + /** + * function searchEntryPointInCandidatesList + * Search an item in m_Connected connected to aPoint + * note m_Connected containts usually more than one candidate + * and searchEntryPointInCandidatesList returns an index to one of these candidates + * Others are neightbor of the indexed item. + * @param aPoint is the reference coordinates + * @return the index of item found or -1 if no candidate + */ + int searchEntryPointInCandidatesList( const wxPoint & aPoint); + + /** + * Function Merge_SubNets + * Change a subnet old value to a new value, for tracks and pads which are connected to + * tracks from m_firstTrack to m_lastTrack and their connected pads. + * and modify the subnet parameter (change the old value to the new value). + * After that, 2 cluster (or subnets) are merged into only one. + * Note: the resulting sub net value is the smallest between aOldSubNet and aNewSubNet + * @return modification count + * @param aOldSubNet = subnet value to modify + * @param aNewSubNet = new subnet value for each item which have old_val as subnet value + */ + int Merge_SubNets( int aOldSubNet, int aNewSubNet ); + + /** + * Function Merge_PadsSubNets + * Change a subnet value to a new value, in m_sortedPads pad list + * After that, 2 cluster (or subnets) are merged into only one. + * Note: the resulting subnet value is the smallest between aOldSubNet et aNewSubNet + * @return modification count + * @param aOldSubNet = subnet value to modify + * @param aNewSubNet = new subnet value for each item which have old_val as subnet value + */ + int Merge_PadsSubNets( int aOldSubNet, int aNewSubNet ); +}; + +#endif // ifndef CONNECT_H diff --git a/pcbnew/dialogs/dialog_cleaning_options.cpp b/pcbnew/dialogs/dialog_cleaning_options.cpp index 2f745caf17..eebd22076d 100644 --- a/pcbnew/dialogs/dialog_cleaning_options.cpp +++ b/pcbnew/dialogs/dialog_cleaning_options.cpp @@ -1,8 +1,29 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: dialog_cleaning_options.cpp -// Author: jean-pierre Charras -///////////////////////////////////////////////////////////////////////////// +/** + * @file dialog_cleaning_options.cpp + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-20112 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ #include #include @@ -14,7 +35,6 @@ DIALOG_CLEANING_OPTIONS::DIALOG_CLEANING_OPTIONS( wxWindow* parent ): m_cleanViasOpt->SetValue( cleanVias ); m_mergeSegmOpt->SetValue( mergeSegments ); m_deleteUnconnectedOpt->SetValue( deleteUnconnectedSegm ); - m_reconnectToPadsOpt->SetValue( connectToPads ); m_sdbSizerOK->SetDefault(); GetSizer()->SetSizeHints(this); @@ -25,5 +45,4 @@ DIALOG_CLEANING_OPTIONS::DIALOG_CLEANING_OPTIONS( wxWindow* parent ): bool DIALOG_CLEANING_OPTIONS::cleanVias = true; bool DIALOG_CLEANING_OPTIONS::mergeSegments = true; bool DIALOG_CLEANING_OPTIONS::deleteUnconnectedSegm = true; -bool DIALOG_CLEANING_OPTIONS::connectToPads = false; diff --git a/pcbnew/dialogs/dialog_cleaning_options.h b/pcbnew/dialogs/dialog_cleaning_options.h index 14dcf0efa2..fba6a428e1 100644 --- a/pcbnew/dialogs/dialog_cleaning_options.h +++ b/pcbnew/dialogs/dialog_cleaning_options.h @@ -14,7 +14,6 @@ public: static bool cleanVias; static bool mergeSegments; static bool deleteUnconnectedSegm; - static bool connectToPads; public: DIALOG_CLEANING_OPTIONS( wxWindow* parent ); @@ -44,7 +43,6 @@ private: cleanVias = m_cleanViasOpt->GetValue( ); mergeSegments = m_mergeSegmOpt->GetValue( ); deleteUnconnectedSegm = m_deleteUnconnectedOpt->GetValue( ); - connectToPads = m_reconnectToPadsOpt->GetValue( ); } }; diff --git a/pcbnew/dialogs/dialog_cleaning_options_base.cpp b/pcbnew/dialogs/dialog_cleaning_options_base.cpp index 86043c9837..f73d8a53df 100644 --- a/pcbnew/dialogs/dialog_cleaning_options_base.cpp +++ b/pcbnew/dialogs/dialog_cleaning_options_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Sep 8 2010) +// C++ code generated with wxFormBuilder (version Apr 10 2012) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -24,7 +24,7 @@ DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wx bSizerUpper->Add( m_cleanViasOpt, 0, wxALL, 5 ); - m_mergeSegmOpt = new wxCheckBox( this, wxID_ANY, _("Merge segments"), wxDefaultPosition, wxDefaultSize, 0 ); + m_mergeSegmOpt = new wxCheckBox( this, wxID_ANY, _("Merge colinear segments"), wxDefaultPosition, wxDefaultSize, 0 ); m_mergeSegmOpt->SetToolTip( _("merge aligned track segments, and remove null segments") ); bSizerUpper->Add( m_mergeSegmOpt, 0, wxALL, 5 ); @@ -34,10 +34,6 @@ DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wx bSizerUpper->Add( m_deleteUnconnectedOpt, 0, wxALL, 5 ); - m_reconnectToPadsOpt = new wxCheckBox( this, wxID_ANY, _("Connect to pads"), wxDefaultPosition, wxDefaultSize, 0 ); - m_reconnectToPadsOpt->SetToolTip( _("Extend dangling tracks which partially cover a pad or via, all the way to pad or via center") ); - - bSizerUpper->Add( m_reconnectToPadsOpt, 0, wxALL, 5 ); bSizerMain->Add( bSizerUpper, 1, wxEXPAND|wxALL, 5 ); @@ -50,8 +46,10 @@ DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wx m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); m_sdbSizer->AddButton( m_sdbSizerCancel ); m_sdbSizer->Realize(); + bSizerMain->Add( m_sdbSizer, 0, wxALIGN_RIGHT|wxALL, 5 ); + this->SetSizer( bSizerMain ); this->Layout(); diff --git a/pcbnew/dialogs/dialog_cleaning_options_base.fbp b/pcbnew/dialogs/dialog_cleaning_options_base.fbp index bd09b4c5d4..1f7c56b07b 100644 --- a/pcbnew/dialogs/dialog_cleaning_options_base.fbp +++ b/pcbnew/dialogs/dialog_cleaning_options_base.fbp @@ -1,12 +1,14 @@ - + C++ 1 source_name + 0 0 + res UTF-8 connect dialog_cleaning_options_base @@ -18,10 +20,13 @@ . 1 + 1 1 0 0 + 0 + wxAUI_MGR_DEFAULT wxBOTH @@ -37,20 +42,22 @@ DIALOG_CLEANING_OPTIONS_BASE - 243,181 + 243,146 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER Cleaning options - - wxFILTER_NONE - wxDefaultValidator - + + + + + + OnCloseWindow @@ -98,24 +105,55 @@ wxALL 0 + 1 + 1 + 1 + 1 + + + + + + + 1 + 0 0 + 1 1 + 0 + Dock + 0 + Left 1 + 1 + 0 0 wxID_ANY Delete redundant vias + + 0 + + 0 + 1 m_cleanViasOpt + 1 + + protected + 1 + Resizable + 1 + 0 remove vias on pads with a through hole bool wxFILTER_NUMERIC @@ -155,24 +193,55 @@ wxALL 0 + 1 + 1 + 1 + 1 + + + + + + + 1 + 0 0 + 1 1 + 0 + Dock + 0 + Left 1 + 1 + 0 0 wxID_ANY - Merge segments + Merge colinear segments + + 0 + + 0 + 1 m_mergeSegmOpt + 1 + + protected + 1 + Resizable + 1 + 0 merge aligned track segments, and remove null segments bool wxFILTER_NUMERIC @@ -212,24 +281,55 @@ wxALL 0 + 1 + 1 + 1 + 1 + + + + + + + 1 + 0 0 + 1 1 + 0 + Dock + 0 + Left 1 + 1 + 0 0 wxID_ANY Delete unconnected tracks + + 0 + + 0 + 1 m_deleteUnconnectedOpt + 1 + + protected + 1 + Resizable + 1 + 0 delete track segment having a dangling end bool wxFILTER_NUMERIC @@ -264,63 +364,6 @@ - - 5 - wxALL - 0 - - - 0 - - 1 - 1 - - - 0 - wxID_ANY - Connect to pads - - - m_reconnectToPadsOpt - protected - - - - - Extend dangling tracks which partially cover a pad or via, all the way to pad or via center - bool - wxFILTER_NUMERIC - wxDefaultValidator - connectToPads - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -328,27 +371,54 @@ wxEXPAND | wxALL 0 + 1 + 1 + 1 + 1 + + + + + + + 1 + 0 + 1 1 + 0 + Dock + 0 + Left 1 + 1 + 0 0 wxID_ANY + + 0 + + 0 + 1 m_staticline + 1 + + protected + 1 + Resizable + 1 wxLI_HORIZONTAL + 0 - - wxFILTER_NONE - wxDefaultValidator - diff --git a/pcbnew/dialogs/dialog_cleaning_options_base.h b/pcbnew/dialogs/dialog_cleaning_options_base.h index cda49ed323..31a83b2bf2 100644 --- a/pcbnew/dialogs/dialog_cleaning_options_base.h +++ b/pcbnew/dialogs/dialog_cleaning_options_base.h @@ -1,15 +1,16 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Sep 8 2010) +// C++ code generated with wxFormBuilder (version Apr 10 2012) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// -#ifndef __dialog_cleaning_options_base__ -#define __dialog_cleaning_options_base__ +#ifndef __DIALOG_CLEANING_OPTIONS_BASE_H__ +#define __DIALOG_CLEANING_OPTIONS_BASE_H__ +#include +#include #include - #include #include #include @@ -35,7 +36,6 @@ class DIALOG_CLEANING_OPTIONS_BASE : public wxDialog wxCheckBox* m_cleanViasOpt; wxCheckBox* m_mergeSegmOpt; wxCheckBox* m_deleteUnconnectedOpt; - wxCheckBox* m_reconnectToPadsOpt; wxStaticLine* m_staticline; wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; @@ -51,11 +51,10 @@ class DIALOG_CLEANING_OPTIONS_BASE : public wxDialog bool cleanVias; bool mergeSegments; bool deleteUnconnectedSegm; - bool connectToPads; - DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleaning options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 243,181 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleaning options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 243,146 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DIALOG_CLEANING_OPTIONS_BASE(); }; -#endif //__dialog_cleaning_options_base__ +#endif //__DIALOG_CLEANING_OPTIONS_BASE_H__