PNS: Fix tuning calculations through locked segments

Fixes https://gitlab.com/kicad/code/kicad/-/issues/9727
This commit is contained in:
Jon Evans 2021-12-05 13:35:28 -05:00
parent 2d1ad52212
commit 7de9e48b57
5 changed files with 57 additions and 24 deletions

View File

@ -95,12 +95,35 @@ public:
return nullptr;
}
///< Return true if the joint is a trivial line corner, connecting two
///< segments of the same net, on the same layer.
bool IsLineCorner() const
/**
* Checks if a joint connects two segments of the same net, layer, and width.
* @param aAllowLockedSegs will consider joints between locked and unlocked segments as trivial
* @return true if the joint is a trivial line corner
*/
bool IsLineCorner( bool aAllowLockedSegs = false ) const
{
if( m_linkedItems.Size() != 2 || m_linkedItems.Count( SEGMENT_T | ARC_T ) != 2 )
return false;
{
if( !aAllowLockedSegs )
{
return false;
}
else if( m_linkedItems.Size() == 3
&& m_linkedItems.Count( SEGMENT_T | ARC_T ) == 2
&& m_linkedItems.Count( VIA_T ) == 1 )
{
assert( static_cast<const ITEM*>( m_linkedItems[2] )->Kind() == VIA_T );
const VIA* via = static_cast<const VIA*>( m_linkedItems[2] );
if( !via->IsVirtual() )
return false;
}
else
{
return false;
}
}
auto seg1 = static_cast<LINKED_ITEM*>( m_linkedItems[0] );
auto seg2 = static_cast<LINKED_ITEM*>( m_linkedItems[1] );
@ -156,9 +179,9 @@ public:
///< For trivial joints, return the segment adjacent to (aCurrent). For non-trival ones,
///< return NULL, indicating the end of line.
LINKED_ITEM* NextSegment( ITEM* aCurrent ) const
LINKED_ITEM* NextSegment( ITEM* aCurrent, bool aAllowLockedSegs = false ) const
{
if( !IsLineCorner() )
if( !IsLineCorner( aAllowLockedSegs ) )
return nullptr;
return static_cast<LINKED_ITEM*>( m_linkedItems[m_linkedItems[0] == aCurrent ? 1 : 0] );

View File

@ -33,6 +33,7 @@
#include "pns_arc.h"
#include "pns_item.h"
#include "pns_itemset.h"
#include "pns_line.h"
#include "pns_node.h"
#include "pns_via.h"
@ -896,7 +897,7 @@ void NODE::Remove( LINE& aLine )
void NODE::followLine( LINKED_ITEM* aCurrent, bool aScanDirection, int& aPos, int aLimit,
VECTOR2I* aCorners, LINKED_ITEM** aSegments, bool* aArcReversed,
bool& aGuardHit, bool aStopAtLockedJoints )
bool& aGuardHit, bool aStopAtLockedJoints, bool aFollowLockedSegments )
{
bool prevReversed = false;
@ -904,19 +905,19 @@ void NODE::followLine( LINKED_ITEM* aCurrent, bool aScanDirection, int& aPos, in
for( int count = 0 ; ; ++count )
{
const VECTOR2I p = aCurrent->Anchor( aScanDirection ^ prevReversed );
const JOINT* jt = FindJoint( p, aCurrent );
const VECTOR2I p = aCurrent->Anchor( aScanDirection ^ prevReversed );
const JOINT* jt = FindJoint( p, aCurrent );
assert( jt );
aCorners[aPos] = jt->Pos();
aSegments[aPos] = aCurrent;
aCorners[aPos] = jt->Pos();
aSegments[aPos] = aCurrent;
aArcReversed[aPos] = false;
if( aCurrent->Kind() == ITEM::ARC_T )
{
if( ( aScanDirection && jt->Pos() == aCurrent->Anchor( 0 ) ) ||
( !aScanDirection && jt->Pos() == aCurrent->Anchor( 1 ) ) )
if( ( aScanDirection && jt->Pos() == aCurrent->Anchor( 0 ) )
|| ( !aScanDirection && jt->Pos() == aCurrent->Anchor( 1 ) ) )
aArcReversed[aPos] = true;
}
@ -933,10 +934,10 @@ void NODE::followLine( LINKED_ITEM* aCurrent, bool aScanDirection, int& aPos, in
bool locked = aStopAtLockedJoints ? jt->IsLocked() : false;
if( locked || !jt->IsLineCorner() || aPos < 0 || aPos == aLimit )
if( locked || !jt->IsLineCorner( aFollowLockedSegments ) || aPos < 0 || aPos == aLimit )
break;
aCurrent = jt->NextSegment( aCurrent );
aCurrent = jt->NextSegment( aCurrent, aFollowLockedSegments );
prevReversed = ( aCurrent && jt->Pos() == aCurrent->Anchor( aScanDirection ) );
}
@ -944,7 +945,7 @@ void NODE::followLine( LINKED_ITEM* aCurrent, bool aScanDirection, int& aPos, in
const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex,
bool aStopAtLockedJoints )
bool aStopAtLockedJoints, bool aFollowLockedSegments )
{
const int MaxVerts = 1024 * 16;
@ -964,12 +965,12 @@ const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex,
pl.SetOwner( this );
followLine( aSeg, false, i_start, MaxVerts, corners.data(), segs.data(), arcReversed.data(),
guardHit, aStopAtLockedJoints );
guardHit, aStopAtLockedJoints, aFollowLockedSegments );
if( !guardHit )
{
followLine( aSeg, true, i_end, MaxVerts, corners.data(), segs.data(), arcReversed.data(),
guardHit, aStopAtLockedJoints );
guardHit, aStopAtLockedJoints, aFollowLockedSegments );
}
int n = 0;

View File

@ -304,10 +304,14 @@ public:
*
* @param aSeg the initial segment.
* @param aOriginSegmentIndex index of aSeg in the resulting line.
* @param aStopAtLockedJoints will terminate the line at the first locked joint encountered
* @param aFollowLockedSegments will consider a joint between a locked segment and an unlocked
* segment of the same width as a trivial joint.
* @return the line
*/
const LINE AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex = nullptr,
bool aStopAtLockedJoints = false );
bool aStopAtLockedJoints = false,
bool aFollowLockedSegments = false );
///< Print the contents and joints structure.
void Dump( bool aLong = false );
@ -432,7 +436,7 @@ private:
///< Scan the joint map, forming a line starting from segment (current).
void followLine( LINKED_ITEM* aCurrent, bool aScanDirection, int& aPos, int aLimit,
VECTOR2I* aCorners, LINKED_ITEM** aSegments, bool* aArcReversed,
bool& aGuardHit, bool aStopAtLockedJoints );
bool& aGuardHit, bool aStopAtLockedJoints, bool aFollowLockedSegments );
private:
struct DEFAULT_OBSTACLE_VISITOR;

