From 7434e68876a3e20b6e9ebe71d99bd2f1e875aa06 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 19 Jul 2016 16:34:09 -0400 Subject: [PATCH] Pcbnew: improved track scripting support. --- include/macros.h | 5 +- pcbnew/class_board.cpp | 206 ++++++++++++++++++++++++++++++++++++++++ pcbnew/class_board.h | 30 +++++- pcbnew/class_netclass.h | 14 +-- pcbnew/class_netinfo.h | 20 ++-- 5 files changed, 254 insertions(+), 21 deletions(-) diff --git a/include/macros.h b/include/macros.h index c93863d960..52580428fc 100644 --- a/include/macros.h +++ b/include/macros.h @@ -33,7 +33,7 @@ #include #include #include - +#include // std::shared_ptr /** * Macro TO_UTF8 @@ -123,11 +123,12 @@ template inline const T& Clamp( const T& lower, const T& value, con /// Declare a std::vector and also the swig %template in unison #define DECL_VEC_FOR_SWIG(TypeName, MemberType) namespace std { %template(TypeName) vector; } typedef std::vector TypeName; #define DECL_MAP_FOR_SWIG(TypeName, KeyType, ValueType) namespace std { %template(TypeName) map; } typedef std::map TypeName; +#define DECL_SPTR_FOR_SWIG(TypeName, MemberType) namespace std { %template(TypeName) std::shared_ptr; } typedef std::shared_ptr TypeName; #else /// Declare a std::vector but no swig %template #define DECL_VEC_FOR_SWIG(TypeName, MemberType) typedef std::vector TypeName; #define DECL_MAP_FOR_SWIG(TypeName, KeyType, ValueType) typedef std::map TypeName; +#define DECL_SPTR_FOR_SWIG(TypeName, MemberType) typedef std::shared_ptr TypeName; #endif - #endif // MACROS_H diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 3fcf32bb2c..f469958e2e 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -176,6 +176,212 @@ void BOARD::Move( const wxPoint& aMoveVector ) // overload } +TRACKS BOARD::TracksInNet( int aNetCode ) +{ + TRACKS ret; + + INSPECTOR_FUNC inspector = [aNetCode,&ret] ( EDA_ITEM* item, void* testData ) + { + TRACK* t = (TRACK*) item; + + if( t->GetNetCode() == aNetCode ) + ret.push_back( t ); + + return SEARCH_CONTINUE; + }; + + // visit this BOARD's TRACKs and VIAs with above TRACK INSPECTOR which + // appends all in aNetCode to ret. + Visit( inspector, NULL, GENERAL_COLLECTOR::Tracks ); + + return ret; +} + + +/** + * Function removeTrack + * removes aOneToRemove from aList, which is a non-owning std::vector + */ +static void removeTrack( TRACKS* aList, TRACK* aOneToRemove ) +{ + aList->erase( std::remove( aList->begin(), aList->end(), aOneToRemove ), aList->end() ); +} + + +static void otherEnd( const TRACK& aTrack, const wxPoint& aNotThisEnd, wxPoint* aOtherEnd ) +{ + if( aTrack.GetStart() == aNotThisEnd ) + { + *aOtherEnd = aTrack.GetEnd(); + } + else + { + wxASSERT( aTrack.GetEnd() == aNotThisEnd ); + *aOtherEnd = aTrack.GetStart(); + } +} + + +/** + * Function find_vias_and_tracks_at + * collects TRACKs and VIAs at aPos and returns true if any were VIAs. + */ +static int find_vias_and_tracks_at( TRACKS& at_next, TRACKS& in_net, LSET& lset, const wxPoint& next ) +{ + // first find all vias (in this net) at 'next' location, and expand LSET with each + for( TRACKS::iterator it = in_net.begin(); it != in_net.end(); ) + { + TRACK* t = *it; + + if( t->Type() == PCB_VIA_T && (t->GetLayerSet() & lset).any() && + ( t->GetStart() == next || t->GetEnd() == next ) ) + { + lset |= t->GetLayerSet(); + at_next.push_back( t ); + in_net.erase( it ); + } + else + ++it; + } + + int track_count = 0; + + // with expanded lset, find all tracks with an end on any of the layers in lset + for( TRACKS::iterator it = in_net.begin(); it != in_net.end(); ) + { + TRACK* t = *it; + + if( (t->GetLayerSet() & lset).any() && + ( t->GetStart() == next || t->GetEnd() == next ) ) + { + at_next.push_back( t ); + in_net.erase( it ); + ++track_count; + } + else + ++it; + } + + return track_count; +} + + +/** + * Function checkConnectedTo + * returns if aTracksInNet contains a copper pathway to aGoal when starting with + * aFirstTrack. aFirstTrack should have one end situated on aStart, and the + * traversal testing begins from the other end of aFirstTrack. + *

