Fixed ratsnest for nodes surrounded with zones

This commit is contained in:
Maciej Suminski 2016-09-13 17:13:16 +02:00
parent b00ca19f41
commit 516510774c
3 changed files with 98 additions and 61 deletions

View File

@ -101,7 +101,7 @@ protected:
#endif
/// Node coordinates
int m_x, m_y;
const int m_x, m_y;
/// Tag for quick connection resolution
int m_tag;

View File

@ -127,6 +127,7 @@ static std::vector<RN_EDGE_MST_PTR>* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges,
// Set tags for marking cycles
boost::unordered_map<RN_NODE_PTR, int> tags;
unsigned int tag = 0;
for( RN_NODE_PTR& node : aNodes )
{
node->SetTag( tag );
@ -158,40 +159,41 @@ static std::vector<RN_EDGE_MST_PTR>* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges,
ratsnestLines = true;
// Update tags
std::list<int>::iterator it, itEnd;
if( ratsnestLines )
{
for( it = cycles[trgTag].begin(), itEnd = cycles[trgTag].end(); it != itEnd; ++it )
for( auto it = cycles[trgTag].begin(); it != cycles[trgTag].end(); ++it )
{
tags[aNodes[*it]] = srcTag;
}
else
{
for( it = cycles[trgTag].begin(), itEnd = cycles[trgTag].end(); it != itEnd; ++it ) {
tags[aNodes[*it]] = srcTag;
aNodes[*it]->SetTag( srcTag );
}
}
// Move nodes that were marked with old tag to the list marked with the new tag
cycles[srcTag].splice( cycles[srcTag].end(), cycles[trgTag] );
if( ratsnestLines )
{
// 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<RN_EDGE_MST>( dt->GetSourceNode(),
dt->GetTargetNode(),
dt->GetWeight() );
assert( newEdge->GetSourceNode()->GetTag() != newEdge->GetTargetNode()->GetTag() );
assert( newEdge->GetWeight() > 0 );
mst->push_back( newEdge );
++mstSize;
}
else
{
//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;
aNodes[*it]->SetTag( srcTag );
}
// Processing a connection, decrease the expected size of the ratsnest MST
--mstExpectedSize;
}
// Move nodes that were marked with old tag to the list marked with the new tag
cycles[srcTag].splice( cycles[srcTag].end(), cycles[trgTag] );
}
// Remove the edge that was just processed
@ -209,42 +211,60 @@ void RN_NET::validateEdge( RN_EDGE_MST_PTR& aEdge )
{
RN_NODE_PTR source = aEdge->GetSourceNode();
RN_NODE_PTR target = aEdge->GetTargetNode();
bool valid = true;
bool update = false, changed = false;
// If any of nodes belonging to the edge has the flag set,
// change it to the closest node that has flag cleared
if( source->GetNoLine() )
// 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
{
valid = false;
std::list<RN_NODE_PTR> closest = GetClosestNodes( source, LINE_TARGET() );
if( changed || source->GetNoLine() )
{
changed = false;
std::list<RN_NODE_PTR> closest = GetClosestNodes( target,
LINE_TARGET_SAME_TAG( source->GetTag() ) );
for( RN_NODE_PTR& node : closest )
if( !closest.empty() )
{
if( node && node != target )
RN_NODE_PTR& node = closest.front();
if( node != source )
{
changed = true;
update = true;
source = node;
break;
}
}
}
if( target->GetNoLine() )
if( changed || target->GetNoLine() )
{
valid = false;
std::list<RN_NODE_PTR> closest = GetClosestNodes( target, LINE_TARGET() );
changed = false;
std::list<RN_NODE_PTR> closest = GetClosestNodes( source,
LINE_TARGET_SAME_TAG( target->GetTag() ) );
for( RN_NODE_PTR& node : closest )
if( !closest.empty() )
{
if( node && node != source )
RN_NODE_PTR& node = closest.front();
if( node != target )
{
changed = true;
update = true;
target = node;
break;
}
}
}
}
while( changed );
assert( source->GetTag() >= 0 && target->GetTag() >= 0 );
assert( source->GetTag() != target->GetTag() );
assert( source != target );
// Replace an invalid edge with new, valid one
if( !valid )
if( update )
aEdge.reset( new RN_EDGE_MST( source, target ) );
}
@ -326,7 +346,8 @@ 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
// Special cases do not need complicated algorithms (actually, it does not work well with
// the Delaunay triangulator)
if( boardNodes.size() <= 2 )
{
m_rnEdges.reset( new std::vector<RN_EDGE_MST_PTR>( 0 ) );
@ -337,12 +358,18 @@ void RN_NET::compute()
RN_LINKS::RN_NODE_SET::iterator last = ++boardNodes.begin();
// There can be only one possible connection, but it is missing
m_rnEdges->push_back( std::make_shared<RN_EDGE_MST>( *boardNodes.begin(), *last ) );
RN_EDGE_MST_PTR edge = std::make_shared<RN_EDGE_MST>( *boardNodes.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 )
node->SetTag( 0 );
}
return;
}
@ -612,8 +639,7 @@ std::list<RN_NODE_PTR> RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, int aN
const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes();
// Copy nodes
for( const RN_NODE_PTR& node : nodes )
closest.push_back( node );
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 ) );
@ -635,9 +661,8 @@ std::list<RN_NODE_PTR> RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode,
std::list<RN_NODE_PTR> closest;
const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes();
// Copy nodes
for( const RN_NODE_PTR& node : nodes )
closest.push_back( node );
// 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 ) );
@ -645,9 +670,6 @@ std::list<RN_NODE_PTR> RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode,
// aNode should not be returned in the results
closest.remove( aNode );
// Filter out by condition
std::remove_if( closest.begin(), closest.end(), aFilter );
// Trim the result to the asked size
if( aNumber > 0 )
closest.resize( std::min( static_cast<size_t>( aNumber ), nodes.size() ) );

View File

@ -92,7 +92,7 @@ struct RN_NODE_FILTER : public std::unary_function<const RN_NODE_PTR&, bool>
RN_NODE_AND_FILTER operator&&( const RN_NODE_FILTER& aFilter1, const RN_NODE_FILTER& aFilter2 );
RN_NODE_OR_FILTER operator||( const RN_NODE_FILTER& aFilter1, const RN_NODE_FILTER& aFilter2 );
///> Filters out nodes that cannot be a ratsnest line target
///> Leaves nodes that can be a ratsnest line target
struct LINE_TARGET : public RN_NODE_FILTER
{
bool operator()( const RN_NODE_PTR& aNode ) const
@ -101,19 +101,34 @@ struct LINE_TARGET : public RN_NODE_FILTER
}
};
///> Filters out nodes with a specific tag
struct DIFF_TAG : public RN_NODE_FILTER
///> Leaves nodes that can be a ratsnest line target and have a specific tag
struct LINE_TARGET_SAME_TAG : public RN_NODE_FILTER
{
DIFF_TAG( int aTag ) :
LINE_TARGET_SAME_TAG( int aTag ) :
m_tag( aTag )
{}
bool operator()( const RN_NODE_PTR& aNode ) const
{
return aNode->GetTag() != m_tag;
return !aNode->GetNoLine() && aNode->GetTag() == m_tag;
}
private:
private:
int m_tag;
};
struct LINE_TARGET_DIFF_TAG : public RN_NODE_FILTER
{
LINE_TARGET_DIFF_TAG( int aTag ) :
m_tag( aTag )
{}
bool operator()( const RN_NODE_PTR& aNode ) const
{
return !aNode->GetNoLine() && aNode->GetTag() == m_tag;
}
private:
int m_tag;
};