View File

@ -247,7 +247,8 @@ bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet,
const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart,
std::pair<JOINT*, JOINT*>* aTerminalJoints )
std::pair<JOINT*, JOINT*>* aTerminalJoints,
bool aFollowLockedSegments )
{
ITEM_SET path;
std::set<ITEM*> visited;
@ -278,7 +279,9 @@ const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart,
if( !seg )
return ITEM_SET();
LINE l = m_world->AssembleLine( seg );
// Assemble a line following through locked segments
// TODO: consider if we want to allow tuning lines with different widths in the future
LINE l = m_world->AssembleLine( seg, nullptr, false, true );
path.Add( l );
@ -301,7 +304,7 @@ const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart,
const ITEM_SET TOPOLOGY::AssembleTuningPath( ITEM* aStart, SOLID** aStartPad, SOLID** aEndPad )
{
std::pair<JOINT*, JOINT*> joints;
ITEM_SET initialPath = AssembleTrivialPath( aStart, &joints );
ITEM_SET initialPath = AssembleTrivialPath( aStart, &joints, true );
PAD* padA = nullptr;
PAD* padB = nullptr;

View File

@ -61,10 +61,12 @@ public:
*
* @param aStart is the item to assemble from.
* @param aTerminalJoints will be filled with the start and end points of the assembled path.
* @param aFollowLockedSegments if true will assemble a path including locked segments
* @return a set of items in the path.
*/
const ITEM_SET AssembleTrivialPath( ITEM* aStart,
std::pair<JOINT*, JOINT*>* aTerminalJoints = nullptr );
std::pair<JOINT*, JOINT*>* aTerminalJoints = nullptr,
bool aFollowLockedSegments = false );
/**
* Like AssembleTrivialPath, but follows the track length algorithm, which discards segments