mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-18 22:11:40 +00:00
Removed legacy connectivity/ratsnest algorithm, replaced with the new one. No legacy autorouting for the moment
This commit is contained in:
parent
9ad886344b
commit
3cba1007eb
include
pcbnew
CMakeLists.txtblock.cppboard_netlist_updater.cppclass_board.cppclass_board.hclass_board_connected_item.hclass_netinfo.hclass_netinfo_item.cppclass_netinfolist.cppclean.cppconnect.cppconnect.hconnectivity.cppconnectivity.hconnectivity_algo.h
dialogs
drag.hdragsegm.cppdrc.cppedit.cppeditrack-part2.cppeditrack.cppfiles.cppglobaleditpad.cpphighlight.cppkicad_plugin.cppmodules.cppmove-drag_pads.cpppcbframe.cppratsnest.cppratsnest_data.cppratsnest_data.hratsnest_viewitem.cpprouter
specctra_import.cpptool_pcb.cpptr_modif.cppzones_by_polygon_fill_functions.cpp@ -481,7 +481,7 @@ public:
|
||||
* It shows the connections from a pad to the nearest connected pad
|
||||
* @param aModule = module to consider.
|
||||
*/
|
||||
void build_ratsnest_module( MODULE* aModule );
|
||||
void build_ratsnest_module( MODULE *mod, wxPoint aMoveVector );
|
||||
|
||||
/**
|
||||
* Function TraceModuleRatsNest
|
||||
@ -526,7 +526,8 @@ public:
|
||||
* When aInit = false, aItemRef is not used (can be NULL)
|
||||
*/
|
||||
void BuildAirWiresTargetsList( BOARD_CONNECTED_ITEM* aItemRef,
|
||||
const wxPoint& aPosition, bool aInit );
|
||||
const wxPoint& aPosition,
|
||||
int aNet );
|
||||
|
||||
/**
|
||||
* Function TestForActiveLinksInRatsnest
|
||||
|
@ -184,25 +184,13 @@ set( PCBNEW_EXPORTERS
|
||||
exporters/gendrill_gerber_writer.cpp
|
||||
)
|
||||
|
||||
set( PCBNEW_AUTOROUTER_SRCS
|
||||
autorouter/rect_placement/rect_placement.cpp
|
||||
autorouter/move_and_route_event_functions.cpp
|
||||
autorouter/auto_place_footprints.cpp
|
||||
autorouter/autorout.cpp
|
||||
autorouter/routing_matrix.cpp
|
||||
autorouter/dist.cpp
|
||||
autorouter/queue.cpp
|
||||
autorouter/spread_footprints.cpp
|
||||
autorouter/solve.cpp
|
||||
autorouter/graphpcb.cpp
|
||||
autorouter/work.cpp
|
||||
)
|
||||
|
||||
set( PCBNEW_MICROWAVE_SRCS
|
||||
microwave/microwave_inductor.cpp
|
||||
)
|
||||
|
||||
set( PCBNEW_CLASS_SRCS
|
||||
autorouter/rect_placement/rect_placement.cpp
|
||||
autorouter/spread_footprints.cpp
|
||||
board_commit.cpp
|
||||
tool_modview.cpp
|
||||
modview_frame.cpp
|
||||
@ -299,7 +287,6 @@ set( PCBNEW_CLASS_SRCS
|
||||
zone_filling_algorithm.cpp
|
||||
zones_functions_for_undo_redo.cpp
|
||||
zones_polygons_insulated_copper_islands.cpp
|
||||
zones_polygons_test_connections.cpp
|
||||
zones_test_and_combine_areas.cpp
|
||||
class_footprint_wizard.cpp
|
||||
class_action_plugin.cpp
|
||||
@ -334,7 +321,6 @@ set( PCBNEW_CLASS_SRCS
|
||||
)
|
||||
|
||||
set( PCBNEW_SRCS
|
||||
${PCBNEW_AUTOROUTER_SRCS}
|
||||
${PCBNEW_MICROWAVE_SRCS}
|
||||
${PCBNEW_CLASS_SRCS}
|
||||
${PCBNEW_DIALOGS}
|
||||
@ -622,7 +608,6 @@ add_library( pcbnew_kiface MODULE
|
||||
${PCBNEW_COMMON_SRCS}
|
||||
${PCBNEW_SCRIPTING_SRCS}
|
||||
)
|
||||
|
||||
set_target_properties( pcbnew_kiface PROPERTIES
|
||||
# Decorate OUTPUT_NAME with PREFIX and SUFFIX, creating something like
|
||||
# _pcbnew.so, _pcbnew.dll, or _pcbnew.kiface
|
||||
|
@ -50,6 +50,8 @@
|
||||
#include <pcbnew.h>
|
||||
#include <protos.h>
|
||||
|
||||
#include <connectivity.h>
|
||||
|
||||
#define BLOCK_OUTLINE_COLOR YELLOW
|
||||
|
||||
/**
|
||||
@ -536,6 +538,7 @@ void PCB_EDIT_FRAME::Block_Delete()
|
||||
{
|
||||
BOARD_ITEM* item = (BOARD_ITEM*) itemsList->GetPickedItem( ii );
|
||||
itemsList->SetPickedItemStatus( UR_DELETED, ii );
|
||||
GetBoard()->GetConnectivity()->Remove( item );
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
@ -599,6 +602,7 @@ void PCB_EDIT_FRAME::Block_Rotate()
|
||||
{
|
||||
BOARD_ITEM* item = (BOARD_ITEM*) itemsList->GetPickedItem( ii );
|
||||
wxASSERT( item );
|
||||
|
||||
itemsList->SetPickedItemStatus( UR_CHANGED, ii );
|
||||
|
||||
switch( item->Type() )
|
||||
@ -642,6 +646,7 @@ void PCB_EDIT_FRAME::Block_Rotate()
|
||||
BOARD_ITEM* item = (BOARD_ITEM*) itemsList->GetPickedItem( ii );
|
||||
wxASSERT( item );
|
||||
item->Rotate( centre, rotAngle );
|
||||
GetBoard()->GetConnectivity()->Update( item );
|
||||
}
|
||||
|
||||
Compile_Ratsnest( NULL, true );
|
||||
@ -667,6 +672,7 @@ void PCB_EDIT_FRAME::Block_Flip()
|
||||
wxASSERT( item );
|
||||
itemsList->SetPickedItemStatus( UR_FLIPPED, ii );
|
||||
item->Flip( center );
|
||||
GetBoard()->GetConnectivity()->Update( item );
|
||||
|
||||
// If a connected item is flipped, the ratsnest is no more OK
|
||||
switch( item->Type() )
|
||||
@ -721,6 +727,7 @@ void PCB_EDIT_FRAME::Block_Move()
|
||||
BOARD_ITEM* item = (BOARD_ITEM*) itemsList->GetPickedItem( ii );
|
||||
itemsList->SetPickedItemStatus( UR_MOVED, ii );
|
||||
item->Move( MoveVector );
|
||||
GetBoard()->GetConnectivity()->Update( item );
|
||||
item->ClearFlags( IS_MOVED );
|
||||
|
||||
switch( item->Type() )
|
||||
|
@ -596,7 +596,7 @@ bool BOARD_NETLIST_UPDATER::testConnectivity( NETLIST& aNetlist )
|
||||
if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
|
||||
continue;
|
||||
|
||||
int nc = zone->GetNet()->GetNodesCount();
|
||||
int nc = m_board->GetConnectivity()->GetPadCount( zone->GetNetCode() );
|
||||
|
||||
if( nc == 0 )
|
||||
{
|
||||
@ -668,7 +668,6 @@ bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist )
|
||||
if( !m_isDryRun )
|
||||
{
|
||||
m_commit.Push( _( "Update netlist" ) );
|
||||
m_frame->Compile_Ratsnest( NULL, false );
|
||||
m_board->GetConnectivity()->Build( m_board );
|
||||
testConnectivity( aNetlist );
|
||||
}
|
||||
|
@ -120,9 +120,6 @@ BOARD::~BOARD()
|
||||
Delete( area_to_remove );
|
||||
}
|
||||
|
||||
m_FullRatsnest.clear();
|
||||
m_LocalRatsnest.clear();
|
||||
|
||||
DeleteMARKERs();
|
||||
DeleteZONEOutlines();
|
||||
|
||||
@ -1157,20 +1154,8 @@ void BOARD::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
||||
txt.Printf( wxT( "%d" ), m_NetInfo.GetNetCount() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Nets" ), txt, RED ) );
|
||||
|
||||
/* These parameters are known only if the full ratsnest is available,
|
||||
* so, display them only if this is the case
|
||||
*/
|
||||
if( (m_Status_Pcb & NET_CODES_OK) )
|
||||
{
|
||||
txt.Printf( wxT( "%d" ), GetRatsnestsCount() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Links" ), txt, DARKGREEN ) );
|
||||
|
||||
txt.Printf( wxT( "%d" ), GetRatsnestsCount() - GetUnconnectedNetCount() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Connections" ), txt, DARKGREEN ) );
|
||||
|
||||
txt.Printf( wxT( "%d" ), GetUnconnectedNetCount() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Unconnected" ), txt, BLUE ) );
|
||||
}
|
||||
txt.Printf( wxT( "%d" ), GetConnectivity()->GetUnconnectedCount() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Unconnected" ), txt, BLUE ) );
|
||||
}
|
||||
|
||||
|
||||
@ -1428,10 +1413,14 @@ MODULE* BOARD::FindModule( const wxString& aRefOrTimeStamp, bool aSearchByTimeSt
|
||||
// Sort nets by decreasing pad count. For same pad count, sort by alphabetic names
|
||||
static bool sortNetsByNodes( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
|
||||
{
|
||||
if( b->GetNodesCount() == a->GetNodesCount() )
|
||||
return a->GetNetname() < b->GetNetname();
|
||||
auto connectivity = a->GetParent()->GetConnectivity();
|
||||
int countA = connectivity->GetPadCount( a->GetNet() );
|
||||
int countB = connectivity->GetPadCount( b->GetNet() );
|
||||
|
||||
return b->GetNodesCount() < a->GetNodesCount();
|
||||
if( countA == countB )
|
||||
return a->GetNetname() < b->GetNetname();
|
||||
else
|
||||
return countB < countA;
|
||||
}
|
||||
|
||||
// Sort nets by alphabetic names
|
||||
@ -1619,10 +1608,10 @@ D_PAD* BOARD::GetPad( TRACK* aTrace, ENDPOINT_T aEndPoint )
|
||||
|
||||
D_PAD* BOARD::GetPadFast( const wxPoint& aPosition, LSET aLayerSet )
|
||||
{
|
||||
for( unsigned i=0; i<GetPadCount(); ++i )
|
||||
for( auto mod : Modules() )
|
||||
{
|
||||
D_PAD* pad = m_NetInfo.GetPad(i);
|
||||
|
||||
for ( auto pad : mod->PadsIter() )
|
||||
{
|
||||
if( pad->GetPosition() != aPosition )
|
||||
continue;
|
||||
|
||||
@ -1630,8 +1619,9 @@ D_PAD* BOARD::GetPadFast( const wxPoint& aPosition, LSET aLayerSet )
|
||||
if( ( pad->GetLayerSet() & aLayerSet ).any() )
|
||||
return pad;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -1744,29 +1734,23 @@ bool sortPadsByXthenYCoord( D_PAD* const & ref, D_PAD* const & comp )
|
||||
|
||||
void BOARD::GetSortedPadListByXthenYCoord( std::vector<D_PAD*>& aVector, int aNetCode )
|
||||
{
|
||||
if( aNetCode < 0 )
|
||||
for ( auto mod : Modules() )
|
||||
{
|
||||
aVector.insert( aVector.end(), m_NetInfo.m_PadsFullList.begin(),
|
||||
m_NetInfo.m_PadsFullList.end() );
|
||||
}
|
||||
else
|
||||
{
|
||||
const NETINFO_ITEM* net = m_NetInfo.GetNetItem( aNetCode );
|
||||
if( net )
|
||||
for ( auto pad : mod->PadsIter( ) )
|
||||
{
|
||||
aVector.insert( aVector.end(), net->m_PadInNetList.begin(),
|
||||
net->m_PadInNetList.end() );
|
||||
if( aNetCode < 0 || pad->GetNetCode() == aNetCode )
|
||||
{
|
||||
aVector.push_back( pad );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
|
||||
std::sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
|
||||
}
|
||||
|
||||
|
||||
void BOARD::PadDelete( D_PAD* aPad )
|
||||
{
|
||||
m_NetInfo.DeletePad( aPad );
|
||||
|
||||
aPad->DeleteStructure();
|
||||
}
|
||||
|
||||
@ -2830,7 +2814,7 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets,
|
||||
if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
|
||||
continue;
|
||||
|
||||
if( zone->GetNet()->GetNodesCount() == 0 )
|
||||
if( GetConnectivity()->GetPadCount( zone->GetNetCode() ) == 0 )
|
||||
{
|
||||
msg.Printf( _( "Copper zone (net name '%s'): net has no pads connected." ),
|
||||
GetChars( zone->GetNet()->GetNetname() ) );
|
||||
@ -2900,3 +2884,42 @@ bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
|
||||
{
|
||||
return m_connectivity->GetRatsnest();
|
||||
}*/
|
||||
|
||||
const std::vector<D_PAD*> BOARD::GetPads()
|
||||
{
|
||||
std::vector<D_PAD*> rv;
|
||||
for ( auto mod: Modules() )
|
||||
{
|
||||
for ( auto pad: mod->PadsIter() )
|
||||
rv.push_back ( pad );
|
||||
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
unsigned BOARD::GetPadCount() const
|
||||
{
|
||||
return m_connectivity->GetPadCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetPad
|
||||
* @return D_PAD* - at the \a aIndex
|
||||
*/
|
||||
D_PAD* BOARD::GetPad( unsigned aIndex ) const
|
||||
{
|
||||
unsigned count = 0;
|
||||
for ( MODULE *mod = m_Modules; mod ; mod = mod->Next() ) // FIXME: const DLIST_ITERATOR
|
||||
{
|
||||
for ( D_PAD *pad = mod->Pads(); pad; pad = pad->Next() )
|
||||
{
|
||||
if ( count == aIndex )
|
||||
return pad;
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -257,15 +257,11 @@ public:
|
||||
DLIST_ITERATOR_WRAPPER<MODULE> Modules() { return DLIST_ITERATOR_WRAPPER<MODULE>(m_Modules); }
|
||||
DLIST_ITERATOR_WRAPPER<BOARD_ITEM> Drawings() { return DLIST_ITERATOR_WRAPPER<BOARD_ITEM>(m_Drawings); }
|
||||
|
||||
|
||||
|
||||
// will be deprecated as soon as append board functionality is fixed
|
||||
DLIST<BOARD_ITEM>& DrawingsList() { return m_Drawings; }
|
||||
|
||||
/// Ratsnest list for the BOARD
|
||||
std::vector<RATSNEST_ITEM> m_FullRatsnest;
|
||||
|
||||
/// Ratsnest list relative to a given footprint (used while moving a footprint).
|
||||
std::vector<RATSNEST_ITEM> m_LocalRatsnest;
|
||||
|
||||
/// zone contour currently in progress
|
||||
ZONE_CONTAINER* m_CurrentZoneContour;
|
||||
|
||||
@ -712,15 +708,6 @@ public:
|
||||
/** Calculate the zone segment count */
|
||||
int GetNumSegmZone() const;
|
||||
|
||||
/**
|
||||
* Function GetNumRatsnests
|
||||
* @return int - The number of rats
|
||||
*/
|
||||
unsigned GetRatsnestsCount() const
|
||||
{
|
||||
return m_FullRatsnest.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetNodesCount
|
||||
* @return the number of pads members of nets (i.e. with netcode > 0)
|
||||
@ -753,19 +740,13 @@ public:
|
||||
* Function GetPadCount
|
||||
* @return the number of pads in board
|
||||
*/
|
||||
unsigned GetPadCount() const
|
||||
{
|
||||
return m_NetInfo.GetPadCount();
|
||||
}
|
||||
unsigned GetPadCount() const;
|
||||
|
||||
/**
|
||||
* Function GetPad
|
||||
* @return D_PAD* - at the \a aIndex from m_NetInfo
|
||||
* @return D_PAD* - at the \a aIndex
|
||||
*/
|
||||
D_PAD* GetPad( unsigned aIndex ) const
|
||||
{
|
||||
return m_NetInfo.GetPad( aIndex );
|
||||
}
|
||||
D_PAD* GetPad( unsigned aIndex ) const;
|
||||
|
||||
/**
|
||||
* Function GetPads
|
||||
@ -774,7 +755,7 @@ public:
|
||||
* ownership of the respective PADs.
|
||||
* @return D_PADS - a full list of pads
|
||||
*/
|
||||
const D_PADS& GetPads() { return m_NetInfo.m_PadsFullList; }
|
||||
const std::vector<D_PAD*> GetPads();
|
||||
|
||||
void BuildListOfNets()
|
||||
{
|
||||
@ -1192,14 +1173,6 @@ public:
|
||||
int Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_Examine,
|
||||
bool aCreate_Markers );
|
||||
|
||||
/****** function relative to ratsnest calculations: */
|
||||
|
||||
/**
|
||||
* Function Test_Connection_To_Copper_Areas
|
||||
* init .m_ZoneSubnet parameter in tracks and pads according to the connections to areas found
|
||||
* @param aNetcode = netcode to analyze. if -1, analyze all nets
|
||||
*/
|
||||
void Test_Connections_To_Copper_Areas( int aNetcode = -1 );
|
||||
|
||||
/**
|
||||
* Function GetViaByPosition
|
||||
|
@ -48,15 +48,7 @@ class CN_BOARD_ITEM_DATA;
|
||||
*/
|
||||
class BOARD_CONNECTED_ITEM : public BOARD_ITEM
|
||||
{
|
||||
friend class CONNECTIONS;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
// These 2 members are used for temporary storage during connections calculations:
|
||||
std::vector<TRACK*> m_TracksConnected; // list of other tracks connected to me
|
||||
std::vector<D_PAD*> m_PadsConnected; // list of other pads connected to me
|
||||
|
||||
BOARD_CONNECTED_ITEM( BOARD_ITEM* aParent, KICAD_T idtype );
|
||||
|
||||
// Do not create a copy constructor & operator=.
|
||||
|
@ -60,61 +60,6 @@ class MSG_PANEL_ITEM;
|
||||
#define CH_ACTIF 8 /* Not routed. */
|
||||
#define LOCAL_RATSNEST_ITEM 0x8000 /* Line between two pads of a single module. */
|
||||
|
||||
|
||||
/**
|
||||
* Class RATSNEST_ITEM
|
||||
* describes a ratsnest line: a straight line connecting 2 pads
|
||||
*/
|
||||
class RATSNEST_ITEM
|
||||
{
|
||||
private:
|
||||
int m_NetCode; // netcode ( = 1.. n , 0 is the value used for not connected items)
|
||||
|
||||
public:
|
||||
int m_Status; // State: see previous defines (CH_ ...)
|
||||
D_PAD* m_PadStart; // pointer to the starting pad
|
||||
D_PAD* m_PadEnd; // pointer to ending pad
|
||||
int m_Length; // length of the line (used in some calculations)
|
||||
|
||||
RATSNEST_ITEM();
|
||||
|
||||
/**
|
||||
* Function GetNet
|
||||
* @return int - the net code.
|
||||
*/
|
||||
int GetNet() const
|
||||
{
|
||||
return m_NetCode;
|
||||
}
|
||||
|
||||
void SetNet( int aNetCode )
|
||||
{
|
||||
m_NetCode = aNetCode;
|
||||
}
|
||||
|
||||
bool IsVisible()
|
||||
{
|
||||
return (m_Status & CH_VISIBLE) != 0;
|
||||
}
|
||||
|
||||
bool IsActive()
|
||||
{
|
||||
return (m_Status & CH_ACTIF) != 0;
|
||||
}
|
||||
|
||||
bool IsLocal()
|
||||
{
|
||||
return (m_Status & LOCAL_RATSNEST_ITEM) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Draw
|
||||
*/
|
||||
void Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE aDrawMode,
|
||||
const wxPoint& offset );
|
||||
};
|
||||
|
||||
|
||||
DECL_VEC_FOR_SWIG( D_PADS, D_PAD* )
|
||||
|
||||
/**
|
||||
@ -142,25 +87,6 @@ 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
|
||||
* net (included) in a general buffer of
|
||||
* ratsnest (a vector<RATSNEST_ITEM*>
|
||||
* buffer) */
|
||||
|
||||
unsigned m_RatsnestEndIdx; // Ending point of ratsnests of this net
|
||||
// (excluded) in this buffer
|
||||
|
||||
NETINFO_ITEM( BOARD* aParent, const wxString& aNetName = wxEmptyString, int aNetCode = -1 );
|
||||
~NETINFO_ITEM();
|
||||
|
||||
@ -340,12 +266,6 @@ public:
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
m_PadInNetList.clear();
|
||||
|
||||
m_RatsnestStartIdx = 0; // Starting point of ratsnests of this net in a
|
||||
// general buffer of ratsnest
|
||||
m_RatsnestEndIdx = 0; // Ending point of ratsnests of this net
|
||||
|
||||
SetClass( NETCLASSPTR() );
|
||||
}
|
||||
|
||||
@ -540,15 +460,6 @@ public:
|
||||
* Function GetPadCount
|
||||
* @return the number of pads in board
|
||||
*/
|
||||
unsigned GetPadCount() const { return m_PadsFullList.size(); }
|
||||
|
||||
/**
|
||||
* Function GetPads
|
||||
* returns a list of all the pads (so long as buildPadsFullList() has
|
||||
* been recently called). Returned list contains non-owning pointers.
|
||||
* @return D_PADS& - a full list of pads
|
||||
*/
|
||||
const D_PADS& GetPads() const { return m_PadsFullList; }
|
||||
|
||||
/// Return the name map, at least for python.
|
||||
const NETNAMES_MAP& NetsByName() const { return m_netNames; }
|
||||
@ -556,14 +467,6 @@ public:
|
||||
/// Return the netcode map, at least for python.
|
||||
const NETCODES_MAP& NetsByNetcode() const { return m_netCodes; }
|
||||
|
||||
/**
|
||||
* Function GetPad
|
||||
* @return D_PAD* - the pad from m_PadsFullList or nullptr if bad @a aIdx
|
||||
*/
|
||||
D_PAD* GetPad( unsigned aIdx ) const;
|
||||
|
||||
bool DeletePad( D_PAD* aPad );
|
||||
|
||||
///> Constant that holds the "unconnected net" number (typically 0)
|
||||
///> all items "connected" to this net are actually not connected items
|
||||
static const int UNCONNECTED;
|
||||
@ -680,9 +583,6 @@ private:
|
||||
NETNAMES_MAP m_netNames; ///< map of <wxString, NETINFO_ITEM*>, is NETINFO_ITEM owner
|
||||
NETCODES_MAP m_netCodes; ///< map of <int, NETINFO_ITEM*> is NOT owner
|
||||
|
||||
D_PADS m_PadsFullList; ///< contains all pads, sorted by pad's netname.
|
||||
///< can be used in ratsnest calculations.
|
||||
|
||||
int m_newNetCode; ///< possible value for new net code assignment
|
||||
};
|
||||
|
||||
@ -695,17 +595,13 @@ private:
|
||||
#define START_ON_TRACK 0x40
|
||||
#define END_ON_TRACK 0x80
|
||||
|
||||
|
||||
/* Status bit (OR'ed bits) for class BOARD member .m_Status_Pcb */
|
||||
enum StatusPcbFlags {
|
||||
LISTE_PAD_OK = 1, /* Pad list is Ok */
|
||||
LISTE_RATSNEST_ITEM_OK = 2, /* General Ratsnest is Ok */
|
||||
|
||||
RATSNEST_ITEM_LOCAL_OK = 4, /* current MODULE ratsnest is Ok */
|
||||
CONNEXION_OK = 8, /* List of connections exists. */
|
||||
NET_CODES_OK = 0x10, /* Bit indicating that Netcode is OK,
|
||||
* do not change net name. */
|
||||
DO_NOT_SHOW_GENERAL_RASTNEST = 0x20 /* Do not display the general
|
||||
* ratsnest (used in module moves) */
|
||||
};
|
||||
|
||||
|
||||
#endif // CLASS_NETINFO_
|
||||
|
@ -54,10 +54,6 @@ NETINFO_ITEM::NETINFO_ITEM( BOARD* aParent, const wxString& aNetName, int aNetCo
|
||||
m_NetCode( aNetCode ), m_Netname( aNetName ), m_ShortNetname( m_Netname.AfterLast( '/' ) )
|
||||
{
|
||||
m_parent = aParent;
|
||||
m_RatsnestStartIdx = 0; // Starting point of ratsnests of this net in a
|
||||
// general buffer of ratsnest
|
||||
m_RatsnestEndIdx = 0; // Ending point of ratsnests of this net
|
||||
|
||||
m_NetClassName = NETCLASS::Default;
|
||||
}
|
||||
|
||||
@ -144,39 +140,4 @@ void NETINFO_ITEM::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
||||
// Displays the net length of internal ICs connections (wires inside ICs):
|
||||
txt = ::LengthDoubleToString( lengthPadToDie );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "In Package" ), txt, RED ) );
|
||||
}
|
||||
|
||||
|
||||
/***********************/
|
||||
/* class RATSNEST_ITEM */
|
||||
/***********************/
|
||||
|
||||
RATSNEST_ITEM::RATSNEST_ITEM()
|
||||
{
|
||||
m_NetCode = 0; // netcode ( = 1.. n , 0 is the value used for not
|
||||
// connected items)
|
||||
m_Status = 0; // state
|
||||
m_PadStart = NULL; // pointer to the starting pad
|
||||
m_PadEnd = NULL; // pointer to ending pad
|
||||
m_Length = 0; // length of the line (temporary used in some
|
||||
// calculations)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function Draw
|
||||
* Draws a line (a ratsnest) from the starting pad to the ending pad
|
||||
*/
|
||||
void RATSNEST_ITEM::Draw( EDA_DRAW_PANEL* panel,
|
||||
wxDC* DC,
|
||||
GR_DRAWMODE aDrawMode,
|
||||
const wxPoint& aOffset )
|
||||
{
|
||||
GRSetDrawMode( DC, aDrawMode );
|
||||
|
||||
COLOR4D color = g_ColorsSettings.GetItemColor( LAYER_RATSNEST );
|
||||
|
||||
GRLine( panel->GetClipBox(), DC,
|
||||
m_PadStart->GetPosition() - aOffset,
|
||||
m_PadEnd->GetPosition() - aOffset, 0, color );
|
||||
}
|
||||
}
|
@ -62,7 +62,6 @@ void NETINFO_LIST::clear()
|
||||
for( it = m_netNames.begin(), itEnd = m_netNames.end(); it != itEnd; ++it )
|
||||
delete it->second;
|
||||
|
||||
m_PadsFullList.clear();
|
||||
m_netNames.clear();
|
||||
m_netCodes.clear();
|
||||
m_newNetCode = 0;
|
||||
@ -143,32 +142,6 @@ void NETINFO_LIST::AppendNet( NETINFO_ITEM* aNewElement )
|
||||
}
|
||||
|
||||
|
||||
D_PAD* NETINFO_LIST::GetPad( unsigned aIdx ) const
|
||||
{
|
||||
if( aIdx < m_PadsFullList.size() )
|
||||
return m_PadsFullList[aIdx];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool NETINFO_LIST::DeletePad( D_PAD* aPad )
|
||||
{
|
||||
std::vector<D_PAD*>::iterator it = m_PadsFullList.begin();
|
||||
std::vector<D_PAD*>::iterator end = m_PadsFullList.end();
|
||||
|
||||
for( ; it != end; ++it )
|
||||
{
|
||||
if( *it == aPad )
|
||||
{
|
||||
m_PadsFullList.erase( it );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* sort function, to sort pad list by netnames
|
||||
* this is a case sensitive sort.
|
||||
* DO NOT change it because NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname )
|
||||
@ -199,47 +172,15 @@ void NETINFO_LIST::buildListOfNets()
|
||||
D_PAD* pad;
|
||||
int nodes_count = 0;
|
||||
|
||||
// Build the PAD list, sorted by net
|
||||
buildPadsFullList();
|
||||
|
||||
// Restore the initial state of NETINFO_ITEMs
|
||||
for( NETINFO_LIST::iterator net( begin() ), netEnd( end() ); net != netEnd; ++net )
|
||||
net->Clear();
|
||||
|
||||
// Assign pads to appropriate NETINFO_ITEMs
|
||||
for( unsigned ii = 0; ii < m_PadsFullList.size(); ii++ )
|
||||
{
|
||||
pad = m_PadsFullList[ii];
|
||||
|
||||
if( pad->GetNetCode() == NETINFO_LIST::UNCONNECTED ) // pad not connected
|
||||
continue;
|
||||
|
||||
if( !( pad->GetLayerSet() & LSET::AllCuMask() ).any() )
|
||||
// pad not a copper layer (happens when building complex shapes)
|
||||
continue;
|
||||
|
||||
// Add pad to the appropriate list of pads
|
||||
NETINFO_ITEM* net = pad->GetNet();
|
||||
|
||||
// it should not be possible for BOARD_CONNECTED_ITEM to return NULL as a result of GetNet()
|
||||
wxASSERT( net );
|
||||
|
||||
if( net )
|
||||
net->m_PadInNetList.push_back( pad );
|
||||
|
||||
++nodes_count;
|
||||
}
|
||||
|
||||
m_Parent->SetNodeCount( nodes_count );
|
||||
|
||||
m_Parent->SynchronizeNetsAndNetClasses( );
|
||||
|
||||
m_Parent->m_Status_Pcb |= NET_CODES_OK;
|
||||
|
||||
m_Parent->SetAreasNetCodesFromNetNames();
|
||||
}
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
void NETINFO_LIST::Show() const
|
||||
{
|
||||
@ -255,46 +196,6 @@ void NETINFO_LIST::Show() const
|
||||
#endif
|
||||
|
||||
|
||||
void NETINFO_LIST::buildPadsFullList()
|
||||
{
|
||||
/*
|
||||
* initialize:
|
||||
* m_Pads (list of pads)
|
||||
* set m_Status_Pcb = LISTE_PAD_OK;
|
||||
* also clear m_Pcb->m_FullRatsnest that could have bad data
|
||||
* (m_Pcb->m_FullRatsnest uses pointer to pads)
|
||||
* Be aware NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname )
|
||||
* when search a net by its net name does a binary search
|
||||
* and expects to have a nets list sorted by an alphabetic case sensitive sort
|
||||
* So do not change the sort function used here
|
||||
*/
|
||||
|
||||
if( m_Parent->m_Status_Pcb & LISTE_PAD_OK )
|
||||
return;
|
||||
|
||||
// empty the old list
|
||||
m_PadsFullList.clear();
|
||||
m_Parent->m_FullRatsnest.clear();
|
||||
|
||||
// Clear variables used in ratsnest computation
|
||||
for( MODULE* module = m_Parent->m_Modules; module; module = module->Next() )
|
||||
{
|
||||
for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
|
||||
{
|
||||
m_PadsFullList.push_back( pad );
|
||||
|
||||
pad->SetSubRatsnest( 0 );
|
||||
pad->SetParent( module );
|
||||
}
|
||||
}
|
||||
|
||||
// Sort pad list per net
|
||||
sort( m_PadsFullList.begin(), m_PadsFullList.end(), padlistSortByNetnames );
|
||||
|
||||
m_Parent->m_Status_Pcb = LISTE_PAD_OK;
|
||||
}
|
||||
|
||||
|
||||
int NETINFO_LIST::getFreeNetCode()
|
||||
{
|
||||
do {
|
||||
@ -373,4 +274,4 @@ NETINFO_ITEM* NETINFO_MAPPING::iterator::operator->() const
|
||||
const int NETINFO_LIST::UNCONNECTED = 0;
|
||||
const int NETINFO_LIST::ORPHANED = -1;
|
||||
|
||||
NETINFO_ITEM NETINFO_LIST::ORPHANED_ITEM = NETINFO_ITEM( NULL, wxEmptyString, NETINFO_LIST::UNCONNECTED );
|
||||
NETINFO_ITEM NETINFO_LIST::ORPHANED_ITEM = NETINFO_ITEM( NULL, wxEmptyString, NETINFO_LIST::UNCONNECTED );
|
||||
|
120
pcbnew/clean.cpp
120
pcbnew/clean.cpp
@ -35,14 +35,12 @@
|
||||
#include <pcbnew.h>
|
||||
#include <class_board.h>
|
||||
#include <class_track.h>
|
||||
#include <connect.h>
|
||||
#include <dialog_cleaning_options.h>
|
||||
#include <board_commit.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <connectivity.h>
|
||||
|
||||
// Helper class used to clean tracks and vias
|
||||
class TRACKS_CLEANER: CONNECTIONS
|
||||
class TRACKS_CLEANER
|
||||
{
|
||||
public:
|
||||
TRACKS_CLEANER( BOARD* aPcb, BOARD_COMMIT& aCommit );
|
||||
@ -69,7 +67,7 @@ private:
|
||||
* Removes redundant vias like vias at same location
|
||||
* or on pad through
|
||||
*/
|
||||
bool clean_vias();
|
||||
bool cleanupVias();
|
||||
|
||||
/**
|
||||
* Removes all the following THT vias on the same position of the
|
||||
@ -98,12 +96,6 @@ private:
|
||||
*/
|
||||
bool clean_segments();
|
||||
|
||||
/**
|
||||
* helper function
|
||||
* Rebuild list of tracks, and connected tracks
|
||||
* this info must be rebuilt when tracks are erased
|
||||
*/
|
||||
void buildTrackConnectionInfo();
|
||||
|
||||
/**
|
||||
* helper function
|
||||
@ -147,7 +139,6 @@ void PCB_EDIT_FRAME::Clean_Pcb()
|
||||
// Clear undo and redo lists to avoid inconsistencies between lists
|
||||
SetCurItem( NULL );
|
||||
commit.Push( _( "Board cleanup" ) );
|
||||
Compile_Ratsnest( NULL, true );
|
||||
}
|
||||
|
||||
m_canvas->Refresh( true );
|
||||
@ -165,13 +156,12 @@ bool TRACKS_CLEANER::CleanupBoard( bool aRemoveMisConnected,
|
||||
bool aMergeSegments,
|
||||
bool aDeleteUnconnected )
|
||||
{
|
||||
buildTrackConnectionInfo();
|
||||
|
||||
bool modified = false;
|
||||
|
||||
// delete redundant vias
|
||||
if( aCleanVias )
|
||||
modified |= clean_vias();
|
||||
modified |= cleanupVias();
|
||||
|
||||
// Remove null segments and intermediate points on aligned segments
|
||||
// If not asked, remove null segments only if remove misconnected is asked
|
||||
@ -187,7 +177,7 @@ bool TRACKS_CLEANER::CleanupBoard( bool aRemoveMisConnected,
|
||||
modified = true;
|
||||
|
||||
// Refresh track connection info
|
||||
buildTrackConnectionInfo();
|
||||
//buildTrackConnectionInfo(); FIXME: update connectivity
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,7 +185,9 @@ bool TRACKS_CLEANER::CleanupBoard( bool aRemoveMisConnected,
|
||||
if( aDeleteUnconnected )
|
||||
{
|
||||
if( modified ) // Refresh track connection info
|
||||
buildTrackConnectionInfo();
|
||||
{
|
||||
//buildTrackConnectionInfo(); FIXME: update connectivity
|
||||
}
|
||||
|
||||
if( deleteDanglingTracks() )
|
||||
{
|
||||
@ -214,101 +206,37 @@ bool TRACKS_CLEANER::CleanupBoard( bool aRemoveMisConnected,
|
||||
|
||||
|
||||
TRACKS_CLEANER::TRACKS_CLEANER( BOARD* aPcb, BOARD_COMMIT& aCommit )
|
||||
: CONNECTIONS( aPcb ), m_brd( aPcb ), m_commit( aCommit )
|
||||
: m_brd( aPcb ), m_commit( aCommit )
|
||||
{
|
||||
// Be sure pad list is up to date
|
||||
BuildPadsList();
|
||||
}
|
||||
|
||||
|
||||
void TRACKS_CLEANER::buildTrackConnectionInfo()
|
||||
{
|
||||
BuildTracksCandidatesList( m_brd->m_Track, NULL );
|
||||
|
||||
// clear flags and variables used in cleanup
|
||||
for( TRACK* track = m_brd->m_Track; track != NULL; track = track->Next() )
|
||||
{
|
||||
track->start = NULL;
|
||||
track->end = NULL;
|
||||
track->m_PadsConnected.clear();
|
||||
track->SetState( START_ON_PAD | END_ON_PAD | BUSY, false );
|
||||
}
|
||||
|
||||
// Build connections info tracks to pads
|
||||
SearchTracksConnectedToPads();
|
||||
|
||||
for( TRACK* track = m_brd->m_Track; track != NULL; track = track->Next() )
|
||||
{
|
||||
// Mark track if connected to pads
|
||||
for( unsigned jj = 0; jj < track->m_PadsConnected.size(); jj++ )
|
||||
{
|
||||
D_PAD * pad = track->m_PadsConnected[jj];
|
||||
|
||||
if( pad->HitTest( track->GetStart() ) )
|
||||
{
|
||||
track->start = pad;
|
||||
track->SetState( START_ON_PAD, true );
|
||||
}
|
||||
|
||||
if( pad->HitTest( track->GetEnd() ) )
|
||||
{
|
||||
track->end = pad;
|
||||
track->SetState( END_ON_PAD, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TRACKS_CLEANER::removeBadTrackSegments()
|
||||
{
|
||||
// The rastsnet is expected to be up to date (Compile_Ratsnest was called)
|
||||
|
||||
// Rebuild physical connections.
|
||||
// the list of physical connected items to a given item is in
|
||||
// m_PadsConnected and m_TracksConnected members of each item
|
||||
BuildTracksCandidatesList( m_brd->m_Track );
|
||||
|
||||
// build connections between track segments and pads.
|
||||
SearchTracksConnectedToPads();
|
||||
|
||||
TRACK* segment;
|
||||
|
||||
// build connections between track ends
|
||||
for( segment = m_brd->m_Track; segment; segment = segment->Next() )
|
||||
{
|
||||
SearchConnectedTracks( segment );
|
||||
GetConnectedTracks( segment );
|
||||
}
|
||||
|
||||
bool isModified = false;
|
||||
auto connectivity = m_brd->GetConnectivity();
|
||||
|
||||
for( segment = m_brd->m_Track; segment; segment = segment->Next() )
|
||||
for( auto segment : m_brd->Tracks() )
|
||||
{
|
||||
segment->SetState( FLAG0, false );
|
||||
|
||||
for( unsigned ii = 0; ii < segment->m_PadsConnected.size(); ++ii )
|
||||
for( auto testedPad : connectivity->GetConnectedPads( segment ) )
|
||||
{
|
||||
if( segment->GetNetCode() != segment->m_PadsConnected[ii]->GetNetCode() )
|
||||
if( segment->GetNetCode() != testedPad->GetNetCode() )
|
||||
segment->SetState( FLAG0, true );
|
||||
}
|
||||
|
||||
for( unsigned ii = 0; ii < segment->m_TracksConnected.size(); ++ii )
|
||||
for( auto testedTrack : connectivity->GetConnectedTracks( segment ) )
|
||||
{
|
||||
TRACK* tested = segment->m_TracksConnected[ii];
|
||||
|
||||
if( segment->GetNetCode() != tested->GetNetCode() && !tested->GetState( FLAG0 ) )
|
||||
if( segment->GetNetCode() != testedTrack->GetNetCode() && !testedTrack->GetState( FLAG0 ) )
|
||||
segment->SetState( FLAG0, true );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove tracks having a flagged segment
|
||||
TRACK* next;
|
||||
|
||||
for( segment = m_brd->m_Track; segment; segment = next )
|
||||
for( auto segment : m_brd->Tracks() )
|
||||
{
|
||||
next = segment->Next();
|
||||
|
||||
if( segment->GetState( FLAG0 ) ) // Segment is flagged to be removed
|
||||
{
|
||||
isModified = true;
|
||||
@ -317,15 +245,6 @@ bool TRACKS_CLEANER::removeBadTrackSegments()
|
||||
}
|
||||
}
|
||||
|
||||
if( isModified )
|
||||
{ // some pointers are invalid. Clear the m_TracksConnected list,
|
||||
// to avoid any issue
|
||||
for( segment = m_brd->m_Track; segment; segment = segment->Next() )
|
||||
segment->m_TracksConnected.clear();
|
||||
|
||||
m_brd->m_Status_Pcb = 0;
|
||||
}
|
||||
|
||||
return isModified;
|
||||
}
|
||||
|
||||
@ -353,7 +272,7 @@ bool TRACKS_CLEANER::remove_duplicates_of_via( const VIA *aVia )
|
||||
}
|
||||
|
||||
|
||||
bool TRACKS_CLEANER::clean_vias()
|
||||
bool TRACKS_CLEANER::cleanupVias()
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
@ -376,9 +295,10 @@ bool TRACKS_CLEANER::clean_vias()
|
||||
/* To delete through Via on THT pads at same location
|
||||
* Examine the list of connected pads:
|
||||
* if one through pad is found, the via can be removed */
|
||||
for( unsigned ii = 0; ii < via->m_PadsConnected.size(); ++ii )
|
||||
|
||||
const auto pads = m_brd->GetConnectivity()->GetConnectedPads( via );
|
||||
for( const auto pad : pads )
|
||||
{
|
||||
const D_PAD* pad = via->m_PadsConnected[ii];
|
||||
const LSET all_cu = LSET::AllCuMask();
|
||||
|
||||
if( ( pad->GetLayerSet() & all_cu ) == all_cu )
|
||||
|
@ -39,756 +39,63 @@
|
||||
// Helper classes to handle connection points
|
||||
#include <connect.h>
|
||||
|
||||
const bool g_UseLegacyConnectionAlgo = false;
|
||||
|
||||
extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb );
|
||||
extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode );
|
||||
|
||||
|
||||
|
||||
// Local functions
|
||||
static void RebuildTrackChain( BOARD* pcb );
|
||||
|
||||
|
||||
CONNECTIONS::CONNECTIONS( BOARD * aBrd )
|
||||
{
|
||||
m_brd = aBrd;
|
||||
m_firstTrack = NULL;
|
||||
m_lastTrack = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Fills m_sortedPads with all pads that be connected to tracks
|
||||
* pads are sorted by X coordinate ( and Y coordinates for same X value )
|
||||
* aNetcode = net code to filter pads or < 0 to put all pads in list
|
||||
/*
|
||||
* Function SortTracksByNetCode used in RebuildTrackChain()
|
||||
* to sort track segments by net code.
|
||||
*/
|
||||
void CONNECTIONS::BuildPadsList( int aNetcode )
|
||||
static bool SortTracksByNetCode( const TRACK* const & ref, const TRACK* const & compare )
|
||||
{
|
||||
// Creates sorted pad list if not exists
|
||||
m_sortedPads.clear();
|
||||
m_brd->GetSortedPadListByXthenYCoord( m_sortedPads, aNetcode < 0 ? -1 : aNetcode );
|
||||
// For items having the same Net, keep the order in list
|
||||
if( ref->GetNetCode() == compare->GetNetCode())
|
||||
return ref->m_Param < compare->m_Param;
|
||||
|
||||
return ref->GetNetCode() < compare->GetNetCode();
|
||||
}
|
||||
|
||||
/* Explores the list of pads and adds to m_PadsConnected member
|
||||
* of each pad pads connected to
|
||||
* Here, connections are due to intersecting pads, not tracks
|
||||
*/
|
||||
void CONNECTIONS::SearchConnectionsPadsToIntersectingPads()
|
||||
{
|
||||
std::vector<CONNECTED_POINT*> candidates;
|
||||
|
||||
BuildPadsCandidatesList();
|
||||
|
||||
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
|
||||
{
|
||||
D_PAD* pad = m_sortedPads[ii];
|
||||
|
||||
pad->m_PadsConnected.clear();
|
||||
candidates.clear();
|
||||
|
||||
CollectItemsNearTo( candidates, pad->ShapePos(), pad->GetBoundingRadius() );
|
||||
|
||||
// add pads to pad.m_PadsConnected, if they are connected
|
||||
for( unsigned jj = 0; jj < candidates.size(); jj++ )
|
||||
{
|
||||
CONNECTED_POINT* item = candidates[jj];
|
||||
|
||||
D_PAD* candidate_pad = item->GetPad();
|
||||
|
||||
if( pad == candidate_pad )
|
||||
continue;
|
||||
|
||||
if( !( pad->GetLayerSet() & candidate_pad->GetLayerSet() ).any() )
|
||||
continue;
|
||||
|
||||
if( pad->HitTest( item->GetPoint() ) )
|
||||
{
|
||||
pad->m_PadsConnected.push_back( candidate_pad );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Explores the list of pads
|
||||
* Adds to m_PadsConnected member of each track the pad(s) connected to
|
||||
* Adds to m_TracksConnected member of each pad the track(s) connected to
|
||||
* D_PAD::m_TracksConnected is cleared before adding items
|
||||
* TRACK::m_PadsConnected is not cleared
|
||||
*/
|
||||
void CONNECTIONS::SearchTracksConnectedToPads( bool add_to_padlist, bool add_to_tracklist)
|
||||
{
|
||||
std::vector<CONNECTED_POINT*> candidates;
|
||||
|
||||
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
|
||||
{
|
||||
D_PAD * pad = m_sortedPads[ii];
|
||||
pad->m_TracksConnected.clear();
|
||||
candidates.clear();
|
||||
|
||||
CollectItemsNearTo( candidates, pad->GetPosition(), pad->GetBoundingRadius() );
|
||||
|
||||
// add this pad to track.m_PadsConnected, if it is connected
|
||||
for( unsigned jj = 0; jj < candidates.size(); jj++ )
|
||||
{
|
||||
CONNECTED_POINT* cp_item = candidates[jj];
|
||||
|
||||
if( !( pad->GetLayerSet() & cp_item->GetTrack()->GetLayerSet() ).any() )
|
||||
continue;
|
||||
|
||||
if( pad->HitTest( cp_item->GetPoint() ) )
|
||||
{
|
||||
if( add_to_padlist )
|
||||
cp_item->GetTrack()->m_PadsConnected.push_back( pad );
|
||||
|
||||
if( add_to_tracklist )
|
||||
pad->m_TracksConnected.push_back( cp_item->GetTrack() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CONNECTIONS::CollectItemsNearTo( std::vector<CONNECTED_POINT*>& aList,
|
||||
const wxPoint& aPosition, int aDistMax )
|
||||
{
|
||||
/* Search items in m_Candidates that position is <= aDistMax from aPosition
|
||||
* (Rectilinear distance)
|
||||
* m_Candidates is sorted by X then Y values, so a fast binary search is used
|
||||
* to locate the "best" entry point in list
|
||||
* The best entry is a pad having its m_Pos.x == (or near) aPosition.x
|
||||
* All candidates are near this candidate in list
|
||||
* So from this entry point, a linear search is made to find all candidates
|
||||
*/
|
||||
int idxmax = m_candidates.size()-1;
|
||||
|
||||
int delta = m_candidates.size();
|
||||
|
||||
int idx = 0; // Starting index is the beginning of list
|
||||
while( delta )
|
||||
{
|
||||
// Calculate half size of remaining interval to test.
|
||||
// Ensure the computed value is not truncated (too small)
|
||||
if( (delta & 1) && ( delta > 1 ) )
|
||||
delta++;
|
||||
delta /= 2;
|
||||
|
||||
CONNECTED_POINT& item = m_candidates[idx];
|
||||
|
||||
int dist = item.GetPoint().x - aPosition.x;
|
||||
if( abs(dist) <= aDistMax )
|
||||
{
|
||||
break; // A good entry point is found. The list can be scanned from this point.
|
||||
}
|
||||
|
||||
else if( item.GetPoint().x < aPosition.x ) // We should search after this item
|
||||
{
|
||||
idx += delta;
|
||||
if( idx > idxmax )
|
||||
idx = idxmax;
|
||||
}
|
||||
else // We should search before this item
|
||||
{
|
||||
idx -= delta;
|
||||
if( idx < 0 )
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now explore the candidate list from the "best" entry point found
|
||||
* (candidate "near" aPosition.x)
|
||||
* We explore the list until abs(candidate->m_Point.x - aPosition.x) > aDistMax
|
||||
* because the list is sorted by X position (and for a given X pos, by Y pos)
|
||||
* Currently a linear search is made because the number of candidates
|
||||
* having the right X position is usually small
|
||||
*/
|
||||
// search next candidates in list
|
||||
wxPoint diff;
|
||||
for( int ii = idx; ii <= idxmax; ii++ )
|
||||
{
|
||||
CONNECTED_POINT* item = &m_candidates[ii];
|
||||
diff = item->GetPoint() - aPosition;
|
||||
if( abs(diff.x) > aDistMax )
|
||||
break; // Exit: the distance is to long, we cannot find other candidates
|
||||
if( abs(diff.y) > aDistMax )
|
||||
continue; // the y distance is to long, but we can find other candidates
|
||||
// We have here a good candidate: add it
|
||||
aList.push_back( item );
|
||||
}
|
||||
// search previous candidates in list
|
||||
for( int ii = idx-1; ii >=0; ii-- )
|
||||
{
|
||||
CONNECTED_POINT * item = &m_candidates[ii];
|
||||
diff = item->GetPoint() - aPosition;
|
||||
if( abs(diff.x) > aDistMax )
|
||||
break;
|
||||
if( abs(diff.y) > aDistMax )
|
||||
continue;
|
||||
// We have here a good candidate:add it
|
||||
aList.push_back( item );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CONNECTIONS::BuildPadsCandidatesList()
|
||||
{
|
||||
m_candidates.clear();
|
||||
m_candidates.reserve( m_sortedPads.size() );
|
||||
|
||||
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
|
||||
{
|
||||
D_PAD * pad = m_sortedPads[ii];
|
||||
CONNECTED_POINT candidate( pad, pad->GetPosition() );
|
||||
m_candidates.push_back( candidate );
|
||||
}
|
||||
}
|
||||
|
||||
/* sort function used to sort .m_Connected by X the Y values
|
||||
* items are sorted by X coordinate value,
|
||||
* and for same X value, by Y coordinate value.
|
||||
*/
|
||||
static bool sortConnectedPointByXthenYCoordinates( const CONNECTED_POINT & aRef,
|
||||
const CONNECTED_POINT & aTst )
|
||||
{
|
||||
if( aRef.GetPoint().x == aTst.GetPoint().x )
|
||||
return aRef.GetPoint().y < aTst.GetPoint().y;
|
||||
return aRef.GetPoint().x < aTst.GetPoint().x;
|
||||
}
|
||||
|
||||
void CONNECTIONS::BuildTracksCandidatesList( TRACK* aBegin, TRACK* aEnd)
|
||||
{
|
||||
m_candidates.clear();
|
||||
m_firstTrack = m_lastTrack = aBegin;
|
||||
|
||||
unsigned ii = 0;
|
||||
|
||||
// Count candidates ( i.e. end points )
|
||||
for( const TRACK* track = aBegin; track; track = track->Next() )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
ii++;
|
||||
else
|
||||
ii += 2;
|
||||
|
||||
m_lastTrack = track;
|
||||
|
||||
if( track == aEnd )
|
||||
break;
|
||||
}
|
||||
|
||||
// Build candidate list
|
||||
m_candidates.reserve( ii );
|
||||
for( TRACK* track = aBegin; track; track = track->Next() )
|
||||
{
|
||||
CONNECTED_POINT candidate( track, track->GetStart() );
|
||||
|
||||
m_candidates.push_back( candidate );
|
||||
if( track->Type() != PCB_VIA_T )
|
||||
{
|
||||
CONNECTED_POINT candidate2( track, track->GetEnd());
|
||||
m_candidates.push_back( candidate2 );
|
||||
}
|
||||
|
||||
if( track == aEnd )
|
||||
break;
|
||||
}
|
||||
|
||||
// Sort list by increasing X coordinate,
|
||||
// and for increasing Y coordinate when items have the same X coordinate
|
||||
// So candidates to the same location are consecutive in list.
|
||||
sort( m_candidates.begin(), m_candidates.end(), sortConnectedPointByXthenYCoordinates );
|
||||
}
|
||||
|
||||
|
||||
/* Populates .m_connected with tracks/vias connected to aTrack
|
||||
* param aTrack = track or via to use as reference
|
||||
* For calculation time reason, an exhaustive search cannot be made
|
||||
* and a proximity search is made:
|
||||
* Only tracks with one end near one end of aTrack are collected.
|
||||
* near means dist <= aTrack width / 2
|
||||
* because with this constraint we can make a fast search in track list
|
||||
* m_candidates is expected to be populated by the track candidates ends list
|
||||
*/
|
||||
int CONNECTIONS::SearchConnectedTracks( const TRACK* aTrack )
|
||||
{
|
||||
int count = 0;
|
||||
m_connected.clear();
|
||||
|
||||
LSET layerMask = aTrack->GetLayerSet();
|
||||
|
||||
// Search for connections to starting point:
|
||||
#define USE_EXTENDED_SEARCH
|
||||
|
||||
#ifdef USE_EXTENDED_SEARCH
|
||||
int dist_max = aTrack->GetWidth() / 2;
|
||||
static std::vector<CONNECTED_POINT*> tracks_candidates;
|
||||
#endif
|
||||
|
||||
wxPoint position = aTrack->GetStart();
|
||||
|
||||
for( int kk = 0; kk < 2; kk++ )
|
||||
{
|
||||
#ifndef USE_EXTENDED_SEARCH
|
||||
int idx = searchEntryPointInCandidatesList( position );
|
||||
|
||||
if( idx >= 0 )
|
||||
{
|
||||
// search after:
|
||||
for( unsigned ii = idx; ii < m_candidates.size(); ii ++ )
|
||||
{
|
||||
if( m_candidates[ii].GetTrack() == aTrack )
|
||||
continue;
|
||||
|
||||
if( m_candidates[ii].GetPoint() != position )
|
||||
break;
|
||||
|
||||
if( ( m_candidates[ii].GetTrack()->GetLayerSet() & layerMask ).any() )
|
||||
m_connected.push_back( m_candidates[ii].GetTrack() );
|
||||
}
|
||||
|
||||
// search before:
|
||||
for( int ii = idx-1; ii >= 0; ii -- )
|
||||
{
|
||||
if( m_candidates[ii].GetTrack() == aTrack )
|
||||
continue;
|
||||
|
||||
if( m_candidates[ii].GetPoint() != position )
|
||||
break;
|
||||
|
||||
if( ( m_candidates[ii].GetTrack()->GetLayerSet() & layerMask ).any() )
|
||||
m_connected.push_back( m_candidates[ii].GetTrack() );
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
tracks_candidates.clear();
|
||||
|
||||
CollectItemsNearTo( tracks_candidates, position, dist_max );
|
||||
|
||||
for( unsigned ii = 0; ii < tracks_candidates.size(); ii++ )
|
||||
{
|
||||
TRACK* ctrack = tracks_candidates[ii]->GetTrack();
|
||||
|
||||
if( !( ctrack->GetLayerSet() & layerMask ).any() )
|
||||
continue;
|
||||
|
||||
if( ctrack == aTrack )
|
||||
continue;
|
||||
|
||||
// We have a good candidate: calculate the actual distance
|
||||
// between ends, which should be <= dist max.
|
||||
wxPoint delta = tracks_candidates[ii]->GetPoint() - position;
|
||||
|
||||
int dist = KiROUND( EuclideanNorm( delta ) );
|
||||
|
||||
if( dist > dist_max )
|
||||
continue;
|
||||
|
||||
m_connected.push_back( ctrack );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Search for connections to ending point:
|
||||
if( aTrack->Type() == PCB_VIA_T )
|
||||
break;
|
||||
|
||||
position = aTrack->GetEnd();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int CONNECTIONS::searchEntryPointInCandidatesList( const wxPoint& aPoint )
|
||||
{
|
||||
// Search the aPoint coordinates in m_Candidates
|
||||
// m_Candidates is sorted by X then Y values, and a fast binary search is used
|
||||
int idxmax = m_candidates.size()-1;
|
||||
|
||||
int delta = m_candidates.size();
|
||||
|
||||
int idx = 0; // Starting index is the beginning of list
|
||||
|
||||
while( delta )
|
||||
{
|
||||
// Calculate half size of remaining interval to test.
|
||||
// Ensure the computed value is not truncated (too small)
|
||||
if( ( delta & 1 ) && ( delta > 1 ) )
|
||||
delta++;
|
||||
|
||||
delta /= 2;
|
||||
|
||||
CONNECTED_POINT& candidate = m_candidates[idx];
|
||||
|
||||
if( candidate.GetPoint() == aPoint ) // candidate found
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
|
||||
// Not found: test the middle of the remaining sub list
|
||||
if( candidate.GetPoint().x == aPoint.x ) // Must search considering Y coordinate
|
||||
{
|
||||
if(candidate.GetPoint().y < aPoint.y) // Must search after this item
|
||||
{
|
||||
idx += delta;
|
||||
if( idx > idxmax )
|
||||
idx = idxmax;
|
||||
}
|
||||
else // Must search before this item
|
||||
{
|
||||
idx -= delta;
|
||||
if( idx < 0 )
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
else if( candidate.GetPoint().x < aPoint.x ) // Must search after this item
|
||||
{
|
||||
idx += delta;
|
||||
if( idx > idxmax )
|
||||
idx = idxmax;
|
||||
}
|
||||
else // Must search before this item
|
||||
{
|
||||
idx -= delta;
|
||||
if( idx < 0 )
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Used after a track change (delete a track ou add a track)
|
||||
* Connections to pads are recalculated
|
||||
* Note also aFirstTrack (and aLastTrack ) can be NULL
|
||||
*/
|
||||
void CONNECTIONS::Build_CurrNet_SubNets_Connections( TRACK* aFirstTrack, TRACK* aLastTrack, int aNetcode )
|
||||
{
|
||||
m_firstTrack = aFirstTrack; // The first track used to build m_Candidates
|
||||
m_lastTrack = aLastTrack; // The last track used to build m_Candidates
|
||||
|
||||
// Pads subnets are expected already cleared, because this function
|
||||
// does not know the full list of pads
|
||||
BuildTracksCandidatesList( aFirstTrack, aLastTrack );
|
||||
TRACK* curr_track;
|
||||
for( curr_track = aFirstTrack; curr_track != NULL; curr_track = curr_track->Next() )
|
||||
{
|
||||
// Clear track subnet id (Pads subnets are cleared outside this function)
|
||||
curr_track->SetSubNet( 0 );
|
||||
curr_track->m_TracksConnected.clear();
|
||||
curr_track->m_PadsConnected.clear();
|
||||
|
||||
// Update connections between tracks:
|
||||
SearchConnectedTracks( curr_track );
|
||||
curr_track->m_TracksConnected = m_connected;
|
||||
|
||||
if( curr_track == aLastTrack )
|
||||
break;
|
||||
}
|
||||
|
||||
// Update connections between tracks and pads
|
||||
BuildPadsList( aNetcode );
|
||||
SearchTracksConnectedToPads();
|
||||
|
||||
// Update connections between intersecting pads (no tracks)
|
||||
SearchConnectionsPadsToIntersectingPads();
|
||||
|
||||
// Creates sub nets (clusters) for the current net:
|
||||
Propagate_SubNets();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change a subnet value to a new value, in m_sortedPads pad list
|
||||
* After that, 2 cluster (or subnets) are merged into only one.
|
||||
* Note: the resulting subnet value is the smallest between aOldSubNet et aNewSubNet
|
||||
* Helper function RebuildTrackChain
|
||||
* rebuilds the track segment linked list in order to have a chain
|
||||
* sorted by increasing netcodes.
|
||||
* We try to keep order of track segments in list, when possible
|
||||
* @param pcb = board to rebuild
|
||||
*/
|
||||
int CONNECTIONS::Merge_PadsSubNets( int aOldSubNet, int aNewSubNet )
|
||||
static void RebuildTrackChain( BOARD* pcb )
|
||||
{
|
||||
int change_count = 0;
|
||||
if( pcb->m_Track == NULL )
|
||||
return;
|
||||
|
||||
if( aOldSubNet == aNewSubNet )
|
||||
return 0;
|
||||
int item_count = pcb->m_Track.GetCount();
|
||||
|
||||
if( (aOldSubNet > 0) && (aOldSubNet < aNewSubNet) )
|
||||
std::swap( aOldSubNet, aNewSubNet );
|
||||
std::vector<TRACK*> trackList;
|
||||
trackList.reserve( item_count );
|
||||
|
||||
// Examine connections between intersecting pads
|
||||
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
|
||||
// Put track list in a temporary list to sort tracks by netcode
|
||||
// We try to keep the initial order of track segments in list, when possible
|
||||
// so we use m_Param (a member variable used for temporary storage)
|
||||
// to temporary keep trace of the order of segments
|
||||
// The sort function uses this variable to sort items that
|
||||
// have the same net code.
|
||||
// Without this, during sorting, the initial order is sometimes lost
|
||||
// by the sort algorithm
|
||||
for( int ii = 0; ii < item_count; ++ii )
|
||||
{
|
||||
D_PAD * curr_pad = m_sortedPads[ii];
|
||||
if( curr_pad->GetSubNet() != aOldSubNet )
|
||||
continue;
|
||||
|
||||
change_count++;
|
||||
curr_pad->SetSubNet( aNewSubNet );
|
||||
pcb->m_Track->m_Param = ii;
|
||||
trackList.push_back( pcb->m_Track.PopFront() );
|
||||
}
|
||||
|
||||
return change_count;
|
||||
// the list is empty now
|
||||
wxASSERT( pcb->m_Track == NULL && pcb->m_Track.GetCount()==0 );
|
||||
|
||||
sort( trackList.begin(), trackList.end(), SortTracksByNetCode );
|
||||
|
||||
// add them back to the list
|
||||
for( int i = 0; i < item_count; ++i )
|
||||
pcb->m_Track.PushBack( trackList[i] );
|
||||
}
|
||||
|
||||
/*
|
||||
* Change a subnet value to a new value, for tracks and pads which are connected to.
|
||||
* The result is merging 2 clusters (or subnets) into only one cluster.
|
||||
* Note: the resulting sub net value is the smallest between aOldSubNet et aNewSubNet
|
||||
*/
|
||||
int CONNECTIONS::Merge_SubNets( int aOldSubNet, int aNewSubNet )
|
||||
{
|
||||
TRACK* curr_track;
|
||||
int change_count = 0;
|
||||
|
||||
if( aOldSubNet == aNewSubNet )
|
||||
return 0;
|
||||
|
||||
if( (aOldSubNet > 0) && (aOldSubNet < aNewSubNet) )
|
||||
std::swap( aOldSubNet, aNewSubNet );
|
||||
|
||||
curr_track = (TRACK*)m_firstTrack;
|
||||
|
||||
for( ; curr_track != NULL; curr_track = curr_track->Next() )
|
||||
{
|
||||
if( curr_track->GetSubNet() != aOldSubNet )
|
||||
{
|
||||
if( curr_track == m_lastTrack )
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
change_count++;
|
||||
curr_track->SetSubNet( aNewSubNet );
|
||||
|
||||
for( unsigned ii = 0; ii < curr_track->m_PadsConnected.size(); ii++ )
|
||||
{
|
||||
D_PAD * pad = curr_track->m_PadsConnected[ii];
|
||||
if( pad->GetSubNet() == aOldSubNet )
|
||||
{
|
||||
pad->SetSubNet( curr_track->GetSubNet() );
|
||||
}
|
||||
}
|
||||
|
||||
if( curr_track == m_lastTrack )
|
||||
break;
|
||||
}
|
||||
|
||||
return change_count;
|
||||
}
|
||||
|
||||
|
||||
/* Test a list of track segments, to create or propagate a sub netcode to pads and
|
||||
* segments connected together.
|
||||
* The track list must be sorted by nets, and all segments
|
||||
* from m_firstTrack to m_lastTrack have the same net
|
||||
* When 2 items are connected (a track to a pad, or a track to an other track),
|
||||
* they are grouped in a cluster.
|
||||
* The .m_Subnet member is the cluster identifier (subnet id)
|
||||
* For a given net, if all tracks are created, there is only one cluster.
|
||||
* but if not all tracks are created, there are more than one cluster,
|
||||
* and some ratsnests will be left active.
|
||||
* A ratsnest is active when it "connect" 2 items having different subnet id
|
||||
*/
|
||||
void CONNECTIONS::Propagate_SubNets()
|
||||
{
|
||||
int sub_netcode = 1;
|
||||
|
||||
TRACK* curr_track = (TRACK*)m_firstTrack;
|
||||
if( curr_track )
|
||||
curr_track->SetSubNet( sub_netcode );
|
||||
|
||||
// Examine connections between tracks and pads
|
||||
for( ; curr_track != NULL; curr_track = curr_track->Next() )
|
||||
{
|
||||
// First: handling connections to pads
|
||||
for( unsigned ii = 0; ii < curr_track->m_PadsConnected.size(); ii++ )
|
||||
{
|
||||
D_PAD * pad = curr_track->m_PadsConnected[ii];
|
||||
|
||||
if( curr_track->GetSubNet() ) // the track segment is already a cluster member
|
||||
{
|
||||
if( pad->GetSubNet() > 0 )
|
||||
{
|
||||
// The pad is already a cluster member, so we can merge the 2 clusters
|
||||
Merge_SubNets( pad->GetSubNet(), curr_track->GetSubNet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The pad is not yet attached to a cluster , so we can add this pad to
|
||||
* the cluster */
|
||||
pad->SetSubNet( curr_track->GetSubNet() );
|
||||
}
|
||||
}
|
||||
else // the track segment is not attached to a cluster
|
||||
{
|
||||
if( pad->GetSubNet() > 0 )
|
||||
{
|
||||
// it is connected to a pad in a cluster, merge this track
|
||||
curr_track->SetSubNet( pad->GetSubNet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* it is connected to a pad not in a cluster, so we must create a new
|
||||
* cluster (only with the 2 items: the track and the pad) */
|
||||
sub_netcode++;
|
||||
curr_track->SetSubNet( sub_netcode );
|
||||
pad->SetSubNet( curr_track->GetSubNet() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test connections between segments
|
||||
for( unsigned ii = 0; ii < curr_track->m_TracksConnected.size(); ii++ )
|
||||
{
|
||||
BOARD_CONNECTED_ITEM* track = curr_track->m_TracksConnected[ii];
|
||||
|
||||
if( curr_track->GetSubNet() ) // The current track is already a cluster member
|
||||
{
|
||||
// The other track is already a cluster member, so we can merge the 2 clusters
|
||||
if( track->GetSubNet() )
|
||||
{
|
||||
Merge_SubNets( track->GetSubNet(), curr_track->GetSubNet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// The other track is not yet attached to a cluster , so we can add this
|
||||
// other track to the cluster
|
||||
track->SetSubNet( curr_track->GetSubNet() );
|
||||
}
|
||||
}
|
||||
else // the current track segment is not yet attached to a cluster
|
||||
{
|
||||
if( track->GetSubNet() )
|
||||
{
|
||||
// The other track is already a cluster member, so we can add
|
||||
// the current segment to the cluster
|
||||
curr_track->SetSubNet( track->GetSubNet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// it is connected to an other segment not in a cluster, so we must
|
||||
// create a new cluster (only with the 2 track segments)
|
||||
sub_netcode++;
|
||||
curr_track->SetSubNet( sub_netcode );
|
||||
track->SetSubNet( curr_track->GetSubNet() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( curr_track == m_lastTrack )
|
||||
break;
|
||||
}
|
||||
|
||||
// Examine connections between intersecting pads, and propagate
|
||||
// sub_netcodes to intersecting pads
|
||||
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
|
||||
{
|
||||
D_PAD* curr_pad = m_sortedPads[ii];
|
||||
|
||||
for( unsigned jj = 0; jj < curr_pad->m_PadsConnected.size(); jj++ )
|
||||
{
|
||||
D_PAD* pad = curr_pad->m_PadsConnected[jj];
|
||||
|
||||
if( curr_pad->GetSubNet() ) // the current pad is already attached to a cluster
|
||||
{
|
||||
if( pad->GetSubNet() > 0 )
|
||||
{
|
||||
// The pad is already a cluster member, so we can merge the 2 clusters
|
||||
// Store the initial subnets, which will be modified by Merge_PadsSubNets
|
||||
int subnet1 = pad->GetSubNet();
|
||||
int subnet2 = curr_pad->GetSubNet();
|
||||
|
||||
// merge subnets of pads only, even those not connected by tracks
|
||||
Merge_PadsSubNets( subnet1, subnet2 );
|
||||
|
||||
// merge subnets of tracks (and pads, which are already merged)
|
||||
Merge_SubNets( subnet1, subnet2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// The pad is not yet attached to a cluster,
|
||||
// so we can add this pad to the cluster
|
||||
pad->SetSubNet( curr_pad->GetSubNet() );
|
||||
}
|
||||
}
|
||||
else // the current pad is not attached to a cluster
|
||||
{
|
||||
if( pad->GetSubNet() > 0 )
|
||||
{
|
||||
// the connected pad is in a cluster,
|
||||
// so we can add the current pad to the cluster
|
||||
curr_pad->SetSubNet( pad->GetSubNet() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// the connected pad is not in a cluster,
|
||||
// so we must create a new cluster, with the 2 pads.
|
||||
sub_netcode++;
|
||||
curr_pad->SetSubNet( sub_netcode );
|
||||
pad->SetSubNet( curr_pad->GetSubNet() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test all connections of the board,
|
||||
* and update subnet variable of pads and tracks
|
||||
* TestForActiveLinksInRatsnest must be called after this function
|
||||
* to update active/inactive ratsnest items status
|
||||
*/
|
||||
void PCB_BASE_FRAME::TestConnections()
|
||||
{
|
||||
// Clear the cluster identifier for all pads
|
||||
for( unsigned i = 0; i< m_Pcb->GetPadCount(); ++i )
|
||||
{
|
||||
D_PAD* pad = m_Pcb->GetPad(i);
|
||||
|
||||
pad->SetZoneSubNet( 0 );
|
||||
pad->SetSubNet( 0 );
|
||||
}
|
||||
|
||||
m_Pcb->Test_Connections_To_Copper_Areas();
|
||||
|
||||
// Test existing connections net by net
|
||||
// note some nets can have no tracks, and pads intersecting
|
||||
// so Build_CurrNet_SubNets_Connections must be called for each net
|
||||
CONNECTIONS connections( m_Pcb );
|
||||
|
||||
int last_net_tested = 0;
|
||||
int current_net_code = 0;
|
||||
|
||||
for( TRACK* track = m_Pcb->m_Track; track; )
|
||||
{
|
||||
// At this point, track is the first track of a given net
|
||||
current_net_code = track->GetNetCode();
|
||||
|
||||
// Get last track of the current net
|
||||
TRACK* lastTrack = track->GetEndNetCode( current_net_code );
|
||||
|
||||
if( current_net_code > 0 ) // do not spend time if net code = 0 ( dummy net )
|
||||
{
|
||||
// Test all previous nets having no tracks
|
||||
for( int net = last_net_tested+1; net < current_net_code; net++ )
|
||||
connections.Build_CurrNet_SubNets_Connections( NULL, NULL, net );
|
||||
|
||||
connections.Build_CurrNet_SubNets_Connections( track, lastTrack, current_net_code );
|
||||
last_net_tested = current_net_code;
|
||||
}
|
||||
|
||||
track = lastTrack->Next(); // this is now the first track of the next net
|
||||
}
|
||||
|
||||
// Test last nets without tracks, if any
|
||||
int netsCount = m_Pcb->GetNetCount();
|
||||
for( int net = last_net_tested+1; net < netsCount; net++ )
|
||||
connections.Build_CurrNet_SubNets_Connections( NULL, NULL, net );
|
||||
|
||||
Merge_SubNets_Connected_By_CopperAreas( m_Pcb );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::TestNetConnection( wxDC* aDC, int aNetCode )
|
||||
{
|
||||
#if 0
|
||||
// Skip dummy net -1, and "not connected" net 0 (grouping all not connected pads)
|
||||
if( aNetCode <= 0 )
|
||||
return;
|
||||
@ -855,98 +162,5 @@ void PCB_BASE_FRAME::TestNetConnection( wxDC* aDC, int aNetCode )
|
||||
SetStatusText( msg );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void PCB_BASE_FRAME::ComputeLegacyConnections()
|
||||
{
|
||||
|
||||
// Build the net info list
|
||||
GetBoard()->BuildListOfNets();
|
||||
|
||||
// Reset variables and flags used in computation
|
||||
for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
|
||||
{
|
||||
t->m_TracksConnected.clear();
|
||||
t->m_PadsConnected.clear();
|
||||
t->start = NULL;
|
||||
t->end = NULL;
|
||||
t->SetState( BUSY | IN_EDIT | BEGIN_ONPAD | END_ONPAD, false );
|
||||
t->SetZoneSubNet( 0 );
|
||||
}
|
||||
|
||||
// If no pad, reset pointers and netcode, and do nothing else
|
||||
if( m_Pcb->GetPadCount() == 0 )
|
||||
return;
|
||||
|
||||
CONNECTIONS connections( m_Pcb );
|
||||
connections.BuildPadsList();
|
||||
connections.BuildTracksCandidatesList(m_Pcb->m_Track);
|
||||
|
||||
// First pass: build connections between track segments and pads.
|
||||
connections.SearchTracksConnectedToPads();
|
||||
|
||||
for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
|
||||
{
|
||||
connections.SearchConnectedTracks( t );
|
||||
connections.GetConnectedTracks( t );
|
||||
}
|
||||
|
||||
// Sort the track list by net codes:
|
||||
RebuildTrackChain( m_Pcb );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Function SortTracksByNetCode used in RebuildTrackChain()
|
||||
* to sort track segments by net code.
|
||||
*/
|
||||
static bool SortTracksByNetCode( const TRACK* const & ref, const TRACK* const & compare )
|
||||
{
|
||||
// For items having the same Net, keep the order in list
|
||||
if( ref->GetNetCode() == compare->GetNetCode())
|
||||
return ref->m_Param < compare->m_Param;
|
||||
|
||||
return ref->GetNetCode() < compare->GetNetCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function RebuildTrackChain
|
||||
* rebuilds the track segment linked list in order to have a chain
|
||||
* sorted by increasing netcodes.
|
||||
* We try to keep order of track segments in list, when possible
|
||||
* @param pcb = board to rebuild
|
||||
*/
|
||||
static void RebuildTrackChain( BOARD* pcb )
|
||||
{
|
||||
if( pcb->m_Track == NULL )
|
||||
return;
|
||||
|
||||
int item_count = pcb->m_Track.GetCount();
|
||||
|
||||
std::vector<TRACK*> trackList;
|
||||
trackList.reserve( item_count );
|
||||
|
||||
// Put track list in a temporary list to sort tracks by netcode
|
||||
// We try to keep the initial order of track segments in list, when possible
|
||||
// so we use m_Param (a member variable used for temporary storage)
|
||||
// to temporary keep trace of the order of segments
|
||||
// The sort function uses this variable to sort items that
|
||||
// have the same net code.
|
||||
// Without this, during sorting, the initial order is sometimes lost
|
||||
// by the sort algorithm
|
||||
for( int ii = 0; ii < item_count; ++ii )
|
||||
{
|
||||
pcb->m_Track->m_Param = ii;
|
||||
trackList.push_back( pcb->m_Track.PopFront() );
|
||||
}
|
||||
|
||||
// the list is empty now
|
||||
wxASSERT( pcb->m_Track == NULL && pcb->m_Track.GetCount()==0 );
|
||||
|
||||
sort( trackList.begin(), trackList.end(), SortTracksByNetCode );
|
||||
|
||||
// add them back to the list
|
||||
for( int i = 0; i < item_count; ++i )
|
||||
pcb->m_Track.PushBack( trackList[i] );
|
||||
#endif
|
||||
}
|
||||
|
225
pcbnew/connect.h
225
pcbnew/connect.h
@ -34,230 +34,5 @@
|
||||
#include <class_board.h>
|
||||
|
||||
|
||||
// Helper classes to handle connection points (i.e. candidates) for tracks
|
||||
|
||||
/* class CONNECTED_POINT describes a coordinate having a track or pad parent.
|
||||
* when a pad is the parent, the coordinate is (obviously) the connection point
|
||||
* when a track is the parent, the coordinate is the staring point
|
||||
* or the ending point.
|
||||
* therefore when building a list of CONNECTED_POINT, a pad or via generates one item,
|
||||
* and a track generates 2 items.
|
||||
*/
|
||||
class CONNECTED_POINT
|
||||
{
|
||||
private:
|
||||
BOARD_CONNECTED_ITEM * m_item; // a link to the parent item (track, via or pad)
|
||||
wxPoint m_point; // coordinates of this connected point
|
||||
|
||||
public:
|
||||
// ctor to build a CONNECTED_POINT instance, when the parent is a track or via
|
||||
CONNECTED_POINT( TRACK * aTrack, const wxPoint & aPoint)
|
||||
{
|
||||
m_item = aTrack;
|
||||
m_point = aPoint;
|
||||
}
|
||||
|
||||
// ctor to build a CONNECTED_POINT instance, when the parent is a pad
|
||||
CONNECTED_POINT( D_PAD * aPad, const wxPoint & aPoint)
|
||||
{
|
||||
m_item = aPad;
|
||||
m_point = aPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetTrack
|
||||
* @return the parent track or via of this connected point,
|
||||
* or null if the parent is a pad
|
||||
*/
|
||||
TRACK* GetTrack() const
|
||||
{
|
||||
return m_item->Type() != PCB_PAD_T ? (TRACK*) m_item : NULL ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetPad
|
||||
* @return the parent pad of this connected point,
|
||||
* or null if the parent is a track or via
|
||||
*/
|
||||
D_PAD * GetPad() const
|
||||
{
|
||||
return m_item->Type() == PCB_PAD_T ? (D_PAD*) m_item : NULL;
|
||||
}
|
||||
|
||||
const wxPoint & GetPoint() const { return m_point; }
|
||||
};
|
||||
|
||||
// A helper class to handle connections calculations:
|
||||
class CONNECTIONS
|
||||
{
|
||||
private:
|
||||
std::vector <TRACK*> m_connected; // List of connected tracks/vias
|
||||
// to a given track or via
|
||||
std::vector <CONNECTED_POINT> m_candidates; // List of points to test
|
||||
// (end points of tracks or vias location )
|
||||
BOARD * m_brd; // the master board.
|
||||
const TRACK * m_firstTrack; // The first track used to build m_Candidates
|
||||
const TRACK * m_lastTrack; // The last track used to build m_Candidates
|
||||
std::vector<D_PAD*> m_sortedPads; // list of sorted pads by X (then Y) coordinate
|
||||
|
||||
public:
|
||||
CONNECTIONS( BOARD * aBrd );
|
||||
~CONNECTIONS() {};
|
||||
|
||||
/**
|
||||
* Function BuildPadsList
|
||||
* Fills m_sortedPads with all pads that be connected to tracks
|
||||
* pads are sorted by X then Y coordinates to allow fast binary search in list
|
||||
* @param aNetcode = net code to use to filter pads
|
||||
* if aNetcode < 0, all pads will be put in list (default)
|
||||
*/
|
||||
void BuildPadsList( int aNetcode = -1 );
|
||||
|
||||
/**
|
||||
* Function GetPadsList
|
||||
* @return the pads list used in connections calculations
|
||||
*/
|
||||
std::vector<D_PAD*>& GetPadsList() { return m_sortedPads; }
|
||||
|
||||
/**
|
||||
* Function Build_CurrNet_SubNets_Connections
|
||||
* should be called after a track change (delete or add a track):
|
||||
* Connections to pads and to tracks are recalculated
|
||||
* If a track is deleted, the other pointers to pads do not change.
|
||||
* When a new track is added in track list, its pointers to pads are already initialized
|
||||
* Builds the subnets inside a net (tracks from aFirstTrack to aFirstTrack).
|
||||
* subnets are clusters of pads and tracks that are connected together.
|
||||
* When all tracks are created relative to the net, there is only a cluster
|
||||
* when not tracks there are a cluster per pad
|
||||
* @param aFirstTrack = first track of the given net
|
||||
* @param aLastTrack = last track of the given net
|
||||
* @param aNetcode = the netcode of the given net
|
||||
*/
|
||||
void Build_CurrNet_SubNets_Connections( TRACK* aFirstTrack, TRACK* aLastTrack, int aNetcode );
|
||||
|
||||
/**
|
||||
* Function BuildTracksCandidatesList
|
||||
* Fills m_Candidates with all connecting points (track ends or via location)
|
||||
* with tracks from aBegin to aEnd.
|
||||
* @param aBegin = first track to store in list (should not be NULL)
|
||||
* @param aEnd = last track to store in list
|
||||
* if aEnd == NULL, uses all tracks from aBegin
|
||||
*/
|
||||
void BuildTracksCandidatesList( TRACK * aBegin, TRACK * aEnd = NULL);
|
||||
|
||||
/**
|
||||
* Function BuildPadsCandidatesList
|
||||
* Populates m_candidates with all pads connecting points (pads position)
|
||||
* m_sortedPads is expected to be populated by the pad candidates list
|
||||
*/
|
||||
void BuildPadsCandidatesList();
|
||||
|
||||
/**
|
||||
* function SearchConnectedTracks
|
||||
* Populates .m_connected with tracks/vias connected to aTrack
|
||||
* m_candidates is expected to be populated by the track candidates ends list
|
||||
* @param aTrack = track or via to use as reference
|
||||
*/
|
||||
int SearchConnectedTracks( const TRACK * aTrack );
|
||||
|
||||
/**
|
||||
* Function GetConnectedTracks
|
||||
* Copy m_Connected that contains the list of tracks connected
|
||||
* calculated by SearchConnectedTracks
|
||||
* in aTrack->m_TracksConnected
|
||||
* @param aTrack = track or via to fill with connected tracks
|
||||
*/
|
||||
void GetConnectedTracks(TRACK * aTrack)
|
||||
{
|
||||
aTrack->m_TracksConnected = m_connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* function SearchConnectionsPadsToIntersectingPads
|
||||
* Explores the list of pads and adds to m_PadsConnected member
|
||||
* of each pad pads connected to
|
||||
* Here, connections are due to intersecting pads, not tracks
|
||||
* m_sortedPads must be initialized
|
||||
*/
|
||||
void SearchConnectionsPadsToIntersectingPads();
|
||||
|
||||
/**
|
||||
* function SearchTracksConnectedToPads
|
||||
* Explores the list of pads.
|
||||
* if( add_to_padlist )
|
||||
* adds to m_PadsConnected member of each track the pad(s) connected to
|
||||
* if add_to_tracklist
|
||||
* adds to m_TracksConnected member of each pad the track(s) connected to
|
||||
* D_PAD::m_TracksConnected is cleared before adding items
|
||||
* TRACK::m_PadsConnected is not cleared
|
||||
* @param add_to_padlist = true to fill m_PadsConnected member of each track
|
||||
* @param add_to_tracklist = true to fill m_TracksConnected member of each pad
|
||||
*/
|
||||
void SearchTracksConnectedToPads( bool add_to_padlist = true, bool add_to_tracklist = true);
|
||||
|
||||
/**
|
||||
* function CollectItemsNearTo
|
||||
* Used by SearchTracksConnectedToPads
|
||||
* Fills aList with pads near to aPosition
|
||||
* near means aPosition to pad position <= aDistMax
|
||||
* @param aList = list to fill
|
||||
* @param aPosition = aPosition to use as reference
|
||||
* @param aDistMax = dist max from aPosition to a candidate to select it
|
||||
*/
|
||||
void CollectItemsNearTo( std::vector<CONNECTED_POINT*>& aList,
|
||||
const wxPoint& aPosition, int aDistMax );
|
||||
|
||||
/**
|
||||
* Function Propagate_SubNets
|
||||
* Test a list of tracks, to create or propagate a sub netcode to pads and
|
||||
* segments connected together.
|
||||
* The track list must be sorted by nets, and all segments
|
||||
* from m_firstTrack to m_lastTrack have the same net.
|
||||
* When 2 items are connected (a track to a pad, or a track to an other track),
|
||||
* they are grouped in a cluster.
|
||||
* For pads, this is the .m_physical_connexion member which is a cluster identifier
|
||||
* For tracks, this is the .m_Subnet member which is a cluster identifier
|
||||
* For a given net, if all tracks are created, there is only one cluster.
|
||||
* but if not all tracks are created, there are more than one cluster,
|
||||
* and some ratsnests will be left active.
|
||||
*/
|
||||
void Propagate_SubNets();
|
||||
|
||||
private:
|
||||
/**
|
||||
* function searchEntryPointInCandidatesList
|
||||
* Search an item in m_Connected connected to aPoint
|
||||
* note m_Connected containts usually more than one candidate
|
||||
* and searchEntryPointInCandidatesList returns an index to one of these candidates
|
||||
* Others are neightbor of the indexed item.
|
||||
* @param aPoint is the reference coordinates
|
||||
* @return the index of item found or -1 if no candidate
|
||||
*/
|
||||
int searchEntryPointInCandidatesList( const wxPoint & aPoint);
|
||||
|
||||
/**
|
||||
* Function Merge_SubNets
|
||||
* Change a subnet old value to a new value, for tracks and pads which are connected to
|
||||
* tracks from m_firstTrack to m_lastTrack and their connected pads.
|
||||
* and modify the subnet parameter (change the old value to the new value).
|
||||
* After that, 2 cluster (or subnets) are merged into only one.
|
||||
* Note: the resulting sub net value is the smallest between aOldSubNet and aNewSubNet
|
||||
* @return modification count
|
||||
* @param aOldSubNet = subnet value to modify
|
||||
* @param aNewSubNet = new subnet value for each item which have old_val as subnet value
|
||||
*/
|
||||
int Merge_SubNets( int aOldSubNet, int aNewSubNet );
|
||||
|
||||
/**
|
||||
* Function Merge_PadsSubNets
|
||||
* Change a subnet value to a new value, in m_sortedPads pad list
|
||||
* After that, 2 cluster (or subnets) are merged into only one.
|
||||
* Note: the resulting subnet value is the smallest between aOldSubNet et aNewSubNet
|
||||
* @return modification count
|
||||
* @param aOldSubNet = subnet value to modify
|
||||
* @param aNewSubNet = new subnet value for each item which have old_val as subnet value
|
||||
*/
|
||||
int Merge_PadsSubNets( int aOldSubNet, int aNewSubNet );
|
||||
};
|
||||
|
||||
#endif // ifndef CONNECT_H
|
||||
|
@ -21,8 +21,6 @@
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
#define PROFILE
|
||||
|
||||
#ifdef PROFILE
|
||||
#include <profile.h>
|
||||
#endif
|
||||
@ -127,7 +125,6 @@ void CONNECTIVITY_DATA::updateRatsnest()
|
||||
#ifdef PROFILE
|
||||
rnUpdate.Show();
|
||||
#endif /* PROFILE */
|
||||
printf( "Dirty: %d\n", nDirty );
|
||||
}
|
||||
|
||||
|
||||
@ -165,6 +162,7 @@ void CONNECTIVITY_DATA::RecalculateRatsnest()
|
||||
|
||||
|
||||
|
||||
|
||||
for( auto c : clusters )
|
||||
{
|
||||
int net = c->OriginNet();
|
||||
@ -181,7 +179,7 @@ void CONNECTIVITY_DATA::RecalculateRatsnest()
|
||||
}
|
||||
|
||||
|
||||
void CONNECTIVITY_DATA::blockRatsnestItems( const std::vector<BOARD_ITEM*>& aItems )
|
||||
void CONNECTIVITY_DATA::BlockRatsnestItems( const std::vector<BOARD_ITEM*>& aItems )
|
||||
{
|
||||
std::vector<BOARD_CONNECTED_ITEM*> citems;
|
||||
|
||||
@ -231,7 +229,7 @@ void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector<BOARD_ITEM*>&
|
||||
|
||||
m_dynamicRatsnest.clear();
|
||||
|
||||
blockRatsnestItems( aItems );
|
||||
BlockRatsnestItems( aItems );
|
||||
|
||||
for( unsigned int nc = 1; nc < m_dynamicConnectivity->m_nets.size(); nc++ )
|
||||
{
|
||||
@ -333,45 +331,182 @@ const std::list<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetConnectedItems(
|
||||
const KICAD_T aTypes[] ) const
|
||||
{
|
||||
std::list<BOARD_CONNECTED_ITEM*> rv;
|
||||
const auto clusters = m_connAlgo->SearchClusters( CN_CONNECTIVITY_ALGO::CSM_CONNECTIVITY_CHECK, aTypes, aItem->GetNetCode() );
|
||||
const auto clusters = m_connAlgo->SearchClusters( CN_CONNECTIVITY_ALGO::CSM_CONNECTIVITY_CHECK,
|
||||
aTypes, aItem->GetNetCode() );
|
||||
|
||||
for ( auto cl : clusters )
|
||||
if ( cl->Contains (aItem ) )
|
||||
for( auto cl : clusters )
|
||||
if( cl->Contains( aItem ) )
|
||||
{
|
||||
for ( const auto item : *cl )
|
||||
for( const auto item : *cl )
|
||||
rv.push_back( item->Parent() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
const std::list<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetNetItems(
|
||||
int aNetCode,
|
||||
const std::list<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetNetItems( int aNetCode,
|
||||
const KICAD_T aTypes[] ) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool CONNECTIVITY_DATA::CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport )
|
||||
{
|
||||
RecalculateRatsnest();
|
||||
|
||||
for ( auto net : m_nets )
|
||||
for( auto net : m_nets )
|
||||
{
|
||||
if ( net )
|
||||
if( net )
|
||||
{
|
||||
for ( const auto& edge: net->GetEdges() )
|
||||
for( const auto& edge : net->GetEdges() )
|
||||
{
|
||||
CN_DISJOINT_NET_ENTRY ent;
|
||||
ent.net = edge.GetSourceNode()->Parent()->GetNetCode();
|
||||
ent.a = edge.GetSourceNode()->Parent();
|
||||
ent.b = edge.GetTargetNode()->Parent();
|
||||
ent.a = edge.GetSourceNode()->Parent();
|
||||
ent.b = edge.GetTargetNode()->Parent();
|
||||
ent.anchorA = edge.GetSourceNode()->Pos();
|
||||
ent.anchorB = edge.GetTargetNode()->Pos();
|
||||
aReport.push_back( ent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aReport.empty();
|
||||
}
|
||||
|
||||
|
||||
const std::vector<TRACK*> CONNECTIVITY_DATA::GetConnectedTracks( const BOARD_CONNECTED_ITEM* aItem )
|
||||
const
|
||||
{
|
||||
auto& entry = m_connAlgo->ItemEntry( aItem );
|
||||
|
||||
std::set<TRACK*> tracks;
|
||||
std::vector<TRACK*> rv;
|
||||
|
||||
for( auto citem : entry.GetItems() )
|
||||
{
|
||||
for( auto connected : citem->ConnectedItems() )
|
||||
{
|
||||
if( connected->Parent()->Type() == PCB_TRACE_T )
|
||||
tracks.insert( static_cast<TRACK*> ( connected->Parent() ) );
|
||||
}
|
||||
}
|
||||
|
||||
std::copy( tracks.begin(), tracks.end(), std::back_inserter( rv ) );
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
const std::vector<D_PAD*> CONNECTIVITY_DATA::GetConnectedPads( const BOARD_CONNECTED_ITEM* aItem )
|
||||
const
|
||||
{
|
||||
auto& entry = m_connAlgo->ItemEntry( aItem );
|
||||
|
||||
std::set<D_PAD*> pads;
|
||||
std::vector<D_PAD*> rv;
|
||||
|
||||
for( auto citem : entry.GetItems() )
|
||||
{
|
||||
for( auto connected : citem->ConnectedItems() )
|
||||
{
|
||||
if( connected->Parent()->Type() == PCB_PAD_T )
|
||||
pads.insert( static_cast<D_PAD*> ( connected->Parent() ) );
|
||||
}
|
||||
}
|
||||
|
||||
std::copy( pads.begin(), pads.end(), std::back_inserter( rv ) );
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
unsigned int CONNECTIVITY_DATA::GetLinksCount() const
|
||||
{
|
||||
return 0;
|
||||
assert( false );
|
||||
}
|
||||
|
||||
|
||||
unsigned int CONNECTIVITY_DATA::GetConnectedCount() const
|
||||
{
|
||||
return 0;
|
||||
assert( false );
|
||||
}
|
||||
|
||||
|
||||
unsigned int CONNECTIVITY_DATA::GetNodeCount( int aNet ) const
|
||||
{
|
||||
return 0;
|
||||
assert( false );
|
||||
}
|
||||
|
||||
|
||||
unsigned int CONNECTIVITY_DATA::GetPadCount( int aNet ) const
|
||||
{
|
||||
return 0;
|
||||
assert( false );
|
||||
}
|
||||
|
||||
|
||||
const std::vector<VECTOR2I> CONNECTIVITY_DATA::NearestUnconnectedTargets(
|
||||
const BOARD_CONNECTED_ITEM* aRef,
|
||||
const VECTOR2I& aPos,
|
||||
int aNet )
|
||||
{
|
||||
CN_CLUSTER_PTR refCluster;
|
||||
int refNet = -1;
|
||||
|
||||
if( aRef )
|
||||
refNet = aRef->GetNetCode();
|
||||
|
||||
if( aNet >= 0 )
|
||||
refNet = aNet;
|
||||
|
||||
if( aRef )
|
||||
{
|
||||
for( auto cl : m_connAlgo->GetClusters() )
|
||||
{
|
||||
if( cl->Contains( aRef ) )
|
||||
{
|
||||
refCluster = cl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set <VECTOR2I> anchors;
|
||||
|
||||
for( auto cl : m_connAlgo->GetClusters() )
|
||||
{
|
||||
if( cl != refCluster )
|
||||
{
|
||||
for( auto item : *cl )
|
||||
{
|
||||
if( item->Parent()->GetNetCode() == refNet
|
||||
&& item->Parent()->Type() != PCB_ZONE_AREA_T )
|
||||
for( auto anchor : item->Anchors() )
|
||||
{
|
||||
anchors.insert( anchor->Pos() );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<VECTOR2I> rv;
|
||||
|
||||
std::copy( anchors.begin(), anchors.end(), std::back_inserter( rv ) );
|
||||
std::sort( rv.begin(), rv.end(), [aPos] ( const VECTOR2I& a, const VECTOR2I& b )
|
||||
{
|
||||
auto da = (a - aPos).EuclideanNorm();
|
||||
auto db = (b - aPos).EuclideanNorm();
|
||||
|
||||
return da < db;
|
||||
} );
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ class BOARD_ITEM;
|
||||
class ZONE_CONTAINER;
|
||||
class RN_DATA;
|
||||
class RN_NET;
|
||||
class TRACK;
|
||||
class D_PAD;
|
||||
|
||||
struct CN_DISJOINT_NET_ENTRY
|
||||
{
|
||||
@ -130,7 +132,7 @@ public:
|
||||
*/
|
||||
void PropagateNets();
|
||||
|
||||
bool CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport );
|
||||
bool CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport );
|
||||
|
||||
/**
|
||||
* Function FindIsolatedCopperIslands()
|
||||
@ -152,6 +154,18 @@ public:
|
||||
*/
|
||||
unsigned int GetUnconnectedCount() const;
|
||||
|
||||
unsigned int GetLinksCount() const;
|
||||
|
||||
unsigned int GetConnectedCount() const;
|
||||
|
||||
|
||||
unsigned int GetNodeCount( int aNet = -1 ) const;
|
||||
|
||||
unsigned int GetPadCount( int aNet = -1 ) const;
|
||||
|
||||
const std::vector<TRACK*> GetConnectedTracks( const BOARD_CONNECTED_ITEM* aItem ) const;
|
||||
|
||||
const std::vector<D_PAD*> GetConnectedPads( const BOARD_CONNECTED_ITEM* aItem ) const;
|
||||
|
||||
/**
|
||||
* Function ClearDynamicRatsnest()
|
||||
@ -188,11 +202,16 @@ public:
|
||||
const std::list<BOARD_CONNECTED_ITEM*> GetNetItems( int aNetCode,
|
||||
const KICAD_T aTypes[] ) const;
|
||||
|
||||
const std::vector<VECTOR2I> NearestUnconnectedTargets( const BOARD_CONNECTED_ITEM* aRef,
|
||||
const VECTOR2I& aPos,
|
||||
int aMaxCount = -1 );
|
||||
|
||||
void BlockRatsnestItems( const std::vector<BOARD_ITEM*>& aItems );
|
||||
|
||||
private:
|
||||
|
||||
void updateRatsnest();
|
||||
void addRatsnestCluster( std::shared_ptr<CN_CLUSTER> aCluster );
|
||||
void blockRatsnestItems( const std::vector<BOARD_ITEM*>& aItems );
|
||||
|
||||
std::unique_ptr<CONNECTIVITY_DATA> m_dynamicConnectivity;
|
||||
std::shared_ptr<CN_CONNECTIVITY_ALGO> m_connAlgo;
|
||||
|
@ -160,10 +160,31 @@ public:
|
||||
void SetTargetNode( const CN_ANCHOR_PTR& aNode ) { m_target = aNode; }
|
||||
void SetWeight( unsigned int weight ) { m_weight = weight; }
|
||||
|
||||
void SetVisible( bool aVisible )
|
||||
{
|
||||
m_visible = aVisible;
|
||||
}
|
||||
|
||||
bool IsVisible() const
|
||||
{
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
const VECTOR2I GetSourcePos() const
|
||||
{
|
||||
return m_source->Pos();
|
||||
}
|
||||
|
||||
const VECTOR2I GetTargetPos() const
|
||||
{
|
||||
return m_target->Pos();
|
||||
}
|
||||
|
||||
private:
|
||||
CN_ANCHOR_PTR m_source;
|
||||
CN_ANCHOR_PTR m_target;
|
||||
unsigned int m_weight = 0;
|
||||
bool m_visible = true;
|
||||
};
|
||||
|
||||
class CN_CLUSTER
|
||||
|
@ -238,10 +238,6 @@ void DIALOG_GLOBAL_DELETION::AcceptPcbDelete()
|
||||
if( gen_rastnest )
|
||||
m_Parent->Compile_Ratsnest( NULL, true );
|
||||
|
||||
// There is a chance that some of tracks have changed their nets, so rebuild ratsnest from scratch
|
||||
if( m_Parent->IsGalCanvasActive() )
|
||||
pcb->GetRatsnest()->ProcessBoard();
|
||||
else
|
||||
m_Parent->GetCanvas()->Refresh();
|
||||
|
||||
// There is a chance that some of tracks have changed their nets, so rebuild ratsnest from scratch
|
||||
m_Parent->GetCanvas()->Refresh();
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <view/view.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <connectivity.h>
|
||||
|
||||
#define COL_NETNAME 0
|
||||
#define COL_NETINFO 1
|
||||
@ -141,7 +142,9 @@ void DIALOG_SELECT_NET_FROM_LIST::buildNetsList()
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !m_cbShowZeroPad->IsChecked() && net->m_PadInNetList.size() == 0 )
|
||||
unsigned nPads = m_brd->GetConnectivity()->GetPadCount( netcode );
|
||||
|
||||
if( !m_cbShowZeroPad->IsChecked() && nPads == 0 )
|
||||
continue;
|
||||
|
||||
if( m_netsListGrid->GetNumberRows() <= row_idx )
|
||||
@ -154,7 +157,7 @@ void DIALOG_SELECT_NET_FROM_LIST::buildNetsList()
|
||||
|
||||
if( netcode )
|
||||
{
|
||||
txt.Printf( wxT( "%u" ), (unsigned) net->m_PadInNetList.size() );
|
||||
txt.Printf( wxT( "%u" ), nPads );
|
||||
m_netsListGrid->SetCellValue( row_idx, COL_NETINFO, txt );
|
||||
}
|
||||
else // For the net 0 (unconnected pads), the pad count is not known
|
||||
|
@ -150,7 +150,7 @@ private:
|
||||
/** Fills m_DragList with of track segments connected to pads in aConnections
|
||||
* For each selected track segment the EDIT flag is set
|
||||
*/
|
||||
void fillList( CONNECTIONS& aConnections );
|
||||
void fillList( std::vector<D_PAD*>& aList );
|
||||
};
|
||||
|
||||
|
||||
|
@ -41,8 +41,7 @@
|
||||
|
||||
#include <class_module.h>
|
||||
#include <class_board.h>
|
||||
#include <connect.h>
|
||||
|
||||
#include <connectivity.h>
|
||||
|
||||
/* a list of DRAG_SEGM_PICKER items used to move or drag tracks */
|
||||
std::vector<DRAG_SEGM_PICKER> g_DragSegmentList;
|
||||
@ -61,7 +60,6 @@ DRAG_SEGM_PICKER::DRAG_SEGM_PICKER( TRACK* aTrack )
|
||||
m_Flipped = false;
|
||||
}
|
||||
|
||||
|
||||
void DRAG_SEGM_PICKER::SetAuxParameters()
|
||||
{
|
||||
MODULE* module = NULL;
|
||||
@ -155,16 +153,14 @@ void DRAG_LIST::BuildDragListe( MODULE* aModule )
|
||||
m_Pad = NULL;
|
||||
m_Module = aModule;
|
||||
|
||||
// Build connections info
|
||||
CONNECTIONS connections( m_Brd );
|
||||
std::vector<D_PAD*>&padList = connections.GetPadsList();
|
||||
std::vector<D_PAD*> padList;
|
||||
|
||||
for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
|
||||
for ( auto pad : aModule->PadsIter() )
|
||||
padList.push_back( pad );
|
||||
|
||||
sort( padList.begin(), padList.end(), sortPadsByXthenYCoord );
|
||||
|
||||
fillList( connections );
|
||||
fillList( padList );
|
||||
}
|
||||
|
||||
|
||||
@ -174,11 +170,10 @@ void DRAG_LIST::BuildDragListe( D_PAD* aPad )
|
||||
m_Module = NULL;
|
||||
|
||||
// Build connections info
|
||||
CONNECTIONS connections( m_Brd );
|
||||
std::vector<D_PAD*>&padList = connections.GetPadsList();
|
||||
std::vector<D_PAD*> padList;
|
||||
padList.push_back( aPad );
|
||||
|
||||
fillList( connections );
|
||||
fillList( padList );
|
||||
}
|
||||
|
||||
|
||||
@ -188,26 +183,18 @@ bool sort_tracklist( const DRAG_SEGM_PICKER& ref, const DRAG_SEGM_PICKER& tst )
|
||||
return ref.m_Track < tst.m_Track;
|
||||
}
|
||||
|
||||
|
||||
void DRAG_LIST::fillList( CONNECTIONS& aConnections )
|
||||
void DRAG_LIST::fillList( std::vector<D_PAD*>& aList )
|
||||
{
|
||||
aConnections.BuildTracksCandidatesList( m_Brd->m_Track, NULL);
|
||||
|
||||
// Build connections info tracks to pads
|
||||
// Rebuild pads to track info only)
|
||||
aConnections.SearchTracksConnectedToPads( false, true );
|
||||
|
||||
std::vector<D_PAD*>padList = aConnections.GetPadsList();
|
||||
|
||||
printf("FillList!\n");
|
||||
// clear flags and variables of selected tracks
|
||||
for( unsigned ii = 0; ii < padList.size(); ii++ )
|
||||
for( auto pad : aList )
|
||||
{
|
||||
D_PAD * pad = padList[ii];
|
||||
auto connectedTracks = m_Brd->GetConnectivity()->GetConnectedTracks( pad );
|
||||
|
||||
// store track connected to the pad
|
||||
for( unsigned jj = 0; jj < pad->m_TracksConnected.size(); jj++ )
|
||||
for ( auto track : connectedTracks )
|
||||
{
|
||||
TRACK * track = pad->m_TracksConnected[jj];
|
||||
track->start = NULL;
|
||||
track->end = NULL;
|
||||
track->SetState( START_ON_PAD|END_ON_PAD|BUSY, false );
|
||||
@ -215,14 +202,13 @@ void DRAG_LIST::fillList( CONNECTIONS& aConnections )
|
||||
}
|
||||
|
||||
// store tracks connected to pads
|
||||
for( unsigned ii = 0; ii < padList.size(); ii++ )
|
||||
for( auto pad : aList )
|
||||
{
|
||||
D_PAD * pad = padList[ii];
|
||||
auto connectedTracks = m_Brd->GetConnectivity()->GetConnectedTracks( pad );
|
||||
|
||||
// store track connected to the pad
|
||||
for( unsigned jj = 0; jj < pad->m_TracksConnected.size(); jj++ )
|
||||
for ( auto track : connectedTracks )
|
||||
{
|
||||
TRACK * track = pad->m_TracksConnected[jj];
|
||||
|
||||
if( pad->HitTest( track->GetStart() ) )
|
||||
{
|
||||
|
@ -196,19 +196,6 @@ void DRC::RunTests( wxTextCtrl* aMessages )
|
||||
// ( the board can be reloaded )
|
||||
m_pcb = m_pcbEditorFrame->GetBoard();
|
||||
|
||||
// Ensure ratsnest is up to date:
|
||||
if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
|
||||
{
|
||||
if( aMessages )
|
||||
{
|
||||
aMessages->AppendText( _( "Compile ratsnest...\n" ) );
|
||||
wxSafeYield();
|
||||
}
|
||||
|
||||
m_pcbEditorFrame->Compile_Ratsnest( NULL, true );
|
||||
//m_pcb->GetRatsnest()->ProcessBoard();
|
||||
}
|
||||
|
||||
// someone should have cleared the two lists before calling this.
|
||||
|
||||
if( !testNetClasses() )
|
||||
@ -636,7 +623,7 @@ void DRC::testZones()
|
||||
// perhaps a "dead" net, which happens when all pads in this net were removed
|
||||
// Remark: a netcode < 0 should not happen (this is more a bug somewhere)
|
||||
int pads_in_net = (test_area->GetNetCode() > 0) ?
|
||||
test_area->GetNet()->GetNodesCount() : 1;
|
||||
m_pcb->GetConnectivity()->GetPadCount( test_area->GetNetCode() ) : 1;
|
||||
|
||||
if( ( netcode < 0 ) || pads_in_net == 0 )
|
||||
{
|
||||
|
@ -663,8 +663,6 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
|
||||
}
|
||||
|
||||
SetCurItem( NULL ); // CurItem might be deleted by this command, clear the pointer
|
||||
TestConnections();
|
||||
TestForActiveLinksInRatsnest( 0 ); // Recalculate the active ratsnest, i.e. the unconnected links
|
||||
OnModify();
|
||||
SetMsgPanel( GetBoard() );
|
||||
m_canvas->Refresh();
|
||||
@ -673,7 +671,6 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
|
||||
case ID_POPUP_PCB_FILL_ZONE:
|
||||
m_canvas->MoveCursorToCrossHair();
|
||||
Fill_Zone( (ZONE_CONTAINER*) GetCurItem() );
|
||||
TestNetConnection( NULL, ( (ZONE_CONTAINER*) GetCurItem() )->GetNetCode() );
|
||||
SetMsgPanel( GetBoard() );
|
||||
m_canvas->Refresh();
|
||||
break;
|
||||
@ -1449,11 +1446,7 @@ void PCB_EDIT_FRAME::OnSelectTool( wxCommandEvent& aEvent )
|
||||
SetToolID( id, wxCURSOR_PENCIL, _( "Add tracks" ) );
|
||||
else
|
||||
SetToolID( id, wxCURSOR_QUESTION_ARROW, _( "Add tracks" ) );
|
||||
|
||||
if( (GetBoard()->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
|
||||
{
|
||||
Compile_Ratsnest( &dc, true );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -1523,8 +1516,7 @@ void PCB_EDIT_FRAME::OnSelectTool( wxCommandEvent& aEvent )
|
||||
case ID_PCB_SHOW_1_RATSNEST_BUTT:
|
||||
SetToolID( id, wxCURSOR_HAND, _( "Select rats nest" ) );
|
||||
|
||||
if( ( GetBoard()->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK ) == 0 )
|
||||
Compile_Ratsnest( &dc, true );
|
||||
Compile_Ratsnest( &dc, true );
|
||||
|
||||
break;
|
||||
|
||||
|
@ -227,9 +227,9 @@ void PCB_EDIT_FRAME::Show_1_Ratsnest( EDA_ITEM* item, wxDC* DC )
|
||||
if( GetBoard()->IsElementVisible( LAYER_RATSNEST ) )
|
||||
return;
|
||||
|
||||
if( ( GetBoard()->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK ) == 0 )
|
||||
Compile_Ratsnest( DC, true );
|
||||
Compile_Ratsnest( DC, true );
|
||||
|
||||
#if 0
|
||||
if( item )
|
||||
{
|
||||
if( item->Type() == PCB_PAD_T )
|
||||
@ -311,4 +311,5 @@ void PCB_EDIT_FRAME::Show_1_Ratsnest( EDA_ITEM* item, wxDC* DC )
|
||||
for( unsigned ii = 0; ii < GetBoard()->GetRatsnestsCount(); ii++ )
|
||||
GetBoard()->m_FullRatsnest[ii].m_Status &= ~CH_VISIBLE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <class_board.h>
|
||||
#include <class_track.h>
|
||||
#include <class_zone.h>
|
||||
#include <connectivity.h>
|
||||
|
||||
|
||||
static void Abort_Create_Track( EDA_DRAW_PANEL* panel, wxDC* DC );
|
||||
@ -158,7 +159,11 @@ TRACK* PCB_EDIT_FRAME::Begin_Route( TRACK* aTrack, wxDC* aDC )
|
||||
|
||||
DBG( g_CurrentTrackList.VerifyListIntegrity() );
|
||||
|
||||
BuildAirWiresTargetsList( lockPoint, wxPoint( 0, 0 ), true );
|
||||
int net = -1;
|
||||
if (lockPoint)
|
||||
net = lockPoint->GetNetCode();
|
||||
|
||||
BuildAirWiresTargetsList( lockPoint, wxPoint( 0, 0 ), net );
|
||||
|
||||
DBG( g_CurrentTrackList.VerifyListIntegrity() );
|
||||
|
||||
@ -183,7 +188,6 @@ TRACK* PCB_EDIT_FRAME::Begin_Route( TRACK* aTrack, wxDC* aDC )
|
||||
|
||||
if( pad )
|
||||
{
|
||||
g_CurrentTrackSegment->m_PadsConnected.push_back( pad );
|
||||
// Useful to display track length, if the pad has a die length:
|
||||
g_CurrentTrackSegment->SetState( BEGIN_ONPAD, true );
|
||||
g_CurrentTrackSegment->start = pad;
|
||||
@ -268,12 +272,6 @@ TRACK* PCB_EDIT_FRAME::Begin_Route( TRACK* aTrack, wxDC* aDC )
|
||||
|
||||
D_PAD* pad = GetBoard()->GetPad( previousTrack, ENDPOINT_END );
|
||||
|
||||
if( pad )
|
||||
{
|
||||
newTrack->m_PadsConnected.push_back( pad );
|
||||
previousTrack->m_PadsConnected.push_back( pad );
|
||||
}
|
||||
|
||||
newTrack->start = previousTrack->end;
|
||||
|
||||
DBG( g_CurrentTrackList.VerifyListIntegrity(); );
|
||||
@ -489,6 +487,7 @@ bool PCB_EDIT_FRAME::End_Route( TRACK* aTrack, wxDC* aDC )
|
||||
ITEM_PICKER picker( track, UR_NEW );
|
||||
s_ItemsListPicker.PushItem( picker );
|
||||
GetBoard()->m_Track.Insert( track, insertBeforeMe );
|
||||
GetBoard()->GetConnectivity()->Add( track );
|
||||
}
|
||||
|
||||
TraceAirWiresToTargets( aDC );
|
||||
@ -534,6 +533,8 @@ bool PCB_EDIT_FRAME::End_Route( TRACK* aTrack, wxDC* aDC )
|
||||
m_canvas->SetMouseCapture( NULL, NULL );
|
||||
SetCurItem( NULL );
|
||||
|
||||
GetBoard()->GetConnectivity()->RecalculateRatsnest();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -825,7 +826,7 @@ void ShowNewTrackWhenMovingCursor( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPo
|
||||
displ_opts->m_ShowTrackClearanceMode = showTrackClearanceMode;
|
||||
displ_opts->m_DisplayPcbTrackFill = tmp;
|
||||
|
||||
frame->BuildAirWiresTargetsList( NULL, g_CurrentTrackSegment->GetEnd(), false );
|
||||
frame->BuildAirWiresTargetsList( NULL, g_CurrentTrackSegment->GetEnd(), g_CurrentTrackSegment->GetNetCode() );
|
||||
frame->TraceAirWiresToTargets( aDC );
|
||||
}
|
||||
|
||||
|
@ -701,8 +701,6 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
|
||||
backupFileName = create_backup_file( aFileName );
|
||||
}
|
||||
|
||||
GetBoard()->m_Status_Pcb &= ~CONNEXION_OK;
|
||||
|
||||
GetBoard()->SynchronizeNetsAndNetClasses();
|
||||
|
||||
// Select default Netclass before writing file.
|
||||
@ -787,7 +785,6 @@ bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName )
|
||||
return false;
|
||||
}
|
||||
|
||||
GetBoard()->m_Status_Pcb &= ~CONNEXION_OK;
|
||||
GetBoard()->SynchronizeNetsAndNetClasses();
|
||||
|
||||
// Select default Netclass before writing file.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user