From 74f42a62414d293c00203423e21ced92a9752a3d Mon Sep 17 00:00:00 2001 From: "tomasz.wlostowski@cern.ch" Date: Wed, 18 Sep 2013 19:37:56 +0200 Subject: [PATCH 1/4] geometry/rtree.h: fix compiler warnings --- include/geometry/rtree.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/geometry/rtree.h b/include/geometry/rtree.h index d64d7a074a..abc7833286 100644 --- a/include/geometry/rtree.h +++ b/include/geometry/rtree.h @@ -1287,7 +1287,7 @@ int RTREE_QUAL::PickBranch( Rect* a_rect, Node* a_node ) ELEMTYPEREAL bestIncr = (ELEMTYPEREAL) -1; ELEMTYPEREAL area; ELEMTYPEREAL bestArea; - int best; + int best = 0; Rect tempRect; for( int index = 0; index < a_node->m_count; ++index ) @@ -1480,7 +1480,7 @@ void RTREE_QUAL::ChoosePartition( PartitionVars* a_parVars, int a_minFill ) ASSERT( a_parVars ); ELEMTYPEREAL biggestDiff; - int group, chosen, betterGroup; + int group, chosen = 0, betterGroup = 0; InitParVars( a_parVars, a_parVars->m_branchCount, a_minFill ); PickSeeds( a_parVars ); @@ -1603,7 +1603,7 @@ void RTREE_QUAL::InitParVars( PartitionVars* a_parVars, int a_maxRects, int a_mi RTREE_TEMPLATE void RTREE_QUAL::PickSeeds( PartitionVars* a_parVars ) { - int seed0, seed1; + int seed0 = 0, seed1 = 0; ELEMTYPEREAL worst, waste; ELEMTYPEREAL area[MAXNODES + 1]; From db62d672d21f88c1921af2fca4f5f3361352af34 Mon Sep 17 00:00:00 2001 From: "tomasz.wlostowski@cern.ch" Date: Wed, 18 Sep 2013 19:55:16 +0200 Subject: [PATCH 2/4] Initial version of the P&S router. Buggy and crappy. --- pcbnew/router/CMakeLists.txt | 50 ++ pcbnew/router/direction.h | 295 +++++++++ pcbnew/router/pns_index.h | 241 +++++++ pcbnew/router/pns_item.cpp | 72 ++ pcbnew/router/pns_item.h | 155 +++++ pcbnew/router/pns_itemset.cpp | 73 +++ pcbnew/router/pns_itemset.h | 62 ++ pcbnew/router/pns_joint.h | 188 ++++++ pcbnew/router/pns_layerset.h | 118 ++++ pcbnew/router/pns_line.cpp | 704 ++++++++++++++++++++ pcbnew/router/pns_line.h | 251 +++++++ pcbnew/router/pns_line_placer.cpp | 715 ++++++++++++++++++++ pcbnew/router/pns_line_placer.h | 156 +++++ pcbnew/router/pns_node.cpp | 901 ++++++++++++++++++++++++++ pcbnew/router/pns_node.h | 260 ++++++++ pcbnew/router/pns_optimizer.cpp | 704 ++++++++++++++++++++ pcbnew/router/pns_optimizer.h | 164 +++++ pcbnew/router/pns_router.cpp | 774 ++++++++++++++++++++++ pcbnew/router/pns_router.h | 197 ++++++ pcbnew/router/pns_routing_settings.h | 53 ++ pcbnew/router/pns_segment.h | 119 ++++ pcbnew/router/pns_shove.cpp | 469 ++++++++++++++ pcbnew/router/pns_shove.h | 82 +++ pcbnew/router/pns_solid.cpp | 64 ++ pcbnew/router/pns_solid.h | 70 ++ pcbnew/router/pns_utils.cpp | 42 ++ pcbnew/router/pns_utils.h | 33 + pcbnew/router/pns_via.cpp | 148 +++++ pcbnew/router/pns_via.h | 118 ++++ pcbnew/router/pns_walkaround.cpp | 221 +++++++ pcbnew/router/pns_walkaround.h | 99 +++ pcbnew/router/router_preview_item.cpp | 197 ++++++ pcbnew/router/router_preview_item.h | 103 +++ pcbnew/router/router_tool.cpp | 397 ++++++++++++ pcbnew/router/router_tool.h | 79 +++ pcbnew/router/trace.h | 51 ++ 36 files changed, 8425 insertions(+) create mode 100644 pcbnew/router/CMakeLists.txt create mode 100644 pcbnew/router/direction.h create mode 100644 pcbnew/router/pns_index.h create mode 100644 pcbnew/router/pns_item.cpp create mode 100644 pcbnew/router/pns_item.h create mode 100644 pcbnew/router/pns_itemset.cpp create mode 100644 pcbnew/router/pns_itemset.h create mode 100644 pcbnew/router/pns_joint.h create mode 100644 pcbnew/router/pns_layerset.h create mode 100644 pcbnew/router/pns_line.cpp create mode 100644 pcbnew/router/pns_line.h create mode 100644 pcbnew/router/pns_line_placer.cpp create mode 100644 pcbnew/router/pns_line_placer.h create mode 100644 pcbnew/router/pns_node.cpp create mode 100644 pcbnew/router/pns_node.h create mode 100644 pcbnew/router/pns_optimizer.cpp create mode 100644 pcbnew/router/pns_optimizer.h create mode 100644 pcbnew/router/pns_router.cpp create mode 100644 pcbnew/router/pns_router.h create mode 100644 pcbnew/router/pns_routing_settings.h create mode 100644 pcbnew/router/pns_segment.h create mode 100644 pcbnew/router/pns_shove.cpp create mode 100644 pcbnew/router/pns_shove.h create mode 100644 pcbnew/router/pns_solid.cpp create mode 100644 pcbnew/router/pns_solid.h create mode 100644 pcbnew/router/pns_utils.cpp create mode 100644 pcbnew/router/pns_utils.h create mode 100644 pcbnew/router/pns_via.cpp create mode 100644 pcbnew/router/pns_via.h create mode 100644 pcbnew/router/pns_walkaround.cpp create mode 100644 pcbnew/router/pns_walkaround.h create mode 100644 pcbnew/router/router_preview_item.cpp create mode 100644 pcbnew/router/router_preview_item.h create mode 100644 pcbnew/router/router_tool.cpp create mode 100644 pcbnew/router/router_tool.h create mode 100644 pcbnew/router/trace.h diff --git a/pcbnew/router/CMakeLists.txt b/pcbnew/router/CMakeLists.txt new file mode 100644 index 0000000000..7995cb4af9 --- /dev/null +++ b/pcbnew/router/CMakeLists.txt @@ -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}) diff --git a/pcbnew/router/direction.h b/pcbnew/router/direction.h new file mode 100644 index 0000000000..9df3523987 --- /dev/null +++ b/pcbnew/router/direction.h @@ -0,0 +1,295 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __DIRECTION_H +#define __DIRECTION_H + +#include +#include + +/** + * 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 ""; + } + } + + /** + * 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 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 diff --git a/pcbnew/router/pns_index.h b/pcbnew/router/pns_index.h new file mode 100644 index 0000000000..c7f8704202 --- /dev/null +++ b/pcbnew/router/pns_index.h @@ -0,0 +1,241 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_INDEX_H +#define __PNS_INDEX_H + +#include +#include + +#include +#include + +#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 NetItemsList; + typedef SHAPE_INDEX ItemShapeIndex; + typedef boost::unordered_set ItemSet; + + PNS_INDEX(); + ~PNS_INDEX(); + + void Add( PNS_ITEM *aItem ); + void Remove ( PNS_ITEM *aItem ); + void Replace ( PNS_ITEM *aOldItem, PNS_ITEM *aNewItem ); + + template + int Query( const PNS_ITEM *aItem, int aMinDistance, Visitor &v); + template + 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 + int querySingle( int index, const SHAPE *aShape, int aMinDistance, Visitor &v); + + ItemShapeIndex *getSubindex( const PNS_ITEM *aItem ); + + ItemShapeIndex *m_subIndices[ MaxSubIndices ]; + std::map 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 + 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 + 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 + 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 diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp new file mode 100644 index 0000000000..43a6b97976 --- /dev/null +++ b/pcbnew/router/pns_item.cpp @@ -0,0 +1,72 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#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 (aOther); + if(line -> EndsWithVia()) + return collideSimple( &line->GetVia(), aClearance - line->GetWidth() / 2, aNeedMTV, aMTV ); + } + + return false; +} + +const std::string PNS_ITEM::GetKindStr() const +{ + switch(m_kind) + { + case LINE: return "line"; + case SEGMENT: return "segment"; + case VIA: return "via"; + case JOINT: return "joint"; + case SOLID: return "solid"; + default: return "unknown"; + } +} + +PNS_ITEM::~PNS_ITEM() +{ + +} + \ No newline at end of file diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h new file mode 100644 index 0000000000..22114ed6af --- /dev/null +++ b/pcbnew/router/pns_item.h @@ -0,0 +1,155 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_ITEM_H +#define __PNS_ITEM_H + +#include + +#include +#include + +#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 + diff --git a/pcbnew/router/pns_itemset.cpp b/pcbnew/router/pns_itemset.cpp new file mode 100644 index 0000000000..494c1a07a9 --- /dev/null +++ b/pcbnew/router/pns_itemset.cpp @@ -0,0 +1,73 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include + +#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; +} + diff --git a/pcbnew/router/pns_itemset.h b/pcbnew/router/pns_itemset.h new file mode 100644 index 0000000000..91ebea1a20 --- /dev/null +++ b/pcbnew/router/pns_itemset.h @@ -0,0 +1,62 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_ITEMSET_H +#define __PNS_ITEMSET_H + +#include + +#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 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 diff --git a/pcbnew/router/pns_joint.h b/pcbnew/router/pns_joint.h new file mode 100644 index 0000000000..88f7477a75 --- /dev/null +++ b/pcbnew/router/pns_joint.h @@ -0,0 +1,188 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_JOINT_H +#define __PNS_JOINT_H + +#include +#include + +#include + +#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 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 (m_linkedItems[0]); + PNS_SEGMENT *seg2 = static_cast (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 (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 diff --git a/pcbnew/router/pns_layerset.h b/pcbnew/router/pns_layerset.h new file mode 100644 index 0000000000..2809eb485c --- /dev/null +++ b/pcbnew/router/pns_layerset.h @@ -0,0 +1,118 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_LAYERSET_H +#define __PNS_LAYERSET_H + +#include + +/** + * 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 diff --git a/pcbnew/router/pns_line.cpp b/pcbnew/router/pns_line.cpp new file mode 100644 index 0000000000..cb729f96d0 --- /dev/null +++ b/pcbnew/router/pns_line.cpp @@ -0,0 +1,704 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include +#include + +#include + + +#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 outside, on_edge, inside; + SHAPE_LINE_CHAIN path; + + vector 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]) ; +} diff --git a/pcbnew/router/pns_line.h b/pcbnew/router/pns_line.h new file mode 100644 index 0000000000..cd47005606 --- /dev/null +++ b/pcbnew/router/pns_line.h @@ -0,0 +1,251 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_LINE_H +#define __PNS_LINE_H + +#include + +#include +#include +#include + +#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 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 (); + 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 + diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp new file mode 100644 index 0000000000..1057dc9062 --- /dev/null +++ b/pcbnew/router/pns_line_placer.cpp @@ -0,0 +1,715 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include +#include +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_line_placer.h" +#include "pns_walkaround.h" +#include "pns_shove.h" +#include "pns_utils.h" + +using namespace std; +using boost::optional; + +PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_NODE *aWorld ) +{ + m_initial_direction = DIRECTION_45(DIRECTION_45::N); + m_follow_mouse = false; + m_smoothing_step = 100000; + m_smooth_mouse = false; + m_iteration = 0; + m_world = aWorld; + m_mode = RM_Smart; + m_follow_mouse = true; + m_shove = NULL; +}; + +PNS_LINE_PLACER::~PNS_LINE_PLACER() +{ + if(m_shove) + delete m_shove; +} + +void PNS_LINE_PLACER::ApplySettings ( const PNS_ROUTING_SETTINGS& aSettings ) +{ + m_follow_mouse = aSettings.m_followMouse; + m_mode = aSettings.m_routingMode; + m_walkaroundIterationLimit = aSettings.m_walkaroundIterationLimit; + m_smartPads = aSettings.m_smartPads; +} + +void PNS_LINE_PLACER::StartPlacement(const VECTOR2I& aStart, int aNet, int aWidth, int aLayer ) +{ + m_direction = m_initial_direction; + TRACE(1, "world %p, intitial-direction %s layer %d\n", m_world % m_direction.Format().c_str() % aLayer); + m_head.SetNet(aNet); + m_tail.SetNet(aNet); + m_head.SetWidth(aWidth); + m_tail.SetWidth(aWidth); + m_head.GetLine().Clear(); + m_tail.GetLine().Clear(); + m_head.SetLayer(aLayer); + m_tail.SetLayer(aLayer); + m_iteration = 0; + m_p_start = aStart; + m_currentNode = m_world->Branch(); + m_head.SetWorld(m_currentNode); + m_tail.SetWorld(m_currentNode); + //if(m_shove) + // delete m_shove; + m_shove = new PNS_SHOVE(m_currentNode); + m_placingVia = false; +} + +void PNS_LINE_PLACER::SetInitialDirection(const DIRECTION_45& aDirection) +{ + m_initial_direction = aDirection; + + if(m_tail.GetCLine().SegmentCount() == 0) + m_direction = aDirection; +} + +/** + * Function handleSelfIntersections() + * + * Checks if the head of the track intersects its tail. If so, cuts the tail up to the + * intersecting segment and fixes the head direction to match the last segment before the cut. + * @return true if the line has been changed. + */ +bool PNS_LINE_PLACER::handleSelfIntersections() +{ + SHAPE_LINE_CHAIN::Intersections ips; + SHAPE_LINE_CHAIN& head = m_head.GetLine(); + SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); + + // if there is no tail, there is nothing to intersect with + if(tail.PointCount() < 2) + return false; + + tail.Intersect(head, ips); + + // no intesection points - nothing to reduce + if (ips.empty()) + return false; + + int n = INT_MAX; + VECTOR2I ipoint; + + // if there is more than one intersection, find the one that is + // closest to the beginning of the tail. + BOOST_FOREACH(SHAPE_LINE_CHAIN::Intersection i, ips) + { + if (i.our.Index() < n) + { + n = i.our.Index(); + ipoint = i.p; + } + } + + // ignore the point where head and tail meet + if(ipoint == head.CPoint(0) || ipoint == tail.CPoint(-1)) + return false; + + // Intersection point is on the first or the second segment: just start routing + // from the beginning + if (n < 2) + { + m_p_start = tail.Point(0); + m_direction = m_initial_direction; + tail.Clear(); + head.Clear(); + return true; + } else { + // Clip till the last tail segment before intersection. + // Set the direction to the one of this segment. + const SEG last = tail.CSegment(n - 1); + m_p_start = last.a; + m_direction = DIRECTION_45(last); + tail.Remove(n, -1); + return true; + } + return false; +} + +/** + * Function handlePullback() + * + * Deals with pull-back: reduces the tail if head trace is moved backwards wrs + * to the current tail direction. + * @return true if the line has been changed. + */ +bool PNS_LINE_PLACER::handlePullback() +{ + SHAPE_LINE_CHAIN& head = m_head.GetLine(); + SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); + + int n = tail.PointCount(); + + if(n == 0) + return false; + else if (n == 1) + { + m_p_start = tail.CPoint(0); + tail.Clear(); + return true; + } + + DIRECTION_45 first_head (head.Segment(0)); + DIRECTION_45 last_tail (tail.Segment(-1)); + DIRECTION_45::AngleType angle = first_head.Angle(last_tail); + + // case 1: we have a defined routing direction, and the currently computed head + // goes in different one. + bool pullback_1 = false;//(m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head); + + // case 2: regardless of the current routing direction, if the tail/head extremities form + // an acute or right angle, reduce the tail by one segment (and hope that further iterations) + // will result with a cleaner trace + bool pullback_2 = (angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE); + + if(pullback_1 || pullback_2) + { + + const SEG last = tail.CSegment(-1); + m_direction = DIRECTION_45(last); + m_p_start = last.a; + + + TRACE(0, "Placer: pullback triggered [%d] [%s %s]", n % last_tail.Format().c_str() % first_head.Format().c_str()); + // erase the last point in the tail, hoping that the next iteration will result with a head + // trace that starts with a segment following our current direction. + if(n < 2) + tail.Clear(); // don't leave a single-point tail + else + tail.Remove(-1, -1); + + if( !tail.SegmentCount() ) + m_direction = m_initial_direction; + + return true; + } + + return false; +} + +/** + * Function reduceTail() + * + * Attempts to reduce the numer of segments in the tail by trying to replace a certain number + * of latest tail segments with a direct trace leading to aEnd that does not collide with anything. + * @param aEnd: current routing destination point. + * @return true if the line has been changed. + */ +bool PNS_LINE_PLACER::reduceTail(const VECTOR2I& aEnd) +{ + SHAPE_LINE_CHAIN& head = m_head.GetLine(); + SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); + + int n = tail.SegmentCount(); + + // Don't attempt this for too short tails + if (n < 2) + return false; + + // Start from the segment farthest from the end of the tail + //int start_index = std::max(n - 1 - ReductionDepth, 0); + + DIRECTION_45 new_direction; + VECTOR2I new_start; + int reduce_index = -1; + + DIRECTION_45 head_dir ( head.Segment(0) ); + + for(int i = tail.SegmentCount() - 1; i >= 0; i--) + { + const SEG s = tail.CSegment(i); + DIRECTION_45 dir (s); + + // calculate a replacement route and check if it matches the direction of the segment to be replaced + SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace(s.a, aEnd); + + PNS_LINE tmp (m_tail, replacement); + + if (m_currentNode->CheckColliding(&tmp, PNS_ITEM::ANY)) + break; + + if(DIRECTION_45(replacement.Segment(0)) == dir) + { + new_start = s.a; + new_direction = dir; + reduce_index = i; + } + } + + if(reduce_index >= 0) + { + + TRACE(0, "Placer: reducing tail: %d", reduce_index); + SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace(new_start, aEnd); + + m_p_start = new_start; + m_direction = new_direction; + tail.Remove(reduce_index+1, -1); + head.Clear(); + return true; + } + + if( !tail.SegmentCount() ) + m_direction = m_initial_direction; + + return false; +} + + +/** + * Function checkObtusity() + * + * Helper that checks if segments a and b form an obtuse angle (in 45-degree regime). + * @return true, if angle (a, b) is obtuse + */ +bool PNS_LINE_PLACER::checkObtusity(const SEG& a, const SEG& b) const +{ + const DIRECTION_45 dir_a(a); + const DIRECTION_45 dir_b(b); + return dir_a.IsObtuse(dir_b) || dir_a == dir_b; +} + + +/** + * Function mergeHead() + * + * Moves "estabished" segments from the head to the tail if certain conditions are met. + * @return true, if the line has been changed. + */ +bool PNS_LINE_PLACER::mergeHead() +{ + SHAPE_LINE_CHAIN& head = m_head.GetLine(); + SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); + + const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_UNDEFINED; + + head.Simplify(); + tail.Simplify(); + + int n_head = head.SegmentCount(); + int n_tail = tail.SegmentCount(); + + if( n_head < 3 ) + { + TRACEn(4, "Merge failed: not enough head segs."); + return false; + } + + if (n_tail && head.CPoint(0) != tail.CPoint(-1)) + { + TRACEn(4, "Merge failed: head and tail discontinuous."); + return false; + } + + if( m_head.CountCorners(ForbiddenAngles) != 0 ) + return false; + + DIRECTION_45 dir_tail, dir_head; + + dir_head = DIRECTION_45(head.CSegment(0)); + + if(n_tail) + { + dir_tail = DIRECTION_45(tail.CSegment(-1)); + if(dir_head.Angle(dir_tail) & ForbiddenAngles) + return false; + } + + if(!n_tail) + tail.Append(head.CSegment(0).a); + + for (int i = 0; i < n_head - 2; i++) + { + tail.Append(head.CSegment(i).b); + } + + tail.Simplify(); + + SEG last = tail.CSegment(-1); + + m_p_start = last.b; + m_direction = DIRECTION_45(last).Right(); + + + head.Remove(0, n_head - 2); + + TRACE(0, "Placer: merge %d, new direction: %s", n_head % m_direction.Format().c_str()); + + + head.Simplify(); + tail.Simplify(); + + return true; +} + +bool PNS_LINE_PLACER::handleViaPlacement ( PNS_LINE& aHead ) +{ + if(!m_placingVia) + return true; + + PNS_LAYERSET allLayers (0, 15); + PNS_VIA v (aHead.GetCLine().CPoint(-1), allLayers, m_viaDiameter, aHead.GetNet()); + v.SetDrill(m_viaDrill); + + VECTOR2I force; + VECTOR2I lead = aHead.GetCLine().CPoint(-1) - aHead.GetCLine().CPoint(0); + + + if( v.PushoutForce ( m_shove->GetCurrentNode(), lead, force, true, 20 ) ) + { + SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace(aHead.GetCLine().CPoint(0), aHead.GetCLine().CPoint(-1) + force); + aHead = PNS_LINE(aHead, line); + + v.SetPos(v.GetPos() + force); + return true; + } + + return false; +} + +/** + * Function routeHead() + * + * Computes the head trace between the current start point (m_p_start) and point aP, + * starting with direction defined in m_direction. The trace walks around all + * colliding solid or non-movable items. Movable segments are ignored, as they'll be handled + * later by the shove algorithm. + */ +bool PNS_LINE_PLACER::routeHead(const VECTOR2I& aP, PNS_LINE& aNewHead, bool aCwWalkaround) +{ + // STAGE 1: route a simple two-segment trace between m_p_start and aP... + SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace(m_p_start, aP); + + PNS_LINE initTrack (m_head, line); + PNS_LINE walkFull, walkSolids; + + + if(m_mode == RM_Ignore) + { + aNewHead = initTrack; + return true; + } + handleViaPlacement(initTrack); + + m_currentNode = m_shove->GetCurrentNode(); + + PNS_OPTIMIZER optimizer(m_currentNode); + PNS_WALKAROUND walkaround( m_currentNode ); + + walkaround.SetSolidsOnly(false); + walkaround.SetIterationLimit(m_mode == RM_Walkaround ? 8 : 5 ); + //walkaround.SetApproachCursor(true, aP); + + PNS_WALKAROUND::WalkaroundStatus wf = walkaround.Route(initTrack, walkFull); + +#if 0 + + if(m_mode == RM_Walkaround) + { + // walkaround. +// PNSDisplayDebugLine (walkFull.GetCLine(), 4); + + if(wf == PNS_WALKAROUND::STUCK) + { + aNewHead = m_head; + aNewHead.SetShape(walkFull.GetCLine()); + aNewHead = aNewHead.ClipToNearestObstacle(m_currentNode); + return false; + } + + aNewHead = m_head; + aNewHead.SetShape(walkFull.GetCLine()); + +// printf("nh w %d l %d\n", aNewHead.GetWidth(), aNewHead.GetLayers().Start()); + return true; + } +#endif + + PNS_COST_ESTIMATOR cost_walk, cost_orig; + + walkaround.SetApproachCursor ( false, aP ); + walkaround.SetSolidsOnly(true); + walkaround.SetIterationLimit( 10 ); + PNS_WALKAROUND::WalkaroundStatus stat_solids = walkaround.Route(initTrack, walkSolids); + + optimizer.SetEffortLevel ( PNS_OPTIMIZER::MERGE_SEGMENTS ); + optimizer.SetCollisionMask (PNS_ITEM::SOLID); + optimizer.Optimize(&walkSolids); + #if 0 + optimizer.SetCollisionMask (-1); + optimizer.Optimize(&walkFull); + #endif + cost_orig.Add(initTrack); + cost_walk.Add(walkFull); + + if(m_mode == RM_Smart || m_mode == RM_Shove) + { + PNS_LINE l2; + + bool walk_better = cost_orig.IsBetter(cost_walk, 1.5, 10.0); + walk_better = false; + +#if 0 + printf("RtTrk width %d %d %d", initTrack.GetWidth(), walkFull.GetWidth(), walkSolids.GetWidth()); + printf("init-coll %d\n", m_currentNode->CheckColliding(&initTrack)? 1: 0); + printf("total cost: walk cor %.0f len %.0f orig cor %.0f len %.0f walk-better %d\n", + cost_walk.GetCornerCost(), cost_walk.GetLengthCost(), + cost_orig.GetCornerCost(), cost_orig.GetLengthCost(), + walk_better ); +#endif + + if(m_mode == RM_Smart && wf == PNS_WALKAROUND::DONE && walk_better && walkFull.GetCLine().CPoint(-1) == initTrack.GetCLine().CPoint(-1)) + l2 = walkFull; + else if (stat_solids == PNS_WALKAROUND::DONE) + l2 = walkSolids; + else + l2 = initTrack.ClipToNearestObstacle(m_shove->GetCurrentNode()); + + PNS_LINE l ( m_tail ); + l.GetLine().Append( l2.GetCLine() ); + l.GetLine().Simplify(); + + if(m_placingVia) + { + PNS_LAYERSET allLayers(0,15); + PNS_VIA v1( l.GetCLine().CPoint(-1), allLayers, m_viaDiameter ); + PNS_VIA v2( l2.GetCLine().CPoint(-1), allLayers, m_viaDiameter ); + v1.SetDrill(m_viaDrill); + v2.SetDrill(m_viaDrill); + + l.AppendVia ( v1 ); + l2.AppendVia ( v2 ); + } + + PNS_SHOVE::ShoveStatus status = m_shove->ShoveLines(&l); + m_currentNode = m_shove->GetCurrentNode(); + + if (status == PNS_SHOVE::SH_OK) + { + + optimizer.SetWorld (m_currentNode); + optimizer.ClearCache(); + optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS ); + optimizer.SetCollisionMask (-1); + optimizer.Optimize(&l2); + + aNewHead = l2; + + return true; + } else { + walkaround.SetWorld( m_currentNode ); + walkaround.SetSolidsOnly(false); + walkaround.SetIterationLimit( 10 ); + walkaround.SetApproachCursor ( true, aP ); + walkaround.Route(initTrack, l2); + aNewHead = l2.ClipToNearestObstacle (m_shove->GetCurrentNode()); + //aNewHead = l2; + + return false; + } + + } + + return false; +} + + +/** + * Function optimizeTailHeadTransition() + * + * Tries to reduce the corner count of the most recent part of tail/head by merging + * obtuse/collinear segments. + * @return true, if the line has been changed. + */ +bool PNS_LINE_PLACER::optimizeTailHeadTransition() +{ + SHAPE_LINE_CHAIN& head = m_head.GetLine(); + SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); + + const int TailLookbackSegments = 5; + + int threshold = min(tail.PointCount(), TailLookbackSegments + 1); + + if(tail.SegmentCount() < 3) + return false; + + // assemble TailLookbackSegments tail segments with the current head + SHAPE_LINE_CHAIN opt_line = tail.Slice(-threshold, -1); + + opt_line.Append(head); +// opt_line.Simplify(); + + PNS_LINE new_head(m_tail, opt_line); + + // and see if it could be made simpler by merging obtuse/collnear segments. If so, + // replace the (threshold) last tail points and the head with the optimized line + + //if(PNS_OPTIMIZER::Optimize(&new_head, PNS_OPTIMIZER::MERGE_SEGMENTS)) + + + if(new_head.MergeSegments()) + { + PNS_LINE tmp(m_tail, opt_line); + + TRACE(0, "Placer: optimize tail-head [%d]", threshold); + + head.Clear(); + tail.Replace(-threshold, -1, new_head.GetCLine()); + tail.Simplify(); + + m_p_start = new_head.GetCLine().CPoint(-1); + m_direction = DIRECTION_45(new_head.GetCLine().CSegment(-1)); + + return true; + } + + return false; +} + +/** + * Function routeStep() + * + * Performs a single routing alorithm step, for the end point aP. + * @param aP ending point of current route + * @return true, if the line has been changed. + */ + + +void PNS_LINE_PLACER::routeStep(const VECTOR2I& aP) +{ + bool fail = false; + bool go_back = false; + + int i, n_iter = 1; + + PNS_LINE new_head; + + m_follow_mouse = true; + + TRACE(2,"INIT-DIR: %s head: %d, tail: %d segs\n", m_initial_direction.Format().c_str() % m_head.GetCLine().SegmentCount() % m_tail.GetCLine().SegmentCount()); + + for(i = 0; i < n_iter; i++) + { + + if(!go_back && m_follow_mouse) + reduceTail(aP); + + go_back = false; + + if(!routeHead(aP, new_head, true)) + fail = true; + + if(!new_head.Is45Degree()) + fail = true; + + if(!m_follow_mouse) + return; + + m_head = new_head; + + if(handleSelfIntersections()) + { + n_iter++; + go_back = true; + } + + if(!go_back && handlePullback()) + { + n_iter++; + go_back = true; + } + } + + if(!fail) + { + if(optimizeTailHeadTransition()) + return; + mergeHead(); + } + +} + +/** + * Function Route() + * + * Re-routes the current track to point aP. Returns true, when routing has completed + * successfully (i.e. the trace end has reached point aP), and false if the trace was stuck somewhere + * on the way. May call routeStep() repetitively due to mouse smoothing. + * @param aP ending point of current route. + * @return true, if the routing is complete. + */ +bool PNS_LINE_PLACER::Route(const VECTOR2I& aP) +{ + if(m_smooth_mouse) + { + VECTOR2I p_cur = m_p_start; + VECTOR2I step = (aP - m_p_start).Resize(m_smoothing_step); + + do + { + if ((p_cur - aP).EuclideanNorm() <= m_smoothing_step) + p_cur = aP; + else + p_cur += step; + + routeStep(p_cur); + + } while (p_cur != aP); + } else + routeStep(aP); + + return CurrentEnd() == aP; +} + + +const PNS_LINE PNS_LINE_PLACER::GetTrace() const +{ + PNS_LINE tmp(m_head); + tmp.SetShape( m_tail.GetCLine() ); + tmp.GetLine().Append( m_head.GetCLine() ); + tmp.GetLine().Simplify(); + return tmp; +} + +void PNS_LINE_PLACER::FlipPosture() +{ + m_initial_direction = m_initial_direction.Right(); + m_direction = m_direction.Right(); +} + +void PNS_LINE_PLACER::GetUpdatedItems( PNS_NODE::ItemVector& aRemoved, PNS_NODE::ItemVector& aAdded) +{ + return m_shove->GetCurrentNode()->GetUpdatedItems(aRemoved, aAdded); +} + +PNS_NODE *PNS_LINE_PLACER::GetCurrentNode() const +{ + return m_shove->GetCurrentNode(); +} \ No newline at end of file diff --git a/pcbnew/router/pns_line_placer.h b/pcbnew/router/pns_line_placer.h new file mode 100644 index 0000000000..40d769cc88 --- /dev/null +++ b/pcbnew/router/pns_line_placer.h @@ -0,0 +1,156 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_LINE_PLACER_H +#define __PNS_LINE_PLACER_H + +#include + +#include +#include + +#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 diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp new file mode 100644 index 0000000000..5a1932f1e6 --- /dev/null +++ b/pcbnew/router/pns_node.cpp @@ -0,0 +1,901 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#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 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(a) -> GetWidth() / 2; + + if( a->OfKind (PNS_ITEM::LINE) ) + clearance += static_cast(a) -> GetWidth() / 2; + + if( b->OfKind (PNS_ITEM::SEGMENT) ) + clearance += static_cast(b) -> GetWidth() / 2; + + if( b->OfKind (PNS_ITEM::LINE) ) + clearance += static_cast(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::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 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(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(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(aItem)); + break; + + case PNS_ITEM::SEGMENT: + addSegment(static_cast(aItem)); + break; + + case PNS_ITEM::LINE: + addLine( static_cast (aItem)); + break; + + case PNS_ITEM::VIA: + addVia (static_cast(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 *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(aItem)); + break; + case PNS_ITEM::LINE: + removeLine(static_cast(aItem)); + break; + case PNS_ITEM::VIA: + removeVia(static_cast(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 &aLines ) +{ + BOOST_FOREACH(PNS_ITEM *item, a.GetLinkList()) + { + if(item->GetKind() == PNS_ITEM::SEGMENT) + { + PNS_SEGMENT *seg = static_cast(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 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 all_segs; + SHAPE_INDEX_LIST::iterator i; + + for(i = m_items.begin(); i != m_items.end() ; i++) + { + if((*i)->GetKind() == PNS_ITEM::SEGMENT) + all_segs.insert(static_cast(*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(*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(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::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 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& 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); + } +} diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h new file mode 100644 index 0000000000..25056cbd02 --- /dev/null +++ b/pcbnew/router/pns_node.h @@ -0,0 +1,260 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ +#ifndef __PNS_NODE_H +#define __PNS_NODE_H + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#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 OptObstacle; + typedef std::vector ItemVector; + typedef std::vector Obstacles; + typedef boost::optional 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 &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 &aConnectedJoints ); + + ///> Destroys all child nodes. Applicable only to the root node. + void KillChildren(); + + void AllItemsInNet ( int aNet, std::list& aItems); + +private: + + struct obstacleVisitor; + typedef boost::unordered_multimap 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 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 m_children; + + ///> hash of root's items that are more recent in this node + boost::unordered_set 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 diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp new file mode 100644 index 0000000000..7291fba1ce --- /dev/null +++ b/pcbnew/router/pns_optimizer.cpp @@ -0,0 +1,704 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ +#include + +#include +#include + +#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_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 *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 (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 (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(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 (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 RtVariant; + vector 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); +} diff --git a/pcbnew/router/pns_optimizer.h b/pcbnew/router/pns_optimizer.h new file mode 100644 index 0000000000..f090528273 --- /dev/null +++ b/pcbnew/router/pns_optimizer.h @@ -0,0 +1,164 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ +#ifndef __PNS_OPTIMIZER_H +#define __PNS_OPTIMIZER_H + +#include +#include + +#include +#include + +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 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 m_cache; + + typedef boost::unordered_map CachedItemTags; + CachedItemTags m_cacheTags; + PNS_NODE *m_world; + int m_collisionKindMask; + int m_effortLevel; + bool m_keepPostures; +}; + +#endif diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp new file mode 100644 index 0000000000..720d4f06e0 --- /dev/null +++ b/pcbnew/router/pns_router.cpp @@ -0,0 +1,774 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include + +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 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<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 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 (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(item)->GetCenter(); + aSplitsSegment = false; + break; + case PNS_ITEM::VIA: + anchor = static_cast(item)->GetPos(); + aSplitsSegment = false; + break; + case PNS_ITEM::SEGMENT: + { + PNS_SEGMENT *seg = static_cast(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(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(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(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(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 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); + } +} + diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h new file mode 100644 index 0000000000..e0016b2dc8 --- /dev/null +++ b/pcbnew/router/pns_router.h @@ -0,0 +1,197 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ +#ifndef __PNS_ROUTER_H +#define __PNS_ROUTER_H + +#include + +#include +#include + +#include + +#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 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 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 m_hiddenItems; + +}; + +#endif \ No newline at end of file diff --git a/pcbnew/router/pns_routing_settings.h b/pcbnew/router/pns_routing_settings.h new file mode 100644 index 0000000000..86c955dfc1 --- /dev/null +++ b/pcbnew/router/pns_routing_settings.h @@ -0,0 +1,53 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#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 + diff --git a/pcbnew/router/pns_segment.h b/pcbnew/router/pns_segment.h new file mode 100644 index 0000000000..6cef1b15a6 --- /dev/null +++ b/pcbnew/router/pns_segment.h @@ -0,0 +1,119 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_SEGMENT_H +#define __PNS_SEGMENT_H + +#include + +#include +#include +#include + +#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(&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 diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp new file mode 100644 index 0000000000..bd3e12b72e --- /dev/null +++ b/pcbnew/router/pns_shove.cpp @@ -0,0 +1,469 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include +#include + +#include + +#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 + +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 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(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; + } +} + diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h new file mode 100644 index 0000000000..c40dbe9858 --- /dev/null +++ b/pcbnew/router/pns_shove.h @@ -0,0 +1,82 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_SHOVE_H +#define __PNS_SHOVE_H + +#include +#include + +#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 m_nodeStack; + PNS_NODE *m_root; + PNS_NODE *m_currentNode; + int m_iterLimit; +}; + +#endif diff --git a/pcbnew/router/pns_solid.cpp b/pcbnew/router/pns_solid.cpp new file mode 100644 index 0000000000..e3841e72ca --- /dev/null +++ b/pcbnew/router/pns_solid.cpp @@ -0,0 +1,64 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include + +#include +#include +#include +#include + +#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 (m_shape); + return OctagonalHull( rect->GetPosition(), + rect->GetSize(), + aClearance + 1, + 0.2 * aClearance ); + } + + case SH_CIRCLE: + { + SHAPE_CIRCLE *circle = static_cast (m_shape); + int r = circle->GetRadius(); + return OctagonalHull( circle->GetCenter() - VECTOR2I(r, r), + VECTOR2I(2 * r, 2 * r), + aClearance + 1, + 0.52 * (r + aClearance) ); + } + default: + break; + } + + return SHAPE_LINE_CHAIN(); +} + +PNS_ITEM *PNS_SOLID::Clone() const +{ + // solids are never cloned as the shove algorithm never moves them + assert(false); +} \ No newline at end of file diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h new file mode 100644 index 0000000000..d89135d00f --- /dev/null +++ b/pcbnew/router/pns_solid.h @@ -0,0 +1,70 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_SOLID_H +#define __PNS_SOLID_H + +#include + +#include +#include +#include + +#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 + diff --git a/pcbnew/router/pns_utils.cpp b/pcbnew/router/pns_utils.cpp new file mode 100644 index 0000000000..6a2fbb49b3 --- /dev/null +++ b/pcbnew/router/pns_utils.cpp @@ -0,0 +1,42 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#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; +} + diff --git a/pcbnew/router/pns_utils.h b/pcbnew/router/pns_utils.h new file mode 100644 index 0000000000..97ce5d9884 --- /dev/null +++ b/pcbnew/router/pns_utils.h @@ -0,0 +1,33 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_UTILS_H +#define __PNS_UTILS_H + +#include +#include + + +/** Various utility functions */ + +const SHAPE_LINE_CHAIN OctagonalHull(const VECTOR2I& aP0, const VECTOR2I& aSize, int aClearance, int aChamfer); + +#endif // __PNS_UTILS_H + diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp new file mode 100644 index 0000000000..81a3d56ef2 --- /dev/null +++ b/pcbnew/router/pns_via.cpp @@ -0,0 +1,148 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include "pns_via.h" +#include "pns_node.h" +#include "pns_utils.h" + +#include + +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(shape); + return Circle2Circle( cir->GetCenter(), p, cir->GetRadius(), r + clearance + 1, force ); + } + case SH_RECT: + { + const SHAPE_RECT *rect = static_cast(shape); + return Rect2Circle( rect->GetPosition(), rect->GetSize(), p, r + clearance + 1, force ); + } + default: + return false; + + } + return false; +} + + +bool PNS_VIA::PushoutForce ( PNS_NODE *aNode, const VECTOR2I &aDirection, VECTOR2I& aForce, bool aSolidsOnly, int aMaxIterations) +{ + int iter = 0; + PNS_VIA mv ( *this); + VECTOR2I force, totalForce; + + while(iter < aMaxIterations) + { + PNS_NODE::OptObstacle obs = aNode->CheckColliding( &mv, aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY); + + if(!obs) + break; + + int clearance = aNode->GetClearance(obs->item, &mv); + + if(iter > 10) + { + VECTOR2I l = - aDirection.Resize(m_diameter / 4); + totalForce += l; + mv.SetPos(mv.GetPos() + l); + } + + if( ShPushoutForce(obs->item->GetShape(), mv.GetPos(), mv.GetDiameter() / 2, force, clearance) ) + { + totalForce += force; + mv.SetPos(mv.GetPos() + force); + } + + + iter++; + } + + if(iter == aMaxIterations) + return false; + + aForce = totalForce; + return true; +} + +const SHAPE_LINE_CHAIN PNS_VIA::Hull(int aClearance, int aWalkaroundThickness) const +{ + return OctagonalHull( m_pos - VECTOR2I(m_diameter/2, m_diameter/2), VECTOR2I(m_diameter, m_diameter), aClearance + 1, (2*aClearance + m_diameter) * 0.26); +} + \ No newline at end of file diff --git a/pcbnew/router/pns_via.h b/pcbnew/router/pns_via.h new file mode 100644 index 0000000000..3fdd8f8508 --- /dev/null +++ b/pcbnew/router/pns_via.h @@ -0,0 +1,118 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __PNS_VIA_H +#define __PNS_VIA_H + +#include +#include + +#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 diff --git a/pcbnew/router/pns_walkaround.cpp b/pcbnew/router/pns_walkaround.cpp new file mode 100644 index 0000000000..c384fdbee5 --- /dev/null +++ b/pcbnew/router/pns_walkaround.cpp @@ -0,0 +1,221 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include + +#include +#include + +#include + +#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& 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; +} diff --git a/pcbnew/router/pns_walkaround.h b/pcbnew/router/pns_walkaround.h new file mode 100644 index 0000000000..0c71e0f190 --- /dev/null +++ b/pcbnew/router/pns_walkaround.h @@ -0,0 +1,99 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#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 + diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp new file mode 100644 index 0000000000..b63233c821 --- /dev/null +++ b/pcbnew/router/router_preview_item.cpp @@ -0,0 +1,197 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include + +#include "class_track.h" +#include + +#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(aItem)->GetWidth(); + m_line = * static_cast(aItem->GetShape()); + break; + + case PNS_ITEM::SEGMENT: + m_type = PR_LINE; + m_width = static_cast(aItem)->GetWidth(); + m_line = * static_cast(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(aItem)->GetDiameter(); + m_viaCenter = static_cast(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 (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; +} diff --git a/pcbnew/router/router_preview_item.h b/pcbnew/router/router_preview_item.h new file mode 100644 index 0000000000..bedd8a0c2a --- /dev/null +++ b/pcbnew/router/router_preview_item.h @@ -0,0 +1,103 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __ROUTER_PREVIEW_ITEM_H +#define __ROUTER_PREVIEW_ITEM_H + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +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 diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp new file mode 100644 index 0000000000..7583dce6ce --- /dev/null +++ b/pcbnew/router/router_tool.cpp @@ -0,0 +1,397 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#include +#include + +#include "class_drawpanel_gal.h" +#include "class_board_item.h" +#include "class_board.h" + +#include +#include +#include +#include + +#include +#include + +#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 (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 (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 (); + + 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 (); + + 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(m_startItem)->GetWidth(); + + m_router->SetCurrentWidth(width); + m_router->SwitchLayer(m_startLayer); + + getEditFrame() -> 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() -> 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() -> SetTopLayer (m_router->GetCurrentLayer()); + m_router->Move ( m_endSnapPoint, m_endItem ); + + break; + + case '-': + m_router->SwitchLayer ( m_router->NextCopperLayer (false) ); + getEditFrame() -> 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; +} + + diff --git a/pcbnew/router/router_tool.h b/pcbnew/router/router_tool.h new file mode 100644 index 0000000000..abf7dabb38 --- /dev/null +++ b/pcbnew/router/router_tool.h @@ -0,0 +1,79 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __ROUTER_TOOL_H +#define __ROUTER_TOOL_H + +#include +#include + +#include +#include + +#include +#include + +#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 m_menu;*/ + CONTEXT_MENU * m_menu; +}; + +#endif diff --git a/pcbnew/router/trace.h b/pcbnew/router/trace.h new file mode 100644 index 0000000000..afce94d148 --- /dev/null +++ b/pcbnew/router/trace.h @@ -0,0 +1,51 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * 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 . + */ + +#ifndef __TRACE_H +#define __TRACE_H + + +#ifdef DEBUG + +#include +#include +#include + +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 From 87dccc9f817a5d568a0e516cf74f52b644ebfc56 Mon Sep 17 00:00:00 2001 From: "tomasz.wlostowski@cern.ch" Date: Wed, 18 Sep 2013 19:56:37 +0200 Subject: [PATCH 3/4] P&S router: some missing files --- include/profile.h | 147 +++++++++++++++++++++++++++++++++++++ include/tool/tool_action.h | 86 ++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 include/profile.h create mode 100644 include/tool/tool_action.h diff --git a/include/profile.h b/include/profile.h new file mode 100644 index 0000000000..f779dfae59 --- /dev/null +++ b/include/profile.h @@ -0,0 +1,147 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file profile.h: + * @brief Simple profiling functions for measuring code execution time. + */ + +#ifndef __TPROFILE_H +#define __TPROFILE_H + +#include +#include + +/** + * Function rdtsc + * Returns processor's time-stamp counter. Main purpose is precise time measuring of code + * execution time. + * @return unsigned long long - Value of time-stamp counter. + */ +#if defined(__i386__) +static __inline__ unsigned long long rdtsc() +{ + unsigned long long int x; + __asm__ volatile ( ".byte 0x0f, 0x31" : "=A" ( x ) ); + + return x; +} + + +#elif defined(__x86_64__) +static __inline__ unsigned long long rdtsc() +{ + unsigned hi, lo; + __asm__ __volatile__ ( "rdtsc" : "=a" ( lo ), "=d" ( hi ) ); + + return ( (unsigned long long) lo ) | ( ( (unsigned long long) hi ) << 32 ); +} + + +#elif defined(__powerpc__) +static __inline__ unsigned long long rdtsc() +{ + unsigned long long int result = 0; + unsigned long int upper, lower, tmp; + __asm__ volatile ( + "0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r" ( upper ), "=r" ( lower ), "=r" ( tmp ) + ); + + result = upper; + result = result << 32; + result = result | lower; + + return result; +} + + +#endif /* __powerpc__ */ + +// Fixme: OS X version +/** + * Function get_tics + * Returns the number of microseconds that have elapsed since the system was started. + * @return uint64_t Number of microseconds. + */ +static inline uint64_t get_tics() +{ + struct timeval tv; + gettimeofday( &tv, NULL ); + + return (uint64_t) tv.tv_sec * 1000000ULL + (uint64_t) tv.tv_usec; +} + + +/** + * Structure for storing data related to profiling counters. + */ +struct prof_counter +{ + uint64_t value; /// Stored timer value + bool use_rdtsc; /// Method of time measuring (rdtsc or tics) +}; + +/** + * Function prof_start + * Begins code execution time counting for a given profiling counter. + * @param cnt is the counter which should be started. + * @param use_rdtsc tells if processor's time-stamp counter should be used for time counting. + * Otherwise is system tics method will be used. IMPORTANT: time-stamp counter should not + * be used on multicore machines executing threaded code. + */ +static inline void prof_start( prof_counter* cnt, bool use_rdtsc ) +{ + cnt->use_rdtsc = use_rdtsc; + + if( use_rdtsc ) + { + cnt->value = rdtsc(); + } + else + { + cnt->value = get_tics(); + } +} + + +/** + * Function prof_stop + * Ends code execution time counting for a given profiling counter. + * @param cnt is the counter which should be stopped. + */ +static inline void prof_end( prof_counter* cnt ) +{ + if( cnt->use_rdtsc ) + cnt->value = rdtsc() - cnt->value; + else + cnt->value = get_tics() - cnt->value; +} + +#endif diff --git a/include/tool/tool_action.h b/include/tool/tool_action.h new file mode 100644 index 0000000000..e2eced3359 --- /dev/null +++ b/include/tool/tool_action.h @@ -0,0 +1,86 @@ +#ifndef __TOOL_ACTION_H +#define __TOOL_ACTION_H + +#include +#include + +#include + +///> Scope of tool actions +enum TOOL_ActionScope { + SCOPE_CONTEXT = 1, ///> Action belongs to a particular tool (i.e. a part of a pop-up menu) + SCOPE_GLOBAL ///> Global action (toolbar/main menu event, global shortcut) +}; + +// TOOL_ACTION - represents a single action. For instance: +// - changing layer to top by pressing PgUp +// - running the DRC from the menu +// and so on, and so forth.... +class TOOL_ACTION +{ + public: + + + TOOL_ACTION + ( + const std::string& name, + TOOL_ActionScope scope = SCOPE_GLOBAL, + int aDefaultHotKey = 0, + const wxString& menuItem = wxT(""), + const wxString& menuDesc = wxT("") + ) : + m_name(name), + m_scope(scope), + m_defaultHotKey(aDefaultHotKey), + m_currentHotKey(aDefaultHotKey), + m_menuItem(menuItem), + m_menuDescription(menuDesc) {} + + bool operator == ( const TOOL_ACTION& rhs ) const + { + return m_id == rhs.m_id; + } + + bool operator != ( const TOOL_ACTION& rhs ) const + { + return m_id != rhs.m_id; + } + + bool hasHotKey() const + { + return m_currentHotKey > 0; + } + + private: + friend class TOOL_MANAGER; + + void setId ( int aId ) + { + m_id = aId; + } + + + // name of the action (convention is: app.[tool.]action.name) + std::string m_name; + TOOL_ActionScope m_scope; + int m_defaultHotKey; + int m_currentHotKey; + + // Menu item text + wxString m_menuItem; + // Pop-up help + wxString m_menuDescription; + + //KiBitmap m_bitmap; + + // Unique ID for fast matching. Assigned by TOOL_MANAGER + int m_id; + + // Origin of the action + TOOL_BASE *m_origin; + + // Originating UI object + wxWindow *m_uiOrigin; +}; + +#endif From 979f549e993ef20e0b5b915f02cc59a4f2ecd9ee Mon Sep 17 00:00:00 2001 From: "tomasz.wlostowski@cern.ch" Date: Wed, 18 Sep 2013 20:11:21 +0200 Subject: [PATCH 4/4] A very short insturction for the P&S Router. --- Documentation/pns_router.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Documentation/pns_router.txt diff --git a/Documentation/pns_router.txt b/Documentation/pns_router.txt new file mode 100644 index 0000000000..209a950931 --- /dev/null +++ b/Documentation/pns_router.txt @@ -0,0 +1,11 @@ +So, finally we've got an integrated interactive, push-and-sometimes-shove router, although with a very limited user interface: + +- Edit->Interactive router launches the tool, +- while routing: 'V' key places a via, +- '+' and '-' keys cycle through available layers, +- '/' key switches track posture. + +Via/track dimensions are taken from the netclasses. +There are no other options available for the time being - promise to add them soon :) + +Tom \ No newline at end of file