kicad/pcbnew/editrack.cpp

1065 lines
32 KiB
C++

/*******************************/
/* Edition des pistes */
/* Routines de trace de pistes */
/*******************************/
#include "fctsys.h"
#include "common.h"
#include "class_drawpanel.h"
#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "class_board_design_settings.h"
#include "drc_stuff.h"
#include "trigo.h"
#include "protos.h"
/* Routines Locales */
static void Exit_Editrack( WinEDA_DrawPanel* panel, wxDC* DC );
void ShowNewTrackWhenMovingCursor( WinEDA_DrawPanel* panel,
wxDC* DC, bool erase );
static void ComputeBreakPoint( TRACK* track, int n, wxPoint end );
static void DeleteNullTrackSegments( BOARD* pcb, DLIST<TRACK>& aTrackList );
static void EnsureEndTrackOnPad( D_PAD* Pad );
/* variables locales */
static int OldNetCodeSurbrillance;
static int OldEtatSurbrillance;
static PICKED_ITEMS_LIST s_ItemsListPicker;
/************************************************************/
static void Exit_Editrack( WinEDA_DrawPanel* Panel, wxDC* DC )
/************************************************************/
/* routine d'annulation de la Commande Begin_Route si une piste est en cours
* de tracage, ou de sortie de l'application EDITRACK.
*/
{
WinEDA_PcbFrame* frame = (WinEDA_PcbFrame*) Panel->m_Parent;
TRACK* track = (TRACK*) frame->GetCurItem();
if( track && ( track->Type()==TYPE_VIA || track->Type()==TYPE_TRACK ) )
{
/* Erase the current drawing */
ShowNewTrackWhenMovingCursor( Panel, DC, false );
if( g_HightLigt_Status )
frame->Hight_Light( DC );
g_HightLigth_NetCode = OldNetCodeSurbrillance;
if( OldEtatSurbrillance )
frame->Hight_Light( DC );
frame->MsgPanel->EraseMsgBox();
// Undo pending changes (mainly a lock point cretion) and clear the undo picker list:
frame->PutDataInPreviousState( &s_ItemsListPicker, false, false );
s_ItemsListPicker.ClearListAndDeleteItems();
// Delete current (new) track
g_CurrentTrackList.DeleteAll();
}
Panel->ManageCurseur = NULL;
Panel->ForceCloseManageCurseur = NULL;
frame->SetCurItem( NULL );
}
/*************************************************************/
TRACK* WinEDA_PcbFrame::Begin_Route( TRACK* aTrack, wxDC* DC )
/*************************************************************/
/*
* Routine d'initialisation d'un trace de piste et/ou de mise en place d'un
* nouveau point piste
*
* Si pas de piste en cours de trace:
* - Recherche de netname de la nouvelle piste ( pad de depart out netname
* de la piste si depart sur une ancienne piste
* - Met en surbrillance tout le net
* - Initilise les divers pointeurs de trace
* Si piste en cours:
* - controle DRC
* - si DRC OK : addition d'un nouveau point piste
*/
{
D_PAD* pt_pad = NULL;
TRACK* TrackOnStartPoint = NULL;
int masquelayer = g_TabOneLayerMask[( (PCB_SCREEN*) GetScreen() )->m_Active_Layer];
BOARD_ITEM* LockPoint;
wxPoint pos = GetScreen()->m_Curseur;
DrawPanel->ManageCurseur = ShowNewTrackWhenMovingCursor;
DrawPanel->ForceCloseManageCurseur = Exit_Editrack;
if( aTrack == NULL ) /* Starting a new track */
{
// Prepare the undo command info
s_ItemsListPicker.ClearListAndDeleteItems(); // Should not be necessary, but...
/* erase old highlight */
OldNetCodeSurbrillance = g_HightLigth_NetCode;
OldEtatSurbrillance = g_HightLigt_Status;
if( g_HightLigt_Status )
Hight_Light( DC );
g_CurrentTrackList.PushBack( new TRACK( GetBoard() ) );
g_CurrentTrackSegment->m_Flags = IS_NEW;
g_HightLigth_NetCode = 0;
// Search for a starting point of the new track, a track or pad
LockPoint = LocateLockPoint( GetBoard(), pos, masquelayer );
if( LockPoint ) // An item (pad or track) is found
{
if( LockPoint->Type() == TYPE_PAD )
{
pt_pad = (D_PAD*) LockPoint;
/* A pad is found: put the starting point on pad centre */
pos = pt_pad->m_Pos;
g_HightLigth_NetCode = pt_pad->GetNet();
}
else /* A track segment is found */
{
TrackOnStartPoint = (TRACK*) LockPoint;
g_HightLigth_NetCode = TrackOnStartPoint->GetNet();
CreateLockPoint( pos, TrackOnStartPoint, NULL, &s_ItemsListPicker );
}
}
else // no starting point, but a filled zone area can exist. This is also a good starting point.
{
ZONE_CONTAINER* zone = GetBoard()->HitTestForAnyFilledArea( pos,
GetScreen()->
m_Active_Layer );
if( zone )
g_HightLigth_NetCode = zone->GetNet();
}
D( g_CurrentTrackList.VerifyListIntegrity(); );
build_ratsnest_pad( LockPoint, wxPoint( 0, 0 ), true );
D( g_CurrentTrackList.VerifyListIntegrity(); );
Hight_Light( DC );
// Display info about track Net class, and init track and vias sizes:
g_CurrentTrackSegment->SetNet( g_HightLigth_NetCode );
GetBoard()->SetCurrentNetClass( g_CurrentTrackSegment->GetNetClassName() );
m_TrackAndViasSizesList_Changed = true;
AuxiliaryToolBar_Update_UI();
g_CurrentTrackSegment->SetLayer( GetScreen()->m_Active_Layer );
g_CurrentTrackSegment->m_Width = g_DesignSettings.m_CurrentTrackWidth;
if( g_DesignSettings.m_UseConnectedTrackWidth )
{
if( TrackOnStartPoint && TrackOnStartPoint->Type() == TYPE_TRACK )
g_CurrentTrackSegment->m_Width = TrackOnStartPoint->m_Width;
}
g_CurrentTrackSegment->m_Start = pos;
g_CurrentTrackSegment->m_End = pos;
if( pt_pad )
{
g_CurrentTrackSegment->start = pt_pad;
g_CurrentTrackSegment->SetState( BEGIN_ONPAD, ON );
}
else
g_CurrentTrackSegment->start = TrackOnStartPoint;
if( g_TwoSegmentTrackBuild )
{
// Create 2nd segment
g_CurrentTrackList.PushBack( g_CurrentTrackSegment->Copy() );
D( g_CurrentTrackList.VerifyListIntegrity(); );
g_CurrentTrackSegment->start = g_FirstTrackSegment;
g_FirstTrackSegment->end = g_CurrentTrackSegment;
g_FirstTrackSegment->SetState( BEGIN_ONPAD | END_ONPAD, OFF );
}
D( g_CurrentTrackList.VerifyListIntegrity(); );
g_CurrentTrackSegment->DisplayInfoBase( this );
SetCurItem( g_CurrentTrackSegment, false );
DrawPanel->ManageCurseur( DrawPanel, DC, false );
if( Drc_On )
{
if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
{
return g_CurrentTrackSegment;
}
}
}
else /* Track in progress : segment coordinates are updated by ShowNewTrackWhenMovingCursor*/
{
/* Tst for a D.R.C. error: */
if( Drc_On )
{
if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
return NULL;
// We must handle 2 segments
if( g_TwoSegmentTrackBuild && g_CurrentTrackSegment->Back() )
{
if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment->Back(), GetBoard()->m_Track ) )
return NULL;
}
}
/* Current track is Ok: current segment is kept, and a new one is created
* unless the current segment is null, or 2 last are null if a 2 segments track build
*/
bool CanCreateNewSegment = true;
if( !g_TwoSegmentTrackBuild && g_CurrentTrackSegment->IsNull() )
CanCreateNewSegment = false;
if( g_TwoSegmentTrackBuild && g_CurrentTrackSegment->IsNull()
&& g_CurrentTrackSegment->Back() && g_CurrentTrackSegment->Back()->IsNull() )
CanCreateNewSegment = false;
if( CanCreateNewSegment )
{
/* Erase old track on screen */
D( g_CurrentTrackList.VerifyListIntegrity(); );
ShowNewTrackWhenMovingCursor( DrawPanel, DC, false );
D( g_CurrentTrackList.VerifyListIntegrity(); );
if( g_Raccord_45_Auto )
{
Add_45_degrees_Segment( DC );
}
TRACK* oneBeforeLatest = g_CurrentTrackSegment;
TRACK* newTrack = g_CurrentTrackSegment->Copy();
g_CurrentTrackList.PushBack( newTrack );
newTrack->m_Flags = IS_NEW;
newTrack->SetState( BEGIN_ONPAD | END_ONPAD, OFF );
oneBeforeLatest->end = Locate_Pad_Connecte( GetBoard(), oneBeforeLatest, END );
if( oneBeforeLatest->end )
{
oneBeforeLatest->SetState( END_ONPAD, ON );
newTrack->SetState( BEGIN_ONPAD, ON );
}
newTrack->start = oneBeforeLatest->end;
D( g_CurrentTrackList.VerifyListIntegrity(); );
newTrack->m_Start = newTrack->m_End;
newTrack->SetLayer( ( (PCB_SCREEN*) GetScreen() )->m_Active_Layer );
if( !g_DesignSettings.m_UseConnectedTrackWidth )
{
newTrack->m_Width = g_DesignSettings.m_CurrentTrackWidth;
}
D( g_CurrentTrackList.VerifyListIntegrity(); );
/* Show the new position */
ShowNewTrackWhenMovingCursor( DrawPanel, DC, false );
}
}
SetCurItem( g_CurrentTrackSegment, false );
return g_CurrentTrackSegment;
}
/**************************************************************************/
bool WinEDA_PcbFrame::Add_45_degrees_Segment( wxDC* DC )
/***************************************************************************/
/* rectifie un virage a 90 et le modifie par 2 coudes a 45
* n'opere que sur des segments horizontaux ou verticaux.
*
* entree : pointeur sur le segment qui vient d'etre trace
* On suppose que le segment precedent est celui qui a ete
* precedement trace
* retourne:
* 1 si ok
* 0 si impossible
*/
{
int pas_45;
int dx0, dy0, dx1, dy1;
if( g_CurrentTrackList.GetCount() < 2 )
return false; /* il faut au moins 2 segments */
TRACK* curTrack = g_CurrentTrackSegment;
TRACK* prevTrack = curTrack->Back();
// Test s'il y a 2 segments consecutifs a raccorder
if( curTrack->Type() != TYPE_TRACK || prevTrack->Type() != TYPE_TRACK )
{
return false;
}
pas_45 = (int) GetScreen()->GetGridSize().x / 2;
if( pas_45 < curTrack->m_Width )
pas_45 = (int) GetScreen()->GetGridSize().x;
while( pas_45 < curTrack->m_Width )
pas_45 *= 2;
// OK : tst si les segments sont a 90 degre et vertic ou horiz
dx0 = prevTrack->m_End.x - prevTrack->m_Start.x;
dy0 = prevTrack->m_End.y - prevTrack->m_Start.y;
dx1 = curTrack->m_End.x - curTrack->m_Start.x;
dy1 = curTrack->m_End.y - curTrack->m_Start.y;
// les segments doivent etre de longueur suffisante:
if( MAX( abs( dx0 ), abs( dy0 ) ) < (pas_45 * 2) )
return false;
if( MAX( abs( dx1 ), abs( dy1 ) ) < (pas_45 * 2) )
return false;
/* creation du nouveau segment, raccordant des 2 segm: */
TRACK* newTrack = curTrack->Copy();
newTrack->m_Start = prevTrack->m_End;
newTrack->m_End = curTrack->m_Start;
if( dx0 == 0 ) // Segment precedent Vertical
{
if( dy1 != 0 ) // les 2 segments ne sont pas a 90 ;
{
delete newTrack;
return false;
}
/* Calcul des coordonnees du point de raccord :
* le nouveau segment raccorde le 1er segment Vertical
* au 2eme segment Horizontal */
if( dy0 > 0 )
newTrack->m_Start.y -= pas_45;
else
newTrack->m_Start.y += pas_45;
if( dx1 > 0 )
newTrack->m_End.x += pas_45;
else
newTrack->m_End.x -= pas_45;
if( Drc_On && BAD_DRC==m_drc->Drc( curTrack, GetBoard()->m_Track ) )
{
delete newTrack;
return false;
}
prevTrack->m_End = newTrack->m_Start;
curTrack->m_Start = newTrack->m_End;
g_CurrentTrackList.Insert( newTrack, curTrack );
return true;
}
if( dy0 == 0 ) // Segment precedent Horizontal : dy0 = 0
{
if( dx1 != 0 ) // les 2 segments ne sont pas a 90 ;
{
delete newTrack;
return false;
}
// Segments a 90
/* Modif des coordonnees du point de raccord :
* un nouveau segment a ete cree, raccordant le 1er segment Horizontal
* au 2eme segment Vertical */
if( dx0 > 0 )
newTrack->m_Start.x -= pas_45;
else
newTrack->m_Start.x += pas_45;
if( dy1 > 0 )
newTrack->m_End.y += pas_45;
else
newTrack->m_End.y -= pas_45;
if( Drc_On && BAD_DRC==m_drc->Drc( newTrack, GetBoard()->m_Track ) )
{
delete newTrack;
return false;
}
prevTrack->m_End = newTrack->m_Start;
curTrack->m_Start = newTrack->m_End;
g_CurrentTrackList.Insert( newTrack, curTrack );
return true;
}
return false;
}
/**************************************************************/
void WinEDA_PcbFrame::End_Route( TRACK* aTrack, wxDC* DC )
/*************************************************************/
/*
* Routine de fin de trace d'une piste (succession de segments)
*/
{
int masquelayer = g_TabOneLayerMask[( (PCB_SCREEN*) GetScreen() )->m_Active_Layer];
if( aTrack == NULL )
return;
if( Drc_On && BAD_DRC==m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
return;
/* Sauvegarde des coord du point terminal de la piste */
wxPoint pos = g_CurrentTrackSegment->m_End;
D( g_CurrentTrackList.VerifyListIntegrity(); );
if( Begin_Route( aTrack, DC ) == NULL )
return;
ShowNewTrackWhenMovingCursor( DrawPanel, DC, true ); /* mise a jour trace reel */
ShowNewTrackWhenMovingCursor( DrawPanel, DC, false ); /* efface trace piste*/
trace_ratsnest_pad( DC ); /* efface trace chevelu*/
/* cleanup
* if( g_CurrentTrackSegment->Next() != NULL )
* {
* delete g_CurrentTrackSegment->Next();
* g_CurrentTrackSegment->SetNext( NULL );
* }
*/
D( g_CurrentTrackList.VerifyListIntegrity(); );
/* La piste est ici non chainee a la liste des segments de piste.
* Il faut la replacer dans la zone de net,
* le plus pres possible du segment d'attache ( ou de fin ), car
* ceci contribue a la reduction du temps de calcul */
/* Accrochage de la fin de la piste */
BOARD_ITEM* LockPoint = LocateLockPoint( GetBoard(), pos, masquelayer );
if( LockPoint ) /* La fin de la piste est sur un PAD */
{
if( LockPoint->Type() == TYPE_PAD )
{
EnsureEndTrackOnPad( (D_PAD*) LockPoint );
}
else /* la fin de la piste est sur une autre piste: il faudra
* peut-etre creer un point d'ancrage */
{
TRACK* adr_buf = (TRACK*) LockPoint;
g_HightLigth_NetCode = adr_buf->GetNet();
/* creation eventuelle d'un point d'accrochage */
LockPoint = CreateLockPoint( g_CurrentTrackSegment->m_End,
adr_buf,
g_CurrentTrackSegment,
&s_ItemsListPicker );
}
}
// Delete Null segments:
DeleteNullTrackSegments( GetBoard(), g_CurrentTrackList );
// Insert new segments if they exist. This can be NULL on a double click on the start point
if( g_FirstTrackSegment != NULL )
{
int netcode = g_FirstTrackSegment->GetNet();
TRACK* firstTrack = g_FirstTrackSegment;
int newCount = g_CurrentTrackList.GetCount();
// Put entire new current segment list in BOARD, ans prepare undo command
TRACK* track;
TRACK* insertBeforeMe = g_CurrentTrackSegment->GetBestInsertPoint( GetBoard() );
while( ( track = g_CurrentTrackList.PopFront() ) != NULL )
{
ITEM_PICKER picker( track, UR_NEW );
s_ItemsListPicker.PushItem( picker );
GetBoard()->m_Track.Insert( track, insertBeforeMe );
}
trace_ratsnest_pad( DC );
Trace_Une_Piste( DrawPanel, DC, firstTrack, newCount, GR_OR );
int i = 0;
for( track = firstTrack; track && i<newCount; ++i, track = track->Next() )
{
track->m_Flags = 0;
track->SetState( BUSY, OFF );
}
// erase the old track, if exists
if( g_AutoDeleteOldTrack )
{
EraseRedundantTrack( DC, firstTrack, newCount, &s_ItemsListPicker );
}
SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items
/* compute the new rastnest : */
test_1_net_connexion( DC, netcode );
GetScreen()->SetModify();
GetBoard()->DisplayInfo( this );
}
wxASSERT( g_FirstTrackSegment==NULL );
wxASSERT( g_CurrentTrackSegment==NULL );
wxASSERT( g_CurrentTrackList.GetCount()==0 );
if( g_HightLigt_Status )
Hight_Light( DC );
g_HightLigth_NetCode = OldNetCodeSurbrillance;
if( OldEtatSurbrillance )
Hight_Light( DC );
DrawPanel->ManageCurseur = NULL;
DrawPanel->ForceCloseManageCurseur = NULL;
SetCurItem( NULL );
}
TRACK* LocateIntrusion( TRACK* listStart, TRACK* aTrack )
{
int net = aTrack->GetNet();
int width = aTrack->m_Width;
int layer = ( (PCB_SCREEN*) ActiveScreen )->m_Active_Layer;
wxPoint ref = ActiveScreen->RefPos( true );
TRACK* found = NULL;
for( TRACK* track = listStart; track; track = track->Next() )
{
if( track->Type() == TYPE_TRACK ) // skip vias
{
if( track->GetState( BUSY | DELETED ) )
continue;
if( layer != track->GetLayer() )
continue;
if( track->GetNet() == net )
continue;
/* TRACK::HitTest */
int dist = (width + track->m_Width) / 2 + aTrack->GetClearance( track );
wxPoint pos = ref - track->m_Start;
wxPoint vec = track->m_End - track->m_Start;
if( !DistanceTest( dist, vec.x, vec.y, pos.x, pos.y ) )
continue;
found = track;
/* prefer intrusions from the side, not the end */
double tmp = (double) pos.x * vec.x + (double) pos.y * vec.y;
if( tmp >= 0 && tmp <= (double) vec.x * vec.x + (double) vec.y * vec.y )
break;
}
}
return found;
}
/**
* Function PushTrack
* detects if the mouse is pointing into a conflicting track.
* In this case, it tries to push the new track out of the conflicting track's
* clearance zone. This gives us a cheap mechanism for drawing tracks that
* tightly follow others, independent of grid settings.
*
* KNOWN BUGS:
* - we do the same sort of search and calculation up to three times:
* 1) we search for magnetic hits (in controle.cpp)
* 2) we check if there's a DRC violation in the making (also controle.cpp)
* 3) we try to fix the DRC violation (here)
* - if we have a magnetic hit and a DRC violation at the same time, we choose
* the magnetic hit instead of solving the violation
* - should locate conflicting tracks also when we're crossing over them
*/
static void PushTrack( WinEDA_DrawPanel* panel )
{
BOARD* pcb = ( (WinEDA_BasePcbFrame*)(panel->m_Parent) )->GetBoard();
wxPoint cursor = ActiveScreen->m_Curseur;
wxPoint cv, vec, n;
TRACK* track = g_CurrentTrackSegment;
TRACK* other;
double det;
int dist;
double f;
other = LocateIntrusion( pcb->m_Track, track );
/* are we currently pointing into a conflicting trace ? */
if( !other )
return;
if( other->GetNet() == track->GetNet() )
return;
cv = cursor - other->m_Start;
vec = other->m_End - other->m_Start;
det = (double) cv.x * vec.y - (double) cv.y * vec.x;
/* cursor is right at the center of the old track */
if( !det )
return;
dist = (track->m_Width + 1) / 2 + (other->m_Width + 1) / 2 + track->GetClearance( other ) + 2;
/*
* DRC wants >, so +1.
* We may have a quantization error of 1/sqrt(2), so +1 again.
*/
/* Vector "n" is perpendicular to "other", pointing towards the cursor. */
if( det > 0 )
{
n.x = vec.y;
n.y = -vec.x;
}
else
{
n.x = -vec.y;
n.y = vec.x;
}
f = dist / hypot( double(n.x), double(n.y) );
n.x = wxRound( f * n.x );
n.y = wxRound( f * n.y );
Project( &track->m_End, cursor, other );
track->m_End += n;
}
/****************************************************************************/
void ShowNewTrackWhenMovingCursor( WinEDA_DrawPanel* panel, wxDC* DC, bool erase )
/****************************************************************************/
/* Redraw the current track beiing created when the mouse cursor is moved
*/
{
D( g_CurrentTrackList.VerifyListIntegrity(); );
PCB_SCREEN* screen = (PCB_SCREEN*) panel->GetScreen();
WinEDA_BasePcbFrame* frame = (WinEDA_BasePcbFrame*) panel->m_Parent;
bool Track_fill_copy = DisplayOpt.DisplayPcbTrackFill;
DisplayOpt.DisplayPcbTrackFill = true;
int showTrackClearanceMode = DisplayOpt.ShowTrackClearanceMode;
NETCLASS* netclass = g_FirstTrackSegment->GetNetClass();
if( showTrackClearanceMode != DO_NOT_SHOW_CLEARANCE )
DisplayOpt.ShowTrackClearanceMode = SHOW_CLEARANCE_ALWAYS;
/* Erase old track */
if( erase )
{
Trace_Une_Piste( panel, DC, g_FirstTrackSegment, g_CurrentTrackList.GetCount(), GR_XOR );
frame->trace_ratsnest_pad( DC );
if( showTrackClearanceMode >= SHOW_CLEARANCE_NEW_TRACKS_AND_VIA_AREAS ) // Show the via area
{
int color = g_DesignSettings.m_LayerColor[g_CurrentTrackSegment->GetLayer()];
GRCircle( &panel->m_ClipBox, DC, g_CurrentTrackSegment->m_End.x,
g_CurrentTrackSegment->m_End.y,
(netclass->GetViaDiameter() / 2) + netclass->GetClearance(),
color );
}
}
// MacOSX seems to need this.
if( g_CurrentTrackList.GetCount() == 0 )
return;
// Set track parameters, that can be modified while creating the track
g_CurrentTrackSegment->SetLayer( screen->m_Active_Layer );
g_CurrentTrackSegment->m_Width = g_DesignSettings.m_CurrentTrackWidth;
if( g_TwoSegmentTrackBuild )
{
TRACK* previous_track = g_CurrentTrackSegment->Back();
if( previous_track && previous_track->Type()==TYPE_TRACK )
{
previous_track->SetLayer( screen->m_Active_Layer );
if( !g_DesignSettings.m_UseConnectedTrackWidth )
previous_track->m_Width = g_DesignSettings.m_CurrentTrackWidth;
}
}
if( Track_45_Only )
{
if( g_TwoSegmentTrackBuild )
{
g_CurrentTrackSegment->m_End = ActiveScreen->m_Curseur;
if( Drc_On )
PushTrack( panel );
ComputeBreakPoint( g_CurrentTrackSegment, g_CurrentTrackList.GetCount(),
g_CurrentTrackSegment->m_End );
}
else
{
/* Calcul de l'extremite de la piste pour orientations permises:
* horiz,vertical ou 45 degre
*/
Calcule_Coord_Extremite_45( g_CurrentTrackSegment->m_Start.x,
g_CurrentTrackSegment->m_Start.y,
&g_CurrentTrackSegment->m_End.x,
&g_CurrentTrackSegment->m_End.y );
}
}
else /* ici l'angle d'inclinaison est quelconque */
{
g_CurrentTrackSegment->m_End = screen->m_Curseur;
}
/* Redraw the new track */
D( g_CurrentTrackList.VerifyListIntegrity(); );
Trace_Une_Piste( panel, DC, g_FirstTrackSegment, g_CurrentTrackList.GetCount(), GR_XOR );
if( showTrackClearanceMode >= SHOW_CLEARANCE_NEW_TRACKS_AND_VIA_AREAS ) // Show the via area
{
int color = g_DesignSettings.m_LayerColor[g_CurrentTrackSegment->GetLayer()];
GRCircle( &panel->m_ClipBox, DC, g_CurrentTrackSegment->m_End.x,
g_CurrentTrackSegment->m_End.y,
(netclass->GetViaDiameter() / 2) + netclass->GetClearance(),
color );
}
/* Display info about currrent segment and the full new track:
* Choose the interesting segment: because we are using a 2 segments step,
* the last segment can be null, and the previous segment can be the interesting segment.
*/
TRACK* isegm = g_CurrentTrackSegment;
if( isegm->GetLength() == 0 && g_CurrentTrackSegment->Back() )
isegm = g_CurrentTrackSegment->Back();
// display interesting segment info only:
isegm->DisplayInfoBase( frame );
// Add current track length
int trackLen = 0;
wxString msg;
for( TRACK* track = g_FirstTrackSegment; track; track = track->Next() )
trackLen += track->GetLength();
valeur_param( trackLen, msg );
frame->AppendMsgPanel( _( "Track Len" ), msg, DARKCYAN );
// Add current segments count (number of segments in this new track):
msg.Printf( wxT( "%d" ), g_CurrentTrackList.GetCount() );
frame->AppendMsgPanel( _( "Segs Count" ), msg, DARKCYAN );
DisplayOpt.ShowTrackClearanceMode = showTrackClearanceMode;
DisplayOpt.DisplayPcbTrackFill = Track_fill_copy;
frame->build_ratsnest_pad( NULL, g_CurrentTrackSegment->m_End, false );
frame->trace_ratsnest_pad( DC );
}
/*****************************************************************/
void Calcule_Coord_Extremite_45( int ox, int oy, int* fx, int* fy )
/*****************************************************************/
/* determine les parametres .fx et .fy du segment pointe par pt_segm
* pour avoir un segment oriente a 0, 90 ou 45 degres, selon position
* du oint d'origine et de la souris
*/
{
int deltax, deltay, angle;
deltax = ActiveScreen->m_Curseur.x - ox;
deltay = ActiveScreen->m_Curseur.y - oy;
/* calcul de l'angle preferentiel : 0, 45 , 90 degre */
deltax = abs( deltax );
deltay = abs( deltay );
angle = 45;
if( deltax >= deltay )
{
if( deltax == 0 )
angle = 0;
else if( ( (deltay << 6 ) / deltax ) < 26 )
angle = 0;
}
else
{
angle = 45;
if( deltay == 0 )
angle = 90;
else if( ( (deltax << 6 ) / deltay ) < 26 )
angle = 90;
}
switch( angle )
{
case 0:
*fx = ActiveScreen->m_Curseur.x;
*fy = oy;
break;
case 45:
deltax = MIN( deltax, deltay );
deltay = deltax;
/* recalcul des signes de deltax et deltay */
if( (ActiveScreen->m_Curseur.x - ox) < 0 )
deltax = -deltax;
if( (ActiveScreen->m_Curseur.y - oy) < 0 )
deltay = -deltay;
*fx = ox + deltax;
*fy = oy + deltay;
break;
case 90:
*fx = ox;
*fy = ActiveScreen->m_Curseur.y;
break;
}
}
/********************************************************/
void ComputeBreakPoint( TRACK* track, int SegmentCount, wxPoint end )
/********************************************************/
/**
* Compute new track angle based on previous track.
*/
{
int iDx = 0;
int iDy = 0;
int iAngle = 0;
if( SegmentCount <= 0 )
return;
if( track == NULL )
return;
TRACK* newTrack = track;
track = track->Back();
SegmentCount--;
if( track )
{
iDx = end.x - track->m_Start.x;
iDy = end.y - track->m_Start.y;
iDx = abs( iDx );
iDy = abs( iDy );
}
TRACK* lastTrack = track ? track->Back() : NULL;
if( lastTrack )
{
if( (lastTrack->m_End.x == lastTrack->m_Start.x)
|| (lastTrack->m_End.y == lastTrack->m_Start.y) )
{
iAngle = 45;
}
}
if( iAngle == 0 )
{
if( iDx >= iDy )
iAngle = 0;
else
iAngle = 90;
}
if( track == NULL )
iAngle = -1;
switch( iAngle )
{
case - 1:
break;
case 0:
if( (end.x - track->m_Start.x) < 0 )
track->m_End.x = end.x + iDy;
else
track->m_End.x = end.x - iDy;
track->m_End.y = track->m_Start.y;
break;
case 45:
iDx = MIN( iDx, iDy );
iDy = iDx;
/* recalcul des signes de deltax et deltay */
if( (end.x - track->m_Start.x) < 0 )
iDx = -iDx;
if( (end.y - track->m_Start.y) < 0 )
iDy = -iDy;
track->m_End.x = track->m_Start.x + iDx;
track->m_End.y = track->m_Start.y + iDy;
break;
case 90:
if( (end.y - track->m_Start.y) < 0 )
track->m_End.y = end.y + iDx;
else
track->m_End.y = end.y - iDx;
track->m_End.x = track->m_Start.x;
break;
}
if( track )
{
if( track->IsNull() )
track->m_End = end;
newTrack->m_Start = track->m_End;
}
newTrack->m_End = end;
}
/****************************************************************************/
void DeleteNullTrackSegments( BOARD* pcb, DLIST<TRACK>& aTrackList )
/****************************************************************************/
/* Delete track segments which have len = 0; after creating a new track
* return a pointer on the first segment (start of track list)
*/
{
if( aTrackList.GetCount() == 0 )
return;
TRACK* track = aTrackList.GetFirst();
TRACK* firsttrack = track;
TRACK* oldtrack;
BOARD_ITEM* LockPoint = track->start;
while( track != NULL )
{
oldtrack = track;
track = track->Next();
if( !oldtrack->IsNull() )
{
continue;
}
// NULL segment, delete it
if( firsttrack == oldtrack )
firsttrack = track;
delete aTrackList.Remove( oldtrack );
}
if( aTrackList.GetCount() == 0 )
return; // all the new track segments have been deleted
// we must set the pointers on connected items and the connection status
oldtrack = track = firsttrack;
firsttrack->start = NULL;
while( track != NULL )
{
oldtrack = track;
track = track->Next();
oldtrack->end = track;
if( track )
track->start = oldtrack;
oldtrack->SetStatus( 0 );
}
firsttrack->start = LockPoint;
if( LockPoint && LockPoint->Type()==TYPE_PAD )
firsttrack->SetState( BEGIN_ONPAD, ON );
track = firsttrack;
while( track != NULL )
{
TRACK* next_track = track->Next();
LockPoint = Locate_Pad_Connecte( pcb, track, END );
if( LockPoint )
{
track->end = LockPoint;
track->SetState( END_ONPAD, ON );
if( next_track )
{
next_track->start = LockPoint;
next_track->SetState( BEGIN_ONPAD, ON );
}
}
track = next_track;
}
}
/************************************/
void EnsureEndTrackOnPad( D_PAD* Pad )
/************************************/
/* Ensure the end point of g_CurrentTrackSegment is on the pas "Pad"
* if no, create a new track segment if necessary
* and move current (or new) end segment on pad
*/
{
if( g_CurrentTrackSegment->m_End == Pad->m_Pos ) // Ok !
{
g_CurrentTrackSegment->end = Pad;
g_CurrentTrackSegment->SetState( END_ONPAD, ON );
return;
}
TRACK* lasttrack = g_CurrentTrackSegment;
if( !g_CurrentTrackSegment->IsNull() )
{
/* Must create a new segment, from track end to pad center */
g_CurrentTrackList.PushBack( lasttrack->Copy() );
lasttrack->end = g_CurrentTrackSegment;
}
g_CurrentTrackSegment->m_End = Pad->m_Pos;
g_CurrentTrackSegment->SetState( END_ONPAD, OFF );
g_CurrentTrackSegment->end = Pad;
g_CurrentTrackSegment->SetState( END_ONPAD, ON );
}