Connectivity: local ratsnest for both legacy & GAL. Further removal of legacy code

This commit is contained in:
Tomasz Włostowski 2017-03-28 18:30:49 +02:00
parent 07b7600ae5
commit c4c329e393
17 changed files with 131 additions and 684 deletions

9
.gitignore vendored
View File

@ -62,3 +62,12 @@ demos/**/_autosave-*
.*.swp
*~
.DS_Store
*.png
*.kiface
*.o
*.a
*.cmake
*.orig
*.rej
*.so
*.old

View File

@ -37,8 +37,7 @@
#include <connectivity.h>
BOARD_CONNECTED_ITEM::BOARD_CONNECTED_ITEM( BOARD_ITEM* aParent, KICAD_T idtype ) :
BOARD_ITEM( aParent, idtype ), m_netinfo( &NETINFO_LIST::ORPHANED_ITEM ),
m_Subnet( 0 ), m_ZoneSubnet( 0 )
BOARD_ITEM( aParent, idtype ), m_netinfo( &NETINFO_LIST::ORPHANED_ITEM )
{
}

View File

@ -116,34 +116,6 @@ public:
*/
bool SetNetCode( int aNetCode, bool aNoAssert=false );
/**
* Function GetSubNet
* @return int - the sub net code.
*/
int GetSubNet() const
{
return m_Subnet;
}
void SetSubNet( int aSubNetCode )
{
m_Subnet = aSubNetCode;
}
/**
* Function GetZoneSubNet
* @return int - the sub net code in zone connections.
*/
int GetZoneSubNet() const
{
return m_ZoneSubnet;
}
void SetZoneSubNet( int aSubNetCode )
{
m_ZoneSubnet = aSubNetCode;
}
/**
* Function GetNetname
* @return wxString - the full netname
@ -189,17 +161,23 @@ public:
*/
wxString GetNetClassName() const;
void SetLocalRatsnestVisible( bool aVisible )
{
m_localRatsnestVisible = aVisible;
}
bool GetLocalRatsnestVisible() const
{
return m_localRatsnestVisible;
}
protected:
/// Stores all informations about the net that item belongs to
NETINFO_ITEM* m_netinfo;
private:
int m_Subnet; /* In rastnest routines : for the current net, block number
* (number common to the current connected items found)
*/
int m_ZoneSubnet; // used in rastnest computations : for the current net,
// handle cluster number in zone connection
bool m_localRatsnestVisible;
};

View File

@ -658,13 +658,6 @@ void D_PAD::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM>& aList )
aList.push_back( MSG_PANEL_ITEM( _( "Net" ), GetNetname(), DARKCYAN ) );
/* For test and debug only: display m_physical_connexion and
* m_logical_connexion */
#if 1 // Used only to debug connectivity calculations
Line.Printf( wxT( "%d-%d-%d " ), GetSubRatsnest(), GetSubNet(), GetZoneSubNet() );
aList.push_back( MSG_PANEL_ITEM( wxT( "L-P-Z" ), Line, DARKGREEN ) );
#endif
board = GetBoard();
aList.push_back( MSG_PANEL_ITEM( _( "Layer" ),

View File

@ -1094,7 +1094,7 @@ void TRACK::GetMsgPanelInfoBase_Common( std::vector< MSG_PANEL_ITEM >& aList )
aList.push_back( MSG_PANEL_ITEM( _( "NetName" ), msg, RED ) );
// Display net code : (useful in test or debug)
msg.Printf( wxT( "%d.%d" ), GetNetCode(), GetSubNet() );
msg.Printf( wxT( "%d" ), GetNetCode() );
aList.push_back( MSG_PANEL_ITEM( _( "NetCode" ), msg, RED ) );
}

View File

@ -95,72 +95,4 @@ static void RebuildTrackChain( BOARD* pcb )
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;
if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
Compile_Ratsnest( aDC, true );
// Clear the cluster identifier (subnet) of pads for this net
// Pads are grouped by netcode (and in netname alphabetic order)
for( unsigned i = 0; i < m_Pcb->GetPadCount(); ++i )
{
D_PAD* pad = m_Pcb->GetPad(i);
if( m_Pcb->GetPad(i)->GetNetCode() == aNetCode )
pad->SetSubNet( 0 );
}
m_Pcb->Test_Connections_To_Copper_Areas( aNetCode );
// Search for the first and the last segment relative to the given net code
if( m_Pcb->m_Track )
{
CONNECTIONS connections( m_Pcb );
TRACK* lastTrack = NULL;
TRACK* firstTrack = m_Pcb->m_Track.GetFirst()->GetStartNetCode( aNetCode );
if( firstTrack )
lastTrack = firstTrack->GetEndNetCode( aNetCode );
if( firstTrack && lastTrack ) // i.e. if there are segments
{
connections.Build_CurrNet_SubNets_Connections( firstTrack, lastTrack, aNetCode );
}
}
Merge_SubNets_Connected_By_CopperAreas( m_Pcb, aNetCode );
// rebuild the active ratsnest for this net
DrawGeneralRatsnest( aDC, aNetCode );
TestForActiveLinksInRatsnest( aNetCode );
DrawGeneralRatsnest( aDC, aNetCode );
// Display results
wxString msg;
int net_notconnected_count = 0;
NETINFO_ITEM* net = m_Pcb->FindNet( aNetCode );
if( net ) // Should not occur, but ...
{
for( unsigned ii = net->m_RatsnestStartIdx; ii < net->m_RatsnestEndIdx; ii++ )
{
if( m_Pcb->m_FullRatsnest[ii].IsActive() )
net_notconnected_count++;
}
msg.Printf( wxT( "links %d nc %d net %d: not conn %d" ),
m_Pcb->GetRatsnestsCount(), m_Pcb->GetUnconnectedNetCount(), aNetCode,
net_notconnected_count );
}
else
msg.Printf( wxT( "net not found: netcode %d" ), aNetCode );
SetStatusText( msg );
return;
#endif
}

