PNS: A new approach to arc placement
Fixes https://gitlab.com/kicad/code/kicad/-/issues/6334
This commit is contained in:
parent
f6a1d703ec
commit
79502a0d88
|
@ -194,20 +194,17 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Function BuildInitialTrace()
|
||||
*
|
||||
* Builds a 2-segment line chain between points aP0 and aP1 and following 45-degree routing
|
||||
* regime. If aStartDiagonal is true, the trace starts with a diagonal segment.
|
||||
* @param aP0 starting point
|
||||
* @param aP1 ending point
|
||||
* @param aStartDiagonal whether the first segment has to be diagonal
|
||||
* @param aRadius is the radius of curvature for rounding. If =0, do not insert arcs
|
||||
* @param aFillet if true will fillet the 45-degree portion of the line chain
|
||||
* @return the trace
|
||||
*/
|
||||
const SHAPE_LINE_CHAIN BuildInitialTrace( const VECTOR2I& aP0,
|
||||
const VECTOR2I& aP1,
|
||||
bool aStartDiagonal = false,
|
||||
int aMaxRadius = 0 ) const;
|
||||
const SHAPE_LINE_CHAIN BuildInitialTrace( const VECTOR2I& aP0, const VECTOR2I& aP1,
|
||||
bool aStartDiagonal = false,
|
||||
bool aFillet = false ) const;
|
||||
|
||||
bool operator==( const DIRECTION_45& aOther ) const
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <geometry/direction45.h>
|
||||
|
||||
const SHAPE_LINE_CHAIN DIRECTION_45::BuildInitialTrace( const VECTOR2I& aP0, const VECTOR2I& aP1,
|
||||
bool aStartDiagonal, int aMaxRadius ) const
|
||||
bool aStartDiagonal, bool aFillet ) const
|
||||
{
|
||||
bool start_diagonal;
|
||||
|
||||
|
@ -34,68 +34,133 @@ const SHAPE_LINE_CHAIN DIRECTION_45::BuildInitialTrace( const VECTOR2I& aP0, con
|
|||
int sw = sign( aP1.x - aP0.x );
|
||||
int sh = sign( aP1.y - aP0.y );
|
||||
|
||||
int radius = std::min( aMaxRadius, std::min( w, h ) );
|
||||
bool use_rounded = aMaxRadius > 0;
|
||||
int dist90 = use_rounded ? KiROUND( ( M_SQRT2 - 1.0 ) * radius ) : 0;
|
||||
int dist45 = use_rounded ? KiROUND( radius * ( 1.0 - M_SQRT1_2 ) ) : 0;
|
||||
int radius = std::min( w, h );
|
||||
|
||||
VECTOR2I mp0, mp1, arc_offset_90, arc_offset_45;
|
||||
VECTOR2I arcStart, arcEnd;
|
||||
double diagLength;
|
||||
int tangentLength;
|
||||
|
||||
// we are more horizontal than vertical?
|
||||
// if( m_90deg )
|
||||
// {
|
||||
// if( m_dir == N || m_dir == S )
|
||||
//
|
||||
// }
|
||||
/*
|
||||
* Non-filleted case:
|
||||
*
|
||||
* For width greater than height, we're calculating something like this.
|
||||
* mp0 will be used if we start straight; mp1 if we start diagonal.
|
||||
*
|
||||
* aP0 ----------------- mp0
|
||||
* . \
|
||||
* . \
|
||||
* . \
|
||||
* mp1 . . . . . . . . aP1
|
||||
*
|
||||
* Filleted case:
|
||||
*
|
||||
* For a fillet, we need to know the arc start point (A in the diagram below)
|
||||
* A straight segment will be needed between aP0 and A if we are starting straight,
|
||||
* or between the arc end and aP1 if we are starting diagonally.
|
||||
*
|
||||
* aP0 -- A --___ mp0
|
||||
* . ---
|
||||
* . --
|
||||
* . --
|
||||
* mp1 . . . . . . . . aP1
|
||||
*
|
||||
* For the length of this segment (tangentLength), we subtract the length of the "diagonal"
|
||||
* line from the "straight" line (i.e. dist(aP0, mp0) - dist(mp0, aP1))
|
||||
* In the example above, we will have a straight segment from aP0 to A, and then we can use
|
||||
* the distance from A to aP1 (diagLength) to calculate the radius of the arc.
|
||||
*/
|
||||
|
||||
if( w > h )
|
||||
{
|
||||
mp0 = VECTOR2I( ( w - h - dist90 ) * sw, 0 ); // direction: E
|
||||
mp1 = VECTOR2I( ( h - dist45 ) * sw, ( h - dist45 ) * sh ); // direction: NE
|
||||
arc_offset_90 = VECTOR2I( 0, radius * sh );
|
||||
arc_offset_45 = VECTOR2I( sw * radius * M_SQRT1_2, -sh * radius * M_SQRT1_2 );
|
||||
mp0 = VECTOR2I( ( w - h ) * sw, 0 ); // direction: E
|
||||
mp1 = VECTOR2I( h * sw, h * sh ); // direction: NE
|
||||
tangentLength = ( w - h ) - mp1.EuclideanNorm();
|
||||
}
|
||||
else
|
||||
{
|
||||
mp0 = VECTOR2I( 0, sh * ( h - w - dist90 ) ); // direction: N
|
||||
mp1 = VECTOR2I( sw * ( w - dist45 ), sh * ( w - dist45 ) ); // direction: NE
|
||||
arc_offset_90 = VECTOR2I( radius * sw, 0 );
|
||||
arc_offset_45 = VECTOR2I( -sw * radius * M_SQRT1_2, sh * radius * M_SQRT1_2 );
|
||||
mp0 = VECTOR2I( 0, sh * ( h - w ) ); // direction: N
|
||||
mp1 = VECTOR2I( sw * w, sh * w ); // direction: NE
|
||||
tangentLength = ( h - w ) - mp1.EuclideanNorm();
|
||||
}
|
||||
|
||||
SHAPE_LINE_CHAIN pl;
|
||||
VECTOR2I arc_center;
|
||||
|
||||
pl.Append( aP0 );
|
||||
VECTOR2I next_point;
|
||||
|
||||
if( start_diagonal )
|
||||
// TODO: if tangentLength zero, we could still place a small arc at the start...
|
||||
if( aFillet )
|
||||
{
|
||||
next_point = aP0 + mp1;
|
||||
arc_center = aP0 + mp1 + arc_offset_45;
|
||||
double diag2 = tangentLength >= 0 ? mp1.SquaredEuclideanNorm() : mp0.SquaredEuclideanNorm();
|
||||
diagLength = std::sqrt( ( 2 * diag2 ) - ( 2 * diag2 * std::cos( 3 * M_PI_4 ) ) );
|
||||
int arcRadius = KiROUND( diagLength / ( 2.0 * std::cos( 67.5 * M_PI / 180.0 ) ) );
|
||||
|
||||
if( start_diagonal )
|
||||
{
|
||||
if( tangentLength >= 0 )
|
||||
{
|
||||
// Positive tangentLength, diagonal start: arc goes at the start
|
||||
int rotationSign = ( w > h ) ? ( sw * sh * -1 ) : ( sw * sh );
|
||||
arcStart = aP0 + mp1 + mp0.Resize( mp1.EuclideanNorm() );
|
||||
VECTOR2D centerDir( mp0.Rotate( M_PI_2 * rotationSign ) );
|
||||
VECTOR2D arcCenter( arcStart + centerDir.Resize( arcRadius ) );
|
||||
SHAPE_ARC new_arc( arcCenter, aP0, 45 * rotationSign );
|
||||
pl.Append( new_arc );
|
||||
pl.Append( aP1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pl.Append( aP0 );
|
||||
// Negative tangentLength, diagonal start: arc goes at the end
|
||||
int rotationSign = ( w > h ) ? ( sw * sh * -1 ) : ( sw * sh );
|
||||
arcStart = aP0 + mp1.Resize( std::abs( tangentLength ) );
|
||||
VECTOR2D centerDir( mp0.Rotate( M_PI_2 * rotationSign ) );
|
||||
VECTOR2D arcCenter( aP1 + centerDir.Resize( arcRadius ) );
|
||||
SHAPE_ARC new_arc( arcCenter, arcStart, 45 * rotationSign );
|
||||
pl.Append( new_arc );
|
||||
|
||||
// TODO: nicer way of fixing these up
|
||||
if( new_arc.GetP1() != aP1 )
|
||||
pl.Replace( -1, -1, aP1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( tangentLength >= 0 )
|
||||
{
|
||||
pl.Append( aP0 );
|
||||
// Positive tangentLength: arc goes at the end
|
||||
int rotationSign = ( w > h ) ? ( sw * sh ) : ( sw * sh * -1 );
|
||||
arcStart = aP0 + mp0.Resize( tangentLength );
|
||||
VECTOR2D centerDir( mp0.Rotate( M_PI_2 * rotationSign ) );
|
||||
VECTOR2D arcCenter( arcStart + centerDir.Resize( arcRadius ) );
|
||||
SHAPE_ARC new_arc( arcCenter, arcStart, 45 * rotationSign );
|
||||
pl.Append( new_arc );
|
||||
|
||||
// TODO: nicer way of fixing these up
|
||||
if( new_arc.GetP1() != aP1 )
|
||||
pl.Replace( -1, -1, aP1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Negative tangentLength: arc goes at the start
|
||||
int rotationSign = ( w > h ) ? ( sw * sh ) : ( sw * sh * -1 );
|
||||
arcStart = aP0;
|
||||
VECTOR2D centerDir( mp0.Rotate( M_PI_2 * rotationSign ) );
|
||||
VECTOR2D arcCenter( arcStart + centerDir.Resize( arcRadius ) );
|
||||
SHAPE_ARC new_arc( arcCenter, arcStart, 45 * rotationSign );
|
||||
pl.Append( new_arc );
|
||||
pl.Append( aP1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
next_point = aP0 + mp0;
|
||||
arc_center = aP0 + mp0 + arc_offset_90;
|
||||
pl.Append( aP0 );
|
||||
pl.Append( start_diagonal ? ( aP0 + mp1 ) : ( aP0 + mp0 ) );
|
||||
pl.Append( aP1 );
|
||||
}
|
||||
|
||||
if( use_rounded )
|
||||
{
|
||||
int sa = start_diagonal ? -sw * sh : sw * sh;
|
||||
|
||||
if( h > w )
|
||||
sa = -sa;
|
||||
|
||||
SHAPE_ARC new_arc( arc_center, next_point, sa * 45.0 );
|
||||
pl.Append( new_arc );
|
||||
}
|
||||
else
|
||||
{
|
||||
pl.Append( next_point );
|
||||
}
|
||||
|
||||
pl.Append( aP1 );
|
||||
// TODO: be careful about including P0 and P1 above, because SHAPE_LINE_CHAIN::Simplify
|
||||
// does not seem to properly handle when an arc overlaps P0.
|
||||
|
||||
pl.Simplify();
|
||||
return pl;
|
||||
|
|
|
@ -448,7 +448,7 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine )
|
|||
{
|
||||
const VECTOR2I p = aOtherLine.CPoint( 0 );
|
||||
m_points.push_back( p );
|
||||
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
|
||||
m_shapes.push_back( aOtherLine.CShapes()[0] );
|
||||
m_bbox.Merge( p );
|
||||
}
|
||||
|
||||
|
|
|
@ -1196,7 +1196,7 @@ bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinis
|
|||
{
|
||||
ssize_t arcIndex = l.ArcIndex( i );
|
||||
|
||||
if( arcIndex < 0 )
|
||||
if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 ) )
|
||||
{
|
||||
seg = SEGMENT( pl.CSegment( i ), m_currentNet );
|
||||
seg.SetWidth( pl.Width() );
|
||||
|
@ -1427,10 +1427,11 @@ void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
|
|||
bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead )
|
||||
{
|
||||
SHAPE_LINE_CHAIN l;
|
||||
int initial_radius = 0;
|
||||
|
||||
DIRECTION_45 guessedDir = m_postureSolver.GetPosture( aP );
|
||||
|
||||
// Rounded corners don't make sense when routing orthogonally (single track at a time)
|
||||
bool fillet = !m_orthoMode && Settings().GetCornerMode() == CORNER_MODE::ROUNDED_45;
|
||||
|
||||
if( m_p_start == aP )
|
||||
{
|
||||
l.Clear();
|
||||
|
@ -1443,15 +1444,10 @@ bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead )
|
|||
}
|
||||
else
|
||||
{
|
||||
// Rounded corners don't make sense when routing orthogonally (single track at a time)
|
||||
if( Settings().GetRounded() && !m_orthoMode )
|
||||
initial_radius = Settings().GetMaxRadius();
|
||||
|
||||
|
||||
if( !m_tail.PointCount() )
|
||||
l = guessedDir.BuildInitialTrace( m_p_start, aP, false, initial_radius );
|
||||
l = guessedDir.BuildInitialTrace( m_p_start, aP, false, fillet );
|
||||
else
|
||||
l = m_direction.BuildInitialTrace( m_p_start, aP, false, initial_radius );
|
||||
l = m_direction.BuildInitialTrace( m_p_start, aP, false, fillet );
|
||||
}
|
||||
|
||||
if( l.SegmentCount() > 1 && m_orthoMode )
|
||||
|
@ -1486,7 +1482,7 @@ bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead )
|
|||
if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) )
|
||||
{
|
||||
SHAPE_LINE_CHAIN line = guessedDir.BuildInitialTrace( m_p_start, aP + force, false,
|
||||
initial_radius );
|
||||
fillet );
|
||||
aHead = LINE( aHead, line );
|
||||
|
||||
v.SetPos( v.Pos() + force );
|
||||
|
|
|
@ -1071,6 +1071,10 @@ bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VEC
|
|||
if( aEffortLevel & OPTIMIZER::PRESERVE_VERTEX )
|
||||
opt.SetPreserveVertex( aV );
|
||||
|
||||
// TODO: fix optimizer for arcs
|
||||
if( aLine->ArcCount() )
|
||||
return -1;
|
||||
|
||||
return opt.Optimize( aLine );
|
||||
}
|
||||
|
||||
|
|
|
@ -537,7 +537,19 @@ bool ROUTER::IsPlacingVia() const
|
|||
|
||||
void ROUTER::ToggleRounded()
|
||||
{
|
||||
m_settings->SetRounded( !m_settings->GetRounded() );
|
||||
CORNER_MODE newMode = CORNER_MODE::MITERED_45;
|
||||
|
||||
switch( m_settings->GetCornerMode() )
|
||||
{
|
||||
case CORNER_MODE::MITERED_45:
|
||||
newMode = CORNER_MODE::ROUNDED_45;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_settings->SetCornerMode( newMode );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,9 +51,7 @@ ROUTING_SETTINGS::ROUTING_SETTINGS( JSON_SETTINGS* aParent, const std::string& a
|
|||
m_snapToTracks = false;
|
||||
m_snapToPads = false;
|
||||
m_optimizeDraggedTrack = true;
|
||||
m_minRadius = 0;
|
||||
m_maxRadius = 1000000;
|
||||
m_roundedCorners = false;
|
||||
m_cornerMode = CORNER_MODE::MITERED_45;
|
||||
m_autoPosture = true;
|
||||
m_fixAllSegments = true;
|
||||
|
||||
|
@ -94,12 +92,13 @@ ROUTING_SETTINGS::ROUTING_SETTINGS( JSON_SETTINGS* aParent, const std::string& a
|
|||
m_params.emplace_back( new PARAM<bool>( "snap_to_pads", &m_snapToPads, false ) );
|
||||
m_params.emplace_back( new PARAM<bool>( "optimize_dragged_track", &m_optimizeDraggedTrack, true ) );
|
||||
|
||||
m_params.emplace_back( new PARAM<int>( "min_radius", &m_minRadius, 0 ) );
|
||||
m_params.emplace_back( new PARAM<int>( "max_radius", &m_maxRadius, 1000000 ) );
|
||||
m_params.emplace_back( new PARAM<bool>( "use_rounded", &m_roundedCorners, false ) );
|
||||
m_params.emplace_back( new PARAM<bool>( "auto_posture", &m_autoPosture, true ) );
|
||||
m_params.emplace_back( new PARAM<bool>( "fix_all_segments", &m_fixAllSegments, true ) );
|
||||
|
||||
m_params.emplace_back( new PARAM_ENUM<CORNER_MODE>( "corner_mode", &m_cornerMode,
|
||||
CORNER_MODE::MITERED_45, CORNER_MODE::ROUNDED_90,
|
||||
CORNER_MODE::MITERED_45 ) );
|
||||
|
||||
LoadFromFile();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,15 @@ enum PNS_OPTIMIZATION_EFFORT
|
|||
OE_FULL = 2
|
||||
};
|
||||
|
||||
///> What kind of corners to create in the line placers
|
||||
enum class CORNER_MODE
|
||||
{
|
||||
MITERED_90, ///< H/V only (90-degree corners) (not yet implemented)
|
||||
MITERED_45, ///< H/V/45 with mitered corners (default)
|
||||
ROUNDED_90, ///< H/V/45 with filleted corners
|
||||
ROUNDED_45 ///< H/V with filleted corners (not yet implemented)
|
||||
};
|
||||
|
||||
/**
|
||||
* ROUTING_SETTINGS
|
||||
*
|
||||
|
@ -141,8 +150,8 @@ public:
|
|||
bool GetSnapToTracks() const { return m_snapToTracks; }
|
||||
bool GetSnapToPads() const { return m_snapToPads; }
|
||||
|
||||
bool GetRounded() const { return m_roundedCorners; }
|
||||
void SetRounded( bool aRound ) { m_roundedCorners = aRound; }
|
||||
CORNER_MODE GetCornerMode() const { return m_cornerMode; }
|
||||
void SetCornerMode( CORNER_MODE aMode ) { m_cornerMode = aMode; }
|
||||
|
||||
bool GetOptimizeDraggedTrack() const { return m_optimizeDraggedTrack; }
|
||||
void SetOptimizeDraggedTrack( bool aEnable ) { m_optimizeDraggedTrack = aEnable; }
|
||||
|
@ -153,25 +162,6 @@ public:
|
|||
bool GetFixAllSegments() const { return m_fixAllSegments; }
|
||||
void SetFixAllSegments( bool aEnable ) { m_fixAllSegments = aEnable; }
|
||||
|
||||
void SetMinRadius( int aRadius )
|
||||
{
|
||||
m_minRadius = aRadius;
|
||||
|
||||
if( m_maxRadius < m_minRadius )
|
||||
m_maxRadius = m_minRadius;
|
||||
}
|
||||
|
||||
void SetMaxRadius( int aRadius )
|
||||
{
|
||||
m_maxRadius = aRadius;
|
||||
|
||||
if( m_maxRadius < m_minRadius )
|
||||
m_minRadius = m_maxRadius;
|
||||
}
|
||||
|
||||
int GetMinRadius() const { return m_minRadius; }
|
||||
int GetMaxRadius() const { return m_maxRadius; }
|
||||
|
||||
private:
|
||||
bool m_shoveVias;
|
||||
bool m_startDiagonal;
|
||||
|
@ -186,13 +176,11 @@ private:
|
|||
bool m_inlineDragEnabled;
|
||||
bool m_snapToTracks;
|
||||
bool m_snapToPads;
|
||||
bool m_roundedCorners;
|
||||
bool m_optimizeDraggedTrack;
|
||||
bool m_autoPosture;
|
||||
bool m_fixAllSegments;
|
||||
|
||||
int m_minRadius;
|
||||
int m_maxRadius;
|
||||
CORNER_MODE m_cornerMode;
|
||||
|
||||
PNS_MODE m_routingMode;
|
||||
PNS_OPTIMIZATION_EFFORT m_optimizerEffort;
|
||||
|
|
Loading…
Reference in New Issue