kicad/pcbnew/track.cpp

546 lines
18 KiB
C++

/*****************
* track.cpp
*****************/
#include "fctsys.h"
#include "common.h"
#include "pcbnew.h"
#include "protos.h"
/* Functions to recognize a track.
* A track is a list of connected segments (or/and vias)
* from a starting to an ending point
* starting and ending points are a pad or a point with more than 2 segments
*connected
* (and obviously a dangling segment end)
*/
typedef std::vector<TRACK*> TRACK_PTRS; // buffer of item candidates when
// search for items on the same track
/* Local functions */
static void Marque_Chaine_segments( BOARD* Pcb,
wxPoint ref_pos,
int masklayer,
TRACK_PTRS* aList );
/**
* Function Marque_Une_Piste
* marks a chain of track segments, connected to aTrackList.
* Each segment is marked by setting the BUSY bit into m_Flags. Electrical
* continuity is detected by walking each segment, and finally the segments
* are rearranged into a contiguous chain within the given list.
* @param aPcb = the board to analyze
* @param aStartSegm - The first interesting segment within a list of track
* segment of aPcb
* @param aSegmCount = a pointer to an integer where to return the number of
* interesting segments
* @param aTrackLen = a pointer to an integer where to return the length of the
* track
* @param aReorder = bool:
* true for reorder the interesting segments (useful for track
*edition/deletion)
* in this case the flag BUSY is set (the user is responsible of flag
*clearing)
* false for no reorder : useful when we want just calculate the track length
* in this case, flags are reset
* @return TRACK* the first in the chain of interesting segments.
*/
TRACK* Marque_Une_Piste( BOARD* aPcb,
TRACK* aStartSegm,
int* aSegmCount,
int* aTrackLen,
bool aReorder )
{
int NbSegmBusy;
TRACK_PTRS trackList;
if( aSegmCount )
*aSegmCount = 0;
if( aTrackLen )
*aTrackLen = 0;
if( aStartSegm == NULL )
return NULL;
// Ensure the flag BUSY of all tracks of the board is cleared
// because we use it to mark segments of the track
for( TRACK* track = aPcb->m_Track; track; track = track->Next() )
track->SetState( BUSY, OFF );
/* Set flags of the initial track segment */
aStartSegm->SetState( BUSY, ON );
int masque_layer = aStartSegm->ReturnMaskLayer();
trackList.push_back( aStartSegm );
/* Examine the initial track segment : if it is really a segment, this is
* easy.
* If it is a via, one must search for connected segments.
* If <=2, this via connect 2 segments (or is connected to only one
* segment) and this via and these 2 segments are a part of a track.
* If > 2 only this via is flagged (the track has only this via)
*/
if( aStartSegm->Type() == TYPE_VIA )
{
TRACK* Segm1, * Segm2 = NULL, * Segm3 = NULL;
Segm1 = Fast_Locate_Piste( aPcb->m_Track, NULL,
aStartSegm->m_Start, masque_layer );
if( Segm1 )
{
Segm2 = Fast_Locate_Piste( Segm1->Next(), NULL,
aStartSegm->m_Start, masque_layer );
}
if( Segm2 )
{
Segm3 = Fast_Locate_Piste( Segm2->Next(), NULL,
aStartSegm->m_Start, masque_layer );
}
if( Segm3 ) // More than 2 segments are connected to this via. the
// "track" is only this via
{
if( aSegmCount )
*aSegmCount = 1;
return aStartSegm;
}
if( Segm1 ) // search for others segments connected to the initial
// segment start point
{
masque_layer = Segm1->ReturnMaskLayer();
Marque_Chaine_segments( aPcb, aStartSegm->m_Start, masque_layer,
&trackList );
}
if( Segm2 ) // search for others segments connected to the initial
// segment end point
{
masque_layer = Segm2->ReturnMaskLayer();
Marque_Chaine_segments( aPcb, aStartSegm->m_Start, masque_layer,
&trackList );
}
}
else // mark the chain using both ends of the initial segment
{
Marque_Chaine_segments( aPcb,
aStartSegm->m_Start,
masque_layer,
&trackList );
Marque_Chaine_segments( aPcb,
aStartSegm->m_End,
masque_layer,
&trackList );
}
// Now examine selected vias and flag them if they are on the track
// If a via is connected to only one or 2 segments, it is flagged (is on
// the track)
// If a via is connected to more than 2 segments, it is a track end, and it
// is removed from the list
// go through the list backwards.
for( int i = trackList.size() - 1; i>=0; --i )
{
TRACK* via = trackList[i];
if( via->Type() != TYPE_VIA )
continue;
if( via == aStartSegm )
continue;
via->SetState( BUSY, ON ); // Try to flag it. the flag will be cleared
// later if needed
masque_layer = via->ReturnMaskLayer();
TRACK* track = Fast_Locate_Piste( aPcb->m_Track,
NULL,
via->m_Start,
masque_layer );
// Fast_Locate_Piste does not consider tracks flagged BUSY.
// So if no connected track found, this via is on the current track
// only: keep it
if( track == NULL )
continue;
/* If a track is found, this via connects also others segments of an
* other track. This case happens when the vias ends the selected
* track but must we consider this via is on the selected track, or
* on an other track.
* (this is important when selecting a track for deletion: must this
* via be deleted or not?)
* We consider here this via on the track if others segment connected
* to this via remain connected when removing this via.
* We search for all others segment connected together:
* if there are on the same layer, the via is on the selected track
* if there are on different layers, the via is on an other track
*/
int layer = track->GetLayer();
while( ( track = Fast_Locate_Piste( track->Next(), NULL,
via->m_Start,
masque_layer ) ) != NULL )
{
if( layer != track->GetLayer() )
{
// The via connects segments of an other track: it is removed
// from list because it is member of an other track
via->SetState( BUSY, OFF );
break;
}
}
}
/* Rearrange the track list in order to have flagged segments linked
* from firstTrack so the NbSegmBusy segments are consecutive segments
* in list, the first item in the full track list is firstTrack, and
* the NbSegmBusy-1 next items (NbSegmBusy when including firstTrack)
* are the flagged segments
*/
NbSegmBusy = 0;
TRACK* firstTrack;
for( firstTrack = aPcb->m_Track;
firstTrack;
firstTrack = firstTrack->Next() )
{
// Search for the first flagged BUSY segments
if( firstTrack->GetState( BUSY ) )
{
NbSegmBusy = 1;
break;
}
}
if( firstTrack == NULL )
return NULL;
double full_len = 0;
if( aReorder )
{
DLIST<TRACK>* list = (DLIST<TRACK>*)firstTrack->GetList();
wxASSERT( list );
/* Rearrange the chain starting at firstTrack
* All others flagged items are moved from their position to the end
* of the flagged list
*/
TRACK* next;
for( TRACK* track = firstTrack->Next(); track; track = next )
{
next = track->Next();
if( track->GetState( BUSY ) ) // move it!
{
NbSegmBusy++;
track->UnLink();
list->Insert( track, firstTrack->Next() );
if( aTrackLen )
full_len += track->GetLength();
}
}
}
else if( aTrackLen )
{
NbSegmBusy = 0;
for( TRACK* track = firstTrack; track; track = track->Next() )
{
if( track->GetState( BUSY ) )
{
NbSegmBusy++;
track->SetState( BUSY, OFF );
full_len += track->GetLength();
}
}
}
if( aTrackLen )
*aTrackLen = wxRound( full_len );
if( aSegmCount )
*aSegmCount = NbSegmBusy;
return firstTrack;
}
/**
* Function used by Marque_Une_Piste()
* - Set the BUSY flag of connected segments, the first search point is
* ref_pos on layers allowed in masque_layer
* - Put segments fount in aList
* Vias are put in list but their flags BUSY is not set
* @param Pcb = the board
* @param aRef_pos = the reference coordinate of the starting search
* @param aLayerMask = the allowed layers for segments to search
* (1 layer when starting point is on a segment, but more than one when
* starting point is on a via)
* @param aList = the track list to fill with points of segments flagged
*/
static void Marque_Chaine_segments( BOARD* aPcb,
wxPoint aRef_pos,
int aLayerMask,
TRACK_PTRS* aList )
{
TRACK* pt_segm, // The current segment being analyzed.
* pt_via, // The via identified, eventually destroy
* SegmentCandidate; // The end segment to destroy (or NULL =
// pt_segm
int NbSegm;
if( aPcb->m_Track == NULL )
return;
/* Set the BUSY flag of all connected segments, first search starting at
* aRef_pos
* Search ends when:
* - a pad is found (end of a track)
* - a segment end has more than one other segment end connected
* - and obviously when no connected item found
* Vias are a special case, because we must see others segment connected
* on others layers and they change the layer mask. They can be a track
* end or not
* They will be analyzer later, and vias on terminal points of the track
* will be considered as part of this track if they do not connect segments
* of an other track together and will be considered as part of an other
* track if when removing the via, the segments of that other track are
* disconnected
*/
for( ; ; )
{
if( Fast_Locate_Pad_Connecte( aPcb, aRef_pos, aLayerMask ) != NULL )
return;
/* Test for a via: a via changes the layer mask and can connect a lot
* of segments at location aRef_pos. When found, the via is just
* pushed in list. Vias will be examined later, when all connected
* segment are found and push in list. This is because when a via
* is found we do not know at this time the number of connected items
* and we do not know if this via is on the track or finish the track
*/
pt_via = Fast_Locate_Via( aPcb->m_Track, NULL, aRef_pos, aLayerMask );
if( pt_via )
{
aLayerMask = pt_via->ReturnMaskLayer();
aList->push_back( pt_via );
}
/* Now we search all segments connected to point aRef_pos
* if only 1 segment: this segment is candidate
* if > 1 segment:
* end of track (more than 2 segment connected at this location)
*/
pt_segm = aPcb->m_Track; SegmentCandidate = NULL;
NbSegm = 0;
while( ( pt_segm = Fast_Locate_Piste( pt_segm, NULL,
aRef_pos, aLayerMask ) ) != NULL )
{
if( pt_segm->GetState( BUSY ) ) // already found and selected: skip
// it
{
pt_segm = pt_segm->Next();
continue;
}
if( pt_segm == pt_via ) // just previously found: skip it
{
pt_segm = pt_segm->Next();
continue;
}
NbSegm++;
if( NbSegm == 1 ) /* First time we found a connected item: pt_segm
* is candidate */
{
SegmentCandidate = pt_segm;
pt_segm = pt_segm->Next();
}
else /* More than 1 segment connected -> this location is an end of
* the track */
{
return;
}
}
if( SegmentCandidate ) // A candidate is found: flag it an push it
// in list
{
/* Initialize parameters to search items connected to this
* candidate:
* we must analyze connections to its other end
*/
aLayerMask = SegmentCandidate->ReturnMaskLayer();
if( aRef_pos == SegmentCandidate->m_Start )
{
aRef_pos = SegmentCandidate->m_End;
}
else
{
aRef_pos = SegmentCandidate->m_Start;
}
pt_segm = aPcb->m_Track; /* restart list of tracks to analyze */
/* flag this item an push it in list of selected items */
aList->push_back( SegmentCandidate );
SegmentCandidate->SetState( BUSY, ON );
}
else
return;
}
}
/* Calculate the end points coordinates of a track (a list of connected
* segments)
* RefTrack is a segment of the track
* return 1 if OK, 0 when a track is a closed loop
* and the beginning and the end of the track in *StartTrack and *EndTrack
* Modify *StartTrack en *EndTrack :
* (*StartTrack)->m_Start coordinate is the beginning of the track
* (*EndTrack)->m_End coordinate is the end of the track
* Segments connected must be consecutive in list
*/
int ReturnEndsTrack( TRACK* RefTrack, int NbSegm,
TRACK** StartTrack, TRACK** EndTrack )
{
TRACK* Track, * via, * segm, * TrackListEnd;
int NbEnds, masque_layer, ii, ok = 0;
if( NbSegm <= 1 )
{
*StartTrack = *EndTrack = RefTrack;
return 1;
}
/* Calculation of the limit analysis. */
*StartTrack = *EndTrack = NULL;
TrackListEnd = Track = RefTrack; ii = 0;
for( ; ( Track != NULL ) && ( ii < NbSegm ); ii++, Track = Track->Next() )
{
TrackListEnd = Track;
Track->m_Param = 0;
}
/* Calculate the extremes. */
NbEnds = 0; Track = RefTrack; ii = 0;
for( ; ( Track != NULL ) && ( ii < NbSegm ); ii++, Track = Track->Next() )
{
if( Track->Type() == TYPE_VIA )
continue;
masque_layer = Track->ReturnMaskLayer();
via = Fast_Locate_Via( RefTrack, TrackListEnd,
Track->m_Start, masque_layer );
if( via )
{
masque_layer |= via->ReturnMaskLayer();
via->SetState( BUSY, ON );
}
Track->SetState( BUSY, ON );
segm = Fast_Locate_Piste( RefTrack, TrackListEnd,
Track->m_Start, masque_layer );
Track->SetState( BUSY, OFF );
if( via )
via->SetState( BUSY, OFF );
if( segm == NULL )
{
switch( NbEnds )
{
case 0:
*StartTrack = Track; NbEnds++;
break;
case 1:
int BeginPad, EndPad;
*EndTrack = Track;
/* Swap ox, oy with fx, fy */
BeginPad = Track->GetState( BEGIN_ONPAD );
EndPad = Track->GetState( END_ONPAD );
Track->SetState( BEGIN_ONPAD | END_ONPAD, OFF );
if( BeginPad )
Track->SetState( END_ONPAD, ON );
if( EndPad )
Track->SetState( BEGIN_ONPAD, ON );
EXCHG( Track->m_Start, Track->m_End );
EXCHG( Track->start, Track->end );
ok = 1;
return ok;
}
}
masque_layer = Track->ReturnMaskLayer();
via = Fast_Locate_Via( RefTrack, TrackListEnd,
Track->m_End, masque_layer );
if( via )
{
masque_layer |= via->ReturnMaskLayer();
via->SetState( BUSY, ON );
}
Track->SetState( BUSY, ON );
segm = Fast_Locate_Piste( RefTrack, TrackListEnd,
Track->m_End, masque_layer );
Track->SetState( BUSY, OFF );
if( via )
via->SetState( BUSY, OFF );
if( segm == NULL )
{
switch( NbEnds )
{
case 0:
int BeginPad, EndPad;
*StartTrack = Track;
NbEnds++;
/* Swap ox, oy with fx, fy */
BeginPad = Track->GetState( BEGIN_ONPAD );
EndPad = Track->GetState( END_ONPAD );
Track->SetState( BEGIN_ONPAD | END_ONPAD, OFF );
if( BeginPad )
Track->SetState( END_ONPAD, ON );
if( EndPad )
Track->SetState( BEGIN_ONPAD, ON );
EXCHG( Track->m_Start, Track->m_End );
EXCHG( Track->start, Track->end );
break;
case 1:
*EndTrack = Track;
ok = 1;
return ok;
}
}
}
return ok;
}
/* Set to onoff the .m_State member, bit mask State of a list of items
*/
void ListSetState( EDA_BaseStruct* Start, int NbItem, int State, int onoff )
{
if( Start == NULL )
return;
for( ; (Start != NULL ) && ( NbItem > 0 ); NbItem--, Start = Start->Next() )
{
Start->SetState( State, onoff );
}
}