View File

@ -285,6 +285,8 @@ const std::vector<RN_DYNAMIC_LINE>& CONNECTIVITY_DATA::GetDynamicRatsnest() cons
void CONNECTIVITY_DATA::ClearDynamicRatsnest()
{
m_connAlgo->ForEachAnchor( [] (CN_ANCHOR_PTR anchor ) { anchor->SetNoLine( false ); } );
m_dynamicConnectivity.reset();
m_dynamicRatsnest.clear();
}

View File

@ -480,7 +480,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
auto zoneItem = static_cast<CN_ZONE *> (item);
auto searchZones = std::bind( checkForConnection, _1, zoneItem );
if( zoneItem->Dirty() )
if( zoneItem->Dirty() || m_padList.IsDirty() || m_trackList.IsDirty() || m_viaList.IsDirty() )
{
totalDirtyCount++;
m_viaList.FindNearby( zoneItem->BBox(), searchZones );
@ -901,3 +901,15 @@ void CN_CONNECTIVITY_ALGO::Clear()
m_zoneList.Clear();
}
void CN_CONNECTIVITY_ALGO::ForEachAnchor( std::function<void(CN_ANCHOR_PTR)> aFunc )
{
for ( auto anchor : m_padList.Anchors() )
aFunc( anchor );
for ( auto anchor : m_viaList.Anchors() )
aFunc( anchor );
for ( auto anchor : m_trackList.Anchors() )
aFunc( anchor );
for ( auto anchor : m_zoneList.Anchors() )
aFunc( anchor );
}

View File

@ -504,6 +504,8 @@ public:
ITER begin() { return m_items.begin(); };
ITER end() { return m_items.end(); };
std::vector<CN_ANCHOR_PTR>& Anchors() { return m_anchors; }
template <class T>
void FindNearby( VECTOR2I aPosition, int aDistMax, T aFunc, bool aDirtyOnly = false );
@ -940,6 +942,8 @@ public:
const CLUSTERS& GetClusters();
int GetUnconnectedCount();
void ForEachAnchor( std::function<void(CN_ANCHOR_PTR)> aFunc );
};
bool operator<( const CN_ANCHOR_PTR a, const CN_ANCHOR_PTR b );

View File

