Initial version of the P&S router. Buggy and crappy.

parent 74f42a62
include_directories(BEFORE ${INC_BEFORE})
include_directories(
./
../
../../include
../../pcbnew
../../polygon
${INC_AFTER}
)
set(PCBNEW_PNS_SRCS
direction.h
pns_via.h
pns_routing_settings.h
pns_shove.cpp
pns_line.cpp
pns_utils.h
pns_layerset.h
trace.h
pns_line.h
pns_walkaround.cpp
pns_node.h
pns_line_placer.cpp
pns_utils.cpp
pns_solid.h
pns_item.cpp
pns_via.cpp
pns_node.cpp
pns_solid.cpp
pns_line_placer.h
pns_optimizer.h
pns_walkaround.h
pns_shove.h
pns_router.h
pns_router.cpp
pns_index.h
pns_item.h
pns_optimizer.cpp
pns_joint.h
pns_segment.h
pns_itemset.h
pns_itemset.cpp
router_tool.cpp
router_tool.h
router_preview_item.cpp
router_preview_item.h
)
add_library(pnsrouter STATIC ${PCBNEW_PNS_SRCS})
/*
* 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/>.
*/
#ifndef __DIRECTION_H
#define __DIRECTION_H
#include <geometry/seg.h>
#include <geometry/shape_line_chain.h>
/**
* Class DIRECTION_45.
* Represents route directions & corner angles in a 45-degree metric.
*/
class DIRECTION_45
{
public:
/**
* Enum Directions
* Represents available directions - there are 8 of them, as on a rectilinear map (north = up) +
* an extra undefined direction, reserved for traces that don't respect 45-degree routing regime.
*/
enum Directions {
N = 0,
NE = 1,
E = 2,
SE = 3,
S = 4,
SW = 5,
W = 6,
NW = 7,
UNDEFINED = -1
};
/**
* Enum AngleType
* Represents kind of angle formed by vectors heading in two DIRECTION_45s.
*/
enum AngleType {
ANG_OBTUSE = 0x1,
ANG_RIGHT = 0x2,
ANG_ACUTE = 0x4,
ANG_STRAIGHT = 0x8,
ANG_HALF_FULL = 0x10,
ANG_UNDEFINED = 0x20
};
DIRECTION_45(Directions aDir = UNDEFINED): m_dir(aDir) {};
/**
* Constructor
* @param aVec vector, whose direction will be translated into a DIRECTION_45.
*/
DIRECTION_45(const VECTOR2I& aVec)
{
construct(aVec);
}
/**
* Constructor
* @param aSeg segment, whose direction will be translated into a DIRECTION_45.
*/
DIRECTION_45(const SEG& aSeg)
{
construct( aSeg.b - aSeg.a );
}
/**
* Function Format()
* Formats the direction in a human readable word.
* @return name of the direction
*/
const std::string Format() const
{
switch(m_dir)
{
case N : return "north";
case NE : return "north-east";
case E : return "east";
case SE : return "south-east";
case S : return "south";
case SW : return "south-west";
case W : return "west";
case NW : return "north-west";
case UNDEFINED : return "undefined";
default: return "<Error>";
}
}
/**
* Function Opposite()
* Returns a direction opposite (180 degree) to (this)
* @return opposite direction
*/
DIRECTION_45 Opposite() const
{
if(m_dir == UNDEFINED)
return UNDEFINED;
const Directions OppositeMap[] = { S, SW, W, NW, N, NE, E, SE } ;
return OppositeMap[m_dir];
}
/**
* Function Angle()
* Returns the type of angle between directions (this) and aOther.
* @param aOther direction to compare angle with
*/
AngleType Angle(const DIRECTION_45& aOther) const
{
if(m_dir == UNDEFINED || aOther.m_dir == UNDEFINED)
return ANG_UNDEFINED;
int d = std::abs(m_dir - aOther.m_dir);
if(d == 1 || d == 7)
return ANG_OBTUSE;
else if(d == 2 || d == 6)
return ANG_RIGHT;
else if(d == 3 || d == 5)
return ANG_ACUTE;
else if(d == 4)
return ANG_HALF_FULL;
else
return ANG_STRAIGHT;
}
/**
* Function IsObtuse()
* @return true, when (this) forms an obtuse angle with aOther
*/
bool IsObtuse(const DIRECTION_45& aOther) const
{
return Angle(aOther) == ANG_OBTUSE;
}
/**
* Function IsDiagonal()
* Returns true if the direction is diagonal (e.g. North-West, South-East, etc)
* @return true, when diagonal.
*/
bool IsDiagonal() const
{
return (m_dir % 2) == 1;
}
/**
* Function BuildInitialTrace()
*
* Builds a 2-segment line chain between points aP0 and aP1 and following 45-degree routing
* regime. If aStartDiagonal is true, the trace starts with a diagonal segment.
* @param aP0 starting point
* @param aP1 ending point
* @param aStartDiagonal whether the first segment has to be diagonal
* @return the trace
*/
const SHAPE_LINE_CHAIN BuildInitialTrace(const VECTOR2I& aP0, const VECTOR2I &aP1, bool aStartDiagonal = false) const
{
int w = abs(aP1.x - aP0.x);
int h = abs(aP1.y - aP0.y);
int sw = sign(aP1.x - aP0.x);
int sh = sign(aP1.y - aP0.y);
VECTOR2I mp0, mp1;
// we are more horizontal than vertical?
if(w > h)
{
mp0 = VECTOR2I((w - h) * sw, 0); // direction: E
mp1 = VECTOR2I(h * sw, h * sh); // direction: NE
} else {
mp0 = VECTOR2I(0, sh * (h - w)); // direction: N
mp1 = VECTOR2I(sw * w, sh * w); // direction: NE
}
bool start_diagonal;
if(m_dir == UNDEFINED)
start_diagonal = aStartDiagonal;
else
start_diagonal = IsDiagonal();
SHAPE_LINE_CHAIN pl;
pl.Append(aP0);
if (start_diagonal)
pl.Append(aP0 + mp1);
else
pl.Append(aP0 + mp0);
pl.Append(aP1);
pl.Simplify();
return pl;
};
bool operator==(const DIRECTION_45& aOther) const
{
return aOther.m_dir == m_dir;
}
bool operator!=(const DIRECTION_45& aOther) const
{
return aOther.m_dir != m_dir;
}
const DIRECTION_45 Right() const
{
DIRECTION_45 r;
r.m_dir = (Directions) (m_dir + 1);
if(r.m_dir == NW)
r.m_dir = N;
return r;
}
private:
template <typename T> int sign(T val) const {
return (T(0) < val) - (val < T(0));
}
/**
* Function construct()
* Calculates the direction from a vector. If the vector's angle is not a multiple of 45
* degrees, the direction is rounded to the nearest octant.
* @param aVec our vector
*/
void construct(const VECTOR2I& aVec)
{
m_dir = UNDEFINED;
if(aVec.x == 0 && aVec.y == 0)
return;
double mag = 360.0 - (180.0 / M_PI * atan2 ((double) aVec.y, (double) aVec.x )) + 90.0;
if (mag >= 360.0)
mag -= 360.0;
if(mag < 0.0)
mag += 360.0;
m_dir = (Directions) ((mag + 22.5) / 45.0);
if(m_dir >= 8)
m_dir = (Directions) (m_dir - 8);
if(m_dir < 0)
m_dir = (Directions) (m_dir + 8);
return ;
if(aVec.y < 0)
{
if(aVec.x > 0)
m_dir = NE;
else if(aVec.x < 0)
m_dir = NW;
else
m_dir = N;
}
else if(aVec.y == 0)
{
if(aVec.x > 0)
m_dir = E;
else
m_dir = W;
}
else // aVec.y>0
{
if(aVec.x > 0)
m_dir = SE;
else if(aVec.x < 0)
m_dir = SW;
else
m_dir = S;
}
}
Directions m_dir; ///> our actual direction
};
#endif // __DIRECTION_H
/*
* 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/>.
*/
#ifndef __PNS_INDEX_H
#define __PNS_INDEX_H
#include <boost/foreach.hpp>
#include <boost/range/adaptor/map.hpp>
#include <list>
#include <geometry/shape_index.h>
#include "pns_item.h"
/**
* Class PNS_INDEX
*
* Custom spatial index, holding our board items and allowing for very fast searches. Items
* are assigned to separate R-Tree subundices depending on their type and spanned layers, reducing
* overlap and improving search time.
**/
class PNS_INDEX {
public:
typedef std::list<PNS_ITEM *> NetItemsList;
typedef SHAPE_INDEX<PNS_ITEM *> ItemShapeIndex;
typedef boost::unordered_set<PNS_ITEM *> ItemSet;
PNS_INDEX();
~PNS_INDEX();
void Add( PNS_ITEM *aItem );
void Remove ( PNS_ITEM *aItem );
void Replace ( PNS_ITEM *aOldItem, PNS_ITEM *aNewItem );
template<class Visitor>
int Query( const PNS_ITEM *aItem, int aMinDistance, Visitor &v);
template<class Visitor>
int Query( const SHAPE *aShape, int aMinDistance, Visitor &v);
void Clear();
NetItemsList* GetItemsForNet ( int aNet ) ;
ItemSet::iterator begin() { return m_allItems.begin(); }
ItemSet::iterator end() { return m_allItems.end(); }
bool Contains ( PNS_ITEM *aItem ) const {
return m_allItems.find(aItem) != m_allItems.end();
}
int Size() const { return m_allItems.size(); }
private:
static const int MaxSubIndices = 64;
static const int SI_Multilayer = 2;
static const int SI_SegDiagonal = 0;
static const int SI_SegStraight = 1;
static const int SI_Traces = 3;
static const int SI_PadsTop = 0;
static const int SI_PadsBottom = 1;
template<class Visitor>
int querySingle( int index, const SHAPE *aShape, int aMinDistance, Visitor &v);
ItemShapeIndex *getSubindex( const PNS_ITEM *aItem );
ItemShapeIndex *m_subIndices[ MaxSubIndices ];
std::map<int, NetItemsList> m_netMap;
ItemSet m_allItems;
};
PNS_INDEX::PNS_INDEX()
{
memset(m_subIndices, 0, sizeof(m_subIndices));
}
PNS_INDEX::ItemShapeIndex *PNS_INDEX::getSubindex(const PNS_ITEM *aItem )
{
int idx_n = -1;
const PNS_LAYERSET l = aItem->GetLayers();
switch(aItem->GetKind())
{
case PNS_ITEM::VIA:
idx_n = SI_Multilayer;
break;
case PNS_ITEM::SOLID:
{
if( l.IsMultilayer() )
idx_n = SI_Multilayer;
else if (l.Start() == 0) // fixme: use kicad layer codes
idx_n = SI_PadsTop;
else if (l.Start() == 15)
idx_n = SI_PadsBottom;
break;
}
case PNS_ITEM::SEGMENT:
case PNS_ITEM::LINE:
idx_n = SI_Traces + 2 * l.Start() + SI_SegStraight;
break;
default:
break;
}
assert(idx_n >= 0 && idx_n < MaxSubIndices);
if(!m_subIndices[idx_n])
m_subIndices[idx_n] = new ItemShapeIndex;
return m_subIndices[idx_n];
}
void PNS_INDEX::Add( PNS_ITEM *aItem )
{
ItemShapeIndex *idx = getSubindex(aItem);
idx->Add(aItem);
m_allItems.insert(aItem);
int net = aItem->GetNet();
if(net >= 0)
{
m_netMap[net].push_back(aItem);
}
}
void PNS_INDEX::Remove( PNS_ITEM *aItem )
{
ItemShapeIndex *idx = getSubindex(aItem);
idx->Remove(aItem);
m_allItems.erase (aItem);
int net = aItem->GetNet();
if(net >= 0 && m_netMap.find(net) != m_netMap.end())
m_netMap[net].remove(aItem);
}
void PNS_INDEX::Replace( PNS_ITEM *aOldItem, PNS_ITEM *aNewItem )
{
Remove(aOldItem);
Add(aNewItem);
}
template<class Visitor>
int PNS_INDEX::querySingle( int index, const SHAPE *aShape, int aMinDistance, Visitor &v)
{
if(!m_subIndices[index])
return 0;
return m_subIndices[index] -> Query(aShape, aMinDistance, v, false);
}
template<class Visitor>
int PNS_INDEX::Query( const PNS_ITEM *aItem, int aMinDistance, Visitor &v)
{
const SHAPE *shape = aItem->GetShape();
int total = 0;
total += querySingle(SI_Multilayer, shape, aMinDistance, v);
const PNS_LAYERSET layers = aItem->GetLayers();
if(layers.IsMultilayer())
{
total += querySingle(SI_PadsTop, shape, aMinDistance, v);
total += querySingle(SI_PadsBottom, shape, aMinDistance, v);
for(int i = layers.Start(); i <= layers.End(); ++i )
total += querySingle( SI_Traces + 2 * i + SI_SegStraight, shape, aMinDistance, v);
} else {
int l = layers.Start();
if(l == 0)
total += querySingle(SI_PadsTop, shape, aMinDistance, v);
else if(l == 15)
total += querySingle(SI_PadsBottom, shape, aMinDistance, v);
total += querySingle ( SI_Traces + 2 * l + SI_SegStraight, shape, aMinDistance, v);
}
return total;
}
template<class Visitor>
int PNS_INDEX::Query( const SHAPE *aShape, int aMinDistance, Visitor &v)
{
int total = 0;
for(int i = 0; i < MaxSubIndices; i++)
total += querySingle(i, aShape, aMinDistance, v);
return total;
}
void PNS_INDEX::Clear()
{
for(int i = 0; i < MaxSubIndices; ++i)
{
ItemShapeIndex *idx = m_subIndices[i];
if(idx)
delete idx;
m_subIndices[i] = NULL;
}
}
PNS_INDEX::~PNS_INDEX()
{
Clear();
}
PNS_INDEX::NetItemsList* PNS_INDEX::GetItemsForNet ( int aNet )
{
if(m_netMap.find(aNet) == m_netMap.end())
return NULL;
return &m_netMap[aNet];
}
#endif
/*
* 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 "pns_item.h"
#include "pns_line.h"
bool PNS_ITEM::collideSimple ( const PNS_ITEM *aOther, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) const
{
// same nets? no collision!
if( m_net == aOther->m_net )
return false;
// check if we are not on completely different layers first
if (!m_layers.Overlaps (aOther->m_layers))
return false;
return GetShape()->Collide ( aOther->GetShape(), aClearance );
// fixme: MTV
}
bool PNS_ITEM::Collide( const PNS_ITEM *aOther, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) const
{
if( collideSimple( aOther, aClearance, aNeedMTV, aMTV ) )
return true;
// special case for "head" line with a via attached at the end.
if( aOther->m_kind == LINE )
{
const PNS_LINE *line = static_cast<const PNS_LINE *> (aOther);
if(line -> EndsWithVia())
return collideSimple( &line->GetVia(), aClearance - line->GetWidth() / 2, aNeedMTV, aMTV );
}
return false;
}
const std::string PNS_ITEM::GetKindStr() const
{
switch(m_kind)
{
case LINE: return "line";
case SEGMENT: return "segment";
case VIA: return "via";
case JOINT: return "joint";
case SOLID: return "solid";
default: return "unknown";
}
}
PNS_ITEM::~PNS_ITEM()
{
}
\ No newline at end of file
/*
* 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/>.
*/
#ifndef __PNS_ITEM_H
#define __PNS_ITEM_H
#include <math/vector2d.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include "pns_layerset.h"
class BOARD_ITEM;
class PNS_NODE;
/**
* Class PNS_ITEM
*
* Base class for PNS router board items. Implements the shared properties of all PCB items -
* net, spanned layers, geometric shape & refererence to owning model.
*/
class PNS_ITEM
{
public:
static const int UnusedNet = INT_MAX;
///> Supported item types
enum PnsKind {
SOLID = 1,
LINE = 2,
JOINT = 4,
SEGMENT = 8,
VIA = 16,
ANY = 0xff
};
PNS_ITEM(PnsKind aKind)
{
m_net = UnusedNet;
m_movable = true;
m_kind = aKind;
m_parent = NULL;
m_world = NULL;
m_owner = NULL;
}
PNS_ITEM( const PNS_ITEM& aOther )
{
m_layers = aOther.m_layers;
m_net = aOther.m_net;
m_movable = aOther.m_movable;
m_kind = aOther.m_kind;
m_world = aOther.m_world;
m_parent = aOther.m_parent;
m_owner = NULL;
}
virtual ~PNS_ITEM();
virtual PNS_ITEM *Clone() const = 0;
///> Returns a convex polygon "hull" of a the item, that is used as the walkaround
/// path.
/// aClearance defines how far from the body of the item the hull should be,
/// aWalkaroundThickness is the width of the line that walks around this hull.
virtual const SHAPE_LINE_CHAIN Hull(int aClearance = 0, int aWalkaroundThickness = 0) const
{
return SHAPE_LINE_CHAIN();
};
PnsKind GetKind() const { return m_kind; }
bool OfKind( int aKind ) const { return (aKind & m_kind) != 0; }
const std::string GetKindStr() const;
///> Gets/Sets the corresponding parent object in the host application's model (pcbnew)
void SetParent(BOARD_ITEM *aParent) { m_parent = aParent; }
BOARD_ITEM *GetParent() const { return m_parent; }
///> Net accessors
int GetNet() const { return m_net; }
void SetNet(int aNet) { m_net = aNet; }
///> Layers accessors
const PNS_LAYERSET& GetLayers() const { return m_layers; }
void SetLayers ( const PNS_LAYERSET& aLayers ) { m_layers = aLayers; }
void SetLayer ( int aLayer ) { m_layers = PNS_LAYERSET (aLayer, aLayer); }
///> Ownership management. An item can belong to a single PNS_NODE or stay unowned.
void SetOwner (PNS_NODE *aOwner) { m_owner = aOwner; }
bool BelongsTo (PNS_NODE *aNode) const { return m_owner == aNode; }
PNS_NODE *GetOwner() const { return m_owner; }
///> Sets the world that is used for collision resolution.
void SetWorld (PNS_NODE *aWorld) { m_world = aWorld; }
PNS_NODE *GetWorld() const { return m_world; }
///> Collision function. Checks if the item aOther is closer to us than
/// aClearance and returns true if so. It can also calculate a minimum translation vector that resolves the
/// collision if needed.
virtual bool Collide( const PNS_ITEM *aOther, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) const;
///> A shortcut without MTV calculation
bool Collide( const PNS_ITEM *aOther, int aClearance ) const
{
VECTOR2I dummy;
return Collide(aOther, aClearance, false, dummy);
}
///> Returns the geometric shape of the item
virtual const SHAPE* GetShape() const {
return NULL;
}
private:
bool collideSimple ( const PNS_ITEM *aOther, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) const;
protected:
PnsKind m_kind;
BOARD_ITEM *m_parent;
PNS_NODE *m_world;
PNS_NODE *m_owner;
PNS_LAYERSET m_layers;
bool m_movable;
int m_net;
};
#endif // __PNS_ITEM_H
/*
* 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 <boost/foreach.hpp>
#include "pns_itemset.h"
PNS_ITEMSET::PNS_ITEMSET()
{
}
PNS_ITEMSET::~PNS_ITEMSET()
{
}
PNS_ITEMSET& PNS_ITEMSET::FilterLayers ( int aStart, int aEnd )
{
ItemVector newItems;
PNS_LAYERSET l;
if(aEnd < 0)
l = PNS_LAYERSET(aStart);
else
l = PNS_LAYERSET(aStart, aEnd);
BOOST_FOREACH( PNS_ITEM *item, m_items )
if(item->GetLayers(). Overlaps ( l ))
newItems.push_back(item);
m_items = newItems;
return *this;
}
PNS_ITEMSET& PNS_ITEMSET::FilterKinds ( int aKindMask )
{
ItemVector newItems;
BOOST_FOREACH( PNS_ITEM *item, m_items )
if(item->GetKind() & aKindMask )
newItems.push_back(item);
m_items = newItems;
return *this;
}
PNS_ITEMSET& PNS_ITEMSET::FilterNet ( int aNet )
{
ItemVector newItems;
BOOST_FOREACH( PNS_ITEM *item, m_items )
if(item->GetNet() == aNet)
newItems.push_back(item);
m_items = newItems;
return *this;
}
/*
* 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/>.
*/
#ifndef __PNS_ITEMSET_H
#define __PNS_ITEMSET_H
#include <vector>
#include "pns_item.h"
/**
* Class PNS_ITEMSET
*
* Holds a list of board items, that can be filtered against net, kinds, layers, etc.
**/
class PNS_ITEMSET
{
public:
typedef std::vector<PNS_ITEM *> ItemVector;
PNS_ITEMSET();
~PNS_ITEMSET();
ItemVector& Items() { return m_items; }
PNS_ITEMSET& FilterLayers ( int aStart, int aEnd = -1 );
PNS_ITEMSET& FilterKinds ( int aKindMask );
PNS_ITEMSET& FilterNet ( int aNet );
int Size() { return m_items.size(); }
void Add(PNS_ITEM *item)
{
m_items.push_back(item);
}
PNS_ITEM *Get( int index ) const { return m_items[index]; }
private:
ItemVector m_items;
};
#endif
/*
* 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/>.
*/
#ifndef __PNS_JOINT_H
#define __PNS_JOINT_H
#include <vector>
#include <boost/functional/hash.hpp>
#include <math/vector2d.h>
#include "pns_item.h"
#include "pns_segment.h"
/**
* Class PNS_JOINT
*
* Represents a 2D point on a given set of layers and belonging to a certain net,
* that links together a number of board items.
* A hash table of joints is used by the router to follow connectivity between the items.
**/
class PNS_JOINT : public PNS_ITEM
{
public:
typedef std::vector<PNS_ITEM *> LinkedItems;
///> joints are hashed by their position, layers and net. Linked items are, obviously, not hashed
struct HashTag {
VECTOR2I pos;
int net;
};
PNS_JOINT():
PNS_ITEM(JOINT) {}
PNS_JOINT(const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet = -1):
PNS_ITEM(JOINT)
{
m_tag.pos = aPos;
m_tag.net = aNet;
m_layers = aLayers;
}
PNS_JOINT(const PNS_JOINT& b):
PNS_ITEM(JOINT)
{
m_layers = b.m_layers;
m_tag.pos = b.m_tag.pos;
m_tag.net = b.m_tag.net;
m_linkedItems = b.m_linkedItems;
m_layers = b.m_layers;
}
PNS_ITEM *Clone() const
{
assert(false);
return NULL;
}
///> returns true if the joint is a trivial line corner, connecting two segments of the same net, on the same layer.
bool IsLineCorner() const
{
if(m_linkedItems.size() != 2)
return false;
if( m_linkedItems[0]->GetKind() != SEGMENT || m_linkedItems[1]->GetKind() != SEGMENT )
return false;
PNS_SEGMENT *seg1 = static_cast<PNS_SEGMENT *> (m_linkedItems[0]);
PNS_SEGMENT *seg2 = static_cast<PNS_SEGMENT *> (m_linkedItems[1]);
// joints between segments of different widths are not trivial.
return (seg1->GetWidth() == seg2->GetWidth());
}
///> Links the joint to a given board item (when it's added to the PNS_NODE)
void Link ( PNS_ITEM *aItem )
{
LinkedItems::iterator f = std::find(m_linkedItems.begin(), m_linkedItems.end(), aItem);
if(f != m_linkedItems.end())
return;
m_linkedItems.push_back(aItem);
}
///> Unlinks a given board item from the joint (upon its removal from a PNS_NODE)
///> Returns true if the joint became dangling after unlinking.
bool Unlink ( PNS_ITEM *aItem )
{
LinkedItems::iterator f = std::find(m_linkedItems.begin(), m_linkedItems.end(), aItem);
if(f != m_linkedItems.end())
m_linkedItems.erase(f);
return (m_linkedItems.size() == 0);
}
///> For trivial joints, returns the segment adjacent to (aCurrent). For non-trival ones, returns
///> NULL, indicating the end of line.
PNS_SEGMENT* NextSegment( PNS_SEGMENT* aCurrent) const
{
if(!IsLineCorner())
return NULL;
return static_cast<PNS_SEGMENT *> (m_linkedItems [ m_linkedItems[0] == aCurrent ? 1 : 0 ] );
}
/// trivial accessors
const HashTag& GetTag() const { return m_tag; }
const VECTOR2I& GetPos() const { return m_tag.pos; }
int GetNet() const { return m_tag.net; }
LinkedItems & GetLinkList() { return m_linkedItems; };
///> Returns the number of linked items of types listed in aMask.
int LinkCount( int aMask = -1 ) const
{
int n = 0;
for(LinkedItems::const_iterator i = m_linkedItems.begin(); i!= m_linkedItems.end(); ++i)
if( (*i)->GetKind() & aMask )
n++;
return n;
}
void Dump() const;
bool operator==(const PNS_JOINT& rhs) const
{
return m_tag.pos == rhs.m_tag.pos && m_tag.net == rhs.m_tag.net;
}
void Merge( const PNS_JOINT& aJoint )
{
if(!Overlaps(aJoint))
return;
m_layers.Merge (aJoint.m_layers);
// fixme: duplicate links (?)
for(LinkedItems::const_iterator i =aJoint.m_linkedItems.begin(); i!=aJoint.m_linkedItems.end();++i)
m_linkedItems.push_back(*i);
}
bool Overlaps(const PNS_JOINT& rhs) const
{
return m_tag.pos == rhs.m_tag.pos && m_tag.net == rhs.m_tag.net && m_layers.Overlaps(rhs.m_layers);
}
private:
///> hash tag for unordered_multimap
HashTag m_tag;
///> list of items linked to this joint
LinkedItems m_linkedItems;
};
// hash function & comparison operator for boost::unordered_map<>
inline bool operator==(PNS_JOINT::HashTag const& p1, PNS_JOINT::HashTag const& p2)
{
return p1.pos == p2.pos && p1.net == p2.net;
}
inline std::size_t hash_value(PNS_JOINT::HashTag const& p)
{
std::size_t seed = 0;
boost::hash_combine(seed, p.pos.x);
boost::hash_combine(seed, p.pos.y);
boost::hash_combine(seed, p.net);
return seed;
}
#endif // __PNS_JOINT_H
/*
* 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/>.
*/
#ifndef __PNS_LAYERSET_H
#define __PNS_LAYERSET_H
#include <algorithm>
/**
* Class PNS_LAYERSET
*
* Represents a contiguous set of PCB layers.
*/
class PNS_LAYERSET
{
public:
PNS_LAYERSET():
m_start(-1),
m_end(-1)
{};
PNS_LAYERSET (int aStart, int aEnd)
{
if(aStart > aEnd)
std::swap(aStart, aEnd);
m_start = aStart;
m_end = aEnd;
}
PNS_LAYERSET (int aLayer)
{
m_start = m_end = aLayer;
}
PNS_LAYERSET(const PNS_LAYERSET &b) :
m_start(b.m_start),
m_end (b.m_end)
{}
~PNS_LAYERSET () {};
const PNS_LAYERSET& operator= ( const PNS_LAYERSET& b)
{
m_start = b.m_start;
m_end = b.m_end;
return *this;
}
bool Overlaps ( const PNS_LAYERSET& aOther ) const
{
return m_end >= aOther.m_start && m_start <= aOther.m_end;
}
bool Overlaps ( const int aLayer ) const
{
return aLayer >= m_start && aLayer <= m_end;
}
bool IsMultilayer ( ) const
{
return m_start != m_end;
}
int Start() const {
return m_start;
}
int End() const {
return m_end;
}
void Merge ( const PNS_LAYERSET& aOther )
{
if(m_start < 0 || m_end < 0)
{
m_start = aOther.m_start;
m_end = aOther.m_end;
return;
}
if(aOther.m_start < m_start)
m_start = aOther.m_start;
if(aOther.m_end > m_end)
m_end = aOther.m_end;
}
///> Shortcut for comparisons/overlap tests
static PNS_LAYERSET All()
{
return PNS_LAYERSET(0, 256);
}
private:
int m_start;
int m_end;
};
#endif // __PNS_LAYERSET_H
/*
* 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 <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <math/vector2d.h>
#include "pns_line.h"
#include "pns_node.h"
#include "pns_via.h"
#include "pns_utils.h"
#include "pns_router.h"
using namespace std;
using boost::optional;
PNS_LINE *PNS_LINE::Clone() const
{
PNS_LINE *l = new PNS_LINE();
l->m_line = m_line;
l->m_width = m_width;
l->m_layers = m_layers;
l->m_net = m_net;
l->m_movable = m_movable;
l->m_segmentRefs = NULL;
l->m_hasVia = m_hasVia;
l->m_via = m_via;
return l;
}
PNS_LINE *PNS_LINE::CloneProperties() const
{
PNS_LINE *l = new PNS_LINE();
l->m_width = m_width;
l->m_layers = m_layers;
l->m_net = m_net;
l->m_movable = m_movable;
return l;
}
PNS_SEGMENT *PNS_SEGMENT::Clone() const
{
PNS_SEGMENT *s = new PNS_SEGMENT;
s->m_width = m_width;
s->m_net = m_net;
s->m_shape = m_shape;
s->m_layers = m_layers;
return s; //assert(false);
}
#if 1
bool PNS_LINE::MergeObtuseSegments( )
{
int step = m_line.PointCount() - 3;
int iter = 0;
int segs_pre = m_line.SegmentCount();
if(step < 0)
return false;
SHAPE_LINE_CHAIN current_path (m_line);
while(1)
{
iter++;
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if(step > max_step)
step = max_step;
if(step < 2)
{
m_line = current_path;
return current_path.SegmentCount() < segs_pre;
}
bool found_anything = false;
int n = 0;
while (n < n_segs - step)
{
const SEG s1 = current_path.CSegment(n);
const SEG s2 = current_path.CSegment(n + step);
SEG s1opt, s2opt;
if (DIRECTION_45(s1).IsObtuse(DIRECTION_45(s2)))
{
VECTOR2I ip = *s1.IntersectLines(s2);
if(s1.Distance(ip) <= 1 || s2.Distance(ip) <= 1)
{
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
} else {
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
}
if (DIRECTION_45(s1opt).IsObtuse(DIRECTION_45(s2opt)))
{
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1opt.a);
opt_path.Append(s1opt.b);
opt_path.Append(s2opt.b);
PNS_LINE opt_track (*this, opt_path);
if(!m_world->CheckColliding(&opt_track, PNS_ITEM::ANY))
{
current_path.Replace(s1.Index() + 1, s2.Index(), ip);
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++;
}
if(!found_anything)
{
if( step <= 2 )
{
m_line = current_path;
return m_line.SegmentCount() < segs_pre;
}
step --;
}
}
return m_line.SegmentCount() < segs_pre;
}
bool PNS_LINE::MergeSegments( )
{
int step = m_line.PointCount() - 3;
int iter = 0;
int segs_pre = m_line.SegmentCount();
if(step < 0)
return false;
SHAPE_LINE_CHAIN current_path (m_line);
while(1)
{
iter++;
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if(step > max_step)
step = max_step;
if(step < 2)
{
m_line = current_path;
return current_path.SegmentCount() < segs_pre;
}
bool found_anything = false;
int n = 0;
while (n < n_segs - step)
{
const SEG s1 = current_path.CSegment(n);
const SEG s2 = current_path.CSegment(n + step);
SEG s1opt, s2opt;
if(n > 0)
{
SHAPE_LINE_CHAIN path_straight = DIRECTION_45().BuildInitialTrace(s1.a, s2.a, false);
SHAPE_LINE_CHAIN path_diagonal = DIRECTION_45().BuildInitialTrace(s1.a, s2.a, true);
}
if (DIRECTION_45(s1) == DIRECTION_45(s2))
{
if(s1.Collinear(s2))
{
//printf("Colinear: np %d step %d n1 %d n2 %d\n", n_segs, step, n, n+step);
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1.a);
opt_path.Append(s2.b);
PNS_LINE tmp (*this, opt_path);
if(!m_world->CheckColliding(&tmp, PNS_ITEM::ANY))
{
current_path.Remove(s1.Index() + 1, s2.Index());
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
else if (DIRECTION_45(s1).IsObtuse(DIRECTION_45(s2)))
{
VECTOR2I ip = *s1.IntersectLines(s2);
if(s1.Distance(ip) <= 1 || s2.Distance(ip) <= 1)
{
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
} else {
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
}
if (DIRECTION_45(s1opt).IsObtuse(DIRECTION_45(s2opt)))
{
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1opt.a);
opt_path.Append(s1opt.b);
opt_path.Append(s2opt.b);
PNS_LINE opt_track (*this, opt_path);
if(!m_world->CheckColliding(&opt_track, PNS_ITEM::ANY))
{
current_path.Replace(s1.Index() + 1, s2.Index(), ip);
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++;
}
if(!found_anything)
{
if( step <= 2 )
{
m_line = current_path;
return m_line.SegmentCount() < segs_pre;
}
step --;
}
}
return m_line.SegmentCount() < segs_pre;
}
#endif
int PNS_LINE::CountCorners(int aAngles)
{
int count = 0;
for(int i = 0; i < m_line.SegmentCount() - 1; i ++)
{
const SEG seg1 = m_line.CSegment(i);
const SEG seg2 = m_line.CSegment(i + 1);
const DIRECTION_45 dir1(seg1);
const DIRECTION_45 dir2(seg2);
DIRECTION_45::AngleType a = dir1.Angle(dir2);
if(a & aAngles)
count++;
}
return count;
}
//#define DUMP_TEST_CASES
// fixme: damn f*****g inefficient and incredibly crappily written
void PNS_LINE::NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPrePath,
SHAPE_LINE_CHAIN& aWalkaroundPath,
SHAPE_LINE_CHAIN& aPostPath,
bool aCw ) const
{
typedef SHAPE_LINE_CHAIN::Intersection Intersection;
SHAPE_LINE_CHAIN l_orig(m_line);
SHAPE_LINE_CHAIN l_hull;
vector<bool> outside, on_edge, inside;
SHAPE_LINE_CHAIN path;
vector<Intersection> isects;
// don't calculate walkaround for empty lines
if(m_line.PointCount() < 2)
return;
#ifdef DUMP_TEST_CASES
printf("%s\n", m_line.Format().c_str());
printf("%s\n", aObstacle.Format().c_str());
#endif
aObstacle.Intersect(m_line, isects);
//printf("NewWalk intersectiosn :%d\n" ,isects.size());
if( !aCw )
l_hull = aObstacle.Reverse();
else
l_hull = aObstacle;
BOOST_FOREACH( Intersection isect, isects )
{
l_orig.Split(isect.p);
l_hull.Split(isect.p);
}
#ifdef DUMP_TEST_CASES
printf("%s\n", m_line.Format().c_str());
printf("%s\n", aObstacle.Format().c_str());
printf("%s\n", l_orig.Format().c_str());
printf("%s\n", l_hull.Format().c_str());
#endif
//printf("Pts: line %d hull %d\n", l_orig.PointCount(), l_hull.PointCount());
int first_post = -1;
int last_pre = -1;
for (int i = 0; i < l_orig.PointCount(); i++)
{
int ei = l_hull.Find(l_orig.CPoint(i)) ;
bool edge = ei >= 0;
bool in = l_hull.PointInside( l_orig.CPoint(i)) && !edge;
bool out = !( in || edge);
outside.push_back( out );
on_edge.push_back( edge );
inside.push_back( in );
}
for(int i = l_orig.PointCount() - 1; i >= 1; i --)
if(inside[i] && outside[i-1])
{
SHAPE_LINE_CHAIN::Intersections ips;
l_hull.Intersect( SEG( l_orig.CPoint(i), l_orig.CPoint(i - 1) ), ips );
l_orig.Remove(i, -1);
l_orig.Append(ips[0].p);
break;
}
else if (inside[i] && on_edge[i-1])
{
l_orig.Remove(i, -1);
//n = i;
} else if(!inside[i])
break;
if(!outside.size() && on_edge.size() < 2)
return;
for (int i = 0; i < l_orig.PointCount(); i++)
{
const VECTOR2I p = l_orig.Point(i);
if( outside[i] || (on_edge[i] && i == (l_orig.PointCount() - 1)))
{
if(last_pre < 0)
aPrePath.Append(p);
path.Append(p);
}
else if ( on_edge[i] )
{
int li = -1;
if(last_pre < 0)
{
aPrePath.Append(p);
last_pre = path.PointCount();
}
if( i == l_orig.PointCount() - 1 || outside[i+1])
{
path.Append(p);
}
else
{
int vi2 = l_hull.Find( l_orig.CPoint(i) );
path.Append(l_hull.CPoint(vi2));
for(int j = (vi2 + 1) % l_hull.PointCount(); j != vi2; j = (j + 1) % l_hull.PointCount())
{
path.Append(l_hull.CPoint(j));
li = l_orig.Find(l_hull.CPoint(j));
if(li >= 0 && (li == (l_orig.PointCount() - 1) || outside[li+1]))
break;
}
if(li >= 0) {
if(i >= li)
break;
else {
i = li;
}
}
}
first_post = path.PointCount() - 1;
}
}
if(last_pre < 0 && first_post < 0)
return;
aWalkaroundPath = path.Slice( last_pre, first_post );
if(first_post >= 0)
aPostPath = path.Slice( first_post, -1 );
}
bool PNS_LINE::onEdge(const SHAPE_LINE_CHAIN &obstacle, VECTOR2I p, int& ei, bool& is_vertex) const
{
int vtx = obstacle.Find(p);
if(vtx >= 0)
{
ei = vtx;
is_vertex =true;
return true;
}
for(int s = 0; s< obstacle.SegmentCount(); s++)
{
if(obstacle.CSegment(s).Contains(p))
{
ei = s;
is_vertex = false;
return true;
}
}
return false;
}
bool PNS_LINE::walkScan(const SHAPE_LINE_CHAIN &line, const SHAPE_LINE_CHAIN &obstacle, bool reverse, VECTOR2I &ip, int& index_o, int& index_l, bool& is_vertex) const
{
int sc = line.SegmentCount();
for(int i = 0; i < line.SegmentCount(); i++)
{
printf("check-seg rev %d %d/%d %d\n",reverse, i, sc, sc - 1 - i);
SEG tmp = line.CSegment(reverse ? sc - 1 - i : i);
SEG s (tmp.a, tmp.b);
if(reverse)
{
s.a = tmp.b;
s.b = tmp.a;
}
if(onEdge(obstacle, s.a, index_o, is_vertex))
{
index_l = (reverse ? sc-1-i : i);
ip = s.a;
printf("vertex %d on-%s %d\n", index_l, is_vertex?"vertex":"edge",index_o);
return true;
}
if(onEdge(obstacle, s.b, index_o, is_vertex))
{
index_l = (reverse? sc-1-i-1 : i + 1);
ip = s.b;
printf("vertex %d on-%s %d\n", index_l, is_vertex?"vertex":"edge",index_o);
return true;
}
SHAPE_LINE_CHAIN::Intersections ips;
int n_is = obstacle.Intersect ( s, ips );
if (n_is > 0)
{
index_o = ips[0].our.Index();
index_l = reverse ?sc-1-i:i;
printf("segment-%d intersects edge-%d\n", index_l, index_o);
ip = ips[0].p;
return true;
}
}
return false;
}
bool PNS_LINE::Walkaround(SHAPE_LINE_CHAIN obstacle, SHAPE_LINE_CHAIN &pre, SHAPE_LINE_CHAIN &walk, SHAPE_LINE_CHAIN &post, bool cw) const
{
const SHAPE_LINE_CHAIN &line = GetCLine();
VECTOR2I ip_start;
int index_o_start, index_l_start;
VECTOR2I ip_end;
int index_o_end, index_l_end;
bool is_vertex_start, is_vertex_end;
if(line.SegmentCount() < 1)
return false;
if(obstacle.PointInside(line.CPoint(0)) || obstacle.PointInside(line.CPoint(-1)))
return false;
// printf("forward:\n");
bool found = walkScan(line, obstacle, false, ip_start, index_o_start, index_l_start, is_vertex_start);
//printf("reverse:\n");
found |= walkScan(line, obstacle, true, ip_end, index_o_end, index_l_end, is_vertex_end);
if(!found || ip_start == ip_end)
{
pre = line;
return true;
}
pre = line.Slice ( 0, index_l_start );
pre.Append (ip_start);
walk.Clear();
walk.Append(ip_start);
if(cw)
{
int is = (index_o_start + 1) % obstacle.PointCount();
int ie = (is_vertex_end ? index_o_end : index_o_end + 1) % obstacle.PointCount();
while(1)
{
printf("is %d\n", is);
walk.Append(obstacle.CPoint(is));
if(is == ie)
break;
is ++;
if (is == obstacle.PointCount() )
is = 0;
}
} else {
int is = index_o_start;
int ie = (is_vertex_end ? index_o_end : index_o_end) % obstacle.PointCount();
while(1)
{
printf("is %d\n", is);
walk.Append(obstacle.CPoint(is));
if(is == ie)
break;
is --;
if ( is < 0 )
is = obstacle.PointCount() - 1;
}
}
walk.Append(ip_end);
post.Clear();
post.Append(ip_end);
post.Append(line.Slice (is_vertex_end ? index_l_end : index_l_end + 1 , -1));
//for(int i = (index_o_start + 1) % obstacle.PointCount();
// i != (index_o_end + 1) % obstacle.PointCount(); i=(i+1) % obstacle.PointCount())
//{
//printf("append %d\n", i);
//walk.Append(obstacle.CPoint(i));
//}
return true;
}
void PNS_LINE::NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const
{
SHAPE_LINE_CHAIN walk, post;
NewWalkaround(aObstacle, aPath, walk, post, aCw);
aPath.Append(walk);
aPath.Append(post);
aPath.Simplify();
}
void PNS_LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const
{
SHAPE_LINE_CHAIN walk, post;
Walkaround(aObstacle, aPath, walk, post, aCw);
aPath.Append(walk);
aPath.Append(post);
aPath.Simplify();
}
const SHAPE_LINE_CHAIN PNS_SEGMENT::Hull(int aClearance, int aWalkaroundThickness) const
{
int d = aClearance + 10;
int x = (int) (2.0 / (1.0 + M_SQRT2) * d) + 2;
const VECTOR2I a = m_shape.CPoint(0);
const VECTOR2I b = m_shape.CPoint(1);
VECTOR2I dir = b - a;
VECTOR2I p0 = dir.Perpendicular().Resize(d);
VECTOR2I ds = dir.Perpendicular().Resize(x / 2);
VECTOR2I pd = dir.Resize(x / 2);
VECTOR2I dp = dir.Resize(d);
SHAPE_LINE_CHAIN s;
s.SetClosed( true );
s.Append(b + p0 + pd);
s.Append(b + dp + ds);
s.Append(b + dp - ds);
s.Append(b - p0 + pd);
s.Append(a - p0 - pd);
s.Append(a - dp - ds);
s.Append(a - dp + ds);
s.Append(a + p0 - pd);
// make sure the hull outline is always clockwise
if(s.CSegment(0).Side(a) < 0)
return s.Reverse();
else
return s;
}
bool PNS_LINE::Is45Degree()
{
for(int i = 0; i < m_line.SegmentCount(); i++)
{
const SEG &s = m_line.CSegment(i);
double angle = 180.0 / M_PI * atan2((double)s.b.y - (double)s.a.y, (double)s.b.x - (double)s.a.x);
if(angle < 0)
angle+=360.0;
double angle_a = fabs(fmod(angle, 45.0));
if(angle_a > 1.0 && angle_a < 44.0)
return false;
}
return true;
}
const PNS_LINE PNS_LINE::ClipToNearestObstacle( PNS_NODE *aNode ) const
{
PNS_LINE l (*this);
PNS_NODE::OptObstacle obs = aNode->NearestObstacle ( &l );
if(obs)
{
l.RemoveVia();
int p = l.GetLine().Split(obs -> ip_first);
l.GetLine().Remove(p + 1, -1);
}
return l;
}
void PNS_LINE::ShowLinks()
{
if(!m_segmentRefs)
{
printf("line %p: no links\n", this);
return;
}
printf("line %p: %d linked segs\n", this, m_segmentRefs->size());
for (int i= 0; i<(int)m_segmentRefs->size(); i++) printf("seg %d: %p\n", i, (*m_segmentRefs)[i]) ;
}
/*
* 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/>.
*/
#ifndef __PNS_LINE_H
#define __PNS_LINE_H
#include <math/vector2d.h>
#include <geometry/seg.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include "direction.h"
#include "pns_item.h"
#include "pns_via.h"
class PNS_NODE;
class PNS_SEGMENT;
class PNS_VIA;
/**
* Class PNS_LINE
*
* Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,
* junctions between multiple traces or two traces different widths and combinations of these).
* PNS_LINEs are NOT stored in the model (PNS_NODE) - instead, they are assembled on-the-fly, based on
* a via/pad/segment that belongs/begins them.
*
* PNS_LINEs can be either loose (consisting of segments that do not belong to any PNS_NODE) or owned (with segments
* taken from a PNS_NODE) - these are returned by PNS_NODE::AssembleLine and friends.
*
* A PNS_LINE may have a PNS_VIA attached at its and - this is used by via dragging/force propagation stuff.
*/
class PNS_LINE : public PNS_ITEM
{
public:
typedef std::vector<PNS_SEGMENT *> LinkedSegments;
PNS_LINE ():
PNS_ITEM(LINE)
{
m_segmentRefs = NULL;
m_hasVia = false;
m_affectedRangeStart = -1;
};
PNS_LINE (int aLayer, int aWidth, const SHAPE_LINE_CHAIN& aLine) :
PNS_ITEM(LINE)
{
m_line = aLine;
m_width = aWidth;
m_segmentRefs = NULL;
m_hasVia = false;
m_affectedRangeStart = -1;
SetLayer(aLayer);
}
PNS_LINE(const PNS_LINE& aOther) :
PNS_ITEM(aOther),
m_line(aOther.m_line),
m_width(aOther.m_width)
{
m_net = aOther.m_net;
m_movable = aOther.m_movable;
m_world = aOther.m_world;
m_layers = aOther.m_layers;
m_segmentRefs = NULL;
m_via = aOther.m_via;
m_hasVia = aOther.m_hasVia;
m_affectedRangeStart = -1;
}
/**
* Constructor
* copies properties (net, layers from a base line), and replaces the shape
* by aLine
**/
PNS_LINE(const PNS_LINE& aBase, const SHAPE_LINE_CHAIN& aLine) :
PNS_ITEM(aBase),
m_line(aLine),
m_width(aBase.m_width)
{
m_net = aBase.m_net;
m_layers = aBase.m_layers;
m_segmentRefs = NULL;
m_hasVia = false;
m_affectedRangeStart = -1;
}
~PNS_LINE ()
{
if(m_segmentRefs)
delete m_segmentRefs;
};
virtual PNS_LINE *Clone() const ;
///> clones the line without cloning the shape (just the properties - net, width, layers, etc.)
PNS_LINE *CloneProperties() const ;
int GetLayer() const { return GetLayers().Start(); }
///> Geometry accessors
void SetShape(const SHAPE_LINE_CHAIN& aLine) { m_line = aLine; }
const SHAPE* GetShape() const { return &m_line; }
SHAPE_LINE_CHAIN& GetLine() { return m_line; }
const SHAPE_LINE_CHAIN& GetCLine() const { return m_line; }
///> Width accessors
void SetWidth( int aWidth ) { m_width = aWidth; }
int GetWidth () const { return m_width; }
///> Links a segment from a PNS_NODE to this line, making it owned by the node
void LinkSegment(PNS_SEGMENT *aSeg)
{
if(!m_segmentRefs)
m_segmentRefs = new std::vector<PNS_SEGMENT *> ();
m_segmentRefs->push_back(aSeg);
}
///> Returns a list of segments from the owning node that constitute this line (or NULL if
///> the line is loose)
LinkedSegments* GetLinkedSegments()
{
return m_segmentRefs;
}
bool ContainsSegment (PNS_SEGMENT *aSeg) const
{
if (!m_segmentRefs)
return false;
return std::find( m_segmentRefs->begin(), m_segmentRefs->end(), aSeg) != m_segmentRefs->end();
}
///> Returns this line, but clipped to the nearest obstacle along, to avoid collision.
const PNS_LINE ClipToNearestObstacle( PNS_NODE *aNode ) const;
///> DEPRECATED optimization functions (moved to PNS_OPTIMIZER)
bool MergeObtuseSegments();
bool MergeSegments();
///> Returns the number of corners of angles specified by mask aAngles.
int CountCorners(int aAngles);
///> Calculates a line thightly wrapping a convex hull of an obstacle object (aObstacle).
///> aPrePath = path from origin to the obstacle
///> aWalkaroundPath = path around the obstacle
///> aPostPath = past from obstacle till the end
///> aCW = whether to walkaround in clockwise or counter-clockwise direction.
void NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPrePath,
SHAPE_LINE_CHAIN& aWalkaroundPath,
SHAPE_LINE_CHAIN& aPostPath,
bool aCw ) const;
void NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const;
bool Walkaround(SHAPE_LINE_CHAIN obstacle, SHAPE_LINE_CHAIN &pre, SHAPE_LINE_CHAIN &walk, SHAPE_LINE_CHAIN &post, bool cw) const;
void Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const;
bool Is45Degree();
///> Prints out all linked segments
void ShowLinks();
bool EndsWithVia() const { return m_hasVia; }
void AppendVia ( const PNS_VIA &aVia ) {
m_hasVia = true;
m_via = aVia;
m_via.SetNet ( m_net ) ;
}
void RemoveVia() { m_hasVia = false; }
const PNS_VIA& GetVia() const { return m_via; }
void SetAffectedRange ( int aStart, int aEnd )
{
m_affectedRangeStart = aStart;
m_affectedRangeEnd = aEnd;
}
void ClearAffectedRange ( )
{
m_affectedRangeStart = -1;
}
bool GetAffectedRange ( int& aStart, int& aEnd )
{
if(m_affectedRangeStart >= 0)
{
aStart = m_affectedRangeStart;
aEnd = m_affectedRangeEnd;
return true;
} else {
aStart = 0;
aEnd = m_line.PointCount();
return false;
}
}
private:
bool onEdge(const SHAPE_LINE_CHAIN &obstacle, VECTOR2I p, int& ei, bool& is_vertex) const;
bool walkScan(const SHAPE_LINE_CHAIN &line, const SHAPE_LINE_CHAIN &obstacle, bool reverse, VECTOR2I &ip, int& index_o, int& index_l, bool& is_vertex) const;
///> List of semgments in a PNS_NODE (PNS_ITEM::m_owner) that constitute this line.
LinkedSegments* m_segmentRefs;
///> Shape of the line
SHAPE_LINE_CHAIN m_line;
int m_width;
///> Via at the end and a flag indicating if it's enabled.
PNS_VIA m_via;
bool m_hasVia;
int m_affectedRangeStart;
int m_affectedRangeEnd;
};
#endif // __PNS_LINE_H
/*
* 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 <vector>
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include "trace.h"
#include "pns_node.h"
#include "pns_line_placer.h"
#include "pns_walkaround.h"
#include "pns_shove.h"
#include "pns_utils.h"
using namespace std;
using boost::optional;
PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_NODE *aWorld )
{
m_initial_direction = DIRECTION_45(DIRECTION_45::N);
m_follow_mouse = false;
m_smoothing_step = 100000;
m_smooth_mouse = false;
m_iteration = 0;
m_world = aWorld;
m_mode = RM_Smart;
m_follow_mouse = true;
m_shove = NULL;
};
PNS_LINE_PLACER::~PNS_LINE_PLACER()
{
if(m_shove)
delete m_shove;
}
void PNS_LINE_PLACER::ApplySettings ( const PNS_ROUTING_SETTINGS& aSettings )
{
m_follow_mouse = aSettings.m_followMouse;
m_mode = aSettings.m_routingMode;
m_walkaroundIterationLimit = aSettings.m_walkaroundIterationLimit;
m_smartPads = aSettings.m_smartPads;
}
void PNS_LINE_PLACER::StartPlacement(const VECTOR2I& aStart, int aNet, int aWidth, int aLayer )
{
m_direction = m_initial_direction;
TRACE(1, "world %p, intitial-direction %s layer %d\n", m_world % m_direction.Format().c_str() % aLayer);
m_head.SetNet(aNet);
m_tail.SetNet(aNet);
m_head.SetWidth(aWidth);
m_tail.SetWidth(aWidth);
m_head.GetLine().Clear();
m_tail.GetLine().Clear();
m_head.SetLayer(aLayer);
m_tail.SetLayer(aLayer);
m_iteration = 0;
m_p_start = aStart;
m_currentNode = m_world->Branch();
m_head.SetWorld(m_currentNode);
m_tail.SetWorld(m_currentNode);
//if(m_shove)
// delete m_shove;
m_shove = new PNS_SHOVE(m_currentNode);
m_placingVia = false;
}
void PNS_LINE_PLACER::SetInitialDirection(const DIRECTION_45& aDirection)
{
m_initial_direction = aDirection;
if(m_tail.GetCLine().SegmentCount() == 0)
m_direction = aDirection;
}
/**
* Function handleSelfIntersections()
*
* Checks if the head of the track intersects its tail. If so, cuts the tail up to the
* intersecting segment and fixes the head direction to match the last segment before the cut.
* @return true if the line has been changed.
*/
bool PNS_LINE_PLACER::handleSelfIntersections()
{
SHAPE_LINE_CHAIN::Intersections ips;
SHAPE_LINE_CHAIN& head = m_head.GetLine();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
// if there is no tail, there is nothing to intersect with
if(tail.PointCount() < 2)
return false;
tail.Intersect(head, ips);
// no intesection points - nothing to reduce
if (ips.empty())
return false;
int n = INT_MAX;
VECTOR2I ipoint;
// if there is more than one intersection, find the one that is
// closest to the beginning of the tail.
BOOST_FOREACH(SHAPE_LINE_CHAIN::Intersection i, ips)
{
if (i.our.Index() < n)
{
n = i.our.Index();
ipoint = i.p;
}
}
// ignore the point where head and tail meet
if(ipoint == head.CPoint(0) || ipoint == tail.CPoint(-1))
return false;
// Intersection point is on the first or the second segment: just start routing
// from the beginning
if (n < 2)
{
m_p_start = tail.Point(0);
m_direction = m_initial_direction;
tail.Clear();
head.Clear();
return true;
} else {
// Clip till the last tail segment before intersection.
// Set the direction to the one of this segment.
const SEG last = tail.CSegment(n - 1);
m_p_start = last.a;
m_direction = DIRECTION_45(last);
tail.Remove(n, -1);
return true;
}
return false;
}
/**
* Function handlePullback()
*
* Deals with pull-back: reduces the tail if head trace is moved backwards wrs
* to the current tail direction.
* @return true if the line has been changed.
*/
bool PNS_LINE_PLACER::handlePullback()
{
SHAPE_LINE_CHAIN& head = m_head.GetLine();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
int n = tail.PointCount();
if(n == 0)
return false;
else if (n == 1)
{
m_p_start = tail.CPoint(0);
tail.Clear();
return true;
}
DIRECTION_45 first_head (head.Segment(0));
DIRECTION_45 last_tail (tail.Segment(-1));
DIRECTION_45::AngleType angle = first_head.Angle(last_tail);
// case 1: we have a defined routing direction, and the currently computed head
// goes in different one.
bool pullback_1 = false;//(m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head);
// case 2: regardless of the current routing direction, if the tail/head extremities form
// an acute or right angle, reduce the tail by one segment (and hope that further iterations)
// will result with a cleaner trace
bool pullback_2 = (angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE);
if(pullback_1 || pullback_2)
{
const SEG last = tail.CSegment(-1);
m_direction = DIRECTION_45(last);
m_p_start = last.a;
TRACE(0, "Placer: pullback triggered [%d] [%s %s]", n % last_tail.Format().c_str() % first_head.Format().c_str());
// erase the last point in the tail, hoping that the next iteration will result with a head
// trace that starts with a segment following our current direction.
if(n < 2)
tail.Clear(); // don't leave a single-point tail
else
tail.Remove(-1, -1);
if( !tail.SegmentCount() )
m_direction = m_initial_direction;
return true;
}
return false;
}
/**
* Function reduceTail()
*
* Attempts to reduce the numer of segments in the tail by trying to replace a certain number
* of latest tail segments with a direct trace leading to aEnd that does not collide with anything.
* @param aEnd: current routing destination point.
* @return true if the line has been changed.
*/
bool PNS_LINE_PLACER::reduceTail(const VECTOR2I& aEnd)
{
SHAPE_LINE_CHAIN& head = m_head.GetLine();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
int n = tail.SegmentCount();
// Don't attempt this for too short tails
if (n < 2)
return false;
// Start from the segment farthest from the end of the tail
//int start_index = std::max(n - 1 - ReductionDepth, 0);
DIRECTION_45 new_direction;
VECTOR2I new_start;
int reduce_index = -1;
DIRECTION_45 head_dir ( head.Segment(0) );
for(int i = tail.SegmentCount() - 1; i >= 0; i--)
{
const SEG s = tail.CSegment(i);
DIRECTION_45 dir (s);
// calculate a replacement route and check if it matches the direction of the segment to be replaced
SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace(s.a, aEnd);
PNS_LINE tmp (m_tail, replacement);
if (m_currentNode->CheckColliding(&tmp, PNS_ITEM::ANY))
break;
if(DIRECTION_45(replacement.Segment(0)) == dir)
{
new_start = s.a;
new_direction = dir;
reduce_index = i;
}
}
if(reduce_index >= 0)
{
TRACE(0, "Placer: reducing tail: %d", reduce_index);
SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace(new_start, aEnd);
m_p_start = new_start;
m_direction = new_direction;
tail.Remove(reduce_index+1, -1);
head.Clear();
return true;
}
if( !tail.SegmentCount() )
m_direction = m_initial_direction;
return false;
}
/**
* Function checkObtusity()
*
* Helper that checks if segments a and b form an obtuse angle (in 45-degree regime).
* @return true, if angle (a, b) is obtuse
*/
bool PNS_LINE_PLACER::checkObtusity(const SEG& a, const SEG& b) const
{
const DIRECTION_45 dir_a(a);
const DIRECTION_45 dir_b(b);
return dir_a.IsObtuse(dir_b) || dir_a == dir_b;
}
/**
* Function mergeHead()
*
* Moves "estabished" segments from the head to the tail if certain conditions are met.
* @return true, if the line has been changed.
*/
bool PNS_LINE_PLACER::mergeHead()
{
SHAPE_LINE_CHAIN& head = m_head.GetLine();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_UNDEFINED;
head.Simplify();
tail.Simplify();
int n_head = head.SegmentCount();
int n_tail = tail.SegmentCount();
if( n_head < 3 )
{
TRACEn(4, "Merge failed: not enough head segs.");
return false;
}
if (n_tail && head.CPoint(0) != tail.CPoint(-1))
{
TRACEn(4, "Merge failed: head and tail discontinuous.");
return false;
}
if( m_head.CountCorners(ForbiddenAngles) != 0 )
return false;
DIRECTION_45 dir_tail, dir_head;
dir_head = DIRECTION_45(head.CSegment(0));
if(n_tail)
{
dir_tail = DIRECTION_45(tail.CSegment(-1));
if(dir_head.Angle(dir_tail) & ForbiddenAngles)
return false;
}
if(!n_tail)
tail.Append(head.CSegment(0).a);
for (int i = 0; i < n_head - 2; i++)
{
tail.Append(head.CSegment(i).b);
}
tail.Simplify();
SEG last = tail.CSegment(-1);
m_p_start = last.b;
m_direction = DIRECTION_45(last).Right();
head.Remove(0, n_head - 2);
TRACE(0, "Placer: merge %d, new direction: %s", n_head % m_direction.Format().c_str());
head.Simplify();
tail.Simplify();
return true;
}
bool PNS_LINE_PLACER::handleViaPlacement ( PNS_LINE& aHead )
{
if(!m_placingVia)
return true;
PNS_LAYERSET allLayers (0, 15);
PNS_VIA v (aHead.GetCLine().CPoint(-1), allLayers, m_viaDiameter, aHead.GetNet());
v.SetDrill(m_viaDrill);
VECTOR2I force;
VECTOR2I lead = aHead.GetCLine().CPoint(-1) - aHead.GetCLine().CPoint(0);
if( v.PushoutForce ( m_shove->GetCurrentNode(), lead, force, true, 20 ) )
{
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace(aHead.GetCLine().CPoint(0), aHead.GetCLine().CPoint(-1) + force);
aHead = PNS_LINE(aHead, line);
v.SetPos(v.GetPos() + force);
return true;
}
return false;
}
/**
* Function routeHead()
*
* Computes the head trace between the current start point (m_p_start) and point aP,
* starting with direction defined in m_direction. The trace walks around all
* colliding solid or non-movable items. Movable segments are ignored, as they'll be handled
* later by the shove algorithm.
*/
bool PNS_LINE_PLACER::routeHead(const VECTOR2I& aP, PNS_LINE& aNewHead, bool aCwWalkaround)
{
// STAGE 1: route a simple two-segment trace between m_p_start and aP...
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace(m_p_start, aP);
PNS_LINE initTrack (m_head, line);
PNS_LINE walkFull, walkSolids;
if(m_mode == RM_Ignore)
{
aNewHead = initTrack;
return true;
}
handleViaPlacement(initTrack);
m_currentNode = m_shove->GetCurrentNode();
PNS_OPTIMIZER optimizer(m_currentNode);
PNS_WALKAROUND walkaround( m_currentNode );
walkaround.SetSolidsOnly(false);
walkaround.SetIterationLimit(m_mode == RM_Walkaround ? 8 : 5 );
//walkaround.SetApproachCursor(true, aP);
PNS_WALKAROUND::WalkaroundStatus wf = walkaround.Route(initTrack, walkFull);
#if 0
if(m_mode == RM_Walkaround)
{
// walkaround.
// PNSDisplayDebugLine (walkFull.GetCLine(), 4);
if(wf == PNS_WALKAROUND::STUCK)
{
aNewHead = m_head;
aNewHead.SetShape(walkFull.GetCLine());
aNewHead = aNewHead.ClipToNearestObstacle(m_currentNode);
return false;
}
aNewHead = m_head;
aNewHead.SetShape(walkFull.GetCLine());
// printf("nh w %d l %d\n", aNewHead.GetWidth(), aNewHead.GetLayers().Start());
return true;
}
#endif
PNS_COST_ESTIMATOR cost_walk, cost_orig;
walkaround.SetApproachCursor ( false, aP );
walkaround.SetSolidsOnly(true);
walkaround.SetIterationLimit( 10 );
PNS_WALKAROUND::WalkaroundStatus stat_solids = walkaround.Route(initTrack, walkSolids);
optimizer.SetEffortLevel ( PNS_OPTIMIZER::MERGE_SEGMENTS );
optimizer.SetCollisionMask (PNS_ITEM::SOLID);
optimizer.Optimize(&walkSolids);
#if 0
optimizer.SetCollisionMask (-1);
optimizer.Optimize(&walkFull);
#endif
cost_orig.Add(initTrack);
cost_walk.Add(walkFull);
if(m_mode == RM_Smart || m_mode == RM_Shove)
{
PNS_LINE l2;
bool walk_better = cost_orig.IsBetter(cost_walk, 1.5, 10.0);
walk_better = false;
#if 0
printf("RtTrk width %d %d %d", initTrack.GetWidth(), walkFull.GetWidth(), walkSolids.GetWidth());
printf("init-coll %d\n", m_currentNode->CheckColliding(&initTrack)? 1: 0);
printf("total cost: walk cor %.0f len %.0f orig cor %.0f len %.0f walk-better %d\n",
cost_walk.GetCornerCost(), cost_walk.GetLengthCost(),
cost_orig.GetCornerCost(), cost_orig.GetLengthCost(),
walk_better );
#endif
if(m_mode == RM_Smart && wf == PNS_WALKAROUND::DONE && walk_better && walkFull.GetCLine().CPoint(-1) == initTrack.GetCLine().CPoint(-1))
l2 = walkFull;
else if (stat_solids == PNS_WALKAROUND::DONE)
l2 = walkSolids;
else
l2 = initTrack.ClipToNearestObstacle(m_shove->GetCurrentNode());
PNS_LINE l ( m_tail );
l.GetLine().Append( l2.GetCLine() );
l.GetLine().Simplify();
if(m_placingVia)
{
PNS_LAYERSET allLayers(0,15);
PNS_VIA v1( l.GetCLine().CPoint(-1), allLayers, m_viaDiameter );
PNS_VIA v2( l2.GetCLine().CPoint(-1), allLayers, m_viaDiameter );
v1.SetDrill(m_viaDrill);
v2.SetDrill(m_viaDrill);
l.AppendVia ( v1 );
l2.AppendVia ( v2 );
}
PNS_SHOVE::ShoveStatus status = m_shove->ShoveLines(&l);
m_currentNode = m_shove->GetCurrentNode();
if (status == PNS_SHOVE::SH_OK)
{
optimizer.SetWorld (m_currentNode);
optimizer.ClearCache();
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS );
optimizer.SetCollisionMask (-1);
optimizer.Optimize(&l2);
aNewHead = l2;
return true;
} else {
walkaround.SetWorld( m_currentNode );
walkaround.SetSolidsOnly(false);
walkaround.SetIterationLimit( 10 );
walkaround.SetApproachCursor ( true, aP );
walkaround.Route(initTrack, l2);
aNewHead = l2.ClipToNearestObstacle (m_shove->GetCurrentNode());
//aNewHead = l2;
return false;
}
}
return false;
}
/**
* Function optimizeTailHeadTransition()
*
* Tries to reduce the corner count of the most recent part of tail/head by merging
* obtuse/collinear segments.
* @return true, if the line has been changed.
*/
bool PNS_LINE_PLACER::optimizeTailHeadTransition()
{
SHAPE_LINE_CHAIN& head = m_head.GetLine();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
const int TailLookbackSegments = 5;
int threshold = min(tail.PointCount(), TailLookbackSegments + 1);
if(tail.SegmentCount() < 3)
return false;
// assemble TailLookbackSegments tail segments with the current head
SHAPE_LINE_CHAIN opt_line = tail.Slice(-threshold, -1);
opt_line.Append(head);
// opt_line.Simplify();
PNS_LINE new_head(m_tail, opt_line);
// and see if it could be made simpler by merging obtuse/collnear segments. If so,
// replace the (threshold) last tail points and the head with the optimized line
//if(PNS_OPTIMIZER::Optimize(&new_head, PNS_OPTIMIZER::MERGE_SEGMENTS))
if(new_head.MergeSegments())
{
PNS_LINE tmp(m_tail, opt_line);
TRACE(0, "Placer: optimize tail-head [%d]", threshold);
head.Clear();
tail.Replace(-threshold, -1, new_head.GetCLine());
tail.Simplify();
m_p_start = new_head.GetCLine().CPoint(-1);
m_direction = DIRECTION_45(new_head.GetCLine().CSegment(-1));
return true;
}
return false;
}
/**
* Function routeStep()
*
* Performs a single routing alorithm step, for the end point aP.
* @param aP ending point of current route
* @return true, if the line has been changed.
*/
void PNS_LINE_PLACER::routeStep(const VECTOR2I& aP)
{
bool fail = false;
bool go_back = false;
int i, n_iter = 1;
PNS_LINE new_head;
m_follow_mouse = true;
TRACE(2,"INIT-DIR: %s head: %d, tail: %d segs\n", m_initial_direction.Format().c_str() % m_head.GetCLine().SegmentCount() % m_tail.GetCLine().SegmentCount());
for(i = 0; i < n_iter; i++)
{
if(!go_back && m_follow_mouse)
reduceTail(aP);
go_back = false;
if(!routeHead(aP, new_head, true))
fail = true;
if(!new_head.Is45Degree())
fail = true;
if(!m_follow_mouse)
return;
m_head = new_head;
if(handleSelfIntersections())
{
n_iter++;
go_back = true;
}
if(!go_back && handlePullback())
{
n_iter++;
go_back = true;
}
}
if(!fail)
{
if(optimizeTailHeadTransition())
return;
mergeHead();
}
}
/**
* Function Route()
*
* Re-routes the current track to point aP. Returns true, when routing has completed
* successfully (i.e. the trace end has reached point aP), and false if the trace was stuck somewhere
* on the way. May call routeStep() repetitively due to mouse smoothing.
* @param aP ending point of current route.
* @return true, if the routing is complete.
*/
bool PNS_LINE_PLACER::Route(const VECTOR2I& aP)
{
if(m_smooth_mouse)
{
VECTOR2I p_cur = m_p_start;
VECTOR2I step = (aP - m_p_start).Resize(m_smoothing_step);
do
{
if ((p_cur - aP).EuclideanNorm() <= m_smoothing_step)
p_cur = aP;
else
p_cur += step;
routeStep(p_cur);
} while (p_cur != aP);
} else
routeStep(aP);
return CurrentEnd() == aP;
}
const PNS_LINE PNS_LINE_PLACER::GetTrace() const
{
PNS_LINE tmp(m_head);
tmp.SetShape( m_tail.GetCLine() );
tmp.GetLine().Append( m_head.GetCLine() );
tmp.GetLine().Simplify();
return tmp;
}
void PNS_LINE_PLACER::FlipPosture()
{
m_initial_direction = m_initial_direction.Right();
m_direction = m_direction.Right();
}
void PNS_LINE_PLACER::GetUpdatedItems( PNS_NODE::ItemVector& aRemoved, PNS_NODE::ItemVector& aAdded)
{
return m_shove->GetCurrentNode()->GetUpdatedItems(aRemoved, aAdded);
}
PNS_NODE *PNS_LINE_PLACER::GetCurrentNode() const
{
return m_shove->GetCurrentNode();
}
\ No newline at end of file
/*
* 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/>.
*/
#ifndef __PNS_LINE_PLACER_H
#define __PNS_LINE_PLACER_H
#include <math/vector2d.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include "pns_node.h"
#include "pns_via.h"
#include "pns_line.h"
#include "pns_routing_settings.h"
class PNS_ROUTER;
class PNS_SHOVE;
class PNS_OPTIMIZER;
class PNS_ROUTER_BASE;
/**
* Class PNS_LINE_PLACER
*
* Interactively routes a single track. Runs shove and walkaround algorithms when needed.
*/
class PNS_LINE_PLACER
{
public:
PNS_LINE_PLACER( PNS_NODE *aWorld );
~PNS_LINE_PLACER();
///> Appends a via at the end of currently placed line.
void AddVia ( bool aEnabled, int aDiameter, int aDrill )
{
m_viaDiameter = aDiameter;
m_viaDrill = aDrill;
m_placingVia = aEnabled;
}
///> Starts placement of a line at point aStart.
void StartPlacement(const VECTOR2I& aStart, int aNet, int aWidth, int aLayer);
///> Updates the routed line with a new ending point.
bool Route(const VECTOR2I& aP);
///> Sets initial routing direction/posture
void SetInitialDirection(const DIRECTION_45& aDirection);
void ApplySettings ( const PNS_ROUTING_SETTINGS& aSettings );
///> Returns the "head" of the line being placed, that is the volatile part that has not been settled yet
const PNS_LINE& GetHead() const { return m_head; }
///> Returns the "tail" of the line being placed the part that has been fixed already (follow mouse mode only)
const PNS_LINE& GetTail() const { return m_tail; }
///> Returns the whole routed line
const PNS_LINE GetTrace() const;
///> Returns the current end of the line being placed. It may not be equal to the cursor position due to collisions.
const VECTOR2I& CurrentEnd() const
{
if(m_head.GetCLine().PointCount() > 0)
return m_head.GetCLine().CPoint(-1);
else if(m_tail.GetCLine().PointCount() > 0)
return m_tail.GetCLine().CPoint(-1);
else
return m_p_start;
}
///> Returns all items in the world that have been affected by the routing operation. Used
/// to update data structures of the host application
void GetUpdatedItems( PNS_NODE::ItemVector& aRemoved, PNS_NODE::ItemVector& aAdded);
///> Toggles the current posture (straight/diagonal) of the trace head.
void FlipPosture();
///> Returns the most recent world state
PNS_NODE *GetCurrentNode() const;
private:
static const double m_shoveLengthThreshold = 1.7;
bool handleViaPlacement ( PNS_LINE& aHead );
bool checkObtusity(const SEG& a, const SEG& b) const;
bool handleSelfIntersections();
bool handlePullback();
bool mergeHead();
bool reduceTail(const VECTOR2I& aEnd);
void fixHeadPosture();
bool optimizeTailHeadTransition();
bool routeHead(const VECTOR2I& aP, PNS_LINE& aNewHead, bool aCwWalkaround = true);
void routeStep(const VECTOR2I& aP);
///> routing mode (walkaround, shove, etc.)
PNS_MODE m_mode;
///> follow mouse trail by attaching new segments to the head as the cursor moves
bool m_follow_mouse;
///> mouse smoothing active
bool m_smooth_mouse;
///> mouse smoothing step (in world units)
int m_smoothing_step;
///> current routing direction
DIRECTION_45 m_direction;
///> routing direction for new traces
DIRECTION_45 m_initial_direction;
///> routing "head": volatile part of the track from the previously
/// analyzed point to the current routing destination
PNS_LINE m_head;
///> routing "tail": part of the track that has been already fixed due to collisions with obstacles
PNS_LINE m_tail;
///> current algorithm iteration
int m_iteration;
///> pointer to world to search colliding items
PNS_NODE *m_world;
///> current routing start point (end of tail, beginning of head)
VECTOR2I m_p_start;
///> The shove engine
PNS_SHOVE *m_shove;
///> Current world state
PNS_NODE *m_currentNode;
///> Are we placing a via?
bool m_placingVia;
///> current via diameter
int m_viaDiameter;
///> current via drill
int m_viaDrill;
///> walkaround algorithm iteration limit
int m_walkaroundIterationLimit;
///> smart pads optimizer enabled.
bool m_smartPads;
};
#endif // __PNS_LINE_PLACER_H
/*
* 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 <vector>
#include <cassert>
#include <math/vector2d.h>
#include <geometry/seg.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_index.h>
#include "trace.h"
#include "pns_item.h"
#include "pns_line.h"
#include "pns_node.h"
#include "pns_via.h"
#include "pns_solid.h"
#include "pns_joint.h"
#include "pns_index.h"
using namespace std;
using boost::unordered_set;
using boost::unordered_map;
static boost::unordered_set<PNS_NODE *> allocNodes;
PNS_NODE::PNS_NODE()
{
//printf("MakeNode [%p, total = %d]\n", this, allocNodes.size());
m_root = this;
m_parent = NULL;
m_maxClearance = 800000; // fixme: depends on how thick traces are.
m_index = new PNS_INDEX;
allocNodes.insert(this);
}
PNS_NODE::~PNS_NODE()
{
if(!m_children.empty())
{
TRACEn(0, "attempting to free a node that has kids.\n");
assert(false);
}
if(allocNodes.find(this) == allocNodes.end())
{
TRACEn(0, "attempting to free an already-free'd node.\n");
assert(false);
}
allocNodes.erase(this);
for(PNS_INDEX::ItemSet::iterator i = m_index->begin(); i != m_index->end(); ++i)
if( (*i) ->BelongsTo(this))
delete *i;
unlinkParent();
delete m_index;
}
int PNS_NODE::GetClearance(const PNS_ITEM *a, const PNS_ITEM *b) const
{
int clearance = (* m_clearanceFunctor) (a, b);
if( a->OfKind (PNS_ITEM::SEGMENT) )
clearance += static_cast<const PNS_SEGMENT *>(a) -> GetWidth() / 2;
if( a->OfKind (PNS_ITEM::LINE) )
clearance += static_cast<const PNS_LINE *>(a) -> GetWidth() / 2;
if( b->OfKind (PNS_ITEM::SEGMENT) )
clearance += static_cast<const PNS_SEGMENT *>(b) -> GetWidth() / 2;
if( b->OfKind (PNS_ITEM::LINE) )
clearance += static_cast<const PNS_LINE *>(b) -> GetWidth() / 2;
return clearance;
}
PNS_NODE* PNS_NODE::Branch()
{
PNS_NODE *child = new PNS_NODE;
m_children.push_back(child);
child->m_parent = this;
child->m_clearanceFunctor = m_clearanceFunctor;
child->m_root = isRoot() ? this : m_root;
// immmediate offspring of the root branch needs not copy anything. For the rest,
// deep-copy joints, overridden item map and pointers to stored items.
if(!isRoot())
{
JointMap::iterator j;
for(PNS_INDEX::ItemSet::iterator i = m_index->begin(); i != m_index->end(); ++i)
child->m_index->Add(*i);
child->m_joints = m_joints;
child->m_override = m_override;
}
TRACE(2, "%d items, %d joints, %d overrides", child->m_index->Size() % child->m_joints.size() % child->m_override.size());
return child;
}
void PNS_NODE::unlinkParent ( )
{
if( isRoot() )
return;
for( vector<PNS_NODE *>::iterator i = m_parent->m_children.begin(); i != m_parent->m_children.end(); ++i)
{
if (*i == this)
{
m_parent->m_children.erase(i);
return;
}
}
}
// function object that visits potential obstacles and performs the actual collision refining
struct PNS_NODE::obstacleVisitor {
///> node we are searching in (either root or a branch)
PNS_NODE *m_node;
///> node that overrides root entries
PNS_NODE *m_override;
///> list of encountered obstacles
Obstacles& m_tab;
///> the item we are looking for collisions with
const PNS_ITEM* m_item;
///> acccepted kinds of colliding items (solids, vias, segments, etc...)
int m_kindMask;
///> max number of hits
int m_limitCount;
///> number of items found so far
int m_matchCount;
obstacleVisitor( PNS_NODE::Obstacles& aTab,
const PNS_ITEM* aItem,
int aKindMask )
: m_tab(aTab),
m_item(aItem),
m_kindMask(aKindMask),
m_limitCount(-1),
m_matchCount(0)
{ };
void SetCountLimit(int aLimit)
{
m_limitCount = aLimit;
}
void SetWorld(PNS_NODE *aNode, PNS_NODE *aOverride = NULL)
{
m_node = aNode;
m_override = aOverride;
}
bool operator()( PNS_ITEM *aItem )
{
if( !aItem->OfKind(m_kindMask))
return true;
// check if there is a more recent branch with a newer (possibily modified) version of this item.
if ( m_override && m_override -> overrides (aItem) )
return true;
int clearance = m_node->GetClearance(aItem, m_item);
if(!aItem->Collide(m_item, clearance))
return true;
PNS_OBSTACLE obs;
obs.item = aItem;
m_tab.push_back(obs);
m_matchCount ++;
if(m_limitCount > 0 && m_matchCount >= m_limitCount)
return false;
return true;
};
};
int PNS_NODE::QueryColliding( const PNS_ITEM* aItem, PNS_NODE::Obstacles& aObstacles, int aKindMask, int aLimitCount)
{
obstacleVisitor visitor ( aObstacles, aItem, aKindMask );
assert(allocNodes.find(this) != allocNodes.end());
visitor.SetCountLimit(aLimitCount);
visitor.SetWorld( this, NULL );
// first, look for colliding items ourselves
m_index->Query(aItem, m_maxClearance, visitor);
// if we haven't found enough items, look in the root branch as well.
if(!isRoot() && ( visitor.m_matchCount < aLimitCount || aLimitCount < 0) )
{
visitor.SetWorld ( m_root, this );
m_root->m_index->Query(aItem, m_maxClearance, visitor);
}
return aObstacles.size();
}
PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE *aItem, int aKindMask)
{
Obstacles obs_list;
bool found_isects = false;
const SHAPE_LINE_CHAIN &line = aItem->GetCLine();
obs_list.reserve(100);
int n = 0;
for(int i = 0; i < line.SegmentCount(); i++)
{
const PNS_SEGMENT s ( *aItem, line.CSegment(i));
n += QueryColliding ( &s, obs_list, aKindMask );
}
if( aItem->EndsWithVia () )
n += QueryColliding ( &aItem->GetVia(), obs_list, aKindMask );
//if(! QueryColliding ( aItem, obs_list, aKindMask ))
if(!n)
return OptObstacle();
PNS_LINE& aLine = (PNS_LINE&) *aItem;
PNS_OBSTACLE nearest;
nearest.item = NULL;
nearest.dist_first = INT_MAX;
BOOST_FOREACH(PNS_OBSTACLE obs, obs_list)
{
VECTOR2I ip_first, ip_last;
int dist_max = INT_MIN;
vector<SHAPE_LINE_CHAIN::Intersection> isect_list;
int clearance = GetClearance(obs.item, &aLine);
SHAPE_LINE_CHAIN hull = obs.item->Hull ( clearance );
if(aLine.EndsWithVia())
{
int clearance = GetClearance(obs.item, &aLine.GetVia());
SHAPE_LINE_CHAIN viaHull = aLine.GetVia().Hull (clearance);
viaHull.Intersect(hull, isect_list);
BOOST_FOREACH(SHAPE_LINE_CHAIN::Intersection isect, isect_list)
{
int dist = aLine.GetCLine().Length() + (isect.p - aLine.GetVia().GetPos()).EuclideanNorm();
if(dist < nearest.dist_first)
{
found_isects = true;
nearest.dist_first = dist;
nearest.ip_first = isect.p;
nearest.item = obs.item;
nearest.hull = hull;
}
if(dist > dist_max)
{
dist_max = dist;
ip_last = isect.p;
}
}
}
isect_list.clear();
hull.Intersect(aLine.GetCLine(), isect_list);
BOOST_FOREACH(SHAPE_LINE_CHAIN::Intersection isect, isect_list)
{
int dist = aLine.GetCLine().PathLength(isect.p);
if(dist < nearest.dist_first)
{
found_isects = true;
nearest.dist_first = dist;
nearest.ip_first = isect.p;
nearest.item = obs.item;
nearest.hull = hull;
}
if(dist > dist_max)
{
dist_max = dist;
ip_last = isect.p;
}
}
nearest.ip_last = ip_last;
nearest.dist_last = dist_max;
}
return found_isects ? nearest : OptObstacle();
}
PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM *aItemA, int aKindMask )
{
Obstacles obs;
obs.reserve(100);
if(aItemA->GetKind() == PNS_ITEM::LINE)
{
int n = 0;
const PNS_LINE *line = static_cast<const PNS_LINE *>(aItemA);
const SHAPE_LINE_CHAIN &l = line->GetCLine();
for(int i = 0; i < l.SegmentCount(); i++)
{
const PNS_SEGMENT s ( *line, l.CSegment(i));
n += QueryColliding ( &s, obs, aKindMask, 1 );
if(n)
return OptObstacle(obs[0]);
}
if( line->EndsWithVia () )
{
n += QueryColliding ( &line->GetVia(), obs, aKindMask, 1 );
if(n)
return OptObstacle(obs[0]);
}
} else if (QueryColliding(aItemA, obs, aKindMask, 1) > 0)
return OptObstacle(obs[0]);
return OptObstacle();
}
bool PNS_NODE::CheckColliding( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB, int aKindMask )
{
Obstacles dummy;
assert(aItemB);
// return QueryColliding(aItemA, dummy, aKindMask, 1) > 0;
return aItemA->Collide(aItemB, GetClearance(aItemA, aItemB));
}
struct hitVisitor {
PNS_ITEMSET& m_items;
const VECTOR2I& m_point;
PNS_NODE *m_world;
hitVisitor( PNS_ITEMSET& aTab,
const VECTOR2I& aPoint,
PNS_NODE *aWorld )
: m_items(aTab), m_point(aPoint), m_world(aWorld) { };
bool operator()( PNS_ITEM *aItem ) {
SHAPE_CIRCLE cp (m_point, 0);
int cl = 0;
if(aItem->GetKind() == PNS_ITEM::SEGMENT)
cl += static_cast<PNS_SEGMENT*>(aItem)->GetWidth() / 2;
if(aItem->GetShape()->Collide(&cp, cl))
m_items.Add(aItem);
return true;
}
};
const PNS_ITEMSET PNS_NODE::HitTest( const VECTOR2I& aPoint )
{
PNS_ITEMSET items;
SHAPE_CIRCLE s (aPoint, 0); // fixme: we treat a point as an infinitely small circle - this is inefficient.
hitVisitor visitor ( items, aPoint, this );
m_index->Query(&s, m_maxClearance, visitor);
if( ! isRoot() ) // fixme: could be made cleaner
{
PNS_ITEMSET items_root;
hitVisitor visitor_root ( items_root, aPoint, m_root );
m_root->m_index->Query( &s, m_maxClearance, visitor_root );
BOOST_FOREACH(PNS_ITEM *item, items_root.Items())
{
if (!overrides(item))
items.Add(item);
}
}
return items;
}
void PNS_NODE::addSolid(PNS_SOLID *aSolid)
{
linkJoint( aSolid->GetCenter(), aSolid->GetLayers(), aSolid->GetNet(), aSolid );
m_index->Add(aSolid);
}
void PNS_NODE::addVia(PNS_VIA *aVia)
{
linkJoint( aVia->GetPos(), aVia->GetLayers(), aVia->GetNet(), aVia );
m_index->Add(aVia);
}
void PNS_NODE::addLine( PNS_LINE *aLine )
{
const SHAPE_LINE_CHAIN& l = aLine->GetLine();
for(int i = 0; i < l.SegmentCount(); i++)
{
SEG s = l.CSegment(i);
if(s.a != s.b)
{
PNS_SEGMENT *pseg = new PNS_SEGMENT(*aLine, s);
pseg->SetOwner(this);
linkJoint( s.a, pseg->GetLayers(), aLine->GetNet(), pseg );
linkJoint( s.b, pseg->GetLayers(), aLine->GetNet(), pseg );
aLine->LinkSegment(pseg);
m_index->Add(pseg);
}
}
}
void PNS_NODE::addSegment( PNS_SEGMENT *aSeg )
{
if(aSeg->GetSeg().a == aSeg->GetSeg().b)
{
TRACEn(0, "attempting to add a segment with same end coordinates, ignoring.")
return;
}
aSeg->SetOwner (this);
linkJoint( aSeg->GetSeg().a, aSeg->GetLayers(), aSeg->GetNet(), aSeg );
linkJoint( aSeg->GetSeg().b, aSeg->GetLayers(), aSeg->GetNet(), aSeg );
m_index->Add(aSeg);
}
void PNS_NODE::Add(PNS_ITEM* aItem)
{
aItem->SetOwner(this);
switch(aItem -> GetKind())
{
case PNS_ITEM::SOLID:
addSolid(static_cast<PNS_SOLID*>(aItem));
break;
case PNS_ITEM::SEGMENT:
addSegment(static_cast<PNS_SEGMENT*>(aItem));
break;
case PNS_ITEM::LINE:
addLine( static_cast<PNS_LINE*> (aItem));
break;
case PNS_ITEM::VIA:
addVia (static_cast<PNS_VIA*>(aItem));
break;
default:
assert (false);
}
}
void PNS_NODE::doRemove ( PNS_ITEM *aItem )
{
// case 1: removing an item that is stored in the root node from any branch: mark it as overridden, but do not remove
if( aItem->BelongsTo(m_root) && !isRoot() )
m_override.insert(aItem);
// case 2: the item belongs to this branch or a parent, non-root branch, or the root itself and we are the root: remove from the index
else if( !aItem->BelongsTo(m_root) || isRoot() )
m_index->Remove( aItem );
// the item belongs to this particular branch: un-reference it
if( aItem->BelongsTo( this ))
aItem->SetOwner(NULL);
}
void PNS_NODE::removeSegment (PNS_SEGMENT *aSeg )
{
unlinkJoint(aSeg->GetSeg().a, aSeg->GetLayers(), aSeg->GetNet(), aSeg);
unlinkJoint(aSeg->GetSeg().b, aSeg->GetLayers(), aSeg->GetNet(), aSeg);
doRemove(aSeg);
}
void PNS_NODE::removeLine( PNS_LINE *aLine )
{
vector<PNS_SEGMENT *> *segRefs = aLine->GetLinkedSegments();
if(!segRefs)
return;
assert ( aLine->GetOwner () );
BOOST_FOREACH(PNS_SEGMENT *seg, *segRefs)
{
removeSegment(seg);
}
aLine->SetOwner(NULL);
}
void PNS_NODE::removeVia ( PNS_VIA *aVia )
{
unlinkJoint(aVia->GetPos(), aVia->GetLayers(), aVia->GetNet(), aVia);
doRemove(aVia);
}
void PNS_NODE::Replace(PNS_ITEM *aOldItem, PNS_ITEM *aNewItem)
{
Remove(aOldItem);
Add(aNewItem);
}
void PNS_NODE::Remove(PNS_ITEM *aItem)
{
switch(aItem -> GetKind())
{
case PNS_ITEM::SOLID:
assert(false);
break;
case PNS_ITEM::SEGMENT:
removeSegment(static_cast<PNS_SEGMENT*>(aItem));
break;
case PNS_ITEM::LINE:
removeLine(static_cast<PNS_LINE *>(aItem));
break;
case PNS_ITEM::VIA:
removeVia(static_cast<PNS_VIA *>(aItem));
break;
default:
break;
}
}
void PNS_NODE::followLine(PNS_SEGMENT *current, bool scanDirection, int& pos, int limit, VECTOR2I *corners, PNS_SEGMENT **segments)
{
bool prevReversed = false;
for(;;)
{
const VECTOR2I p = (scanDirection ^ prevReversed) ? current->GetSeg().b : current->GetSeg().a;
const OptJoint jt = FindJoint(p, current->GetLayer(), current->GetNet());
assert (jt);
assert (pos > 0 && pos < limit);
corners [pos] = jt->GetPos();
segments [pos] = current;
pos += (scanDirection ? 1 : -1);
if(!jt->IsLineCorner())
break;
current = jt->NextSegment( current );
prevReversed = (jt->GetPos() == (scanDirection ? current->GetSeg().b : current->GetSeg().a ));
}
}
PNS_LINE *PNS_NODE::AssembleLine(PNS_SEGMENT *aSeg, const OptJoint& a, const OptJoint& b)
{
const int MaxVerts = 1024;
VECTOR2I corners [ MaxVerts + 1 ];
PNS_SEGMENT *segs [ MaxVerts + 1 ];
PNS_LINE *pl = new PNS_LINE;
int i_start = MaxVerts/2, i_end = i_start + 1;
pl->SetWidth( aSeg->GetWidth() );
pl->SetLayers( aSeg->GetLayers() );
pl->SetNet ( aSeg->GetNet() );
pl->SetOwner(this);
//pl->LinkSegment(aSeg);
followLine (aSeg, false, i_start, MaxVerts, corners, segs );
followLine (aSeg, true, i_end, MaxVerts, corners, segs );
int clip_start = -1, clip_end = -1;
for(int i = i_start+1 ; i < i_end ; i++)
{
const VECTOR2I &p = corners[i];
if (a && (p == a->GetPos() || p == b->GetPos() ) )
{
clip_start = std::min(clip_start, i);
clip_end = std::max(clip_end, i);
}
pl->GetLine().Append(p);
if(segs[i-1] != segs[i])
pl->LinkSegment(segs[i]);
}
return pl;
}
void PNS_NODE::FindLineEnds (PNS_LINE *aLine, PNS_JOINT& a, PNS_JOINT& b )
{
a = *FindJoint(aLine->GetCLine().CPoint(0), aLine->GetLayers().Start(), aLine->GetNet());
b = *FindJoint(aLine->GetCLine().CPoint(-1), aLine->GetLayers().Start(), aLine->GetNet());
}
int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& a, PNS_JOINT& b, vector<PNS_LINE *> &aLines )
{
BOOST_FOREACH(PNS_ITEM *item, a.GetLinkList())
{
if(item->GetKind() == PNS_ITEM::SEGMENT)
{
PNS_SEGMENT *seg = static_cast<PNS_SEGMENT*>(item);
PNS_LINE *line = AssembleLine(seg);
PNS_JOINT j_start, j_end;
FindLineEnds( line, j_start, j_end );
if( (j_start == a && j_end == b )|| (j_end == a && j_start == b))
aLines.push_back(line);
else
delete line;
}
}
return 0;
}
const PNS_NODE::OptJoint PNS_NODE::FindJoint(const VECTOR2I &aPos, int aLayer, int aNet )
{
PNS_JOINT::HashTag tag;
tag.net = aNet;
tag.pos = aPos;
JointMap::iterator f = m_joints.find(tag), end = m_joints.end();
if(f == end && !isRoot())
{
end = m_root->m_joints.end();
f = m_root->m_joints.find(tag); //m_root->FindJoint(aPos, aLayer, aNet);
}
if(f == end)
return OptJoint();
while (f != end)
{
if(f->second.GetLayers().Overlaps(aLayer))
return f->second;
++f;
}
return OptJoint();
}
PNS_JOINT& PNS_NODE::touchJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet )
{
PNS_JOINT::HashTag tag;
tag.pos = aPos;
tag.net = aNet;
// try to find the joint in this node.
JointMap::iterator f = m_joints.find(tag);
pair<JointMap::iterator, JointMap::iterator> range;
// not found and we are not root? find in the root and copy results here.
if(f == m_joints.end() && !isRoot())
{
range = m_root->m_joints.equal_range(tag);
for( f = range.first; f != range.second; ++f)
m_joints.insert( *f );
}
// now insert and combine overlapping joints
PNS_JOINT jt (aPos, aLayers, aNet);
bool merged;
do
{
merged = false;
range = m_joints.equal_range(tag);
if(range.first == m_joints.end())
break;
for(f = range.first; f != range.second; ++f)
{
if(aLayers.Overlaps (f->second.GetLayers()))
{
jt.Merge(f->second);
m_joints.erase(f);
merged = true;
break;
}
}
} while (merged);
return m_joints.insert ( TagJointPair(tag, jt) )->second;
}
void PNS_JOINT::Dump() const
{
printf("joint layers %d-%d, net %d, pos %s, links: %d\n", m_layers.Start(), m_layers.End(), m_tag.net, m_tag.pos.Format().c_str(), LinkCount() );
}
void PNS_NODE::linkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet, PNS_ITEM *aWhere )
{
PNS_JOINT& jt = touchJoint( aPos, aLayers, aNet );
jt.Link(aWhere);
}
void PNS_NODE::unlinkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet, PNS_ITEM *aWhere )
{
// fixme: remove dangling joints
PNS_JOINT& jt = touchJoint( aPos, aLayers, aNet );
jt.Unlink(aWhere);
}
void PNS_NODE::Dump(bool aLong)
{
#if 0
boost::unordered_set<PNS_SEGMENT *> all_segs;
SHAPE_INDEX_LIST<PNS_ITEM *>::iterator i;
for(i = m_items.begin(); i != m_items.end() ; i++)
{
if((*i)->GetKind() == PNS_ITEM::SEGMENT)
all_segs.insert(static_cast<PNS_SEGMENT*>(*i));
}
if(!isRoot())
for(i = m_root->m_items.begin(); i != m_root->m_items.end() ; i++)
{
if((*i)->GetKind() == PNS_ITEM::SEGMENT && !overrides(*i))
all_segs.insert(static_cast<PNS_SEGMENT*>(*i));
}
JointMap::iterator j;
if(aLong)
for(j=m_joints.begin(); j!=m_joints.end(); ++j)
{
printf("joint : %s, links : %d\n", j->second.GetPos().Format().c_str(), j->second.LinkCount());
PNS_JOINT::LinkedItems::const_iterator k;
for(k = j->second.GetLinkList().begin(); k != j->second.GetLinkList().end(); ++k)
{
const PNS_ITEM *item = *k;
switch(item->GetKind())
{
case PNS_ITEM::SEGMENT:
{
const PNS_SEGMENT *seg = static_cast<const PNS_SEGMENT *>(item);
printf(" -> seg %s %s\n", seg->GetSeg().a.Format().c_str(), seg->GetSeg().b.Format().c_str());
break;
}
default:
break;
}
}
}
int lines_count = 0;
while(!all_segs.empty())
{
PNS_SEGMENT *s = *all_segs.begin();
PNS_LINE *l = AssembleLine(s);
PNS_LINE::LinkedSegments* seg_refs = l->GetLinkedSegments();
if(aLong)
printf("Line: %s, net %d ", l->GetLine().Format().c_str(), l->GetNet() );
for(vector<PNS_SEGMENT *>::iterator j = seg_refs->begin(); j != seg_refs->end(); ++j)
{
printf("%s ", (*j)->GetSeg().a.Format().c_str() );
if(j+1 == seg_refs->end())
printf("%s\n", (*j)->GetSeg().b.Format().c_str() );
all_segs.erase(*j);
}
lines_count++;
}
printf("Local joints: %d, lines : %d \n", m_joints.size(), lines_count);
#endif
}
void PNS_NODE::GetUpdatedItems( ItemVector& aRemoved, ItemVector& aAdded)
{
aRemoved.reserve(m_override.size());
aAdded.reserve(m_index->Size());
if(isRoot ())
return;
BOOST_FOREACH(PNS_ITEM *item, m_override)
aRemoved.push_back(item);
for(PNS_INDEX::ItemSet::iterator i = m_index->begin(); i!=m_index->end(); ++i)
aAdded.push_back(*i);
}
void PNS_NODE::releaseChildren ()
{
// copy the kids as the PNS_NODE destructor erases the item from the parent node.
vector<PNS_NODE *> kids = m_children;
BOOST_FOREACH(PNS_NODE *node, kids)
{
node->releaseChildren();
delete node;
}
}
void PNS_NODE::Commit( PNS_NODE *aNode )
{
if(aNode->isRoot())
return;
BOOST_FOREACH( PNS_ITEM *item, aNode->m_override )
Remove(item);
for(PNS_INDEX::ItemSet::iterator i = aNode->m_index->begin(); i!= aNode ->m_index->end(); ++i)
Add(*i);
releaseChildren();
}
void PNS_NODE::KillChildren()
{
assert (isRoot());
releaseChildren();
}
void PNS_NODE::AllItemsInNet ( int aNet, std::list<PNS_ITEM *>& aItems)
{
PNS_INDEX::NetItemsList* l_cur = m_index->GetItemsForNet ( aNet );
if(!l_cur)
return;
std::copy(aItems.begin(), l_cur->begin(), l_cur->end() );
if( !isRoot () )
{
PNS_INDEX::NetItemsList* l_root = m_root->m_index->GetItemsForNet ( aNet );
for(PNS_INDEX::NetItemsList::iterator i = l_root->begin(); i!= l_root->end(); ++i)
if( !overrides( *i ))
aItems.push_back(*i);
}
}
/*
* 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/>.
*/
#ifndef __PNS_NODE_H
#define __PNS_NODE_H
#include <vector>
#include <list>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/optional.hpp>
#include <boost/smart_ptr.hpp>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_index.h>
#include "pns_item.h"
#include "pns_joint.h"
#include "pns_itemset.h"
class PNS_SEGMENT;
class PNS_LINE;
class PNS_SOLID;
class PNS_VIA;
class PNS_RATSNEST;
class PNS_INDEX;
using boost::shared_ptr;
class PNS_CLEARANCE_FUNC {
public:
virtual int operator() ( const PNS_ITEM *a , const PNS_ITEM *b) = 0;
};
/**
* Struct PNS_OBSTACLE
*
* Holds an object colliding with another object, along with
* some useful data about the collision.
**/
struct PNS_OBSTACLE
{
///> Item we search collisions with
PNS_ITEM *head;
///> Item found to be colliding with head
PNS_ITEM *item;
///> Hull of the colliding item
SHAPE_LINE_CHAIN hull;
///> First and last intersection point between the head item and the hull of the
//// colliding item
VECTOR2I ip_first, ip_last;
///> ... and the distance thereof
int dist_first, dist_last;
};
/**
* Class PNS_NODE
*
* Keeps the router "world" - i.e. all the tracks, vias, solids in a hierarchical and indexed way.
* Features:
* - spatial-indexed container for PCB item shapes
* - collision search (with clearance checking)
* - assembly of lines connecting joints, finding loops and unique paths
* - lightweight cloning/branching (for recursive optimization and shove springback)
**/
class PNS_NODE {
public:
typedef boost::optional<PNS_OBSTACLE> OptObstacle;
typedef std::vector<PNS_ITEM *> ItemVector;
typedef std::vector<PNS_OBSTACLE> Obstacles;
typedef boost::optional<PNS_JOINT> OptJoint;
PNS_NODE ();
~PNS_NODE ();
///> Returns the expected clearance between items a and b.
int GetClearance(const PNS_ITEM *a, const PNS_ITEM *b) const;
///> Returns the pre-set worst case clearance between any pair of items
int GetMaxClearance() const
{
return m_maxClearance;
}
void SetMaxClearance( int aClearance )
{
m_maxClearance = aClearance;
}
void SetClearanceFunctor (PNS_CLEARANCE_FUNC *aFunc)
{
m_clearanceFunctor = aFunc;
}
///> Finds items that collide with aItem and stores collision information in aObstacles.
int QueryColliding( const PNS_ITEM* aItem, Obstacles& aObstacles, int aKindMask = PNS_ITEM::ANY, int aLimitCount = -1);
///> Finds the nearest item that collides with aItem.
OptObstacle NearestObstacle( const PNS_LINE *aItem, int aKindMask = PNS_ITEM::ANY);
///> Checks if the item collides with anything else in the world, and returns it if so.
OptObstacle CheckColliding ( const PNS_ITEM *aItem, int aKindMask = PNS_ITEM::ANY);
///> Checks if two items collide [deprecated].
bool CheckColliding( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB, int aKindMask = PNS_ITEM::ANY);
///> Hit detection
const PNS_ITEMSET HitTest( const VECTOR2I& aPoint );
void Add(PNS_ITEM *aItem);
void Remove(PNS_ITEM *aItem);
void Replace(PNS_ITEM *aOldItem, PNS_ITEM *aNewItem);
///> Creates a lightweight copy ("branch") of self. Note that if there are any branches
/// in use, their parents must NOT be deleted.
PNS_NODE *Branch();
///> Assembles a line connecting two non-trivial joints the segment aSeg belongs to.
PNS_LINE *AssembleLine(PNS_SEGMENT *aSeg, const OptJoint& a = OptJoint(), const OptJoint& b = OptJoint());
///> Dumps the contents and joints structure
void Dump(bool aLong = false);
///> Returns the number of joints
int JointCount() const
{
return m_joints.size();
}
///> Returns the lists of items removed and added in this branch, with respect
///> to the root.
void GetUpdatedItems( ItemVector& aRemoved, ItemVector& aAdded);
///> Copies the changes from a given branch (aNode) to the root. Called on
///> a non-root branch will fail.
void Commit (PNS_NODE *aNode);
///> finds a joint at a given position, layer and nets
const OptJoint FindJoint(const VECTOR2I &aPos, int aLayer, int aNet);
///> finds all linest between a pair of joints. Used by the loop removal engine.
int FindLinesBetweenJoints( PNS_JOINT& a, PNS_JOINT& b, std::vector<PNS_LINE *> &aLines );
///> finds the joints corresponding to the ends of line aLine
void FindLineEnds (PNS_LINE *aLine, PNS_JOINT& a, PNS_JOINT& b );
///> finds all joints that have an (in)direct connection(s) (i.e. segments/vias) with the joint aJoint.
void FindConnectedJoints( const PNS_JOINT& aJoint, std::vector<PNS_JOINT *> &aConnectedJoints );
///> Destroys all child nodes. Applicable only to the root node.
void KillChildren();
void AllItemsInNet ( int aNet, std::list<PNS_ITEM *>& aItems);
private:
struct obstacleVisitor;
typedef boost::unordered_multimap<PNS_JOINT::HashTag, PNS_JOINT> JointMap;
typedef JointMap::value_type TagJointPair;
/// nodes are not copyable
PNS_NODE( const PNS_NODE& b);
PNS_NODE &operator=(const PNS_NODE& b);
///> tries to find matching joint and creates a new one if not found
PNS_JOINT& touchJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet );
///> touches a joint and links it to an item
void linkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet, PNS_ITEM *aWhere );
///> unlinks an item from a joint
void unlinkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet, PNS_ITEM *aWhere );
///> helpers for adding/removing items
void addSolid( PNS_SOLID *aSeg );
void addSegment( PNS_SEGMENT *aSeg );
void addLine( PNS_LINE *aLine );
void addVia( PNS_VIA *aVia );
void removeSolid( PNS_SOLID *aSeg );
void removeLine( PNS_LINE *aLine );
void removeSegment (PNS_SEGMENT *aSeg );
void removeVia (PNS_VIA *aVia );
void doRemove( PNS_ITEM *aItem );
void unlinkParent ( );
void releaseChildren ();
bool isRoot() const
{
return m_parent == NULL;
}
///> checks if this branch contains an updated version of the item from the root branch.
bool overrides ( PNS_ITEM * aItem ) const
{
return m_override.find(aItem) != m_override.end();
}
///> scans the joint map, forming a line starting from segment (current).
void followLine(PNS_SEGMENT *current, bool scanDirection, int& pos, int limit, VECTOR2I *corners, PNS_SEGMENT **segments);
///> spatial index of all items
//SHAPE_INDEX_LIST<PNS_ITEM *> m_items;
///> hash table with the joints, linking the items. Joints are hashed by their
///> position, layer set and net.
JointMap m_joints;
///> node this node was branched from
PNS_NODE *m_parent;
///> root node of the whole hierarchy
PNS_NODE *m_root;
///> list of nodes branched from this one
std::vector<PNS_NODE *> m_children;
///> hash of root's items that are more recent in this node
boost::unordered_set<PNS_ITEM *> m_override;
///> worst case item-item clearance
int m_maxClearance;
///> Clearance resolution functor
PNS_CLEARANCE_FUNC *m_clearanceFunctor;
///> Geometric/Net index of the items
PNS_INDEX *m_index;
///> list of currently processed obstacles.
Obstacles m_obstacleList;
};
#endif
/*
* 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 <boost/foreach.hpp>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include "pns_line.h"
#include "pns_node.h"
#include "pns_optimizer.h"
#include "pns_utils.h"
using namespace std;
/**
Cost Estimator Methods
**/
int PNS_COST_ESTIMATOR::CornerCost( const SEG& a, const SEG& b)
{
DIRECTION_45 dir_a(a), dir_b(b);
switch(dir_a.Angle(dir_b))
{
case DIRECTION_45::ANG_OBTUSE:
return 1;
case DIRECTION_45::ANG_STRAIGHT:
return 0;
case DIRECTION_45::ANG_ACUTE:
return 50;
case DIRECTION_45::ANG_RIGHT:
return 30;
case DIRECTION_45::ANG_HALF_FULL:
return 60;
default:
return 100;
}
}
int PNS_COST_ESTIMATOR::CornerCost ( const SHAPE_LINE_CHAIN& aLine )
{
int total = 0;
for (int i = 0; i < aLine.SegmentCount() - 1; ++i)
total += CornerCost(aLine.CSegment(i), aLine.CSegment(i + 1));
return total;
}
int PNS_COST_ESTIMATOR::CornerCost ( const PNS_LINE& aLine )
{
return CornerCost(aLine.GetCLine());
}
void PNS_COST_ESTIMATOR::Add(PNS_LINE &aLine)
{
m_lengthCost += aLine.GetCLine().Length();
m_cornerCost += CornerCost(aLine);
}
void PNS_COST_ESTIMATOR::Remove(PNS_LINE &aLine)
{
m_lengthCost -= aLine.GetCLine().Length();
m_cornerCost -= CornerCost(aLine);
}
void PNS_COST_ESTIMATOR::Replace(PNS_LINE &aOldLine, PNS_LINE& aNewLine)
{
m_lengthCost -= aOldLine.GetCLine().Length();
m_cornerCost -= CornerCost(aOldLine);
m_lengthCost += aNewLine.GetCLine().Length();
m_cornerCost += CornerCost(aNewLine);
}
bool PNS_COST_ESTIMATOR::IsBetter( PNS_COST_ESTIMATOR& aOther, double aLengthTollerance, double aCornerTollerance ) const
{
if(aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost)
return true;
else if(aOther.m_cornerCost < m_cornerCost * aCornerTollerance && aOther.m_lengthCost < m_lengthCost * aLengthTollerance)
return true;
return false;
}
/**
Optimizer
**/
PNS_OPTIMIZER::PNS_OPTIMIZER( PNS_NODE *aWorld ) :
m_world( aWorld ), m_collisionKindMask (PNS_ITEM::ANY), m_effortLevel(MERGE_SEGMENTS)
{
// m_cache = new SHAPE_INDEX_LIST<PNS_ITEM*>();
}
PNS_OPTIMIZER::~PNS_OPTIMIZER ( )
{
//delete m_cache;
}
struct PNS_OPTIMIZER::CacheVisitor
{
CacheVisitor( const PNS_ITEM * aOurItem, PNS_NODE *aNode, int aMask ) :
m_ourItem(aOurItem),
m_collidingItem(NULL),
m_node(aNode),
m_mask(aMask)
{};
bool operator() (PNS_ITEM *aOtherItem)
{
if(! m_mask & aOtherItem->GetKind())
return true;
int clearance = m_node->GetClearance(aOtherItem, m_ourItem);
if(!aOtherItem->Collide(m_ourItem, clearance))
return true;
m_collidingItem = aOtherItem;
return false;
}
const PNS_ITEM *m_ourItem;
PNS_ITEM *m_collidingItem;
PNS_NODE *m_node;
int m_mask;
};
void PNS_OPTIMIZER::cacheAdd( PNS_ITEM *aItem, bool aIsStatic = false)
{
if(m_cacheTags.find(aItem) != m_cacheTags.end())
return;
m_cache.Add(aItem);
m_cacheTags[aItem].hits = 1;
m_cacheTags[aItem].isStatic = aIsStatic;
}
void PNS_OPTIMIZER::removeCachedSegments (PNS_LINE *aLine, int aStartVertex, int aEndVertex)
{
std::vector<PNS_SEGMENT *> *segs = aLine->GetLinkedSegments();
if(!segs)
return;
if(aEndVertex < 0)
aEndVertex += aLine->GetCLine().PointCount();
for(int i = aStartVertex; i < aEndVertex - 1; i++)
{
PNS_SEGMENT *s = (*segs)[i];
m_cacheTags.erase(s);
m_cache.Remove(s);
}//*cacheRemove( (*segs)[i] );
}
void PNS_OPTIMIZER::CacheRemove ( PNS_ITEM *aItem )
{
if(aItem->GetKind() == PNS_ITEM::LINE)
removeCachedSegments(static_cast<PNS_LINE *> (aItem));
}
void PNS_OPTIMIZER::CacheStaticItem (PNS_ITEM *aItem)
{
cacheAdd(aItem, true);
}
void PNS_OPTIMIZER::ClearCache( bool aStaticOnly )
{
if(!aStaticOnly)
{
m_cacheTags.clear();
m_cache.Clear();
return;
}
for(CachedItemTags::iterator i = m_cacheTags.begin(); i!= m_cacheTags.end(); ++i)
{
if(i->second.isStatic)
{
m_cache.Remove(i->first);
m_cacheTags.erase(i->first);
}
}
}
bool PNS_OPTIMIZER::checkColliding ( PNS_ITEM *aItem, bool aUpdateCache )
{
CacheVisitor v(aItem, m_world, m_collisionKindMask);
return m_world->CheckColliding(aItem);
// something is wrong with the cache, need to investigate.
m_cache.Query(aItem->GetShape(), m_world->GetMaxClearance(), v, false);
if(!v.m_collidingItem)
{
PNS_NODE::OptObstacle obs = m_world->CheckColliding(aItem);
if(obs) {
if(aUpdateCache)
cacheAdd(obs->item);
return true;
}
} else {
m_cacheTags[v.m_collidingItem].hits++;
return true;
}
return false;
}
bool PNS_OPTIMIZER::checkColliding( PNS_LINE *aLine, const SHAPE_LINE_CHAIN& aOptPath )
{
PNS_LINE tmp(*aLine, aOptPath);
return checkColliding(&tmp);
}
bool PNS_OPTIMIZER::mergeObtuse (PNS_LINE *aLine)
{
SHAPE_LINE_CHAIN &line = aLine->GetLine();
int step = line.PointCount() - 3;
int iter = 0;
int segs_pre = line.SegmentCount();
if(step < 0)
return false;
SHAPE_LINE_CHAIN current_path (line);
while(1)
{
iter++;
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if(step > max_step)
step = max_step;
if(step < 2)
{
line = current_path;
return current_path.SegmentCount() < segs_pre;
}
bool found_anything = false;
int n = 0;
while (n < n_segs - step)
{
const SEG s1 = current_path.CSegment(n);
const SEG s2 = current_path.CSegment(n + step);
SEG s1opt, s2opt;
if (DIRECTION_45(s1).IsObtuse(DIRECTION_45(s2)))
{
VECTOR2I ip = *s1.IntersectLines(s2);
if(s1.Distance(ip) <= 1 || s2.Distance(ip) <= 1)
{
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
} else {
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
}
if (DIRECTION_45(s1opt).IsObtuse(DIRECTION_45(s2opt)))
{
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1opt.a);
opt_path.Append(s1opt.b);
opt_path.Append(s2opt.b);
PNS_LINE opt_track (*aLine, opt_path);
if(!checkColliding(&opt_track))
{
current_path.Replace(s1.Index() + 1, s2.Index(), ip);
//removeCachedSegments(aLine, s1.Index(), s2.Index());
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++;
}
if(!found_anything)
{
if( step <= 2 )
{
line = current_path;
return line.SegmentCount() < segs_pre;
}
step --;
}
}
return line.SegmentCount() < segs_pre;
}
bool PNS_OPTIMIZER::mergeFull(PNS_LINE *aLine)
{
SHAPE_LINE_CHAIN &line = aLine->GetLine();
int step = line.SegmentCount() - 1;
int segs_pre = line.SegmentCount();
line.Simplify();
if(step < 0)
return false;
SHAPE_LINE_CHAIN current_path (line);
while(1)
{
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if(step > max_step)
step = max_step;
if(step < 1)
break;
bool found_anything = mergeStep(aLine, current_path, step);
if(!found_anything)
step --;
}
aLine->SetShape(current_path);
return current_path.SegmentCount() < segs_pre;
}
bool PNS_OPTIMIZER::Optimize ( PNS_LINE *aLine, PNS_LINE *aResult , int aStartVertex , int aEndVertex )
{
if(!aResult)
aResult = aLine;
else
*aResult = *aLine;
m_keepPostures = false;
bool rv = false;
if(m_effortLevel & MERGE_SEGMENTS)
rv |= mergeFull(aResult);
if(m_effortLevel & MERGE_OBTUSE)
rv |= mergeObtuse(aResult);
if(m_effortLevel & SMART_PADS)
rv |= runSmartPads(aResult);
return rv;
}
bool PNS_OPTIMIZER::mergeStep ( PNS_LINE *aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
{
int n = 0;
int n_segs = aCurrentPath.SegmentCount();
int cost_orig = PNS_COST_ESTIMATOR::CornerCost(aCurrentPath);
if(aLine->GetCLine().SegmentCount() < 4)
return false;
DIRECTION_45 orig_start (aLine->GetCLine().CSegment(0));
DIRECTION_45 orig_end (aLine->GetCLine().CSegment(-1));
while (n < n_segs - step )
{
const SEG s1 = aCurrentPath.CSegment(n);
const SEG s2 = aCurrentPath.CSegment(n + step);
SHAPE_LINE_CHAIN path[2], *picked = NULL;
int cost[2];
for(int i = 0; i < 2; i++)
{
bool postureMatch = true;
SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace(s1.a, s2.b, i);
cost[i] = INT_MAX;
if ( n == 0 && orig_start != DIRECTION_45( bypass.CSegment(0) ) )
postureMatch = false;
else if (n == n_segs-step && orig_end != DIRECTION_45( bypass.CSegment(-1)))
postureMatch = false;
if((postureMatch || !m_keepPostures) && !checkColliding(aLine, bypass))
{
path[i] = aCurrentPath;
path[i].Replace(s1.Index(), s2.Index(), bypass);
path[i].Simplify();
cost[i] = PNS_COST_ESTIMATOR::CornerCost(path[i]);
}
}
if(cost[0] < cost_orig && cost[0] < cost[1])
picked = &path[0];
else if (cost[1] < cost_orig)
picked = &path[1];
if(picked)
{
n_segs = aCurrentPath.SegmentCount();
aCurrentPath = *picked;
return true;
}
n++;
}
return false;
}
PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::circleBreakouts( int aWidth, const SHAPE *aShape, bool aPermitDiagonal ) const
{
BreakoutList breakouts;
for(int angle = 0; angle < 360; angle += 45)
{
const SHAPE_CIRCLE *cir = static_cast<const SHAPE_CIRCLE *> (aShape);
SHAPE_LINE_CHAIN l;
VECTOR2I p0 = cir->GetCenter ();
VECTOR2I v0 (cir->GetRadius() * M_SQRT2, 0);
l.Append ( p0 );
l.Append ( p0 + v0.Rotate ( angle * M_PI / 180.0 ) );
breakouts.push_back(l);
}
return breakouts;
}
PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::rectBreakouts( int aWidth, const SHAPE *aShape, bool aPermitDiagonal ) const
{
const SHAPE_RECT *rect = static_cast<const SHAPE_RECT *>(aShape);
VECTOR2I s = rect->GetSize(), c = rect->GetPosition() + VECTOR2I (s.x / 2, s.y / 2);
BreakoutList breakouts;
VECTOR2I d_offset;
d_offset.x = (s.x > s.y) ? (s.x - s.y) / 2 : 0;
d_offset.y = (s.x < s.y) ? (s.y - s.x) / 2 : 0;
VECTOR2I d_vert = VECTOR2I ( 0, s.y / 2 + aWidth);
VECTOR2I d_horiz = VECTOR2I ( s.x / 2 + aWidth, 0);
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c + d_horiz ) );
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c - d_horiz ) );
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c + d_vert ) );
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c - d_vert ) );
if(aPermitDiagonal)
{
int l = aWidth + std::min(s.x, s.y) / 2;
VECTOR2I d_diag ;
if(s.x >= s.y)
{
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c + d_offset, c + d_offset + VECTOR2I(l, l)));
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c + d_offset, c + d_offset - VECTOR2I(-l, l)));
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c - d_offset, c - d_offset + VECTOR2I(-l, l)));
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c - d_offset, c - d_offset - VECTOR2I(l, l)));
} else {
// fixme: this could be done more efficiently
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c + d_offset, c + d_offset + VECTOR2I(l, l)));
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c - d_offset, c - d_offset - VECTOR2I(-l, l)));
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c + d_offset, c + d_offset + VECTOR2I(-l, l)));
breakouts.push_back ( SHAPE_LINE_CHAIN ( c, c - d_offset, c - d_offset - VECTOR2I(l, l)));
}
}
return breakouts;
}
PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::computeBreakouts( int aWidth, const PNS_ITEM *aItem, bool aPermitDiagonal ) const
{
switch(aItem->GetKind())
{
case PNS_ITEM::VIA:
{
const PNS_VIA *via = static_cast<const PNS_VIA *> (aItem);
return circleBreakouts ( aWidth, via->GetShape(), aPermitDiagonal );
}
case PNS_ITEM::SOLID:
{
const SHAPE *shape = aItem->GetShape();
switch(shape->Type())
{
case SH_RECT:
return rectBreakouts (aWidth, shape, aPermitDiagonal);
case SH_CIRCLE:
return circleBreakouts (aWidth, shape, aPermitDiagonal);
default:
break;
}
}
default:
break;
}
return BreakoutList();
}
PNS_ITEM *PNS_OPTIMIZER::findPadOrVia ( int aLayer, int aNet, const VECTOR2I& aP) const
{
PNS_NODE::OptJoint jt = m_world->FindJoint ( aP, aLayer, aNet );
if(!jt)
return NULL;
BOOST_FOREACH (PNS_ITEM *item, jt->GetLinkList() )
{
if(item->GetKind() == PNS_ITEM::VIA || item->GetKind() == PNS_ITEM::SOLID)
return item;
}
return NULL;
}
int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE *aLine, PNS_ITEM *aPad, bool aEnd, int aEndVertex )
{
int min_cost = INT_MAX;//PNS_COST_ESTIMATOR::CornerCost( line );
int min_len = INT_MAX;
DIRECTION_45 dir;
const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_UNDEFINED;
typedef pair<int, SHAPE_LINE_CHAIN> RtVariant;
vector<RtVariant> variants;
BreakoutList breakouts = computeBreakouts( aLine->GetWidth(), aPad, true );
SHAPE_LINE_CHAIN line = (aEnd ? aLine->GetCLine().Reverse() : aLine->GetCLine());
//bool startDiagonal = DIRECTION_45( line.CSegment(0) ).IsDiagonal();
int p_end = min (aEndVertex, min (3 , line.PointCount() - 1));
for (int p = 1; p <= p_end; p++)
{
BOOST_FOREACH(SHAPE_LINE_CHAIN& l, breakouts)
{
//PNSDisplayDebugLine (l, 0);
for(int diag = 0; diag < 2; diag++)
{
SHAPE_LINE_CHAIN v;
SHAPE_LINE_CHAIN connect = dir.BuildInitialTrace( l.CPoint(-1), line.CPoint(p), diag == 0);
DIRECTION_45 dir_bkout ( l.CSegment(-1 ));
//DIRECTION_45 dir_head ( line.CSegment(p + 1));
int ang1 = dir_bkout.Angle ( DIRECTION_45(connect.CSegment(0) ));
int ang2 = 0;
//int ang2 = dir_head.Angle ( DIRECTION_45(connect.CSegment(-1) ));
if( (ang1 | ang2) & ForbiddenAngles )
continue;
if(l.Length() > line.Length())
continue;
v = l;
v.Append ( connect );
for(int i = p + 1; i < line.PointCount(); i++)
v.Append( line.CPoint(i) );
PNS_LINE tmp(*aLine, v);
//tmp.GetLine().Simplify();
int cc = tmp.CountCorners(ForbiddenAngles);
if(cc == 0)
{
RtVariant vp;
vp.first = p;
vp.second = aEnd ? v.Reverse() : v;
vp.second.Simplify();
variants.push_back(vp);
}
}
}
}
SHAPE_LINE_CHAIN l_best;
bool found = false;
int p_best = -1;
BOOST_FOREACH(RtVariant& vp, variants)
{
PNS_LINE tmp (*aLine, vp.second);
int cost = PNS_COST_ESTIMATOR::CornerCost(vp.second);
int len = vp.second.Length();
if(!checkColliding(&tmp))
{
/* if(aEnd)
PNSDisplayDebugLine (l_best, 6);
else
PNSDisplayDebugLine (l_best, 5);*/
if(cost < min_cost || (cost == min_cost && len < min_len))
{
l_best = vp.second;
p_best = vp.first;
found = true;
//if(cost == min_cost)
if(cost == min_cost)
min_len = std::min(len, min_len);
min_cost = std::min(cost, min_cost);
}
}
}
if(found)
{
// printf("end: %d, p-best: %d, p-end: %d, p-total: %d\n", aEnd, p_best, p_end, l_best.PointCount());
// if(!aEnd)
// PNSDisplayDebugLine (l_best, 5);
// else
aLine->SetShape(l_best);
return p_best;
}
return -1;
}
bool PNS_OPTIMIZER::runSmartPads(PNS_LINE *aLine)
{
SHAPE_LINE_CHAIN& line = aLine->GetLine();
if (line.PointCount() < 3)
return false;
VECTOR2I p_start = line.CPoint(0), p_end = line.CPoint(-1);
PNS_ITEM *startPad = findPadOrVia( aLine->GetLayer(), aLine->GetNet(), p_start);
PNS_ITEM *endPad = findPadOrVia( aLine->GetLayer(), aLine->GetNet(), p_end);
int vtx = -1;
if(startPad)
vtx = smartPadsSingle(aLine, startPad, false, 3);
if(endPad)
smartPadsSingle(aLine, endPad, true, vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx);
aLine->GetLine().Simplify();
return true;
}
bool PNS_OPTIMIZER::Optimize ( PNS_LINE *aLine, int aEffortLevel, PNS_NODE *aWorld )
{
PNS_OPTIMIZER opt( aWorld ? aWorld : aLine->GetWorld() );
opt.SetEffortLevel (aEffortLevel);
opt.SetCollisionMask(-1);
return opt.Optimize(aLine);
}
/*
* 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/>.
*/
#ifndef __PNS_OPTIMIZER_H
#define __PNS_OPTIMIZER_H
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <geometry/shape_index_list.h>
#include <geometry/shape_line_chain.h>
class PNS_NODE;
class PNS_LINE;
class PNS_ROUTER;
/**
* Class PNS_COST_ESTIMATOR
*
* Calculates the cost of a given line, taking corner angles and total length into account.
**/
class PNS_COST_ESTIMATOR
{
public:
PNS_COST_ESTIMATOR():
m_lengthCost (0),
m_cornerCost (0)
{};
PNS_COST_ESTIMATOR(const PNS_COST_ESTIMATOR &b):
m_lengthCost (b.m_lengthCost),
m_cornerCost (b.m_cornerCost)
{};
~PNS_COST_ESTIMATOR() {};
static int CornerCost ( const SEG& a, const SEG& b);
static int CornerCost ( const SHAPE_LINE_CHAIN& aLine );
static int CornerCost ( const PNS_LINE& aLine);
void Add(PNS_LINE &aLine);
void Remove (PNS_LINE &aLine);
void Replace(PNS_LINE &aOldLine, PNS_LINE& aNewLine);
bool IsBetter( PNS_COST_ESTIMATOR& aOther, double aLengthTollerance, double aCornerTollerace ) const;
double GetLengthCost() const { return m_lengthCost; }
double GetCornerCost() const { return m_cornerCost; }
private:
double m_lengthCost;
int m_cornerCost;
};
/**
* Class PNS_OPTIMIZER
*
* Performs various optimizations of the lines being routed, attempting to make the lines shorter
* and less cornery. There are 3 kinds of optimizations so far:
* - Merging obtuse segments (MERGE_OBTUSE): tries to join together as many
* obtuse segments as possible without causing collisions
* - Rerouting path between pair of line corners with a 2-segment "\__" line and iteratively repeating
* the procedure as long as the total cost of the line keeps decreasing
* - "Smart Pads" - that is, rerouting pad/via exits to make them look nice (SMART_PADS).
**/
class PNS_OPTIMIZER
{
public:
enum OptimizationEffort {
MERGE_SEGMENTS = 0x1,
SMART_PADS = 0x2,
MERGE_OBTUSE = 0x4
};
PNS_OPTIMIZER( PNS_NODE *aWorld );
~PNS_OPTIMIZER();
///> a quick shortcut to optmize a line without creating and setting up an optimizer
static bool Optimize ( PNS_LINE *aLine, int aEffortLevel, PNS_NODE *aWorld = NULL );
bool Optimize ( PNS_LINE *aLine, PNS_LINE *aResult = NULL, int aStartVertex = 0, int aEndVertex = -1);
void SetWorld(PNS_NODE *aNode) { m_world = aNode; }
void CacheStaticItem (PNS_ITEM *aItem);
void CacheRemove( PNS_ITEM *aItem );
void ClearCache( bool aStaticOnly = false );
void SetCollisionMask ( int aMask )
{
m_collisionKindMask = aMask;
}
void SetEffortLevel ( int aEffort )
{
m_effortLevel = aEffort;
}
private:
static const int MaxCachedItems = 256;
typedef std::vector<SHAPE_LINE_CHAIN> BreakoutList;
struct CacheVisitor;
struct CachedItem
{
int hits;
bool isStatic;
};
bool mergeObtuse (PNS_LINE *aLine);
bool mergeFull (PNS_LINE *aLine);
bool removeUglyCorners (PNS_LINE *aLine);
bool runSmartPads(PNS_LINE *aLine);
bool mergeStep ( PNS_LINE *aLine, SHAPE_LINE_CHAIN& aCurrentLine, int step );
bool checkColliding( PNS_ITEM *aItem, bool aUpdateCache = true );
bool checkColliding( PNS_LINE *aLine, const SHAPE_LINE_CHAIN& aOptPath );
void cacheAdd( PNS_ITEM *aItem, bool aIsStatic );
void removeCachedSegments (PNS_LINE *aLine, int aStartVertex = 0, int aEndVertex = -1);
BreakoutList circleBreakouts( int aWidth, const SHAPE *aShape, bool aPermitDiagonal ) const;
BreakoutList rectBreakouts( int aWidth, const SHAPE *aShape, bool aPermitDiagonal ) const;
BreakoutList ovalBreakouts( int aWidth, const SHAPE *aShape, bool aPermitDiagonal ) const;
BreakoutList computeBreakouts( int aWidth, const PNS_ITEM *aItem, bool aPermitDiagonal ) const;
int smartPadsSingle( PNS_LINE *aLine, PNS_ITEM *aPad, bool aEnd, int aEndVertex );
PNS_ITEM *findPadOrVia ( int aLayer, int aNet, const VECTOR2I& aP) const;
SHAPE_INDEX_LIST<PNS_ITEM *> m_cache;
typedef boost::unordered_map<PNS_ITEM*, CachedItem> CachedItemTags;
CachedItemTags m_cacheTags;
PNS_NODE *m_world;
int m_collisionKindMask;
int m_effortLevel;
bool m_keepPostures;
};
#endif
/*
* 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 (&current, true);
if(current.EndsWithVia())
DisplayItem( &current.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);
}
}
/*
* 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/>.
*/
#ifndef __PNS_ROUTER_H
#define __PNS_ROUTER_H
#include <list>
#include <boost/optional.hpp>
#include <boost/unordered_set.hpp>
#include <geometry/shape_line_chain.h>
#include "pns_routing_settings.h"
#include "pns_item.h"
#include "pns_itemset.h"
class BOARD;
class BOARD_ITEM;
class D_PAD;
class TRACK;
class SEGVIA;
class PNS_NODE;
class PNS_LINE_PLACER;
class PNS_ITEM;
class PNS_LINE;
class PNS_SOLID;
class PNS_SEGMENT;
class PNS_JOINT;
class PNS_VIA;
class PNS_CLEARANCE_FUNC;
class VIEW_GROUP;
namespace KiGfx {
class VIEW;
class VIEW_GROUP;
};
/**
* Class PNS_ROUTER
*
* Main router class.
*/
class PNS_ROUTER {
private:
enum RouterState {
IDLE,
START_ROUTING,
ROUTE_TRACK,
FINISH_TRACK
};
public:
PNS_ROUTER ();
~PNS_ROUTER ();
static PNS_ROUTER *GetInstance();
void ClearWorld();
void SetBoard( BOARD *aBoard );
void SyncWorld();
void SetView(KiGfx::VIEW *aView);
bool RoutingInProgress() const;
void StartRouting(const VECTOR2I& aP, PNS_ITEM *aItem);
void Move(const VECTOR2I& aP, PNS_ITEM *aItem);
bool FixRoute(const VECTOR2I& aP, PNS_ITEM *aItem);
void StopRouting();
const VECTOR2I GetCurrentEnd() const;
int GetClearance(const PNS_ITEM* a, const PNS_ITEM *b ) const;
PNS_NODE* GetWorld() const
{
return m_world;
}
void FlipPosture();
void DisplayItem ( const PNS_ITEM *aItem, bool aIsHead = false );
void DisplayDebugLine ( const SHAPE_LINE_CHAIN &aLine, int aType = 0, int aWidth = 0);
void DisplayDebugBox ( const BOX2I& aBox, int aType = 0, int aWidth = 0);
void EraseView ( );
void SwitchLayer (int layer );
int GetCurrentLayer() const { return m_currentLayer; }
void ToggleViaPlacement ();
void SetCurrentWidth(int w);
void SetCurrentViaDiameter(int d) { m_currentViaDiameter = d;}
void SetCurrentViaDrill(int d) { m_currentViaDrill = d;}
int GetCurrentWidth() const { return m_currentWidth; }
int GetCurrentViaDiameter() const { return m_currentViaDiameter; }
int GetCurrentViaDrill() const { return m_currentViaDrill; }
int GetCurrentNet() const { return m_currentNet; }
PNS_CLEARANCE_FUNC *GetClearanceFunc() const
{
return m_clearanceFunc;
}
bool IsPlacingVia() const
{
return m_placingVia;
}
int NextCopperLayer( bool aUp );
//typedef boost::optional<hoverItem> optHoverItem;
const PNS_ITEMSET QueryHoverItems(const VECTOR2I& aP);
const VECTOR2I SnapToItem( PNS_ITEM *item, VECTOR2I aP, bool& aSplitsSegment );
private:
void clearViewFlags();
//optHoverItem queryHoverItemEx(const VECTOR2I& aP);
PNS_ITEM *pickSingleItem ( PNS_ITEMSET &aItems ) const; //std::vector<PNS_ITEM*> aItems) const;
void splitAdjacentSegments(PNS_NODE *aNode, PNS_ITEM *aSeg, const VECTOR2I& aP ); //optHoverItem& aItem);
void commitRouting ( PNS_NODE *aNode );
PNS_NODE *removeLoops ( PNS_NODE *aNode, PNS_SEGMENT *aLatestSeg );
PNS_NODE *removeLoops ( PNS_NODE *aNode, PNS_LINE *aNewLine );
PNS_VIA *checkLoneVia ( PNS_JOINT* aJoint ) const;
PNS_ITEM *syncPad( D_PAD *aPad );
PNS_ITEM *syncTrack( TRACK *aTrack );
PNS_ITEM *syncVia( SEGVIA *aVia );
void commitPad( PNS_SOLID *aPad );
void commitSegment( PNS_SEGMENT *aTrack );
void commitVia( PNS_VIA *aVia );
void highlightCurrent( bool enabled );
int m_currentLayer;
int m_currentNet;
int m_currentWidth;
int m_currentViaDiameter;
int m_currentViaDrill;
bool m_start_diagonal;
RouterState m_state;
BOARD *m_board;
PNS_NODE *m_world;
PNS_LINE_PLACER *m_placer;
KiGfx::VIEW *m_view;
KiGfx::VIEW_GROUP *m_previewItems;
VECTOR2I m_currentEnd;
VECTOR2I m_currentStart;
VECTOR2I m_originalStart;
bool m_placingVia;
bool m_startsOnVia;
// optHoverItem m_startItem, m_endItem;
PNS_ROUTING_SETTINGS m_settings;
PNS_CLEARANCE_FUNC *m_clearanceFunc;
boost::unordered_set<BOARD_ITEM *> m_hiddenItems;
};
#endif
\ No newline at end of file
/*
* 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/>.
*/
#ifndef __PNS_ROUTER_SETTINGS
#define __PNS_ROUTER_SETTINGS
///> Routing modes
enum PNS_MODE {
RM_Ignore = 0, ///> Ignore collisions
RM_Shove, ///> Only shove
RM_Walkaround, ///> Only walkaround
RM_Smart ///> Guess what's better
};
class PNS_ROUTING_SETTINGS
{
public:
PNS_MODE m_routingMode;
bool m_removeLoops;
bool m_smartPads;
bool m_suggestEnding;
bool m_shoveOnRequest;
bool m_changePostures;
bool m_followMouse;
int m_lineWidth;
int m_viaDiameter;
int m_viaDrill;
int m_preferredLayer;
int m_walkaroundIterationLimit;
int m_shoveIterationLimit;
};
#endif
/*
* 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/>.
*/
#ifndef __PNS_SEGMENT_H
#define __PNS_SEGMENT_H
#include <math/vector2d.h>
#include <geometry/seg.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include "pns_item.h"
#include "pns_line.h"
class PNS_NODE;
class PNS_SEGMENT : public PNS_ITEM {
public:
PNS_SEGMENT ():
PNS_ITEM(SEGMENT)
{};
PNS_SEGMENT (const SEG& aSeg, int aNet):
PNS_ITEM(SEGMENT)
{
m_net = aNet;
m_shape.Clear();
m_shape.Append(aSeg.a);
m_shape.Append(aSeg.b);
};
PNS_SEGMENT (const PNS_LINE &aParentLine, const SEG& aSeg):
PNS_ITEM(SEGMENT)
{
m_net = aParentLine.GetNet();
m_layers = aParentLine.GetLayers();
m_width = aParentLine.GetWidth();
m_shape.Clear();
m_shape.Append(aSeg.a);
m_shape.Append(aSeg.b);
};
PNS_SEGMENT *Clone() const;
const SHAPE* GetShape() const {
return static_cast<const SHAPE *>(&m_shape);
}
void SetLayer (int aLayer)
{
SetLayers (PNS_LAYERSET ( aLayer ));
}
int GetLayer() const
{
return GetLayers().Start();
}
const SHAPE_LINE_CHAIN& GetCLine() const
{
return m_shape;
}
void SetWidth( int aWidth )
{
m_width = aWidth;
}
int GetWidth() const {
return m_width;
}
const SEG GetSeg() const {
assert(m_shape.PointCount() >= 1);
if(m_shape.PointCount() == 1)
return SEG(m_shape.CPoint(0), m_shape.CPoint(0));
return SEG(m_shape.CPoint(0), m_shape.CPoint(1));
}
void SetEnds ( const VECTOR2I& a, const VECTOR2I& b)
{
m_shape.Clear();
m_shape.Append(a);
m_shape.Append(b);
}
void SwapEnds()
{
m_shape = m_shape.Reverse();
}
const SHAPE_LINE_CHAIN Hull(int aClearance, int aWalkaroundThickness) const;
private:
SHAPE_LINE_CHAIN m_shape;
int m_width;
};
#endif
/*
* 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 <deque>
#include <cassert>
#include <wx/timer.h>
#include "trace.h"
#include "pns_line.h"
#include "pns_node.h"
#include "pns_walkaround.h"
#include "pns_shove.h"
#include "pns_optimizer.h"
#include "pns_via.h"
#include "pns_utils.h"
#include <profile.h>
using namespace std;
PNS_SHOVE::PNS_SHOVE( PNS_NODE *aWorld )
{
m_root = aWorld;
m_iterLimit = 100;
};
PNS_SHOVE::~PNS_SHOVE()
{
}
struct range {
range()
{
min_v = max_v = -1;
}
void add ( int x )
{
if(min_v < 0) min_v = x;
if(max_v < 0) max_v = x;
if(x < min_v)
min_v = x;
else if (x > max_v)
max_v = x;
}
int start()
{
return min_v;
}
int end()
{
return max_v;
}
int min_v, max_v;
};
// fixme: this is damn f***ing inefficient. And fails much too often due to broken direction finding algorithm.
bool PNS_SHOVE::tryShove(PNS_NODE *aNode, PNS_LINE *aHead, PNS_LINE *aObstacle, PNS_SEGMENT& aObstacleSeg, PNS_LINE *aResult, bool aInvertWinding )
{
const SHAPE_LINE_CHAIN &head = aHead->GetCLine();
bool cw = false;
int i;
if(aHead->EndsWithVia() && !aHead->GetLayers().Overlaps(aObstacle->GetLayers()))
{
int clearance = aNode->GetClearance(aHead, aObstacle);
SHAPE_LINE_CHAIN hull = aHead->GetVia().Hull( clearance - aObstacle->GetWidth() / 2 );
//SHAPE_LINE_CHAIN path_pre, path_walk_cw, path_walk_ccw, path_post;
SHAPE_LINE_CHAIN path_cw, path_ccw, *path;
aObstacle->NewWalkaround(hull, path_cw, true);
aObstacle->NewWalkaround(hull, path_ccw, false);
path = path_ccw.Length() < path_cw.Length() ? &path_ccw : &path_cw;
aResult->SetShape(*path);
//PNSDisplayDebugLine (*path, 5);
if(!aResult->Is45Degree())
{
//printf("polyset non-45\npoly %s\nendpolyset\n", aResult->GetCLine().Format().c_str());
}
/*... special case for vias? */
return !aNode->CheckColliding(aResult, aHead);
}
int ns = head.SegmentCount();
if(aHead->EndsWithVia())
ns ++;
for(i = 0; i < head.SegmentCount(); i++)
{
const PNS_SEGMENT hs (*aHead, head.CSegment(i));
if(aNode->CheckColliding(&hs, aObstacle))
{
VECTOR2I v1 = hs.GetSeg().b - hs.GetSeg().a;
VECTOR2I v2 = aObstacleSeg.GetSeg().b - aObstacleSeg.GetSeg().a;
VECTOR2I::extended_type det = v1.Cross(v2);
if(det > 0)
cw = true;
else
cw = false;
break;
}
}
if(aInvertWinding)
{
if(cw)
cw = false;
else
cw = true;
}
PNS_LINE shoved (*aObstacle);
int clearance = aNode->GetClearance(aHead, aObstacle);
range r;
for(i = 0; i < ns; i++)
{
SHAPE_LINE_CHAIN hull;
if(i < head.SegmentCount())
{
const PNS_SEGMENT hs (*aHead, head.CSegment(i));
hull = hs.Hull( clearance, 0 );
} else
hull = aHead->GetVia().Hull( clearance - aObstacle->GetWidth() / 2);
SHAPE_LINE_CHAIN path_pre, path_walk, path_post, tmp;
SHAPE_LINE_CHAIN path_pre2, path_walk2, path_post2;
//shoved.NewWalkaround(hull, path_pre, path_walk, path_post, cw);
shoved.NewWalkaround(hull, path_pre, path_walk, path_post, cw);
/*if(path_pre != path_pre2 || path_post != path_post2 || path_walk != path_walk2 )
{
TRACE(5, "polyset orig\npoly %s\npoly %s\npoly %s\nendpolyset\n", path_pre.Format().c_str() % path_walk.Format().c_str() % path_post.Format().c_str());
TRACE(5, "polyset err\npoly %s\npoly %s\npoly %s\nendpolyset\n", path_pre2.Format().c_str() % path_walk2.Format().c_str() % path_post2.Format().c_str());
}*/
tmp = shoved.GetCLine();
if(path_walk.SegmentCount())
r.add(i);
path_pre.Append(path_walk);
path_pre.Append(path_post);
path_pre.Simplify();
shoved.SetShape(path_pre);
// shoved.SetAffectedRange ( start, end );
*aResult = shoved;
if(!aResult->Is45Degree())
{
//TRACE(5, "polyset non-45\npoly %s\npoly %s\npoly %s\nendpolyset\n", tmp.Format().c_str() % hull.Format().c_str() % aResult->GetCLine().Format().c_str());
}
}
TRACE(2, "CW %d affectedRange %d-%d [total %d]", (cw?1:0) % r.start() % r.end() % ns);
return !aNode->CheckColliding(aResult, aHead);
}
PNS_SHOVE::ShoveStatus PNS_SHOVE::shoveSingleLine(PNS_NODE *aNode, PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_SEGMENT& aObstacleSeg, PNS_LINE *aResult )
{
bool rv = tryShove(aNode, aCurrent, aObstacle, aObstacleSeg, aResult, false);
if( !rv )
rv = tryShove(aNode, aCurrent, aObstacle, aObstacleSeg, aResult, true);
if( !rv )
{
TRACEn(2, "Shove failed" );
return SH_INCOMPLETE;
}
aResult->GetLine().Simplify();
const SHAPE_LINE_CHAIN& sh_shoved = aResult->GetCLine();
const SHAPE_LINE_CHAIN& sh_orig = aObstacle->GetCLine();
if(sh_shoved.SegmentCount() > 1 && sh_shoved.CPoint(0) == sh_orig.CPoint(0) && sh_shoved.CPoint(-1) == sh_orig.CPoint(-1) )
return SH_OK;
else if (!sh_shoved.SegmentCount())
return SH_NULL;
else
return SH_INCOMPLETE;
}
bool PNS_SHOVE::reduceSpringback( PNS_LINE *aHead )
{
bool rv = false;
while(!m_nodeStack.empty())
{
SpringbackTag st_stack = m_nodeStack.back();
bool tail_ok = true;
if(!st_stack.node->CheckColliding(aHead) && tail_ok)
{
rv = true;
delete st_stack.node;
m_nodeStack.pop_back();
} else
break;
}
return rv;
}
bool PNS_SHOVE::pushSpringback( PNS_NODE *aNode, PNS_LINE *aHead, const PNS_COST_ESTIMATOR& aCost )
{
BOX2I headBB = aHead->GetCLine().BBox();
SpringbackTag st;
st.node = aNode;
st.cost = aCost;
st.length = std::max(headBB.GetWidth(), headBB.GetHeight());;
m_nodeStack.push_back(st);
return true;
}
const PNS_COST_ESTIMATOR PNS_SHOVE::TotalCost() const
{
if(m_nodeStack.empty())
return PNS_COST_ESTIMATOR();
else
return m_nodeStack.back().cost;
}
PNS_SHOVE::ShoveStatus PNS_SHOVE::ShoveLines(PNS_LINE* aCurrentHead)
{
stack <PNS_LINE *> lineStack;
PNS_NODE *node, *parent;
PNS_VIA *headVia = NULL;
bool fail = false;
int iter = 0;
PNS_LINE *head = aCurrentHead->Clone();
reduceSpringback(aCurrentHead);
parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().node;
node = parent->Branch();
lineStack.push(head);
//node->Add(tail);
node->Add(head);
if(head->EndsWithVia())
{
headVia = head->GetVia().Clone();
node->Add( headVia );
}
PNS_OPTIMIZER optimizer (node);
optimizer.SetEffortLevel (PNS_OPTIMIZER::MERGE_SEGMENTS | PNS_OPTIMIZER::SMART_PADS);
optimizer.SetCollisionMask( -1 );
PNS_NODE::OptObstacle nearest;
optimizer.CacheStaticItem(head);
if(headVia)
optimizer.CacheStaticItem(headVia);
TRACE(1, "ShoveStart [root: %d jts, node: %d jts]", m_root->JointCount() % node->JointCount());
//PNS_ITEM *lastWalkSolid = NULL;
prof_counter totalRealTime;
wxLongLong t_start = wxGetLocalTimeMillis();
while(!lineStack.empty())
{
wxLongLong t_cur = wxGetLocalTimeMillis();
if ((t_cur - t_start).ToLong() > ShoveTimeLimit)
{
fail = true;
break;
}
iter++;
if(iter > m_iterLimit)
{
fail = true;
break;
}
PNS_LINE *currentLine = lineStack.top();
prof_start( &totalRealTime, false );
nearest = node->NearestObstacle(currentLine, PNS_ITEM::ANY);
prof_end( &totalRealTime );
TRACE(2,"t-nearestObstacle %lld us", (totalRealTime.value ));
if(!nearest)
{
if(lineStack.size() > 1)
{
PNS_LINE *original = lineStack.top();
PNS_LINE optimized;
int r_start, r_end;
original->GetAffectedRange(r_start, r_end);
TRACE(1, "Iter %d optimize-line [range %d-%d, total %d]", iter % r_start % r_end % original->GetCLine().PointCount() );
//lastWalkSolid = NULL;
prof_start( &totalRealTime, false );
if( optimizer.Optimize(original, &optimized) )
{
node->Remove(original);
optimizer.CacheRemove(original);
node->Add(&optimized);
if(original->BelongsTo(node))
delete original;
}
prof_end( &totalRealTime );
TRACE(2,"t-optimizeObstacle %lld us", (totalRealTime.value ));
}
lineStack.pop();
} else {
switch(nearest->item->GetKind())
{
case PNS_ITEM::SEGMENT:
{
TRACE(1, "Iter %d shove-line", iter );
PNS_SEGMENT *pseg = static_cast<PNS_SEGMENT*>(nearest->item);
PNS_LINE *collidingLine = node->AssembleLine(pseg);
PNS_LINE *shovedLine = collidingLine->CloneProperties();
prof_start( &totalRealTime, false );
ShoveStatus st = shoveSingleLine(node, currentLine, collidingLine, *pseg, shovedLine);
prof_end( &totalRealTime );
TRACE(2,"t-shoveSingle %lld us", (totalRealTime.value ));
if(st == SH_OK)
{
node->Replace(collidingLine, shovedLine);
if(collidingLine->BelongsTo( node ))
delete collidingLine;
optimizer.CacheRemove(collidingLine);
lineStack.push( shovedLine );
} else
fail = true;
//lastWalkSolid = NULL;
break;
} // case SEGMENT
case PNS_ITEM::SOLID:
case PNS_ITEM::VIA:
{
TRACE(1, "Iter %d walkaround-solid [%p]", iter % nearest->item );
if(lineStack.size() == 1)
{
fail = true;
break;
}
/* if(lastWalkSolid == nearest->item)
{
fail = true;
break;
}*/
PNS_WALKAROUND walkaround (node);
PNS_LINE *walkaroundLine = currentLine->CloneProperties();
walkaround.SetSolidsOnly(true);
walkaround.SetSingleDirection(true);
prof_start( &totalRealTime, false );
walkaround.Route(*currentLine, *walkaroundLine, false);
prof_end( &totalRealTime );
TRACE(2,"t-walkSolid %lld us", (totalRealTime.value ));
node->Replace(currentLine, walkaroundLine);
if(currentLine->BelongsTo( node ))
delete currentLine;
optimizer.CacheRemove(currentLine);
lineStack.top() = walkaroundLine;
//lastWalkSolid = nearest->item;
break;
}
default:
break;
} // switch
if(fail)
break;
}
}
node->Remove(head);
delete head;
if(headVia)
{
node->Remove(headVia);
delete headVia;
}
TRACE(1, "Shove status : %s after %d iterations" , (fail ? "FAILED" : "OK") % iter );
if(!fail)
{
pushSpringback(node, aCurrentHead, PNS_COST_ESTIMATOR());
return SH_OK;
} else {
delete node;
return SH_INCOMPLETE;
}
}
/*
* 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/>.
*/
#ifndef __PNS_SHOVE_H
#define __PNS_SHOVE_H
#include <vector>
#include <stack>
#include "pns_optimizer.h"
class PNS_LINE;
class PNS_NODE;
class PNS_ROUTER;
class PNS_SHOVE {
public:
PNS_SHOVE(PNS_NODE *aWorld);
~PNS_SHOVE();
enum ShoveStatus {
SH_OK = 0,
SH_NULL,
SH_INCOMPLETE
};
ShoveStatus ShoveLines(PNS_LINE* aCurrentHead);
PNS_NODE *GetCurrentNode()
{
return m_nodeStack.empty() ? m_root : m_nodeStack.back().node;
}
const PNS_COST_ESTIMATOR TotalCost() const;
void Reset();
void KillChildNodes();
private:
static const int ShoveTimeLimit = 3000;
bool tryShove(PNS_NODE *aWorld, PNS_LINE *aTrack, PNS_LINE * aObstacle, PNS_SEGMENT& aObstacleSeg, PNS_LINE *aResult, bool aInvertWinding );
ShoveStatus shoveSingleLine(PNS_NODE *aNode, PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_SEGMENT& aObstacleSeg, PNS_LINE *aResult );
bool reduceSpringback( PNS_LINE *aHead );
bool pushSpringback( PNS_NODE *aNode, PNS_LINE *aHead, const PNS_COST_ESTIMATOR& aCost );
struct SpringbackTag {
int64_t length;
int segments;
VECTOR2I p;
PNS_NODE *node;
PNS_COST_ESTIMATOR cost;
};
std::vector<SpringbackTag> m_nodeStack;
PNS_NODE *m_root;
PNS_NODE *m_currentNode;
int m_iterLimit;
};
#endif
/*
* 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 <math/vector2d.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_circle.h>
#include "pns_solid.h"
#include "pns_utils.h"
const SHAPE_LINE_CHAIN PNS_SOLID::Hull(int aClearance, int aWalkaroundThickness) const
{
switch(m_shape->Type())
{
case SH_RECT:
{
SHAPE_RECT *rect = static_cast<SHAPE_RECT*> (m_shape);
return OctagonalHull( rect->GetPosition(),
rect->GetSize(),
aClearance + 1,
0.2 * aClearance );
}
case SH_CIRCLE:
{
SHAPE_CIRCLE *circle = static_cast<SHAPE_CIRCLE*> (m_shape);
int r = circle->GetRadius();
return OctagonalHull( circle->GetCenter() - VECTOR2I(r, r),
VECTOR2I(2 * r, 2 * r),
aClearance + 1,
0.52 * (r + aClearance) );
}
default:
break;
}
return SHAPE_LINE_CHAIN();
}
PNS_ITEM *PNS_SOLID::Clone() const
{
// solids are never cloned as the shove algorithm never moves them
assert(false);
}
\ No newline at end of file
/*
* 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/>.
*/
#ifndef __PNS_SOLID_H
#define __PNS_SOLID_H
#include <math/vector2d.h>
#include <geometry/seg.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include "pns_item.h"
class PNS_SOLID : public PNS_ITEM {
public:
PNS_SOLID() : PNS_ITEM(SOLID)
{
m_movable = false;
m_shape = NULL;
}
PNS_ITEM *Clone() const;
const SHAPE* GetShape() const { return m_shape; }
const SHAPE_LINE_CHAIN Hull(int aClearance = 0, int aWalkaroundThickness = 0) const;
void SetShape( SHAPE* shape)
{
if(m_shape)
delete m_shape;
m_shape = shape;
}
const VECTOR2I& GetCenter() const
{
return m_center;
}
void SetCenter( const VECTOR2I& aCenter )
{
m_center = aCenter;
}
private:
VECTOR2I m_center;
SHAPE* m_shape;
};
#endif
/*
* 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 "pns_utils.h"
#include "pns_line.h"
#include "pns_router.h"
const SHAPE_LINE_CHAIN OctagonalHull(const VECTOR2I& aP0, const VECTOR2I& aSize, int aClearance, int aChamfer)
{
SHAPE_LINE_CHAIN s;
s.SetClosed( true );
s.Append(aP0.x - aClearance , aP0.y - aClearance + aChamfer);
s.Append(aP0.x - aClearance + aChamfer, aP0.y - aClearance);
s.Append(aP0.x + aSize.x + aClearance - aChamfer, aP0.y - aClearance);
s.Append(aP0.x + aSize.x + aClearance, aP0.y - aClearance + aChamfer);
s.Append(aP0.x + aSize.x + aClearance, aP0.y + aSize.y + aClearance - aChamfer);
s.Append(aP0.x + aSize.x + aClearance - aChamfer, aP0.y + aSize.y + aClearance);
s.Append(aP0.x - aClearance + aChamfer, aP0.y + aSize.y + aClearance);
s.Append(aP0.x - aClearance, aP0.y + aSize.y + aClearance - aChamfer);
return s;
}
/*
* 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/>.
*/
#ifndef __PNS_UTILS_H
#define __PNS_UTILS_H
#include <math/vector2d.h>
#include <geometry/shape_line_chain.h>
/** Various utility functions */
const SHAPE_LINE_CHAIN OctagonalHull(const VECTOR2I& aP0, const VECTOR2I& aSize, int aClearance, int aChamfer);
#endif // __PNS_UTILS_H
/*
* 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 "pns_via.h"
#include "pns_node.h"
#include "pns_utils.h"
#include <geometry/shape_rect.h>
static bool Circle2Circle( VECTOR2I p1, VECTOR2I p2, int r1, int r2, VECTOR2I& force )
{
int mindist = r1 + r2;
VECTOR2I delta = p2 - p1;
int dist = delta.EuclideanNorm();
if(dist >= mindist)
return false;
force = delta.Resize(abs(mindist - dist) + 1);
return true;
};
static bool Rect2Circle( VECTOR2I rp0, VECTOR2I rsize, VECTOR2I cc, int cr, VECTOR2I& force )
{
VECTOR2I vts[] = { VECTOR2I(rp0.x, rp0.y),
VECTOR2I(rp0.x, rp0.y + rsize.y),
VECTOR2I(rp0.x + rsize.x, rp0.y + rsize.y),
VECTOR2I(rp0.x + rsize.x, rp0.y),
VECTOR2I(rp0.x, rp0.y) };
int dist = INT_MAX;
VECTOR2I nearest;
for (int i = 0; i < 4; i++)
{
SEG s(vts[i], vts[i+1]);
VECTOR2I pn = s.NearestPoint( cc );
int d = (pn - cc).EuclideanNorm();
if( d < dist )
{
nearest = pn;
dist = d;
}
}
bool inside = cc.x >= rp0.x && cc.x <= (rp0.x + rsize.x)
&& cc.y >= rp0.y && cc.y <= (rp0.y + rsize.y);
VECTOR2I delta = cc - nearest;
if(dist >= cr && !inside)
return false;
if(inside)
force = -delta.Resize(abs(cr + dist) + 1);
else
force = delta.Resize(abs(cr - dist) + 1);
return true;
};
static bool ShPushoutForce ( const SHAPE *shape, VECTOR2I p, int r, VECTOR2I& force, int clearance)
{
switch(shape->Type())
{
case SH_CIRCLE:
{
const SHAPE_CIRCLE *cir = static_cast<const SHAPE_CIRCLE*>(shape);
return Circle2Circle( cir->GetCenter(), p, cir->GetRadius(), r + clearance + 1, force );
}
case SH_RECT:
{
const SHAPE_RECT *rect = static_cast<const SHAPE_RECT*>(shape);
return Rect2Circle( rect->GetPosition(), rect->GetSize(), p, r + clearance + 1, force );
}
default:
return false;
}
return false;
}
bool PNS_VIA::PushoutForce ( PNS_NODE *aNode, const VECTOR2I &aDirection, VECTOR2I& aForce, bool aSolidsOnly, int aMaxIterations)
{
int iter = 0;
PNS_VIA mv ( *this);
VECTOR2I force, totalForce;
while(iter < aMaxIterations)
{
PNS_NODE::OptObstacle obs = aNode->CheckColliding( &mv, aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY);
if(!obs)
break;
int clearance = aNode->GetClearance(obs->item, &mv);
if(iter > 10)
{
VECTOR2I l = - aDirection.Resize(m_diameter / 4);
totalForce += l;
mv.SetPos(mv.GetPos() + l);
}
if( ShPushoutForce(obs->item->GetShape(), mv.GetPos(), mv.GetDiameter() / 2, force, clearance) )
{
totalForce += force;
mv.SetPos(mv.GetPos() + force);
}
iter++;
}
if(iter == aMaxIterations)
return false;
aForce = totalForce;
return true;
}
const SHAPE_LINE_CHAIN PNS_VIA::Hull(int aClearance, int aWalkaroundThickness) const
{
return OctagonalHull( m_pos - VECTOR2I(m_diameter/2, m_diameter/2), VECTOR2I(m_diameter, m_diameter), aClearance + 1, (2*aClearance + m_diameter) * 0.26);
}
\ No newline at end of file
/*
* 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/>.
*/
#ifndef __PNS_VIA_H
#define __PNS_VIA_H
#include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h>
#include "pns_item.h"
class PNS_NODE;
class PNS_VIA : public PNS_ITEM
{
public:
PNS_VIA( ):
PNS_ITEM (VIA) {};
PNS_VIA( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aDiameter, int aNet = -1) :
PNS_ITEM (VIA) {
SetNet(aNet);
SetLayers(aLayers);
m_pos = aPos;
m_diameter = aDiameter;
m_shape = SHAPE_CIRCLE(aPos, aDiameter/2);
};
PNS_VIA(const PNS_VIA& b) : PNS_ITEM(VIA)
{
SetNet(b.GetNet());
SetLayers(b.GetLayers());
m_pos = b.m_pos;
m_diameter = b.m_diameter;
m_shape = SHAPE_CIRCLE(m_pos, m_diameter/2);
}
const VECTOR2I& GetPos() const
{
return m_pos;
}
void SetPos( const VECTOR2I& aPos )
{
m_pos = aPos;
m_shape.SetCenter(aPos);
}
int GetDiameter() const
{
return m_diameter;
}
void SetDiameter(int aDiameter)
{
m_diameter = aDiameter;
m_shape.SetRadius(m_diameter/2);
}
int GetDrill() const
{
return m_drill;
}
void SetDrill(int aDrill)
{
m_drill = aDrill;
}
bool PushoutForce ( PNS_NODE *aNode, const VECTOR2I &aDirection, VECTOR2I& aForce, bool aSolidsOnly = true, int aMaxIterations = 10);
const SHAPE *GetShape() const
{
return &m_shape;
}
PNS_VIA *Clone() const
{
PNS_VIA *v = new PNS_VIA();
v->SetNet(GetNet());
v->SetLayers(GetLayers());
v->m_pos = m_pos;
v->m_diameter = m_diameter;
v->m_shape = SHAPE_CIRCLE(m_pos, m_diameter/2);
return v;
}
const SHAPE_LINE_CHAIN Hull(int aClearance = 0, int aWalkaroundThickness = 0) const;
private:
int m_diameter;
int m_drill;
VECTOR2I m_pos;
SHAPE_CIRCLE m_shape;
};
#endif
/*
* 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 <vector>
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <geometry/shape_line_chain.h>
#include "pns_walkaround.h"
#include "pns_optimizer.h"
#include "pns_utils.h"
#include "pns_router.h"
using namespace std;
using boost::optional;
void PNS_WALKAROUND::start( const PNS_LINE& aInitialPath )
{
m_iteration = 0;
m_iteration_limit = 50;
}
PNS_NODE::OptObstacle PNS_WALKAROUND::nearestObstacle(const PNS_LINE& aPath)
{
return m_world->NearestObstacle ( &aPath, m_solids_only ? (PNS_ITEM::SOLID | PNS_ITEM::VIA) : PNS_ITEM::ANY );
}
PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::singleStep(PNS_LINE& aPath, bool aWindingDirection)
{
optional<PNS_OBSTACLE>& current_obs = aWindingDirection ? m_currentObstacle[0] : m_currentObstacle[1];
bool& prev_recursive = aWindingDirection ? m_recursiveCollision[0] : m_recursiveCollision[1];
if(!current_obs)
return DONE;
SHAPE_LINE_CHAIN path_pre[2], path_walk[2], path_post[2];
VECTOR2I last = aPath.GetCLine().CPoint(-1);
if((current_obs->hull).PointInside(last))
{
m_recursiveBlockageCount ++;
if(m_recursiveBlockageCount < 3)
aPath.GetLine().Append( current_obs->hull.NearestPoint(last) );
else {
aPath = aPath.ClipToNearestObstacle(m_world);
return STUCK;
}
}
aPath.NewWalkaround(current_obs->hull, path_pre[0], path_walk[0], path_post[0], aWindingDirection);
aPath.NewWalkaround(current_obs->hull, path_pre[1], path_walk[1], path_post[1], !aWindingDirection);
int len_pre = path_walk[0].Length();
int len_alt = path_walk[1].Length();
PNS_LINE walk_path (aPath, path_walk[1]);
bool alt_collides = m_world->CheckColliding(&walk_path, m_solids_only ? PNS_ITEM::SOLID : PNS_ITEM::ANY);
SHAPE_LINE_CHAIN pnew;
if(!m_forceSingleDirection && len_alt < len_pre && !alt_collides && !prev_recursive)
{
pnew = path_pre[1];
pnew.Append(path_walk[1]);
pnew.Append(path_post[1]);
current_obs = nearestObstacle(PNS_LINE(aPath, path_post[1]));
prev_recursive = false;
} else {
pnew = path_pre[0];
pnew.Append(path_walk[0]);
pnew.Append(path_post[0]);
current_obs = nearestObstacle(PNS_LINE(aPath, path_walk[0]));
if(!current_obs)
{
prev_recursive = false;
current_obs = nearestObstacle(PNS_LINE(aPath, path_post[0]));
} else
prev_recursive = true;
}
pnew.Simplify();
aPath.SetShape(pnew);
return IN_PROGRESS;
}
PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath, bool aOptimize )
{
PNS_LINE path_cw(aInitialPath), path_ccw(aInitialPath);
WalkaroundStatus s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS;
SHAPE_LINE_CHAIN best_path;
start(aInitialPath);
m_currentObstacle[0] = m_currentObstacle[1] = nearestObstacle(aInitialPath);
m_recursiveBlockageCount = 0;
aWalkPath = aInitialPath;
while(m_iteration < m_iteration_limit)
{
if(s_cw != STUCK)
s_cw = singleStep(path_cw, true);
if(s_ccw != STUCK)
s_ccw = singleStep(path_ccw, false);
if((s_cw == DONE && s_ccw == DONE) || (s_cw == STUCK && s_ccw == STUCK))
{
int len_cw = path_cw.GetCLine().Length();
int len_ccw = path_ccw.GetCLine().Length();
if(m_forceLongerPath)
aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw);
else
aWalkPath = (len_cw < len_ccw ? path_cw : path_ccw);
break;
} else if(s_cw == DONE && !m_forceLongerPath) {
aWalkPath = path_cw;
break;
} else if (s_ccw == DONE && !m_forceLongerPath) {
aWalkPath = path_ccw;
break;
}
m_iteration++;
}
if(m_iteration == m_iteration_limit)
{
int len_cw = path_cw.GetCLine().Length();
int len_ccw = path_ccw.GetCLine().Length();
if(m_forceLongerPath)
aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw);
else
aWalkPath = (len_cw < len_ccw ? path_cw : path_ccw);
}
if(m_cursorApproachMode)
{
//int len_cw = path_cw.GetCLine().Length();
//int len_ccw = path_ccw.GetCLine().Length();
bool found = false;
SHAPE_LINE_CHAIN l = aWalkPath.GetCLine();
for(int i = 0; i < l.SegmentCount(); i++)
{
const SEG s = l.Segment(i);
VECTOR2I nearest = s.NearestPoint(m_cursorPos);
VECTOR2I::extended_type dist_a = (s.a - m_cursorPos).SquaredEuclideanNorm();
VECTOR2I::extended_type dist_b = (s.b - m_cursorPos).SquaredEuclideanNorm();
VECTOR2I::extended_type dist_n = (nearest - m_cursorPos).SquaredEuclideanNorm();
if(dist_n <= dist_a && dist_n < dist_b)
{
//PNSDisplayDebugLine(l, 3);
l.Remove(i + 1, -1);
l.Append( nearest );
l.Simplify();
found = true;
break;
}
}
if(found)
{
aWalkPath = aInitialPath;
aWalkPath.SetShape(l);
}
}
aWalkPath.SetWorld(m_world);
aWalkPath.GetLine().Simplify();
WalkaroundStatus st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK;
if(aOptimize && st == DONE)
PNS_OPTIMIZER::Optimize(&aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world);
return st;
}
/*
* 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/>.
*/
#ifndef __PNS_WALKAROUND_H
#define __PNS_WALKAROUND_H
#include "pns_line.h"
#include "pns_node.h"
class PNS_WALKAROUND {
static const int DefaultIterationLimit = 50;
public:
PNS_WALKAROUND( PNS_NODE *aWorld ):
m_world(aWorld), m_iteration_limit(DefaultIterationLimit) {
m_forceSingleDirection = false;
m_forceLongerPath = false;
m_cursorApproachMode = false;
};
~PNS_WALKAROUND() {};
enum WalkaroundStatus {
IN_PROGRESS = 0,
DONE,
STUCK
};
void SetWorld ( PNS_NODE *aNode )
{
m_world = aNode;
}
void SetIterationLimit( const int aIterLimit )
{
m_iteration_limit = aIterLimit;
}
void SetSolidsOnly ( bool aSolidsOnly )
{
m_solids_only = aSolidsOnly;
}
void SetSingleDirection (bool aForceSingleDirection )
{
m_forceSingleDirection = aForceSingleDirection;
m_forceLongerPath = true;
}
void SetApproachCursor ( bool aEnabled, const VECTOR2I& aPos )
{
m_cursorPos = aPos;
m_cursorApproachMode = aEnabled;
}
WalkaroundStatus Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath, bool aOptimize = true);
private:
void start( const PNS_LINE& aInitialPath );
WalkaroundStatus singleStep(PNS_LINE& aPath, bool aWindingDirection);
PNS_NODE::OptObstacle nearestObstacle(const PNS_LINE& aPath);
PNS_NODE *m_world;
int m_recursiveBlockageCount;
int m_iteration;
int m_iteration_limit;
bool m_solids_only;
bool m_forceSingleDirection, m_forceLongerPath;
bool m_cursorApproachMode;
VECTOR2I m_cursorPos;
PNS_NODE::OptObstacle m_currentObstacle[2];
bool m_recursiveCollision[2];
};
#endif // __PNS_WALKAROUND_H
/*
* 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 <gal/color4d.h>
#include "class_track.h"
#include <pcb_painter.h>
#include "router_preview_item.h"
#include "pns_line.h"
#include "pns_segment.h"
#include "pns_via.h"
using namespace KiGfx;
ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS_ITEM *aItem, VIEW_GROUP *aParent )
: EDA_ITEM( NOT_USED )
{
m_Flags = 0;
m_parent = aParent;
if(aItem)
Update(aItem);
}
ROUTER_PREVIEW_ITEM::~ROUTER_PREVIEW_ITEM()
{
}
void ROUTER_PREVIEW_ITEM::Update(const PNS_ITEM *aItem)
{
m_layer = aItem->GetLayers().Start();
m_color = getLayerColor( m_layer );
m_color.a = 0.8;
switch(aItem->GetKind())
{
case PNS_ITEM::LINE:
m_type = PR_LINE;
m_width = static_cast<const PNS_LINE *>(aItem)->GetWidth();
m_line = * static_cast<const SHAPE_LINE_CHAIN *>(aItem->GetShape());
break;
case PNS_ITEM::SEGMENT:
m_type = PR_LINE;
m_width = static_cast<const PNS_SEGMENT *>(aItem)->GetWidth();
m_line = * static_cast<const SHAPE_LINE_CHAIN *>(aItem->GetShape());
break;
case PNS_ITEM::VIA:
m_type = PR_VIA;
m_color = COLOR4D(0.7, 0.7, 0.7, 0.8);
m_width = static_cast<const PNS_VIA *>(aItem)->GetDiameter();
m_viaCenter = static_cast<const PNS_VIA *>(aItem)->GetPos();
break;
default:
break;
}
ViewSetVisible(true);
ViewUpdate(GEOMETRY | APPEARANCE);
}
void ROUTER_PREVIEW_ITEM::MarkAsHead( )
{
if(m_type != PR_VIA)
m_color.Saturate(1.0);
}
const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const
{
BOX2I bbox;
switch(m_type)
{
case PR_LINE:
bbox = m_line.BBox();
bbox.Inflate( m_width / 2);
return bbox;
case PR_VIA:
bbox = BOX2I( m_viaCenter, VECTOR2I(0, 0));
bbox.Inflate( m_width / 2);
return bbox;
default:
break;
}
return bbox;
}
void ROUTER_PREVIEW_ITEM::ViewDraw( int aLayer, KiGfx::GAL* aGal ) const
{
switch( m_type )
{
case PR_LINE:
aGal->SetLayerDepth(-100.0);
aGal->SetLineWidth(m_width);
aGal->SetStrokeColor(m_color);
aGal->SetIsStroke(true);
aGal->SetIsFill(false);
for(int s= 0 ; s < m_line.SegmentCount(); s++)
aGal->DrawLine(m_line.CSegment(s).a, m_line.CSegment(s).b);
if(m_line.IsClosed())
aGal->DrawLine(m_line.CSegment(-1).b, m_line.CSegment(0).a);
break;
case PR_VIA:
aGal->SetLayerDepth(-101.0);
aGal->SetIsStroke(false);
aGal->SetIsFill(true);
aGal->SetFillColor(m_color);
aGal->DrawCircle(m_viaCenter, m_width / 2);
break;
default:
break;
}
}
void ROUTER_PREVIEW_ITEM::DebugLine ( const SHAPE_LINE_CHAIN& aLine, int aWidth , int aStyle )
{
#if 0
m_line = aLine;
m_width = aWidth;
m_color = assignColor(aStyle);
m_type = PR_LINE;
ViewUpdate(GEOMETRY | APPEARANCE);
#endif
}
void ROUTER_PREVIEW_ITEM::DebugBox ( const BOX2I& aBox, int aStyle )
{
#if 0
assert(false);
m_line.Clear();
m_line.Append( aBox.GetX(), aBox.GetY() );
m_line.Append( aBox.GetX() + aBox.GetWidth(), aBox.GetY() + aBox.GetHeight());
m_line.Append( aBox.GetX() + aBox.GetWidth(), aBox.GetY() + aBox.GetHeight());
m_line.Append( aBox.GetX(), aBox.GetY() + aBox.GetHeight());
m_line.SetClosed(true);
m_width = 20000;
m_color = assignColor(aStyle);
m_type = PR_LINE;
ViewUpdate(GEOMETRY | APPEARANCE);
#endif
}
const COLOR4D ROUTER_PREVIEW_ITEM::getLayerColor (int layer ) const
{
//assert (m_view != NULL);
PCB_RENDER_SETTINGS *settings = static_cast <PCB_RENDER_SETTINGS*> (m_parent -> GetView() -> GetPainter() -> GetSettings());
return settings->GetLayerColor(layer);
}
const COLOR4D ROUTER_PREVIEW_ITEM::assignColor ( int style ) const
{
COLOR4D color;
switch(style)
{
case 0: color =COLOR4D(0, 1, 0, 1);break;
case 1: color =COLOR4D(1, 0, 0, 0.3);break;
case 2: color =COLOR4D(1, 0.5, 0.5, 1);break;
case 3: color =COLOR4D(0, 0, 1, 1);break;
case 4: color =COLOR4D(1, 1, 1, 1); break;
case 5: color =COLOR4D(1, 1, 0, 1); break;
case 6: color =COLOR4D(0, 1, 1, 1); break;
case 32: color =COLOR4D(0, 0, 1, 0.5); break;
default: break;
}
return color;
}
/*
* 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/>.
*/
#ifndef __ROUTER_PREVIEW_ITEM_H
#define __ROUTER_PREVIEW_ITEM_H
#include <cstdio>
#include <view/view.h>
#include <view/view_item.h>
#include <view/view_group.h>
#include <math/vector2d.h>
#include <math/box2.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h>
#include <gal/color4d.h>
#include <gal/graphics_abstraction_layer.h>
#include <layers_id_colors_and_visibility.h>
class PNS_ITEM;
class PNS_ROUTER;
class ROUTER_PREVIEW_ITEM : public EDA_ITEM
{
public:
enum ItemType {
PR_VIA,
PR_LINE,
PR_STUCK_MARKER
};
enum ItemFlags {
PR_SUGGESTION = 1
};
ROUTER_PREVIEW_ITEM( const PNS_ITEM *aItem = NULL, KiGfx::VIEW_GROUP *aParent = NULL );
~ROUTER_PREVIEW_ITEM();
void Update ( const PNS_ITEM *aItem);
void StuckMarker( VECTOR2I& aPosition );
void DebugLine ( const SHAPE_LINE_CHAIN& aLine, int aWidth = 0, int aStyle = 0 );
void DebugBox ( const BOX2I& aBox, int aStyle = 0);
void Show(int a, std::ostream& b) const {};
const BOX2I ViewBBox() const;
virtual void ViewDraw( int aLayer, KiGfx::GAL* aGal ) const;
virtual void ViewGetLayers( int aLayers[], int& aCount ) const
{
aLayers[0] = GP_OVERLAY;
aCount = 1;
}
void MarkAsHead( );
private:
const KiGfx::COLOR4D assignColor ( int style ) const;
const KiGfx::COLOR4D getLayerColor (int layer ) const;
KiGfx::VIEW_GROUP *m_parent;
PNS_ROUTER *m_router;
SHAPE_LINE_CHAIN m_line;
ItemType m_type;
int m_style;
int m_width;
int m_layer;
KiGfx::COLOR4D m_color;
VECTOR2I m_stuckPosition;
VECTOR2I m_viaCenter;
};
#endif
/*
* 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 <boost/foreach.hpp>
#include <boost/optional.hpp>
#include "class_drawpanel_gal.h"
#include "class_board_item.h"
#include "class_board.h"
#include <wxPcbStruct.h>
#include <view/view_controls.h>
#include <pcbcommon.h>
#include <pcb_painter.h>
#include <tool/context_menu.h>
#include <tool/tool_action.h>
#include "router_tool.h"
#include "pns_segment.h"
#include "pns_router.h"
#include "trace.h"
using namespace KiGfx;
using namespace std;
using boost::optional;
static TOOL_ACTION ACT_AutoEndRoute ( "AutoEndRoute", SCOPE_CONTEXT, 'F' );
static TOOL_ACTION ACT_PlaceVia ( "PlaceVia", SCOPE_CONTEXT, 'V' );
static TOOL_ACTION ACT_OpenRouteOptions ( "OpenRouterOptions", SCOPE_CONTEXT, 'E' );
static TOOL_ACTION ACT_SwitchPosture ( "SwitchPosture", SCOPE_CONTEXT, '/' );
static TOOL_ACTION ACT_EndTrack ( "SwitchPosture", SCOPE_CONTEXT, WXK_END );
ROUTER_TOOL::ROUTER_TOOL() :
TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" )
{
m_router = NULL;
m_menu = new CONTEXT_MENU ;
m_menu->SetTitle( wxT( "Interactive router") ); // fixme: not implemented yet. Sorry.
m_menu->Add( wxT ("Cancel"), 0);
m_menu->Add( wxT ("New track"), 1);
m_menu->Add( wxT ("End track"), 2);
m_menu->Add( wxT ("Auto-end track"), 2);
m_menu->Add( wxT ("Place via"), 3);
m_menu->Add( wxT ("Switch posture"), 4);
m_menu->Add( wxT ("Routing options..."), 5);
}
ROUTER_TOOL::~ROUTER_TOOL()
{
delete m_router;
}
void ROUTER_TOOL::Reset()
{
if(m_router)
delete m_router;
m_router = new PNS_ROUTER;
TRACEn(0,"Reset");
m_router->ClearWorld();
m_router->SetBoard( getModel <BOARD> (PCB_T) );
m_router->SyncWorld();
if(getView())
m_router->SetView( getView() );
Go( &ROUTER_TOOL::Main, TOOL_EVENT( TC_Command, TA_ActivateTool, GetName() ) );
}
int ROUTER_TOOL::getDefaultWidth( int aNetCode )
{
int w, d1, d2;
getNetclassDimensions( aNetCode, w, d1, d2);
return w;
}
void ROUTER_TOOL::getNetclassDimensions ( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill)
{
BOARD *board = getModel <BOARD> (PCB_T);
NETCLASS* netClass = NULL;
NETINFO_ITEM *ni = board->FindNet(aNetCode);
if(ni)
{
wxString netClassName = ni->GetClassName();
netClass = board->m_NetClasses.Find( netClassName );
}
if( !netClass )
netClass = board->m_NetClasses.GetDefault();
aWidth = netClass->GetTrackWidth();
aViaDiameter = netClass->GetViaDiameter();
aViaDrill = netClass->GetViaDrill();
}
PNS_ITEM *ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer )
{
int tl = getView()->GetTopLayer();
if(aLayer > 0)
tl = aLayer;
PNS_ITEM *picked_seg = NULL, *picked_via = NULL;
PNS_ITEMSET candidates = m_router->QueryHoverItems(aWhere);
BOOST_FOREACH( PNS_ITEM *item, candidates.Items() )
{
if( !IsCopperLayer(item->GetLayers().Start()) )
continue;
if( item->GetParent() && !item->GetParent()->ViewIsVisible() )
continue;
if( aNet < 0 || item->GetNet() == aNet )
{
if( item->OfKind (PNS_ITEM::VIA | PNS_ITEM::SOLID) )
{
if(item->GetLayers().Overlaps(tl) || !picked_via)
picked_via = item;
} else {
if(item->GetLayers().Overlaps(tl) || !picked_seg)
picked_seg = item;
}
}
}
if( DisplayOpt.ContrastModeDisplay )
{
if( picked_seg && !picked_seg->GetLayers().Overlaps(tl))
picked_seg = NULL;
}
PNS_ITEM *rv = picked_via ? picked_via : picked_seg;
if( rv && aLayer >= 0 && !rv-> GetLayers().Overlaps(aLayer) )
rv = NULL;
if(rv)
TRACE(0, "%s, layer : %d, tl: %d", rv->GetKindStr().c_str() % rv->GetLayers().Start() % tl);
return rv;
}
void ROUTER_TOOL::setMsgPanel ( bool enabled, int entry, const wxString& aUpperMessage, const wxString& aLowerMessage )
{
PCB_EDIT_FRAME *frame = getEditFrame<PCB_EDIT_FRAME> ();
if(m_panelItems.size() <= (unsigned int) entry)
m_panelItems.resize(entry + 1);
m_panelItems[entry] = MSG_PANEL_ITEM( aUpperMessage, aLowerMessage, BLACK );
frame->SetMsgPanel(m_panelItems);
}
void ROUTER_TOOL::clearMsgPanel()
{
PCB_EDIT_FRAME *frame = getEditFrame<PCB_EDIT_FRAME> ();
frame->ClearMsgPanel();
}
void ROUTER_TOOL::highlightNet(bool enabled, int netcode)
{
RENDER_SETTINGS *rs = getView()->GetPainter()->GetSettings();
if(netcode >= 0 && enabled)
rs->SetHighlight(true, netcode);
else
rs->SetHighlight(false);
getView()->UpdateAllLayersColor();
}
void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent )
{
VIEW_CONTROLS *ctls = getViewControls();
int tl = getView()->GetTopLayer();
PNS_ITEM *startItem = NULL;
if( aEvent.IsMotion() || aEvent.IsClick() )
{
VECTOR2I p = aEvent.Position();
startItem = pickSingleItem(p);
if(startItem && startItem->GetNet() >= 0)
{
bool dummy;
VECTOR2I cursorPos = m_router->SnapToItem (startItem, p, dummy);
ctls->ForceCursorPosition(true, cursorPos);
m_startSnapPoint = cursorPos;
if(startItem->GetLayers().IsMultilayer())
m_startLayer = tl;
else
m_startLayer = startItem->GetLayers().Start();
m_startItem = startItem;
} else {
m_startItem = NULL;
m_startSnapPoint = p;
m_startLayer = tl;
ctls->ForceCursorPosition(false);
}
}
}
void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent )
{
VIEW_CONTROLS *ctls = getViewControls();
VECTOR2I p = aEvent.Position();
int layer;
if(m_router->GetCurrentNet() < 0 || !m_startItem)
{
m_endItem = NULL;
m_endSnapPoint = p;
return;
}
bool dummy;
if(m_router->IsPlacingVia())
layer = -1;
else
layer = m_router->GetCurrentLayer();
PNS_ITEM *endItem = pickSingleItem(p, m_startItem->GetNet(), layer );
if(endItem)
{
VECTOR2I cursorPos = m_router->SnapToItem (endItem, p, dummy);
ctls->ForceCursorPosition(true, cursorPos);
m_endItem = endItem;
m_endSnapPoint = cursorPos;
} else {
m_endItem = NULL;
m_endSnapPoint = p;
ctls->ForceCursorPosition(false);
}
if(m_endItem)
TRACE(0, "%s, layer : %d", m_endItem->GetKindStr().c_str() % m_endItem->GetLayers().Start() );
}
void ROUTER_TOOL::startRouting ( )
{
VIEW_CONTROLS *ctls = getViewControls();
int width = getDefaultWidth( m_startItem ? m_startItem->GetNet() : -1);
if(m_startItem && m_startItem->OfKind(PNS_ITEM::SEGMENT))
width = static_cast<PNS_SEGMENT *>(m_startItem)->GetWidth();
m_router->SetCurrentWidth(width);
m_router->SwitchLayer(m_startLayer);
getEditFrame<PCB_EDIT_FRAME>() -> SetTopLayer (m_startLayer);
if(m_startItem && m_startItem->GetNet() >= 0)
highlightNet(true, m_startItem->GetNet() );
ctls->ForceCursorPosition(false);
ctls->SetAutoPan(true);
m_router->StartRouting( m_startSnapPoint, m_startItem );
m_endItem = NULL;
m_endSnapPoint = m_startSnapPoint;
while( OPT_TOOL_EVENT evt = Wait() )
{
if( evt->IsCancel() )
break;
else if (evt->IsMotion())
{
updateEndItem( *evt );
m_router->Move ( m_endSnapPoint, m_endItem );
}
else if (evt->IsClick (MB_Left ))
{
updateEndItem( *evt );
if(m_router->FixRoute(m_endSnapPoint, m_endItem))
break;
m_router->Move ( m_endSnapPoint, m_endItem );
} else if (evt->IsKeyUp())
{
switch( evt->KeyCode() )
{
case 'V':
{
int w, diameter, drill;
getNetclassDimensions( m_router->GetCurrentNet(), w, diameter, drill );
m_router->SetCurrentViaDiameter(diameter);
m_router->SetCurrentViaDrill(drill);
m_router->ToggleViaPlacement();
getEditFrame<PCB_EDIT_FRAME>() -> SetTopLayer (m_router->GetCurrentLayer());
m_router->Move ( m_endSnapPoint, m_endItem );
break;
}
case '/':
m_router->FlipPosture();
break;
case '+':
case '=':
m_router->SwitchLayer ( m_router->NextCopperLayer (true) );
updateEndItem( *evt );
getEditFrame<PCB_EDIT_FRAME>() -> SetTopLayer (m_router->GetCurrentLayer());
m_router->Move ( m_endSnapPoint, m_endItem );
break;
case '-':
m_router->SwitchLayer ( m_router->NextCopperLayer (false) );
getEditFrame<PCB_EDIT_FRAME>() -> SetTopLayer (m_router->GetCurrentLayer());
m_router->Move ( m_endSnapPoint, m_endItem );
break;
}
}
}
if(m_router->RoutingInProgress())
m_router->StopRouting();
ctls->SetAutoPan(false);
ctls->ForceCursorPosition(false);
highlightNet(false);
}
int ROUTER_TOOL::Main( TOOL_EVENT& aEvent )
{
VIEW_CONTROLS *ctls = getViewControls();
//SetContextMenu ( m_menu );
//setMsgPanel(true, 0, wxT("KiRouter"), wxT("Pick an item to start routing"));
ctls->SetSnapping ( true );
ctls->ShowCursor( true );
// Main loop: keep receiving events
while( OPT_TOOL_EVENT evt = Wait() )
{
if( evt->IsCancel() )
break; // Finish
else if( evt->IsMotion( ) )
updateStartItem( *evt );
else if( evt->IsClick ( MB_Left ) )
{
updateStartItem( *evt );
startRouting( );
}
}
//clearMsgPanel();
// Restore the default settings
ctls->SetAutoPan( false );
ctls->ShowCursor( false );
return 0;
}
/*
* 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/>.
*/
#ifndef __ROUTER_TOOL_H
#define __ROUTER_TOOL_H
#include <set>
#include <boost/shared_ptr.hpp>
#include <math/vector2d.h>
#include <tool/tool_interactive.h>
#include <wxstruct.h>
#include <msgpanel.h>
#include "pns_layerset.h"
class PNS_ROUTER;
class PNS_ITEM;
class ROUTER_TOOL : public TOOL_INTERACTIVE
{
public:
ROUTER_TOOL();
~ROUTER_TOOL();
void Reset();
int Main( TOOL_EVENT& aEvent );
private:
PNS_ITEM *pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 );
void setMsgPanel ( bool enabled, int entry, const wxString& aUpperMessage = wxT(""), const wxString& aLowerMessage = wxT("") );
void clearMsgPanel();
int getDefaultWidth( int aNetCode );
void startRouting ( );
void highlightNet(bool enabled, int netcode = -1);
void updateStartItem( TOOL_EVENT& aEvent );
void updateEndItem( TOOL_EVENT& aEvent );
void getNetclassDimensions ( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill);
MSG_PANEL_ITEMS m_panelItems;
PNS_ROUTER *m_router;
PNS_ITEM *m_startItem;
int m_startLayer;
VECTOR2I m_startSnapPoint;
PNS_ITEM *m_endItem;
VECTOR2I m_endSnapPoint;
/*boost::shared_ptr<CONTEXT_MENU> m_menu;*/
CONTEXT_MENU * m_menu;
};
#endif
/*
* 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/>.
*/
#ifndef __TRACE_H
#define __TRACE_H
#ifdef DEBUG
#include <string>
#include <iostream>
#include <boost/format.hpp>
static void _trace_print(const char *funcName, int level, const std::string& msg)
{
std::cerr << "trace[" << level << "]: " << funcName << ": " << msg << std::endl;
}
#define TRACE(level, fmt, ...) \
_trace_print(__FUNCTION__, level, (boost::format(fmt) % __VA_ARGS__).str() );
#define TRACEn(level, msg) \
_trace_print(__FUNCTION__, level, std::string(msg));
#else
#define TRACE(level, fmt, ...)
#define TRACEn(level, msg)
#endif
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment