From 9ad886344b057cf117eb81f766c55a045f341ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20W=C5=82ostowski?= Date: Wed, 22 Mar 2017 14:43:10 +0100 Subject: [PATCH] New connectivity algorithm. --- common/draw_frame.cpp | 2 +- common/geometry/hetriang.cpp | 24 +- common/tool/action_manager.cpp | 2 +- demos/simulation/rectifier/rectifier.pro | 86 +- include/geometry/poly_grid_partition.h | 446 +++++ include/geometry/shape_line_chain.h | 1 + include/profile.h | 40 - include/ttl/halfedge/hedart.h | 25 +- include/ttl/halfedge/hetriang.h | 153 +- include/wxBasePcbFrame.h | 2 +- pcbnew/CMakeLists.txt | 2 + pcbnew/board_commit.cpp | 19 +- pcbnew/board_netlist_updater.cpp | 4 +- pcbnew/class_board.cpp | 15 +- pcbnew/class_board.h | 14 +- pcbnew/class_board_connected_item.cpp | 14 +- pcbnew/class_board_connected_item.h | 3 + pcbnew/class_zone.h | 7 + pcbnew/connect.cpp | 73 +- pcbnew/connectivity.cpp | 377 ++++ pcbnew/connectivity.h | 204 +++ pcbnew/connectivity_algo.cpp | 903 ++++++++++ pcbnew/connectivity_algo.h | 926 ++++++++++ pcbnew/deltrack.cpp | 8 +- ...ialog_general_options_BoardEditor_base.fbp | 104 +- pcbnew/dialogs/dialog_global_deletion.cpp | 2 +- pcbnew/dialogs/dialog_netlist.cpp | 8 +- pcbnew/drc.cpp | 24 +- pcbnew/files.cpp | 2 +- pcbnew/loadcmp.cpp | 2 +- pcbnew/netlist.cpp | 2 +- pcbnew/pcb_draw_panel_gal.cpp | 5 +- pcbnew/pcbframe.cpp | 6 +- pcbnew/ratsnest.cpp | 2 +- pcbnew/ratsnest_data.cpp | 1592 +++++------------ pcbnew/ratsnest_data.h | 586 +----- pcbnew/ratsnest_viewitem.cpp | 88 +- pcbnew/ratsnest_viewitem.h | 7 +- pcbnew/tools/common_actions.cpp | 809 +++++++++ pcbnew/tools/edit_tool.cpp | 24 +- pcbnew/tools/edit_tool.h | 8 + pcbnew/tools/pcb_actions.h | 1 + pcbnew/tools/pcb_editor_control.cpp | 88 +- pcbnew/tools/pcbnew_control.cpp | 2 +- pcbnew/tools/point_editor.cpp | 4 +- pcbnew/tools/selection_tool.cpp | 30 +- pcbnew/undo_redo.cpp | 23 +- pcbnew/zones_by_polygon_fill_functions.cpp | 13 +- ...nvert_brd_items_to_polygons_with_Boost.cpp | 15 +- ...ones_polygons_insulated_copper_islands.cpp | 76 +- 50 files changed, 4752 insertions(+), 2121 deletions(-) create mode 100644 include/geometry/poly_grid_partition.h create mode 100644 pcbnew/connectivity.cpp create mode 100644 pcbnew/connectivity.h create mode 100644 pcbnew/connectivity_algo.cpp create mode 100644 pcbnew/connectivity_algo.h create mode 100644 pcbnew/tools/common_actions.cpp diff --git a/common/draw_frame.cpp b/common/draw_frame.cpp index 42d99c7586..b14d385cca 100644 --- a/common/draw_frame.cpp +++ b/common/draw_frame.cpp @@ -1138,6 +1138,7 @@ void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) gal->SetGridOrigin( VECTOR2D( GetGridOrigin() ) ); // Transfer EDA_DRAW_PANEL settings + GetGalCanvas()->GetViewControls()->EnableCursorWarping( !m_canvas->GetEnableZoomNoCenter() ); GetGalCanvas()->GetViewControls()->EnableMousewheelPan( m_canvas->GetEnableMousewheelPan() ); } @@ -1365,4 +1366,3 @@ void EDA_DRAW_FRAME::GeneralControlKeyMovement( int aHotKey, wxPoint *aPos, break; } } - diff --git a/common/geometry/hetriang.cpp b/common/geometry/hetriang.cpp index 01d8405a7c..2654e02c86 100644 --- a/common/geometry/hetriang.cpp +++ b/common/geometry/hetriang.cpp @@ -55,14 +55,6 @@ using namespace hed; #endif -void NODE::updateLayers() -{ - assert( m_layers.none() ); - - for( const BOARD_CONNECTED_ITEM* item : m_parents ) - m_layers |= item->GetLayerSet(); -} - //#define DEBUG_HE #ifdef DEBUG_HE @@ -402,12 +394,11 @@ std::list* TRIANGULATION::GetNodes() const #endif -std::list* TRIANGULATION::GetEdges( bool aSkipBoundaryEdges ) const +void TRIANGULATION::GetEdges( std::list& aEdges, bool aSkipBoundaryEdges ) const { // collect all arcs (one half edge for each arc) // (boundary edges are also collected). std::list::const_iterator it; - std::list* elist = new std::list; for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it ) { @@ -419,13 +410,13 @@ std::list* TRIANGULATION::GetEdges( bool aSkipBoundaryEdges ) const if( ( !twinedge && !aSkipBoundaryEdges ) || ( twinedge && ( (size_t) edge.get() > (size_t) twinedge.get() ) ) ) - elist->push_front( edge ); + { + aEdges.push_front( edge ); + } edge = edge->GetNextEdgeInFace(); } } - - return elist; } @@ -613,7 +604,8 @@ void TRIANGULATION::OptimizeDelaunay() // Collect all interior edges (one half edge for each arc) bool skip_boundary_edges = true; - std::list* elist = GetEdges( skip_boundary_edges ); + std::list elist; + GetEdges( elist, skip_boundary_edges ); // Assumes that elist has only one half-edge for each arc. bool cycling_check = true; @@ -624,7 +616,7 @@ void TRIANGULATION::OptimizeDelaunay() { optimal = true; - for( it = elist->begin(); it != elist->end(); ++it ) + for( it = elist.begin(); it != elist.end(); ++it ) { EDGE_PTR edge = *it; @@ -637,8 +629,6 @@ void TRIANGULATION::OptimizeDelaunay() } } } - - delete elist; } diff --git a/common/tool/action_manager.cpp b/common/tool/action_manager.cpp index e1cbb0b2aa..40642d603a 100644 --- a/common/tool/action_manager.cpp +++ b/common/tool/action_manager.cpp @@ -220,7 +220,7 @@ void ACTION_MANAGER::UpdateHotKeys() ++global_actions_cnt; } - assert( global_actions_cnt <= 1 ); + // assert( global_actions_cnt <= 1 ); } #endif /* not NDEBUG */ } diff --git a/demos/simulation/rectifier/rectifier.pro b/demos/simulation/rectifier/rectifier.pro index 33afc2975f..5d2c541683 100644 --- a/demos/simulation/rectifier/rectifier.pro +++ b/demos/simulation/rectifier/rectifier.pro @@ -1,6 +1,6 @@ -update=13/08/2016 16:32:14 +update=pią, 7 paź 2016, 14:28:36 version=1 -last_client=kicad +last_client=eeschema [general] version=1 RootSch= @@ -29,4 +29,84 @@ version=1 NetIExt=net [eeschema] version=1 -LibDir= +LibDir=/home/twl/Kicad-dev/kicad-library/library +[eeschema/libraries] +LibName1=74xgxx +LibName2=74xx +LibName3=ac-dc +LibName4=actel +LibName5=AD8051 +LibName6=adc-dac +LibName7=Altera +LibName8=analog_devices +LibName9=analog_switches +LibName10=atmel +LibName11=audio +LibName12=bbd +LibName13=brooktre +LibName14=cmos4000 +LibName15=cmos_ieee +LibName16=conn +LibName17=contrib +LibName18=cypress +LibName19=dc-dc +LibName20=device +LibName21=digital-audio +LibName22=diode +LibName23=display +LibName24=dsp +LibName25=elec-unifil +LibName26=ESD_Protection +LibName27=ftdi +LibName28=gennum +LibName29=graphic +LibName30=hc11 +LibName31=intel +LibName32=interface +LibName33=ir +LibName34=Lattice +LibName35=linear +LibName36=logo +LibName37=maxim +LibName38=memory +LibName39=microchip +LibName40=microchip_dspic33dsc +LibName41=microchip_pic10mcu +LibName42=microchip_pic12mcu +LibName43=microchip_pic16mcu +LibName44=microchip_pic18mcu +LibName45=microchip_pic32mcu +LibName46=microcontrollers +LibName47=motor_drivers +LibName48=motorola +LibName49=msp430 +LibName50=nordicsemi +LibName51=nxp_armmcu +LibName52=onsemi +LibName53=opto +LibName54=Oscillators +LibName55=philips +LibName56=power +LibName57=powerint +LibName58=Power_Management +LibName59=references +LibName60=regul +LibName61=relays +LibName62=rfcom +LibName63=sensors +LibName64=silabs +LibName65=siliconi +LibName66=stm8 +LibName67=stm32 +LibName68=supertex +LibName69=switches +LibName70=texas +LibName71=transf +LibName72=transistors +LibName73=ttl_ieee +LibName74=valves +LibName75=video +LibName76=Worldsemi +LibName77=Xicor +LibName78=xilinx +LibName79=Zilog diff --git a/include/geometry/poly_grid_partition.h b/include/geometry/poly_grid_partition.h new file mode 100644 index 0000000000..10de3e5dfa --- /dev/null +++ b/include/geometry/poly_grid_partition.h @@ -0,0 +1,446 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016-2017 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __POLY_GRID_PARTITION_H +#define __POLY_GRID_PARTITION_H + +#include +#include + +#include +#include +#include +#include + +/** + * Class POLY_GRID_PARTITION + * + * Provides a fast test for point inside polygon by splitting the edges + * of the polygon into a rectangular grid. + */ +class POLY_GRID_PARTITION +{ +private: + enum HASH_FLAG + { + LEAD_H = 1, + LEAD_V = 2, + TRAIL_H = 4, + TRAIL_V = 8 + }; + + using EDGE_LIST = std::vector; + + template + inline void hash_combine( std::size_t& seed, const T& v ) + { + std::hash hasher; + seed ^= hasher( v ) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + + struct segsEqual + { + bool operator()( const SEG& a, const SEG& b ) const + { + return (a.A == b.A && a.B == b.B) || (a.A == b.B && a.B == b.A); + } + }; + + struct segHash + { + std::size_t operator()( const SEG& a ) const + { + std::size_t seed = 0; + + return a.A.x + a.B.x + a.A.y + a.B.y; + + return seed; + } + }; + + const VECTOR2I grid2poly( const VECTOR2I& p ) const + { + int px = rescale( p.x, m_bbox.GetWidth(), m_gridSize ) + m_bbox.GetPosition().x; + int py = rescale( p.y, m_bbox.GetHeight(), m_gridSize ) + m_bbox.GetPosition().y; // (int) floor( (double) p.y / m_gridSize * (double) m_bbox.GetHeight() + m_bbox.GetPosition().y ); + + return VECTOR2I( px, py ); + } + + int grid2polyX( int x ) const + { + return rescale( x, m_bbox.GetWidth(), m_gridSize ) + m_bbox.GetPosition().x; + } + + int grid2polyY( int y ) const + { + return rescale( y, m_bbox.GetHeight(), m_gridSize ) + m_bbox.GetPosition().y; + } + + const VECTOR2I poly2grid( const VECTOR2I& p ) const + { + int px = rescale( p.x - m_bbox.GetPosition().x, m_gridSize, m_bbox.GetWidth() ); + int py = rescale( p.y - m_bbox.GetPosition().y, m_gridSize, m_bbox.GetHeight() ); + + if( px < 0 ) + px = 0; + + if( px >= m_gridSize ) + px = m_gridSize - 1; + + if( py < 0 ) + py = 0; + + if( py >= m_gridSize ) + py = m_gridSize - 1; + + return VECTOR2I( px, py ); + } + + int poly2gridX( int x ) const + { + int px = rescale( x - m_bbox.GetPosition().x, m_gridSize, m_bbox.GetWidth() ); + + if( px < 0 ) + px = 0; + + if( px >= m_gridSize ) + px = m_gridSize - 1; + + return px; + } + + int poly2gridY( int y ) const + { + int py = rescale( y - m_bbox.GetPosition().y, m_gridSize, m_bbox.GetHeight() ); + + if( py < 0 ) + py = 0; + + if( py >= m_gridSize ) + py = m_gridSize - 1; + + return py; + } + + void build( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize ) + { + m_outline = aPolyOutline; + m_bbox = m_outline.BBox(); + m_gridSize = gridSize; + + m_outline.SetClosed( true ); + + m_grid.reserve( gridSize * gridSize ); + + for( int y = 0; y < gridSize; y++ ) + { + for( int x = 0; x < gridSize; x++ ) + { + m_grid.push_back( EDGE_LIST() ); + } + } + + VECTOR2I ref_v( 0, 1 ); + VECTOR2I ref_h( 0, 1 ); + + m_flags.reserve( m_outline.SegmentCount() ); + + std::unordered_map edgeSet; + + for( int i = 0; i 0 ) + { + flags |= LEAD_H; + } + else if( dir.Dot( ref_h ) < 0 ) + { + flags |= TRAIL_H; + } + } + + m_flags.push_back( flags ); + + if( !flags ) + continue; + + std::set indices; + + indices.insert( m_gridSize * poly2gridY( edge.A.y ) + poly2gridX( edge.A.x ) ); + indices.insert( m_gridSize * poly2gridY( edge.B.y ) + poly2gridX( edge.B.x ) ); + + if( edge.A.x > edge.B.x ) + std::swap( edge.A, edge.B ); + + dir = edge.B - edge.A; + + if( dir.x != 0 ) + { + int gx0 = poly2gridX( edge.A.x ) + 1; + int gx1 = poly2gridX( edge.B.x ); + + for( int x = gx0; x <= gx1; x++ ) + { + int px = grid2polyX( x ); + int py = ( edge.A.y + rescale( dir.y, px - edge.A.x, dir.x ) ); + int yy = poly2gridY( py ); + + indices.insert( m_gridSize * yy + x ); + + if( x > 0 ) + indices.insert( m_gridSize * yy + x - 1 ); + } + } + + if( edge.A.y > edge.B.y ) + std::swap( edge.A, edge.B ); + + dir = edge.B - edge.A; + + if( dir.y != 0 ) + { + int gy0 = poly2gridY( edge.A.y ) + 1; + int gy1 = poly2gridY( edge.B.y ); + + for( int y = gy0; y <= gy1; y++ ) + { + int py = grid2polyY( y ); + int px = ( edge.A.x + rescale( dir.x, py - edge.A.y, dir.y ) ); + int xx = poly2gridX( px ); + + indices.insert( m_gridSize * y + xx ); + + if( y > 0 ) + indices.insert( m_gridSize * (y - 1) + xx ); + } + } + + for( auto idx : indices ) + m_grid[idx].push_back( i ); + } + } + + bool inRange( int v1, int v2, int x ) const + { + if( v1 < v2 ) + { + return x >= v1 && x <= v2; + } + + return x >= v2 && x <= v1; + } + + struct SCAN_STATE + { + SCAN_STATE() + { + dist_max = INT_MAX; + nearest = -1; + nearest_prev = -1; + }; + + int dist_prev; + int dist_max; + int nearest_prev; + int nearest; + }; + + void scanCell( SCAN_STATE& state, const EDGE_LIST& cell, const VECTOR2I& aP ) const + { + for( auto index : cell ) + { + const SEG& edge = m_outline.CSegment( index ); + + if( edge.A.y == edge.B.y ) // horizontal edge + continue; + + if( m_flags[index] == 0 ) // a slit + continue; + + if( inRange( edge.A.y, edge.B.y, aP.y ) ) + { + int dist = 0; + + if( edge.A.y == aP.y ) + { + dist = -(aP.x - edge.A.x); + } + else if( edge.B.y == aP.y ) + { + dist = -(aP.x - edge.B.x); + } + else + { + const VECTOR2I e( edge.B - edge.A ); + const VECTOR2I ff( 1, 0 ); + const VECTOR2I ac( aP - edge.A ); + + auto d = ff.Cross( e ); + auto q = e.Cross( ac ); + + using ecoord = VECTOR2I::extended_type; + + dist = rescale( q, (ecoord) 1, d ); + } + + if( dist == 0 ) + { + if( state.nearest_prev < 0 || state.nearest != index ) + { + state.dist_prev = state.dist_max; + state.nearest_prev = state.nearest; + } + + state.nearest = index; + state.dist_max = 0; + return; + } + + if( dist != 0 && std::abs( dist ) <= std::abs( state.dist_max ) ) + { + if( state.nearest_prev < 0 || state.nearest != index ) + { + state.dist_prev = state.dist_max; + state.nearest_prev = state.nearest; + } + + state.dist_max = dist; + state.nearest = index; + } + } + } + } + +public: + + POLY_GRID_PARTITION( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize ) + { + build( aPolyOutline, gridSize ); + } + + int ContainsPoint( const VECTOR2I& aP ) // const + { + const auto gridPoint = poly2grid( aP ); + + if( !m_bbox.Contains( aP ) ) + return false; + + SCAN_STATE state; + const EDGE_LIST& cell = m_grid[ m_gridSize * gridPoint.y + gridPoint.x ]; + + scanCell( state, cell, aP ); + + if( state.nearest < 0 ) + { + state = SCAN_STATE(); + + for( int d = 1; d <= m_gridSize; d++ ) + { + int xl = gridPoint.x - d; + int xh = gridPoint.x + d; + + if( xl >= 0 ) + { + const EDGE_LIST& cell2 = m_grid[ m_gridSize * gridPoint.y + xl ]; + scanCell( state, cell2, aP ); + + if( state.nearest >= 0 ) + break; + } + + if( xh < m_gridSize ) + { + const EDGE_LIST& cell2 = m_grid[ m_gridSize * gridPoint.y + xh ]; + scanCell( state, cell2, aP ); + + if( state.nearest >= 0 ) + break; + } + } + } + + if( state.nearest < 0 ) + return 0; + + if( state.dist_max == 0 ) + return 1; + + if( state.nearest_prev >= 0 && state.dist_max == state.dist_prev ) + { + int d = std::abs( state.nearest_prev - state.nearest ); + + if( (d == 1) && ( (m_flags[state.nearest_prev] & m_flags[state.nearest]) == 0 ) ) + { + return 0; + } + else if( d > 1 ) + { + return 1; + } + } + + if( state.dist_max > 0 ) + { + return m_flags[state.nearest] & LEAD_H ? 1 : 0; + } + else + { + return m_flags[state.nearest] & TRAIL_H ? 1 : 0; + } + } + + const BOX2I& BBox() const + { + return m_bbox; + } + +private: + int m_gridSize; + SHAPE_LINE_CHAIN m_outline; + BOX2I m_bbox; + std::vector m_flags; + std::vector m_grid; +}; + +#endif diff --git a/include/geometry/shape_line_chain.h b/include/geometry/shape_line_chain.h index 3b875c45a1..89e54d9b6e 100644 --- a/include/geometry/shape_line_chain.h +++ b/include/geometry/shape_line_chain.h @@ -601,6 +601,7 @@ public: } const VECTOR2I PointAlong( int aPathLength ) const; + const SHAPE_LINE_CHAIN RemoveHoles( ) const; private: /// array of vertices diff --git a/include/profile.h b/include/profile.h index 20eb6da0b3..4a14fc38a7 100644 --- a/include/profile.h +++ b/include/profile.h @@ -37,9 +37,6 @@ #include #include -#include -#include - /** * The class PROF_COUNTER is a small class to help profiling. * It allows the calculation of the elapsed time (in millisecondes) between @@ -154,41 +151,4 @@ private: */ unsigned GetRunningMicroSecs(); -class PROF_COUNTER -{ -public: - PROF_COUNTER(const std::string& name, bool autostart = true) - { - m_name = name; - m_running= false; - if(autostart) - start(); - } - - void start() - { - m_running = true; - prof_start(&m_cnt); - } - - void stop() - { - if(!m_running) - return; - m_running=false; - prof_end(&m_cnt); - } - - void show() - { - stop(); - fprintf(stderr,"%s took %.1f milliseconds.\n", m_name.c_str(), (double)m_cnt.msecs()); - } -private: - std::string m_name; - prof_counter m_cnt; - bool m_running; -}; - - #endif diff --git a/include/ttl/halfedge/hedart.h b/include/ttl/halfedge/hedart.h index 2749c5087c..7fbb849bf7 100644 --- a/include/ttl/halfedge/hedart.h +++ b/include/ttl/halfedge/hedart.h @@ -2,21 +2,21 @@ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, * Applied Mathematics, Norway. * - * Contact information: E-mail: tor.dokken@sintef.no - * SINTEF ICT, Department of Applied Mathematics, - * P.O. Box 124 Blindern, - * 0314 Oslo, Norway. + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. * * This file is part of TTL. * * TTL is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. + * License, or (at your option) any later version. * - * TTL 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 + * TTL 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public @@ -34,7 +34,7 @@ * disclosing the source code of your own applications. * * This file may be used in accordance with the terms contained in a - * written agreement between you and SINTEF ICT. + * written agreement between you and SINTEF ICT. */ #ifndef _HALF_EDGE_DART_ @@ -69,6 +69,7 @@ public: DART( const EDGE_PTR& aEdge, bool aDir = true ) { m_edge = aEdge; + assert ( m_edge ); m_dir = aDir; } @@ -76,6 +77,7 @@ public: DART( const DART& aDart ) { m_edge = aDart.m_edge; + assert ( m_edge ); m_dir = aDart.m_dir; } @@ -91,6 +93,7 @@ public: return *this; m_edge = aDart.m_edge; + assert ( m_edge ); m_dir = aDart.m_dir; return *this; @@ -121,11 +124,13 @@ public: if( m_dir ) { m_edge = m_edge->GetNextEdgeInFace()->GetNextEdgeInFace(); + assert ( m_edge ); m_dir = false; } else { m_edge = m_edge->GetNextEdgeInFace(); + assert ( m_edge ); m_dir = true; } @@ -138,6 +143,7 @@ public: if( m_edge->GetTwinEdge() ) { m_edge = m_edge->GetTwinEdge(); + assert ( m_edge ); m_dir = !m_dir; } @@ -150,6 +156,7 @@ public: void Init( const EDGE_PTR& aEdge, bool aDir = true ) { m_edge = aEdge; + assert(m_edge); m_dir = aDir; } diff --git a/include/ttl/halfedge/hetriang.h b/include/ttl/halfedge/hetriang.h index fb2742ffac..03ba5f9bc3 100644 --- a/include/ttl/halfedge/hetriang.h +++ b/include/ttl/halfedge/hetriang.h @@ -42,8 +42,8 @@ #ifndef _HE_TRIANG_H_ #define _HE_TRIANG_H_ -//#define TTL_USE_NODE_ID // Each node gets it's own unique id -//#define TTL_USE_NODE_FLAG // Each node gets a flag (can be set to true or false) +#define TTL_USE_NODE_ID // Each node gets it's own unique id +#define TTL_USE_NODE_FLAG // Each node gets a flag (can be set to true or false) #include #include @@ -53,8 +53,10 @@ #include #include #include +#include class BOARD_CONNECTED_ITEM; +class CN_CLUSTER; namespace ttl { @@ -103,37 +105,25 @@ protected: /// Node coordinates const int m_x, m_y; - /// Tag for quick connection resolution - int m_tag; - - /// Whether it the node can be a target for ratsnest lines - bool m_noline; - - /// List of board items that share this node - std::unordered_set m_parents; - - /// Layers that are occupied by this node - LSET m_layers; - - /// Recomputes the layers used by this node - void updateLayers(); - public: /// Constructor - NODE( int aX = 0, int aY = 0 ) : + NODE( int aX = 0, int aY = 0, std::shared_ptr aCluster = nullptr ) : #ifdef TTL_USE_NODE_FLAG m_flag( false ), #endif #ifdef TTL_USE_NODE_ID m_id( id_count++ ), #endif - m_x( aX ), m_y( aY ), m_tag( -1 ), m_noline( false ) + m_x( aX ), m_y( aY ) { - m_layers.reset(); } /// Destructor - ~NODE() {} + ~NODE() { + + } + + const VECTOR2D Pos() const { return VECTOR2D( m_x, m_y ); } /// Returns the x-coordinate inline int GetX() const @@ -147,32 +137,19 @@ public: return m_y; } - /// Returns tag, common identifier for connected nodes - inline int GetTag() const + inline VECTOR2I GetPos() const { - return m_tag; - } - - /// Sets tag, common identifier for connected nodes - inline void SetTag( int aTag ) - { - m_tag = aTag; - } - - /// Decides whether this node can be a ratsnest line target - inline void SetNoLine( bool aEnable ) - { - m_noline = aEnable; - } - - /// Returns true if this node can be a target for ratsnest lines - inline const bool& GetNoLine() const - { - return m_noline; + return VECTOR2I( m_x, m_y ); } #ifdef TTL_USE_NODE_ID /// Returns the id (TTL_USE_NODE_ID must be defined) + + inline void SetId( int aId ) + { + m_id = aId; + } + inline int Id() const { return m_id; @@ -192,39 +169,6 @@ public: return m_flag; } #endif - - inline unsigned int GetRefCount() const - { - return m_parents.size(); - } - - inline void AddParent( const BOARD_CONNECTED_ITEM* aParent ) - { - m_parents.insert( aParent ); - m_layers.reset(); // mark as needs updating - } - - inline void RemoveParent( const BOARD_CONNECTED_ITEM* aParent ) - { - auto it = m_parents.find( aParent ); - - if( it != m_parents.end() ) - { - m_parents.erase( it ); - m_layers.reset(); // mark as needs updating - } - } - - const LSET& GetLayers() - { - if( m_layers.none() ) - updateLayers(); - - return m_layers; - } - - // Tag used for unconnected items. - static const int TAG_UNCONNECTED = -1; }; @@ -236,23 +180,14 @@ class EDGE { public: /// Constructor - EDGE() : m_weight( 0 ), m_isLeadingEdge( false ) + EDGE() : m_isLeadingEdge( false ) { } /// Destructor virtual ~EDGE() { - } - /// Returns tag, common identifier for connected nodes - inline int GetTag() const - { - int tag = GetSourceNode()->GetTag(); - if( tag >= 0 ) - return tag; - - return GetTargetNode()->GetTag(); } /// Sets the source node @@ -288,6 +223,9 @@ public: /// Returns the twin edge inline EDGE_PTR GetTwinEdge() const { + if( m_twinEdge.expired() ) + return nullptr; + return m_twinEdge.lock(); } @@ -299,6 +237,7 @@ public: /// Returns the next edge in face inline const EDGE_PTR& GetNextEdgeInFace() const { + assert ( m_nextEdgeInFace ); return m_nextEdgeInFace; } @@ -314,16 +253,6 @@ public: return m_nextEdgeInFace->GetSourceNode(); } - inline void SetWeight( unsigned int weight ) - { - m_weight = weight; - } - - inline unsigned int GetWeight() const - { - return m_weight; - } - void Clear() { m_sourceNode.reset(); @@ -340,41 +269,9 @@ protected: NODE_PTR m_sourceNode; EDGE_WEAK_PTR m_twinEdge; EDGE_PTR m_nextEdgeInFace; - unsigned int m_weight; bool m_isLeadingEdge; }; - - /** - * \class EDGE_MST - * \brief \b Specialization of %EDGE class to be used for Minimum Spanning Tree algorithm. - */ -class EDGE_MST : public EDGE -{ -private: - NODE_PTR m_target; - -public: - EDGE_MST( const NODE_PTR& aSource, const NODE_PTR& aTarget, unsigned int aWeight = 0 ) : - m_target( aTarget ) - { - m_sourceNode = aSource; - m_weight = aWeight; - } - - /// @copydoc Edge::setSourceNode() - virtual const NODE_PTR& GetTargetNode() const override - { - return m_target; - } - -private: - EDGE_MST( const EDGE& aEdge ) - { - assert( false ); - } -}; - class DART; // Forward declaration (class in this namespace) /** @@ -500,7 +397,7 @@ public: } /// Returns a list of half-edges (one half-edge for each arc) - std::list* GetEdges( bool aSkipBoundaryEdges = false ) const; + void GetEdges( std::list& aEdges, bool aSkipBoundaryEdges = false ) const; #ifdef TTL_USE_NODE_FLAG /// Sets flag in all the nodes diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index 5bd389a05a..94604be2a4 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -566,7 +566,7 @@ public: * search connections between tracks and pads and propagate pad net codes to the track * segments. */ - void RecalculateAllTracksNetcode(); + void ComputeLegacyConnections (); /* Functions relative to Undo/redo commands: */ diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 08e45b4aca..0b42e6514b 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -220,6 +220,8 @@ set( PCBNEW_CLASS_SRCS class_pcb_layer_box_selector.cpp clean.cpp connect.cpp + connectivity.cpp + connectivity_algo.cpp controle.cpp dimension.cpp cross-probing.cpp diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index 1c421759c1..23e882fd68 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include using namespace std::placeholders; @@ -60,7 +61,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry ) KIGFX::VIEW* view = m_toolMgr->GetView(); BOARD* board = (BOARD*) m_toolMgr->GetModel(); PCB_BASE_FRAME* frame = (PCB_BASE_FRAME*) m_toolMgr->GetEditFrame(); - RN_DATA* ratsnest = board->GetRatsnest(); + auto connectivity = board->GetConnectivity(); std::set savedModules; if( Empty() ) @@ -262,7 +263,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry ) } view->Update ( boardItem ); - ratsnest->Update( boardItem ); + connectivity->Update( boardItem ); break; } @@ -278,7 +279,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry ) if( TOOL_MANAGER* toolMgr = frame->GetToolManager() ) toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } ); - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); frame->OnModify(); frame->UpdateMsgPanel(); @@ -307,7 +308,7 @@ void BOARD_COMMIT::Revert() PICKED_ITEMS_LIST undoList; KIGFX::VIEW* view = m_toolMgr->GetView(); BOARD* board = (BOARD*) m_toolMgr->GetModel(); - RN_DATA* ratsnest = board->GetRatsnest(); + auto connectivity = board->GetConnectivity(); for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it ) { @@ -325,7 +326,7 @@ void BOARD_COMMIT::Revert() } view->Remove( item ); - ratsnest->Remove( item ); + connectivity->Remove( item ); break; case CHT_REMOVE: @@ -337,7 +338,7 @@ void BOARD_COMMIT::Revert() } view->Add( item ); - ratsnest->Add( item ); + connectivity->Add( item ); break; case CHT_MODIFY: @@ -349,7 +350,7 @@ void BOARD_COMMIT::Revert() } view->Remove( item ); - ratsnest->Remove( item ); + connectivity->Remove( item ); item->SwapData( copy ); @@ -365,7 +366,7 @@ void BOARD_COMMIT::Revert() } view->Add( item ); - ratsnest->Add( item ); + connectivity->Add( item ); delete copy; break; } @@ -376,7 +377,7 @@ void BOARD_COMMIT::Revert() } } - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); clear(); } diff --git a/pcbnew/board_netlist_updater.cpp b/pcbnew/board_netlist_updater.cpp index 61700d0f59..945da66f2f 100644 --- a/pcbnew/board_netlist_updater.cpp +++ b/pcbnew/board_netlist_updater.cpp @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include @@ -669,7 +669,7 @@ bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist ) { m_commit.Push( _( "Update netlist" ) ); m_frame->Compile_Ratsnest( NULL, false ); - m_board->GetRatsnest()->ProcessBoard(); + m_board->GetConnectivity()->Build( m_board ); testConnectivity( aNetlist ); } diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 09a4642d46..726f0b39c8 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -58,6 +58,7 @@ #include #include #include +#include /* This is an odd place for this, but CvPcb won't link if it is @@ -106,7 +107,8 @@ BOARD::BOARD() : m_designSettings.SetCustomViaDrill( m_designSettings.GetCurrentViaDrill() ); // Initialize ratsnest - m_ratsnest = new RN_DATA( this ); + m_connectivity.reset( new CONNECTIVITY_DATA() ); + m_connectivity->Build( this ); } @@ -118,8 +120,6 @@ BOARD::~BOARD() Delete( area_to_remove ); } - delete m_ratsnest; - m_FullRatsnest.clear(); m_LocalRatsnest.clear(); @@ -942,7 +942,7 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode ) } aBoardItem->SetParent( this ); - m_ratsnest->Add( aBoardItem ); + m_connectivity->Add( aBoardItem ); } @@ -1011,7 +1011,7 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem ) wxFAIL_MSG( wxT( "BOARD::Remove() needs more ::Type() support" ) ); } - m_ratsnest->Remove( aBoardItem ); + m_connectivity->Remove( aBoardItem ); } @@ -2895,3 +2895,8 @@ bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, return success; } + +/*RN_DATA* BOARD::GetRatsnest() const +{ + return m_connectivity->GetRatsnest(); +}*/ diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index f8a0a5abfe..199ac175f3 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -45,6 +45,9 @@ #include #include +#include + +using std::unique_ptr; class PCB_BASE_FRAME; class PCB_EDIT_FRAME; @@ -60,7 +63,7 @@ class NETLIST; class REPORTER; class RN_DATA; class SHAPE_POLY_SET; - +class CONNECTIVITY_DATA; /** * Enum LAYER_T @@ -187,7 +190,7 @@ private: int m_fileFormatVersionAtLoad; ///< the version loaded from the file NETINFO_LIST m_NetInfo; ///< net info list (name, design constraints .. - RN_DATA* m_ratsnest; + std::shared_ptr m_connectivity; BOARD_DESIGN_SETTINGS m_designSettings; ZONE_SETTINGS m_zoneSettings; @@ -291,13 +294,14 @@ public: BOARD_ITEM* Duplicate( const BOARD_ITEM* aItem, bool aAddToBoard = false ); /** - * Function GetRatsnest() + * Function GetConnectivity() * returns list of missing connections between components/tracks. * @return RATSNEST* is an object that contains informations about missing connections. */ - RN_DATA* GetRatsnest() const + + std::shared_ptr GetConnectivity() const { - return m_ratsnest; + return m_connectivity; } /** diff --git a/pcbnew/class_board_connected_item.cpp b/pcbnew/class_board_connected_item.cpp index b02a5c29a1..d33a100c47 100644 --- a/pcbnew/class_board_connected_item.cpp +++ b/pcbnew/class_board_connected_item.cpp @@ -34,7 +34,7 @@ #include #include -#include +#include BOARD_CONNECTED_ITEM::BOARD_CONNECTED_ITEM( BOARD_ITEM* aParent, KICAD_T idtype ) : BOARD_ITEM( aParent, idtype ), m_netinfo( &NETINFO_LIST::ORPHANED_ITEM ), @@ -50,11 +50,11 @@ bool BOARD_CONNECTED_ITEM::SetNetCode( int aNetCode, bool aNoAssert ) // set the m_netinfo to the dummy NETINFO_LIST::ORPHANED BOARD* board = GetBoard(); - RN_DATA* ratsnest = board ? board->GetRatsnest() : NULL; - bool addRatsnest = false; + //auto connectivity = board ? board->GetConnectivity() : nullptr; + //bool addRatsnest = false; - if( ratsnest ) - addRatsnest = ratsnest->Remove( this ); + //if( connectivity ) + //addRatsnest = connectivity->Remove( this ); if( ( aNetCode >= 0 ) && board ) m_netinfo = board->FindNet( aNetCode ); @@ -65,8 +65,8 @@ bool BOARD_CONNECTED_ITEM::SetNetCode( int aNetCode, bool aNoAssert ) assert( m_netinfo ); // Add only if it was previously added to the ratsnest - if( addRatsnest ) - ratsnest->Add( this ); + //if( addRatsnest ) + // connectivity->Add( this ); return ( m_netinfo != NULL ); } diff --git a/pcbnew/class_board_connected_item.h b/pcbnew/class_board_connected_item.h index a4e30425c4..947646c89a 100644 --- a/pcbnew/class_board_connected_item.h +++ b/pcbnew/class_board_connected_item.h @@ -37,6 +37,7 @@ class NETCLASS; class TRACK; class D_PAD; +class CN_BOARD_ITEM_DATA; /** * Class BOARD_CONNECTED_ITEM @@ -50,6 +51,8 @@ class BOARD_CONNECTED_ITEM : public BOARD_ITEM friend class CONNECTIONS; public: + + // These 2 members are used for temporary storage during connections calculations: std::vector m_TracksConnected; // list of other tracks connected to me std::vector m_PadsConnected; // list of other pads connected to me diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index f43da75000..ad9ed1d847 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -637,6 +637,12 @@ public: m_FillSegmList.insert( m_FillSegmList.end(), aSegments.begin(), aSegments.end() ); } + SHAPE_POLY_SET& RawPolysList() + { + return m_RawPolysList; + } + + wxString GetSelectMenuText() const override; BITMAP_DEF GetMenuImage() const override; @@ -781,6 +787,7 @@ private: * described by m_Poly can have many filled areas */ SHAPE_POLY_SET m_FilledPolysList; + SHAPE_POLY_SET m_RawPolysList; HATCH_STYLE m_hatchStyle; // hatch style, see enum above int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines diff --git a/pcbnew/connect.cpp b/pcbnew/connect.cpp index 1b42fbd73b..4ec1e8b985 100644 --- a/pcbnew/connect.cpp +++ b/pcbnew/connect.cpp @@ -39,9 +39,13 @@ // Helper classes to handle connection points #include +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 ); @@ -853,13 +857,9 @@ void PCB_BASE_FRAME::TestNetConnection( wxDC* aDC, int aNetCode ) return; } - -/* search connections between tracks and pads and propagate pad net codes to the track - * segments. - * Pads netcodes are assumed to be up to date. - */ -void PCB_BASE_FRAME::RecalculateAllTracksNetcode() +void PCB_BASE_FRAME::ComputeLegacyConnections() { + // Build the net info list GetBoard()->BuildListOfNets(); @@ -872,7 +872,6 @@ void PCB_BASE_FRAME::RecalculateAllTracksNetcode() t->end = NULL; t->SetState( BUSY | IN_EDIT | BEGIN_ONPAD | END_ONPAD, false ); t->SetZoneSubNet( 0 ); - t->SetNetCode( NETINFO_LIST::UNCONNECTED ); } // If no pad, reset pointers and netcode, and do nothing else @@ -886,72 +885,12 @@ void PCB_BASE_FRAME::RecalculateAllTracksNetcode() // First pass: build connections between track segments and pads. connections.SearchTracksConnectedToPads(); - // For tracks connected to at least one pad, - // set the track net code to the pad netcode - for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() ) - { - if( t->m_PadsConnected.size() ) - t->SetNetCode( t->m_PadsConnected[0]->GetNetCode() ); - } - - // Pass 2: build connections between track ends for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() ) { connections.SearchConnectedTracks( t ); connections.GetConnectedTracks( t ); } - // Propagate net codes from a segment to other connected segments - bool new_pass_request = true; // set to true if a track has its netcode changed from 0 - // to a known netcode to re-evaluate netcodes - // of connected items - while( new_pass_request ) - { - new_pass_request = false; - - for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() ) - { - int netcode = t->GetNetCode(); - - if( netcode == 0 ) - { - // try to find a connected item having a netcode - for( unsigned kk = 0; kk < t->m_TracksConnected.size(); kk++ ) - { - int altnetcode = t->m_TracksConnected[kk]->GetNetCode(); - if( altnetcode ) - { - new_pass_request = true; - netcode = altnetcode; - t->SetNetCode(netcode); - break; - } - } - } - - if( netcode ) // this track has a netcode - { - // propagate this netcode to connected tracks having no netcode - for( unsigned kk = 0; kk < t->m_TracksConnected.size(); kk++ ) - { - int altnetcode = t->m_TracksConnected[kk]->GetNetCode(); - if( altnetcode == 0 ) - { - t->m_TracksConnected[kk]->SetNetCode(netcode); - new_pass_request = true; - } - } - } - } - } - - if( IsGalCanvasActive() ) - { - /// @todo LEGACY tracks might have changed their nets, so we need to refresh labels in GAL - for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() ) - GetGalCanvas()->GetView()->Update( track ); - } - // Sort the track list by net codes: RebuildTrackChain( m_Pcb ); } diff --git a/pcbnew/connectivity.cpp b/pcbnew/connectivity.cpp new file mode 100644 index 0000000000..1180db42bf --- /dev/null +++ b/pcbnew/connectivity.cpp @@ -0,0 +1,377 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2017 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#define PROFILE + +#ifdef PROFILE +#include +#endif + +#include +#include +#include + +#ifdef USE_OPENMP +#include +#endif /* USE_OPENMP */ + +CONNECTIVITY_DATA::CONNECTIVITY_DATA() +{ + m_connAlgo.reset( new CN_CONNECTIVITY_ALGO ); +} + + +CONNECTIVITY_DATA::~CONNECTIVITY_DATA() +{ + Clear(); +} + + +bool CONNECTIVITY_DATA::Add( BOARD_ITEM* aItem ) +{ + m_connAlgo->Add( aItem ); + return true; +} + + +bool CONNECTIVITY_DATA::Remove( BOARD_ITEM* aItem ) +{ + m_connAlgo->Remove( aItem ); + return true; +} + + +/** + * Function Update() + * Updates the connectivity data for an item. + * @param aItem is an item to be updated. + * @return True if operation succeeded. The item will not be updated if it was not previously + * added to the ratsnest. + */ +bool CONNECTIVITY_DATA::Update( BOARD_ITEM* aItem ) +{ + m_connAlgo->Remove( aItem ); + m_connAlgo->Add( aItem ); + return true; +} + + +void CONNECTIVITY_DATA::Build( BOARD* aBoard ) +{ + m_connAlgo.reset( new CN_CONNECTIVITY_ALGO ); + m_connAlgo->Build( aBoard ); + + RecalculateRatsnest(); +} + + +void CONNECTIVITY_DATA::Build( const std::vector& aItems ) +{ + m_connAlgo.reset( new CN_CONNECTIVITY_ALGO ); + m_connAlgo->Build( aItems ); + + RecalculateRatsnest(); +} + + +void CONNECTIVITY_DATA::updateRatsnest() +{ + int lastNet = m_connAlgo->NetCount(); + + #ifdef PROFILE + PROF_COUNTER rnUpdate( "update-ratsnest" ); + #endif + + int nDirty = 0; + + int i; + + #ifdef USE_OPENMP + #pragma omp parallel shared(lastNet) private(i) + { + #pragma omp for schedule(guided, 1) + #else /* USE_OPENMP */ + { + #endif + + // Start with net number 1, as 0 stands for not connected + for( i = 1; i < lastNet; ++i ) + { + if( m_nets[i]->IsDirty() ) + { + m_nets[i]->Update(); + nDirty++; + } + } + } /* end of parallel section */ + #ifdef PROFILE + rnUpdate.Show(); + #endif /* PROFILE */ + printf( "Dirty: %d\n", nDirty ); +} + + +void CONNECTIVITY_DATA::addRatsnestCluster( std::shared_ptr aCluster ) +{ + auto rnNet = m_nets[ aCluster->OriginNet() ]; + + rnNet->AddCluster( aCluster ); +} + + +void CONNECTIVITY_DATA::RecalculateRatsnest() +{ + int lastNet = m_connAlgo->NetCount(); + + if( lastNet >= (int) m_nets.size() ) + { + unsigned int prevSize = m_nets.size(); + m_nets.resize( lastNet + 1 ); + + for( unsigned int i = prevSize; i < m_nets.size(); i++ ) + m_nets[i] = new RN_NET; + } + + auto clusters = m_connAlgo->GetClusters(); + + int dirtyNets = 0; + + for( int net = 0; net < lastNet; net++ ) + if( m_connAlgo->IsNetDirty( net ) ) + { + m_nets[net]->Clear(); + dirtyNets++; + } + + + + for( auto c : clusters ) + { + int net = c->OriginNet(); + + if( m_connAlgo->IsNetDirty( net ) ) + { + addRatsnestCluster( c ); + } + } + + m_connAlgo->ClearDirtyFlags(); + + updateRatsnest(); +} + + +void CONNECTIVITY_DATA::blockRatsnestItems( const std::vector& aItems ) +{ + std::vector citems; + + for( auto item : aItems ) + { + if( item->Type() == PCB_MODULE_T ) + { + for( auto pad : static_cast(item)->PadsIter() ) + citems.push_back( pad ); + } + else + { + citems.push_back( static_cast(item) ); + } + } + + for( auto item : citems ) + { + auto& entry = m_connAlgo->ItemEntry( item ); + + for( auto cnItem : entry.GetItems() ) + { + for( auto anchor : cnItem->Anchors() ) + anchor->SetNoLine( true ); + } + } +} + + +int CONNECTIVITY_DATA::GetNetCount() const +{ + return m_connAlgo->NetCount(); +} + + +void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, + std::vector& aIslands ) +{ + m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands ); +} + + +void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector& aItems ) +{ + m_dynamicConnectivity.reset( new CONNECTIVITY_DATA ); + m_dynamicConnectivity->Build( aItems ); + + m_dynamicRatsnest.clear(); + + blockRatsnestItems( aItems ); + + for( unsigned int nc = 1; nc < m_dynamicConnectivity->m_nets.size(); nc++ ) + { + auto dynNet = m_dynamicConnectivity->m_nets[nc]; + + if( dynNet->GetNodeCount() != 0 ) + { + auto ourNet = m_nets[nc]; + CN_ANCHOR_PTR nodeA, nodeB; + + if( ourNet->NearestBicoloredPair( *dynNet, nodeA, nodeB ) ) + { + RN_DYNAMIC_LINE l; + l.a = nodeA->Pos(); + l.b = nodeB->Pos(); + l.netCode = nc; + + m_dynamicRatsnest.push_back( l ); + } + } + } + + for( auto net : m_dynamicConnectivity->m_nets ) + { + if( !net ) + continue; + + const auto& edges = net->GetUnconnected(); + + if( edges.empty() ) + continue; + + for( const auto& edge : edges ) + { + const auto& nodeA = edge.GetSourceNode(); + const auto& nodeB = edge.GetTargetNode(); + RN_DYNAMIC_LINE l; + + l.a = nodeA->Pos(); + l.b = nodeB->Pos(); + l.netCode = 0; + m_dynamicRatsnest.push_back( l ); + } + } +} + + +const std::vector& CONNECTIVITY_DATA::GetDynamicRatsnest() const +{ + return m_dynamicRatsnest; +} + + +void CONNECTIVITY_DATA::ClearDynamicRatsnest() +{ + m_dynamicConnectivity.reset(); + m_dynamicRatsnest.clear(); +} + + +void CONNECTIVITY_DATA::PropagateNets() +{ + m_connAlgo->PropagateNets(); +} + + +unsigned int CONNECTIVITY_DATA::GetUnconnectedCount() const +{ + unsigned int unconnected = 0; + + for( auto net : m_nets ) + { + if( !net ) + continue; + + const auto& edges = net->GetUnconnected(); + + if( edges.empty() ) + continue; + + unconnected += edges.size(); + } + + return unconnected; +} + + +void CONNECTIVITY_DATA::Clear() +{ + for( auto net : m_nets ) + delete net; + + m_nets.clear(); +} + + +const std::list CONNECTIVITY_DATA::GetConnectedItems( + const BOARD_CONNECTED_ITEM* aItem, + const KICAD_T aTypes[] ) const +{ + std::list rv; + const auto clusters = m_connAlgo->SearchClusters( CN_CONNECTIVITY_ALGO::CSM_CONNECTIVITY_CHECK, aTypes, aItem->GetNetCode() ); + + for ( auto cl : clusters ) + if ( cl->Contains (aItem ) ) + { + for ( const auto item : *cl ) + rv.push_back( item->Parent() ); + } + + return rv; +} + + +const std::list CONNECTIVITY_DATA::GetNetItems( + int aNetCode, + const KICAD_T aTypes[] ) const +{ + +} + +bool CONNECTIVITY_DATA::CheckConnectivity( std::vector& aReport ) +{ + RecalculateRatsnest(); + + for ( auto net : m_nets ) + { + if ( net ) + { + 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.anchorA = edge.GetSourceNode()->Pos(); + ent.anchorB = edge.GetTargetNode()->Pos(); + aReport.push_back( ent ); + } + } + } + return aReport.empty(); +} diff --git a/pcbnew/connectivity.h b/pcbnew/connectivity.h new file mode 100644 index 0000000000..d04487b8ca --- /dev/null +++ b/pcbnew/connectivity.h @@ -0,0 +1,204 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013-2017 CERN + * @author Maciej Suminski + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __CONNECTIVITY_H +#define __CONNECTIVITY_H + +#include + +#include +#include +#include +#include + +#include + +class CN_ITEM; +class CN_CLUSTER; +class CN_CONNECTIVITY_ALGO; +class BOARD; +class BOARD_CONNECTED_ITEM; +class BOARD_ITEM; +class ZONE_CONTAINER; +class RN_DATA; +class RN_NET; + +struct CN_DISJOINT_NET_ENTRY +{ + int net; + BOARD_CONNECTED_ITEM* a, * b; + VECTOR2I anchorA, anchorB; +}; + +struct RN_DYNAMIC_LINE +{ + int netCode; + VECTOR2I a, b; +}; + +// a wrapper class encompassing the connectivity computation algorithm and the +class CONNECTIVITY_DATA +{ +public: + CONNECTIVITY_DATA(); + ~CONNECTIVITY_DATA(); + + + /** + * Function Build() + * Builds the connectivity database for the board aBoard. + */ + void Build( BOARD* aBoard ); + + /** + * Function Build() + * Builds the connectivity database for a set of items aItems. + */ + void Build( const std::vector& aItems ); + + /** + * Function Add() + * Adds an item to the connectivity data. + * @param aItem is an item to be added. + * @return True if operation succeeded. + */ + bool Add( BOARD_ITEM* aItem ); + + /** + * Function Remove() + * Removes an item from the connectivity data. + * @param aItem is an item to be updated. + * @return True if operation succeeded. + */ + bool Remove( BOARD_ITEM* aItem ); + + /** + * Function Update() + * Updates the connectivity data for an item. + * @param aItem is an item to be updated. + * @return True if operation succeeded. + */ + bool Update( BOARD_ITEM* aItem ); + + /** + * Function Clear() + * Erases the connectivity database. + */ + + void Clear(); + + /** + * Function GetNetCount() + * Returns the total number of nets in the connectivity database. + */ + int GetNetCount() const; + + /** + * Function GetRatsnestForNet() + * Returns the ratsnest, expressed as a set of graph edges for a given net. + */ + RN_NET* GetRatsnestForNet( int aNet ) + { + return m_nets[aNet]; + } + + /** + * Function PropagateNets() + * Propagates the net codes from the source pads to the tracks/vias. + */ + void PropagateNets(); + + bool CheckConnectivity( std::vector& aReport ); + + /** + * Function FindIsolatedCopperIslands() + * Searches for copper islands in zone aZone that are not connected to any pad. + * @param aZone zone to test + * @param aIslands list of islands that have no connections (outline indices in the polygon set) + */ + void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector& aIslands ); + + /** + * Function RecalculateRatsnest() + * Updates the ratsnest for the board. + */ + void RecalculateRatsnest(); + + /** + * Function GetUnconnectedCount() + * Returns the number of remaining edges in the ratsnest. + */ + unsigned int GetUnconnectedCount() const; + + + /** + * Function ClearDynamicRatsnest() + * Erases the temporary dynamic ratsnest (i.e. the ratsnest lines that) + * pcbnew displays when moving an item/set of items + */ + void ClearDynamicRatsnest(); + + /** + * Function ComputeDynamicRatsnest() + * Calculates the temporary dynamic ratsnest (i.e. the ratsnest lines that) + * for the set of items aItems. + */ + void ComputeDynamicRatsnest( const std::vector& aItems ); + + + const std::vector& GetDynamicRatsnest() const; + + /** + * Function GetConnectedItems() + * Returns a list of items connected to a source item aItem. + * @param aItem is the reference item to find other connected items. + * @param aTypes allows to filter by item types. + */ + const std::list GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, + const KICAD_T aTypes[] ) const; + + /** + * Function GetNetItems() + * Returns the list of items that belong to a certain net. + * @param aNetCode is the net code. + * @param aTypes allows to filter by item types. + */ + const std::list GetNetItems( int aNetCode, + const KICAD_T aTypes[] ) const; + +private: + + void updateRatsnest(); + void addRatsnestCluster( std::shared_ptr aCluster ); + void blockRatsnestItems( const std::vector& aItems ); + + std::unique_ptr m_dynamicConnectivity; + std::shared_ptr m_connAlgo; + + std::vector m_dynamicRatsnest; + std::vector m_nets; +}; + +#endif diff --git a/pcbnew/connectivity_algo.cpp b/pcbnew/connectivity_algo.cpp new file mode 100644 index 0000000000..43067fbb0d --- /dev/null +++ b/pcbnew/connectivity_algo.cpp @@ -0,0 +1,903 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016-2017 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#ifdef PROFILE +#include +#endif + +bool operator<( const CN_ANCHOR_PTR a, const CN_ANCHOR_PTR b ) +{ + if( a->Pos().x == b->Pos().x ) + return a->Pos().y < b->Pos().y; + else + return a->Pos().x < b->Pos().x; +} + +bool CN_ANCHOR::IsDirty() const +{ + return m_item->Dirty(); +} + +CN_CLUSTER::CN_CLUSTER() +{ + m_items.reserve( 64 ); + m_originPad = nullptr; + m_originNet = -1; + m_conflicting = false; +} + +CN_CLUSTER::~CN_CLUSTER() +{ + +} + +wxString CN_CLUSTER::OriginNetName() const +{ + if( !m_originPad ) + return ""; + else + return m_originPad->Parent()->GetNetname(); +} + +bool CN_CLUSTER::Contains( const CN_ITEM* aItem ) +{ + return std::find( m_items.begin(), m_items.end(), aItem ) != m_items.end(); +} + + +bool CN_CLUSTER::Contains( const BOARD_CONNECTED_ITEM* aItem ) +{ + for( auto item : m_items ) + if( item->Parent() == aItem ) + return true; + return false; +} + +void CN_ITEM::Dump() +{ + printf(" valid: %d, connected: \n", !!Valid()); + for(auto i : m_connected ) + { + TRACK *t = static_cast(i->Parent()); + printf(" - %p %d\n", t, t->Type() ); + } +} + +void CN_CLUSTER::Dump() +{ + for( auto item : m_items ) + { + wxLogTrace( "CN", " - item : %p bitem : %p type : %d inet %s\n", item, item->Parent(), + item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() ); + printf( "- item : %p bitem : %p type : %d inet %s\n", item, item->Parent(), + item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() ); + item->Dump(); + + } +} + +void CN_CLUSTER::Add( CN_ITEM* item ) +{ + + m_items.push_back( item ); + + if ( m_originNet < 0 ) + { + m_originNet = item->Net(); + } + + if( item->Parent()->Type() == PCB_PAD_T ) + { + if( !m_originPad ) + { + m_originPad = item; + m_originNet = item->Net(); + } + if( m_originPad && item->Net() != m_originNet ) + { + m_conflicting = true; + } + } +} + + +CN_CONNECTIVITY_ALGO::CN_CONNECTIVITY_ALGO() +{ +} + +CN_CONNECTIVITY_ALGO::~CN_CONNECTIVITY_ALGO() +{ + Clear(); +} + +bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem ) +{ + markItemNetAsDirty ( aItem ); + + switch( aItem->Type() ) + { + case PCB_MODULE_T: + for ( auto pad : static_cast (aItem ) -> PadsIter() ) + { + m_itemMap[ static_cast( pad ) ].MarkItemsAsInvalid(); + m_itemMap.erase ( static_cast( pad ) ); + } + m_padList.SetDirty(true); + + break; + case PCB_PAD_T: + m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); + m_itemMap.erase ( static_cast( aItem ) ); + m_padList.SetDirty(true); + break; + + case PCB_TRACE_T: + m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); + m_itemMap.erase ( static_cast( aItem ) ); + m_trackList.SetDirty(true); + break; + + case PCB_VIA_T: + m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); + m_itemMap.erase ( static_cast( aItem ) ); + m_viaList.SetDirty(true); + + break; + + + case PCB_ZONE_AREA_T: + case PCB_ZONE_T: + { + m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); + m_itemMap.erase ( static_cast( aItem ) ); + m_zoneList.SetDirty(true); + + break; + } + default: + return false; + } + + return true; + +} + +void CN_CONNECTIVITY_ALGO::markItemNetAsDirty( const BOARD_ITEM *aItem ) +{ + if ( aItem->IsConnected () ) + { + auto citem = static_cast ( aItem ); + markNetAsDirty ( citem->GetNetCode() ); + } else { + if ( aItem->Type() == PCB_MODULE_T ) + { + auto mod = static_cast ( aItem ); + for( D_PAD* pad = mod->Pads(); pad; pad = pad->Next() ) + markNetAsDirty ( pad->GetNetCode() ); + } + } +} + + +bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem ) + { + markItemNetAsDirty ( aItem ); + + switch( aItem->Type() ) + { + case PCB_MODULE_T: + for ( auto pad : static_cast (aItem ) -> PadsIter() ) + { + if ( m_itemMap.find ( pad ) != m_itemMap.end() ) + return false; + + add( m_padList, pad ); + } + + break; + + case PCB_PAD_T: + if ( m_itemMap.find ( static_cast ( aItem ) ) != m_itemMap.end() ) + return false; + + add( m_padList, static_cast ( aItem ) ); + + break; + + case PCB_TRACE_T: + { + if ( m_itemMap.find ( static_cast ( aItem ) ) != m_itemMap.end() ) + return false; + + add( m_trackList, static_cast ( aItem ) ); + + break; + } + case PCB_VIA_T: + if ( m_itemMap.find ( static_cast ( aItem ) ) != m_itemMap.end() ) + return false; + + add( m_viaList, static_cast (aItem )); + + break; + + + case PCB_ZONE_AREA_T: + case PCB_ZONE_T: + { + auto zone = static_cast ( aItem ); + + if ( m_itemMap.find ( static_cast ( aItem ) ) != m_itemMap.end() ) + return false; + + m_itemMap[zone] = ITEM_MAP_ENTRY(); + + for( auto zitem : m_zoneList.Add( zone ) ) + m_itemMap[zone].Link(zitem); + + break; + } + default: + return false; + } + + return true; + } + + +void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) +{ + int totalDirtyCount = 0; + + if ( m_lastSearchWithZones != aIncludeZones ) + { + m_padList.MarkAllAsDirty(); + m_viaList.MarkAllAsDirty(); + m_trackList.MarkAllAsDirty(); + m_zoneList.MarkAllAsDirty(); + } + + m_lastSearchWithZones = aIncludeZones; + + auto checkForConnection = [] ( const CN_ANCHOR_PTR point, CN_ITEM *aRefItem, int aMaxDist = 0) + { + const auto parent = aRefItem->Parent(); + + + assert ( point->Item() ); + assert ( point->Item()->Parent() ); + assert ( aRefItem->Parent() ); + + if ( !point->Item()->Valid() ) + return; + + if ( !aRefItem->Valid() ) + return; + + if( parent == point->Item()->Parent() ) + return; + + if( !( parent->GetLayerSet() & + point->Item()->Parent()->GetLayerSet() ).any() ) + return; + + switch ( parent->Type() ) + { + case PCB_PAD_T: + case PCB_VIA_T: + + if( parent->HitTest( wxPoint( point->Pos().x, point->Pos().y ) ) ) + CN_ITEM::Connect( aRefItem, point->Item() ); + + break; + case PCB_TRACE_T: + { + const auto track = static_cast ( parent ); + + const VECTOR2I d_start( VECTOR2I( track->GetStart() ) - point->Pos() ); + const VECTOR2I d_end( VECTOR2I( track->GetEnd() ) - point->Pos() ); + + if( d_start.EuclideanNorm() < aMaxDist + || d_end.EuclideanNorm() < aMaxDist ) + CN_ITEM::Connect( aRefItem, point->Item() ); + break; + + } + + case PCB_ZONE_T: + case PCB_ZONE_AREA_T: + { + const auto zone = static_cast ( parent ); + auto zoneItem = static_cast ( aRefItem ); + + if( point->Item()->Net() != parent->GetNetCode() ) + return; + + if( !( zone->GetLayerSet() & + point->Item()->Parent()->GetLayerSet() ).any() ) + return; + + if ( zoneItem->ContainsAnchor ( point ) ) + { + CN_ITEM::Connect( zoneItem, point->Item() ); + } + + break; + + } + default : + assert ( false ); + } + }; + + auto checkInterZoneConnection = [] ( CN_ZONE* testedZone, CN_ZONE *aRefZone ) + { + const auto parentZone = static_cast(aRefZone->Parent()); + + if( testedZone->Parent()->Type () != PCB_ZONE_AREA_T ) + return; + + if (testedZone == aRefZone) + return; + + if (testedZone->Parent() == aRefZone->Parent()) + return; + + + if( testedZone->Net() != parentZone->GetNetCode() ) + return; // we only test zones belonging to the same net + + if( !( testedZone->Parent()->GetLayerSet() & + parentZone->GetLayerSet() ).any() ) + return; // and on same layer + + const auto& outline = parentZone->GetFilledPolysList().COutline( aRefZone->SubpolyIndex() ); + + for( int i = 0; i < outline.PointCount(); i++ ) + if( testedZone ->ContainsPoint( outline.CPoint(i) ) ) + { + CN_ITEM::Connect ( aRefZone, testedZone ); + return; + } + + const auto testedZoneParent = static_cast(testedZone->Parent()); + + const auto& outline2 = testedZoneParent->GetFilledPolysList().COutline( testedZone->SubpolyIndex() ); + + for( int i = 0; i < outline2.PointCount(); i++ ) + if( aRefZone ->ContainsPoint( outline2.CPoint(i) ) ) + { + CN_ITEM::Connect ( aRefZone, testedZone ); + return; + } + + }; + + +#ifdef CONNECTIVITY_DEBUG + printf("Search start\n"); +#endif + + std::vector garbage; + garbage.reserve(1024); + + m_padList.RemoveInvalidItems(garbage); + m_viaList.RemoveInvalidItems(garbage); + m_trackList.RemoveInvalidItems(garbage); + m_zoneList.RemoveInvalidItems(garbage); + + for ( auto item : garbage ) + delete item; + + //auto all = allItemsInBoard(); + + #ifdef CONNECTIVITY_DEBUG + for ( auto item : m_padList ) + if ( all.find( item->Parent() ) == all.end() ) { printf("FAiling pad : %p\n", item->Parent() ); assert ( false ); } + for ( auto item : m_viaList ) + if ( all.find( item->Parent() ) == all.end() ) { printf("FAiling via : %p\n", item->Parent() ); assert ( false ); } + for ( auto item : m_trackList ) + if ( all.find( item->Parent() ) == all.end() ) { printf("FAiling track : %p\n", item->Parent() ); assert ( false ); } + for ( auto item : m_zoneList ) + if ( all.find( item->Parent() ) == all.end() ) { printf("FAiling zome : %p\n", item->Parent() ); assert ( false ); } + #endif + + + using namespace std::placeholders; + +#ifdef PROFILE + PROF_COUNTER search_cnt( "search-connections" ); + PROF_COUNTER search_basic( "search-basic" ); +#endif + + if ( m_padList.IsDirty() || m_trackList.IsDirty() || m_viaList.IsDirty() ) + { + totalDirtyCount++; + + for( auto padItem : m_padList ) + { + auto pad = static_cast ( padItem->Parent() ); + auto searchPads = std::bind( checkForConnection, _1, padItem ); + + m_padList.FindNearby( pad->ShapePos(), pad->GetBoundingRadius(), searchPads ); + m_trackList.FindNearby( pad->ShapePos(), pad->GetBoundingRadius(), searchPads ); + m_viaList.FindNearby( pad->ShapePos(), pad->GetBoundingRadius(), searchPads ); + } + + for( auto& trackItem : m_trackList ) + { + auto track = static_cast ( trackItem->Parent() ); + int dist_max = track->GetWidth() / 2; + auto searchTracks = std::bind( checkForConnection, _1, trackItem, dist_max ); + + m_trackList.FindNearby( track->GetStart(), dist_max, searchTracks ); + m_trackList.FindNearby( track->GetEnd(), dist_max, searchTracks ); + } + + for( auto& viaItem : m_viaList ) + { + auto via = static_cast ( viaItem->Parent() ); + int dist_max = via->GetWidth() / 2; + auto searchVias = std::bind( checkForConnection, _1, viaItem, dist_max ); + + totalDirtyCount++; + m_viaList.FindNearby( via->GetStart(), dist_max, searchVias ); + m_trackList.FindNearby( via->GetStart(), dist_max, searchVias ); + } + } + +#ifdef PROFILE + search_basic.Show(); +#endif + + if( aIncludeZones ) + { + for( auto& item : m_zoneList ) + { + auto zoneItem = static_cast (item); + auto searchZones = std::bind( checkForConnection, _1, zoneItem ); + + if( zoneItem->Dirty() ) + { + totalDirtyCount++; + m_viaList.FindNearby( zoneItem->BBox(), searchZones ); + m_trackList.FindNearby( zoneItem->BBox(), searchZones ); + m_padList.FindNearby( zoneItem->BBox(), searchZones ); + m_zoneList.FindNearbyZones( zoneItem->BBox(), std::bind( checkInterZoneConnection, _1, zoneItem ) ); + } + + + + } + + m_zoneList.ClearDirtyFlags(); + } + + m_padList.ClearDirtyFlags(); + m_viaList.ClearDirtyFlags(); + m_trackList.ClearDirtyFlags(); + +#ifdef CONNECTIVITY_DEBUG + printf("Search end\n"); +#endif + +#ifdef PROFILE + search_cnt.Show(); +#endif + +} + +void CN_ITEM::RemoveInvalidRefs() +{ + auto lastConn = std::remove_if(m_connected.begin(), m_connected.end(), [] ( CN_ITEM * item) { + return !item->Valid(); + + } ); + + m_connected.resize( lastConn - m_connected.begin() ); +} + +void CN_LIST::RemoveInvalidItems( std::vector& aGarbage ) +{ + auto lastAnchor = std::remove_if(m_anchors.begin(), m_anchors.end(), [] ( const CN_ANCHOR_PTR anchor) { + return !anchor->Valid(); + + } ); + + m_anchors.resize( lastAnchor - m_anchors.begin() ); + + auto lastItem = std::remove_if(m_items.begin(), m_items.end(), [&aGarbage] ( CN_ITEM * item) { + if ( !item->Valid() ) + { + aGarbage.push_back ( item ); + return true; + } + + return false; + } ); + + m_items.resize( lastItem - m_items.begin() ); + + // fixme: mem leaks + + for ( auto item : m_items ) + item->RemoveInvalidRefs(); +} + + +bool CN_CONNECTIVITY_ALGO::isDirty() const +{ + return m_viaList.IsDirty() || m_trackList.IsDirty() || m_zoneList.IsDirty() || m_padList.IsDirty(); +} + +const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode ) +{ + constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_AREA_T, PCB_MODULE_T, EOT }; + return SearchClusters ( aMode, types, -1 ); +} + +const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode, const KICAD_T aTypes[], int aSingleNet ) +{ + bool includeZones = ( aMode != CSM_PROPAGATE ); + bool withinAnyNet = ( aMode != CSM_PROPAGATE ); + + std::deque Q; + CN_ITEM* head = nullptr; + CLUSTERS clusters; + + if ( isDirty() ) + searchConnections( includeZones ); + + auto addToSearchList = [&head, withinAnyNet, aSingleNet, aTypes] ( CN_ITEM *aItem ) + { + if ( withinAnyNet && aItem->Net() <= 0 ) + return; + + if( !aItem->Valid() ) + return; + + if ( aSingleNet >=0 && aItem->Net() != aSingleNet ) + return; + + bool found = false; + + for ( int i = 0; aTypes[i] != EOT; i++ ) + if ( aItem->Parent()->Type() == aTypes[i] ) + { + found = true; + break; + } + + if (!found) + return; + + aItem->ListClear(); + aItem->SetVisited( false ); + + if ( !head ) + head = aItem; + else + head->ListInsert( aItem ); + }; + + std::for_each( m_padList.begin(), m_padList.end(), addToSearchList ); + std::for_each( m_trackList.begin(), m_trackList.end(), addToSearchList ); + std::for_each( m_viaList.begin(), m_viaList.end(), addToSearchList ); + + if (includeZones) + { + std::for_each( m_zoneList.begin(), m_zoneList.end(), addToSearchList ); + } + + + while( head ) + { + CN_CLUSTER_PTR cluster ( new CN_CLUSTER() ); + + Q.clear(); + CN_ITEM* root = head; + root->SetVisited ( true ); + + head = root->ListRemove(); + + Q.push_back( root ); + + while( Q.size() ) + { + CN_ITEM* current = Q.front(); + + Q.pop_front(); + cluster->Add( current ); + + for( auto n : current->ConnectedItems() ) + { + if ( withinAnyNet && n->Net() != root->Net() ) + continue; + + if( !n->Visited() && n->Valid() ) + { + n->SetVisited( true ); + Q.push_back( n ); + head = n->ListRemove(); + } + } + } + + clusters.push_back( cluster ); + } + + + std::sort( clusters.begin(), clusters.end(), []( CN_CLUSTER_PTR a, CN_CLUSTER_PTR b ) { + return a->OriginNet() < b->OriginNet(); + } ); + +#ifdef CONNECTIVITY_DEBUG + printf("Active clusters: %d\n"); + for (auto cl:clusters) + { + printf("Net %d\n", cl->OriginNet()); + cl->Dump(); + } +#endif + + return clusters; +} + +void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard ) +{ + for( int i = 0; iGetAreaCount(); i++ ) + { + auto zone = aBoard->GetArea( i ); + Add( zone ); + } + + for( auto tv : aBoard->Tracks() ) + Add( tv ); + + for( auto mod : aBoard->Modules() ) + for( auto pad : mod->PadsIter() ) + Add( pad ); + + /*wxLogTrace( "CN", "zones : %lu, pads : %lu vias : %lu tracks : %lu\n", + m_zoneList.Size(), m_padList.Size(), + m_viaList.Size(), m_trackList.Size() );*/ +} + +void CN_CONNECTIVITY_ALGO::Build( const std::vector &aItems ) +{ + for ( auto item : aItems ) + { + switch( item->Type() ) + { + case PCB_TRACE_T: + case PCB_VIA_T: + case PCB_ZONE_T: + case PCB_PAD_T: + Add( item ); + break; + + case PCB_MODULE_T: + { + for( auto pad : static_cast(item)->PadsIter() ) + { + Add( pad ); + } + + break; + } + + default: + break; + } + } +} + +void CN_CONNECTIVITY_ALGO::propagateConnections() +{ + for( auto cluster : m_connClusters ) + { + if( cluster->IsConflicting() ) + { + wxLogTrace( "CN", "Conflicting nets in cluster %p\n", cluster.get() ); + } + else if( cluster->IsOrphaned() ) + { + wxLogTrace( "CN", "Skipping orphaned cluster %p [net: %s]\n", cluster.get(), + (const char*) cluster->OriginNetName() ); + } + else if( cluster->HasValidNet() ) + { + // normal cluster: just propagate from the pads + int n_changed = 0; + + for( auto item : *cluster ) + { + if( item->CanChangeNet() ) + { + item->Parent()->SetNetCode( cluster->OriginNet() ); + n_changed++; + } + } + + if( n_changed ) + wxLogTrace( "CN", "Cluster %p : net : %d %s\n", cluster.get(), + cluster->OriginNet(), (const char*) cluster->OriginNetName() ); + else + wxLogTrace( "CN", "Cluster %p : nothing to propagate\n", cluster.get() ); + } + else + { + wxLogTrace( "CN", "Cluster %p : connected to unused net\n", cluster.get() ); + } + } +} + + +void CN_CONNECTIVITY_ALGO::PropagateNets() +{ + //searchConnections( false ); + m_connClusters = SearchClusters( CSM_PROPAGATE ); + propagateConnections(); +} + +void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector& aIslands ) +{ + + if ( aZone->GetFilledPolysList().IsEmpty() ) + return; + + aIslands.clear(); + + Remove( aZone ); + Add( aZone ); + + m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK ); + + for( auto cluster : m_connClusters ) + if( cluster->Contains( aZone ) && cluster->IsOrphaned() ) + { + for( auto z : *cluster ) + { + if( z->Parent() == aZone ) + { + aIslands.push_back( static_cast(z)->SubpolyIndex() ); + } + } + } + + wxLogTrace( "CN", "Found %llu isolated islands\n", aIslands.size() ); +} + +const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters() +{ + m_ratsnestClusters = SearchClusters( CSM_RATSNEST ); + return m_ratsnestClusters; +}; + +void CN_CONNECTIVITY_ALGO::markNetAsDirty ( int aNet ) +{ + if(aNet <= 0) + return; + + if(m_dirtyNets.size() <= aNet ) + m_dirtyNets.resize(aNet + 1); + m_dirtyNets[ aNet ] = true; +} + +int CN_ITEM::AnchorCount() const +{ + return m_parent->Type() == PCB_TRACE_T ? 2 : 1; +} + +const VECTOR2I CN_ITEM::GetAnchor( int n ) const +{ + switch ( m_parent->Type() ) + { + case PCB_PAD_T: + return static_cast(m_parent)->ShapePos(); + break; + + case PCB_TRACE_T: + { + auto tr = static_cast(m_parent); + return (n == 0 ? tr->GetStart() : tr->GetEnd() ); + + break; + } + + case PCB_VIA_T: + return static_cast(m_parent)->GetStart(); + + default: + assert(false); + return VECTOR2I(); + } +} + +int CN_ZONE::AnchorCount() const +{ + const auto zone = static_cast ( Parent() ); + const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex ); + + return outline.PointCount() ? 1 : 0; +} + +const VECTOR2I CN_ZONE::GetAnchor(int n ) const +{ + const auto zone = static_cast ( Parent() ); + const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex ); + + return outline.CPoint(0); +} + +/*const std::vector CN_CLUSTER::GetAnchors() +{ + std::vector anchors; + + for ( auto item : m_items ) + { + int cnt = item->AnchorCount(); + for (int i = 0 ; i < cnt; i++) + { + anchors.push_back( item->GetAnchor(i) ); + } + } + + return anchors; +}*/ + +int CN_ITEM::Net() const +{ + if (!m_parent) + return -1; + return m_parent->GetNetCode(); +} + + +BOARD_CONNECTED_ITEM *CN_ANCHOR::Parent() const +{ + return m_item->Parent(); +} + + +bool CN_ANCHOR::Valid() const +{ + if( !m_item ) + return false; + return m_item->Valid(); +} + +void CN_CONNECTIVITY_ALGO::Clear() +{ + m_ratsnestClusters.clear(); + m_connClusters.clear(); + m_itemMap.clear(); + m_padList.Clear(); + m_trackList.Clear(); + m_viaList.Clear(); + m_zoneList.Clear(); + +} diff --git a/pcbnew/connectivity_algo.h b/pcbnew/connectivity_algo.h new file mode 100644 index 0000000000..6fde29ab03 --- /dev/null +++ b/pcbnew/connectivity_algo.h @@ -0,0 +1,926 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013-2017 CERN + * @author Maciej Suminski + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +// #define CONNECTIVITY_DEBUG + +#ifndef __CONNECTIVITY_ALGO_H +#define __CONNECTIVITY_ALGO_H + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +class CN_ITEM; +class CN_CONNECTIVITY_ALGO_IMPL; +class CN_RATSNEST_NODES; +class CN_CLUSTER; +class BOARD; +class BOARD_CONNECTED_ITEM; +class BOARD_ITEM; +class ZONE_CONTAINER; + +class CN_ANCHOR +{ +public: + CN_ANCHOR() + { + m_item = nullptr; + } + + CN_ANCHOR( const VECTOR2I& aPos, CN_ITEM* aItem ) + { + m_pos = aPos; + m_item = aItem; + assert( m_item ); + } + + bool Valid() const; + + + CN_ITEM* Item() const + { + return m_item; + } + + BOARD_CONNECTED_ITEM* Parent() const; + + const VECTOR2I& Pos() const + { + return m_pos; + } + + bool IsDirty() const; + + /// Returns tag, common identifier for connected nodes + inline int GetTag() const + { + return m_tag; + } + + /// Sets tag, common identifier for connected nodes + inline void SetTag( int aTag ) + { + m_tag = aTag; + } + + /// Decides whether this node can be a ratsnest line target + inline void SetNoLine( bool aEnable ) + { + m_noline = aEnable; + } + + /// Returns true if this node can be a target for ratsnest lines + inline const bool& GetNoLine() const + { + return m_noline; + } + + inline void SetCluster( std::shared_ptr aCluster ) + { + m_cluster = aCluster; + } + + inline std::shared_ptr GetCluster() const + { + return m_cluster; + } + + // Tag used for unconnected items. + static const int TAG_UNCONNECTED = -1; + +private: + /// Position of the anchor + VECTOR2I m_pos; + + /// Item owning the anchor + CN_ITEM* m_item = nullptr; + + /// Tag for quick connection resolution + int m_tag = -1; + + /// Whether it the node can be a target for ratsnest lines + bool m_noline = false; + + /// Cluster to which the anchor belongs + std::shared_ptr m_cluster; +}; + + +typedef std::shared_ptr CN_ANCHOR_PTR; +typedef std::vector CN_ANCHORS; + +class CN_EDGE +{ +public: + CN_EDGE() {}; + CN_EDGE( CN_ANCHOR_PTR aSource, CN_ANCHOR_PTR aTarget, int aWeight = 0 ) : + m_source( aSource ), + m_target( aTarget ), + m_weight( aWeight ) {} + + CN_ANCHOR_PTR GetSourceNode() const { return m_source; } + CN_ANCHOR_PTR GetTargetNode() const { return m_target; } + int GetWeight() const { return m_weight; } + + void SetSourceNode( const CN_ANCHOR_PTR& aNode ) { m_source = aNode; } + void SetTargetNode( const CN_ANCHOR_PTR& aNode ) { m_target = aNode; } + void SetWeight( unsigned int weight ) { m_weight = weight; } + +private: + CN_ANCHOR_PTR m_source; + CN_ANCHOR_PTR m_target; + unsigned int m_weight = 0; +}; + +class CN_CLUSTER +{ +private: + + bool m_conflicting = false; + int m_originNet = 0; + CN_ITEM* m_originPad = nullptr; + std::vector m_items; + +public: + CN_CLUSTER(); + ~CN_CLUSTER(); + + bool HasValidNet() const + { + return m_originNet >= 0; + } + + int OriginNet() const + { + return m_originNet; + } + + wxString OriginNetName() const; + + bool Contains( const CN_ITEM* aItem ); + bool Contains( const BOARD_CONNECTED_ITEM* aItem ); + void Dump(); + + int Size() const + { + return m_items.size(); + } + + bool HasNet() const + { + return m_originNet >= 0; + } + + bool IsOrphaned() const + { + return m_originPad == nullptr; + } + + bool IsConflicting() const + { + return m_conflicting; + } + + void Add( CN_ITEM* item ); + + using ITER = decltype(m_items)::iterator; + + ITER begin() { return m_items.begin(); }; + ITER end() { return m_items.end(); }; +}; + +typedef std::shared_ptr CN_CLUSTER_PTR; + +// a lightweight intrusive list container +template +class INTRUSIVE_LIST +{ +public: + INTRUSIVE_LIST() + { + ListClear(); + } + + void ListClear() + { + m_prev = nullptr; + m_next = nullptr; + m_root = (T*) this; + m_count = 1; + } + + T* ListRemove() + { + if( m_prev ) + m_prev->m_next = m_next; + + if( m_next ) + m_next->m_prev = m_prev; + + m_root->m_count--; + + T* rv = nullptr; + + if( m_prev ) + rv = m_prev; + else if( m_next ) + rv = m_next; + + m_root = nullptr; + m_prev = nullptr; + m_next = nullptr; + return rv; + } + + int ListSize() const + { + return m_root ? m_root->m_count : 0; + } + + void ListInsert( T* item ) + { + if( !m_root ) + m_root = item; + + if( m_next ) + m_next->m_prev = item; + + item->m_prev = (T*) this; + item->m_next = m_next; + item->m_root = m_root; + m_root->m_count++; + + m_next = item; + } + + T* ListNext() const { return m_next; }; + T* ListPrev() const { return m_prev; }; + +private: + int m_count; + T* m_prev; + T* m_next; + T* m_root; +}; + +// basic connectivity item +class CN_ITEM : public INTRUSIVE_LIST +{ +private: + BOARD_CONNECTED_ITEM* m_parent; + + using CONNECTED_ITEMS = std::vector; + +// list of items physically connected (touching) + CONNECTED_ITEMS m_connected; + + CN_ANCHORS m_anchors; + +// visited flag for the BFS scan + bool m_visited; + +// can the net propagator modify the netcode? + bool m_canChangeNet; + +// valid flag, used to identify garbage items (we use lazy removal) + bool m_valid; + +// dirty flag, used to identify recently added item not yet scanned into the connectivity search + bool m_dirty; + +public: + void Dump(); + + CN_ITEM( BOARD_CONNECTED_ITEM* aParent, bool aCanChangeNet, int aAnchorCount = 2 ) + { + m_parent = aParent; + m_canChangeNet = aCanChangeNet; + m_visited = false; + m_valid = true; + m_dirty = true; + m_anchors.reserve( 2 ); + } + + virtual ~CN_ITEM() {}; + + CN_ANCHOR_PTR AddAnchor( const VECTOR2I& aPos ) + { + m_anchors.emplace_back( std::make_shared ( aPos, this ) ); + //printf("%p add %d\n", this, m_anchors.size() ); + return m_anchors.back(); + } + + CN_ANCHORS& Anchors() + { + return m_anchors; + } + + void SetValid( bool aValid ) + { + m_valid = aValid; + } + + bool Valid() const + { + return m_valid; + } + + void SetDirty( bool aDirty ) + { + m_dirty = aDirty; + } + + bool Dirty() const + { + return m_dirty; + } + + BOARD_CONNECTED_ITEM* Parent() const + { + return m_parent; + } + + const CONNECTED_ITEMS& ConnectedItems() const + { + return m_connected; + } + + void ClearConnections() + { + m_connected.clear(); + } + + void SetVisited( bool aVisited ) + { + m_visited = aVisited; + } + + bool Visited() const + { + return m_visited; + } + + bool CanChangeNet() const + { + return m_canChangeNet; + } + + static void Connect( CN_ITEM* a, CN_ITEM* b ) + { + bool foundA = false, foundB = false; + + for( auto item : a->m_connected ) + { + if( item == b ) + { + foundA = true; + break; + } + } + + for( auto item : b->m_connected ) + { + if( item == a ) + { + foundB = true; + break; + } + } + + if( !foundA ) + a->m_connected.push_back( b ); + + if( !foundB ) + b->m_connected.push_back( a ); + } + + void RemoveInvalidRefs(); + + virtual int AnchorCount() const; + virtual const VECTOR2I GetAnchor( int n ) const; + + int Net() const; +}; + +typedef std::shared_ptr CN_ITEM_PTR; + +class CN_LIST +{ +private: + bool m_dirty; + std::vector m_anchors; + +protected: + std::vector m_items; + + void addAnchor( VECTOR2I pos, CN_ITEM* item ) + { + m_anchors.push_back( item->AddAnchor( pos ) ); + } + +private: + + void sort() + { + if( m_dirty ) + { + std::sort( m_anchors.begin(), m_anchors.end() ); + + m_dirty = false; + } + } + +public: + CN_LIST() + { + m_dirty = false; + }; + + void Clear() + { + for( auto item : m_items ) + delete item; + + m_items.clear(); + } + + using ITER = decltype(m_items)::iterator; + + ITER begin() { return m_items.begin(); }; + ITER end() { return m_items.end(); }; + + template + void FindNearby( VECTOR2I aPosition, int aDistMax, T aFunc, bool aDirtyOnly = false ); + + template + void FindNearby( BOX2I aBBox, T aFunc, bool aDirtyOnly = false ); + + void SetDirty( bool aDirty = true ) + { + m_dirty = aDirty; + } + + bool IsDirty() const + { + return m_dirty; + } + + void ClearConnections() + { + for( auto& anchor : m_anchors ) + anchor->Item()->ClearConnections(); + } + + void RemoveInvalidItems( std::vector& aGarbage ); + + void ClearDirtyFlags() + { + for( auto item : m_items ) + item->SetDirty( false ); + + SetDirty( false ); + } + + void MarkAllAsDirty() + { + for( auto item : m_items ) + item->SetDirty( true ); + + SetDirty( true ); + } + + int Size() const + { + return m_items.size(); + } +}; + + +class CN_PAD_LIST : public CN_LIST +{ +public: + + CN_ITEM* Add( D_PAD* pad ) + { + auto item = new CN_ITEM( pad, false, 2 ); + + addAnchor( pad->ShapePos(), item ); + m_items.push_back( item ); + + SetDirty(); + return item; + }; +}; + +class CN_TRACK_LIST : public CN_LIST +{ +public: + CN_ITEM* Add( TRACK* track ) + { + auto item = new CN_ITEM( track, true ); + + m_items.push_back( item ); + + addAnchor( track->GetStart(), item ); + addAnchor( track->GetEnd(), item ); + SetDirty(); + + return item; + }; +}; + +class CN_VIA_LIST : public CN_LIST +{ +public: + CN_ITEM* Add( VIA* via ) + { + auto item = new CN_ITEM( via, true ); + + m_items.push_back( item ); + addAnchor( via->GetStart(), item ); + SetDirty(); + return item; + }; +}; + +class CN_ZONE : public CN_ITEM +{ +public: + CN_ZONE( ZONE_CONTAINER* aParent, bool aCanChangeNet, int aSubpolyIndex ) : + CN_ITEM( aParent, aCanChangeNet ), + m_subpolyIndex( aSubpolyIndex ) + { + SHAPE_LINE_CHAIN outline = aParent->GetFilledPolysList().COutline( aSubpolyIndex ); + + outline.SetClosed( true ); + outline.Simplify(); + + m_cachedPoly.reset( new POLY_GRID_PARTITION( outline, 16 ) ); + } + + int SubpolyIndex() const + { + return m_subpolyIndex; + } + + bool ContainsAnchor( const CN_ANCHOR_PTR anchor ) const + { + return m_cachedPoly->ContainsPoint( anchor->Pos() ); + } + + bool ContainsPoint( const VECTOR2I p ) const + { + return m_cachedPoly->ContainsPoint( p ); + } + + const BOX2I& BBox() const + { + return m_cachedPoly->BBox(); + } + + virtual int AnchorCount() const; + virtual const VECTOR2I GetAnchor( int n ) const; + +private: + std::vector m_testOutlinePoints; + std::unique_ptr m_cachedPoly; + int m_subpolyIndex; +}; + + +class CN_ZONE_LIST : public CN_LIST +{ +public: + CN_ZONE_LIST() {} + + const std::vector Add( ZONE_CONTAINER* zone ) + { + const auto& polys = zone->GetFilledPolysList(); + + std::vector rv; + + for( int j = 0; j < polys.OutlineCount(); j++ ) + { + CN_ZONE* zitem = new CN_ZONE( zone, false, j ); + const auto& outline = zone->GetFilledPolysList().COutline( j ); + + for( int k = 0; k < outline.PointCount(); k++ ) + addAnchor( outline.CPoint( k ), zitem ); + + m_items.push_back( zitem ); + rv.push_back( zitem ); + SetDirty(); + } + + return rv; + }; + + template + void FindNearbyZones( BOX2I aBBox, T aFunc, bool aDirtyOnly = false ); +}; + +template +void CN_LIST::FindNearby( BOX2I aBBox, T aFunc, bool aDirtyOnly ) +{ + for( auto p : m_anchors ) + { + if( p->Valid() && aBBox.Contains( p->Pos() ) ) + if( !aDirtyOnly || p->IsDirty() ) + aFunc( p ); + + + } +} + + +template +void CN_ZONE_LIST::FindNearbyZones( BOX2I aBBox, T aFunc, bool aDirtyOnly ) +{ + for( auto item : m_items ) + { + auto zone = static_cast ( item ); + + if( aBBox.Intersects( zone->BBox() ) ) + { + if( !aDirtyOnly || zone->Dirty() ) + { + aFunc( zone ); + } + } + } +} + + +template +void CN_LIST::FindNearby( VECTOR2I aPosition, int aDistMax, T aFunc, bool aDirtyOnly ) +{ + /* 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 + */ + + sort(); + + int idxmax = m_anchors.size() - 1; + + int delta = idxmax + 1; + 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; + + auto p = m_anchors[idx]; + + int dist = p->Pos().x - aPosition.x; + + if( std::abs( dist ) <= aDistMax ) + { + break; // A good entry point is found. The list can be scanned from this point. + } + else if( p->Pos().x < aPosition.x ) // We should search after this point + { + idx += delta; + + if( idx > idxmax ) + idx = idxmax; + } + else // We should search before this p + { + idx -= delta; + + if( idx < 0 ) + idx = 0; + } + } + + /* Now explore the candidate list from the "best" entry point found + * (candidate "near" aPosition.x) + * We exp the list until abs(candidate->m_Point.x - aPosition.x) > aDistMashar* Currently a linear search is made because the number of candidates + * having the right X position is usually small + */ + // search next candidates in list + VECTOR2I diff; + + for( int ii = idx; ii <= idxmax; ii++ ) + { + auto& p = m_anchors[ii]; + diff = p->Pos() - aPosition;; + + if( std::abs( diff.x ) > aDistMax ) + break; // Exit: the distance is to long, we cannot find other candidates + + if( std::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 + if( p->Valid() ) + if( !aDirtyOnly || p->IsDirty() ) + aFunc( p ); + + + } + + // search previous candidates in list + for( int ii = idx - 1; ii >=0; ii-- ) + { + auto& p = m_anchors[ii]; + diff = p->Pos() - aPosition; + + if( abs( diff.x ) > aDistMax ) + break; + + if( abs( diff.y ) > aDistMax ) + continue; + + // We have here a good candidate:add it + if( p->Valid() ) + if( !aDirtyOnly || p->IsDirty() ) + aFunc( p ); + + + } +} + + +class CN_CONNECTIVITY_ALGO +{ +public: + enum CLUSTER_SEARCH_MODE + { + CSM_PROPAGATE, + CSM_CONNECTIVITY_CHECK, + CSM_RATSNEST + }; + + using CLUSTERS = std::vector; + +private: + + bool m_lastSearchWithZones = false; + + class ITEM_MAP_ENTRY + { +public: + ITEM_MAP_ENTRY( CN_ITEM* aItem = nullptr ) + { + if( aItem ) + m_items.push_back( aItem ); + } + + void MarkItemsAsInvalid() + { + for( auto item : m_items ) + { + item->SetValid( false ); + } + } + + void Link( CN_ITEM* aItem ) + { + m_items.push_back( aItem ); + } + + const std::list GetItems() const + { + return m_items; + } + + std::list m_items; + }; + + + CN_PAD_LIST m_padList; + CN_TRACK_LIST m_trackList; + CN_VIA_LIST m_viaList; + CN_ZONE_LIST m_zoneList; + + using ITEM_MAP_PAIR = std::pair ; + + std::unordered_map m_itemMap; + + CLUSTERS m_connClusters; + CLUSTERS m_ratsnestClusters; + std::vector m_dirtyNets; + + void searchConnections( bool aIncludeZones = false ); + + void update(); + void propagateConnections(); + + template + void add( Container& c, BItem brditem ) + { + auto item = c.Add( brditem ); + + m_itemMap[ brditem ] = ITEM_MAP_ENTRY( item ); + } + + bool addConnectedItem( BOARD_CONNECTED_ITEM* aItem ); + bool isDirty() const; + + void markNetAsDirty( int aNet ); + void markItemNetAsDirty( const BOARD_ITEM* aItem ); + +public: + + CN_CONNECTIVITY_ALGO(); + ~CN_CONNECTIVITY_ALGO(); + + ITEM_MAP_ENTRY& ItemEntry( const BOARD_CONNECTED_ITEM* aItem ) + { + return m_itemMap[ aItem ]; + } + + bool IsNetDirty( int aNet ) const + { + return m_dirtyNets[ aNet ]; + } + + void ClearDirtyFlags() + { + for( auto i = m_dirtyNets.begin(); i != m_dirtyNets.end(); ++i ) + *i = false; + } + + void GetDirtyClusters( CLUSTERS& aClusters ) + { + for( auto cl : m_ratsnestClusters ) + { + int net = cl->OriginNet(); + + if( net >= 0 && m_dirtyNets[net] ) + aClusters.push_back( cl ); + } + } + + int NetCount() const + { + return m_dirtyNets.size(); + } + + void Build( BOARD* aBoard ); + void Build( const std::vector& aItems ); + + void Clear(); + + bool Remove( BOARD_ITEM* aItem ); + bool Add( BOARD_ITEM* aItem ); + + const CLUSTERS SearchClusters( CLUSTER_SEARCH_MODE aMode, const KICAD_T aTypes[], int aSingleNet ); + const CLUSTERS SearchClusters( CLUSTER_SEARCH_MODE aMode ); + + void PropagateNets(); + void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector& aIslands ); + bool CheckConnectivity( std::vector& aReport ); + + const CLUSTERS& GetClusters(); + int GetUnconnectedCount(); +}; + +bool operator<( const CN_ANCHOR_PTR a, const CN_ANCHOR_PTR b ); + +#endif diff --git a/pcbnew/deltrack.cpp b/pcbnew/deltrack.cpp index 47f6d8389b..3b3059559c 100644 --- a/pcbnew/deltrack.cpp +++ b/pcbnew/deltrack.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -123,7 +123,7 @@ TRACK* PCB_EDIT_FRAME::Delete_Segment( wxDC* DC, TRACK* aTrack ) // Remove the segment from list, but do not delete it (it will be stored i n undo list) GetBoard()->Remove( aTrack ); - GetBoard()->GetRatsnest()->Remove( aTrack ); + GetBoard()->GetConnectivity()->Remove( aTrack ); // redraw the area where the track was m_canvas->RefreshDrawingRect( aTrack->GetBoundingBox() ); @@ -173,7 +173,7 @@ void PCB_EDIT_FRAME::Delete_net( wxDC* DC, TRACK* aTrack ) if( segm->GetNetCode() != netcode ) break; - GetBoard()->GetRatsnest()->Remove( segm ); + GetBoard()->GetConnectivity()->Remove( segm ); GetBoard()->m_Track.Remove( segm ); // redraw the area where the track was @@ -219,7 +219,7 @@ void PCB_EDIT_FRAME::Remove_One_Track( wxDC* DC, TRACK* pt_segm ) << TO_UTF8( TRACK::ShowState( tracksegment->GetStatus() ) ) \ << std::endl; ) - GetBoard()->GetRatsnest()->Remove( tracksegment ); + GetBoard()->GetConnectivity()->Remove( tracksegment ); GetBoard()->m_Track.Remove( tracksegment ); // redraw the area where the track was diff --git a/pcbnew/dialogs/dialog_general_options_BoardEditor_base.fbp b/pcbnew/dialogs/dialog_general_options_BoardEditor_base.fbp index 1b2e9dd53c..914e8c4f8a 100644 --- a/pcbnew/dialogs/dialog_general_options_BoardEditor_base.fbp +++ b/pcbnew/dialogs/dialog_general_options_BoardEditor_base.fbp @@ -846,7 +846,6 @@ bMiddleRightBoxSizer wxVERTICAL - 1 none @@ -1580,7 +1579,6 @@ sbSizer2PAN wxVERTICAL - 1 none @@ -1849,6 +1847,108 @@ + + 5 + wxEXPAND + 1 + + wxID_ANY + Advanced/Developer + + sbSizer4 + wxVERTICAL + none + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Use legacy connectivity algorithm + + 0 + + + 0 + + 1 + m_cbUseLegacyConnectivityAlgo + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcbnew/dialogs/dialog_global_deletion.cpp b/pcbnew/dialogs/dialog_global_deletion.cpp index 0074b71675..8a84970dab 100644 --- a/pcbnew/dialogs/dialog_global_deletion.cpp +++ b/pcbnew/dialogs/dialog_global_deletion.cpp @@ -160,7 +160,7 @@ void DIALOG_GLOBAL_DELETION::AcceptPcbDelete() masque_layer &= layers_filter; - for( item = pcb->m_Drawings; item; item = item->Next() ) + for( item = pcb->DrawingsList(); item; item = item->Next() ) { KICAD_T type = item->Type(); LAYER_NUM layer = item->GetLayer(); diff --git a/pcbnew/dialogs/dialog_netlist.cpp b/pcbnew/dialogs/dialog_netlist.cpp index bbde033e3a..4ab271245c 100644 --- a/pcbnew/dialogs/dialog_netlist.cpp +++ b/pcbnew/dialogs/dialog_netlist.cpp @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include @@ -330,9 +330,9 @@ void DIALOG_NETLIST::OnTestFootprintsClick( wxCommandEvent& event ) void DIALOG_NETLIST::OnCompileRatsnestClick( wxCommandEvent& event ) { // Rebuild the board connectivity: - if( m_parent->IsGalCanvasActive() ) - m_parent->GetBoard()->GetRatsnest()->ProcessBoard(); - + auto board = m_parent->GetBoard(); + //board->GetConnectivity()->Build( board ); + board->GetConnectivity()->PropagateNets(); m_parent->Compile_Ratsnest( m_dc, true ); } diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 034622f3d9..0fc321e5e1 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -205,7 +206,7 @@ void DRC::RunTests( wxTextCtrl* aMessages ) } m_pcbEditorFrame->Compile_Ratsnest( NULL, true ); - m_pcb->GetRatsnest()->ProcessBoard(); + //m_pcb->GetRatsnest()->ProcessBoard(); } // someone should have cleared the two lists before calling this. @@ -558,6 +559,25 @@ void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar ) void DRC::testUnconnected() { + std::vector report; + + auto connectivity = m_pcb->GetConnectivity(); + + connectivity->CheckConnectivity( report ); + + printf("Connectivity: %d unconnected\n", report.size()); + + for( auto ent : report ) + { + /* DRC_ITEM* uncItem = new DRC_ITEM( DRCE_UNCONNECTED_PADS, + msg, + padEnd->GetSelectMenuText(), + padStart->GetPosition(), padEnd->GetPosition() );*/ + + } + + +#if 0 if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 ) { wxClientDC dc( m_pcbEditorFrame->GetCanvas() ); @@ -588,6 +608,8 @@ void DRC::testUnconnected() m_unconnected.push_back( uncItem ); } +#endif + } diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index b6a1583da5..947532a6fb 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -584,7 +584,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in { wxBusyCursor dummy; // Displays an Hourglass while building connectivity Compile_Ratsnest( NULL, true ); - GetBoard()->GetRatsnest()->ProcessBoard(); + //GetBoard()->GetRatsnest()->ProcessBoard(); } // Update info shown by the horizontal toolbars diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp index 00290b9836..3c7c7cab2d 100644 --- a/pcbnew/loadcmp.cpp +++ b/pcbnew/loadcmp.cpp @@ -303,7 +303,7 @@ MODULE* PCB_BASE_FRAME::LoadModuleFromLibrary( const wxString& aLibrary, // (Can happen if the lib is an archive built from a board) Rotate_Module( NULL, module, 0, false ); - RecalculateAllTracksNetcode(); + //RecalculateAllTracksNetcode(); if( aDC ) module->Draw( m_canvas, aDC, GR_OR ); diff --git a/pcbnew/netlist.cpp b/pcbnew/netlist.cpp index 9024ffb20b..1d013425f3 100644 --- a/pcbnew/netlist.cpp +++ b/pcbnew/netlist.cpp @@ -170,7 +170,7 @@ void PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFileName, // Rebuild the board connectivity: Compile_Ratsnest( NULL, true ); - board->GetRatsnest()->ProcessBoard(); + //board->GetRatsnest()->ProcessBoard(); SetMsgPanel( board ); m_canvas->Refresh(); diff --git a/pcbnew/pcb_draw_panel_gal.cpp b/pcbnew/pcb_draw_panel_gal.cpp index 15c99e0100..37bf1d2aeb 100644 --- a/pcbnew/pcb_draw_panel_gal.cpp +++ b/pcbnew/pcb_draw_panel_gal.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -154,7 +155,7 @@ void PCB_DRAW_PANEL_GAL::DisplayBoard( const BOARD* aBoard ) m_view->Add( zone ); // Ratsnest - m_ratsnest.reset( new KIGFX::RATSNEST_VIEWITEM( aBoard->GetRatsnest() ) ); + m_ratsnest.reset( new KIGFX::RATSNEST_VIEWITEM( aBoard->GetConnectivity() ) ); m_view->Add( m_ratsnest.get() ); // Display settings @@ -334,7 +335,7 @@ void PCB_DRAW_PANEL_GAL::GetMsgPanelInfo( std::vector& aList ) txt.Printf( wxT( "%d" ), board->GetNetCount() ); aList.push_back( MSG_PANEL_ITEM( _( "Nets" ), txt, RED ) ); - txt.Printf( wxT( "%d" ), board->GetRatsnest()->GetUnconnectedCount() ); + txt.Printf( wxT( "%d" ), board->GetConnectivity()->GetUnconnectedCount() ); aList.push_back( MSG_PANEL_ITEM( _( "Unconnected" ), txt, BLUE ) ); } diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index da73f44d11..e1f06dd745 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -65,7 +65,7 @@ #include #include #include -#include +#include #include #include @@ -509,8 +509,8 @@ void PCB_EDIT_FRAME::SetBoard( BOARD* aBoard ) if( IsGalCanvasActive() ) { - aBoard->GetRatsnest()->ProcessBoard(); - + aBoard->GetConnectivity()->Build ( aBoard ); + // reload the worksheet SetPageSettings( aBoard->GetPageSettings() ); } diff --git a/pcbnew/ratsnest.cpp b/pcbnew/ratsnest.cpp index 21126fefd1..5bde27e269 100644 --- a/pcbnew/ratsnest.cpp +++ b/pcbnew/ratsnest.cpp @@ -170,7 +170,7 @@ void PCB_BASE_FRAME::Compile_Ratsnest( wxDC* aDC, bool aDisplayStatus ) ClearMsgPanel(); // Rebuild the full pads and net info list - RecalculateAllTracksNetcode(); + ComputeLegacyConnections(); if( aDisplayStatus ) { diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp index fb2d6032ce..c9fb1e95e3 100644 --- a/pcbnew/ratsnest_data.cpp +++ b/pcbnew/ratsnest_data.cpp @@ -1,9 +1,9 @@ /* * This program source code file is part of KICAD, a free EDA CAD application. * - * Copyright (C) 2013-2015 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2013-2017 CERN * @author Maciej Suminski + * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,104 +32,77 @@ #include #endif /* USE_OPENMP */ +#ifdef PROFILE +#include +#endif + #include - -#include -#include -#include -#include -#include - #include using namespace std::placeholders; -#include - #include #include #include -#ifdef PROFILE -#include -#endif +#include -static uint64_t getDistance( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2 ) +static uint64_t getDistance( const CN_ANCHOR_PTR& aNode1, const CN_ANCHOR_PTR& aNode2 ) { - // Drop the least significant bits to avoid overflow - int64_t x = ( aNode1->GetX() - aNode2->GetX() ) >> 16; - int64_t y = ( aNode1->GetY() - aNode2->GetY() ) >> 16; + double dx = ( aNode1->Pos().x - aNode2->Pos().x ); + double dy = ( aNode1->Pos().y - aNode2->Pos().y ); - // We do not need sqrt() here, as the distance is computed only for comparison - return ( x * x + y * y ); + return sqrt( dx * dx + dy * dy ); +} + +static bool sortWeight( const CN_EDGE& aEdge1, const CN_EDGE& aEdge2 ) +{ + return aEdge1.GetWeight() < aEdge2.GetWeight(); } -static bool sortDistance( const RN_NODE_PTR& aOrigin, const RN_NODE_PTR& aNode1, - const RN_NODE_PTR& aNode2 ) +/*bool operator==( const RN_NODE_PTR& aFirst, const RN_NODE_PTR& aSecond ) + * { + * return aFirst->GetX() == aSecond->GetX() && aFirst->GetY() == aSecond->GetY(); + * } + * + * bool operator!=( const RN_NODE_PTR& aFirst, const RN_NODE_PTR& aSecond ) + * { + * return aFirst->GetX() != aSecond->GetX() || aFirst->GetY() != aSecond->GetY(); + * } + * + * RN_NODE_AND_FILTER operator&&( const RN_NODE_FILTER& aFilter1, const RN_NODE_FILTER& aFilter2 ) + * { + * return RN_NODE_AND_FILTER( aFilter1, aFilter2 ); + * } + * + * RN_NODE_OR_FILTER operator||( const RN_NODE_FILTER& aFilter1, const RN_NODE_FILTER& aFilter2 ) + * { + * return RN_NODE_OR_FILTER( aFilter1, aFilter2 ); + * } + * + * static bool isEdgeConnectingNode( const RN_EDGE_PTR& aEdge, const RN_NODE_PTR& aNode ) + * { + * return aEdge->GetSourceNode() == aNode || aEdge->GetTargetNode() == aNode; + * } + */ + +static const std::vector kruskalMST( std::list& aEdges, + std::vector& aNodes ) { - return getDistance( aOrigin, aNode1 ) < getDistance( aOrigin, aNode2 ); -} - - -static bool sortWeight( const RN_EDGE_PTR& aEdge1, const RN_EDGE_PTR& aEdge2 ) -{ - return aEdge1->GetWeight() < aEdge2->GetWeight(); -} - - -bool sortArea( const RN_POLY& aP1, const RN_POLY& aP2 ) -{ - return aP1.m_bbox.GetArea() < aP2.m_bbox.GetArea(); -} - - -bool operator==( const RN_NODE_PTR& aFirst, const RN_NODE_PTR& aSecond ) -{ - return aFirst->GetX() == aSecond->GetX() && aFirst->GetY() == aSecond->GetY(); -} - - -bool operator!=( const RN_NODE_PTR& aFirst, const RN_NODE_PTR& aSecond ) -{ - return aFirst->GetX() != aSecond->GetX() || aFirst->GetY() != aSecond->GetY(); -} - - -RN_NODE_AND_FILTER operator&&( const RN_NODE_FILTER& aFilter1, const RN_NODE_FILTER& aFilter2 ) -{ - return RN_NODE_AND_FILTER( aFilter1, aFilter2 ); -} - - -RN_NODE_OR_FILTER operator||( const RN_NODE_FILTER& aFilter1, const RN_NODE_FILTER& aFilter2 ) -{ - return RN_NODE_OR_FILTER( aFilter1, aFilter2 ); -} - - -static bool isEdgeConnectingNode( const RN_EDGE_PTR& aEdge, const RN_NODE_PTR& aNode ) -{ - return aEdge->GetSourceNode() == aNode || aEdge->GetTargetNode() == aNode; -} - - -static std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, - std::vector& aNodes ) -{ - unsigned int nodeNumber = aNodes.size(); - unsigned int mstExpectedSize = nodeNumber - 1; - unsigned int mstSize = 0; + unsigned int nodeNumber = aNodes.size(); + unsigned int mstExpectedSize = nodeNumber - 1; + unsigned int mstSize = 0; bool ratsnestLines = false; + //printf("mst nodes : %d edges : %d\n", aNodes.size(), aEdges.size () ); // The output - std::vector* mst = new std::vector; - mst->reserve( mstExpectedSize ); + std::vector mst; // Set tags for marking cycles - std::unordered_map tags; + std::unordered_map tags; unsigned int tag = 0; - for( RN_NODE_PTR& node : aNodes ) + for( auto& node : aNodes ) { node->SetTag( tag ); tags[node] = tag++; @@ -137,6 +110,7 @@ static std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, // Lists of nodes connected together (subtrees) to detect cycles in the graph std::vector > cycles( nodeNumber ); + for( unsigned int i = 0; i < nodeNumber; ++i ) cycles[i].push_back( i ); @@ -145,10 +119,11 @@ static std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, while( mstSize < mstExpectedSize && !aEdges.empty() ) { - RN_EDGE_PTR& dt = aEdges.front(); + //printf("mstSize %d %d\n", mstSize, mstExpectedSize); + auto& dt = aEdges.front(); - int srcTag = tags[dt->GetSourceNode()]; - int trgTag = tags[dt->GetTargetNode()]; + int srcTag = tags[dt.GetSourceNode()]; + int trgTag = tags[dt.GetTargetNode()]; // Check if by adding this edge we are going to join two different forests if( srcTag != trgTag ) @@ -156,7 +131,7 @@ static std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, // Because edges are sorted by their weight, first we always process connected // items (weight == 0). Once we stumble upon an edge with non-zero weight, // it means that the rest of the lines are ratsnest. - if( !ratsnestLines && dt->GetWeight() != 0 ) + if( !ratsnestLines && dt.GetWeight() != 0 ) ratsnestLines = true; // Update tags @@ -166,23 +141,22 @@ static std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, { tags[aNodes[*it]] = srcTag; } + // Do a copy of edge, but make it RN_EDGE_MST. In contrary to RN_EDGE, // RN_EDGE_MST saves both source and target node and does not require any other // edges to exist for getting source/target nodes - RN_EDGE_MST_PTR newEdge = std::make_shared( dt->GetSourceNode(), - dt->GetTargetNode(), - dt->GetWeight() ); + CN_EDGE newEdge ( dt.GetSourceNode(), dt.GetTargetNode(), dt.GetWeight() ); - assert( newEdge->GetSourceNode()->GetTag() != newEdge->GetTargetNode()->GetTag() ); - assert( newEdge->GetWeight() > 0 ); + assert( newEdge.GetSourceNode()->GetTag() != newEdge.GetTargetNode()->GetTag() ); + assert( newEdge.GetWeight() > 0 ); - mst->push_back( newEdge ); + mst.push_back( newEdge ); ++mstSize; } else { - //for( it = cycles[trgTag].begin(), itEnd = cycles[trgTag].end(); it != itEnd; ++it ) - //for( auto it : cycles[trgTag] ) + // for( it = cycles[trgTag].begin(), itEnd = cycles[trgTag].end(); it != itEnd; ++it ) + // for( auto it : cycles[trgTag] ) for( auto it = cycles[trgTag].begin(); it != cycles[trgTag].end(); ++it ) { tags[aNodes[*it]] = srcTag; @@ -202,172 +176,160 @@ static std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, } // Probably we have discarded some of edges, so reduce the size - mst->resize( mstSize ); + mst.resize( mstSize ); return mst; } - -void RN_NET::validateEdge( RN_EDGE_MST_PTR& aEdge ) +class RN_NET::TRIANGULATOR_STATE { - RN_NODE_PTR source = aEdge->GetSourceNode(); - RN_NODE_PTR target = aEdge->GetTargetNode(); - bool update = false, changed = false; +private: + std::vector m_allNodes; + std::vector m_triangulationNodes; - // If any of nodes belonging to the edge has the flag set, - // change it to the closest node that has flag cleared - // note: finding the right nodes can be done iteratively to get the best results, - // but it is not likely to be worth the time cost - do +public: + + void Clear() { - if( changed || source->GetNoLine() ) + m_allNodes.clear(); + } + + void AddNode( CN_ANCHOR_PTR aNode ) + { + m_allNodes.push_back( aNode ); + } + + const std::list Triangulate() + { + std::list mstEdges; + std::list triangEdges; + std::vector triNodes; + + using ANCHOR_LIST = std::vector; + std::vector anchorChains; + + triNodes.reserve( m_allNodes.size() ); + anchorChains.reserve ( m_allNodes.size() ); + + std::sort( m_allNodes.begin(), m_allNodes.end(), + [] ( const CN_ANCHOR_PTR& aNode1, const CN_ANCHOR_PTR& aNode2 ) { - changed = false; - std::list closest = GetClosestNodes( target, - LINE_TARGET_SAME_TAG( source->GetTag() ) ); - - if( !closest.empty() ) + if( aNode1->Pos().y < aNode2->Pos().y ) + return true; + else if( aNode1->Pos().y == aNode2->Pos().y ) { - RN_NODE_PTR& node = closest.front(); + return aNode1->Pos().x < aNode2->Pos().x; + } - if( node != source ) - { - changed = true; - update = true; - source = node; - } + return false; + } + ); + + CN_ANCHOR_PTR prev, last; + int id = 0; + + for( auto n : m_allNodes ) + { + anchorChains.push_back( ANCHOR_LIST() ); + } + + for( auto n : m_allNodes ) + { + if( !prev || prev->Pos() != n->Pos() ) + { + auto tn = std::make_shared ( n->Pos().x, n->Pos().y ); + tn->SetId( id ); + triNodes.push_back( tn ); + } + + id++; + prev = n; + } + + int prevId = 0; + + for( auto n : triNodes ) + { + for( int i = prevId; i < n->Id(); i++ ) + anchorChains[prevId].push_back( m_allNodes[ i ] ); + + prevId = n->Id(); + } + + for( int i = prevId; i < id; i++ ) + anchorChains[prevId].push_back( m_allNodes[ i ] ); + + + hed::TRIANGULATION triangulator; + triangulator.CreateDelaunay( triNodes.begin(), triNodes.end() ); + triangulator.GetEdges( triangEdges ); + + for( auto e : triangEdges ) + { + auto src = m_allNodes[ e->GetSourceNode()->Id() ]; + auto dst = m_allNodes[ e->GetTargetNode()->Id() ]; + + mstEdges.emplace_back( src, dst, getDistance( src, dst ) ); + } + + for( int i = 0; i < anchorChains.size(); i++ ) + { + auto& chain = anchorChains[i]; + + if( chain.size() < 2 ) + continue; + + std::sort( chain.begin(), chain.end(), + [] ( const CN_ANCHOR_PTR& a, const CN_ANCHOR_PTR& b ) { + return a->GetCluster().get() < b->GetCluster().get(); + } ); + + for( auto j = 1; j < chain.size(); j++ ) + { + const auto& prevNode = chain[j - 1]; + const auto& curNode = chain[j]; + int weight = prevNode->GetCluster() != curNode->GetCluster() ? 1 : 0; + mstEdges.push_back( CN_EDGE ( prevNode, curNode, weight ) ); } } - if( changed || target->GetNoLine() ) - { - changed = false; - std::list closest = GetClosestNodes( source, - LINE_TARGET_SAME_TAG( target->GetTag() ) ); - - if( !closest.empty() ) - { - RN_NODE_PTR& node = closest.front(); - - if( node != target ) - { - changed = true; - update = true; - target = node; - } - } - } + return mstEdges; } - while( changed ); +}; - assert( source->GetTag() >= 0 && target->GetTag() >= 0 ); - assert( source->GetTag() != target->GetTag() ); - assert( source != target ); +#include - // Replace an invalid edge with new, valid one - if( update ) - aEdge.reset( new RN_EDGE_MST( source, target ) ); -} - - -void RN_NET::removeNode( RN_NODE_PTR& aNode, const BOARD_CONNECTED_ITEM* aParent ) +RN_NET::RN_NET() : m_dirty( true ), m_visible( true ) { - aNode->RemoveParent( aParent ); - - if( m_links.RemoveNode( aNode ) ) - { - clearNode( aNode ); - m_dirty = true; - } + m_triangulator.reset( new TRIANGULATOR_STATE ); } - -void RN_NET::removeEdge( RN_EDGE_MST_PTR& aEdge, const BOARD_CONNECTED_ITEM* aParent ) -{ - // Save nodes, so they can be cleared later - RN_NODE_PTR start = aEdge->GetSourceNode(); - RN_NODE_PTR end = aEdge->GetTargetNode(); - - start->RemoveParent( aParent ); - end->RemoveParent( aParent ); - - // Connection has to be removed before running RemoveNode(), - // as RN_NODE influences the reference counter - m_links.RemoveConnection( aEdge ); - - // Remove nodes associated with the edge. It is done in a safe way, there is a check - // if nodes are not used by other edges. - if( m_links.RemoveNode( start ) ) - clearNode( start ); - - if( m_links.RemoveNode( end ) ) - clearNode( end ); - - m_dirty = true; -} - - -const RN_NODE_PTR& RN_LINKS::AddNode( int aX, int aY ) -{ - RN_NODE_SET::iterator node; - bool wasNewElement; - - std::tie( node, wasNewElement ) = m_nodes.emplace( std::make_shared( aX, aY ) ); - - return *node; -} - - -bool RN_LINKS::RemoveNode( const RN_NODE_PTR& aNode ) -{ - if( aNode->GetRefCount() == 0 ) - { - m_nodes.erase( aNode ); - - return true; - } - - return false; -} - - -RN_EDGE_MST_PTR RN_LINKS::AddConnection( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2, - unsigned int aDistance ) -{ - assert( aNode1 != aNode2 ); - RN_EDGE_MST_PTR edge = std::make_shared( aNode1, aNode2, aDistance ); - m_edges.push_back( edge ); - - return edge; -} - - void RN_NET::compute() { - const RN_LINKS::RN_NODE_SET& boardNodes = m_links.GetNodes(); - const RN_LINKS::RN_EDGE_LIST& boardEdges = m_links.GetConnections(); // Special cases do not need complicated algorithms (actually, it does not work well with // the Delaunay triangulator) - if( boardNodes.size() <= 2 ) + //printf("compute nodes : %d\n", m_nodes.size() ); + if( m_nodes.size() <= 2 ) { - m_rnEdges.reset( new std::vector( 0 ) ); + m_rnEdges.clear(); // Check if the only possible connection exists - if( boardEdges.size() == 0 && boardNodes.size() == 2 ) + if( m_boardEdges.size() == 0 && m_nodes.size() == 2 ) { - RN_LINKS::RN_NODE_SET::const_iterator last = ++boardNodes.begin(); + auto last = ++m_nodes.begin(); // There can be only one possible connection, but it is missing - RN_EDGE_MST_PTR edge = std::make_shared( *boardNodes.begin(), *last ); - edge->GetSourceNode()->SetTag( 0 ); - edge->GetTargetNode()->SetTag( 1 ); - m_rnEdges->push_back( edge ); + CN_EDGE edge (*m_nodes.begin(), *last ); + edge.GetSourceNode()->SetTag( 0 ); + edge.GetTargetNode()->SetTag( 1 ); + + m_rnEdges.push_back( edge ); } else { - // Set tags to nodes as connected - for( RN_NODE_PTR node : boardNodes ) + // Set tags to m_nodes as connected + for( auto node : m_nodes ) node->SetTag( 0 ); } @@ -375,941 +337,375 @@ void RN_NET::compute() return; } - // Move and sort (sorting speeds up) all nodes to a vector for the Delaunay triangulation - std::vector nodes( boardNodes.size() ); - std::partial_sort_copy( boardNodes.begin(), boardNodes.end(), nodes.begin(), nodes.end() ); - TRIANGULATOR triangulator; - triangulator.CreateDelaunay( nodes.begin(), nodes.end() ); - std::unique_ptr triangEdges( triangulator.GetEdges() ); + m_triangulator->Clear(); - // Compute weight/distance for edges resulting from triangulation - RN_LINKS::RN_EDGE_LIST::iterator eit, eitEnd; - for( eit = (*triangEdges).begin(), eitEnd = (*triangEdges).end(); eit != eitEnd; ++eit ) - (*eit)->SetWeight( getDistance( (*eit)->GetSourceNode(), (*eit)->GetTargetNode() ) ); + for( auto n : m_nodes ) + { + m_triangulator->AddNode( n ); + } - // Add the currently existing connections list to the results of triangulation - std::copy( boardEdges.begin(), boardEdges.end(), std::front_inserter( *triangEdges ) ); + #ifdef PROFILE + PROF_COUNTER cnt("triangulate"); + #endif + auto triangEdges = m_triangulator->Triangulate(); + #ifdef PROFILE + cnt.Show(); + #endif - // Get the minimal spanning tree - m_rnEdges.reset( kruskalMST( *triangEdges, nodes ) ); + for( const auto& e : m_boardEdges ) + triangEdges.push_back( e ); + +// Get the minimal spanning tree +#ifdef PROFILE + PROF_COUNTER cnt2("mst"); +#endif + m_rnEdges = kruskalMST( triangEdges, m_nodes ); +#ifdef PROFILE + cnt2.Show(); +#endif } -void RN_NET::clearNode( const RN_NODE_PTR& aNode ) -{ - if( !m_rnEdges ) - return; - - std::vector::iterator newEnd; - - // Remove all ratsnest edges for associated with the node - newEnd = std::remove_if( m_rnEdges->begin(), m_rnEdges->end(), - std::bind( isEdgeConnectingNode, _1, std::cref( aNode ) ) ); - - m_rnEdges->resize( std::distance( m_rnEdges->begin(), newEnd ) ); -} - - -RN_POLY::RN_POLY( const SHAPE_POLY_SET* aParent, - int aSubpolygonIndex, - RN_LINKS& aConnections, const BOX2I& aBBox ) : - m_subpolygonIndex( aSubpolygonIndex ), - m_bbox( aBBox ), - m_parentPolyset( aParent ) -{ - const VECTOR2I& p = aParent->CVertex( 0, aSubpolygonIndex, -1 ); - - m_node = aConnections.AddNode( p.x, p.y ); - - // Mark it as not appropriate as a destination of ratsnest edges - // (edges coming out from a polygon vertex look weird) - m_node->SetNoLine( true ); -} - - -bool RN_POLY::HitTest( const RN_NODE_PTR& aNode ) const -{ - VECTOR2I p( aNode->GetX(), aNode->GetY() ); - - return m_parentPolyset->Contains( p, m_subpolygonIndex ); -} - void RN_NET::Update() { - // Add edges resulting from nodes being connected by zones - processZones(); - processPads(); - compute(); - for( RN_EDGE_MST_PTR& edge : *m_rnEdges ) - validateEdge( edge ); - m_dirty = false; } -bool RN_NET::AddItem( const D_PAD* aPad ) +void RN_NET::Clear() { - // Ratsnest is not computed for non-copper pads - if( ( aPad->GetLayerSet() & LSET::AllCuMask() ).none() ) - return false; - - RN_NODE_PTR node = m_links.AddNode( aPad->GetPosition().x, aPad->GetPosition().y ); - node->AddParent( aPad ); - m_pads[aPad].m_Node = node; - m_dirty = true; - - return true; -} - - -bool RN_NET::AddItem( const VIA* aVia ) -{ - RN_NODE_PTR node = m_links.AddNode( aVia->GetPosition().x, aVia->GetPosition().y ); - node->AddParent( aVia ); - m_vias[aVia] = node; - m_dirty = true; - - return true; -} - - -bool RN_NET::AddItem( const TRACK* aTrack ) -{ - if( aTrack->GetStart() == aTrack->GetEnd() ) - return false; - - RN_NODE_PTR start = m_links.AddNode( aTrack->GetStart().x, aTrack->GetStart().y ); - RN_NODE_PTR end = m_links.AddNode( aTrack->GetEnd().x, aTrack->GetEnd().y ); - - start->AddParent( aTrack ); - end->AddParent( aTrack ); - m_tracks[aTrack] = m_links.AddConnection( start, end ); - m_dirty = true; - - return true; -} - - -bool RN_NET::AddItem( const ZONE_CONTAINER* aZone ) -{ - // Prepare a list of polygons (every zone can contain one or more polygons) - const SHAPE_POLY_SET& polySet = aZone->GetFilledPolysList(); - - // This ensures that we record aZone as added even if it contains no polygons. - (void) m_zones[aZone]; - - for( int i = 0; i < polySet.OutlineCount(); ++i ) - { - const SHAPE_LINE_CHAIN& path = polySet.COutline( i ); - - RN_POLY poly = RN_POLY( &polySet, i, m_links, path.BBox() ); - m_zones[aZone].m_Polygons.push_back( poly ); - } + m_rnEdges.clear(); + m_boardEdges.clear(); + m_nodes.clear(); m_dirty = true; - - return true; -} - - -bool RN_NET::RemoveItem( const D_PAD* aPad ) -{ - PAD_NODE_MAP::iterator it = m_pads.find( aPad ); - - if( it == m_pads.end() ) - return false; - - RN_PAD_DATA& pad_data = it->second; - removeNode( pad_data.m_Node, aPad ); - - for( RN_EDGE_MST_PTR& edge : pad_data.m_Edges ) - removeEdge( edge, aPad ); - - m_pads.erase( aPad ); - - return true; -} - - -bool RN_NET::RemoveItem( const VIA* aVia ) -{ - VIA_NODE_MAP::iterator it = m_vias.find( aVia ); - - if( it == m_vias.end() ) - return false; - - removeNode( it->second, aVia ); - m_vias.erase( it ); - - return true; -} - - -bool RN_NET::RemoveItem( const TRACK* aTrack ) -{ - TRACK_EDGE_MAP::iterator it = m_tracks.find( aTrack ); - - if( it == m_tracks.end() ) - return false; - - removeEdge( it->second, aTrack ); - m_tracks.erase( it ); - - return true; -} - - -bool RN_NET::RemoveItem( const ZONE_CONTAINER* aZone ) -{ - ZONE_DATA_MAP::iterator it = m_zones.find( aZone ); - - if( it == m_zones.end() ) - return false; - - RN_ZONE_DATA& zoneData = it->second; - - // Remove all subpolygons that make the zone - std::deque& polygons = zoneData.m_Polygons; - for( RN_POLY& polygon : polygons ) - removeNode( polygon.GetNode(), aZone ); - polygons.clear(); - - // Remove all connections added by the zone - std::deque& edges = zoneData.m_Edges; - - for( RN_EDGE_MST_PTR edge : edges ) - removeEdge( edge, aZone ); - - edges.clear(); - m_zones.erase( it ); - - return true; } +#if 0 const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode ) const { - const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); - RN_LINKS::RN_NODE_SET::const_iterator it, itEnd; - - unsigned int minDistance = std::numeric_limits::max(); - RN_NODE_PTR closest; - - for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) - { - RN_NODE_PTR node = *it; - - // Obviously the distance between node and itself is the shortest, - // that's why we have to skip it - if( node != aNode ) - { - unsigned int distance = getDistance( node, aNode ); - if( distance < minDistance ) - { - minDistance = distance; - closest = node; - } - } - } - - return closest; + /*const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + * RN_LINKS::RN_NODE_LISt::const_iterator it, itEnd; + * + * unsigned int minDistance = std::numeric_limits::max(); + * RN_NODE_PTR closest; + * + * for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) + * { + * RN_NODE_PTR node = *it; + * + * // Obviously the distance between node and itself is the shortest, + * // that's why we have to skip it + * if( node != aNode ) + * { + * unsigned int distance = getDistance( node, aNode ); + * if( distance < minDistance ) + * { + * minDistance = distance; + * closest = node; + * } + * } + * } + * + * return closest;*/ } const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode, - const RN_NODE_FILTER& aFilter ) const + const RN_NODE_FILTER& aFilter ) const { - const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); - RN_LINKS::RN_NODE_SET::const_iterator it, itEnd; - - unsigned int minDistance = std::numeric_limits::max(); - RN_NODE_PTR closest; - - for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) - { - RN_NODE_PTR node = *it; - - // Obviously the distance between node and itself is the shortest, - // that's why we have to skip it - if( node != aNode && aFilter( node ) ) - { - unsigned int distance = getDistance( node, aNode ); - - if( distance < minDistance ) - { - minDistance = distance; - closest = node; - } - } - } - - return closest; + /*const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + * RN_LINKS::RN_NODE_SET::const_iterator it, itEnd; + * + * unsigned int minDistance = std::numeric_limits::max(); + * RN_NODE_PTR closest; + * + * for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) + * { + * RN_NODE_PTR node = *it; + * + * // Obviously the distance between node and itself is the shortest, + * // that's why we have to skip it + * if( node != aNode && aFilter( node ) ) + * { + * unsigned int distance = getDistance( node, aNode ); + * + * if( distance < minDistance ) + * { + * minDistance = distance; + * closest = node; + * } + * } + * } + * + * return closest;*/ } std::list RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber ) const { - std::list closest; - const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); - - // Copy nodes - std::copy( nodes.begin(), nodes.end(), std::back_inserter( closest ) ); - - // Sort by the distance from aNode - closest.sort( std::bind( sortDistance, std::cref( aNode ), _1, _2 ) ); - - // aNode should not be returned in the results - closest.remove( aNode ); - - // Trim the result to the asked size - if( aNumber > 0 ) - closest.resize( std::min( (size_t)aNumber, nodes.size() ) ); - - return closest; + /*std::list closest; + * const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + * + * // Copy nodes + * std::copy( nodes.begin(), nodes.end(), std::back_inserter( closest ) ); + * + * // Sort by the distance from aNode + * closest.sort( std::bind( sortDistance, std::cref( aNode ), _1, _2 ) ); + * + * // aNode should not be returned in the results + * closest.remove( aNode ); + * + * // Trim the result to the asked size + * if( aNumber > 0 ) + * closest.resize( std::min( (size_t)aNumber, nodes.size() ) ); + * return closest;*/ } std::list RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, - const RN_NODE_FILTER& aFilter, int aNumber ) const + const RN_NODE_FILTER& aFilter, int aNumber ) const { - std::list closest; - const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); - - // Copy filtered nodes - std::copy_if( nodes.begin(), nodes.end(), std::back_inserter( closest ), std::cref( aFilter ) ); - - // Sort by the distance from aNode - closest.sort( std::bind( sortDistance, std::cref( aNode ), _1, _2 ) ); - - // aNode should not be returned in the results - closest.remove( aNode ); - - // Trim the result to the asked size - if( aNumber > 0 ) - closest.resize( std::min( static_cast( aNumber ), nodes.size() ) ); - - return closest; -} - - -void RN_NET::AddSimple( const BOARD_CONNECTED_ITEM* aItem ) -{ - for( RN_NODE_PTR node : GetNodes( aItem ) ) - { - // Block all nodes, so they do not become targets for dynamic ratsnest lines - AddBlockedNode( node ); - - // Filter out junctions - if( node->GetRefCount() == 1 ) - m_simpleNodes.insert( node ); - } + /*std::list closest; + * const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + * + * // Copy filtered nodes + * std::copy_if( nodes.begin(), nodes.end(), std::back_inserter( closest ), std::cref( aFilter ) ); + * + * // Sort by the distance from aNode + * closest.sort( std::bind( sortDistance, std::cref( aNode ), _1, _2 ) ); + * + * // aNode should not be returned in the results + * closest.remove( aNode ); + * + * // Trim the result to the asked size + * if( aNumber > 0 ) + * closest.resize( std::min( static_cast( aNumber ), nodes.size() ) ); + * return closest;*/ } std::list RN_NET::GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const { - std::list nodes; - - switch( aItem->Type() ) - { - case PCB_PAD_T: - { - PAD_NODE_MAP::const_iterator it = m_pads.find( static_cast( aItem ) ); - - if( it != m_pads.end() ) - nodes.push_back( it->second.m_Node ); - } - break; - - case PCB_VIA_T: - { - VIA_NODE_MAP::const_iterator it = m_vias.find( static_cast( aItem ) ); - - if( it != m_vias.end() ) - nodes.push_back( it->second ); - } - break; - - case PCB_TRACE_T: - { - TRACK_EDGE_MAP::const_iterator it = m_tracks.find( static_cast( aItem ) ); - - if( it != m_tracks.end() ) - { - nodes.push_back( it->second->GetSourceNode() ); - nodes.push_back( it->second->GetTargetNode() ); - } - } - break; - - case PCB_ZONE_AREA_T: - { - ZONE_DATA_MAP::const_iterator itz = m_zones.find( static_cast( aItem ) ); - - if( itz != m_zones.end() ) - { - const std::deque& polys = itz->second.m_Polygons; - - for( std::deque::const_iterator it = polys.begin(); it != polys.end(); ++it ) - nodes.push_back( it->GetNode() ); - } - } - break; - - default: - break; - } - - return nodes; + /*std::list nodes; + * + * switch( aItem->Type() ) + * { + * case PCB_PAD_T: + * { + * PAD_NODE_MAP::const_iterator it = m_pads.find( static_cast( aItem ) ); + * + * if( it != m_pads.end() ) + * nodes.push_back( it->second.m_Node ); + * } + * break; + * + * case PCB_VIA_T: + * { + * VIA_NODE_MAP::const_iterator it = m_vias.find( static_cast( aItem ) ); + * + * if( it != m_vias.end() ) + * nodes.push_back( it->second ); + * } + * break; + * + * case PCB_TRACE_T: + * { + * TRACK_EDGE_MAP::const_iterator it = m_tracks.find( static_cast( aItem ) ); + * + * if( it != m_tracks.end() ) + * { + * nodes.push_back( it->second->GetSourceNode() ); + * nodes.push_back( it->second->GetTargetNode() ); + * } + * } + * break; + * + * case PCB_ZONE_AREA_T: + * { + * ZONE_DATA_MAP::const_iterator itz = m_zones.find( static_cast( aItem ) ); + * + * if( itz != m_zones.end() ) + * { + * const std::deque& polys = itz->second.m_Polygons; + * + * for( std::deque::const_iterator it = polys.begin(); it != polys.end(); ++it ) + * nodes.push_back( it->GetNode() ); + * } + * } + * break; + * + * default: + * break; + * } + * + * return nodes;*/ } -void RN_NET::GetAllItems( std::list& aOutput, RN_ITEM_TYPE aType ) const +void RN_NET::GetAllItems( std::list& aOutput, const KICAD_T aTypes[] ) const { - if( aType & RN_PADS ) - { - for( auto it : m_pads ) - aOutput.push_back( const_cast( it.first ) ); - } - - if( aType & RN_VIAS ) - { - for( auto it : m_vias ) - aOutput.push_back( const_cast( it.first ) ); - } - - if( aType & RN_TRACKS ) - { - for( auto it : m_tracks ) - aOutput.push_back( const_cast( it.first ) ); - } - - if( aType & RN_ZONES ) - { - for( auto it : m_zones ) - aOutput.push_back( const_cast( it.first ) ); - } -} - - -void RN_NET::ClearSimple() -{ - for( const RN_NODE_PTR& node : m_blockedNodes ) - node->SetNoLine( false ); - - m_blockedNodes.clear(); - m_simpleNodes.clear(); +/* if( aType & RN_PADS ) + * { + * for( auto it : m_pads ) + * aOutput.push_back( const_cast( it.first ) ); + * } + * + * if( aType & RN_VIAS ) + * { + * for( auto it : m_vias ) + * aOutput.push_back( const_cast( it.first ) ); + * } + * + * if( aType & RN_TRACKS ) + * { + * for( auto it : m_tracks ) + * aOutput.push_back( const_cast( it.first ) ); + * } + * + * if( aType & RN_ZONES ) + * { + * for( auto it : m_zones ) + * aOutput.push_back( const_cast( it.first ) ); + * }*/ } void RN_NET::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, - std::list& aOutput, - RN_ITEM_TYPE aTypes ) const + std::list& aOutput, + const KICAD_T aTypes[] ) const { - std::list nodes = GetNodes( aItem ); - assert( !nodes.empty() ); - - int tag = nodes.front()->GetTag(); - assert( tag >= 0 ); - - if( aTypes & RN_PADS ) - { - for( PAD_NODE_MAP::const_iterator it = m_pads.begin(); it != m_pads.end(); ++it ) - { - if( it->second.m_Node->GetTag() == tag ) - aOutput.push_back( const_cast( it->first ) ); - } - } - - if( aTypes & RN_VIAS ) - { - for( VIA_NODE_MAP::const_iterator it = m_vias.begin(); it != m_vias.end(); ++it ) - { - if( it->second->GetTag() == tag ) - aOutput.push_back( const_cast( it->first ) ); - } - } - - if( aTypes & RN_TRACKS ) - { - for( TRACK_EDGE_MAP::const_iterator it = m_tracks.begin(); it != m_tracks.end(); ++it ) - { - if( it->second->GetTag() == tag ) - aOutput.push_back( const_cast( it->first ) ); - } - } - - if( aTypes & RN_ZONES ) - { - for( ZONE_DATA_MAP::const_iterator it = m_zones.begin(); it != m_zones.end(); ++it ) - { - for( const RN_EDGE_MST_PTR& edge : it->second.m_Edges ) - { - if( edge->GetTag() == tag ) - { - aOutput.push_back( const_cast( it->first ) ); - break; - } - } - } - } +/* std::list nodes = GetNodes( aItem ); + * assert( !nodes.empty() ); + * + * int tag = nodes.front()->GetTag(); + * assert( tag >= 0 ); + * + * if( aTypes & RN_PADS ) + * { + * for( PAD_NODE_MAP::const_iterator it = m_pads.begin(); it != m_pads.end(); ++it ) + * { + * if( it->second.m_Node->GetTag() == tag ) + * aOutput.push_back( const_cast( it->first ) ); + * } + * } + * + * if( aTypes & RN_VIAS ) + * { + * for( VIA_NODE_MAP::const_iterator it = m_vias.begin(); it != m_vias.end(); ++it ) + * { + * if( it->second->GetTag() == tag ) + * aOutput.push_back( const_cast( it->first ) ); + * } + * } + * + * if( aTypes & RN_TRACKS ) + * { + * for( TRACK_EDGE_MAP::const_iterator it = m_tracks.begin(); it != m_tracks.end(); ++it ) + * { + * if( it->second->GetTag() == tag ) + * aOutput.push_back( const_cast( it->first ) ); + * } + * } + * + * if( aTypes & RN_ZONES ) + * { + * for( ZONE_DATA_MAP::const_iterator it = m_zones.begin(); it != m_zones.end(); ++it ) + * { + * for( const RN_EDGE_MST_PTR& edge : it->second.m_Edges ) + * { + * if( edge->GetTag() == tag ) + * { + * aOutput.push_back( const_cast( it->first ) ); + * break; + * } + * } + * } + * }*/ } -void RN_DATA::AddSimple( const BOARD_ITEM* aItem ) -{ - if( aItem->IsConnected() ) - { - const BOARD_CONNECTED_ITEM* item = static_cast( aItem ); - int net = item->GetNetCode(); +// const RN_NODE_PTR& RN_NET::AddNode( int aX, int aY ) +// { +// return m_links.AddNode( aX, aY ); +// } - // Do not process orphaned & unconnected items - if( net <= NETINFO_LIST::UNCONNECTED ) - return; - - m_nets[net].AddSimple( item ); - } - else if( aItem->Type() == PCB_MODULE_T ) - { - const MODULE* module = static_cast( aItem ); - - for( const D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - AddSimple( pad ); - } -} - - -void RN_DATA::AddBlocked( const BOARD_ITEM* aItem ) -{ - if( aItem->IsConnected() ) - { - const BOARD_CONNECTED_ITEM* item = static_cast( aItem ); - int net = item->GetNetCode(); - - // Do not process orphaned & unconnected items - if( net <= NETINFO_LIST::UNCONNECTED ) - return; - - // Block all nodes belonging to the item - for( RN_NODE_PTR node : m_nets[net].GetNodes( item ) ) - m_nets[net].AddBlockedNode( node ); - } - else if( aItem->Type() == PCB_MODULE_T ) - { - const MODULE* module = static_cast( aItem ); - - for( const D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - AddBlocked( pad ); - } -} - - -void RN_DATA::GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, - std::list& aOutput, - RN_ITEM_TYPE aTypes ) const -{ - int net = aItem->GetNetCode(); - - if( net < 1 ) - return; - - assert( net < (int) m_nets.size() ); - - m_nets[net].GetConnectedItems( aItem, aOutput, aTypes ); -} - - -void RN_DATA::GetNetItems( int aNetCode, std::list& aOutput, - RN_ITEM_TYPE aTypes ) const -{ - if( aNetCode < 1 ) - return; - - assert( aNetCode < (int) m_nets.size() ); - - m_nets[aNetCode].GetAllItems( aOutput, aTypes ); -} - - -bool RN_DATA::AreConnected( const BOARD_CONNECTED_ITEM* aItem, const BOARD_CONNECTED_ITEM* aOther ) -{ - int net1 = aItem->GetNetCode(); - int net2 = aOther->GetNetCode(); - - if( net1 < 1 || net2 < 1 || net1 != net2 ) - return false; - - assert( net1 < (int) m_nets.size() && net2 < (int) m_nets.size() ); - - // net1 == net2 - std::list items1 = m_nets[net1].GetNodes( aItem ); - std::list items2 = m_nets[net1].GetNodes( aOther ); - - assert( !items1.empty() && !items2.empty() ); - - return ( items1.front()->GetTag() == items2.front()->GetTag() ); -} - - -int RN_DATA::GetUnconnectedCount() const -{ - int count = 0; - - for( unsigned i = 0; i < m_nets.size(); ++i ) - { - const std::vector* unconnected = m_nets[i].GetUnconnected(); - - if( unconnected ) - count += unconnected->size(); - } - - return count; -} - - -void RN_NET::processZones() -{ - for( ZONE_DATA_MAP::iterator it = m_zones.begin(); it != m_zones.end(); ++it ) - { - const ZONE_CONTAINER* zone = it->first; - RN_ZONE_DATA& zoneData = it->second; - - // Reset existing connections - for( RN_EDGE_MST_PTR edge : zoneData.m_Edges ) - m_links.RemoveConnection( edge ); - - zoneData.m_Edges.clear(); - LSET layers = zone->GetLayerSet(); - - // Compute new connections - RN_LINKS::RN_NODE_SET candidates = m_links.GetNodes(); - RN_LINKS::RN_NODE_SET::const_iterator point, pointEnd; - - // Sorting by area should speed up the processing, as smaller polygons are computed - // faster and may reduce the number of points for further checks - std::sort( zoneData.m_Polygons.begin(), zoneData.m_Polygons.end(), sortArea ); - - for( std::deque::iterator poly = zoneData.m_Polygons.begin(), - polyEnd = zoneData.m_Polygons.end(); poly != polyEnd; ++poly ) - { - const RN_NODE_PTR& node = poly->GetNode(); - - point = candidates.begin(); - pointEnd = candidates.end(); - - while( point != pointEnd ) - { - if( *point != node && ( (*point)->GetLayers() & layers ).any() - && poly->HitTest( *point ) ) - { - //(*point)->AddParent( zone ); // do not assign parent for helper links - - RN_EDGE_MST_PTR connection = m_links.AddConnection( node, *point ); - zoneData.m_Edges.push_back( connection ); - - // This point already belongs to a polygon, we do not need to check it anymore - point = candidates.erase( point ); - pointEnd = candidates.end(); - } - else - { - ++point; - } - } - } - } -} - - -void RN_NET::processPads() -{ - for( PAD_NODE_MAP::iterator it = m_pads.begin(); it != m_pads.end(); ++it ) - { - const D_PAD* pad = it->first; - RN_NODE_PTR node = it->second.m_Node; - std::deque& edges = it->second.m_Edges; - - // Reset existing connections - for( RN_EDGE_MST_PTR edge : edges ) - m_links.RemoveConnection( edge ); - - LSET layers = pad->GetLayerSet(); - const RN_LINKS::RN_NODE_SET& candidates = m_links.GetNodes(); - RN_LINKS::RN_NODE_SET::const_iterator point, pointEnd; - - point = candidates.begin(); - pointEnd = candidates.end(); - - while( point != pointEnd ) - { - if( *point != node && ( (*point)->GetLayers() & layers ).any() && - pad->HitTest( wxPoint( (*point)->GetX(), (*point)->GetY() ) ) ) - { - //(*point)->AddParent( pad ); // do not assign parent for helper links - - RN_EDGE_MST_PTR connection = m_links.AddConnection( node, *point ); - edges.push_back( connection ); - } - - ++point; - } - } -} - - -bool RN_DATA::Add( const BOARD_ITEM* aItem ) -{ - int net = NETINFO_LIST::ORPHANED; - - if( aItem->IsConnected() ) - { - net = static_cast( aItem )->GetNetCode(); - } - else if( aItem->Type() == PCB_MODULE_T ) - { - const MODULE* module = static_cast( aItem ); - - for( const D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - { - net = pad->GetNetCode(); - - // Do not process orphaned items - if( net <= NETINFO_LIST::ORPHANED ) - continue; - - Add( pad ); - } - - return true; - } - else if( aItem->Type() == PCB_NETINFO_T ) - { - int netCount = m_board->GetNetCount(); - - if( (unsigned) netCount > m_nets.size() ) - m_nets.resize( netCount ); - - return true; - } - else - { - return false; - } - - if( net < 0 ) - return false; - - // Autoresize is necessary e.g. for module editor - if( net >= (int) m_nets.size() ) - m_nets.resize( net + 1 ); - - switch( aItem->Type() ) - { - case PCB_PAD_T: - return m_nets[net].AddItem( static_cast( aItem ) ); - break; - - case PCB_TRACE_T: - return m_nets[net].AddItem( static_cast( aItem ) ); - break; - - case PCB_VIA_T: - return m_nets[net].AddItem( static_cast( aItem ) ); - break; - - case PCB_ZONE_AREA_T: - return m_nets[net].AddItem( static_cast( aItem ) ); - break; - - default: - break; - } - - return false; -} - - -bool RN_DATA::Remove( const BOARD_ITEM* aItem ) -{ - int net = NETINFO_LIST::ORPHANED; - - if( aItem->IsConnected() ) - { - net = static_cast( aItem )->GetNetCode(); - } - else if( aItem->Type() == PCB_MODULE_T ) - { - const MODULE* module = static_cast( aItem ); - - for( const D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - { - net = pad->GetNetCode(); - - // Do not process orphaned items - if( net <= NETINFO_LIST::ORPHANED ) - continue; - - Remove( pad ); - } - - return true; - } - else - { - return false; - } - - if( net < 0 ) - return false; - - // Autoresize is necessary e.g. for module editor - if( net >= (int) m_nets.size() ) - { - m_nets.resize( net + 1 ); - return false; // if it was resized, then surely the item had not been added before - } - - switch( aItem->Type() ) - { - case PCB_PAD_T: - return m_nets[net].RemoveItem( static_cast( aItem ) ); - break; - - case PCB_TRACE_T: - return m_nets[net].RemoveItem( static_cast( aItem ) ); - break; - - case PCB_VIA_T: - return m_nets[net].RemoveItem( static_cast( aItem ) ); - break; - - case PCB_ZONE_AREA_T: - return m_nets[net].RemoveItem( static_cast( aItem ) ); - break; - - default: - break; - } - - return false; -} - - -bool RN_DATA::Update( const BOARD_ITEM* aItem ) -{ - if( Remove( aItem ) ) - { - bool res = Add( aItem ); - assert( res ); - (void) res; - return true; - } - - return false; -} - - -void RN_DATA::ProcessBoard() -{ - int netCount = m_board->GetNetCount(); - m_nets.clear(); - m_nets.resize( netCount ); - int netCode; - - // Iterate over all items that may need to be connected - for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) - { - for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - { - netCode = pad->GetNetCode(); - - assert( netCode >= 0 && netCode < netCount ); - - if( netCode >= 0 && netCode < netCount ) - m_nets[netCode].AddItem( pad ); - } - } - - for( TRACK* track = m_board->m_Track; track; track = track->Next() ) - { - netCode = track->GetNetCode(); - - assert( netCode >= 0 && netCode < netCount ); - - if( netCode >= 0 && netCode < netCount ) - { - if( track->Type() == PCB_VIA_T ) - m_nets[netCode].AddItem( static_cast( track ) ); - else if( track->Type() == PCB_TRACE_T ) - m_nets[netCode].AddItem( track ); - } - } - - for( int i = 0; i < m_board->GetAreaCount(); ++i ) - { - ZONE_CONTAINER* zone = m_board->GetArea( i ); - - netCode = zone->GetNetCode(); - - assert( netCode >= 0 && netCode < netCount ); - - if( netCode >= 0 && netCode < netCount ) - m_nets[netCode].AddItem( zone ); - } - - Recalculate(); -} - - -void RN_DATA::Recalculate( int aNet ) -{ - unsigned int netCount = m_board->GetNetCount(); - - if( aNet <= 0 && netCount > 1 ) // Recompute everything - { -#ifdef PROFILE - PROF_COUNTER totalRealTime; #endif - unsigned int i; - -#ifdef USE_OPENMP - #pragma omp parallel shared(netCount) private(i) - { - #pragma omp for schedule(guided, 1) -#else /* USE_OPENMP */ - { -#endif - // Start with net number 1, as 0 stands for not connected - for( i = 1; i < netCount; ++i ) - { - if( m_nets[i].IsDirty() ) - updateNet( i ); - } - } /* end of parallel section */ -#ifdef PROFILE - totalRealTime.Stop(); - wxLogDebug( "Recalculate all nets: %.1f ms", totalRealTime.msecs() ); -#endif /* PROFILE */ - } - else if( aNet > 0 ) // Recompute only specific net - { - updateNet( aNet ); - } -} - - -void RN_DATA::updateNet( int aNetCode ) +void RN_NET::AddCluster( CN_CLUSTER_PTR aCluster ) { - assert( aNetCode < (int) m_nets.size() ); + CN_ANCHOR_PTR firstAnchor; - if( aNetCode < 1 || aNetCode > (int) m_nets.size() ) - return; + for( auto item : *aCluster ) + { + bool isZone = dynamic_cast(item) != nullptr; + auto& anchors = item->Anchors(); + int nAnchors = isZone ? 1 : anchors.size(); - m_nets[aNetCode].ClearSimple(); - m_nets[aNetCode].Update(); + if ( nAnchors > anchors.size() ) + nAnchors = anchors.size(); + + //printf("item %p anchors : %d\n", item, anchors.size() ); + //printf("add item %p anchors : %d net : %d\n", item, item->Anchors().size(), item->Parent()->GetNetCode() ); + + for ( int i = 0; i < nAnchors; i++ ) + { + // printf("add anchor %p\n", anchors[i].get() ); + + anchors[i]->SetCluster( aCluster ); + m_nodes.push_back(anchors[i]); + + if( firstAnchor ) + { + if( firstAnchor != anchors[i] ) + { + m_boardEdges.emplace_back( firstAnchor, anchors[i], 0 ); + } + } + else + { + firstAnchor = anchors[i]; + } + } + } +} + +bool RN_NET::NearestBicoloredPair( const RN_NET& aOtherNet, CN_ANCHOR_PTR& aNode1, + CN_ANCHOR_PTR& aNode2 ) const +{ + bool rv = false; + + VECTOR2I::extended_type distMax = VECTOR2I::ECOORD_MAX; + + for( auto nodeA : m_nodes ) + { + for( auto nodeB : aOtherNet.m_nodes ) + { + if( !nodeA->GetNoLine() ) + { + auto squaredDist = (nodeA->Pos() - nodeB->Pos() ).SquaredEuclideanNorm(); + + if( squaredDist < distMax ) + { + rv = true; + distMax = squaredDist; + aNode1 = nodeA; + aNode2 = nodeB; + } + } + } + } + + return rv; +} + + +unsigned int RN_NET::GetNodeCount() const +{ + return m_nodes.size(); } diff --git a/pcbnew/ratsnest_data.h b/pcbnew/ratsnest_data.h index 94d97c4263..67fd8ca0a2 100644 --- a/pcbnew/ratsnest_data.h +++ b/pcbnew/ratsnest_data.h @@ -30,50 +30,28 @@ #ifndef RATSNEST_DATA_H #define RATSNEST_DATA_H -#include -#include - +#include #include #include #include #include +#include +#include + +#include + class BOARD; class BOARD_ITEM; class BOARD_CONNECTED_ITEM; -class MODULE; -class D_PAD; -class VIA; -class TRACK; -class ZONE_CONTAINER; -class SHAPE_POLY_SET; - -///> Types of items that are handled by the class -enum RN_ITEM_TYPE -{ - RN_PADS = 0x01, - RN_VIAS = 0x02, - RN_TRACKS = 0x04, - RN_ZONES = 0x08, - RN_ALL = 0xFF -}; - -// Preserve KiCad coding style policy -typedef hed::NODE RN_NODE; -typedef hed::NODE_PTR RN_NODE_PTR; -typedef hed::EDGE RN_EDGE; -typedef hed::EDGE_PTR RN_EDGE_PTR; -typedef hed::EDGE_MST RN_EDGE_MST; -typedef hed::TRIANGULATION TRIANGULATOR; -typedef std::shared_ptr RN_EDGE_MST_PTR; - -bool operator==( const RN_NODE_PTR& aFirst, const RN_NODE_PTR& aSecond ); -bool operator!=( const RN_NODE_PTR& aFirst, const RN_NODE_PTR& aSecond ); +class CN_CLUSTER; +class CN_CONNECTIVITY_ALGO; struct RN_NODE_OR_FILTER; struct RN_NODE_AND_FILTER; +#if 0 ///> General interface for filtering out nodes in search functions. struct RN_NODE_FILTER : public std::unary_function { @@ -96,7 +74,7 @@ struct LINE_TARGET : public RN_NODE_FILTER { bool operator()( const RN_NODE_PTR& aNode ) const override { - return !aNode->GetNoLine(); + return true; } }; @@ -109,7 +87,7 @@ struct LINE_TARGET_SAME_TAG : public RN_NODE_FILTER bool operator()( const RN_NODE_PTR& aNode ) const override { - return !aNode->GetNoLine() && aNode->GetTag() == m_tag; + return aNode->GetTag() == m_tag; } private: @@ -124,7 +102,7 @@ struct LINE_TARGET_DIFF_TAG : public RN_NODE_FILTER bool operator()( const RN_NODE_PTR& aNode ) const override { - return !aNode->GetNoLine() && aNode->GetTag() == m_tag; + return aNode->GetTag() != m_tag; } private: @@ -163,168 +141,26 @@ struct RN_NODE_OR_FILTER : public RN_NODE_FILTER const RN_NODE_FILTER& m_filter2; }; - ///> Functor comparing if two nodes are equal by their coordinates. It is required to make set of ///> shared pointers work properly. -struct RN_NODE_COMPARE : std::binary_function +struct RN_NODE_COMPARE : std::binary_function { - bool operator()( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2 ) const + bool operator()( const CN_ANCHOR_PTR& aNode1, const CN_ANCHOR_PTR& aNode2 ) const { - return aNode1 == aNode2; + if ( aNode1->GetY() < aNode2->GetY() ) + return true; + else if ( aNode1->GetY() == aNode2->GetY() ) + { + if ( aNode1->GetX() == aNode2->GetX() ) + return aNode1->GetCluster() < aNode2->GetCluster(); + else + return aNode1->GetX() < aNode2->GetX(); + } + return false; } }; -///> Functor calculating hash for a given node. It is required to make set of shared pointers -///> work properly. -struct RN_NODE_HASH : std::unary_function -{ - std::size_t operator()( const RN_NODE_PTR& aNode ) const - { - std::size_t hash = 2166136261u; - - hash ^= aNode->GetX(); - hash *= 16777619; - hash ^= aNode->GetY(); - - return hash; - } -}; - - -/** - * Class RN_LINKS - * Manages data describing nodes and connections for a given net. - */ -class RN_LINKS -{ -public: - // Helper typedefs - typedef std::unordered_set RN_NODE_SET; - typedef std::list RN_EDGE_LIST; - - /** - * Function AddNode() - * Adds a node with given coordinates and returns pointer to the newly added node. If the node - * existed before, only appropriate pointer is returned. - * @param aX is the x coordinate of a node. - * @param aY is the y coordinate of a node. - * @return Pointer to the node with given coordinates. - */ - const RN_NODE_PTR& AddNode( int aX, int aY ); - - /** - * Function RemoveNode() - * Removes a node described by a given node pointer. - * @param aNode is a pointer to node to be removed. - * @return True if node was removed, false if there were other references, so it was kept. - */ - bool RemoveNode( const RN_NODE_PTR& aNode ); - - /** - * Function GetNodes() - * Returns the set of currently used nodes. - * @return The set of currently used nodes. - */ - const RN_NODE_SET& GetNodes() const - { - return m_nodes; - } - - /** - * Function AddConnection() - * Adds a connection between two nodes and of given distance. Edges with distance equal 0 are - * considered to be existing connections. Distance different than 0 means that the connection - * is missing. - * @param aNode1 is the origin node of a new connection. - * @param aNode2 is the end node of a new connection. - * @param aDistance is the distance of the connection (0 means that nodes are actually - * connected, >0 means a missing connection). - */ - RN_EDGE_MST_PTR AddConnection( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2, - unsigned int aDistance = 0 ); - - /** - * Function RemoveConnection() - * Removes a connection described by a given edge pointer. - * @param aEdge is a pointer to edge to be removed. - */ - void RemoveConnection( const RN_EDGE_PTR& aEdge ) - { - m_edges.remove( aEdge ); - } - - /** - * Function GetConnections() - * Returns the list of edges that currently connect nodes. - * @return the list of edges that currently connect nodes. - */ - const RN_EDGE_LIST& GetConnections() const - { - return m_edges; - } - -protected: - ///> Set of nodes that are expected to be connected together (vias, tracks, pads). - RN_NODE_SET m_nodes; - - ///> List of edges that currently connect nodes. - RN_EDGE_LIST m_edges; -}; - - -/** - * Class RN_POLY - * Describes a single subpolygon (ZONE_CONTAINER is supposed to contain one or more of those) and - * performs fast point-inside-polygon test. - */ -class RN_POLY -{ -public: - RN_POLY( const SHAPE_POLY_SET* aParent, - int aSubpolygonIndex, - RN_LINKS& aConnections, const BOX2I& aBBox ); - - /** - * Function GetNode() - * Returns node representing a polygon (it has the same coordinates as the first point of its - * bounding polyline. - */ - inline const RN_NODE_PTR& GetNode() const - { - return m_node; - } - - inline RN_NODE_PTR& GetNode() - { - return m_node; - } - - /** - * Function HitTest() - * Tests if selected node is located within polygon boundaries. - * @param aNode is a node to be checked. - * @return True is the node is located within polygon boundaries. - */ - bool HitTest( const RN_NODE_PTR& aNode ) const; - -private: - - ///> Index of the outline in the parent polygon set - int m_subpolygonIndex; - - ///> Bounding box of the polygon. - BOX2I m_bbox; - - ///> Polygon set containing the geometry - const SHAPE_POLY_SET* m_parentPolyset; - - ///> Node representing a polygon (it has the same coordinates as the first point of its - ///> bounding polyline. - RN_NODE_PTR m_node; - - friend bool sortArea( const RN_POLY& aP1, const RN_POLY& aP2 ); -}; - +#endif /** * Class RN_NET @@ -334,8 +170,7 @@ class RN_NET { public: ///> Default constructor. - RN_NET() : m_dirty( true ), m_visible( true ) - {} + RN_NET(); /** * Function SetVisible() @@ -383,9 +218,9 @@ public: * Returns pointer to a vector of edges that makes ratsnest for a given net. * @return Pointer to a vector of edges that makes ratsnest for a given net. */ - const std::vector* GetUnconnected() const + const std::vector GetUnconnected() const { - return m_rnEdges.get(); + return m_rnEdges; } /** @@ -393,70 +228,12 @@ public: * Recomputes ratsnest for a net. */ void Update(); + void Clear(); - /** - * Function AddItem() - * Adds an appropriate node associated with selected pad, so it is - * taken into account during ratsnest computations. - * @param aPad is a pad for which node is added. - */ - bool AddItem( const D_PAD* aPad ); + void AddCluster( std::shared_ptr aCluster ); - /** - * Function AddItem() - * Adds an appropriate node associated with selected via, so it is - * taken into account during ratsnest computations. - * @param aVia is a via for which node is added. - */ - bool AddItem( const VIA* aVia ); - /** - * Function AddItem() - * Adds appropriate nodes and edges associated with selected track, so they are - * taken into account during ratsnest computations. - * @param aTrack is a track for which nodes and edges are added. - */ - bool AddItem( const TRACK* aTrack ); - - /** - * Function AddItem() - * Processes zone to split it into subpolygons and adds appropriate nodes for them, so they are - * taken into account during ratsnest computations. - * @param aZone is a zone to be processed. - */ - bool AddItem( const ZONE_CONTAINER* aZone ); - - /** - * Function RemoveItem() - * Removes all nodes and edges associated with selected pad, so they are not - * taken into account during ratsnest computations anymore. - * @param aPad is a pad for which nodes and edges are removed. - */ - bool RemoveItem( const D_PAD* aPad ); - - /** - * Function RemoveItem() - * Removes all nodes and edges associated with selected via, so they are not - * taken into account during ratsnest computations anymore. - * @param aVia is a via for which nodes and edges are removed. - */ - bool RemoveItem( const VIA* aVia ); - - /** - * Function RemoveItem() - * Removes all nodes and edges associated with selected track, so they are not - * taken into account during ratsnest computations anymore. - * @param aTrack is a track for which nodes and edges are removed. - */ - bool RemoveItem( const TRACK* aTrack ); - - /** - * Function RemoveItem() - * Removes all nodes and edges associated with selected zone, so they are not - * taken into account during ratsnest computations anymore. - * @param aZone is a zone for which nodes and edges are removed. - */ - bool RemoveItem( const ZONE_CONTAINER* aZone ); + unsigned int GetNodeCount() const; /** * Function GetNodes() @@ -464,7 +241,12 @@ public: * @param aItem is an item for which the list is generated. * @return List of associated nodes. */ - std::list GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const; + std::list GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const; + + const std::vector& GetEdges() const + { + return m_rnEdges; + } /** * Function GetAllItems() @@ -472,14 +254,16 @@ public: * @param aOutput is the list that will have items added. * @param aType determines the type of added items. */ - void GetAllItems( std::list& aOutput, RN_ITEM_TYPE aType = RN_ALL ) const; + void GetAllItems( std::list& aOutput, const KICAD_T aTypes[] ) const; /** * Function GetClosestNode() * Returns a single node that lies in the shortest distance from a specific node. * @param aNode is the node for which the closest node is searched. */ - const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode ) const; + const CN_ANCHOR_PTR GetClosestNode( const CN_ANCHOR_PTR& aNode ) const; + + bool NearestBicoloredPair( const RN_NET& aOtherNet, CN_ANCHOR_PTR& aNode1, CN_ANCHOR_PTR& aNode2 ) const; /** * Function GetClosestNode() @@ -488,8 +272,8 @@ public: * @param aNode is the node for which the closest node is searched. * @param aFilter is a functor that filters nodes. */ - const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode, - const RN_NODE_FILTER& aFilter ) const; + /*const CN_ANCHOR_PTR GetClosestNode( const RN_NODE_PTR& aNode, + const RN_NODE_FILTER& aFilter ) const;*/ /** * Function GetClosestNodes() @@ -499,7 +283,7 @@ public: * belong to the same net are returned. If asked number is greater than number of possible * nodes then the size of list is limited to number of possible nodes. */ - std::list GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber = -1 ) const; + //std::list GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber = -1 ) const; /** * Function GetClosestNodes() @@ -510,291 +294,33 @@ public: * belong to the same net are returned. If asked number is greater than number of possible * nodes then the size of list is limited to number of possible nodes. */ - std::list GetClosestNodes( const RN_NODE_PTR& aNode, - const RN_NODE_FILTER& aFilter, int aNumber = -1 ) const; + //std::list GetClosestNodes( const RN_NODE_PTR& aNode, + // const RN_NODE_FILTER& aFilter, int aNumber = -1 ) const; - /** - * Function AddSimple() - * Changes drawing mode for an item to simple (i.e. one ratsnest line per node). - * @param aItem is the item that changes its drawing mode. - */ - void AddSimple( const BOARD_CONNECTED_ITEM* aItem ); - - /** - * Function AddBlockedNode() - * Specifies a node as not suitable as a ratsnest line target (i.e. ratsnest lines will not - * target the node). The status is cleared after calling ClearSimple(). - * @param aNode is the node that is not going to be used as a ratsnest line target. - */ - inline void AddBlockedNode( RN_NODE_PTR& aNode ) - { - m_blockedNodes.insert( aNode ); - aNode->SetNoLine( true ); - } - - /** - * Function GetSimpleNodes() - * Returns list of nodes for which ratsnest is drawn in simple mode (i.e. one - * ratsnest line per node). - * @return list of nodes for which ratsnest is drawn in simple mode. - */ - inline const std::unordered_set& GetSimpleNodes() const - { - return m_simpleNodes; - } - - /** - * Function ClearSimple() - * Removes all nodes and edges that are used for displaying ratsnest in simple mode. - */ - void ClearSimple(); - - /** - * Function GetConnectedItems() - * Adds items that are connected together to a list. - * @param aItem is the reference item to find other connected items. - * @param aOutput is the list that will contain found items. - * @param aTypes allows to filter by item types. - */ - void GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, - std::list& aOutput, - RN_ITEM_TYPE aTypes = RN_ALL ) const; protected: - ///> Validates edge, i.e. modifies source and target nodes for an edge - ///> to make sure that they are not ones with the flag set. - void validateEdge( RN_EDGE_MST_PTR& aEdge ); - - ///> Removes a link between a node and a parent, - ///> and clears linked edges if it was the last parent. - void removeNode( RN_NODE_PTR& aNode, const BOARD_CONNECTED_ITEM* aParent ); - - ///> Removes a link between an edge and a parent, - ///> and clears its node data if it was the last parent. - void removeEdge( RN_EDGE_MST_PTR& aEdge, const BOARD_CONNECTED_ITEM* aParent ); - - ///> Removes all ratsnest edges for a given node. - void clearNode( const RN_NODE_PTR& aNode ); - - ///> Adds appropriate edges for nodes that are connected by zones. - void processZones(); - - ///> Adds additional edges to account for connections made by items located in pads areas. - void processPads(); - ///> Recomputes ratsnset from scratch. void compute(); - ////> Stores information about connections for a given net. - RN_LINKS m_links; + ///> Vector of nodes + std::vector m_nodes; + + ///> Vector of edges that make pre-defined connections + std::vector m_boardEdges; ///> Vector of edges that makes ratsnest for a given net. - std::shared_ptr< std::vector > m_rnEdges; - - ///> List of nodes which will not be used as ratsnest target nodes. - std::unordered_set m_blockedNodes; - - ///> Nodes to be displayed using the simplified ratsnest algorithm. - std::unordered_set m_simpleNodes; + std::vector m_rnEdges; ///> Flag indicating necessity of recalculation of ratsnest for a net. bool m_dirty; - ///> Structure to hold ratsnest data for ZONE_CONTAINER objects. - typedef struct - { - ///> Subpolygons belonging to a zone - std::deque m_Polygons; - - ///> Connections to other nodes - std::deque m_Edges; - } RN_ZONE_DATA; - - ///> Structureo to hold ratsnest data for D_PAD objects. - typedef struct - { - ///> Node representing the pad. - RN_NODE_PTR m_Node; - - ///> Helper nodes that make for connections to items located in the pad area. - std::deque m_Edges; - } RN_PAD_DATA; - - ///> Helper typedefs - typedef std::unordered_map PAD_NODE_MAP; - typedef std::unordered_map VIA_NODE_MAP; - typedef std::unordered_map TRACK_EDGE_MAP; - typedef std::unordered_map ZONE_DATA_MAP; - - ///> Map that associates nodes in the ratsnest model to respective nodes. - PAD_NODE_MAP m_pads; - - ///> Map that associates nodes in the ratsnest model to respective vias. - VIA_NODE_MAP m_vias; - - ///> Map that associates edges in the ratsnest model to respective tracks. - TRACK_EDGE_MAP m_tracks; - - ///> Map that associates groups of subpolygons in the ratsnest model to respective zones. - ZONE_DATA_MAP m_zones; - ///> Visibility flag. bool m_visible; + + class TRIANGULATOR_STATE; + + std::shared_ptr m_triangulator; }; -/** - * Class RN_DATA - * - * Stores information about unconnected items for a board. - */ -class RN_DATA -{ -public: - /** - * Default constructor - * @param aBoard is the board to be processed in order to look for unconnected items. - */ - RN_DATA( const BOARD* aBoard ) : m_board( aBoard ) {} - - /** - * Function Add() - * Adds an item to the ratsnest data. - * @param aItem is an item to be added. - * @return True if operation succeeded. - */ - bool Add( const BOARD_ITEM* aItem ); - - /** - * Function Remove() - * Removes an item from the ratsnest data. - * @param aItem is an item to be updated. - * @return True if operation succeeded. - */ - bool Remove( const BOARD_ITEM* aItem ); - - /** - * Function Update() - * Updates the ratsnest data for an item. - * @param aItem is an item to be updated. - * @return True if operation succeeded. The item will not be updated if it was not previously - * added to the ratsnest. - */ - bool Update( const BOARD_ITEM* aItem ); - - /** - * Function AddSimple() - * Sets an item to be drawn in simple mode (i.e. one line per node, instead of full ratsnest). - * It is used for drawing quick, temporary ratsnest, eg. while moving an item. - * @param aItem is an item to be drawn in simple node. - */ - void AddSimple( const BOARD_ITEM* aItem ); - - /** - * Function AddBlocked() - * Specifies an item as not suitable as a ratsnest line target (i.e. ratsnest lines will not - * target its node(s)). The status is cleared after calling ClearSimple(). - * @param aItem is the item of which node(s) are not going to be used as a ratsnest line target. - */ - void AddBlocked( const BOARD_ITEM* aItem ); - - /** - * Function ClearSimple() - * Clears the list of nodes for which ratsnest is drawn in simple mode (one line per node). - */ - void ClearSimple() - { - for( RN_NET& net : m_nets ) - net.ClearSimple(); - } - - /** - * Function ProcessBoard() - * Prepares data for computing (computes a list of current nodes and connections). It is - * required to run only once after loading a board. - */ - void ProcessBoard(); - - /** - * Function Recalculate() - * Recomputes ratsnest for selected net number or all nets that need updating. - * @param aNet is a net number. If it is negative, all nets that need updating are recomputed. - */ - void Recalculate( int aNet = -1 ); - - /** - * Function GetNetCount() - * Returns the number of nets handled by the ratsnest. - * @return Number of the nets. - */ - int GetNetCount() const - { - return m_nets.size(); - } - - /** - * Function GetNet() - * Returns ratsnest grouped by net numbers. - * @param aNetCode is the net code. - * @return Ratsnest data for a specified net. - */ - RN_NET& GetNet( int aNetCode ) - { - assert( aNetCode > 0 ); // ratsnest does not handle the unconnected net - - return m_nets[aNetCode]; - } - - /** - * Function GetConnectedItems() - * Adds items that are connected together to a list. - * @param aItem is the reference item to find other connected items. - * @param aOutput is the list that will contain found items. - * @param aTypes allows to filter by item types. - */ - void GetConnectedItems( const BOARD_CONNECTED_ITEM* aItem, - std::list& aOutput, - RN_ITEM_TYPE aTypes = RN_ALL ) const; - - /** - * Function GetNetItems() - * Adds all items that belong to a certain net to a list. - * @param aNetCode is the net code. - * @param aOutput is the list that will have items added. - * @param aTypes allows to filter by item types. - */ - void GetNetItems( int aNetCode, std::list& aOutput, - RN_ITEM_TYPE aTypes = RN_ALL ) const; - - /** - * Function AreConnected() - * Checks if two items are connected with copper. - * @param aItem is the first item. - * @param aOther is the second item. - * @return true if they are connected, false otherwise. - */ - bool AreConnected( const BOARD_CONNECTED_ITEM* aItem, const BOARD_CONNECTED_ITEM* aOther ); - - /** - * Function GetUnconnectedCount() - * Returns the number of missing connections. - * @return Number of missing connections. - */ - int GetUnconnectedCount() const; - -protected: - /** - * Function updateNet() - * Recomputes ratsnest for a single net. - * @param aNetCode is the net number to be recomputed. - */ - void updateNet( int aNetCode ); - - ///> Board to be processed. - const BOARD* m_board; - - ///> Stores information about ratsnest grouped by net numbers. - std::vector m_nets; -}; - #endif /* RATSNEST_DATA_H */ diff --git a/pcbnew/ratsnest_viewitem.cpp b/pcbnew/ratsnest_viewitem.cpp index 2d8fff6fd4..6e7ce40e70 100644 --- a/pcbnew/ratsnest_viewitem.cpp +++ b/pcbnew/ratsnest_viewitem.cpp @@ -29,15 +29,18 @@ #include #include +#include #include #include #include +#include + #include namespace KIGFX { -RATSNEST_VIEWITEM::RATSNEST_VIEWITEM( RN_DATA* aData ) : +RATSNEST_VIEWITEM::RATSNEST_VIEWITEM( std::shared_ptr aData ) : EDA_ITEM( NOT_USED ), m_data( aData ) { } @@ -52,63 +55,76 @@ const BOX2I RATSNEST_VIEWITEM::ViewBBox() const return bbox; } +#include +std::vector 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; + auto gal = aView->GetGAL(); - gal->SetIsStroke( true ); + gal->SetIsStroke( true ); gal->SetIsFill( false ); gal->SetLineWidth( 1.0 ); auto rs = aView->GetPainter()->GetSettings(); auto color = rs->GetColor( NULL, LAYER_RATSNEST ); + 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 ) ); + } 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->GetNet( i ); + RN_NET* net = m_data->GetRatsnestForNet( i ); - if( !net.IsVisible() ) + if( !net->IsVisible() ) continue; - // Set brighter color for the temporary ratsnest - gal->SetStrokeColor( color.Brightened( 0.8 ) ); - - // Draw the "dynamic" ratsnest (i.e. for objects that may be currently being moved) - for( const RN_NODE_PTR& node : net.GetSimpleNodes() ) - { - // Skipping nodes with higher reference count avoids displaying redundant lines - if( node->GetRefCount() > 1 ) - continue; - - RN_NODE_PTR dest = net.GetClosestNode( node, LINE_TARGET() ); - - if( dest ) - { - VECTOR2D origin( node->GetX(), node->GetY() ); - VECTOR2D end( dest->GetX(), dest->GetY() ); - - gal->DrawLine( origin, end ); - } - } - // Draw the "static" ratsnest if( i != highlightedNet ) gal->SetStrokeColor( color ); // using the default ratsnest color for not highlighted - const std::vector* edges = net.GetUnconnected(); - - if( edges == NULL ) - continue; - - for( const RN_EDGE_MST_PTR& edge : *edges ) + for( const auto& edge : net->GetUnconnected() ) { - const RN_NODE_PTR& sourceNode = edge->GetSourceNode(); - const RN_NODE_PTR& targetNode = edge->GetTargetNode(); - VECTOR2D source( sourceNode->GetX(), sourceNode->GetY() ); - VECTOR2D target( targetNode->GetX(), targetNode->GetY() ); + const auto& sourceNode = edge.GetSourceNode(); + const auto& targetNode = edge.GetTargetNode(); + const VECTOR2I source( sourceNode->Pos() ); + const VECTOR2I target( targetNode->Pos() ); - gal->DrawLine( source, target ); + if ( !sourceNode->GetNoLine() && !targetNode->GetNoLine() ) + { + 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 ) ); + } + else + { + gal->DrawLine( source, target ); + } + } } } } diff --git a/pcbnew/ratsnest_viewitem.h b/pcbnew/ratsnest_viewitem.h index 7573e59ce4..22729a03e5 100644 --- a/pcbnew/ratsnest_viewitem.h +++ b/pcbnew/ratsnest_viewitem.h @@ -30,18 +30,19 @@ #ifndef RATSNEST_VIEWITEM_H #define RATSNEST_VIEWITEM_H +#include #include #include class GAL; -class RN_DATA; +class CONNECTIVITY_DATA; namespace KIGFX { class RATSNEST_VIEWITEM : public EDA_ITEM { public: - RATSNEST_VIEWITEM( RN_DATA* aData ); + RATSNEST_VIEWITEM( std::shared_ptr aData ); /// @copydoc VIEW_ITEM::ViewBBox() const BOX2I ViewBBox() const override; @@ -69,7 +70,7 @@ public: protected: ///> Object containing ratsnest data. - RN_DATA* m_data; + std::shared_ptr m_data; }; } // namespace KIGFX diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp new file mode 100644 index 0000000000..e4f35c0b14 --- /dev/null +++ b/pcbnew/tools/common_actions.cpp @@ -0,0 +1,809 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013-2016 CERN + * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * @author Maciej Suminski + * + * 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 "common_actions.h" +#include +#include +#include +#include +#include +#include + +// These members are static in class COMMON_ACTIONS: Build them here: + +// Selection tool actions +TOOL_ACTION COMMON_ACTIONS::selectionActivate( "pcbnew.InteractiveSelection", + AS_GLOBAL, 0, + "", "", NULL, AF_ACTIVATE ); // No description, it is not supposed to be shown anywhere + +TOOL_ACTION COMMON_ACTIONS::selectionCursor( "pcbnew.InteractiveSelection.Cursor", + AS_GLOBAL, 0, + "", "" ); // No description, it is not supposed to be shown anywhere + +TOOL_ACTION COMMON_ACTIONS::selectItem( "pcbnew.InteractiveSelection.SelectItem", + AS_GLOBAL, 0, + "", "" ); // No description, it is not supposed to be shown anywhere + +TOOL_ACTION COMMON_ACTIONS::unselectItem( "pcbnew.InteractiveSelection.UnselectItem", + AS_GLOBAL, 0, + "", "" ); // No description, it is not supposed to be shown anywhere + +TOOL_ACTION COMMON_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear", + AS_GLOBAL, 0, + "", "" ); // No description, it is not supposed to be shown anywhere + +TOOL_ACTION COMMON_ACTIONS::selectConnection( "pcbnew.InteractiveSelection.SelectConnection", + AS_GLOBAL, 'U', + _( "Trivial Connection" ), _( "Selects a connection between two junctions." ) ); + +TOOL_ACTION COMMON_ACTIONS::selectCopper( "pcbnew.InteractiveSelection.SelectCopper", + AS_GLOBAL, 'I', + _( "Copper Connection" ), _( "Selects whole copper connection." ) ); + +TOOL_ACTION COMMON_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet", + AS_GLOBAL, 0, + _( "Whole Net" ), _( "Selects all tracks & vias belonging to the same net." ) ); + +TOOL_ACTION COMMON_ACTIONS::selectSameSheet( "pcbnew.InteractiveSelection.SelectSameSheet", + AS_GLOBAL, 'P', + _( "Same Sheet" ), _( "Selects all modules and tracks in the same schematic sheet" ) ); + +TOOL_ACTION COMMON_ACTIONS::find( "pcbnew.InteractiveSelection.Find", + AS_GLOBAL, 0, //TOOL_ACTION::LegacyHotKey( HK_FIND_ITEM ), // handled by wxWidgets + _( "Find Item" ), _( "Searches the document for an item" ), find_xpm ); + +TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_GET_AND_MOVE_FOOTPRINT ) ); + + +// Edit tool actions +TOOL_ACTION COMMON_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.editFootprintInFpEditor", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT_MODULE_WITH_MODEDIT ), + _( "Open in Footprint Editor" ), + _( "Opens the selected footprint in the Footprint Editor" ), + module_editor_xpm ); + +TOOL_ACTION COMMON_ACTIONS::copyPadToSettings( "pcbnew.InteractiveEdit.copyPadToSettings", + AS_GLOBAL, 0, + _( "Copy Pad Settings to Current Settings" ), + _( "Copies the properties of selected pad to the current template pad settings." ) ); + +TOOL_ACTION COMMON_ACTIONS::copySettingsToPads( "pcbnew.InteractiveEdit.copySettingsToPads", + AS_GLOBAL, 0, + _( "Copy Current Settings to Pads" ), + _( "Copies the current template pad settings to the selected pad(s)." ) ); + +TOOL_ACTION COMMON_ACTIONS::globalEditPads( "pcbnew.InteractiveEdit.globalPadEdit", + AS_GLOBAL, 0, + _( "Global Pad Edition" ), + _( "Changes pad properties globally." ), push_pad_settings_xpm ); + +TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_ITEM ), + _( "Move" ), _( "Moves the selected item(s)" ), move_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drag( "pcbnew.InteractiveEdit.dragItem", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DRAG_TRACK_KEEP_SLOPE ), + _( "Drag" ), _( "Drags the selected item(s)" ), drag_track_segment_xpm ); + +TOOL_ACTION COMMON_ACTIONS::duplicate( "pcbnew.InteractiveEdit.duplicate", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM ), + _( "Duplicate" ), _( "Duplicates the selected item(s)" ), duplicate_module_xpm ); + +TOOL_ACTION COMMON_ACTIONS::duplicateIncrement( "pcbnew.InteractiveEdit.duplicateIncrementPads", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DUPLICATE_ITEM_AND_INCREMENT ), + _( "Duplicate" ), _( "Duplicates the selected item(s), incrementing pad numbers" ) ); + +TOOL_ACTION COMMON_ACTIONS::moveExact( "pcbnew.InteractiveEdit.moveExact", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_MOVE_ITEM_EXACT ), + _( "Move Exactly..." ), _( "Moves the selected item(s) by an exact amount" ), + move_module_xpm ); + +TOOL_ACTION COMMON_ACTIONS::createArray( "pcbnew.InteractiveEdit.createArray", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_CREATE_ARRAY ), + _( "Create Array" ), _( "Create array" ), array_module_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::rotateCw( "pcbnew.InteractiveEdit.rotateCw", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ROTATE_ITEM ), + _( "Rotate Clockwise" ), _( "Rotates selected item(s) clockwise" ), + rotate_cw_xpm, AF_NONE, (void*) 1 ); + +TOOL_ACTION COMMON_ACTIONS::rotateCcw( "pcbnew.InteractiveEdit.rotateCcw", + AS_GLOBAL, MD_SHIFT + 'R', + _( "Rotate Counter-clockwise" ), _( "Rotates selected item(s) counter-clockwise" ), + rotate_ccw_xpm, AF_NONE, (void*) -1 ); + +TOOL_ACTION COMMON_ACTIONS::flip( "pcbnew.InteractiveEdit.flip", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_FLIP_ITEM ), + _( "Flip" ), _( "Flips selected item(s)" ), swap_layer_xpm ); + +TOOL_ACTION COMMON_ACTIONS::mirror( "pcbnew.InteractiveEdit.mirror", + AS_GLOBAL, 0, + _( "Mirror" ), _( "Mirrors selected item" ), mirror_h_xpm ); + +TOOL_ACTION COMMON_ACTIONS::remove( "pcbnew.InteractiveEdit.remove", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_BACK_SPACE ), + _( "Remove" ), _( "Deletes selected item(s)" ), delete_xpm, + AF_NONE, (void*) REMOVE_FLAGS::NORMAL ); + +TOOL_ACTION COMMON_ACTIONS::removeAlt( "pcbnew.InteractiveEdit.removeAlt", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DELETE ), + _( "Remove (Alternative)" ), _( "Deletes selected item(s)" ), delete_xpm, + AF_NONE, (void*) REMOVE_FLAGS::ALT ); + +TOOL_ACTION COMMON_ACTIONS::exchangeFootprints( "pcbnew.InteractiveEdit.ExchangeFootprints", + AS_GLOBAL, 0, + _( "Exchange Footprint(s)" ), _( "Change the footprint used for modules" ), + import_module_xpm ); + + +TOOL_ACTION COMMON_ACTIONS::properties( "pcbnew.InteractiveEdit.properties", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_EDIT_ITEM ), + _( "Properties..." ), _( "Displays item properties dialog" ), editor_xpm ); + +TOOL_ACTION COMMON_ACTIONS::editModifiedSelection( "pcbnew.InteractiveEdit.ModifiedSelection", + AS_GLOBAL, 0, + "", "" ); + + +// Drawing tool actions +TOOL_ACTION COMMON_ACTIONS::drawLine( "pcbnew.InteractiveDrawing.line", + AS_GLOBAL, 0, + _( "Draw Line" ), _( "Draw a line" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawCircle( "pcbnew.InteractiveDrawing.circle", + AS_GLOBAL, 0, + _( "Draw Circle" ), _( "Draw a circle" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawArc( "pcbnew.InteractiveDrawing.arc", + AS_GLOBAL, 0, + _( "Draw Arc" ), _( "Draw an arc" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::placeText( "pcbnew.InteractiveDrawing.text", + AS_GLOBAL, 0, + _( "Add Text" ), _( "Add a text" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawDimension( "pcbnew.InteractiveDrawing.dimension", + AS_GLOBAL, 0, + _( "Add Dimension" ), _( "Add a dimension" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawZone( "pcbnew.InteractiveDrawing.zone", + AS_GLOBAL, 0, + _( "Add Filled Zone" ), _( "Add a filled zone" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawKeepout( "pcbnew.InteractiveDrawing.keepout", + AS_GLOBAL, 0, + _( "Add Keepout Area" ), _( "Add a keepout area" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawZoneCutout( "pcbnew.InteractiveDrawing.zoneCutout", + AS_GLOBAL, 0, + _( "Add a Zone Cutout" ), _( "Add a cutout area of an existing zone" ), + add_zone_cutout_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drawSimilarZone( "pcbnew.InteractiveDrawing.similarZone", + AS_GLOBAL, 0, + _( "Add a Similar Zone" ), _( "Add a zone with the same settings as an existing zone" ), + add_zone_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::placeDXF( "pcbnew.InteractiveDrawing.placeDXF", + AS_GLOBAL, 0, + "Place DXF", "", NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::setAnchor( "pcbnew.InteractiveDrawing.setAnchor", + AS_GLOBAL, 0, + _( "Place the Footprint Anchor" ), _( "Place the footprint anchor" ), + NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::incWidth( "pcbnew.InteractiveDrawing.incWidth", + AS_CONTEXT, '+', + _( "Increase Line Width" ), _( "Increase the line width" ) ); + +TOOL_ACTION COMMON_ACTIONS::decWidth( "pcbnew.InteractiveDrawing.decWidth", + AS_CONTEXT, '-', + _( "Decrease Line Width" ), _( "Decrease the line width" ) ); + +TOOL_ACTION COMMON_ACTIONS::arcPosture( "pcbnew.InteractiveDrawing.arcPosture", + AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_POSTURE ), + _( "Switch Arc Posture" ), _( "Switch the arc posture" ) ); + + +// View Controls +TOOL_ACTION COMMON_ACTIONS::zoomIn( "common.Control.zoomIn", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_IN ), + _( "Zoom In" ), "", zoom_in_xpm ); + +TOOL_ACTION COMMON_ACTIONS::zoomOut( "common.Control.zoomOut", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_OUT ), + _( "Zoom Out" ), "", zoom_out_xpm ); + +TOOL_ACTION COMMON_ACTIONS::zoomInCenter( "common.Control.zoomInCenter", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::zoomOutCenter( "common.Control.zoomOutCenter", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::zoomCenter( "common.Control.zoomCenter", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_CENTER ), + _( "Center" ), "", zoom_center_on_screen_xpm ); + +TOOL_ACTION COMMON_ACTIONS::zoomFitScreen( "common.Control.zoomFitScreen", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_AUTO ), + _( "Zoom Auto" ), "", zoom_fit_in_page_xpm ); + +TOOL_ACTION COMMON_ACTIONS::zoomPreset( "common.Control.zoomPreset", + AS_GLOBAL, 0, + "", "" ); + + +// Display modes +TOOL_ACTION COMMON_ACTIONS::trackDisplayMode( "pcbnew.Control.trackDisplayMode", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_DISPLAY_MODE ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::padDisplayMode( "pcbnew.Control.padDisplayMode", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::viaDisplayMode( "pcbnew.Control.viaDisplayMode", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::zoneDisplayEnable( "pcbnew.Control.zoneDisplayEnable", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::zoneDisplayDisable( "pcbnew.Control.zoneDisplayDisable", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::zoneDisplayOutlines( "pcbnew.Control.zoneDisplayOutlines", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::highContrastMode( "pcbnew.Control.highContrastMode", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_HIGHCONTRAST_MODE ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::highContrastInc( "pcbnew.Control.highContrastInc", + AS_GLOBAL, '>', + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::highContrastDec( "pcbnew.Control.highContrastDec", + AS_GLOBAL, '<', + "", "" ); + + +// Layer control +TOOL_ACTION COMMON_ACTIONS::layerTop( "pcbnew.Control.layerTop", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_COMPONENT ), + "", "", NULL, AF_NONE, (void*) F_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerInner1( "pcbnew.Control.layerInner1", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER1 ), + "", "", NULL, AF_NONE, (void*) In1_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerInner2( "pcbnew.Control.layerInner2", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER2 ), + "", "", NULL, AF_NONE, (void*) In2_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerInner3( "pcbnew.Control.layerInner3", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER3 ), + "", "", NULL, AF_NONE, (void*) In3_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerInner4( "pcbnew.Control.layerInner4", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER4 ), + "", "", NULL, AF_NONE, (void*) In4_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerInner5( "pcbnew.Control.layerInner5", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER5 ), + "", "", NULL, AF_NONE, (void*) In5_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerInner6( "pcbnew.Control.layerInner6", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_INNER6 ), + "", "", NULL, AF_NONE, (void*) In6_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerBottom( "pcbnew.Control.layerBottom", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_COPPER ), + "", "", NULL, AF_NONE, (void*) B_Cu ); + +TOOL_ACTION COMMON_ACTIONS::layerNext( "pcbnew.Control.layerNext", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_NEXT ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::layerPrev( "pcbnew.Control.layerPrev", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_LAYER_TO_PREVIOUS ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::layerToggle( "pcbnew.Control.layerToggle", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_THROUGH_VIA ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::layerAlphaInc( "pcbnew.Control.layerAlphaInc", + AS_GLOBAL, '}', + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::layerAlphaDec( "pcbnew.Control.layerAlphaDec", + AS_GLOBAL, '{', + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::layerChanged( "pcbnew.Control.layerChanged", + AS_GLOBAL, 0, + "", "", NULL, AF_NOTIFY ); + + +// Grid control +TOOL_ACTION COMMON_ACTIONS::gridFast1( "common.Control.gridFast1", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_FASTGRID1 ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::gridFast2( "common.Control.gridFast2", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_FASTGRID2 ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::gridNext( "common.Control.gridNext", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_NEXT ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::gridPrev( "common.Control.gridPrev", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_GRID_TO_PREVIOUS ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::gridSetOrigin( "common.Control.gridSetOrigin", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SET_GRID_ORIGIN ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::gridResetOrigin( "common.Control.gridResetOrigin", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_RESET_GRID_ORIGIN ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::gridPreset( "common.Control.gridPreset", + AS_GLOBAL, 0, + "", "" ); + +// Track & via size control +TOOL_ACTION COMMON_ACTIONS::trackWidthInc( "pcbnew.EditorControl.trackWidthInc", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_WIDTH_TO_NEXT ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::trackWidthDec( "pcbnew.EditorControl.trackWidthDec", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_WIDTH_TO_PREVIOUS ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::viaSizeInc( "pcbnew.EditorControl.viaSizeInc", + AS_GLOBAL, '\'', + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::viaSizeDec( "pcbnew.EditorControl.viaSizeDec", + AS_GLOBAL, '\\', + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::trackViaSizeChanged( "pcbnew.EditorControl.trackViaSizeChanged", + AS_GLOBAL, 0, + "", "", NULL, AF_NOTIFY ); + + +// Zone actions +TOOL_ACTION COMMON_ACTIONS::zoneFill( "pcbnew.EditorControl.zoneFill", + AS_GLOBAL, 0, + _( "Fill" ), _( "Fill zone(s)" ), fill_zone_xpm ); + +TOOL_ACTION COMMON_ACTIONS::zoneFillAll( "pcbnew.EditorControl.zoneFillAll", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_FILL_OR_REFILL ), + _( "Fill All" ), _( "Fill all zones" ) ); + +TOOL_ACTION COMMON_ACTIONS::zoneUnfill( "pcbnew.EditorControl.zoneUnfill", + AS_GLOBAL, 0, + _( "Unfill" ), _( "Unfill zone(s)" ), zone_unfill_xpm ); + +TOOL_ACTION COMMON_ACTIONS::zoneUnfillAll( "pcbnew.EditorControl.zoneUnfillAll", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_REMOVE_FILLED ), + _( "Unfill All" ), _( "Unfill all zones" ) ); + +TOOL_ACTION COMMON_ACTIONS::zoneMerge( "pcbnew.EditorControl.zoneMerge", + AS_GLOBAL, 0, + _( "Merge Zones" ), _( "Merge zones" ) ); + +TOOL_ACTION COMMON_ACTIONS::zoneDuplicate( "pcbnew.EditorControl.zoneDuplicate", + AS_GLOBAL, 0, + _( "Duplicate Zone onto Layer" ), _( "Duplicate zone outline onto a different layer" ), + zone_duplicate_xpm ); + + +TOOL_ACTION COMMON_ACTIONS::placeTarget( "pcbnew.EditorControl.placeTarget", + AS_GLOBAL, 0, + _( "Add Layer Alignment Target" ), _( "Add a layer alignment target" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::placeModule( "pcbnew.EditorControl.placeModule", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_MODULE ), + _( "Add Footprint" ), _( "Add a footprint" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::drillOrigin( "pcbnew.EditorControl.drillOrigin", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::crossProbeSchToPcb( "pcbnew.EditorControl.crossProbSchToPcb", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::toggleLock( "pcbnew.EditorControl.toggleLock", + AS_GLOBAL, 'L', + "Toggle Lock", "" ); + +TOOL_ACTION COMMON_ACTIONS::lock( "pcbnew.EditorControl.lock", + AS_GLOBAL, 0, + _( "Lock" ), "" ); + +TOOL_ACTION COMMON_ACTIONS::unlock( "pcbnew.EditorControl.unlock", + AS_GLOBAL, 0, + _( "Unlock" ), "" ); + +TOOL_ACTION COMMON_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::highlightNet( "pcbnew.EditorControl.highlightNet", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::highlightNetCursor( "pcbnew.EditorControl.highlightNetCursor", + AS_GLOBAL, 0, + "", "" ); + + +// Module editor tools +TOOL_ACTION COMMON_ACTIONS::placePad( "pcbnew.ModuleEditor.placePad", + AS_GLOBAL, 0, + _( "Add Pad" ), _( "Add a pad" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::enumeratePads( "pcbnew.ModuleEditor.enumeratePads", + AS_GLOBAL, 0, + _( "Enumerate Pads" ), _( "Enumerate pads" ), pad_enumerate_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::copyItems( "pcbnew.ModuleEditor.copyItems", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_COPY_ITEM ), + _( "Copy" ), _( "Copy items" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::pasteItems( "pcbnew.ModuleEditor.pasteItems", + AS_GLOBAL, MD_CTRL + int( 'V' ), + _( "Paste" ), _( "Paste items" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::moduleEdgeOutlines( "pcbnew.ModuleEditor.graphicOutlines", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::moduleTextOutlines( "pcbnew.ModuleEditor.textOutlines", + AS_GLOBAL, 0, + "", "" ); + +// Pad tools +TOOL_ACTION COMMON_ACTIONS::copyPadSettings( + "pcbnew.PadTool.CopyPadSettings", + AS_GLOBAL, 0, + _( "Copy Pad Settings" ), _( "Copy current pad's settings to the board design settings" ), + copy_pad_settings_xpm ); + +TOOL_ACTION COMMON_ACTIONS::applyPadSettings( + "pcbnew.PadTool.ApplyPadSettings", + AS_GLOBAL, 0, + _( "Apply Pad Settings" ), _( "Copy the board design settings pad properties to the current pad" ), + apply_pad_settings_xpm ); + +TOOL_ACTION COMMON_ACTIONS::pushPadSettings( + "pcbnew.PadTool.PushPadSettings", + AS_GLOBAL, 0, + _( "Push Pad Settings" ), _( "Copy the current pad settings to other pads" ), + push_pad_settings_xpm ); + +// Cursor control +TOOL_ACTION COMMON_ACTIONS::cursorUp( "pcbnew.Control.cursorUp", + AS_GLOBAL, WXK_UP, "", "", NULL, AF_NONE, (void*) CURSOR_UP ); +TOOL_ACTION COMMON_ACTIONS::cursorDown( "pcbnew.Control.cursorDown", + AS_GLOBAL, WXK_DOWN, "", "" , NULL, AF_NONE, (void*) CURSOR_DOWN ); +TOOL_ACTION COMMON_ACTIONS::cursorLeft( "pcbnew.Control.cursorLeft", + AS_GLOBAL, WXK_LEFT, "", "" , NULL, AF_NONE, (void*) CURSOR_LEFT ); +TOOL_ACTION COMMON_ACTIONS::cursorRight( "pcbnew.Control.cursorRight", + AS_GLOBAL, WXK_RIGHT, "", "" , NULL, AF_NONE, (void*) CURSOR_RIGHT ); + +TOOL_ACTION COMMON_ACTIONS::cursorUpFast( "pcbnew.Control.cursorUpFast", + AS_GLOBAL, MD_CTRL + WXK_UP, "", "", NULL, AF_NONE, (void*) ( CURSOR_UP | CURSOR_FAST_MOVE ) ); +TOOL_ACTION COMMON_ACTIONS::cursorDownFast( "pcbnew.Control.cursorDownFast", + AS_GLOBAL, MD_CTRL + WXK_DOWN, "", "" , NULL, AF_NONE, (void*) ( CURSOR_DOWN | CURSOR_FAST_MOVE ) ); +TOOL_ACTION COMMON_ACTIONS::cursorLeftFast( "pcbnew.Control.cursorLeftFast", + AS_GLOBAL, MD_CTRL + WXK_LEFT, "", "" , NULL, AF_NONE, (void*) ( CURSOR_LEFT | CURSOR_FAST_MOVE ) ); +TOOL_ACTION COMMON_ACTIONS::cursorRightFast( "pcbnew.Control.cursorRightFast", + AS_GLOBAL, MD_CTRL + WXK_RIGHT, "", "" , NULL, AF_NONE, (void*) ( CURSOR_RIGHT | CURSOR_FAST_MOVE ) ); + +TOOL_ACTION COMMON_ACTIONS::cursorClick( "pcbnew.Control.cursorClick", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_LEFT_CLICK ), + "", "", NULL, AF_NONE, (void*) CURSOR_CLICK ); +TOOL_ACTION COMMON_ACTIONS::cursorDblClick( "pcbnew.Control.cursorDblClick", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_LEFT_DCLICK ), + "", "", NULL, AF_NONE, (void*) CURSOR_DBL_CLICK ); + +TOOL_ACTION COMMON_ACTIONS::panUp( "pcbnew.Control.panUp", + AS_GLOBAL, MD_SHIFT + WXK_UP, "", "", NULL, AF_NONE, (void*) CURSOR_UP ); +TOOL_ACTION COMMON_ACTIONS::panDown( "pcbnew.Control.panDown", + AS_GLOBAL, MD_SHIFT + WXK_DOWN, "", "" , NULL, AF_NONE, (void*) CURSOR_DOWN ); +TOOL_ACTION COMMON_ACTIONS::panLeft( "pcbnew.Control.panLeft", + AS_GLOBAL, MD_SHIFT + WXK_LEFT, "", "" , NULL, AF_NONE, (void*) CURSOR_LEFT ); +TOOL_ACTION COMMON_ACTIONS::panRight( "pcbnew.Control.panRight", + AS_GLOBAL, MD_SHIFT + WXK_RIGHT, "", "" , NULL, AF_NONE, (void*) CURSOR_RIGHT ); + +// Miscellaneous +TOOL_ACTION COMMON_ACTIONS::selectionTool( "pcbnew.Control.selectionTool", + AS_GLOBAL, 0, + "", "", NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::zoomTool( "pcbnew.Control.zoomTool", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZOOM_SELECTION ), + _( "Zoom to Selection" ), "", NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::pickerTool( "pcbnew.Picker", AS_GLOBAL, 0, "", "", NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::resetCoords( "pcbnew.Control.resetCoords", + AS_GLOBAL, ' ', + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::switchCursor( "pcbnew.Control.switchCursor", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::switchUnits( "pcbnew.Control.switchUnits", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_UNITS ), + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::deleteItemCursor( "pcbnew.Control.deleteItemCursor", + AS_GLOBAL, 0, + "", "" ); + +TOOL_ACTION COMMON_ACTIONS::showHelp( "pcbnew.Control.showHelp", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_HELP ), + "", "" ); + +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)" ), + _( "Run push & shove router (single tracks)" ), ps_router_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateDiffPair( "pcbnew.InteractiveRouter.DiffPair", + AS_GLOBAL, '6', + _( "Interactive Router (Differential Pairs)" ), + _( "Run push & shove router (differential pairs)" ), ps_diff_pair_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateSettingsDialog( "pcbnew.InteractiveRouter.SettingsDialog", + AS_GLOBAL, 0, + _( "Interactive Router Settings" ), + _( "Open Interactive Router settings" ), NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateDpDimensionsDialog( "pcbnew.InteractiveRouter.DpDimensionsDialog", + AS_GLOBAL, 0, + _( "Differential Pair Dimension settings" ), + _( "Open Differential Pair Dimension settings" ), ps_diff_pair_gap_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneSingleTrace( "pcbnew.LengthTuner.TuneSingleTrack", + AS_GLOBAL, '7', + _( "Tune length of a single track" ), "", ps_tune_length_xpm, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPair( "pcbnew.LengthTuner.TuneDiffPair", + AS_GLOBAL, '8', + _( "Tune length of a differential pair" ), "", NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPairSkew( "pcbnew.LengthTuner.TuneDiffPairSkew", + AS_GLOBAL, '9', + _( "Tune skew of a differential pair" ), "", NULL, AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerInlineDrag( "pcbnew.InteractiveRouter.InlineDrag", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_DRAG_TRACK_KEEP_SLOPE ), + _( "Drag Track/Via" ), _( "Drags tracks and vias without breaking connections" ), + drag_track_segment_xpm ); + +// Point editor +TOOL_ACTION COMMON_ACTIONS::pointEditorAddCorner( "pcbnew.PointEditor.addCorner", + AS_GLOBAL, 0, + _( "Create Corner" ), _( "Create a corner" ), add_corner_xpm ); + +TOOL_ACTION COMMON_ACTIONS::pointEditorRemoveCorner( "pcbnew.PointEditor.removeCorner", + AS_GLOBAL, 0, + _( "Remove Corner" ), _( "Remove corner" ), delete_xpm ); + +// Placement tool +TOOL_ACTION COMMON_ACTIONS::alignTop( "pcbnew.Place.alignTop", + AS_GLOBAL, 0, + _( "Align to Top" ), + _( "Aligns selected items to the top edge" ), up_xpm ); + +TOOL_ACTION COMMON_ACTIONS::alignBottom( "pcbnew.Place.alignBottom", + AS_GLOBAL, 0, + _( "Align to Bottom" ), + _( "Aligns selected items to the bottom edge" ), down_xpm ); + +TOOL_ACTION COMMON_ACTIONS::alignLeft( "pcbnew.Place.alignLeft", + AS_GLOBAL, 0, + _( "Align to Left" ), + _( "Aligns selected items to the left edge" ), left_xpm ); + +TOOL_ACTION COMMON_ACTIONS::alignRight( "pcbnew.Place.alignRight", + AS_GLOBAL, 0, + _( "Align to Right" ), + _( "Aligns selected items to the right edge" ), right_xpm ); + +TOOL_ACTION COMMON_ACTIONS::distributeHorizontally( "pcbnew.Place.distributeHorizontally", + AS_GLOBAL, 0, + _( "Distribute Horizontally" ), + _( "Distributes selected items along the horizontal axis" ), distribute_horizontal_xpm ); + +TOOL_ACTION COMMON_ACTIONS::distributeVertically( "pcbnew.Place.distributeVertically", + AS_GLOBAL, 0, + _( "Distribute Vertically" ), + _( "Distributes selected items along the vertical axis" ), distribute_vertical_xpm ); + + +boost::optional COMMON_ACTIONS::TranslateLegacyId( int aId ) +{ + switch( aId ) + { + case ID_PCB_MODULE_BUTT: + return COMMON_ACTIONS::placeModule.MakeEvent(); + + case ID_TRACK_BUTT: + return COMMON_ACTIONS::routerActivateSingle.MakeEvent(); + + case ID_DIFF_PAIR_BUTT: + return COMMON_ACTIONS::routerActivateDiffPair.MakeEvent(); + + case ID_TUNE_SINGLE_TRACK_LEN_BUTT: + return COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent(); + + case ID_TUNE_DIFF_PAIR_LEN_BUTT: + return COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent(); + + case ID_TUNE_DIFF_PAIR_SKEW_BUTT: + return COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent(); + + case ID_MENU_INTERACTIVE_ROUTER_SETTINGS: + return COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent(); + + case ID_MENU_DIFF_PAIR_DIMENSIONS: + return COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent(); + + case ID_PCB_ZONES_BUTT: + return COMMON_ACTIONS::drawZone.MakeEvent(); + + case ID_PCB_KEEPOUT_AREA_BUTT: + return COMMON_ACTIONS::drawKeepout.MakeEvent(); + + case ID_PCB_ADD_LINE_BUTT: + case ID_MODEDIT_LINE_TOOL: + return COMMON_ACTIONS::drawLine.MakeEvent(); + + case ID_PCB_CIRCLE_BUTT: + case ID_MODEDIT_CIRCLE_TOOL: + return COMMON_ACTIONS::drawCircle.MakeEvent(); + + case ID_PCB_ARC_BUTT: + case ID_MODEDIT_ARC_TOOL: + return COMMON_ACTIONS::drawArc.MakeEvent(); + + case ID_PCB_ADD_TEXT_BUTT: + case ID_MODEDIT_TEXT_TOOL: + return COMMON_ACTIONS::placeText.MakeEvent(); + + case ID_PCB_DIMENSION_BUTT: + return COMMON_ACTIONS::drawDimension.MakeEvent(); + + case ID_PCB_MIRE_BUTT: + return COMMON_ACTIONS::placeTarget.MakeEvent(); + + case ID_MODEDIT_PAD_TOOL: + return COMMON_ACTIONS::placePad.MakeEvent(); + + case ID_GEN_IMPORT_DXF_FILE: + return COMMON_ACTIONS::placeDXF.MakeEvent(); + + case ID_MODEDIT_ANCHOR_TOOL: + return COMMON_ACTIONS::setAnchor.MakeEvent(); + + case ID_PCB_PLACE_GRID_COORD_BUTT: + case ID_MODEDIT_PLACE_GRID_COORD: + return COMMON_ACTIONS::gridSetOrigin.MakeEvent(); + + case ID_ZOOM_IN: // toolbar button "Zoom In" + return COMMON_ACTIONS::zoomInCenter.MakeEvent(); + + case ID_ZOOM_OUT: // toolbar button "Zoom In" + return COMMON_ACTIONS::zoomOutCenter.MakeEvent(); + + case ID_ZOOM_PAGE: // toolbar button "Fit on Screen" + return COMMON_ACTIONS::zoomFitScreen.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_TRACKS_SKETCH: + return COMMON_ACTIONS::trackDisplayMode.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_PADS_SKETCH: + return COMMON_ACTIONS::padDisplayMode.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_VIAS_SKETCH: + return COMMON_ACTIONS::viaDisplayMode.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_ZONES: + return COMMON_ACTIONS::zoneDisplayEnable.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_ZONES_DISABLE: + return COMMON_ACTIONS::zoneDisplayDisable.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_ZONES_OUTLINES_ONLY: + return COMMON_ACTIONS::zoneDisplayOutlines.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_MODULE_EDGE_SKETCH: + return COMMON_ACTIONS::moduleEdgeOutlines.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_MODULE_TEXT_SKETCH: + return COMMON_ACTIONS::moduleTextOutlines.MakeEvent(); + + case ID_TB_OPTIONS_SHOW_HIGH_CONTRAST_MODE: + return COMMON_ACTIONS::highContrastMode.MakeEvent(); + + case ID_FIND_ITEMS: + return COMMON_ACTIONS::find.MakeEvent(); + + case ID_POPUP_PCB_GET_AND_MOVE_MODULE_REQUEST: + return COMMON_ACTIONS::findMove.MakeEvent(); + + case ID_NO_TOOL_SELECTED: + return COMMON_ACTIONS::selectionTool.MakeEvent(); + + case ID_ZOOM_SELECTION: + return COMMON_ACTIONS::zoomTool.MakeEvent(); + + case ID_PCB_DELETE_ITEM_BUTT: + case ID_MODEDIT_DELETE_TOOL: + return COMMON_ACTIONS::deleteItemCursor.MakeEvent(); + + case ID_PCB_PLACE_OFFSET_COORD_BUTT: + return COMMON_ACTIONS::drillOrigin.MakeEvent(); + + case ID_PCB_HIGHLIGHT_BUTT: + return COMMON_ACTIONS::highlightNetCursor.MakeEvent(); + + case ID_APPEND_FILE: + return COMMON_ACTIONS::appendBoard.MakeEvent(); + + case ID_PCB_SHOW_1_RATSNEST_BUTT: + return COMMON_ACTIONS::showLocalRatsnest.MakeEvent(); + } + + return boost::optional(); +} diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index 99d5afd118..4a00d4a5b9 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -301,6 +301,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) { static_cast( item )->Move( movement + m_offset ); } + + updateRatsnest( true ); } else if( !m_dragging ) // Prepare to start dragging { @@ -410,6 +412,7 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) // Update dragging offset (distance between cursor and the first dragged item) m_offset = static_cast( selection.Front() )->GetPosition() - modPoint; getView()->Update( &selection ); + updateRatsnest( true ); } } @@ -422,6 +425,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent ) } } while( ( evt = Wait() ) ); //Should be assignment not equality test + getModel()->GetConnectivity()->ClearDynamicRatsnest(); + controls->ForceCursorPosition( false ); controls->ShowCursor( false ); controls->SetSnapping( false ); @@ -516,6 +521,8 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent ) if( !m_dragging ) m_commit->Push( _( "Rotate" ) ); + else + updateRatsnest( true ); if( selection.IsHover() ) m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); @@ -622,6 +629,8 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent ) if( !m_dragging ) m_commit->Push( _( "Mirror" ) ); + else + updateRatsnest( true ); if( selection.IsHover() ) m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); @@ -655,6 +664,8 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent ) if( !m_dragging ) m_commit->Push( _( "Flip" ) ); + else + updateRatsnest( true ); if( selection.IsHover() ) m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); @@ -1138,6 +1149,17 @@ void EDIT_TOOL::SetTransitions() Go( &EDIT_TOOL::MeasureTool, PCB_ACTIONS::measureTool.MakeEvent() ); } +void EDIT_TOOL::updateRatsnest( bool aRedraw ) +{ + auto& selection = m_selectionTool->GetSelection(); + auto connectivity = getModel()->GetConnectivity(); + std::vector items; + + for ( auto item : selection ) + items.push_back ( static_cast( item ) ); + + connectivity->ComputeDynamicRatsnest( items ); +} wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection ) { diff --git a/pcbnew/tools/edit_tool.h b/pcbnew/tools/edit_tool.h index 6ca3d7be0a..821dd0788b 100644 --- a/pcbnew/tools/edit_tool.h +++ b/pcbnew/tools/edit_tool.h @@ -32,6 +32,7 @@ class BOARD_COMMIT; class BOARD_ITEM; class SELECTION_TOOL; +class CONNECTIVITY_DATA; /** * Class EDIT_TOOL @@ -144,6 +145,13 @@ private: ///> of edit reference point). VECTOR2I m_cursor; + std::unique_ptr m_dynamicConnectivity; + + ///> Updates ratsnest for selected items. + ///> @param aRedraw says if selected items should be drawn using the simple mode (e.g. one line + ///> per item). + void updateRatsnest( bool aRedraw ); + ///> Returns the right modification point (e.g. for rotation), depending on the number of ///> selected items. wxPoint getModificationPoint( const SELECTION& aSelection ); diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index c86c97cee7..fadc5d9c38 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -356,6 +356,7 @@ public: static TOOL_ACTION crossProbeSchToPcb; static TOOL_ACTION appendBoard; static TOOL_ACTION showHelp; + static TOOL_ACTION showLocalRatsnest; static TOOL_ACTION toBeDone; /// Find an item diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index 83305988c3..3baeb7ee92 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -41,8 +41,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -481,7 +480,12 @@ int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent ) } } - view->Remove( &preview ); + controls->ShowCursor( false ); + controls->SetSnapping( false ); + controls->SetAutoPan( false ); + controls->CaptureCursor( false ); + + view->Remove( &preview ); m_frame->SetNoToolSelected(); return 0; @@ -637,7 +641,7 @@ int PCB_EDITOR_CONTROL::ZoneFill( const TOOL_EVENT& aEvent ) { auto selTool = m_toolMgr->GetTool(); const auto& selection = selTool->GetSelection(); - RN_DATA* ratsnest = getModel()->GetRatsnest(); + auto connectivity = getModel()->GetConnectivity(); BOARD_COMMIT commit( this ); @@ -651,13 +655,11 @@ int PCB_EDITOR_CONTROL::ZoneFill( const TOOL_EVENT& aEvent ) m_frame->Fill_Zone( zone ); zone->SetIsFilled( true ); - ratsnest->Update( zone ); - getView()->Update( zone ); } commit.Push( _( "Fill Zone" ) ); - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); return 0; } @@ -666,7 +668,7 @@ int PCB_EDITOR_CONTROL::ZoneFill( const TOOL_EVENT& aEvent ) int PCB_EDITOR_CONTROL::ZoneFillAll( const TOOL_EVENT& aEvent ) { BOARD* board = getModel(); - RN_DATA* ratsnest = board->GetRatsnest(); + auto connectivity = getModel()->GetConnectivity(); BOARD_COMMIT commit( this ); @@ -678,13 +680,11 @@ int PCB_EDITOR_CONTROL::ZoneFillAll( const TOOL_EVENT& aEvent ) m_frame->Fill_Zone( zone ); zone->SetIsFilled( true ); - ratsnest->Update( zone ); - getView()->Update( zone ); } commit.Push( _( "Fill All Zones" ) ); - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); return 0; } @@ -694,7 +694,7 @@ int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent ) { auto selTool = m_toolMgr->GetTool(); const auto& selection = selTool->GetSelection(); - RN_DATA* ratsnest = getModel()->GetRatsnest(); + auto connectivity = getModel()->GetConnectivity(); BOARD_COMMIT commit( this ); @@ -708,13 +708,11 @@ int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent ) zone->SetIsFilled( false ); zone->ClearFilledPolysList(); - ratsnest->Update( zone ); - getView()->Update( zone ); } commit.Push( _( "Unfill Zone" ) ); - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); return 0; } @@ -723,7 +721,7 @@ int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent ) int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) { BOARD* board = getModel(); - RN_DATA* ratsnest = board->GetRatsnest(); + auto connectivity = getModel()->GetConnectivity(); BOARD_COMMIT commit( this ); @@ -735,13 +733,11 @@ int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) zone->SetIsFilled( false ); zone->ClearFilledPolysList(); - ratsnest->Update( zone ); - getView()->Update( zone ); } commit.Push( _( "Unfill All Zones" ) ); - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); return 0; } @@ -808,22 +804,29 @@ int PCB_EDITOR_CONTROL::ZoneMerge( const TOOL_EVENT& aEvent ) netcode = curr_area->GetNetCode(); - if( firstZone->GetNetCode() != netcode ) - continue; + if( firstZone ) + { + if( firstZone->GetNetCode() != netcode ) + continue; - if( curr_area->GetPriority() != firstZone->GetPriority() ) - continue; + if( curr_area->GetPriority() != firstZone->GetPriority() ) + continue; - if( curr_area->GetIsKeepout() != firstZone->GetIsKeepout() ) - continue; + if( curr_area->GetIsKeepout() != firstZone->GetIsKeepout() ) + continue; - if( curr_area->GetLayer() != firstZone->GetLayer() ) - continue; + if( curr_area->GetLayer() != firstZone->GetLayer() ) + continue; - if( !board->TestAreaIntersection( curr_area, firstZone ) ) - continue; + if( !board->TestAreaIntersection( curr_area, firstZone ) ) + continue; - toMerge.push_back( curr_area ); + toMerge.push_back( curr_area ); + } + else + { + toMerge.push_back( curr_area ); + } } m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); @@ -1055,6 +1058,31 @@ 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 ) +{ + aFrame->SetAuxOrigin( wxPoint( aPosition.x, aPosition.y ) ); + aItem->SetPosition( aPosition ); + aView->MarkDirty(); + + return true; +} + +int PCB_EDITOR_CONTROL::ShowLocalRatsnest( const TOOL_EVENT& aEvent ) +{ + Activate(); + + auto picker = m_toolMgr->GetTool(); + 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->SetSnapping( false ); + picker->Activate(); + Wait(); + + return 0; +} int PCB_EDITOR_CONTROL::UpdateSelectionRatsnest( const TOOL_EVENT& aEvent ) { diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp index 9a00d90bfb..51f92df33e 100644 --- a/pcbnew/tools/pcbnew_control.cpp +++ b/pcbnew/tools/pcbnew_control.cpp @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp index 3e55a20893..fa11fa1950 100644 --- a/pcbnew/tools/point_editor.cpp +++ b/pcbnew/tools/point_editor.cpp @@ -43,7 +43,7 @@ using namespace std::placeholders; #include #include #include -#include +#include // Point editor TOOL_ACTION PCB_ACTIONS::pointEditorAddCorner( "pcbnew.PointEditor.addCorner", @@ -551,7 +551,7 @@ void POINT_EDITOR::finishItem() const if( zone->IsFilled() ) { getEditFrame()->Fill_Zone( zone ); - zone->GetBoard()->GetRatsnest()->Recalculate( zone->GetNetCode() ); +// zone->GetBoard()->GetRatsnest()->Recalculate( zone->GetNetCode() ); } } } diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 1b97c11ffa..ed03a30011 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -51,7 +51,7 @@ using namespace std::placeholders; #include #include -#include +#include #include "selection_tool.h" #include "pcb_bright_box.h" @@ -830,24 +830,24 @@ void SELECTION_TOOL::selectAllItemsConnectedToTrack( TRACK& aSourceTrack ) void SELECTION_TOOL::selectAllItemsConnectedToItem( BOARD_CONNECTED_ITEM& aSourceItem ) { - RN_DATA* ratsnest = getModel()->GetRatsnest(); - std::list itemsList; - ratsnest->GetConnectedItems( &aSourceItem, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) ); + constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, EOT }; + auto connectivity = board()->GetConnectivity(); - for( BOARD_CONNECTED_ITEM* i : itemsList ) - select( i ); + std::list items; + items = connectivity->GetConnectedItems( &aSourceItem, types ); + + for( auto item : connectivity->GetConnectedItems( &aSourceItem, types ) ) + select( item ); } void SELECTION_TOOL::selectAllItemsOnNet( int aNetCode ) { - RN_DATA* ratsnest = getModel()->GetRatsnest(); - std::list itemsList; + constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, EOT }; + auto connectivity = board()->GetConnectivity(); - ratsnest->GetNetItems( aNetCode, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) ); - - for( BOARD_CONNECTED_ITEM* i : itemsList ) - select( i ); + for( auto item : connectivity->GetNetItems( aNetCode, types ) ) + select( item ); } @@ -913,12 +913,12 @@ void SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetpath ) // now we need to find all modules that are connected to each of these nets // then we need to determine if these modules are in the list of modules // belonging to this sheet ( modList ) - RN_DATA* ratsnest = getModel()->GetRatsnest(); + //RN_DATA* ratsnest = getModel()->GetRatsnest(); std::list removeCodeList; for( int netCode : netcodeList ) { std::list netPads; - ratsnest->GetNetItems( netCode, netPads, (RN_ITEM_TYPE)( RN_PADS ) ); + // ratsnest->GetNetItems( netCode, netPads, (RN_ITEM_TYPE)( RN_PADS ) ); for( BOARD_CONNECTED_ITEM* mitem : netPads ) { bool found = ( std::find( modList.begin(), modList.end(), mitem->GetParent() ) != modList.end() ); @@ -945,7 +945,7 @@ void SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetpath ) std::list localConnectionList; for( int netCode : netcodeList ) { - ratsnest->GetNetItems( netCode, localConnectionList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) ); + //ratsnest->GetNetItems( netCode, localConnectionList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) ); } for( BOARD_ITEM* i : modList ) diff --git a/pcbnew/undo_redo.cpp b/pcbnew/undo_redo.cpp index ede5340917..16b6923b0b 100644 --- a/pcbnew/undo_redo.cpp +++ b/pcbnew/undo_redo.cpp @@ -45,7 +45,7 @@ using namespace std::placeholders; #include #include -#include +#include #include #include @@ -383,7 +383,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool bool deep_reBuild_ratsnest = false; // true later if pointers must be rebuilt KIGFX::VIEW* view = GetGalCanvas()->GetView(); - RN_DATA* ratsnest = GetBoard()->GetRatsnest(); + auto connectivity = GetBoard()->GetConnectivity(); // Undo in the reverse order of list creation: (this can allow stacked changes // like the same item can be changes and deleted in the same complex command @@ -468,7 +468,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool } view->Remove( item ); - ratsnest->Remove( item ); + connectivity->Remove( item ); item->SwapData( image ); @@ -482,7 +482,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool } view->Add( item ); - ratsnest->Add( item ); + connectivity->Add( item ); item->ClearFlags(); } @@ -518,27 +518,27 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool case UR_MOVED: item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint ); view->Update( item, KIGFX::GEOMETRY ); - ratsnest->Update( item ); + connectivity->Update( item ); break; case UR_ROTATED: item->Rotate( aList->m_TransformPoint, aRedoCommand ? m_rotationAngle : -m_rotationAngle ); view->Update( item, KIGFX::GEOMETRY ); - ratsnest->Update( item ); + connectivity->Update( item ); break; case UR_ROTATED_CLOCKWISE: item->Rotate( aList->m_TransformPoint, aRedoCommand ? -m_rotationAngle : m_rotationAngle ); view->Update( item, KIGFX::GEOMETRY ); - ratsnest->Update( item ); + connectivity->Update( item ); break; case UR_FLIPPED: item->Flip( aList->m_TransformPoint ); view->Update( item, KIGFX::LAYERS ); - ratsnest->Update( item ); + connectivity->Update( item ); break; default: @@ -555,7 +555,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool if( not_found ) wxMessageBox( wxT( "Incomplete undo/redo operation: some items not found" ) ); - // Rebuild pointers and ratsnest that can be changed. + // Rebuild pointers and connectivity that can be changed. if( reBuild_ratsnest ) { // Compile ratsnest propagates nets from pads to tracks @@ -565,10 +565,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool if( IsGalCanvasActive() ) { - if( deep_reBuild_ratsnest ) - ratsnest->ProcessBoard(); - else - ratsnest->Recalculate(); + connectivity->RecalculateRatsnest(); } } } diff --git a/pcbnew/zones_by_polygon_fill_functions.cpp b/pcbnew/zones_by_polygon_fill_functions.cpp index ebe2964ff6..7590ffadda 100644 --- a/pcbnew/zones_by_polygon_fill_functions.cpp +++ b/pcbnew/zones_by_polygon_fill_functions.cpp @@ -43,7 +43,8 @@ #include #include -#include +#include +#include #define FORMAT_STRING _( "Filling zone %d out of %d (net %s)..." ) @@ -116,11 +117,15 @@ int PCB_EDIT_FRAME::Fill_Zone( ZONE_CONTAINER* aZone ) wxBusyCursor dummy; // Shows an hourglass cursor (removed by its destructor) + BOARD_COMMIT commit ( this ); + commit.Modify( aZone ); aZone->BuildFilledSolidAreasPolygons( GetBoard() ); - GetGalCanvas()->GetView()->Update( aZone, KIGFX::ALL ); - GetBoard()->GetRatsnest()->Update( aZone ); + commit.Push ( _("Fill Zone"), false ); - OnModify(); + //GetGalCanvas()->GetView()->Update( aZone, KIGFX::ALL ); + //GetBoard()->GetConnectivity()->Update( aZone ); + + //OnModify(); return 0; } diff --git a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp index cafe6f5606..364a2a3b6b 100644 --- a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp +++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp @@ -74,7 +74,7 @@ * To emit zone data to a file when filling zones for the debugging purposes, * set this 'true' and build. */ -static const bool g_DumpZonesWhenFilling = false; +static const bool g_DumpZonesWhenFilling = true; extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, BOARD* aPcb, ZONE_CONTAINER* aZone, @@ -437,6 +437,8 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) SHAPE_POLY_SET solidAreas = *m_smoothedPoly; + printf("VC %d\n", solidAreas.VertexCount()); + solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); solidAreas.Simplify( POLY_CALC_MODE ); @@ -473,10 +475,6 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) m_FilledPolysList = areas_fractured; - // Remove insulated islands: - if( GetNetCode() > 0 ) - TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); - SHAPE_POLY_SET thermalHoles; // Test thermal stubs connections and add polygons to remove unconnected stubs. @@ -506,10 +504,13 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) m_FilledPolysList = th_fractured; - if( GetNetCode() > 0 ) - TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); } + m_RawPolysList = m_FilledPolysList; + + if( GetNetCode() > 0 ) + TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); + if(g_DumpZonesWhenFilling) dumper->EndGroup(); } diff --git a/pcbnew/zones_polygons_insulated_copper_islands.cpp b/pcbnew/zones_polygons_insulated_copper_islands.cpp index bfb861e12e..5fc36c7aa5 100644 --- a/pcbnew/zones_polygons_insulated_copper_islands.cpp +++ b/pcbnew/zones_polygons_insulated_copper_islands.cpp @@ -5,7 +5,7 @@ * 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. + * Copyright (C) 1992-2016 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 @@ -25,78 +25,24 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include -#include - #include -#include -#include #include - -#include -#include -#include - +#include void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb ) { - if( m_FilledPolysList.IsEmpty() ) - return; + std::vector islands; - // Build a list of points connected to the net: - // list of coordinates of pads and vias on this layer and on this net. - std::vector listPointsCandidates; + auto connectivity = aPcb->GetConnectivity(); - for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) + connectivity->FindIsolatedCopperIslands( this, islands ); + + std::sort( islands.begin(), islands.end(), std::greater() ); + + for( auto idx : islands ) { - for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) - { - if( !pad->IsOnLayer( GetLayer() ) ) - continue; - - if( pad->GetNetCode() != GetNetCode() ) - continue; - - listPointsCandidates.push_back( pad->GetPosition() ); - } + m_FilledPolysList.DeletePolygon( idx ); } - for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) - { - if( !track->IsOnLayer( GetLayer() ) ) - continue; - - if( track->GetNetCode() != GetNetCode() ) - continue; - - listPointsCandidates.push_back( track->GetStart() ); - - if( track->Type() != PCB_VIA_T ) - listPointsCandidates.push_back( track->GetEnd() ); - } - - // test if a point is inside - - for( int outline = 0; outline < m_FilledPolysList.OutlineCount(); outline++ ) - { - bool connected = false; - - for( unsigned ic = 0; ic < listPointsCandidates.size(); ic++ ) - { - // test if this area is connected to a board item: - wxPoint pos = listPointsCandidates[ic]; - - if( m_FilledPolysList.Contains( VECTOR2I( pos.x, pos.y ), outline ) ) - { - connected = true; - break; - } - } - - if( !connected ) // this polygon is connected: analyse next polygon - { - m_FilledPolysList.DeletePolygon( outline ); - outline--; - } - } + connectivity->Update( this ); }