@ -296,7 +296,6 @@ void PCB_EDIT_FRAME::Change_Side_Module( MODULE* Module, wxDC* DC )
if( !Module->IsMoving() ) /* This is a simple flip, no other edition in progress */
{
GetBoard()->m_Status_Pcb &= ~( LISTE_RATSNEST_ITEM_OK | CONNEXION_OK );
if( DC )
{

View File

@ -106,21 +106,22 @@ void PCB_BASE_FRAME::DrawGeneralRatsnest( wxDC* aDC, int aNetcode )
if( ( aNetcode <= 0 ) || ( aNetcode == i ) )
{
for( const auto& edge : net->GetEdges() )
{
if ( edge.IsVisible() )
{
auto s = edge.GetSourcePos();
auto d = edge.GetTargetPos();
auto sn = edge.GetSourceNode();
auto dn = edge.GetTargetNode();
if ( !sn->GetNoLine() && !dn->GetNoLine() )
GRLine( m_canvas->GetClipBox(), aDC, wxPoint(s.x, s.y), wxPoint(d.x, d.y), 0, color );
}
}
}
}
bool enable = !sn->GetNoLine() && !dn->GetNoLine();
bool show = sn->Parent()->GetLocalRatsnestVisible()
|| dn->Parent()->GetLocalRatsnestVisible();
if( enable && show )
GRLine( m_canvas->GetClipBox(), aDC, wxPoint( s.x, s.y ), wxPoint( d.x,
d.y ), 0, color );
}
}
}
}
@ -133,7 +134,8 @@ void PCB_BASE_FRAME::TraceModuleRatsNest( wxDC* DC )
for( const auto& l : GetBoard()->GetConnectivity()->GetDynamicRatsnest() )
{
GRLine( m_canvas->GetClipBox(), DC, wxPoint(l.a.x, l.a.y), wxPoint(l.b.x, l.b.y), 0, tmpcolor );
GRLine( m_canvas->GetClipBox(), DC, wxPoint( l.a.x, l.a.y ), wxPoint( l.b.x,
l.b.y ), 0, tmpcolor );
}
}
@ -175,13 +177,12 @@ void PCB_BASE_FRAME::BuildAirWiresTargetsList( BOARD_CONNECTED_ITEM* aItemRef,
}
static MODULE movedModule( nullptr );
void PCB_BASE_FRAME::build_ratsnest_module( MODULE* mod, wxPoint aMoveVector )
{
auto connectivity = GetBoard()->GetConnectivity();
movedModule = *mod;
movedModule.Move( -aMoveVector );
connectivity->ClearDynamicRatsnest();
@ -207,9 +208,9 @@ void PCB_BASE_FRAME::TraceAirWiresToTargets( wxDC* aDC )
auto p = targets[i];
GRLine( m_canvas->GetClipBox(), aDC, s_CursorPos, wxPoint( p.x, p.y ), 0, YELLOW );
}
}
// Redraw in XOR mode the outlines of the module.
void MODULE::DrawOutlinesWhenMoving( EDA_DRAW_PANEL* panel, wxDC* DC,
const wxPoint& aMoveVector )
@ -239,101 +240,18 @@ void MODULE::DrawOutlinesWhenMoving( EDA_DRAW_PANEL* panel, wxDC* DC,
}
}
void PCB_EDIT_FRAME::Show_1_Ratsnest( EDA_ITEM* item, wxDC* DC )
{
if( GetBoard()->IsElementVisible(RATSNEST_VISIBLE) )
return;
Compile_Ratsnest( DC, true );
printf("show1r: %p\n", item);
auto connectivity = GetBoard()->GetConnectivity();
// FIXME
#if 0
if( item )
if( item->Type() == PCB_MODULE_T )
{
if( item->Type() == PCB_PAD_T )
auto mod = static_cast<MODULE*> (item);
for( auto pad : mod->PadsIter() )
{
pt_pad = (D_PAD*) item;
Module = pt_pad->GetParent();
pad->SetLocalRatsnestVisible( true );
}
if( pt_pad ) // Displaying the ratsnest of the corresponding net.
{
SetMsgPanel( pt_pad );
for( unsigned ii = 0; ii < GetBoard()->GetRatsnestsCount(); ii++ )
{
RATSNEST_ITEM* net = &GetBoard()->m_FullRatsnest[ii];
if( net->GetNet() == pt_pad->GetNetCode() )
{
if( ( net->m_Status & CH_VISIBLE ) != 0 )
continue;
net->m_Status |= CH_VISIBLE;
if( ( net->m_Status & CH_ACTIF ) == 0 )
continue;
net->Draw( m_canvas, DC, GR_XOR, wxPoint( 0, 0 ) );
m_canvas->Refresh();
}
}
}
else
{
if( item->Type() == PCB_MODULE_TEXT_T )
{
if( item->GetParent() && ( item->GetParent()->Type() == PCB_MODULE_T ) )
Module = static_cast<MODULE*>( item->GetParent() );
}
else if( item->Type() == PCB_MODULE_T )
{
Module = static_cast<MODULE*>( item );
}
if( Module )
{
SetMsgPanel( Module );
pt_pad = Module->Pads();
for( ; pt_pad != NULL; pt_pad = pt_pad->Next() )
{
for( unsigned ii = 0; ii < GetBoard()->GetRatsnestsCount(); ii++ )
{
RATSNEST_ITEM* net = &GetBoard()->m_FullRatsnest[ii];
if( ( net->m_PadStart == pt_pad ) || ( net->m_PadEnd == pt_pad ) )
{
if( net->m_Status & CH_VISIBLE )
continue;
net->m_Status |= CH_VISIBLE;
if( (net->m_Status & CH_ACTIF) == 0 )
continue;
net->Draw( m_canvas, DC, GR_XOR, wxPoint( 0, 0 ) );
}
}
}
pt_pad = NULL;
}
}
}
// Erase if no pad or module has been selected.
if( ( pt_pad == NULL ) && ( Module == NULL ) )
{
DrawGeneralRatsnest( DC );
for( unsigned ii = 0; ii < GetBoard()->GetRatsnestsCount(); ii++ )
GetBoard()->m_FullRatsnest[ii].m_Status &= ~CH_VISIBLE;
}
#endif
}

