36 changed files with 8425 additions and 0 deletions
-
50pcbnew/router/CMakeLists.txt
-
295pcbnew/router/direction.h
-
241pcbnew/router/pns_index.h
-
72pcbnew/router/pns_item.cpp
-
155pcbnew/router/pns_item.h
-
73pcbnew/router/pns_itemset.cpp
-
62pcbnew/router/pns_itemset.h
-
188pcbnew/router/pns_joint.h
-
118pcbnew/router/pns_layerset.h
-
704pcbnew/router/pns_line.cpp
-
251pcbnew/router/pns_line.h
-
715pcbnew/router/pns_line_placer.cpp
-
156pcbnew/router/pns_line_placer.h
-
901pcbnew/router/pns_node.cpp
-
260pcbnew/router/pns_node.h
-
704pcbnew/router/pns_optimizer.cpp
-
164pcbnew/router/pns_optimizer.h
-
774pcbnew/router/pns_router.cpp
-
197pcbnew/router/pns_router.h
-
53pcbnew/router/pns_routing_settings.h
-
119pcbnew/router/pns_segment.h
-
469pcbnew/router/pns_shove.cpp
-
82pcbnew/router/pns_shove.h
-
64pcbnew/router/pns_solid.cpp
-
70pcbnew/router/pns_solid.h
-
42pcbnew/router/pns_utils.cpp
-
33pcbnew/router/pns_utils.h
-
148pcbnew/router/pns_via.cpp
-
118pcbnew/router/pns_via.h
-
221pcbnew/router/pns_walkaround.cpp
-
99pcbnew/router/pns_walkaround.h
-
197pcbnew/router/router_preview_item.cpp
-
103pcbnew/router/router_preview_item.h
-
397pcbnew/router/router_tool.cpp
-
79pcbnew/router/router_tool.h
-
51pcbnew/router/trace.h
@ -0,0 +1,50 @@ |
|||
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}) |
|||
@ -0,0 +1,295 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,241 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,72 @@ |
|||
/*
|
|||
* 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() |
|||
{ |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,155 @@ |
|||
/* |
|||
* 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 |
|||
|
|||
@ -0,0 +1,73 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
|
|||
@ -0,0 +1,62 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,188 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,118 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,704 @@ |
|||
/*
|
|||
* 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]) ; |
|||
} |
|||
@ -0,0 +1,251 @@ |
|||
/* |
|||
* 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 |
|||
|
|||
@ -0,0 +1,715 @@ |
|||
/*
|
|||
* 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(); |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,901 @@ |
|||
/*
|
|||
* 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); |
|||
} |
|||
} |
|||
@ -0,0 +1,260 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,704 @@ |
|||
/*
|
|||
* 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); |
|||
} |
|||
@ -0,0 +1,164 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,774 @@ |
|||
/*
|
|||
* KiRouter - a push-and-(sometimes-)shove PCB router |
|||
* |
|||
* Copyright (C) 2013 CERN |
|||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.or/licenses/>.
|
|||
*/ |
|||
#include <cstdio>
|
|||
#include <vector>
|
|||
|
|||
#include <boost/foreach.hpp>
|
|||
|
|||
#include <view/view.h>
|
|||
#include <view/view_item.h>
|
|||
#include <view/view_group.h>
|
|||
#include <gal/graphics_abstraction_layer.h>
|
|||
|
|||
#include <pcb_painter.h>
|
|||
|
|||
#include <geometry/shape.h>
|
|||
#include <geometry/shape_line_chain.h>
|
|||
#include <geometry/shape_rect.h>
|
|||
#include <geometry/shape_circle.h>
|
|||
|
|||
#include "trace.h"
|
|||
#include "pns_node.h"
|
|||
#include "pns_line_placer.h"
|
|||
#include "pns_line.h"
|
|||
#include "pns_solid.h"
|
|||
#include "pns_utils.h"
|
|||
#include "pns_router.h"
|
|||
|
|||
#include <router/router_preview_item.h>
|
|||
|
|||
#include <class_board.h>
|
|||
#include <class_board_item.h>
|
|||
#include <class_pad.h>
|
|||
#include <class_track.h>
|
|||
#include <layers_id_colors_and_visibility.h>
|
|||
|
|||
using namespace std; |
|||
|
|||
// an ugly singleton for drawing debug items within the router context. To be fixed sometime in the future.
|
|||
static PNS_ROUTER *theRouter; |
|||
|
|||
class PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC |
|||
{ |
|||
public: |
|||
PCBNEW_CLEARANCE_FUNC( BOARD *aBoard ) |
|||
{ |
|||
m_clearanceCache.resize(aBoard->GetNetCount()); |
|||
|
|||
for(unsigned int i = 0; i < aBoard->GetNetCount(); i++) |
|||
{ |
|||
NETINFO_ITEM *ni = aBoard->FindNet(i); |
|||
wxString netClassName = ni->GetClassName(); |
|||
NETCLASS *nc = aBoard->m_NetClasses.Find(netClassName); |
|||
int clearance = nc->GetClearance(); |
|||
m_clearanceCache[i] = clearance; |
|||
TRACE(1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() % clearance); |
|||
} |
|||
|
|||
m_defaultClearance = 254000;//aBoard->m_NetClasses.Find ("Default clearance")->GetClearance();
|
|||
} |
|||
|
|||
int operator() (const PNS_ITEM *a , const PNS_ITEM *b) |
|||
{ |
|||
int net_a = a->GetNet(); |
|||
int cl_a = (net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance); |
|||
int net_b = b->GetNet(); |
|||
int cl_b = (net_b >= 0 ? m_clearanceCache[net_b] : m_defaultClearance); |
|||
return std::max(cl_a, cl_b); |
|||
} |
|||
|
|||
private: |
|||
|
|||
vector<int> m_clearanceCache; |
|||
int m_defaultClearance; |
|||
}; |
|||
|
|||
PNS_ITEM *PNS_ROUTER::syncPad( D_PAD *aPad ) |
|||
{ |
|||
PNS_LAYERSET layers; |
|||
|
|||
switch(aPad->GetAttribute()) |
|||
{ |
|||
case PAD_STANDARD: |
|||
layers = PNS_LAYERSET(0, 15); |
|||
break; |
|||
case PAD_SMD: |
|||
case PAD_CONN: |
|||
{ |
|||
LAYER_MSK lmsk = aPad->GetLayerMask(); |
|||
int i; |
|||
|
|||
for(i = FIRST_COPPER_LAYER; i <= LAST_COPPER_LAYER; i++) |
|||
if( lmsk & (1<<i)) |
|||
{ |
|||
layers = PNS_LAYERSET( i ); |
|||
break; |
|||
} |
|||
break; |
|||
} |
|||
default: |
|||
TRACE(0, "unsupported pad type 0x%x", aPad->GetAttribute()); |
|||
return NULL; |
|||
} |
|||
|
|||
PNS_SOLID *solid = new PNS_SOLID; |
|||
|
|||
solid->SetLayers(layers); |
|||
solid->SetNet( aPad->GetNet() ); |
|||
wxPoint wx_c = aPad->GetPosition(); |
|||
wxSize wx_sz = aPad->GetSize(); |
|||
|
|||
VECTOR2I c(wx_c.x, wx_c.y); |
|||
VECTOR2I sz(wx_sz.x, wx_sz.y); |
|||
|
|||
solid->SetCenter( c ); |
|||
|
|||
|
|||
double orient = aPad->GetOrientation() / 10.0; |
|||
|
|||
if(orient == 90.0 || orient == 270.0) |
|||
sz = VECTOR2I(sz.y, sz.x); |
|||
else if (orient != 0.0 && orient != 180.0) |
|||
{ |
|||
TRACEn(0, "non-orthogonal pad rotations not supported yet"); |
|||
delete solid; |
|||
return NULL; |
|||
} |
|||
|
|||
switch(aPad->GetShape()) |
|||
{ |
|||
case PAD_CIRCLE: |
|||
solid->SetShape ( new SHAPE_CIRCLE ( c, sz.x / 2) ); |
|||
break; |
|||
case PAD_OVAL: |
|||
if(sz.x == sz.y) |
|||
solid->SetShape ( new SHAPE_CIRCLE ( c, sz.x / 2) ); |
|||
else |
|||
solid->SetShape ( new SHAPE_RECT ( c - sz / 2, sz.x, sz.y) ); |
|||
break; |
|||
|
|||
case PAD_RECT: |
|||
solid->SetShape ( new SHAPE_RECT ( c - sz / 2, sz.x, sz.y) ); |
|||
break; |
|||
|
|||
default: |
|||
TRACEn(0, "unsupported pad shape"); |
|||
delete solid; |
|||
return NULL; |
|||
} |
|||
|
|||
|
|||
solid->SetParent(aPad); |
|||
return solid; |
|||
} |
|||
|
|||
PNS_ITEM *PNS_ROUTER::syncTrack( TRACK *aTrack ) |
|||
{ |
|||
|
|||
PNS_SEGMENT *s = new PNS_SEGMENT( SEG (aTrack->GetStart(), aTrack->GetEnd() ), aTrack->GetNet() ); |
|||
|
|||
s->SetWidth( aTrack->GetWidth() ); |
|||
s->SetLayers (PNS_LAYERSET(aTrack->GetLayer())); |
|||
s->SetParent(aTrack); |
|||
return s; |
|||
} |
|||
|
|||
|
|||
PNS_ITEM *PNS_ROUTER::syncVia( SEGVIA *aVia ) |
|||
{ |
|||
PNS_VIA *v = new PNS_VIA( |
|||
aVia->GetPosition(), |
|||
PNS_LAYERSET(0, 15), |
|||
aVia->GetWidth(), |
|||
aVia->GetNet()); |
|||
|
|||
v->SetParent(aVia); |
|||
return v; |
|||
} |
|||
|
|||
void PNS_ROUTER::SetBoard( BOARD *aBoard ) |
|||
{ |
|||
m_board = aBoard; |
|||
TRACE(1, "m_board = %p\n", m_board); |
|||
} |
|||
|
|||
int PNS_ROUTER::NextCopperLayer( bool aUp ) |
|||
{ |
|||
LAYER_MSK mask = m_board->GetEnabledLayers() & m_board->GetVisibleLayers(); |
|||
LAYER_NUM l = m_currentLayer; |
|||
|
|||
do { |
|||
l += (aUp ? 1 : -1); |
|||
if(l > LAST_COPPER_LAYER) |
|||
l = FIRST_COPPER_LAYER; |
|||
|
|||
if(l < FIRST_COPPER_LAYER) |
|||
l = LAST_COPPER_LAYER; |
|||
|
|||
if(mask & GetLayerMask(l)) |
|||
return l; |
|||
|
|||
} while (l != m_currentLayer); |
|||
|
|||
return l; |
|||
} |
|||
|
|||
void PNS_ROUTER::SyncWorld() |
|||
{ |
|||
vector<D_PAD *> pads; |
|||
|
|||
if(!m_board) |
|||
{ |
|||
TRACEn(0,"No board attached, aborting sync."); |
|||
return; |
|||
} |
|||
|
|||
ClearWorld(); |
|||
|
|||
|
|||
m_clearanceFunc = new PCBNEW_CLEARANCE_FUNC(m_board); |
|||
m_world = new PNS_NODE(); |
|||
m_world->SetClearanceFunctor ( m_clearanceFunc ); |
|||
m_world->SetMaxClearance ( 1000000 ); //m_board->GetBiggestClearanceValue());
|
|||
pads = m_board->GetPads(); |
|||
|
|||
BOOST_FOREACH( D_PAD *pad, pads ) |
|||
{ |
|||
PNS_ITEM *solid = syncPad(pad); |
|||
if(solid) |
|||
m_world->Add(solid); |
|||
} |
|||
|
|||
for(TRACK *t = m_board->m_Track; t; t = t->Next()) |
|||
{ |
|||
KICAD_T type = t->Type(); |
|||
PNS_ITEM *item = NULL; |
|||
if(type == PCB_TRACE_T) |
|||
item = syncTrack ( t ); |
|||
else if( type == PCB_VIA_T ) |
|||
item = syncVia (static_cast <SEGVIA *>(t)); |
|||
|
|||
if(item) |
|||
m_world->Add(item); |
|||
} |
|||
|
|||
m_placer = new PNS_LINE_PLACER ( m_world ); |
|||
} |
|||
|
|||
PNS_ROUTER::PNS_ROUTER() |
|||
{ |
|||
theRouter = this; |
|||
|
|||
m_clearanceFunc = NULL; |
|||
|
|||
m_currentLayer = 1; |
|||
m_placingVia = false; |
|||
m_currentNet = -1; |
|||
m_state = IDLE; |
|||
m_world = NULL; |
|||
m_placer = NULL; |
|||
m_previewItems = NULL; |
|||
m_start_diagonal = false; |
|||
m_board = NULL; |
|||
|
|||
TRACE(1, "m_board = %p\n", m_board); |
|||
} |
|||
|
|||
|
|||
void PNS_ROUTER::SetView(KiGfx::VIEW *aView) |
|||
{ |
|||
if(m_previewItems) |
|||
{ |
|||
m_previewItems->FreeItems(); |
|||
delete m_previewItems; |
|||
} |
|||
|
|||
m_view = aView; |
|||
m_previewItems = new KiGfx::VIEW_GROUP(m_view); |
|||
m_previewItems->SetLayer(ITEM_GAL_LAYER( GP_OVERLAY )); |
|||
m_view -> Add (m_previewItems); |
|||
m_previewItems->ViewSetVisible(true); |
|||
|
|||
} |
|||
|
|||
PNS_ROUTER *PNS_ROUTER::GetInstance() |
|||
{ |
|||
return theRouter; |
|||
} |
|||
|
|||
PNS_ROUTER::~PNS_ROUTER() |
|||
{ |
|||
ClearWorld(); |
|||
theRouter = NULL; |
|||
} |
|||
|
|||
void PNS_ROUTER::ClearWorld() |
|||
{ |
|||
if(m_world) |
|||
delete m_world; |
|||
if(m_clearanceFunc) |
|||
delete m_clearanceFunc; |
|||
if(m_placer) |
|||
delete m_placer; |
|||
|
|||
m_clearanceFunc = NULL; |
|||
m_world = NULL; |
|||
m_placer = NULL; |
|||
} |
|||
|
|||
void PNS_ROUTER::SetCurrentWidth (int w ) |
|||
{ |
|||
// fixme: change width while routing
|
|||
m_currentWidth = w; |
|||
} |
|||
|
|||
bool PNS_ROUTER::RoutingInProgress() const |
|||
{ |
|||
return m_state != IDLE; |
|||
} |
|||
|
|||
|
|||
const PNS_ITEMSET PNS_ROUTER::QueryHoverItems(const VECTOR2I&aP) |
|||
{ |
|||
if(m_state == IDLE) |
|||
return m_world->HitTest( aP ); |
|||
else |
|||
return m_placer->GetCurrentNode() -> HitTest(aP); |
|||
} |
|||
|
|||
|
|||
const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM *item, VECTOR2I aP, bool& aSplitsSegment ) |
|||
{ |
|||
VECTOR2I anchor; |
|||
|
|||
if(!item) |
|||
{ |
|||
aSplitsSegment = false; |
|||
return aP; |
|||
} |
|||
|
|||
switch(item->GetKind()) |
|||
{ |
|||
case PNS_ITEM::SOLID: |
|||
anchor = static_cast<PNS_SOLID *>(item)->GetCenter(); |
|||
aSplitsSegment = false; |
|||
break; |
|||
case PNS_ITEM::VIA: |
|||
anchor = static_cast<PNS_VIA *>(item)->GetPos(); |
|||
aSplitsSegment = false; |
|||
break; |
|||
case PNS_ITEM::SEGMENT: |
|||
{ |
|||
PNS_SEGMENT *seg = static_cast<PNS_SEGMENT *>(item); |
|||
const SEG& s = seg->GetSeg(); |
|||
int w = seg->GetWidth(); |
|||
|
|||
aSplitsSegment = false; |
|||
|
|||
if ((aP - s.a).EuclideanNorm() < w / 2) |
|||
anchor = s.a; |
|||
else if ((aP - s.b).EuclideanNorm() < w / 2) |
|||
anchor = s.b; |
|||
else { |
|||
anchor = s.NearestPoint(aP); |
|||
aSplitsSegment = true; |
|||
} |
|||
break; |
|||
} |
|||
default: |
|||
break; |
|||
} |
|||
return anchor; |
|||
} |
|||
|
|||
|
|||
void PNS_ROUTER::StartRouting(const VECTOR2I& aP, PNS_ITEM *aStartItem) |
|||
{ |
|||
VECTOR2I p; |
|||
|
|||
static int unknowNetIdx = 0;//-10000;
|
|||
|
|||
m_placingVia = false; |
|||
m_startsOnVia = false; |
|||
m_currentNet = -1; |
|||
|
|||
bool splitSeg = false; |
|||
|
|||
p = SnapToItem( aStartItem, aP, splitSeg ); |
|||
|
|||
if(!aStartItem || aStartItem->GetNet() < 0) |
|||
m_currentNet = unknowNetIdx--; |
|||
else |
|||
m_currentNet = aStartItem->GetNet(); |
|||
|
|||
m_currentStart = p; |
|||
m_originalStart = p; |
|||
m_currentEnd = p; |
|||
|
|||
m_placer->SetInitialDirection(m_start_diagonal ? DIRECTION_45(DIRECTION_45::NE) : DIRECTION_45(DIRECTION_45::N)); |
|||
m_placer->StartPlacement(m_originalStart, m_currentNet, m_currentWidth, m_currentLayer); |
|||
m_state = ROUTE_TRACK; |
|||
|
|||
if(splitSeg) |
|||
splitAdjacentSegments(m_placer->GetCurrentNode(), aStartItem, p); |
|||
|
|||
|
|||
} |
|||
|
|||
const VECTOR2I PNS_ROUTER::GetCurrentEnd( ) const |
|||
{ |
|||
return m_currentEnd; |
|||
} |
|||
|
|||
void PNS_ROUTER::EraseView() |
|||
{ |
|||
BOOST_FOREACH(BOARD_ITEM *item, m_hiddenItems) |
|||
{ |
|||
item->ViewSetVisible(true); |
|||
} |
|||
|
|||
if(m_previewItems) |
|||
m_previewItems->FreeItems(); |
|||
m_previewItems->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY ); |
|||
} |
|||
|
|||
void PNS_ROUTER::DisplayItem(const PNS_ITEM* aItem, bool aIsHead) |
|||
{ |
|||
|
|||
ROUTER_PREVIEW_ITEM * pitem = new ROUTER_PREVIEW_ITEM (aItem, m_previewItems); |
|||
|
|||
m_previewItems->Add (pitem); |
|||
if(aIsHead) |
|||
pitem->MarkAsHead(); |
|||
|
|||
pitem->ViewSetVisible(true); |
|||
m_previewItems->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY | KiGfx::VIEW_ITEM::APPEARANCE ); |
|||
} |
|||
|
|||
void PNS_ROUTER::DisplayDebugLine ( const SHAPE_LINE_CHAIN &aLine, int aType, int aWidth) |
|||
{ |
|||
ROUTER_PREVIEW_ITEM * pitem = new ROUTER_PREVIEW_ITEM (NULL, m_previewItems); |
|||
|
|||
pitem->DebugLine (aLine, aWidth, aType ); |
|||
m_previewItems->Add (pitem); |
|||
pitem->ViewSetVisible(true); |
|||
m_previewItems->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY | KiGfx::VIEW_ITEM::APPEARANCE ); |
|||
} |
|||
|
|||
void PNS_ROUTER::DisplayDebugBox ( const BOX2I& aBox, int aType , int aWidth ) |
|||
{ |
|||
|
|||
} |
|||
|
|||
|
|||
void PNS_ROUTER::Move(const VECTOR2I& aP, PNS_ITEM *endItem) |
|||
{ |
|||
PNS_NODE::ItemVector removed, added; |
|||
VECTOR2I p = aP; |
|||
|
|||
if(m_state == IDLE) |
|||
return; |
|||
|
|||
if(m_state == START_ROUTING) |
|||
{ |
|||
|
|||
} |
|||
|
|||
EraseView(); |
|||
|
|||
m_currentEnd = p; |
|||
m_placer->Route(p); |
|||
|
|||
PNS_LINE current = m_placer->GetTrace(); |
|||
|
|||
DisplayItem (¤t, true); |
|||
|
|||
if(current.EndsWithVia()) |
|||
DisplayItem( ¤t.GetVia(), true ); |
|||
|
|||
m_placer->GetCurrentNode()->GetUpdatedItems(removed, added); |
|||
|
|||
BOOST_FOREACH(PNS_ITEM *item, added) |
|||
{ |
|||
DisplayItem(item); |
|||
} |
|||
|
|||
BOOST_FOREACH(PNS_ITEM *item, removed) |
|||
{ |
|||
BOARD_ITEM *parent = item->GetParent(); |
|||
|
|||
if(parent) |
|||
{ |
|||
if(parent->ViewIsVisible()) |
|||
m_hiddenItems.insert(parent); |
|||
|
|||
parent->ViewSetVisible(false); |
|||
parent->ViewUpdate (KiGfx::VIEW_ITEM::APPEARANCE); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void PNS_ROUTER::splitAdjacentSegments(PNS_NODE *aNode, PNS_ITEM *aSeg, const VECTOR2I& aP ) |
|||
{ |
|||
if(aSeg && aSeg->OfKind( PNS_ITEM::SEGMENT )) |
|||
{ |
|||
PNS_NODE::OptJoint jt = aNode->FindJoint ( aP, aSeg->GetLayers().Start(), aSeg->GetNet()); |
|||
|
|||
if(jt && jt->LinkCount() >= 1) |
|||
return; |
|||
|
|||
PNS_SEGMENT *s_old = static_cast<PNS_SEGMENT*>(aSeg); |
|||
PNS_SEGMENT *s_new [2]; |
|||
|
|||
s_new[0] = s_old->Clone(); |
|||
s_new[1] = s_old->Clone(); |
|||
|
|||
s_new[0]->SetEnds (s_old->GetSeg().a, aP); |
|||
s_new[1]->SetEnds (aP, s_old->GetSeg().b); |
|||
|
|||
|
|||
aNode->Remove( s_old ); |
|||
aNode->Add( s_new [0] ); |
|||
aNode->Add( s_new [1] ); |
|||
} |
|||
} |
|||
|
|||
void PNS_ROUTER::commitRouting( PNS_NODE *aNode ) |
|||
{ |
|||
PNS_NODE::ItemVector removed, added; |
|||
|
|||
aNode->GetUpdatedItems(removed, added); |
|||
|
|||
for(unsigned int i = 0; i < removed.size(); i++) |
|||
{ |
|||
BOARD_ITEM *parent = removed[i]->GetParent(); |
|||
|
|||
if(parent) |
|||
{ |
|||
m_view->Remove(parent); |
|||
m_board->Remove(parent); |
|||
} |
|||
} |
|||
|
|||
BOOST_FOREACH(PNS_ITEM *item, added) |
|||
{ |
|||
BOARD_ITEM *newBI = NULL; |
|||
switch(item->GetKind()) |
|||
{ |
|||
case PNS_ITEM::SEGMENT: |
|||
{ |
|||
PNS_SEGMENT *seg = static_cast<PNS_SEGMENT*>(item); |
|||
TRACK *track = new TRACK(m_board); |
|||
const SEG& s = seg->GetSeg(); |
|||
|
|||
track->SetStart( wxPoint(s.a.x, s.a.y)); |
|||
track->SetEnd( wxPoint(s.b.x, s.b.y )); |
|||
track->SetWidth(seg->GetWidth()); |
|||
track->SetLayer(seg->GetLayers().Start()); |
|||
track->SetNet(seg->GetNet()); |
|||
newBI = track; |
|||
break; |
|||
} |
|||
|
|||
case PNS_ITEM::VIA: |
|||
{ |
|||
SEGVIA *via_board = new SEGVIA(m_board); |
|||
PNS_VIA *via = static_cast<PNS_VIA *>(item); |
|||
via_board->SetPosition ( wxPoint(via->GetPos().x, via->GetPos().y )); |
|||
via_board->SetWidth ( via->GetDiameter() ); |
|||
via_board->SetNet ( via->GetNet() ); |
|||
newBI = via_board; |
|||
break; |
|||
} |
|||
|
|||
default: |
|||
break; |
|||
} |
|||
|
|||
if(newBI) |
|||
{ |
|||
item->SetParent(newBI); |
|||
newBI->ClearFlags(); |
|||
m_view->Add(newBI); |
|||
m_board->Add(newBI); |
|||
newBI->ViewUpdate( KiGfx::VIEW_ITEM::GEOMETRY ); |
|||
} |
|||
} |
|||
|
|||
m_world->Commit( aNode ); |
|||
} |
|||
|
|||
PNS_VIA *PNS_ROUTER::checkLoneVia ( PNS_JOINT* aJoint ) const |
|||
{ |
|||
PNS_VIA *theVia = NULL; |
|||
PNS_LAYERSET l; |
|||
|
|||
BOOST_FOREACH(PNS_ITEM *item, aJoint->GetLinkList()) |
|||
{ |
|||
if(item->GetKind() == PNS_ITEM::VIA) |
|||
theVia = static_cast<PNS_VIA *>(item); |
|||
|
|||
|
|||
l.Merge (item->GetLayers()); |
|||
} |
|||
|
|||
if(l.Start() == l.End()) |
|||
return theVia; |
|||
return NULL; |
|||
} |
|||
|
|||
PNS_NODE *PNS_ROUTER::removeLoops ( PNS_NODE *aNode, PNS_SEGMENT *aLatestSeg ) |
|||
{ |
|||
PNS_LINE *ourLine = aNode->AssembleLine(aLatestSeg); |
|||
PNS_NODE *cleaned = aNode->Branch(); |
|||
PNS_JOINT a, b; |
|||
vector<PNS_LINE *> lines; |
|||
|
|||
|
|||
cleaned->FindLineEnds (ourLine, a, b); |
|||
cleaned->FindLinesBetweenJoints( a, b, lines); |
|||
|
|||
BOOST_FOREACH(PNS_LINE *line, lines) |
|||
{ |
|||
if(! (line->ContainsSegment (aLatestSeg) ) ) |
|||
{ |
|||
cleaned->Remove(line); |
|||
} |
|||
} |
|||
|
|||
return cleaned; |
|||
} |
|||
|
|||
|
|||
|
|||
bool PNS_ROUTER::FixRoute(const VECTOR2I& aP, PNS_ITEM *aEndItem) |
|||
{ |
|||
bool real_end = false; |
|||
|
|||
PNS_LINE pl = m_placer->GetTrace(); |
|||
const SHAPE_LINE_CHAIN& l = pl.GetCLine(); |
|||
|
|||
if(!l.SegmentCount()) |
|||
return true; |
|||
|
|||
VECTOR2I p_pre_last = l.CPoint(-1); |
|||
const VECTOR2I p_last = l.CPoint(-1); |
|||
DIRECTION_45 d_last (l.CSegment(-1)); |
|||
|
|||
if(l.PointCount() > 2) |
|||
p_pre_last = l.CPoint(-2); |
|||
|
|||
if(aEndItem && m_currentNet >= 0 && m_currentNet == aEndItem->GetNet()) |
|||
real_end = true; |
|||
|
|||
int last = (real_end || m_placingVia) ? l.SegmentCount() : max(1, l.SegmentCount() - 1); |
|||
|
|||
PNS_NODE *latest = m_placer->GetCurrentNode(); |
|||
|
|||
if(real_end) |
|||
splitAdjacentSegments(latest, aEndItem, aP); |
|||
|
|||
PNS_SEGMENT *lastSeg = NULL; |
|||
for (int i = 0; i < last; i++) |
|||
{ |
|||
const SEG& s = pl.GetCLine().CSegment(i); |
|||
PNS_SEGMENT *seg = new PNS_SEGMENT( s, m_currentNet ); |
|||
seg->SetWidth(pl.GetWidth()); |
|||
seg->SetLayer(m_currentLayer); |
|||
latest->Add(seg); |
|||
lastSeg = seg; |
|||
} |
|||
|
|||
if( pl.EndsWithVia() ) |
|||
latest->Add(pl.GetVia().Clone()); |
|||
|
|||
if(real_end) |
|||
latest = removeLoops( latest, lastSeg ); |
|||
|
|||
commitRouting(latest); |
|||
|
|||
EraseView(); |
|||
|
|||
if(real_end) |
|||
{ |
|||
m_state = IDLE; |
|||
//m_world->KillChildren();
|
|||
} else { |
|||
|
|||
m_state = ROUTE_TRACK; |
|||
m_placer->SetInitialDirection(d_last); |
|||
m_currentStart = m_placingVia ? p_last : p_pre_last; |
|||
|
|||
if(m_placingVia) |
|||
m_currentLayer = NextCopperLayer(true); |
|||
|
|||
m_placer->StartPlacement(m_currentStart, m_currentNet, m_currentWidth, m_currentLayer); |
|||
|
|||
m_startsOnVia = m_placingVia; |
|||
m_placingVia = false; |
|||
|
|||
} |
|||
|
|||
|
|||
return real_end; |
|||
} |
|||
|
|||
void PNS_ROUTER::StopRouting() |
|||
{ |
|||
if(!RoutingInProgress()) |
|||
return; |
|||
|
|||
//highlightCurrent(false);
|
|||
|
|||
EraseView(); |
|||
|
|||
m_state = IDLE; |
|||
m_world->KillChildren(); |
|||
} |
|||
|
|||
void PNS_ROUTER::FlipPosture() |
|||
{ |
|||
if(m_placer->GetTail().GetCLine().SegmentCount() == 0) |
|||
{ |
|||
m_start_diagonal = !m_start_diagonal; |
|||
m_placer->SetInitialDirection(m_start_diagonal ? DIRECTION_45(DIRECTION_45::NE) : DIRECTION_45(DIRECTION_45::N)); |
|||
} else |
|||
m_placer->FlipPosture(); |
|||
|
|||
Move(m_currentEnd, NULL); |
|||
} |
|||
|
|||
void PNS_ROUTER::SwitchLayer(int layer) |
|||
{ |
|||
switch(m_state) |
|||
{ |
|||
case IDLE: |
|||
m_currentLayer = layer; |
|||
break; |
|||
case ROUTE_TRACK: |
|||
if(m_startsOnVia) |
|||
{ |
|||
m_currentLayer = layer; |
|||
m_placer->StartPlacement(m_currentStart, m_currentNet, m_currentWidth, m_currentLayer); |
|||
} |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void PNS_ROUTER::ToggleViaPlacement () |
|||
{ |
|||
if(m_state == ROUTE_TRACK) |
|||
{ |
|||
m_placingVia = !m_placingVia; |
|||
m_placer->AddVia(m_placingVia, m_currentViaDiameter, m_currentViaDrill); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,197 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,53 @@ |
|||
/* |
|||
* 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 |
|||
|
|||
@ -0,0 +1,119 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,469 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,82 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,64 @@ |
|||
/*
|
|||
* 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); |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
/* |
|||
* 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 |
|||
|
|||
@ -0,0 +1,42 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
|
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* 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 |
|||
|
|||
@ -0,0 +1,148 @@ |
|||
/*
|
|||
* 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); |
|||
} |
|||
|
|||
@ -0,0 +1,118 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,221 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
/* |
|||
* 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 |
|||
|
|||
@ -0,0 +1,197 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,397 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,79 @@ |
|||
/* |
|||
* 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 |
|||
@ -0,0 +1,51 @@ |
|||
/* |
|||
* 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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue