kicad/pcbnew/ratsnest/ratsnest_data.cpp

491 lines
12 KiB
C++
Raw Normal View History

2013-11-25 15:50:03 +00:00
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
2017-03-22 13:43:10 +00:00
* Copyright (C) 2013-2017 CERN
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
2013-11-25 15:50:03 +00:00
* @author Maciej Suminski <maciej.suminski@cern.ch>
2017-03-22 13:43:10 +00:00
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
2013-11-25 15:50:03 +00:00
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file ratsnest_data.cpp
* @brief Class that computes missing connections on a PCB.
*/
2017-03-22 13:43:10 +00:00
#ifdef PROFILE
#include <profile.h>
#endif
2013-11-25 15:50:03 +00:00
#include <ratsnest/ratsnest_data.h>
#include <functional>
using namespace std::placeholders;
2013-11-25 15:50:03 +00:00
#include <algorithm>
#include <cassert>
2013-11-25 15:50:03 +00:00
#include <limits>
#include <queue>
2013-11-25 15:50:03 +00:00
class disjoint_set
{
2017-06-23 11:56:28 +00:00
public:
disjoint_set( size_t size )
{
m_data.resize( size );
m_depth.resize( size, 0 );
for( size_t i = 0; i < size; i++ )
m_data[i] = i;
}
int find( int aVal )
{
int root = aVal;
2013-11-25 15:50:03 +00:00
while( m_data[root] != root )
root = m_data[root];
2013-11-25 15:50:03 +00:00
// Compress the path
while( m_data[aVal] != aVal )
{
auto& tmp = m_data[aVal];
aVal = tmp;
tmp = root;
}
return root;
}
2013-11-25 15:50:03 +00:00
bool unite( int aVal1, int aVal2 )
2013-11-25 15:50:03 +00:00
{
aVal1 = find( aVal1 );
aVal2 = find( aVal2 );
2013-11-25 15:50:03 +00:00
if( aVal1 != aVal2 )
2013-11-25 15:50:03 +00:00
{
if( m_depth[aVal1] < m_depth[aVal2] )
2014-03-05 13:57:14 +00:00
{
m_data[aVal1] = aVal2;
2013-11-25 15:50:03 +00:00
}
else
{
m_data[aVal2] = aVal1;
if( m_depth[aVal1] == m_depth[aVal2] )
m_depth[aVal1]++;
}
return true;
2013-11-25 15:50:03 +00:00
}
return false;
2013-11-25 15:50:03 +00:00
}
private:
std::vector<int> m_data;
std::vector<int> m_depth;
};
void RN_NET::kruskalMST( std::priority_queue<CN_EDGE> &aEdges )
{
disjoint_set dset( m_nodes.size() );
m_rnEdges.clear();
2020-06-23 02:18:46 +00:00
int i = 0;
for( auto& node : m_nodes )
node->SetTag( i++ );
while( !aEdges.empty() )
{
auto& tmp = aEdges.top();
int u = tmp.GetSourceNode()->GetTag();
int v = tmp.GetTargetNode()->GetTag();
if( dset.unite( u, v ) )
{
if( tmp.GetWeight() > 0 )
m_rnEdges.push_back( tmp );
}
2013-11-25 15:50:03 +00:00
aEdges.pop();
}
2013-11-25 15:50:03 +00:00
}
2017-06-23 11:56:28 +00:00
2017-03-22 13:43:10 +00:00
class RN_NET::TRIANGULATOR_STATE
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
private:
std::vector<CN_ANCHOR_PTR> m_allNodes;
std::list<hed::EDGE_PTR> hedTriangulation( std::vector<hed::NODE_PTR>& aNodes )
{
hed::TRIANGULATION triangulator;
triangulator.CreateDelaunay( aNodes.begin(), aNodes.end() );
std::list<hed::EDGE_PTR> edges;
triangulator.GetEdges( edges );
return edges;
}
// Checks if all nodes in aNodes lie on a single line. Requires the nodes to
// have unique coordinates!
bool areNodesColinear( const std::vector<hed::NODE_PTR>& aNodes ) const
{
if ( aNodes.size() <= 2 )
return true;
const auto p0 = aNodes[0]->Pos();
const auto v0 = aNodes[1]->Pos() - p0;
for( unsigned i = 2; i < aNodes.size(); i++ )
{
const auto v1 = aNodes[i]->Pos() - p0;
if( v0.Cross( v1 ) != 0 )
{
return false;
}
}
return true;
}
2017-03-22 13:43:10 +00:00
public:
void Clear()
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
m_allNodes.clear();
}
2017-03-22 13:43:10 +00:00
void AddNode( CN_ANCHOR_PTR aNode )
{
m_allNodes.push_back( aNode );
}
const std::priority_queue<CN_EDGE> Triangulate()
2017-03-22 13:43:10 +00:00
{
std::priority_queue<CN_EDGE> mstEdges;
2017-03-22 13:43:10 +00:00
std::list<hed::EDGE_PTR> triangEdges;
std::vector<hed::NODE_PTR> triNodes;
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
using ANCHOR_LIST = std::vector<CN_ANCHOR_PTR>;
std::vector<ANCHOR_LIST> anchorChains;
2017-03-22 13:43:10 +00:00
triNodes.reserve( m_allNodes.size() );
anchorChains.resize( m_allNodes.size() );
2017-03-22 13:43:10 +00:00
std::sort( m_allNodes.begin(), m_allNodes.end(),
[] ( const CN_ANCHOR_PTR& aNode1, const CN_ANCHOR_PTR& aNode2 )
{
if( aNode1->Pos().y < aNode2->Pos().y )
return true;
else if( aNode1->Pos().y == aNode2->Pos().y )
{
return aNode1->Pos().x < aNode2->Pos().x;
2013-11-25 15:50:03 +00:00
}
2017-03-22 13:43:10 +00:00
return false;
}
);
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
CN_ANCHOR_PTR prev, last;
int id = 0;
2013-11-25 15:50:03 +00:00
for( const auto& n : m_allNodes )
2017-03-22 13:43:10 +00:00
{
if( !prev || prev->Pos() != n->Pos() )
{
auto tn = std::make_shared<hed::NODE> ( n->Pos().x, n->Pos().y );
2017-03-22 13:43:10 +00:00
tn->SetId( id );
triNodes.push_back( tn );
}
2017-03-22 13:43:10 +00:00
id++;
prev = n;
}
2017-03-22 13:43:10 +00:00
int prevId = 0;
for( const auto& n : triNodes )
2017-03-22 13:43:10 +00:00
{
for( int i = prevId; i < n->Id(); i++ )
anchorChains[prevId].push_back( m_allNodes[ i ] );
2017-03-22 13:43:10 +00:00
prevId = n->Id();
}
2017-03-22 13:43:10 +00:00
for( int i = prevId; i < id; i++ )
anchorChains[prevId].push_back( m_allNodes[ i ] );
if( triNodes.size() == 1 )
2017-03-22 13:43:10 +00:00
{
return mstEdges;
}
else if( areNodesColinear( triNodes ) )
{
// special case: all nodes are on the same line - there's no
// triangulation for such set. In this case, we sort along any coordinate
// and chain the nodes together.
for(int i = 0; i < (int)triNodes.size() - 1; i++ )
{
auto src = m_allNodes[ triNodes[i]->Id() ];
auto dst = m_allNodes[ triNodes[i + 1]->Id() ];
mstEdges.emplace( src, dst, src->Dist( *dst ) );
}
2017-03-22 13:43:10 +00:00
}
else
{
hed::TRIANGULATION triangulator;
triangulator.CreateDelaunay( triNodes.begin(), triNodes.end() );
triangulator.GetEdges( triangEdges );
for( const auto& e : triangEdges )
{
auto src = m_allNodes[ e->GetSourceNode()->Id() ];
auto dst = m_allNodes[ e->GetTargetNode()->Id() ];
mstEdges.emplace( src, dst, src->Dist( *dst ) );
}
}
2013-11-25 15:50:03 +00:00
2017-06-23 17:22:44 +00:00
for( unsigned int i = 0; i < anchorChains.size(); i++ )
2017-03-22 13:43:10 +00:00
{
auto& chain = anchorChains[i];
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
if( chain.size() < 2 )
continue;
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
std::sort( chain.begin(), chain.end(),
[] ( const CN_ANCHOR_PTR& a, const CN_ANCHOR_PTR& b ) {
return a->GetCluster().get() < b->GetCluster().get();
} );
2013-11-25 15:50:03 +00:00
2017-06-23 17:22:44 +00:00
for( unsigned int j = 1; j < chain.size(); j++ )
2017-03-22 13:43:10 +00:00
{
const auto& prevNode = chain[j - 1];
const auto& curNode = chain[j];
int weight = prevNode->GetCluster() != curNode->GetCluster() ? 1 : 0;
mstEdges.emplace( prevNode, curNode, weight );
2017-03-22 13:43:10 +00:00
}
}
2017-03-22 13:43:10 +00:00
return mstEdges;
}
2017-03-22 13:43:10 +00:00
};
2013-11-25 15:50:03 +00:00
RN_NET::RN_NET() : m_dirty( true )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
m_triangulator.reset( new TRIANGULATOR_STATE );
2013-11-25 15:50:03 +00:00
}
2017-06-23 11:56:28 +00:00
2013-11-25 15:50:03 +00:00
void RN_NET::compute()
{
// Special cases do not need complicated algorithms (actually, it does not work well with
// the Delaunay triangulator)
2017-03-22 13:43:10 +00:00
if( m_nodes.size() <= 2 )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
m_rnEdges.clear();
2013-11-25 15:50:03 +00:00
// Check if the only possible connection exists
2017-03-22 13:43:10 +00:00
if( m_boardEdges.size() == 0 && m_nodes.size() == 2 )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
auto last = ++m_nodes.begin();
2013-11-25 15:50:03 +00:00
// There can be only one possible connection, but it is missing
2020-06-23 02:18:46 +00:00
CN_EDGE edge ( *m_nodes.begin(), *last );
2017-03-22 13:43:10 +00:00
edge.GetSourceNode()->SetTag( 0 );
edge.GetTargetNode()->SetTag( 1 );
m_rnEdges.push_back( edge );
}
else
{
2017-03-22 13:43:10 +00:00
// Set tags to m_nodes as connected
for( const auto& node : m_nodes )
node->SetTag( 0 );
2013-11-25 15:50:03 +00:00
}
return;
}
2017-03-22 13:43:10 +00:00
m_triangulator->Clear();
2013-11-25 15:50:03 +00:00
for( const auto& n : m_nodes )
2017-03-22 13:43:10 +00:00
{
m_triangulator->AddNode( n );
}
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
#ifdef PROFILE
PROF_COUNTER cnt("triangulate");
#endif
auto triangEdges = m_triangulator->Triangulate();
#ifdef PROFILE
cnt.Show();
#endif
2017-03-22 13:43:10 +00:00
for( const auto& e : m_boardEdges )
triangEdges.push( e );
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
// Get the minimal spanning tree
#ifdef PROFILE
PROF_COUNTER cnt2("mst");
#endif
kruskalMST( triangEdges );
2017-03-22 13:43:10 +00:00
#ifdef PROFILE
cnt2.Show();
#endif
2013-11-25 15:50:03 +00:00
}
void RN_NET::Update()
{
compute();
m_dirty = false;
}
2017-03-22 13:43:10 +00:00
void RN_NET::Clear()
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
m_rnEdges.clear();
m_boardEdges.clear();
m_nodes.clear();
2013-11-25 15:50:03 +00:00
m_dirty = true;
}
2017-03-22 13:43:10 +00:00
void RN_NET::AddCluster( CN_CLUSTER_PTR aCluster )
{
2017-03-22 13:43:10 +00:00
CN_ANCHOR_PTR firstAnchor;
2017-03-22 13:43:10 +00:00
for( auto item : *aCluster )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
bool isZone = dynamic_cast<CN_ZONE*>(item) != nullptr;
auto& anchors = item->Anchors();
2017-06-23 17:22:44 +00:00
unsigned int nAnchors = isZone ? 1 : anchors.size();
2013-11-25 15:50:03 +00:00
2017-06-23 11:56:28 +00:00
if( nAnchors > anchors.size() )
2017-03-22 13:43:10 +00:00
nAnchors = anchors.size();
2013-11-25 15:50:03 +00:00
2017-06-23 17:22:44 +00:00
for( unsigned int i = 0; i < nAnchors; i++ )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
anchors[i]->SetCluster( aCluster );
2020-06-23 02:18:46 +00:00
m_nodes.insert( anchors[i] );
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
if( firstAnchor )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
if( firstAnchor != anchors[i] )
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
m_boardEdges.emplace_back( firstAnchor, anchors[i], 0 );
2013-11-25 15:50:03 +00:00
}
}
2017-03-22 13:43:10 +00:00
else
{
2017-03-22 13:43:10 +00:00
firstAnchor = anchors[i];
}
}
}
}
2017-06-23 11:56:28 +00:00
2017-03-22 13:43:10 +00:00
bool RN_NET::NearestBicoloredPair( const RN_NET& aOtherNet, CN_ANCHOR_PTR& aNode1,
CN_ANCHOR_PTR& aNode2 ) const
2013-11-25 15:50:03 +00:00
{
2017-03-22 13:43:10 +00:00
bool rv = false;
2013-11-25 15:50:03 +00:00
2017-03-22 13:43:10 +00:00
VECTOR2I::extended_type distMax = VECTOR2I::ECOORD_MAX;
2013-11-25 15:50:03 +00:00
auto verify = [&]( auto& aTestNode1, auto& aTestNode2 )
{
auto squaredDist = ( aTestNode1->Pos() - aTestNode2->Pos() ).SquaredEuclideanNorm();
if( squaredDist < distMax )
{
rv = true;
distMax = squaredDist;
aNode1 = aTestNode1;
aNode2 = aTestNode2;
}
};
for( const auto& nodeA : aOtherNet.m_nodes )
{
if( nodeA->GetNoLine() )
continue;
auto fwd_it = m_nodes.lower_bound( nodeA );
auto rev_it = std::make_reverse_iterator( fwd_it );
for( ; fwd_it != m_nodes.end(); ++fwd_it )
{
auto nodeB = *fwd_it;
if( nodeB->GetNoLine() )
continue;
VECTOR2I::extended_type distX = nodeA->Pos().x - nodeB->Pos().x;
if( distX * distX > distMax )
break;
verify( nodeA, nodeB );
}
if( rev_it != m_nodes.rend() )
++rev_it;
for( ; rev_it != m_nodes.rend(); ++rev_it )
2013-11-25 15:50:03 +00:00
{
auto nodeB = *rev_it;
if( nodeB->GetNoLine() )
continue;
2015-06-04 12:54:07 +00:00
VECTOR2I::extended_type distX = nodeA->Pos().x - nodeB->Pos().x;
if( distX * distX > distMax )
break;
verify( nodeA, nodeB );
}
2013-11-25 15:50:03 +00:00
}
2017-03-22 13:43:10 +00:00
return rv;
2013-11-25 15:50:03 +00:00
}
void RN_NET::SetVisible( bool aEnabled )
{
2017-06-23 11:56:28 +00:00
for( auto& edge : m_rnEdges )
edge.SetVisible( aEnabled );
}