2011-10-17 20:01:27 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2012-06-08 09:56:42 +00:00
|
|
|
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
|
|
|
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors.
|
|
|
|
*
|
|
|
|
* First copyright (C) Randy Nevin, 1989 (see PCBCA package)
|
2011-10-17 20:01:27 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2012-06-08 09:56:42 +00:00
|
|
|
/* see "Autorouting With the A* Algorithm" (Dr.Dobbs journal)
|
|
|
|
*/
|
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
/**
|
|
|
|
* @file solve.cpp
|
|
|
|
*/
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <fctsys.h>
|
|
|
|
#include <class_drawpanel.h>
|
|
|
|
#include <confirm.h>
|
|
|
|
#include <wxPcbStruct.h>
|
|
|
|
#include <gr_basic.h>
|
|
|
|
#include <macros.h>
|
|
|
|
#include <pcbcommon.h>
|
2011-09-23 13:57:12 +00:00
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <class_board.h>
|
|
|
|
#include <class_track.h>
|
2009-02-04 15:25:03 +00:00
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <pcbnew.h>
|
|
|
|
#include <protos.h>
|
2012-06-05 19:07:33 +00:00
|
|
|
#include <autorout.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <cell.h>
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
|
2011-03-01 19:26:17 +00:00
|
|
|
static int Autoroute_One_Track( PCB_EDIT_FRAME* pcbframe,
|
|
|
|
wxDC* DC,
|
|
|
|
int two_sides,
|
|
|
|
int row_source,
|
|
|
|
int col_source,
|
|
|
|
int row_target,
|
|
|
|
int col_target,
|
2011-09-20 13:57:40 +00:00
|
|
|
RATSNEST_ITEM* pt_rat );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2011-03-01 19:26:17 +00:00
|
|
|
static int Retrace( PCB_EDIT_FRAME* pcbframe,
|
|
|
|
wxDC* DC,
|
2009-11-20 14:55:20 +00:00
|
|
|
int,
|
|
|
|
int,
|
|
|
|
int,
|
|
|
|
int,
|
|
|
|
int,
|
|
|
|
int net_code );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
static void OrCell_Trace( BOARD* pcb,
|
|
|
|
int col,
|
|
|
|
int row,
|
|
|
|
int side,
|
|
|
|
int orient,
|
|
|
|
int current_net_code );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
|
|
|
static void AddNewTrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC );
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
|
|
|
|
static int segm_oX, segm_oY;
|
|
|
|
static int segm_fX, segm_fY; /* Origin and position of the current
|
|
|
|
* trace segment. */
|
2009-05-24 18:28:36 +00:00
|
|
|
static RATSNEST_ITEM* pt_cur_ch;
|
2009-11-20 14:55:20 +00:00
|
|
|
static int s_Clearance; // Clearance value used in autorouter
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2011-12-13 20:42:42 +00:00
|
|
|
static PICKED_ITEMS_LIST s_ItemsListPicker;
|
|
|
|
|
2012-06-26 17:57:37 +00:00
|
|
|
int OpenNodes; /* total number of nodes opened */
|
|
|
|
int ClosNodes; /* total number of nodes closed */
|
|
|
|
int MoveNodes; /* total number of nodes moved */
|
|
|
|
int MaxNodes; /* maximum number of nodes opened at one time */
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
#define NOSUCCESS 0
|
|
|
|
#define STOP_FROM_ESC -1
|
|
|
|
#define ERR_MEMORY -2
|
|
|
|
#define SUCCESS 1
|
2007-06-05 12:10:51 +00:00
|
|
|
#define TRIVIAL_SUCCESS 2
|
|
|
|
|
|
|
|
/*
|
|
|
|
** visit neighboring cells like this (where [9] is on the other side):
|
|
|
|
**
|
2011-09-15 17:58:35 +00:00
|
|
|
** +---+---+---+
|
|
|
|
** | 1 | 2 | 3 |
|
|
|
|
** +---+---+---+
|
|
|
|
** | 4 |[9]| 5 |
|
|
|
|
** +---+---+---+
|
|
|
|
** | 6 | 7 | 8 |
|
|
|
|
** +---+---+---+
|
2007-06-05 12:10:51 +00:00
|
|
|
*/
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* for visiting neighbors on the same side: increments/decrements coord of
|
|
|
|
* [] [0] = row [] (1] = col was added to the coord of the midpoint for
|
|
|
|
* Get the coord of the 8 neighboring points.
|
|
|
|
*/
|
2012-06-05 19:07:33 +00:00
|
|
|
static const int delta[8][2] =
|
2009-11-20 14:55:20 +00:00
|
|
|
{
|
2011-09-15 17:58:35 +00:00
|
|
|
{ 1, -1 }, /* northwest */
|
|
|
|
{ 1, 0 }, /* north */
|
|
|
|
{ 1, 1 }, /* northeast */
|
|
|
|
{ 0, -1 }, /* west */
|
|
|
|
{ 0, 1 }, /* east */
|
|
|
|
{ -1, -1 }, /* southwest */
|
|
|
|
{ -1, 0 }, /* south */
|
|
|
|
{ -1, 1 } /* southeast */
|
2007-08-23 04:28:46 +00:00
|
|
|
};
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2012-06-05 19:07:33 +00:00
|
|
|
static const int ndir[8] =
|
2009-11-20 14:55:20 +00:00
|
|
|
{
|
|
|
|
/* for building paths back to source */
|
2009-01-25 17:38:30 +00:00
|
|
|
FROM_SOUTHEAST, FROM_SOUTH, FROM_SOUTHWEST,
|
2007-08-23 04:28:46 +00:00
|
|
|
FROM_EAST, FROM_WEST,
|
2009-01-25 17:38:30 +00:00
|
|
|
FROM_NORTHEAST, FROM_NORTH, FROM_NORTHWEST
|
2007-08-23 04:28:46 +00:00
|
|
|
};
|
2007-06-05 12:10:51 +00:00
|
|
|
|
|
|
|
/* blocking masks for neighboring cells */
|
2008-04-30 11:52:34 +00:00
|
|
|
#define BLOCK_NORTHEAST ( DIAG_NEtoSW | BENT_StoNE | BENT_WtoNE \
|
2009-11-20 14:55:20 +00:00
|
|
|
| ANGLE_NEtoSE | ANGLE_NWtoNE \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_NtoNE | SHARP_EtoNE | HOLE )
|
|
|
|
#define BLOCK_SOUTHEAST ( DIAG_SEtoNW | BENT_NtoSE | BENT_WtoSE \
|
2009-11-20 14:55:20 +00:00
|
|
|
| ANGLE_NEtoSE | ANGLE_SEtoSW \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_EtoSE | SHARP_StoSE | HOLE )
|
|
|
|
#define BLOCK_SOUTHWEST ( DIAG_NEtoSW | BENT_NtoSW | BENT_EtoSW \
|
2009-11-20 14:55:20 +00:00
|
|
|
| ANGLE_SEtoSW | ANGLE_SWtoNW \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_StoSW | SHARP_WtoSW | HOLE )
|
|
|
|
#define BLOCK_NORTHWEST ( DIAG_SEtoNW | BENT_EtoNW | BENT_StoNW \
|
2009-11-20 14:55:20 +00:00
|
|
|
| ANGLE_SWtoNW | ANGLE_NWtoNE \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_WtoNW | SHARP_NtoNW | HOLE )
|
2009-11-20 14:55:20 +00:00
|
|
|
#define BLOCK_NORTH ( LINE_VERTICAL | BENT_NtoSE | BENT_NtoSW \
|
|
|
|
| BENT_EtoNW | BENT_WtoNE \
|
|
|
|
| BENT_StoNE | BENT_StoNW \
|
|
|
|
| CORNER_NORTHEAST | CORNER_NORTHWEST \
|
2008-04-30 11:52:34 +00:00
|
|
|
| ANGLE_NEtoSE | ANGLE_SWtoNW | ANGLE_NWtoNE \
|
2009-11-20 14:55:20 +00:00
|
|
|
| DIAG_NEtoSW | DIAG_SEtoNW \
|
|
|
|
| SHARP_NtoNE | SHARP_NtoNW \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_EtoNE | SHARP_WtoNW | HOLE )
|
2009-11-20 14:55:20 +00:00
|
|
|
#define BLOCK_EAST ( LINE_HORIZONTAL | BENT_EtoSW | BENT_EtoNW \
|
|
|
|
| BENT_NtoSE | BENT_StoNE \
|
|
|
|
| BENT_WtoNE | BENT_WtoSE \
|
|
|
|
| CORNER_NORTHEAST | CORNER_SOUTHEAST \
|
2008-04-30 11:52:34 +00:00
|
|
|
| ANGLE_NEtoSE | ANGLE_SEtoSW | ANGLE_NWtoNE \
|
2009-11-20 14:55:20 +00:00
|
|
|
| DIAG_NEtoSW | DIAG_SEtoNW \
|
|
|
|
| SHARP_EtoNE | SHARP_EtoSE \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_NtoNE | SHARP_StoSE | HOLE )
|
2009-11-20 14:55:20 +00:00
|
|
|
#define BLOCK_SOUTH ( LINE_VERTICAL | BENT_StoNE | BENT_StoNW \
|
|
|
|
| BENT_EtoSW | BENT_WtoSE \
|
|
|
|
| BENT_NtoSE | BENT_NtoSW \
|
|
|
|
| CORNER_SOUTHEAST | CORNER_SOUTHWEST \
|
2008-04-30 11:52:34 +00:00
|
|
|
| ANGLE_NEtoSE | ANGLE_SWtoNW | ANGLE_SEtoSW \
|
2009-11-20 14:55:20 +00:00
|
|
|
| DIAG_NEtoSW | DIAG_SEtoNW \
|
|
|
|
| SHARP_StoSE | SHARP_StoSW \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_EtoSE | SHARP_WtoSW | HOLE )
|
2009-11-20 14:55:20 +00:00
|
|
|
#define BLOCK_WEST ( LINE_HORIZONTAL | BENT_WtoNE | BENT_WtoSE \
|
|
|
|
| BENT_NtoSW | BENT_StoNW \
|
|
|
|
| BENT_EtoSW | BENT_EtoNW \
|
|
|
|
| CORNER_SOUTHWEST | CORNER_NORTHWEST \
|
2008-04-30 11:52:34 +00:00
|
|
|
| ANGLE_SWtoNW | ANGLE_SEtoSW | ANGLE_NWtoNE \
|
2009-11-20 14:55:20 +00:00
|
|
|
| DIAG_NEtoSW | DIAG_SEtoNW \
|
|
|
|
| SHARP_WtoSW | SHARP_WtoNW \
|
2008-04-30 11:52:34 +00:00
|
|
|
| SHARP_NtoNW | SHARP_StoSW | HOLE )
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
struct block
|
|
|
|
{
|
|
|
|
int r1, c1;
|
|
|
|
long b1;
|
|
|
|
int r2, c2;
|
|
|
|
long b2;
|
|
|
|
};
|
|
|
|
|
2008-04-30 11:52:34 +00:00
|
|
|
/* blocking masks for diagonal traces */
|
|
|
|
static struct block blocking[8] =
|
|
|
|
{ {
|
|
|
|
0, -1,
|
|
|
|
BLOCK_NORTHEAST,
|
|
|
|
1, 0,
|
|
|
|
BLOCK_SOUTHWEST
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, 0, 0,
|
|
|
|
0, 0, 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
1, 0,
|
|
|
|
BLOCK_SOUTHEAST,
|
|
|
|
0, 1,
|
|
|
|
BLOCK_NORTHWEST
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, 0, 0,
|
|
|
|
0, 0, 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, 0, 0,
|
|
|
|
0, 0, 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, -1,
|
|
|
|
BLOCK_SOUTHEAST,
|
|
|
|
-1, 0,
|
|
|
|
BLOCK_NORTHWEST
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0, 0, 0,
|
|
|
|
0, 0, 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
-1, 0,
|
|
|
|
BLOCK_NORTHEAST,
|
|
|
|
0, 1,
|
|
|
|
BLOCK_SOUTHWEST
|
|
|
|
} };
|
2007-06-05 12:10:51 +00:00
|
|
|
|
|
|
|
/* mask for hole-related blocking effects */
|
2007-08-23 04:28:46 +00:00
|
|
|
static struct
|
|
|
|
{
|
|
|
|
long trace;
|
|
|
|
int present;
|
2009-11-20 14:55:20 +00:00
|
|
|
} selfok2[8] =
|
|
|
|
{
|
|
|
|
{ HOLE_NORTHWEST, 0 },
|
|
|
|
{ HOLE_NORTH, 0 },
|
|
|
|
{ HOLE_NORTHEAST, 0 },
|
|
|
|
{ HOLE_WEST, 0 },
|
|
|
|
{ HOLE_EAST, 0 },
|
|
|
|
{ HOLE_SOUTHWEST, 0 },
|
|
|
|
{ HOLE_SOUTH, 0 },
|
|
|
|
{ HOLE_SOUTHEAST, 0 }
|
2007-08-23 04:28:46 +00:00
|
|
|
};
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
static long newmask[8] =
|
|
|
|
{
|
|
|
|
/* patterns to mask out in neighbor cells */
|
|
|
|
0,
|
|
|
|
CORNER_NORTHWEST | CORNER_NORTHEAST,
|
2009-01-25 17:38:30 +00:00
|
|
|
0,
|
2009-11-20 14:55:20 +00:00
|
|
|
CORNER_NORTHWEST | CORNER_SOUTHWEST,
|
|
|
|
CORNER_NORTHEAST | CORNER_SOUTHEAST,
|
|
|
|
0,
|
|
|
|
CORNER_SOUTHWEST | CORNER_SOUTHEAST,
|
2009-01-25 17:38:30 +00:00
|
|
|
0
|
2007-08-23 04:28:46 +00:00
|
|
|
};
|
2007-06-05 12:10:51 +00:00
|
|
|
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Route all traces
|
* KIWAY Milestone A): Make major modules into DLL/DSOs.
! The initial testing of this commit should be done using a Debug build so that
all the wxASSERT()s are enabled. Also, be sure and keep enabled the
USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it
off is senseless anyways. If you want stable code, go back to a prior version,
the one tagged with "stable".
* Relocate all functionality out of the wxApp derivative into more finely
targeted purposes:
a) DLL/DSO specific
b) PROJECT specific
c) EXE or process specific
d) configuration file specific data
e) configuration file manipulations functions.
All of this functionality was blended into an extremely large wxApp derivative
and that was incompatible with the desire to support multiple concurrently
loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects.
An amazing amount of organization come from simply sorting each bit of
functionality into the proper box.
* Switch to wxConfigBase from wxConfig everywhere except instantiation.
* Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD,
PGM_SINGLE_TOP,
* Remove "Return" prefix on many function names.
* Remove obvious comments from CMakeLists.txt files, and from else() and endif()s.
* Fix building boost for use in a DSO on linux.
* Remove some of the assumptions in the CMakeLists.txt files that windows had
to be the host platform when building windows binaries.
* Reduce the number of wxStrings being constructed at program load time via
static construction.
* Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that
these functions are useful even when the wxConfigBase comes from another
source, as is the case in the KICAD_MANAGER_FRAME.
* Move the setting of the KIPRJMOD environment variable into class PROJECT,
so that it can be moved into a project variable soon, and out of FP_LIB_TABLE.
* Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all
its child wxFrames and wxDialogs now have a Kiway() member function which
returns a KIWAY& that that window tree branch is in support of. This is like
wxWindows DNA in that child windows get this member with proper value at time
of construction.
* Anticipate some of the needs for milestones B) and C) and make code
adjustments now in an effort to reduce work in those milestones.
* No testing has been done for python scripting, since milestone C) has that
being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
|
|
|
* :
|
2009-11-20 14:55:20 +00:00
|
|
|
* 1 if OK
|
|
|
|
* -1 if escape (stop being routed) request
|
|
|
|
* -2 if default memory allocation
|
2007-08-23 04:28:46 +00:00
|
|
|
*/
|
2012-06-26 17:57:37 +00:00
|
|
|
int PCB_EDIT_FRAME::Solve( wxDC* DC, int aLayersCount )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
int current_net_code;
|
|
|
|
int row_source, col_source, row_target, col_target;
|
|
|
|
int success, nbsucces = 0, nbunsucces = 0;
|
2009-05-24 18:28:36 +00:00
|
|
|
NETINFO_ITEM* net;
|
2011-04-12 14:19:59 +00:00
|
|
|
bool stop = false;
|
2009-11-20 14:55:20 +00:00
|
|
|
wxString msg;
|
2012-06-10 16:21:18 +00:00
|
|
|
int routedCount = 0; // routed ratsnest count
|
2012-06-26 17:57:37 +00:00
|
|
|
bool two_sides = aLayersCount == 2;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2011-12-29 20:11:42 +00:00
|
|
|
m_canvas->SetAbortRequest( false );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-10-21 19:16:25 +00:00
|
|
|
s_Clearance = GetBoard()->m_NetClasses.GetDefault()->GetClearance();
|
|
|
|
|
2011-12-13 20:42:42 +00:00
|
|
|
// Prepare the undo command info
|
|
|
|
s_ItemsListPicker.ClearListAndDeleteItems(); // Should not be necessary, but...
|
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
/* go until no more work to do */
|
|
|
|
GetWork( &row_source, &col_source, ¤t_net_code,
|
2009-11-20 14:55:20 +00:00
|
|
|
&row_target, &col_target, &pt_cur_ch ); // First net to route.
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source,
|
2009-11-20 14:55:20 +00:00
|
|
|
¤t_net_code, &row_target,
|
|
|
|
&col_target,
|
2009-01-25 17:38:30 +00:00
|
|
|
&pt_cur_ch ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Test to stop routing ( escape key pressed ) */
|
2007-08-23 04:28:46 +00:00
|
|
|
wxYield();
|
2011-04-12 14:19:59 +00:00
|
|
|
|
2011-12-29 20:11:42 +00:00
|
|
|
if( m_canvas->GetAbortRequest() )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
if( IsOK( this, _( "Abort routing?" ) ) )
|
|
|
|
{
|
|
|
|
success = STOP_FROM_ESC;
|
2011-03-09 14:30:39 +00:00
|
|
|
stop = true;
|
2007-08-23 04:28:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
2011-09-07 19:41:04 +00:00
|
|
|
{
|
2011-12-29 20:11:42 +00:00
|
|
|
m_canvas->SetAbortRequest( false );
|
2011-09-07 19:41:04 +00:00
|
|
|
}
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2011-04-12 14:19:59 +00:00
|
|
|
EraseMsgBox();
|
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
routedCount++;
|
2009-05-24 18:28:36 +00:00
|
|
|
net = GetBoard()->FindNet( current_net_code );
|
2011-04-12 14:19:59 +00:00
|
|
|
|
2009-05-24 18:28:36 +00:00
|
|
|
if( net )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2009-10-14 18:14:58 +00:00
|
|
|
msg.Printf( wxT( "[%8.8s]" ), GetChars( net->GetNetname() ) );
|
2011-04-12 14:19:59 +00:00
|
|
|
AppendMsgPanel( wxT( "Net route" ), msg, BROWN );
|
2012-06-10 16:21:18 +00:00
|
|
|
msg.Printf( wxT( "%d / %d" ), routedCount, RoutingMatrix.m_RouteCount );
|
2011-04-12 14:19:59 +00:00
|
|
|
AppendMsgPanel( wxT( "Activity" ), msg, BROWN );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2012-06-05 19:07:33 +00:00
|
|
|
segm_oX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_source);
|
|
|
|
segm_oY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_source);
|
|
|
|
segm_fX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_target);
|
|
|
|
segm_fY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_target);
|
2009-11-20 14:55:20 +00:00
|
|
|
|
|
|
|
/* Draw segment. */
|
2012-06-10 16:21:18 +00:00
|
|
|
GRLine( m_canvas->GetClipBox(), DC,
|
|
|
|
segm_oX, segm_oY, segm_fX, segm_fY,
|
2012-09-02 12:06:47 +00:00
|
|
|
0, WHITE );
|
2011-12-22 13:28:11 +00:00
|
|
|
pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );
|
|
|
|
pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
success = Autoroute_One_Track( this, DC,
|
|
|
|
two_sides, row_source, col_source,
|
|
|
|
row_target, col_target, pt_cur_ch );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
switch( success )
|
|
|
|
{
|
|
|
|
case NOSUCCESS:
|
2009-05-24 18:28:36 +00:00
|
|
|
pt_cur_ch->m_Status |= CH_UNROUTABLE;
|
2007-08-23 04:28:46 +00:00
|
|
|
nbunsucces++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STOP_FROM_ESC:
|
2011-03-09 14:30:39 +00:00
|
|
|
stop = true;
|
2007-08-23 04:28:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ERR_MEMORY:
|
2011-03-09 14:30:39 +00:00
|
|
|
stop = true;
|
2007-08-23 04:28:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
nbsucces++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-04-12 14:19:59 +00:00
|
|
|
msg.Printf( wxT( "%d" ), nbsucces );
|
2013-04-09 17:49:01 +00:00
|
|
|
AppendMsgPanel( wxT( "OK" ), msg, GREEN );
|
2011-04-12 14:19:59 +00:00
|
|
|
msg.Printf( wxT( "%d" ), nbunsucces );
|
|
|
|
AppendMsgPanel( wxT( "Fail" ), msg, RED );
|
2013-01-09 18:52:44 +00:00
|
|
|
msg.Printf( wxT( " %d" ), GetBoard()->GetUnconnectedNetCount() );
|
2011-09-15 17:58:35 +00:00
|
|
|
AppendMsgPanel( wxT( "Not Connected" ), msg, CYAN );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Delete routing from display. */
|
2011-12-22 13:28:11 +00:00
|
|
|
pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_AND );
|
|
|
|
pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_AND );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
if( stop )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-12-13 20:42:42 +00:00
|
|
|
SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
|
|
|
|
s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items
|
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
return SUCCESS;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Route a trace on the BOARD.
|
|
|
|
* Parameters:
|
|
|
|
* 1 side / 2 sides (0 / 1)
|
|
|
|
* Coord source (row, col)
|
|
|
|
* Coord destination (row, col)
|
|
|
|
* Net_code
|
|
|
|
* Pointer to the ratsnest reference
|
2008-02-12 01:02:53 +00:00
|
|
|
*
|
2009-11-20 14:55:20 +00:00
|
|
|
* Returns:
|
|
|
|
* SUCCESS if routed
|
2011-03-09 14:30:39 +00:00
|
|
|
* TRIVIAL_SUCCESS if pads are connected by overlay (no track needed)
|
2009-11-20 14:55:20 +00:00
|
|
|
* If failure NOSUCCESS
|
|
|
|
* Escape STOP_FROM_ESC if demand
|
|
|
|
* ERR_MEMORY if memory allocation failed.
|
2007-08-23 04:28:46 +00:00
|
|
|
*/
|
2011-03-01 19:26:17 +00:00
|
|
|
static int Autoroute_One_Track( PCB_EDIT_FRAME* pcbframe,
|
|
|
|
wxDC* DC,
|
|
|
|
int two_sides,
|
|
|
|
int row_source,
|
|
|
|
int col_source,
|
|
|
|
int row_target,
|
|
|
|
int col_target,
|
2011-09-20 13:57:40 +00:00
|
|
|
RATSNEST_ITEM* pt_rat )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
int r, c, side, d, apx_dist, nr, nc;
|
|
|
|
int result, skip;
|
|
|
|
int i;
|
|
|
|
long curcell, newcell, buddy, lastopen, lastclos, lastmove;
|
|
|
|
int newdist, olddir, _self;
|
|
|
|
int current_net_code;
|
2011-05-13 13:15:28 +00:00
|
|
|
int marge;
|
2011-09-07 19:41:04 +00:00
|
|
|
int padLayerMaskStart; /* Mask layers belonging to the starting pad. */
|
|
|
|
int padLayerMaskEnd; /* Mask layers belonging to the ending pad. */
|
2013-03-27 18:32:12 +00:00
|
|
|
int topLayerMask = GetLayerMask( g_Route_Layer_TOP );
|
|
|
|
int bottomLayerMask = GetLayerMask( g_Route_Layer_BOTTOM );
|
2011-09-07 19:41:04 +00:00
|
|
|
int routeLayerMask; /* Mask two layers for routing. */
|
|
|
|
int tab_mask[2]; /* Enables the calculation of the mask layer being
|
2009-11-20 14:55:20 +00:00
|
|
|
* tested. (side = TOP or BOTTOM) */
|
|
|
|
int start_mask_layer = 0;
|
|
|
|
wxString msg;
|
|
|
|
|
|
|
|
wxBusyCursor dummy_cursor; // Set an hourglass cursor while routing a
|
|
|
|
// track
|
2009-01-25 17:48:59 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
result = NOSUCCESS;
|
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
marge = s_Clearance + ( pcbframe->GetBoard()->GetCurrentTrackWidth() / 2 );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
/* clear direction flags */
|
2012-06-10 16:21:18 +00:00
|
|
|
i = RoutingMatrix.m_Nrows * RoutingMatrix.m_Ncols * sizeof(DIR_CELL);
|
2013-03-06 09:04:27 +00:00
|
|
|
|
|
|
|
if( two_sides )
|
|
|
|
memset( RoutingMatrix.m_DirSide[TOP], FROM_NOWHERE, i );
|
2012-06-05 19:07:33 +00:00
|
|
|
memset( RoutingMatrix.m_DirSide[BOTTOM], FROM_NOWHERE, i );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
lastopen = lastclos = lastmove = 0;
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Set tab_masque[side] for final test of routing. */
|
2013-03-06 09:04:27 +00:00
|
|
|
if( two_sides )
|
|
|
|
tab_mask[TOP] = topLayerMask;
|
2011-09-07 19:41:04 +00:00
|
|
|
tab_mask[BOTTOM] = bottomLayerMask;
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Set active layers mask. */
|
2011-09-07 19:41:04 +00:00
|
|
|
routeLayerMask = topLayerMask | bottomLayerMask;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
pt_cur_ch = pt_rat;
|
2012-02-19 04:02:19 +00:00
|
|
|
|
|
|
|
current_net_code = pt_rat->GetNet();
|
|
|
|
padLayerMaskStart = pt_cur_ch->m_PadStart->GetLayerMask();
|
|
|
|
|
|
|
|
padLayerMaskEnd = pt_cur_ch->m_PadEnd->GetLayerMask();
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* First Test if routing possible ie if the pads are accessible
|
|
|
|
* on the routing layers.
|
|
|
|
*/
|
2011-09-07 19:41:04 +00:00
|
|
|
if( ( routeLayerMask & padLayerMaskStart ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
goto end_of_route;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
|
|
|
if( ( routeLayerMask & padLayerMaskEnd ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
goto end_of_route;
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Then test if routing possible ie if the pads are accessible
|
|
|
|
* On the routing grid (1 grid point must be in the pad)
|
|
|
|
*/
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-05 19:07:33 +00:00
|
|
|
int cX = ( RoutingMatrix.m_GridRouting * col_source )
|
2011-12-21 13:42:02 +00:00
|
|
|
+ pcbframe->GetBoard()->GetBoundingBox().GetX();
|
2012-06-05 19:07:33 +00:00
|
|
|
int cY = ( RoutingMatrix.m_GridRouting * row_source )
|
2011-12-21 13:42:02 +00:00
|
|
|
+ pcbframe->GetBoard()->GetBoundingBox().GetY();
|
2012-02-19 04:02:19 +00:00
|
|
|
int dx = pt_cur_ch->m_PadStart->GetSize().x / 2;
|
|
|
|
int dy = pt_cur_ch->m_PadStart->GetSize().y / 2;
|
2009-05-24 18:28:36 +00:00
|
|
|
int px = pt_cur_ch->m_PadStart->GetPosition().x;
|
|
|
|
int py = pt_cur_ch->m_PadStart->GetPosition().y;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
if( ( ( int( pt_cur_ch->m_PadStart->GetOrientation() ) / 900 ) & 1 ) != 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
EXCHG( dx, dy );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( ( abs( cX - px ) > dx ) || ( abs( cY - py ) > dy ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
goto end_of_route;
|
|
|
|
|
2012-06-05 19:07:33 +00:00
|
|
|
cX = ( RoutingMatrix.m_GridRouting * col_target )
|
2011-12-21 13:42:02 +00:00
|
|
|
+ pcbframe->GetBoard()->GetBoundingBox().GetX();
|
2012-06-05 19:07:33 +00:00
|
|
|
cY = ( RoutingMatrix.m_GridRouting * row_target )
|
2011-12-21 13:42:02 +00:00
|
|
|
+ pcbframe->GetBoard()->GetBoundingBox().GetY();
|
2012-02-19 04:02:19 +00:00
|
|
|
dx = pt_cur_ch->m_PadEnd->GetSize().x / 2;
|
|
|
|
dy = pt_cur_ch->m_PadEnd->GetSize().y / 2;
|
2009-05-24 18:28:36 +00:00
|
|
|
px = pt_cur_ch->m_PadEnd->GetPosition().x;
|
|
|
|
py = pt_cur_ch->m_PadEnd->GetPosition().y;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
if( ( ( int( pt_cur_ch->m_PadEnd->GetOrientation() ) / 900) & 1 ) != 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
EXCHG( dx, dy );
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( ( abs( cX - px ) > dx ) || ( abs( cY - py ) > dy ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
goto end_of_route;
|
|
|
|
}
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Test the trivial case: direct connection overlay pads. */
|
|
|
|
if( ( row_source == row_target ) && ( col_source == col_target )
|
2011-09-07 19:41:04 +00:00
|
|
|
&& ( padLayerMaskEnd & padLayerMaskStart &
|
2010-01-31 20:01:46 +00:00
|
|
|
g_TabAllCopperLayerMask[pcbframe->GetBoard()->GetCopperLayerCount() - 1] ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
result = TRIVIAL_SUCCESS;
|
|
|
|
goto end_of_route;
|
|
|
|
}
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Placing the bit to remove obstacles on 2 pads to a link. */
|
2011-02-11 20:48:13 +00:00
|
|
|
pcbframe->SetStatusText( wxT( "Gen Cells" ) );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2012-06-17 16:06:12 +00:00
|
|
|
PlacePad( pt_cur_ch->m_PadStart, CURRENT_PAD, marge, WRITE_OR_CELL );
|
|
|
|
PlacePad( pt_cur_ch->m_PadEnd, CURRENT_PAD, marge, WRITE_OR_CELL );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2011-09-20 13:57:40 +00:00
|
|
|
/* Regenerates the remaining barriers (which may encroach on the placement bits precedent)
|
2009-11-20 14:55:20 +00:00
|
|
|
*/
|
2011-12-10 05:33:24 +00:00
|
|
|
i = pcbframe->GetBoard()->GetPadCount();
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2011-12-10 05:33:24 +00:00
|
|
|
for( unsigned ii = 0; ii < pcbframe->GetBoard()->GetPadCount(); ii++ )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2011-12-10 05:33:24 +00:00
|
|
|
D_PAD* ptr = pcbframe->GetBoard()->GetPad( ii );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( ( pt_cur_ch->m_PadStart != ptr ) && ( pt_cur_ch->m_PadEnd != ptr ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-17 16:06:12 +00:00
|
|
|
PlacePad( ptr, ~CURRENT_PAD, marge, WRITE_AND_CELL );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
InitQueue(); /* initialize the search queue */
|
2012-06-26 17:57:37 +00:00
|
|
|
apx_dist = RoutingMatrix.GetApxDist( row_source, col_source, row_target, col_target );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Initialize first search. */
|
|
|
|
if( two_sides ) /* Preferred orientation. */
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
if( abs( row_target - row_source ) > abs( col_target - col_source ) )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
if( padLayerMaskStart & topLayerMask )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
start_mask_layer = 2;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
|
|
|
if( padLayerMaskStart & bottomLayerMask )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
start_mask_layer |= 1;
|
|
|
|
|
|
|
|
if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
if( padLayerMaskStart & bottomLayerMask )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
start_mask_layer = 1;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
|
|
|
if( padLayerMaskStart & topLayerMask )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
start_mask_layer |= 2;
|
|
|
|
|
|
|
|
if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
else if( padLayerMaskStart & bottomLayerMask )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
start_mask_layer = 1;
|
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist, row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* search until success or we exhaust all possibilities */
|
|
|
|
GetQueue( &r, &c, &side, &d, &apx_dist );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
for( ; r != ILLEGAL; GetQueue( &r, &c, &side, &d, &apx_dist ) )
|
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
curcell = RoutingMatrix.GetCell( r, c, side );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( curcell & CURRENT_PAD )
|
|
|
|
curcell &= ~HOLE;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( (r == row_target) && (c == col_target) /* success if layer OK */
|
2011-09-07 19:41:04 +00:00
|
|
|
&& ( tab_mask[side] & padLayerMaskEnd) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Remove link. */
|
2007-08-23 04:28:46 +00:00
|
|
|
GRSetDrawMode( DC, GR_XOR );
|
2011-12-29 20:11:42 +00:00
|
|
|
GRLine( pcbframe->GetCanvas()->GetClipBox(),
|
2009-01-25 17:38:30 +00:00
|
|
|
DC,
|
|
|
|
segm_oX,
|
|
|
|
segm_oY,
|
|
|
|
segm_fX,
|
|
|
|
segm_fY,
|
|
|
|
0,
|
|
|
|
WHITE );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Generate trace. */
|
2007-08-23 04:28:46 +00:00
|
|
|
if( Retrace( pcbframe, DC, row_source, col_source,
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target, side, current_net_code ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
result = SUCCESS; /* Success : Route OK */
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
break; /* Routing complete. */
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2011-12-29 20:11:42 +00:00
|
|
|
if( pcbframe->GetCanvas()->GetAbortRequest() )
|
2008-04-30 11:52:34 +00:00
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
result = STOP_FROM_ESC;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2009-09-10 15:22:26 +00:00
|
|
|
/* report every COUNT new nodes or so */
|
2009-01-28 17:08:35 +00:00
|
|
|
#define COUNT 20000
|
2011-12-29 20:11:42 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( ( OpenNodes - lastopen > COUNT )
|
|
|
|
|| ( ClosNodes - lastclos > COUNT )
|
|
|
|
|| ( MoveNodes - lastmove > COUNT ) )
|
2009-09-10 15:22:26 +00:00
|
|
|
{
|
|
|
|
lastopen = OpenNodes;
|
2009-01-28 17:08:35 +00:00
|
|
|
lastclos = ClosNodes;
|
2009-09-10 15:22:26 +00:00
|
|
|
lastmove = MoveNodes;
|
2009-11-20 14:55:20 +00:00
|
|
|
msg.Printf( wxT( "Activity: Open %d Closed %d Moved %d" ),
|
|
|
|
OpenNodes, ClosNodes, MoveNodes );
|
2011-02-11 20:48:13 +00:00
|
|
|
pcbframe->SetStatusText( msg );
|
2009-09-10 15:22:26 +00:00
|
|
|
}
|
2009-01-28 17:08:35 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
_self = 0;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( curcell & HOLE )
|
|
|
|
{
|
|
|
|
_self = 5;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
/* set 'present' bits */
|
|
|
|
for( i = 0; i < 8; i++ )
|
|
|
|
{
|
|
|
|
selfok2[i].present = 0;
|
2011-09-20 13:57:40 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( curcell & selfok2[i].trace )
|
2007-08-23 04:28:46 +00:00
|
|
|
selfok2[i].present = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = 0; i < 8; i++ ) /* consider neighbors */
|
|
|
|
{
|
2011-09-20 13:57:40 +00:00
|
|
|
nr = r + delta[i][0];
|
|
|
|
nc = c + delta[i][1];
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
/* off the edge? */
|
2012-06-10 16:21:18 +00:00
|
|
|
if( nr < 0 || nr >= RoutingMatrix.m_Nrows ||
|
|
|
|
nc < 0 || nc >= RoutingMatrix.m_Ncols )
|
2009-11-20 14:55:20 +00:00
|
|
|
continue; /* off the edge */
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
if( _self == 5 && selfok2[i].present )
|
|
|
|
continue;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
newcell = RoutingMatrix.GetCell( nr, nc, side );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( newcell & CURRENT_PAD )
|
|
|
|
newcell &= ~HOLE;
|
|
|
|
|
|
|
|
/* check for non-target hole */
|
|
|
|
if( newcell & HOLE )
|
|
|
|
{
|
|
|
|
if( nr != row_target || nc != col_target )
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* check for traces */
|
|
|
|
else if( newcell & HOLE & ~(newmask[i]) )
|
2011-09-07 19:41:04 +00:00
|
|
|
{
|
2007-08-23 04:28:46 +00:00
|
|
|
continue;
|
2011-09-07 19:41:04 +00:00
|
|
|
}
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
/* check blocking on corner neighbors */
|
|
|
|
if( delta[i][0] && delta[i][1] )
|
|
|
|
{
|
|
|
|
/* check first buddy */
|
2012-06-10 16:21:18 +00:00
|
|
|
buddy = RoutingMatrix.GetCell( r + blocking[i].r1, c + blocking[i].c1, side );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( buddy & CURRENT_PAD )
|
|
|
|
buddy &= ~HOLE;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( buddy & HOLE )
|
|
|
|
continue;
|
|
|
|
|
2011-09-15 17:58:35 +00:00
|
|
|
// if (buddy & (blocking[i].b1)) continue;
|
2007-08-23 04:28:46 +00:00
|
|
|
/* check second buddy */
|
2012-06-10 16:21:18 +00:00
|
|
|
buddy = RoutingMatrix.GetCell( r + blocking[i].r2, c + blocking[i].c2, side );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( buddy & CURRENT_PAD )
|
|
|
|
buddy &= ~HOLE;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( buddy & HOLE )
|
|
|
|
continue;
|
|
|
|
|
2011-09-15 17:58:35 +00:00
|
|
|
// if (buddy & (blocking[i].b2)) continue;
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
olddir = RoutingMatrix.GetDir( r, c, side );
|
2012-06-26 17:57:37 +00:00
|
|
|
newdist = d + RoutingMatrix.CalcDist( ndir[i], olddir,
|
2009-11-20 14:55:20 +00:00
|
|
|
( olddir == FROM_OTHERSIDE ) ?
|
2012-06-10 16:21:18 +00:00
|
|
|
RoutingMatrix.GetDir( r, c, 1 - side ) : 0, side );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
/* if (a) not visited yet, or (b) we have */
|
|
|
|
/* found a better path, add it to queue */
|
2012-06-10 16:21:18 +00:00
|
|
|
if( !RoutingMatrix.GetDir( nr, nc, side ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
RoutingMatrix.SetDir( nr, nc, side, ndir[i] );
|
|
|
|
RoutingMatrix.SetDist( nr, nc, side, newdist );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( SetQueue( nr, nc, side, newdist,
|
2012-06-26 17:57:37 +00:00
|
|
|
RoutingMatrix.GetApxDist( nr, nc, row_target, col_target ),
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
2012-06-10 16:21:18 +00:00
|
|
|
else if( newdist < RoutingMatrix.GetDist( nr, nc, side ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
RoutingMatrix.SetDir( nr, nc, side, ndir[i] );
|
|
|
|
RoutingMatrix.SetDist( nr, nc, side, newdist );
|
2007-08-23 04:28:46 +00:00
|
|
|
ReSetQueue( nr, nc, side, newdist,
|
2012-06-26 17:57:37 +00:00
|
|
|
RoutingMatrix.GetApxDist( nr, nc, row_target, col_target ),
|
2009-01-25 17:38:30 +00:00
|
|
|
row_target, col_target );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/** Test the other layer. **/
|
2010-02-24 15:33:03 +00:00
|
|
|
if( two_sides )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
olddir = RoutingMatrix.GetDir( r, c, side );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( olddir == FROM_OTHERSIDE )
|
|
|
|
continue; /* useless move, so don't bother */
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( curcell ) /* can't drill via if anything here */
|
|
|
|
continue;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
/* check for holes or traces on other side */
|
2012-06-10 16:21:18 +00:00
|
|
|
if( ( newcell = RoutingMatrix.GetCell( r, c, 1 - side ) ) != 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
continue;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
/* check for nearby holes or traces on both sides */
|
|
|
|
for( skip = 0, i = 0; i < 8; i++ )
|
|
|
|
{
|
|
|
|
nr = r + delta[i][0]; nc = c + delta[i][1];
|
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
if( nr < 0 || nr >= RoutingMatrix.m_Nrows ||
|
|
|
|
nc < 0 || nc >= RoutingMatrix.m_Ncols )
|
2009-11-20 14:55:20 +00:00
|
|
|
continue; /* off the edge !! */
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
if( RoutingMatrix.GetCell( nr, nc, side ) /* & blocking2[i]*/ )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
skip = 1; /* can't drill via here */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-06-10 16:21:18 +00:00
|
|
|
if( RoutingMatrix.GetCell( nr, nc, 1 - side ) /* & blocking2[i]*/ )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
skip = 1; /* can't drill via here */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( skip ) /* neighboring hole or trace? */
|
|
|
|
continue; /* yes, can't drill via here */
|
|
|
|
|
2012-06-26 17:57:37 +00:00
|
|
|
newdist = d + RoutingMatrix.CalcDist( FROM_OTHERSIDE, olddir, 0, side );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
/* if (a) not visited yet,
|
|
|
|
* or (b) we have found a better path,
|
|
|
|
* add it to queue */
|
2012-06-10 16:21:18 +00:00
|
|
|
if( !RoutingMatrix.GetDir( r, c, 1 - side ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
RoutingMatrix.SetDir( r, c, 1 - side, FROM_OTHERSIDE );
|
|
|
|
RoutingMatrix.SetDist( r, c, 1 - side, newdist );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
|
|
|
if( SetQueue( r, c, 1 - side, newdist, apx_dist, row_target, col_target ) == 0 )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
return ERR_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
2012-06-10 16:21:18 +00:00
|
|
|
else if( newdist < RoutingMatrix.GetDist( r, c, 1 - side ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
RoutingMatrix.SetDir( r, c, 1 - side, FROM_OTHERSIDE );
|
|
|
|
RoutingMatrix.SetDist( r, c, 1 - side, newdist );
|
|
|
|
ReSetQueue( r, c,
|
2009-11-20 14:55:20 +00:00
|
|
|
1 - side,
|
|
|
|
newdist,
|
|
|
|
apx_dist,
|
|
|
|
row_target,
|
|
|
|
col_target );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2009-11-20 14:55:20 +00:00
|
|
|
} /* Finished attempt to route on other layer. */
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
|
|
|
|
end_of_route:
|
2012-06-17 16:06:12 +00:00
|
|
|
PlacePad( pt_cur_ch->m_PadStart, ~CURRENT_PAD, marge, WRITE_AND_CELL );
|
|
|
|
PlacePad( pt_cur_ch->m_PadEnd, ~CURRENT_PAD, marge, WRITE_AND_CELL );
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
msg.Printf( wxT( "Activity: Open %d Closed %d Moved %d"),
|
|
|
|
OpenNodes, ClosNodes, MoveNodes );
|
2011-02-11 20:48:13 +00:00
|
|
|
pcbframe->SetStatusText( msg );
|
2009-01-28 17:08:35 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
return result;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
static long bit[8][9] =
|
|
|
|
{
|
|
|
|
/* OT=Otherside */
|
2007-08-23 04:28:46 +00:00
|
|
|
/* N, NE, E, SE, S, SW, W, NW, OT */
|
2008-04-30 11:52:34 +00:00
|
|
|
/* N */
|
2009-11-20 14:55:20 +00:00
|
|
|
{ LINE_VERTICAL,
|
|
|
|
BENT_StoNE,
|
|
|
|
CORNER_SOUTHEAST,
|
|
|
|
SHARP_StoSE,
|
|
|
|
0,
|
|
|
|
SHARP_StoSW,
|
|
|
|
CORNER_SOUTHWEST,
|
|
|
|
BENT_StoNW,
|
|
|
|
( HOLE | HOLE_SOUTH )
|
|
|
|
},
|
|
|
|
/* NE */
|
|
|
|
{
|
|
|
|
BENT_NtoSW,
|
|
|
|
DIAG_NEtoSW,
|
|
|
|
BENT_EtoSW,
|
|
|
|
ANGLE_SEtoSW,
|
|
|
|
SHARP_StoSW,
|
|
|
|
0,
|
|
|
|
SHARP_WtoSW,
|
|
|
|
ANGLE_SWtoNW,
|
|
|
|
( HOLE | HOLE_SOUTHWEST )
|
|
|
|
},
|
|
|
|
/* E */
|
|
|
|
{
|
|
|
|
CORNER_NORTHWEST,
|
|
|
|
BENT_WtoNE,
|
|
|
|
LINE_HORIZONTAL,
|
|
|
|
BENT_WtoSE,
|
|
|
|
CORNER_SOUTHWEST,
|
|
|
|
SHARP_WtoSW,
|
|
|
|
0,
|
|
|
|
SHARP_WtoNW,
|
|
|
|
( HOLE | HOLE_WEST )
|
|
|
|
},
|
|
|
|
/* SE */
|
|
|
|
{
|
|
|
|
SHARP_NtoNW,
|
|
|
|
ANGLE_NWtoNE,
|
|
|
|
BENT_EtoNW,
|
|
|
|
DIAG_SEtoNW,
|
|
|
|
BENT_StoNW,
|
|
|
|
ANGLE_SWtoNW,
|
|
|
|
SHARP_WtoNW,
|
|
|
|
0,
|
|
|
|
( HOLE | HOLE_NORTHWEST )
|
|
|
|
},
|
|
|
|
/* S */
|
|
|
|
{
|
|
|
|
0,
|
|
|
|
SHARP_NtoNE,
|
|
|
|
CORNER_NORTHEAST,
|
|
|
|
BENT_NtoSE,
|
|
|
|
LINE_VERTICAL,
|
|
|
|
BENT_NtoSW,
|
|
|
|
CORNER_NORTHWEST,
|
|
|
|
SHARP_NtoNW,
|
|
|
|
( HOLE | HOLE_NORTH )
|
|
|
|
},
|
|
|
|
/* SW */
|
|
|
|
{
|
|
|
|
SHARP_NtoNE,
|
|
|
|
0,
|
|
|
|
SHARP_EtoNE,
|
|
|
|
ANGLE_NEtoSE,
|
|
|
|
BENT_StoNE,
|
|
|
|
DIAG_NEtoSW,
|
|
|
|
BENT_WtoNE,
|
|
|
|
ANGLE_NWtoNE,
|
|
|
|
( HOLE | HOLE_NORTHEAST )
|
|
|
|
},
|
|
|
|
/* W */
|
|
|
|
{
|
|
|
|
CORNER_NORTHEAST,
|
|
|
|
SHARP_EtoNE,
|
|
|
|
0,
|
|
|
|
SHARP_EtoSE,
|
|
|
|
CORNER_SOUTHEAST,
|
|
|
|
BENT_EtoSW,
|
|
|
|
LINE_HORIZONTAL,
|
|
|
|
BENT_EtoNW,
|
|
|
|
( HOLE | HOLE_EAST )
|
|
|
|
},
|
|
|
|
/* NW */
|
|
|
|
{
|
|
|
|
BENT_NtoSE,
|
|
|
|
ANGLE_NEtoSE,
|
|
|
|
SHARP_EtoSE,
|
|
|
|
0,
|
|
|
|
SHARP_StoSE,
|
|
|
|
ANGLE_SEtoSW,
|
|
|
|
BENT_WtoSE,
|
|
|
|
DIAG_SEtoNW,
|
|
|
|
( HOLE | HOLE_SOUTHEAST )
|
|
|
|
}
|
2007-08-23 04:28:46 +00:00
|
|
|
};
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-01-25 17:38:30 +00:00
|
|
|
|
2007-06-05 12:10:51 +00:00
|
|
|
/* work from target back to source, actually laying the traces
|
2009-11-20 14:55:20 +00:00
|
|
|
* Parameters:
|
|
|
|
* start on side target_side, of coordinates row_target, col_target.
|
|
|
|
* arrive on side masque_layer_start, coordinate row_source, col_source
|
|
|
|
* The search is done in reverse routing, the point of arrival (target) to
|
|
|
|
* the starting point (source)
|
|
|
|
* The router.
|
2008-02-12 01:02:53 +00:00
|
|
|
*
|
2009-11-20 14:55:20 +00:00
|
|
|
* Target_side = symbol (TOP / BOTTOM) of departure
|
|
|
|
* = Mask_layer_source mask layers Arrival
|
2008-02-12 01:02:53 +00:00
|
|
|
*
|
2009-11-20 14:55:20 +00:00
|
|
|
* Returns:
|
|
|
|
* 0 if error
|
|
|
|
* > 0 if Ok
|
2007-08-23 04:28:46 +00:00
|
|
|
*/
|
2011-03-01 19:26:17 +00:00
|
|
|
static int Retrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC,
|
2009-11-20 14:55:20 +00:00
|
|
|
int row_source, int col_source,
|
|
|
|
int row_target, int col_target, int target_side,
|
|
|
|
int current_net_code )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-23 04:28:46 +00:00
|
|
|
int r0, c0, s0;
|
2009-11-20 14:55:20 +00:00
|
|
|
int r1, c1, s1; /* row, col, starting side. */
|
|
|
|
int r2, c2, s2; /* row, col, ending side. */
|
2007-08-23 04:28:46 +00:00
|
|
|
int x, y = -1;
|
|
|
|
long b;
|
|
|
|
|
|
|
|
r1 = row_target;
|
|
|
|
c1 = col_target; /* start point is target ( end point is source )*/
|
|
|
|
s1 = target_side;
|
|
|
|
r0 = c0 = s0 = ILLEGAL;
|
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
wxASSERT( g_CurrentTrackList.GetCount() == 0 );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
do
|
|
|
|
{
|
2007-08-23 04:28:46 +00:00
|
|
|
/* find where we came from to get here */
|
|
|
|
r2 = r1; c2 = c1; s2 = s1;
|
2012-06-10 16:21:18 +00:00
|
|
|
x = RoutingMatrix.GetDir( r1, c1, s1 );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
switch( x )
|
|
|
|
{
|
|
|
|
case FROM_NORTH:
|
2009-11-20 14:55:20 +00:00
|
|
|
r2++;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_EAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
c2++;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTH:
|
2009-11-20 14:55:20 +00:00
|
|
|
r2--;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_WEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
c2--;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_NORTHEAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
r2++;
|
|
|
|
c2++;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTHEAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
r2--;
|
|
|
|
c2++;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTHWEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
r2--;
|
|
|
|
c2--;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_NORTHWEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
r2++;
|
|
|
|
c2--;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_OTHERSIDE:
|
2009-11-20 14:55:20 +00:00
|
|
|
s2 = 1 - s2;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
default:
|
2012-06-10 16:21:18 +00:00
|
|
|
wxMessageBox( wxT( "Retrace: internal error: no way back" ) );
|
2007-08-23 04:28:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( r0 != ILLEGAL )
|
2012-06-10 16:21:18 +00:00
|
|
|
y = RoutingMatrix.GetDir( r0, c0, s0 );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
/* see if target or hole */
|
2011-09-20 13:57:40 +00:00
|
|
|
if( ( ( r1 == row_target ) && ( c1 == col_target ) ) || ( s1 != s0 ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
|
|
|
int p_dir;
|
|
|
|
|
|
|
|
switch( x )
|
|
|
|
{
|
|
|
|
case FROM_NORTH:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_NORTH;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_EAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_EAST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTH:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_SOUTH;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_WEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_WEST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_NORTHEAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_NORTHEAST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTHEAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_SOUTHEAST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTHWEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_SOUTHWEST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_NORTHWEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_NORTHWEST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_OTHERSIDE:
|
|
|
|
default:
|
|
|
|
DisplayError( pcbframe, wxT( "Retrace: error 1" ) );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
OrCell_Trace( pcbframe->GetBoard(), r1, c1, s1, p_dir, current_net_code );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
if( ( y == FROM_NORTH || y == FROM_NORTHEAST
|
|
|
|
|| y == FROM_EAST || y == FROM_SOUTHEAST
|
|
|
|
|| y == FROM_SOUTH || y == FROM_SOUTHWEST
|
|
|
|
|| y == FROM_WEST || y == FROM_NORTHWEST )
|
|
|
|
&& ( x == FROM_NORTH || x == FROM_NORTHEAST
|
|
|
|
|| x == FROM_EAST || x == FROM_SOUTHEAST
|
|
|
|
|| x == FROM_SOUTH || x == FROM_SOUTHWEST
|
|
|
|
|| x == FROM_WEST || x == FROM_NORTHWEST
|
|
|
|
|| x == FROM_OTHERSIDE )
|
|
|
|
&& ( ( b = bit[y - 1][x - 1] ) != 0 ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
OrCell_Trace( pcbframe->GetBoard(), r1, c1, s1, b, current_net_code );
|
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( b & HOLE )
|
2011-09-07 19:41:04 +00:00
|
|
|
OrCell_Trace( pcbframe->GetBoard(), r2, c2, s2, HOLE, current_net_code );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-06-10 16:21:18 +00:00
|
|
|
wxMessageBox( wxT( "Retrace: error 2" ) );
|
2007-08-23 04:28:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( ( r2 == row_source ) && ( c2 == col_source ) ) /* see if source */
|
|
|
|
{
|
2007-08-23 04:28:46 +00:00
|
|
|
int p_dir;
|
|
|
|
|
|
|
|
switch( x )
|
|
|
|
{
|
|
|
|
case FROM_NORTH:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_SOUTH;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_EAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_WEST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTH:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_NORTH;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_WEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_EAST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_NORTHEAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_SOUTHWEST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTHEAST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_NORTHWEST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_SOUTHWEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_NORTHEAST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_NORTHWEST:
|
2009-11-20 14:55:20 +00:00
|
|
|
p_dir = HOLE_SOUTHEAST;
|
|
|
|
break;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
|
|
|
case FROM_OTHERSIDE:
|
|
|
|
default:
|
2012-06-10 16:21:18 +00:00
|
|
|
wxMessageBox( wxT( "Retrace: error 3" ) );
|
2007-08-23 04:28:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
OrCell_Trace( pcbframe->GetBoard(), r2, c2, s2, p_dir, current_net_code );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
/* move to next cell */
|
2009-11-20 14:55:20 +00:00
|
|
|
r0 = r1;
|
|
|
|
c0 = c1;
|
|
|
|
s0 = s1;
|
|
|
|
r1 = r2;
|
|
|
|
c1 = c2;
|
|
|
|
s1 = s2;
|
|
|
|
} while( !( ( r2 == row_source ) && ( c2 == col_source ) ) );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
AddNewTrace( pcbframe, DC );
|
2007-08-23 04:28:46 +00:00
|
|
|
return 1;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-23 21:03:26 +00:00
|
|
|
/* This function is used by Retrace and read the autorouting matrix data cells to create
|
|
|
|
* the real track on the physical board
|
|
|
|
*/
|
2007-08-23 04:28:46 +00:00
|
|
|
static void OrCell_Trace( BOARD* pcb, int col, int row,
|
|
|
|
int side, int orient, int current_net_code )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2007-08-23 04:28:46 +00:00
|
|
|
int dx0, dy0, dx1, dy1;
|
2008-12-04 04:28:11 +00:00
|
|
|
TRACK* newTrack;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
if( orient == HOLE ) // placement of a via
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2008-12-04 04:28:11 +00:00
|
|
|
newTrack = new SEGVIA( pcb );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
g_CurrentTrackList.PushBack( newTrack );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2013-03-27 18:32:12 +00:00
|
|
|
g_CurrentTrackSegment->SetState( TRACK_AR, true );
|
2007-08-23 04:28:46 +00:00
|
|
|
g_CurrentTrackSegment->SetLayer( 0x0F );
|
2008-12-04 04:28:11 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetStart(wxPoint( pcb->GetBoundingBox().GetX() +
|
2013-03-06 09:04:27 +00:00
|
|
|
( RoutingMatrix.m_GridRouting * row ),
|
2013-01-13 00:04:00 +00:00
|
|
|
pcb->GetBoundingBox().GetY() +
|
|
|
|
( RoutingMatrix.m_GridRouting * col )));
|
|
|
|
g_CurrentTrackSegment->SetEnd( g_CurrentTrackSegment->GetStart() );
|
2013-03-06 09:04:27 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetWidth( pcb->GetCurrentViaSize() );
|
|
|
|
g_CurrentTrackSegment->SetShape( pcb->GetDesignSettings().m_CurrentViaType );
|
2008-12-04 04:28:11 +00:00
|
|
|
|
2014-02-25 10:40:34 +00:00
|
|
|
g_CurrentTrackSegment->SetNetCode( current_net_code );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2008-12-04 04:28:11 +00:00
|
|
|
else // placement of a standard segment
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2008-12-04 04:28:11 +00:00
|
|
|
newTrack = new TRACK( pcb );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
g_CurrentTrackList.PushBack( newTrack );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2013-03-27 18:32:12 +00:00
|
|
|
g_CurrentTrackSegment->SetLayer( g_Route_Layer_BOTTOM );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( side == TOP )
|
2013-03-27 18:32:12 +00:00
|
|
|
g_CurrentTrackSegment->SetLayer( g_Route_Layer_TOP );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2013-03-27 18:32:12 +00:00
|
|
|
g_CurrentTrackSegment->SetState( TRACK_AR, true );
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetEnd( wxPoint( pcb->GetBoundingBox().GetX() +
|
|
|
|
( RoutingMatrix.m_GridRouting * row ),
|
|
|
|
pcb->GetBoundingBox().GetY() +
|
|
|
|
( RoutingMatrix.m_GridRouting * col )));
|
2014-02-25 10:40:34 +00:00
|
|
|
g_CurrentTrackSegment->SetNetCode( current_net_code );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( g_CurrentTrackSegment->Back() == NULL ) /* Start trace. */
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetStart( wxPoint( segm_fX, segm_fY ) );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Placement on the center of the pad if outside grid. */
|
2013-01-13 00:04:00 +00:00
|
|
|
dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
|
|
|
|
dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;
|
2008-12-04 04:28:11 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
dx0 = pt_cur_ch->m_PadEnd->GetPosition().x - g_CurrentTrackSegment->GetStart().x;
|
|
|
|
dy0 = pt_cur_ch->m_PadEnd->GetPosition().y - g_CurrentTrackSegment->GetStart().y;
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* If aligned, change the origin point. */
|
|
|
|
if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetStart( pt_cur_ch->m_PadEnd->GetPosition() );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2008-12-04 04:28:11 +00:00
|
|
|
else // Creation of a supplemental segment
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetStart( pt_cur_ch->m_PadEnd->GetPosition() );
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2012-01-14 19:50:32 +00:00
|
|
|
newTrack = (TRACK*)g_CurrentTrackSegment->Clone();
|
2013-01-13 00:04:00 +00:00
|
|
|
newTrack->SetStart( g_CurrentTrackSegment->GetEnd());
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
g_CurrentTrackList.PushBack( newTrack );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-11-24 06:53:43 +00:00
|
|
|
if( g_CurrentTrackSegment->Back() )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetStart( g_CurrentTrackSegment->Back()->GetEnd() );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetWidth( pcb->GetCurrentTrackWidth() );
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
if( g_CurrentTrackSegment->GetStart() != g_CurrentTrackSegment->GetEnd() )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Reduce aligned segments by one. */
|
2008-12-04 04:28:11 +00:00
|
|
|
TRACK* oldTrack = g_CurrentTrackSegment->Back();
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
if( oldTrack && oldTrack->Type() != PCB_VIA_T )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
|
|
|
|
dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;
|
2008-12-04 04:28:11 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
dx0 = oldTrack->GetEnd().x - oldTrack->GetStart().x;
|
|
|
|
dy0 = oldTrack->GetEnd().y - oldTrack->GetStart().y;
|
2008-12-04 04:28:11 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
oldTrack->SetEnd( g_CurrentTrackSegment->GetEnd() );
|
2008-12-04 04:28:11 +00:00
|
|
|
|
|
|
|
delete g_CurrentTrackList.PopBack();
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Insert the new track created in the list of tracks.
|
|
|
|
* amend the points of beginning and end of the track so that they are
|
|
|
|
* connected
|
|
|
|
* Center on pads even if they are off grid.
|
2007-08-23 04:28:46 +00:00
|
|
|
*/
|
2011-09-07 19:41:04 +00:00
|
|
|
static void AddNewTrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2009-01-25 17:38:30 +00:00
|
|
|
if( g_FirstTrackSegment == NULL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int dx0, dy0, dx1, dy1;
|
|
|
|
int marge, via_marge;
|
2011-12-22 13:28:11 +00:00
|
|
|
EDA_DRAW_PANEL* panel = pcbframe->GetCanvas();
|
2011-02-01 15:46:25 +00:00
|
|
|
PCB_SCREEN* screen = pcbframe->GetScreen();
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2011-02-01 15:46:25 +00:00
|
|
|
marge = s_Clearance + ( pcbframe->GetBoard()->GetCurrentTrackWidth() / 2 );
|
2009-11-20 14:55:20 +00:00
|
|
|
via_marge = s_Clearance + ( pcbframe->GetBoard()->GetCurrentViaSize() / 2 );
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
|
|
|
|
dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* Place on center of pad if off grid. */
|
2013-01-13 00:04:00 +00:00
|
|
|
dx0 = pt_cur_ch->m_PadStart->GetPosition().x - g_CurrentTrackSegment->GetStart().x;
|
|
|
|
dy0 = pt_cur_ch->m_PadStart->GetPosition().y - g_CurrentTrackSegment->GetStart().y;
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-11-20 14:55:20 +00:00
|
|
|
/* If aligned, change the origin point. */
|
|
|
|
if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
g_CurrentTrackSegment->SetEnd( pt_cur_ch->m_PadStart->GetPosition() );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2009-11-20 14:55:20 +00:00
|
|
|
else
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-01-14 19:50:32 +00:00
|
|
|
TRACK* newTrack = (TRACK*)g_CurrentTrackSegment->Clone();
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
newTrack->SetEnd( pt_cur_ch->m_PadStart->GetPosition() );
|
|
|
|
newTrack->SetStart( g_CurrentTrackSegment->GetEnd() );
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
g_CurrentTrackList.PushBack( newTrack );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2012-07-25 18:46:25 +00:00
|
|
|
g_FirstTrackSegment->start = pcbframe->GetBoard()->GetPad( g_FirstTrackSegment, FLG_START );
|
2011-02-01 15:46:25 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( g_FirstTrackSegment->start )
|
2013-03-27 18:32:12 +00:00
|
|
|
g_FirstTrackSegment->SetState( BEGIN_ONPAD, true );
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2012-07-25 18:46:25 +00:00
|
|
|
g_CurrentTrackSegment->end = pcbframe->GetBoard()->GetPad( g_CurrentTrackSegment, FLG_END );
|
2011-02-01 15:46:25 +00:00
|
|
|
|
2007-08-23 04:28:46 +00:00
|
|
|
if( g_CurrentTrackSegment->end )
|
2013-03-27 18:32:12 +00:00
|
|
|
g_CurrentTrackSegment->SetState( END_ONPAD, true );
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-01-25 17:38:30 +00:00
|
|
|
/* Out the new track on the matrix board */
|
2009-11-20 14:55:20 +00:00
|
|
|
for( TRACK* track = g_FirstTrackSegment; track; track = track->Next() )
|
2007-08-23 04:28:46 +00:00
|
|
|
{
|
2012-06-17 16:06:12 +00:00
|
|
|
TraceSegmentPcb( track, HOLE, marge, WRITE_CELL );
|
|
|
|
TraceSegmentPcb( track, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL );
|
2007-08-23 04:28:46 +00:00
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2009-01-25 17:38:30 +00:00
|
|
|
// Insert new segments in real board
|
2014-02-25 10:40:34 +00:00
|
|
|
int netcode = g_FirstTrackSegment->GetNetCode();
|
2009-01-25 17:38:30 +00:00
|
|
|
TRACK* firstTrack = g_FirstTrackSegment;
|
|
|
|
int newCount = g_CurrentTrackList.GetCount();
|
|
|
|
|
|
|
|
// Put entire new current segment list in BOARD
|
|
|
|
TRACK* track;
|
2011-02-01 15:46:25 +00:00
|
|
|
TRACK* insertBeforeMe = g_CurrentTrackSegment->GetBestInsertPoint( pcbframe->GetBoard() );
|
2009-11-20 14:55:20 +00:00
|
|
|
|
2009-01-25 17:38:30 +00:00
|
|
|
while( ( track = g_CurrentTrackList.PopFront() ) != NULL )
|
|
|
|
{
|
2011-12-13 20:42:42 +00:00
|
|
|
ITEM_PICKER picker( track, UR_NEW );
|
|
|
|
s_ItemsListPicker.PushItem( picker );
|
2009-01-25 17:38:30 +00:00
|
|
|
pcbframe->GetBoard()->m_Track.Insert( track, insertBeforeMe );
|
|
|
|
}
|
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
DrawTraces( panel, DC, firstTrack, newCount, GR_OR );
|
2009-01-25 17:38:30 +00:00
|
|
|
|
2011-09-15 17:58:35 +00:00
|
|
|
pcbframe->TestNetConnection( DC, netcode );
|
2009-01-25 17:38:30 +00:00
|
|
|
|
2011-02-01 15:46:25 +00:00
|
|
|
screen->SetModify();
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|