775 lines
16 KiB
C++
775 lines
16 KiB
C++
/*
|
|
* KiRouter - a push-and-(sometimes-)shove PCB router
|
|
*
|
|
* Copyright (C) 2013 CERN
|
|
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
*
|
|
* 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 3 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, see <http://www.gnu.or/licenses/>.
|
|
*/
|
|
#include <cstdio>
|
|
#include <vector>
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
#include <view/view.h>
|
|
#include <view/view_item.h>
|
|
#include <view/view_group.h>
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
|
|
#include <pcb_painter.h>
|
|
|
|
#include <geometry/shape.h>
|
|
#include <geometry/shape_line_chain.h>
|
|
#include <geometry/shape_rect.h>
|
|
#include <geometry/shape_circle.h>
|
|
|
|
#include "trace.h"
|
|
#include "pns_node.h"
|
|
#include "pns_line_placer.h"
|
|
#include "pns_line.h"
|
|
#include "pns_solid.h"
|
|
#include "pns_utils.h"
|
|
#include "pns_router.h"
|
|
|
|
#include <router/router_preview_item.h>
|
|
|
|
#include <class_board.h>
|
|
#include <class_board_item.h>
|
|
#include <class_pad.h>
|
|
#include <class_track.h>
|
|
#include <layers_id_colors_and_visibility.h>
|
|
|
|
using namespace std;
|
|
|
|
// an ugly singleton for drawing debug items within the router context. To be fixed sometime in the future.
|
|
static PNS_ROUTER *theRouter;
|
|
|
|
class PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC
|
|
{
|
|
public:
|
|
PCBNEW_CLEARANCE_FUNC( BOARD *aBoard )
|
|
{
|
|
m_clearanceCache.resize(aBoard->GetNetCount());
|
|
|
|
for(unsigned int i = 0; i < aBoard->GetNetCount(); i++)
|
|
{
|
|
NETINFO_ITEM *ni = aBoard->FindNet(i);
|
|
wxString netClassName = ni->GetClassName();
|
|
NETCLASS *nc = aBoard->m_NetClasses.Find(netClassName);
|
|
int clearance = nc->GetClearance();
|
|
m_clearanceCache[i] = clearance;
|
|
TRACE(1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() % clearance);
|
|
}
|
|
|
|
m_defaultClearance = 254000;//aBoard->m_NetClasses.Find ("Default clearance")->GetClearance();
|
|
}
|
|
|
|
int operator() (const PNS_ITEM *a , const PNS_ITEM *b)
|
|
{
|
|
int net_a = a->GetNet();
|
|
int cl_a = (net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance);
|
|
int net_b = b->GetNet();
|
|
int cl_b = (net_b >= 0 ? m_clearanceCache[net_b] : m_defaultClearance);
|
|
return std::max(cl_a, cl_b);
|
|
}
|
|
|
|
private:
|
|
|
|
vector<int> m_clearanceCache;
|
|
int m_defaultClearance;
|
|
};
|
|
|
|
PNS_ITEM *PNS_ROUTER::syncPad( D_PAD *aPad )
|
|
{
|
|
PNS_LAYERSET layers;
|
|
|
|
switch(aPad->GetAttribute())
|
|
{
|
|
case PAD_STANDARD:
|
|
layers = PNS_LAYERSET(0, 15);
|
|
break;
|
|
case PAD_SMD:
|
|
case PAD_CONN:
|
|
{
|
|
LAYER_MSK lmsk = aPad->GetLayerMask();
|
|
int i;
|
|
|
|
for(i = FIRST_COPPER_LAYER; i <= LAST_COPPER_LAYER; i++)
|
|
if( lmsk & (1<<i))
|
|
{
|
|
layers = PNS_LAYERSET( i );
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
TRACE(0, "unsupported pad type 0x%x", aPad->GetAttribute());
|
|
return NULL;
|
|
}
|
|
|
|
PNS_SOLID *solid = new PNS_SOLID;
|
|
|
|
solid->SetLayers(layers);
|
|
solid->SetNet( aPad->GetNet() );
|
|
wxPoint wx_c = aPad->GetPosition();
|
|
wxSize wx_sz = aPad->GetSize();
|
|
|
|
VECTOR2I c(wx_c.x, wx_c.y);
|
|
VECTOR2I sz(wx_sz.x, wx_sz.y);
|
|
|
|
solid->SetCenter( c );
|
|
|
|
|
|
double orient = aPad->GetOrientation() / 10.0;
|
|
|
|
if(orient == 90.0 || orient == 270.0)
|
|
sz = VECTOR2I(sz.y, sz.x);
|
|
else if (orient != 0.0 && orient != 180.0)
|
|
{
|
|
TRACEn(0, "non-orthogonal pad rotations not supported yet");
|
|
delete solid;
|
|
return NULL;
|
|
}
|
|
|
|
switch(aPad->GetShape())
|
|
{
|
|
case PAD_CIRCLE:
|
|
solid->SetShape ( new SHAPE_CIRCLE ( c, sz.x / 2) );
|
|
break;
|
|
case PAD_OVAL:
|
|
if(sz.x == sz.y)
|
|
solid->SetShape ( new SHAPE_CIRCLE ( c, sz.x / 2) );
|
|
else
|
|
solid->SetShape ( new SHAPE_RECT ( c - sz / 2, sz.x, sz.y) );
|
|
break;
|
|
|
|
case PAD_RECT:
|
|
solid->SetShape ( new SHAPE_RECT ( c - sz / 2, sz.x, sz.y) );
|
|
break;
|
|
|
|
default:
|
|
TRACEn(0, "unsupported pad shape");
|
|
delete solid;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
solid->SetParent(aPad);
|
|
return solid;
|
|
}
|
|
|
|
PNS_ITEM *PNS_ROUTER::syncTrack( TRACK *aTrack )
|
|
{
|
|
|
|
PNS_SEGMENT *s = new PNS_SEGMENT( SEG (aTrack->GetStart(), aTrack->GetEnd() ), aTrack->GetNet() );
|
|
|
|
s->SetWidth( aTrack->GetWidth() );
|
|
s->SetLayers (PNS_LAYERSET(aTrack->GetLayer()));
|
|
s->SetParent(aTrack);
|
|
return s;
|
|
}
|
|
|
|
|
|
PNS_ITEM *PNS_ROUTER::syncVia( SEGVIA *aVia )
|
|
{
|
|
PNS_VIA *v = new PNS_VIA(
|
|
aVia->GetPosition(),
|
|
PNS_LAYERSET(0, 15),
|
|
aVia->GetWidth(),
|
|
aVia->GetNet());
|
|
|
|
v->SetParent(aVia);
|
|
return v;
|
|
}
|
|
|
|
void PNS_ROUTER::SetBoard( BOARD *aBoard )
|
|
{
|
|
m_board = aBoard;
|
|
TRACE(1, "m_board = %p\n", m_board);
|
|
}
|
|
|
|
int PNS_ROUTER::NextCopperLayer( bool aUp )
|
|
{
|
|
LAYER_MSK mask = m_board->GetEnabledLayers() & m_board->GetVisibleLayers();
|
|
LAYER_NUM l = m_currentLayer;
|
|
|
|
do {
|
|
l += (aUp ? 1 : -1);
|
|
if(l > LAST_COPPER_LAYER)
|
|
l = FIRST_COPPER_LAYER;
|
|
|
|
if(l < FIRST_COPPER_LAYER)
|
|
l = LAST_COPPER_LAYER;
|
|
|
|
if(mask & GetLayerMask(l))
|
|
return l;
|
|
|
|
} while (l != m_currentLayer);
|
|
|
|
return l;
|
|
}
|
|
|
|
void PNS_ROUTER::SyncWorld()
|
|
{
|
|
vector<D_PAD *> pads;
|
|
|
|
if(!m_board)
|
|
{
|
|
TRACEn(0,"No board attached, aborting sync.");
|
|
return;
|
|
}
|
|
|
|
ClearWorld();
|
|
|
|
|
|
m_clearanceFunc = new PCBNEW_CLEARANCE_FUNC(m_board);
|
|
m_world = new PNS_NODE();
|
|
m_world->SetClearanceFunctor ( m_clearanceFunc );
|
|
m_world->SetMaxClearance ( 1000000 ); //m_board->GetBiggestClearanceValue());
|
|
pads = m_board->GetPads();
|
|
|
|
BOOST_FOREACH( D_PAD *pad, pads )
|
|
{
|
|
PNS_ITEM *solid = syncPad(pad);
|
|
if(solid)
|
|
m_world->Add(solid);
|
|
}
|
|
|
|
for(TRACK *t = m_board->m_Track; t; t = t->Next())
|
|
{
|
|
KICAD_T type = t->Type();
|
|
PNS_ITEM *item = NULL;
|
|
if(type == PCB_TRACE_T)
|
|
item = syncTrack ( t );
|
|
else if( type == PCB_VIA_T )
|
|
item = syncVia (static_cast <SEGVIA *>(t));
|
|
|
|
if(item)
|
|
m_world->Add(item);
|
|
}
|
|
|
|
m_placer = new PNS_LINE_PLACER ( m_world );
|
|
}
|
|
|
|
PNS_ROUTER::PNS_ROUTER()
|
|
{
|
|
theRouter = this;
|
|
|
|
m_clearanceFunc = NULL;
|
|
|
|
m_currentLayer = 1;
|
|
m_placingVia = false;
|
|
m_currentNet = -1;
|
|
m_state = IDLE;
|
|
m_world = NULL;
|
|
m_placer = NULL;
|
|
m_previewItems = NULL;
|
|
m_start_diagonal = false;
|
|
m_board = NULL;
|
|
|
|
TRACE(1, "m_board = %p\n", m_board);
|
|
}
|
|
|
|
|
|
void PNS_ROUTER::SetView(KiGfx::VIEW *aView)
|
|
{
|
|
if(m_previewItems)
|
|
{
|
|
m_previewItems->FreeItems();
|
|
delete m_previewItems;
|
|
}
|
|
|
|
m_view = aView;
|
|
m_previewItems = new KiGfx::VIEW_GROUP(m_view);
|
|
m_previewItems->SetLayer(ITEM_GAL_LAYER( GP_OVERLAY ));
|
|
m_view -> Add (m_previewItems);
|
|
m_previewItems->ViewSetVisible(true);
|
|
|
|
}
|
|
|
|
PNS_ROUTER *PNS_ROUTER::GetInstance()
|
|
{
|
|
return theRouter;
|
|
}
|
|
|
|
PNS_ROUTER::~PNS_ROUTER()
|
|
{
|
|
ClearWorld();
|
|
theRouter = NULL;
|
|
}
|
|
|
|
void PNS_ROUTER::ClearWorld()
|
|
{
|
|
if(m_world)
|
|
delete m_world;
|
|
if(m_clearanceFunc)
|
|
delete m_clearanceFunc;
|
|
if(m_placer)
|
|
delete m_placer;
|
|
|
|
m_clearanceFunc = NULL;
|
|
m_world = NULL;
|
|
m_placer = NULL;
|
|
}
|
|
|
|
void PNS_ROUTER::SetCurrentWidth (int w )
|
|
{
|
|
// fixme: change width while routing
|
|
m_currentWidth = w;
|
|
}
|
|
|
|
bool PNS_ROUTER::RoutingInProgress() const
|
|
{
|
|
return m_state != IDLE;
|
|
}
|
|
|
|
|
|
const PNS_ITEMSET PNS_ROUTER::QueryHoverItems(const VECTOR2I&aP)
|
|
{
|
|
if(m_state == IDLE)
|
|
return m_world->HitTest( aP );
|
|
else
|
|
return m_placer->GetCurrentNode() -> HitTest(aP);
|
|
}
|
|
|
|
|
|
const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM *item, VECTOR2I aP, bool& aSplitsSegment )
|
|
{
|
|
VECTOR2I anchor;
|
|
|
|
if(!item)
|
|
{
|
|
aSplitsSegment = false;
|
|
return aP;
|
|
}
|
|
|
|
switch(item->GetKind())
|
|
{
|
|
case PNS_ITEM::SOLID:
|
|
anchor = static_cast<PNS_SOLID *>(item)->GetCenter();
|
|
aSplitsSegment = false;
|
|
break;
|
|
case PNS_ITEM::VIA:
|
|
anchor = static_cast<PNS_VIA *>(item)->GetPos();
|
|
aSplitsSegment = false;
|
|
break;
|
|
case PNS_ITEM::SEGMENT:
|
|
{
|
|
PNS_SEGMENT *seg = static_cast<PNS_SEGMENT *>(item);
|
|
const SEG& s = seg->GetSeg();
|
|
int w = seg->GetWidth();
|
|
|
|
aSplitsSegment = false;
|
|
|
|
if ((aP - s.a).EuclideanNorm() < w / 2)
|
|
anchor = s.a;
|
|
else if ((aP - s.b).EuclideanNorm() < w / 2)
|
|
anchor = s.b;
|
|
else {
|
|
anchor = s.NearestPoint(aP);
|
|
aSplitsSegment = true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return anchor;
|
|
}
|
|
|
|
|
|
void PNS_ROUTER::StartRouting(const VECTOR2I& aP, PNS_ITEM *aStartItem)
|
|
{
|
|
VECTOR2I p;
|
|
|
|
static int unknowNetIdx = 0;//-10000;
|
|
|
|
m_placingVia = false;
|
|
m_startsOnVia = false;
|
|
m_currentNet = -1;
|
|
|
|
bool splitSeg = false;
|
|
|
|
p = SnapToItem( aStartItem, aP, splitSeg );
|
|
|
|
if(!aStartItem || aStartItem->GetNet() < 0)
|
|
m_currentNet = unknowNetIdx--;
|
|
else
|
|
m_currentNet = aStartItem->GetNet();
|
|
|
|
m_currentStart = p;
|
|
m_originalStart = p;
|
|
m_currentEnd = p;
|
|
|
|
m_placer->SetInitialDirection(m_start_diagonal ? DIRECTION_45(DIRECTION_45::NE) : DIRECTION_45(DIRECTION_45::N));
|
|
m_placer->StartPlacement(m_originalStart, m_currentNet, m_currentWidth, m_currentLayer);
|
|
m_state = ROUTE_TRACK;
|
|
|
|
if(splitSeg)
|
|
splitAdjacentSegments(m_placer->GetCurrentNode(), aStartItem, p);
|
|
|
|
|
|
}
|
|
|
|
const VECTOR2I PNS_ROUTER::GetCurrentEnd( ) const
|
|
{
|
|
return m_currentEnd;
|
|
}
|
|
|
|
void PNS_ROUTER::EraseView()
|
|
{
|
|
BOOST_FOREACH(BOARD_ITEM *item, m_hiddenItems)
|
|
{
|
|
item->ViewSetVisible(true);
|
|
}
|
|
|
|
if(m_previewItems)
|
|
m_previewItems->FreeItems();
|
|
m_previewItems->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY );
|
|
}
|
|
|
|
void PNS_ROUTER::DisplayItem(const PNS_ITEM* aItem, bool aIsHead)
|
|
{
|
|
|
|
ROUTER_PREVIEW_ITEM * pitem = new ROUTER_PREVIEW_ITEM (aItem, m_previewItems);
|
|
|
|
m_previewItems->Add (pitem);
|
|
if(aIsHead)
|
|
pitem->MarkAsHead();
|
|
|
|
pitem->ViewSetVisible(true);
|
|
m_previewItems->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY | KiGfx::VIEW_ITEM::APPEARANCE );
|
|
}
|
|
|
|
void PNS_ROUTER::DisplayDebugLine ( const SHAPE_LINE_CHAIN &aLine, int aType, int aWidth)
|
|
{
|
|
ROUTER_PREVIEW_ITEM * pitem = new ROUTER_PREVIEW_ITEM (NULL, m_previewItems);
|
|
|
|
pitem->DebugLine (aLine, aWidth, aType );
|
|
m_previewItems->Add (pitem);
|
|
pitem->ViewSetVisible(true);
|
|
m_previewItems->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY | KiGfx::VIEW_ITEM::APPEARANCE );
|
|
}
|
|
|
|
void PNS_ROUTER::DisplayDebugBox ( const BOX2I& aBox, int aType , int aWidth )
|
|
{
|
|
|
|
}
|
|
|
|
|
|
void PNS_ROUTER::Move(const VECTOR2I& aP, PNS_ITEM *endItem)
|
|
{
|
|
PNS_NODE::ItemVector removed, added;
|
|
VECTOR2I p = aP;
|
|
|
|
if(m_state == IDLE)
|
|
return;
|
|
|
|
if(m_state == START_ROUTING)
|
|
{
|
|
|
|
}
|
|
|
|
EraseView();
|
|
|
|
m_currentEnd = p;
|
|
m_placer->Route(p);
|
|
|
|
PNS_LINE current = m_placer->GetTrace();
|
|
|
|
DisplayItem (¤t, true);
|
|
|
|
if(current.EndsWithVia())
|
|
DisplayItem( ¤t.GetVia(), true );
|
|
|
|
m_placer->GetCurrentNode()->GetUpdatedItems(removed, added);
|
|
|
|
BOOST_FOREACH(PNS_ITEM *item, added)
|
|
{
|
|
DisplayItem(item);
|
|
}
|
|
|
|
BOOST_FOREACH(PNS_ITEM *item, removed)
|
|
{
|
|
BOARD_ITEM *parent = item->GetParent();
|
|
|
|
if(parent)
|
|
{
|
|
if(parent->ViewIsVisible())
|
|
m_hiddenItems.insert(parent);
|
|
|
|
parent->ViewSetVisible(false);
|
|
parent->ViewUpdate (KiGfx::VIEW_ITEM::APPEARANCE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PNS_ROUTER::splitAdjacentSegments(PNS_NODE *aNode, PNS_ITEM *aSeg, const VECTOR2I& aP )
|
|
{
|
|
if(aSeg && aSeg->OfKind( PNS_ITEM::SEGMENT ))
|
|
{
|
|
PNS_NODE::OptJoint jt = aNode->FindJoint ( aP, aSeg->GetLayers().Start(), aSeg->GetNet());
|
|
|
|
if(jt && jt->LinkCount() >= 1)
|
|
return;
|
|
|
|
PNS_SEGMENT *s_old = static_cast<PNS_SEGMENT*>(aSeg);
|
|
PNS_SEGMENT *s_new [2];
|
|
|
|
s_new[0] = s_old->Clone();
|
|
s_new[1] = s_old->Clone();
|
|
|
|
s_new[0]->SetEnds (s_old->GetSeg().a, aP);
|
|
s_new[1]->SetEnds (aP, s_old->GetSeg().b);
|
|
|
|
|
|
aNode->Remove( s_old );
|
|
aNode->Add( s_new [0] );
|
|
aNode->Add( s_new [1] );
|
|
}
|
|
}
|
|
|
|
void PNS_ROUTER::commitRouting( PNS_NODE *aNode )
|
|
{
|
|
PNS_NODE::ItemVector removed, added;
|
|
|
|
aNode->GetUpdatedItems(removed, added);
|
|
|
|
for(unsigned int i = 0; i < removed.size(); i++)
|
|
{
|
|
BOARD_ITEM *parent = removed[i]->GetParent();
|
|
|
|
if(parent)
|
|
{
|
|
m_view->Remove(parent);
|
|
m_board->Remove(parent);
|
|
}
|
|
}
|
|
|
|
BOOST_FOREACH(PNS_ITEM *item, added)
|
|
{
|
|
BOARD_ITEM *newBI = NULL;
|
|
switch(item->GetKind())
|
|
{
|
|
case PNS_ITEM::SEGMENT:
|
|
{
|
|
PNS_SEGMENT *seg = static_cast<PNS_SEGMENT*>(item);
|
|
TRACK *track = new TRACK(m_board);
|
|
const SEG& s = seg->GetSeg();
|
|
|
|
track->SetStart( wxPoint(s.a.x, s.a.y));
|
|
track->SetEnd( wxPoint(s.b.x, s.b.y ));
|
|
track->SetWidth(seg->GetWidth());
|
|
track->SetLayer(seg->GetLayers().Start());
|
|
track->SetNet(seg->GetNet());
|
|
newBI = track;
|
|
break;
|
|
}
|
|
|
|
case PNS_ITEM::VIA:
|
|
{
|
|
SEGVIA *via_board = new SEGVIA(m_board);
|
|
PNS_VIA *via = static_cast<PNS_VIA *>(item);
|
|
via_board->SetPosition ( wxPoint(via->GetPos().x, via->GetPos().y ));
|
|
via_board->SetWidth ( via->GetDiameter() );
|
|
via_board->SetNet ( via->GetNet() );
|
|
newBI = via_board;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(newBI)
|
|
{
|
|
item->SetParent(newBI);
|
|
newBI->ClearFlags();
|
|
m_view->Add(newBI);
|
|
m_board->Add(newBI);
|
|
newBI->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY );
|
|
}
|
|
}
|
|
|
|
m_world->Commit( aNode );
|
|
}
|
|
|
|
PNS_VIA *PNS_ROUTER::checkLoneVia ( PNS_JOINT* aJoint ) const
|
|
{
|
|
PNS_VIA *theVia = NULL;
|
|
PNS_LAYERSET l;
|
|
|
|
BOOST_FOREACH(PNS_ITEM *item, aJoint->GetLinkList())
|
|
{
|
|
if(item->GetKind() == PNS_ITEM::VIA)
|
|
theVia = static_cast<PNS_VIA *>(item);
|
|
|
|
|
|
l.Merge (item->GetLayers());
|
|
}
|
|
|
|
if(l.Start() == l.End())
|
|
return theVia;
|
|
return NULL;
|
|
}
|
|
|
|
PNS_NODE *PNS_ROUTER::removeLoops ( PNS_NODE *aNode, PNS_SEGMENT *aLatestSeg )
|
|
{
|
|
PNS_LINE *ourLine = aNode->AssembleLine(aLatestSeg);
|
|
PNS_NODE *cleaned = aNode->Branch();
|
|
PNS_JOINT a, b;
|
|
vector<PNS_LINE *> lines;
|
|
|
|
|
|
cleaned->FindLineEnds (ourLine, a, b);
|
|
cleaned->FindLinesBetweenJoints( a, b, lines);
|
|
|
|
BOOST_FOREACH(PNS_LINE *line, lines)
|
|
{
|
|
if(! (line->ContainsSegment (aLatestSeg) ) )
|
|
{
|
|
cleaned->Remove(line);
|
|
}
|
|
}
|
|
|
|
return cleaned;
|
|
}
|
|
|
|
|
|
|
|
bool PNS_ROUTER::FixRoute(const VECTOR2I& aP, PNS_ITEM *aEndItem)
|
|
{
|
|
bool real_end = false;
|
|
|
|
PNS_LINE pl = m_placer->GetTrace();
|
|
const SHAPE_LINE_CHAIN& l = pl.GetCLine();
|
|
|
|
if(!l.SegmentCount())
|
|
return true;
|
|
|
|
VECTOR2I p_pre_last = l.CPoint(-1);
|
|
const VECTOR2I p_last = l.CPoint(-1);
|
|
DIRECTION_45 d_last (l.CSegment(-1));
|
|
|
|
if(l.PointCount() > 2)
|
|
p_pre_last = l.CPoint(-2);
|
|
|
|
if(aEndItem && m_currentNet >= 0 && m_currentNet == aEndItem->GetNet())
|
|
real_end = true;
|
|
|
|
int last = (real_end || m_placingVia) ? l.SegmentCount() : max(1, l.SegmentCount() - 1);
|
|
|
|
PNS_NODE *latest = m_placer->GetCurrentNode();
|
|
|
|
if(real_end)
|
|
splitAdjacentSegments(latest, aEndItem, aP);
|
|
|
|
PNS_SEGMENT *lastSeg = NULL;
|
|
for (int i = 0; i < last; i++)
|
|
{
|
|
const SEG& s = pl.GetCLine().CSegment(i);
|
|
PNS_SEGMENT *seg = new PNS_SEGMENT( s, m_currentNet );
|
|
seg->SetWidth(pl.GetWidth());
|
|
seg->SetLayer(m_currentLayer);
|
|
latest->Add(seg);
|
|
lastSeg = seg;
|
|
}
|
|
|
|
if( pl.EndsWithVia() )
|
|
latest->Add(pl.GetVia().Clone());
|
|
|
|
if(real_end)
|
|
latest = removeLoops( latest, lastSeg );
|
|
|
|
commitRouting(latest);
|
|
|
|
EraseView();
|
|
|
|
if(real_end)
|
|
{
|
|
m_state = IDLE;
|
|
//m_world->KillChildren();
|
|
} else {
|
|
|
|
m_state = ROUTE_TRACK;
|
|
m_placer->SetInitialDirection(d_last);
|
|
m_currentStart = m_placingVia ? p_last : p_pre_last;
|
|
|
|
if(m_placingVia)
|
|
m_currentLayer = NextCopperLayer(true);
|
|
|
|
m_placer->StartPlacement(m_currentStart, m_currentNet, m_currentWidth, m_currentLayer);
|
|
|
|
m_startsOnVia = m_placingVia;
|
|
m_placingVia = false;
|
|
|
|
}
|
|
|
|
|
|
return real_end;
|
|
}
|
|
|
|
void PNS_ROUTER::StopRouting()
|
|
{
|
|
if(!RoutingInProgress())
|
|
return;
|
|
|
|
//highlightCurrent(false);
|
|
|
|
EraseView();
|
|
|
|
m_state = IDLE;
|
|
m_world->KillChildren();
|
|
}
|
|
|
|
void PNS_ROUTER::FlipPosture()
|
|
{
|
|
if(m_placer->GetTail().GetCLine().SegmentCount() == 0)
|
|
{
|
|
m_start_diagonal = !m_start_diagonal;
|
|
m_placer->SetInitialDirection(m_start_diagonal ? DIRECTION_45(DIRECTION_45::NE) : DIRECTION_45(DIRECTION_45::N));
|
|
} else
|
|
m_placer->FlipPosture();
|
|
|
|
Move(m_currentEnd, NULL);
|
|
}
|
|
|
|
void PNS_ROUTER::SwitchLayer(int layer)
|
|
{
|
|
switch(m_state)
|
|
{
|
|
case IDLE:
|
|
m_currentLayer = layer;
|
|
break;
|
|
case ROUTE_TRACK:
|
|
if(m_startsOnVia)
|
|
{
|
|
m_currentLayer = layer;
|
|
m_placer->StartPlacement(m_currentStart, m_currentNet, m_currentWidth, m_currentLayer);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void PNS_ROUTER::ToggleViaPlacement ()
|
|
{
|
|
if(m_state == ROUTE_TRACK)
|
|
{
|
|
m_placingVia = !m_placingVia;
|
|
m_placer->AddVia(m_placingVia, m_currentViaDiameter, m_currentViaDrill);
|
|
}
|
|
}
|
|
|