View File

@ -55,16 +55,9 @@ const BOX2I RATSNEST_VIEWITEM::ViewBBox() const
return bbox;
}
#include <geometry/seg.h>
std::vector<SEG> delEdges;
void clearDEdges() { delEdges.clear(); }
void addDEdge ( SEG edge ) { delEdges.push_back(edge); }
void RATSNEST_VIEWITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
{
static const double crossSize = 100000.0;
constexpr int CROSS_SIZE = 200000;
auto gal = aView->GetGAL();
gal->SetIsStroke( true );
@ -76,23 +69,19 @@ void RATSNEST_VIEWITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
int highlightedNet = rs->GetHighlightNetCode();
gal->SetStrokeColor( color.Brightened( 0.8 ) );
for (auto s : delEdges)
gal->DrawLine( s.A, s.B );
// Draw the "dynamic" ratsnest (i.e. for objects that may be currently being moved)
for( const auto& l : m_data->GetDynamicRatsnest() )
{
if ( l.a == l.b )
{
gal->DrawLine( VECTOR2I( l.a.x - crossSize, l.a.y - crossSize ), VECTOR2I( l.b.x + crossSize, l.b.y + crossSize ) );
gal->DrawLine( VECTOR2I( l.a.x - crossSize, l.a.y + crossSize ), VECTOR2I( l.b.x + crossSize, l.b.y - crossSize ) );
gal->DrawLine( VECTOR2I( l.a.x - CROSS_SIZE, l.a.y - CROSS_SIZE ), VECTOR2I( l.b.x + CROSS_SIZE, l.b.y + CROSS_SIZE ) );
gal->DrawLine( VECTOR2I( l.a.x - CROSS_SIZE, l.a.y + CROSS_SIZE ), VECTOR2I( l.b.x + CROSS_SIZE, l.b.y - CROSS_SIZE ) );
} else {
gal->DrawLine( l.a, l.b );
}
}
// Dynamic ratsnest (for e.g. dragged items)
for( int i = 1; i < m_data->GetNetCount(); ++i )
{
RN_NET* net = m_data->GetRatsnestForNet( i );
@ -103,20 +92,21 @@ void RATSNEST_VIEWITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
for( const auto& edge : net->GetUnconnected() )
{
if ( !edge.IsVisible() )
continue;
//if ( !edge.IsVisible() )
// continue;
const auto& sourceNode = edge.GetSourceNode();
const auto& targetNode = edge.GetTargetNode();
const VECTOR2I source( sourceNode->Pos() );
const VECTOR2I target( targetNode->Pos() );
if ( !sourceNode->GetNoLine() && !targetNode->GetNoLine() )
bool enable = !sourceNode->GetNoLine() && !targetNode->GetNoLine();
bool show = sourceNode->Parent()->GetLocalRatsnestVisible() || targetNode->Parent()->GetLocalRatsnestVisible();
if ( enable && show )
{
if ( source == target )
{
constexpr int CROSS_SIZE = 200000;
gal->DrawLine( VECTOR2I( source.x - CROSS_SIZE, source.y - CROSS_SIZE ), VECTOR2I( source.x + CROSS_SIZE, source.y + CROSS_SIZE ) );
gal->DrawLine( VECTOR2I( source.x - CROSS_SIZE, source.y + CROSS_SIZE ), VECTOR2I( source.x + CROSS_SIZE, source.y - CROSS_SIZE ) );
}

View File

@ -588,11 +588,6 @@ TOOL_ACTION COMMON_ACTIONS::toBeDone( "pcbnew.Control.toBeDone",
AS_GLOBAL, 0, // dialog saying it is not implemented yet
"", "" ); // so users are aware of that
TOOL_ACTION COMMON_ACTIONS::showLocalRatsnest( "pcbnew.Control.showLocalRatsnest",
AS_GLOBAL, 0,
"", "" );
TOOL_ACTION COMMON_ACTIONS::routerActivateSingle( "pcbnew.InteractiveRouter.SingleTrack",
AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_NEW_TRACK ),
_( "Interactive Router (Single Tracks)" ),

View File

@ -162,7 +162,7 @@ boost::optional<TOOL_EVENT> PCB_ACTIONS::TranslateLegacyId( int aId )
return PCB_ACTIONS::appendBoard.MakeEvent();
case ID_PCB_SHOW_1_RATSNEST_BUTT:
return PCB_ACTIONS::toBeDone.MakeEvent();
return PCB_ACTIONS::showLocalRatsnest.MakeEvent();
case ID_PCB_MUWAVE_TOOL_GAP_CMD:
return PCB_ACTIONS::microwaveCreateGap.MakeEvent();

View File

@ -148,6 +148,10 @@ TOOL_ACTION PCB_ACTIONS::highlightNetCursor( "pcbnew.EditorControl.highlightNetC
AS_GLOBAL, 0,
"", "" );
TOOL_ACTION PCB_ACTIONS::showLocalRatsnest( "pcbnew.Control.showLocalRatsnest",
AS_GLOBAL, 0,
"", "" );
class ZONE_CONTEXT_MENU : public CONTEXT_MENU
{
@ -1058,25 +1062,45 @@ int PCB_EDITOR_CONTROL::HighlightNetCursor( const TOOL_EVENT& aEvent )
return 0;
}
static bool showLocalRatsnest( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
KIGFX::ORIGIN_VIEWITEM* aItem, const VECTOR2D& aPosition )
static bool showLocalRatsnest( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition )
{
aFrame->SetAuxOrigin( wxPoint( aPosition.x, aPosition.y ) );
aItem->SetPosition( aPosition );
aView->MarkDirty();
auto selectionTool = aToolMgr->GetTool<SELECTION_TOOL>();
aToolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
aToolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
const SELECTION& selection = selectionTool->GetSelection();
if( selection.Empty() )
return true;
for ( auto item : selection )
{
if ( item->Type() == PCB_MODULE_T )
{
for ( auto pad : static_cast<MODULE *> (item)->PadsIter() )
{
pad->SetLocalRatsnestVisible( true );
}
}
}
return true;
}
int PCB_EDITOR_CONTROL::ShowLocalRatsnest( const TOOL_EVENT& aEvent )
{
printf("ShowLocalRTool!\n");
Activate();
auto picker = m_toolMgr->GetTool<PICKER_TOOL>();
assert( picker );
m_frame->SetToolID( ID_PCB_SHOW_1_RATSNEST_BUTT, wxCURSOR_PENCIL, _( "Pick Components for Local Ratsnest" ) );
//picker->SetClickHandler( std::bind( showLocalRatsnest, m_toolMgr, _1 ) );
picker->SetClickHandler( std::bind( showLocalRatsnest, m_toolMgr, _1 ) );
picker->SetSnapping( false );
picker->Activate();
Wait();
@ -1086,7 +1110,7 @@ int PCB_EDITOR_CONTROL::ShowLocalRatsnest( const TOOL_EVENT& aEvent )
int PCB_EDITOR_CONTROL::UpdateSelectionRatsnest( const TOOL_EVENT& aEvent )
{
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
/* SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
const SELECTION& selection = selTool->GetSelection();
RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
@ -1099,7 +1123,7 @@ int PCB_EDITOR_CONTROL::UpdateSelectionRatsnest( const TOOL_EVENT& aEvent )
ratsnest->AddSimple( static_cast<BOARD_ITEM*>( item ) );
}
return 0;
return 0;*/
}
@ -1132,6 +1156,7 @@ void PCB_EDITOR_CONTROL::SetTransitions()
Go( &PCB_EDITOR_CONTROL::DrillOrigin, PCB_ACTIONS::drillOrigin.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::HighlightNet, PCB_ACTIONS::highlightNet.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::HighlightNetCursor, PCB_ACTIONS::highlightNetCursor.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::ShowLocalRatsnest, PCB_ACTIONS::showLocalRatsnest.MakeEvent() );
Go( &PCB_EDITOR_CONTROL::UpdateSelectionRatsnest, PCB_ACTIONS::selectionModified.MakeEvent() );
}

View File

@ -45,7 +45,7 @@
#include <protos.h>
#include <zones_functions_for_undo_redo.h>
#include <drc_stuff.h>
#include <ratsnest_data.h>
#include <connectivity.h>
// Outline creation:
static void Abort_Zone_Create_Outline( EDA_DRAW_PANEL* Panel, wxDC* DC );
@ -951,12 +951,12 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* aZone )
if( zone->IsFilled() )
{
Fill_Zone( zone );
GetBoard()->GetRatsnest()->Recalculate( zone->GetNetCode() );
}
}
commit.Stage( s_PickedList );
commit.Push( _( "Modify zone properties" ) );
GetBoard()->GetConnectivity()->RecalculateRatsnest();
s_PickedList.ClearItemsList(); // s_ItemsListPicker is no longer owner of picked items
}

View File

@ -1,409 +0,0 @@
/**
* @file zones_polygons_test_connections.cpp
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <algorithm> // sort
#include <fctsys.h>
#include <common.h>
#include <macros.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_zone.h>
#include <pcbnew.h>
#include <zones.h>
#include <polygon_test_point_inside.h>
static bool CmpZoneSubnetValue( const BOARD_CONNECTED_ITEM* a, const BOARD_CONNECTED_ITEM* b );
void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode );
// This helper function sort a list of zones by netcode,
// and for a given netcode by zone size
// zone size = size of the m_FilledPolysList buffer
bool sort_areas( const ZONE_CONTAINER* ref, const ZONE_CONTAINER* tst )
{
if( ref->GetNetCode() == tst->GetNetCode() )
return ref->GetFilledPolysList().TotalVertices() <
tst->GetFilledPolysList().TotalVertices();
else
return ref->GetNetCode() < tst->GetNetCode();
}
/**
* 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 analyse. if -1, analyse all nets
*/
void BOARD::Test_Connections_To_Copper_Areas( int aNetcode )
{
// list of pads and tracks candidates on this layer and on this net.
// It is static to avoid multiple memory realloc.
static std::vector <BOARD_CONNECTED_ITEM*> candidates;
// clear .m_ZoneSubnet parameter for pads
for( MODULE* module = m_Modules; module; module = module->Next() )
{
for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
if( aNetcode < 0 || aNetcode == pad->GetNetCode() )
pad->SetZoneSubNet( 0 );
}
// clear .m_ZoneSubnet parameter for tracks and vias
for( TRACK* track = m_Track; track; track = track->Next() )
{
if( aNetcode < 0 || aNetcode == track->GetNetCode() )
track->SetZoneSubNet( 0 );
}
// examine all zones, net by net:
int subnet = 0;
// Build zones candidates list
std::vector<ZONE_CONTAINER*> zones_candidates;
zones_candidates.reserve( GetAreaCount() );
for( int index = 0; index < GetAreaCount(); index++ )
{
ZONE_CONTAINER* zone = GetArea( index );
if( !zone->IsOnCopperLayer() )
continue;
if( aNetcode >= 0 && aNetcode != zone->GetNetCode() )
continue;
if( zone->GetFilledPolysList().IsEmpty() )
continue;
zones_candidates.push_back( zone );
}
// sort them by netcode then vertices count.
// For a given net, examine the smaller zones first slightly speed up calculation
// (25% faster)
// this is only noticeable with very large boards and depends on board zones topology
// This is due to the fact some items are connected by small zones ares,
// before examining large zones areas and these items are not tested after a connection is found
sort( zones_candidates.begin(), zones_candidates.end(), sort_areas );
int oldnetcode = -1;
for( unsigned idx = 0; idx < zones_candidates.size(); idx++ )
{
ZONE_CONTAINER* zone = zones_candidates[idx];
int netcode = zone->GetNetCode();
// Build a list of candidates connected to the net:
// At this point, layers are not considered, because areas on different layers can
// be connected by a via or a pad.
// (because zones are sorted by netcode, there is made only once per net)
NETINFO_ITEM* net = FindNet( netcode );
wxASSERT( net );
if( net == NULL )
continue;
if( oldnetcode != netcode )
{
oldnetcode = netcode;
candidates.clear();
// Build the list of pads candidates connected to the net:
candidates.reserve( net->m_PadInNetList.size() );
for( unsigned ii = 0; ii < net->m_PadInNetList.size(); ii++ )
candidates.push_back( net->m_PadInNetList[ii] );
// If we have any tracks...
if( m_Track.GetCount() > 0 )
{
// Build the list of track candidates connected to the net:
TRACK* track = m_Track.GetFirst()->GetStartNetCode( netcode );
for( ; track; track = track->Next() )
{
if( track->GetNetCode() != netcode )
break;
candidates.push_back( track );
}
}
}
// test if a candidate is inside a filled area of this zone
const SHAPE_POLY_SET& polysList = zone->GetFilledPolysList();
for( int outline = 0; outline < polysList.OutlineCount(); outline++ )
{
subnet++;
for( unsigned ic = 0; ic < candidates.size(); ic++ )
{
// test if this area is connected to a board item:
BOARD_CONNECTED_ITEM* item = candidates[ic];
if( item->GetZoneSubNet() == subnet ) // Already merged
continue;
if( !item->IsOnLayer( zone->GetLayer() ) )
continue;
wxPoint pos1, pos2;
if( item->Type() == PCB_PAD_T )
{
// For pads we use the shape position instead of
// the pad position, because the zones are connected
// to the center of the shape, not the pad position
// (this is important for pads with thermal relief)
pos1 = pos2 = ( (D_PAD*) item )->ShapePos();
}
else if( item->Type() == PCB_VIA_T )
{
const VIA *via = static_cast<const VIA*>( item );
pos1 = via->GetStart();
pos2 = pos1;
}
else if( item->Type() == PCB_TRACE_T )
{
const TRACK *trk = static_cast<const TRACK*>( item );
pos1 = trk->GetStart();
pos2 = trk->GetEnd();
}
else
{
continue;
}
bool connected = false;
if( polysList.Contains( VECTOR2I( pos1.x, pos1.y ), outline ) )
connected = true;
if( !connected && ( pos1 != pos2 ) )
{
if( polysList.Contains( VECTOR2I( pos2.x, pos2.y ), outline ) )
connected = true;
}
if( connected )
{
// Set ZoneSubnet to the current subnet value.
// If the previous subnet is not 0, merge all items with old subnet
// to the new one
int old_subnet = item->GetZoneSubNet();
item->SetZoneSubNet( subnet );
// Merge previous subnet with the current
if( (old_subnet > 0) && (old_subnet != subnet) )
{
for( unsigned jj = 0; jj < candidates.size(); jj++ )
{
BOARD_CONNECTED_ITEM* item_to_merge = candidates[jj];
if( old_subnet == item_to_merge->GetZoneSubNet() )
{
item_to_merge->SetZoneSubNet( subnet );
}
}
} // End if ( old_subnet > 0 )
} // End if( connected )
}
}
} // End read all zones candidates
}
/**
* Function Merge_SubNets_Connected_By_CopperAreas(BOARD* aPcb)
* Calls Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode ) for each
* netcode found in zone list
* @param aPcb = the current board
*/
void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb )
{
for( int index = 0; index < aPcb->GetAreaCount(); index++ )
{
ZONE_CONTAINER* zone = aPcb->GetArea( index );
if ( ! zone->IsOnCopperLayer() )
continue;
if ( zone->GetNetCode() <= 0 )
continue;
Merge_SubNets_Connected_By_CopperAreas( aPcb, zone->GetNetCode() );
}
}
/**
* Function Merge_SubNets_Connected_By_CopperAreas(BOARD* aPcb, int aNetcode)
* Used after connections by tracks calculations
* Merge subnets, in tracks ans pads when they are connected by a filled copper area
* for pads, this is the .m_physical_connexion member which is tested and modified
* for tracks, this is the .m_Subnet member which is tested and modified
* these members are block numbers (or cluster numbers) for a given net,
* calculated by Build_Pads_Info_Connections_By_Tracks()
* The result is merging 2 blocks (or subnets)
* @param aPcb = the current board
* @param aNetcode = netcode to consider
*/
void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode )
{
// Ensure a zone with the given netcode exists: examine all zones:
bool found = false;
for( int index = 0; index < aPcb->GetAreaCount(); index++ )
{
ZONE_CONTAINER* zone = aPcb->GetArea( index );
if( aNetcode == zone->GetNetCode() )
{
found = true;
break;
}
}
if( !found ) // No zone with this netcode, therefore no connection by zone
return;
// list of pads and tracks candidates to test:
// It is static to avoid multiple memory realloc.
static std::vector <BOARD_CONNECTED_ITEM*> Candidates;
Candidates.clear();
// Build the list of pads candidates connected to the net:
NETINFO_ITEM* net = aPcb->FindNet( aNetcode );
wxASSERT( net );
Candidates.reserve( net->m_PadInNetList.size() );
for( unsigned ii = 0; ii < net->m_PadInNetList.size(); ii++ )
Candidates.push_back( net->m_PadInNetList[ii] );
// Build the list of track candidates connected to the net:
TRACK* track;
if( aPcb->m_Track.GetCount() > 0 )
{
track = aPcb->m_Track.GetFirst()->GetStartNetCode( aNetcode );
for( ; track; track = track->Next() )
{
if( track->GetNetCode() != aNetcode )
break;
Candidates.push_back( track );
}
}
if( Candidates.size() == 0 )
return;
int next_subnet_free_number = 0;
for( unsigned ii = 0; ii < Candidates.size(); ii++ )
{
int subnet = Candidates[ii]->GetSubNet();
next_subnet_free_number = std::max( next_subnet_free_number, subnet );
}
next_subnet_free_number++; // This is a subnet we can use with not connected items
// by tracks, but connected by zone.
// Sort by zone_subnet:
sort( Candidates.begin(), Candidates.end(), CmpZoneSubnetValue );
// Some items can be not connected, but they can be connected to a filled area:
// give them a subnet common to these items connected only by the area,
// and not already used.
// a value like next_subnet_free_number+zone_subnet is right
for( unsigned jj = 0; jj < Candidates.size(); jj++ )
{
BOARD_CONNECTED_ITEM* item = Candidates[jj];
if ( item->GetSubNet() == 0 && (item->GetZoneSubNet() > 0) )
{
item->SetSubNet( next_subnet_free_number + item->GetZoneSubNet() );
}
}
// Now, for each zone subnet, we search for 2 items with different subnets.
// if found, the 2 subnet are merged in the whole candidate list.
int old_subnet = 0;
int old_zone_subnet = 0;
for( unsigned ii = 0; ii < Candidates.size(); ii++ )
{
BOARD_CONNECTED_ITEM* item = Candidates[ii];
int zone_subnet = item->GetZoneSubNet();
if( zone_subnet == 0 ) // Not connected by a filled area, skip it
continue;
int subnet = item->GetSubNet();
if( zone_subnet != old_zone_subnet ) // a new zone subnet is found
{
old_subnet = subnet;
old_zone_subnet = zone_subnet;
continue;
}
// 2 successive items already from the same cluster: nothing to do
if( subnet == old_subnet )
continue;
// Here we have 2 items connected by the same area have 2 differents subnets: merge subnets
if( (subnet > old_subnet) || ( subnet <= 0) )
std::swap( subnet, old_subnet );
for( unsigned jj = 0; jj < Candidates.size(); jj++ )
{
BOARD_CONNECTED_ITEM * item_to_merge = Candidates[jj];
if( item_to_merge->GetSubNet() == old_subnet )
item_to_merge->SetSubNet( subnet );
}
old_subnet = subnet;
}
}
/* Compare function used for sorting candidates by increasing zone subnet
*/
static bool CmpZoneSubnetValue( const BOARD_CONNECTED_ITEM* a, const BOARD_CONNECTED_ITEM* b )
{
int asubnet, bsubnet;
asubnet = a->GetZoneSubNet();
bsubnet = b->GetZoneSubNet();
return asubnet < bsubnet;
}