+ * The function throws an exception instead of returning bool so that detailed + * information can be provided about a possible failure in the track layout. + * + * @throw IO_ERROR - if points are not connected, with text saying why. + */ +static void checkConnectedTo( BOARD* aBoard, TRACKS* aList, const TRACKS& aTracksInNet, + const wxPoint& aGoal, const wxPoint& aStart, TRACK* aFirstTrack ) +{ + TRACKS in_net = aTracksInNet; // copy source list so the copy can be modified + wxPoint next; + + otherEnd( *aFirstTrack, aStart, &next ); + + aList->push_back( aFirstTrack ); + removeTrack( &in_net, aFirstTrack ); + + LSET lset( aFirstTrack->GetLayer() ); + + while( in_net.size() ) + { + if( next == aGoal ) + return; // success + + if( aBoard->GetPad( next, lset ) ) + { + std::string m = StrPrintf( + "there is an intervening pad at:(xy %s) between start:(xy %s) and goal:(xy %s)", + BOARD_ITEM::FormatInternalUnits( next ).c_str(), + BOARD_ITEM::FormatInternalUnits( aStart ).c_str(), + BOARD_ITEM::FormatInternalUnits( aGoal ).c_str() + ); + THROW_IO_ERROR( m ); + } + + int track_count = find_vias_and_tracks_at( *aList, in_net, lset, next ); + + if( track_count != 1 ) + { + std::string m = StrPrintf( + "found %d tracks intersecting at (xy %s), exactly 2 would be acceptable.", + track_count, + BOARD_ITEM::FormatInternalUnits( next ).c_str() + ); + THROW_IO_ERROR( m ); + } + + // reduce lset down to the layer that the only track at 'next' is on. + lset = aList->back()->GetLayerSet(); + + otherEnd( *aList->back(), next, &next ); + } + + std::string m = StrPrintf( + "not enough tracks connecting start:(xy %s) and goal:(xy %s).", + BOARD_ITEM::FormatInternalUnits( aStart ).c_str(), + BOARD_ITEM::FormatInternalUnits( aGoal ).c_str() + ); + THROW_IO_ERROR( m ); +} + + +TRACKS BOARD::TracksInNetBetweenPoints( const wxPoint& aStartPos, const wxPoint& aEndPos, int aNetCode ) +{ + TRACKS in_between_pts; + TRACKS on_start_pad; + TRACKS in_net = TracksInNet( aNetCode ); // a small subset of TRACKs and VIAs + + for( auto t : in_net ) + { + if( t->Type() == PCB_TRACE_T && ( t->GetStart() == aStartPos || t->GetEnd() == aStartPos ) ) + on_start_pad.push_back( t ); + } + + IO_ERROR last_error; + + for( auto t : on_start_pad ) + { + // checkConnectedTo() fills in_between_pts on every attempt. For failures + // this set needs to be cleared. + in_between_pts.clear(); + + try + { + checkConnectedTo( this, &in_between_pts, in_net, aEndPos, aStartPos, t ); + } + catch( const IO_ERROR& ioe ) // means not connected + { + last_error = ioe; + continue; // keep trying, there may be other paths leaving from aStartPos + } + + // success, no exception means a valid connection, + // return this set of TRACKS without throwing. + return in_between_pts; + } + + if( on_start_pad.size() == 1 ) + throw last_error; + else + { + std::string m = StrPrintf( + "no path connecting start:(xy %s) with end:(xy %s)", + BOARD_ITEM::FormatInternalUnits( aStartPos ).c_str(), + BOARD_ITEM::FormatInternalUnits( aEndPos ).c_str() + ); + THROW_IO_ERROR( m ); + } +} + + void BOARD::chainMarkedSegments( wxPoint aPosition, const LSET& aLayerSet, TRACKS* aList ) { LSET layer_set = aLayerSet; diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 452daf6516..cc827e46ca 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -1328,11 +1328,39 @@ public: * set (the user is responsible of flag clearing). False * for no reorder : useful when we want just calculate the * track length in this case, flags are reset - * @return TRACK* The first in the chain of interesting segments. + * @return TRACK* - The first in the chain of interesting segments. */ TRACK* MarkTrace( TRACK* aTrace, int* aCount, double* aTraceLength, double* aInPackageLength, bool aReorder ); + /** + * Function TrackInNet + * collects all the TRACKs and VIAs that are members of a net given by aNetCode. + * Used from python. + * @param aList is a non-owning container that is appended to with the TRACKs and VIAs, + * and is not initiallly cleared. + * @param aNetCode gives the id of the net. + * @return TRACKS - which are in the net identified by @a aNetCode. + */ + TRACKS TracksInNet( int aNetCode ); + + /** + * Function TrackInNetBetweenPoints + * collects all the TRACKs and VIAs that are members of a net given by aNetCode and that + * make up a path between two end points. The end points must be carefully chosen, + * and are typically the locations of two neighboring pads. The function fails if there + * is an intervening pad or a 3 way intersection at a track or via. + * Used from python. + * @param aStartPos must correspond to a point on the BOARD which has a TRACK end or start, + * typically the location of either a via or pad. + * @param aEndPos must correspond to a point on the BOARD which has a TRACK end or start, + * typically the location of either a via or pad. + * @param aNetCode gives the id of the net. + * @return TRACKS - non empty if success, empty if your aStartPos or aEndPos are bad or + * the net is interrupted along the way by an intervening D_PAD or a 3 way path. + */ + TRACKS TracksInNetBetweenPoints( const wxPoint& aStartPos, const wxPoint& aEndPos, int aNetCode ); + /** * Function GetFootprint * get a footprint by its bounding rectangle at \a aPosition on \a aLayer. diff --git a/pcbnew/class_netclass.h b/pcbnew/class_netclass.h index 3a4c206601..5f86f9be57 100644 --- a/pcbnew/class_netclass.h +++ b/pcbnew/class_netclass.h @@ -31,15 +31,11 @@ #ifndef CLASS_NETCLASS_H #define CLASS_NETCLASS_H -#include -#include #include -#include - -#include - +#include #include +#include class LINE_READER; @@ -209,11 +205,7 @@ public: }; -typedef std::shared_ptr NETCLASSPTR; -#ifdef SWIG -%shared_ptr( NETCLASSPTR ); -#endif - +DECL_SPTR_FOR_SWIG( NETCLASSPTR, NETCLASS ) DECL_MAP_FOR_SWIG( NETCLASS_MAP, wxString, NETCLASSPTR ); diff --git a/pcbnew/class_netinfo.h b/pcbnew/class_netinfo.h index e179fb694a..ae4af1bc17 100644 --- a/pcbnew/class_netinfo.h +++ b/pcbnew/class_netinfo.h @@ -142,6 +142,15 @@ private: public: + D_PADS& Pads() { return m_PadInNetList; } + + /** + * Function GetNodesCount + * @return int - number of pad nodes in the net + */ + int GetNodesCount() const { return m_PadInNetList.size(); } + + D_PADS m_PadInNetList; ///< List of pads connected to this net unsigned m_RatsnestStartIdx; /* Starting point of ratsnests of this @@ -302,12 +311,6 @@ public: void SetNetCode( int aNetCode ) { m_NetCode = aNetCode; } - /** - * Function GetNodesCount - * @return int - number of nodes in the net - */ - int GetNodesCount() const { return m_PadInNetList.size(); } - /** * Function GetNetname * @return const wxString&, a reference to the full netname @@ -552,7 +555,10 @@ public: const D_PADS& GetPads() const { return m_PadsFullList; } /// Return the name map, at least for python. - const NETNAMES_MAP& GetNets() const { return m_netNames; } + const NETNAMES_MAP& NetsByName() const { return m_netNames; } + + /// Return the netcode map, at least for python. + const NETCODES_MAP& NetsByNetcode() const { return m_netCodes; } /** * Function GetPad