2013-09-18 17:55:16 +00:00
|
|
|
/*
|
|
|
|
* KiRouter - a push-and-(sometimes-)shove PCB router
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 CERN
|
|
|
|
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
2013-09-26 21:53:54 +00:00
|
|
|
*
|
2013-09-18 17:55:16 +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 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
2013-09-26 21:53:54 +00:00
|
|
|
*
|
2013-09-18 17:55:16 +00:00
|
|
|
* 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.
|
2013-09-26 21:53:54 +00:00
|
|
|
*
|
2013-09-18 17:55:16 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.or/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
|
|
|
|
#include "trace.h"
|
|
|
|
|
|
|
|
#include "pns_node.h"
|
|
|
|
#include "pns_line_placer.h"
|
|
|
|
#include "pns_walkaround.h"
|
|
|
|
#include "pns_shove.h"
|
|
|
|
#include "pns_utils.h"
|
|
|
|
|
|
|
|
using boost::optional;
|
|
|
|
|
2014-03-16 17:40:23 +00:00
|
|
|
const double PNS_LINE_PLACER::m_shoveLengthThreshold = 1.7;
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_NODE* aWorld )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
m_initial_direction = DIRECTION_45( DIRECTION_45::N );
|
|
|
|
m_follow_mouse = false;
|
|
|
|
m_smoothing_step = 100000;
|
|
|
|
m_smooth_mouse = false;
|
|
|
|
m_iteration = 0;
|
|
|
|
m_world = aWorld;
|
|
|
|
m_mode = RM_Smart;
|
|
|
|
m_follow_mouse = true;
|
|
|
|
m_shove = NULL;
|
2013-09-18 17:55:16 +00:00
|
|
|
};
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
PNS_LINE_PLACER::~PNS_LINE_PLACER()
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
if( m_shove )
|
|
|
|
delete m_shove;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
void PNS_LINE_PLACER::ApplySettings( const PNS_ROUTING_SETTINGS& aSettings )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
m_follow_mouse = aSettings.m_followMouse;
|
|
|
|
m_mode = aSettings.m_routingMode;
|
|
|
|
m_walkaroundIterationLimit = aSettings.m_walkaroundIterationLimit;
|
|
|
|
m_smartPads = aSettings.m_smartPads;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
void PNS_LINE_PLACER::StartPlacement( const VECTOR2I& aStart, int aNet,
|
2013-10-14 14:13:35 +00:00
|
|
|
int aWidth, int aLayer )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
m_direction = m_initial_direction;
|
|
|
|
TRACE( 1, "world %p, intitial-direction %s layer %d\n",
|
|
|
|
m_world % m_direction.Format().c_str() % aLayer );
|
|
|
|
m_head.SetNet( aNet );
|
|
|
|
m_tail.SetNet( aNet );
|
|
|
|
m_head.SetWidth( aWidth );
|
|
|
|
m_tail.SetWidth( aWidth );
|
|
|
|
m_head.GetLine().Clear();
|
|
|
|
m_tail.GetLine().Clear();
|
|
|
|
m_head.SetLayer( aLayer );
|
|
|
|
m_tail.SetLayer( aLayer );
|
|
|
|
m_iteration = 0;
|
|
|
|
m_p_start = aStart;
|
|
|
|
m_currentNode = m_world->Branch();
|
|
|
|
m_head.SetWorld( m_currentNode );
|
|
|
|
m_tail.SetWorld( m_currentNode );
|
|
|
|
// if(m_shove)
|
|
|
|
// delete m_shove;
|
|
|
|
m_shove = new PNS_SHOVE( m_currentNode );
|
|
|
|
m_placingVia = false;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
void PNS_LINE_PLACER::SetInitialDirection( const DIRECTION_45& aDirection )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
m_initial_direction = aDirection;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( m_tail.GetCLine().SegmentCount() == 0 )
|
|
|
|
m_direction = aDirection;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::handleSelfIntersections()
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
SHAPE_LINE_CHAIN::INTERSECTIONS ips;
|
2013-09-26 21:53:54 +00:00
|
|
|
SHAPE_LINE_CHAIN& head = m_head.GetLine();
|
|
|
|
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
|
|
|
|
|
|
|
|
// if there is no tail, there is nothing to intersect with
|
|
|
|
if( tail.PointCount() < 2 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
tail.Intersect( head, ips );
|
|
|
|
|
|
|
|
// no intesection points - nothing to reduce
|
|
|
|
if( ips.empty() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int n = INT_MAX;
|
|
|
|
VECTOR2I ipoint;
|
|
|
|
|
|
|
|
// if there is more than one intersection, find the one that is
|
|
|
|
// closest to the beginning of the tail.
|
2013-10-14 18:40:36 +00:00
|
|
|
BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION i, ips )
|
2013-09-26 21:53:54 +00:00
|
|
|
{
|
|
|
|
if( i.our.Index() < n )
|
|
|
|
{
|
|
|
|
n = i.our.Index();
|
|
|
|
ipoint = i.p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore the point where head and tail meet
|
|
|
|
if( ipoint == head.CPoint( 0 ) || ipoint == tail.CPoint( -1 ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Intersection point is on the first or the second segment: just start routing
|
|
|
|
// from the beginning
|
|
|
|
if( n < 2 )
|
|
|
|
{
|
|
|
|
m_p_start = tail.Point( 0 );
|
|
|
|
m_direction = m_initial_direction;
|
|
|
|
tail.Clear();
|
|
|
|
head.Clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Clip till the last tail segment before intersection.
|
|
|
|
// Set the direction to the one of this segment.
|
|
|
|
const SEG last = tail.CSegment( n - 1 );
|
2013-10-14 18:40:36 +00:00
|
|
|
m_p_start = last.A;
|
2013-09-26 21:53:54 +00:00
|
|
|
m_direction = DIRECTION_45( last );
|
|
|
|
tail.Remove( n, -1 );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::handlePullback()
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
SHAPE_LINE_CHAIN& head = m_head.GetLine();
|
|
|
|
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
|
|
|
|
|
|
|
|
int n = tail.PointCount();
|
|
|
|
|
|
|
|
if( n == 0 )
|
|
|
|
return false;
|
|
|
|
else if( n == 1 )
|
|
|
|
{
|
|
|
|
m_p_start = tail.CPoint( 0 );
|
|
|
|
tail.Clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
DIRECTION_45 first_head( head.Segment( 0 ) );
|
|
|
|
DIRECTION_45 last_tail( tail.Segment( -1 ) );
|
|
|
|
DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
|
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
// case 1: we have a defined routing direction, and the currently computed
|
2013-09-26 21:53:54 +00:00
|
|
|
// head goes in different one.
|
|
|
|
bool pullback_1 = false; // (m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head);
|
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
// case 2: regardless of the current routing direction, if the tail/head
|
2013-09-26 21:53:54 +00:00
|
|
|
// extremities form an acute or right angle, reduce the tail by one segment
|
|
|
|
// (and hope that further iterations) will result with a cleaner trace
|
2013-10-14 14:13:35 +00:00
|
|
|
bool pullback_2 = (angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE);
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
if( pullback_1 || pullback_2 )
|
|
|
|
{
|
|
|
|
const SEG last = tail.CSegment( -1 );
|
|
|
|
m_direction = DIRECTION_45( last );
|
2013-10-14 18:40:36 +00:00
|
|
|
m_p_start = last.A;
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
TRACE( 0, "Placer: pullback triggered [%d] [%s %s]",
|
|
|
|
n % last_tail.Format().c_str() % first_head.Format().c_str() );
|
|
|
|
|
|
|
|
// erase the last point in the tail, hoping that the next iteration will
|
2013-10-14 11:43:57 +00:00
|
|
|
// result with a head trace that starts with a segment following our
|
2013-09-26 21:53:54 +00:00
|
|
|
// current direction.
|
|
|
|
if( n < 2 )
|
|
|
|
tail.Clear(); // don't leave a single-point tail
|
|
|
|
else
|
|
|
|
tail.Remove( -1, -1 );
|
|
|
|
|
|
|
|
if( !tail.SegmentCount() )
|
|
|
|
m_direction = m_initial_direction;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
SHAPE_LINE_CHAIN& head = m_head.GetLine();
|
|
|
|
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
|
|
|
|
|
|
|
|
int n = tail.SegmentCount();
|
|
|
|
|
|
|
|
// Don't attempt this for too short tails
|
|
|
|
if( n < 2 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Start from the segment farthest from the end of the tail
|
|
|
|
// int start_index = std::max(n - 1 - ReductionDepth, 0);
|
|
|
|
|
|
|
|
DIRECTION_45 new_direction;
|
|
|
|
VECTOR2I new_start;
|
|
|
|
int reduce_index = -1;
|
|
|
|
|
|
|
|
DIRECTION_45 head_dir( head.Segment( 0 ) );
|
|
|
|
|
|
|
|
for( int i = tail.SegmentCount() - 1; i >= 0; i-- )
|
|
|
|
{
|
|
|
|
const SEG s = tail.CSegment( i );
|
|
|
|
DIRECTION_45 dir( s );
|
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
// calculate a replacement route and check if it matches
|
2013-09-26 21:53:54 +00:00
|
|
|
// the direction of the segment to be replaced
|
2013-10-14 18:40:36 +00:00
|
|
|
SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace( s.A, aEnd );
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
PNS_LINE tmp( m_tail, replacement );
|
|
|
|
|
|
|
|
if( m_currentNode->CheckColliding( &tmp, PNS_ITEM::ANY ) )
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( DIRECTION_45( replacement.Segment( 0 ) ) == dir )
|
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
new_start = s.A;
|
2013-09-26 21:53:54 +00:00
|
|
|
new_direction = dir;
|
|
|
|
reduce_index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( reduce_index >= 0 )
|
|
|
|
{
|
|
|
|
TRACE( 0, "Placer: reducing tail: %d", reduce_index );
|
|
|
|
SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace( new_start, aEnd );
|
|
|
|
|
|
|
|
m_p_start = new_start;
|
|
|
|
m_direction = new_direction;
|
|
|
|
tail.Remove( reduce_index + 1, -1 );
|
|
|
|
head.Clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !tail.SegmentCount() )
|
|
|
|
m_direction = m_initial_direction;
|
|
|
|
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
bool PNS_LINE_PLACER::checkObtusity( const SEG& a, const SEG& b ) const
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
const DIRECTION_45 dir_a( a );
|
|
|
|
const DIRECTION_45 dir_b( b );
|
|
|
|
|
|
|
|
return dir_a.IsObtuse( dir_b ) || dir_a == dir_b;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::mergeHead()
|
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
SHAPE_LINE_CHAIN& head = m_head.GetLine();
|
|
|
|
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
|
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE |
|
2013-09-26 21:53:54 +00:00
|
|
|
DIRECTION_45::ANG_HALF_FULL |
|
|
|
|
DIRECTION_45::ANG_UNDEFINED;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
head.Simplify();
|
|
|
|
tail.Simplify();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
int n_head = head.SegmentCount();
|
|
|
|
int n_tail = tail.SegmentCount();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( n_head < 3 )
|
|
|
|
{
|
|
|
|
TRACEn( 4, "Merge failed: not enough head segs." );
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( n_tail && head.CPoint( 0 ) != tail.CPoint( -1 ) )
|
|
|
|
{
|
|
|
|
TRACEn( 4, "Merge failed: head and tail discontinuous." );
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( m_head.CountCorners( ForbiddenAngles ) != 0 )
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
DIRECTION_45 dir_tail, dir_head;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
dir_head = DIRECTION_45( head.CSegment( 0 ) );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( n_tail )
|
|
|
|
{
|
|
|
|
dir_tail = DIRECTION_45( tail.CSegment( -1 ) );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( dir_head.Angle( dir_tail ) & ForbiddenAngles )
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( !n_tail )
|
2013-10-14 18:40:36 +00:00
|
|
|
tail.Append( head.CSegment( 0 ).A );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
for( int i = 0; i < n_head - 2; i++ )
|
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
tail.Append( head.CSegment( i ).B );
|
2013-09-26 21:53:54 +00:00
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
tail.Simplify();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
SEG last = tail.CSegment( -1 );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-10-14 18:40:36 +00:00
|
|
|
m_p_start = last.B;
|
2013-09-26 21:53:54 +00:00
|
|
|
m_direction = DIRECTION_45( last ).Right();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
head.Remove( 0, n_head - 2 );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
TRACE( 0, "Placer: merge %d, new direction: %s", n_head % m_direction.Format().c_str() );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
head.Simplify();
|
|
|
|
tail.Simplify();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
return true;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::handleViaPlacement( PNS_LINE& aHead )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
if( !m_placingVia )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
PNS_LAYERSET allLayers( 0, 15 );
|
|
|
|
PNS_VIA v( aHead.GetCLine().CPoint( -1 ), allLayers, m_viaDiameter, aHead.GetNet() );
|
|
|
|
v.SetDrill( m_viaDrill );
|
|
|
|
|
|
|
|
VECTOR2I force;
|
|
|
|
VECTOR2I lead = aHead.GetCLine().CPoint( -1 ) - aHead.GetCLine().CPoint( 0 );
|
|
|
|
|
|
|
|
if( v.PushoutForce( m_shove->GetCurrentNode(), lead, force, true, 20 ) )
|
|
|
|
{
|
2013-10-14 11:43:57 +00:00
|
|
|
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace(
|
2013-09-26 21:53:54 +00:00
|
|
|
aHead.GetCLine().CPoint( 0 ),
|
|
|
|
aHead.GetCLine().CPoint( -1 ) + force );
|
|
|
|
aHead = PNS_LINE( aHead, line );
|
|
|
|
|
|
|
|
v.SetPos( v.GetPos() + force );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
bool PNS_LINE_PLACER::routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead,
|
2013-10-14 14:13:35 +00:00
|
|
|
bool aCwWalkaround )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
// STAGE 1: route a simple two-segment trace between m_p_start and aP...
|
|
|
|
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
PNS_LINE initTrack( m_head, line );
|
|
|
|
PNS_LINE walkFull, walkSolids;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( m_mode == RM_Ignore )
|
|
|
|
{
|
|
|
|
aNewHead = initTrack;
|
|
|
|
return true;
|
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
handleViaPlacement( initTrack );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
m_currentNode = m_shove->GetCurrentNode();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
PNS_OPTIMIZER optimizer( m_currentNode );
|
|
|
|
PNS_WALKAROUND walkaround( m_currentNode );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
walkaround.SetSolidsOnly( false );
|
|
|
|
walkaround.SetIterationLimit( m_mode == RM_Walkaround ? 8 : 5 );
|
|
|
|
// walkaround.SetApproachCursor(true, aP);
|
|
|
|
|
|
|
|
PNS_WALKAROUND::WalkaroundStatus wf = walkaround.Route( initTrack, walkFull );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
|
|
|
#if 0
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
if( m_mode == RM_Walkaround )
|
|
|
|
{
|
|
|
|
// walkaround.
|
|
|
|
// PNSDisplayDebugLine (walkFull.GetCLine(), 4);
|
|
|
|
|
|
|
|
if( wf == PNS_WALKAROUND::STUCK )
|
|
|
|
{
|
|
|
|
aNewHead = m_head;
|
|
|
|
aNewHead.SetShape( walkFull.GetCLine() );
|
|
|
|
aNewHead = aNewHead.ClipToNearestObstacle( m_currentNode );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aNewHead = m_head;
|
|
|
|
aNewHead.SetShape( walkFull.GetCLine() );
|
|
|
|
|
|
|
|
// printf("nh w %d l %d\n", aNewHead.GetWidth(), aNewHead.GetLayers().Start());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-09-18 17:55:16 +00:00
|
|
|
#endif
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
PNS_COST_ESTIMATOR cost_walk, cost_orig;
|
|
|
|
|
|
|
|
walkaround.SetApproachCursor( false, aP );
|
|
|
|
walkaround.SetSolidsOnly( true );
|
|
|
|
walkaround.SetIterationLimit( 10 );
|
|
|
|
PNS_WALKAROUND::WalkaroundStatus stat_solids = walkaround.Route( initTrack, walkSolids );
|
|
|
|
|
|
|
|
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_SEGMENTS );
|
|
|
|
optimizer.SetCollisionMask( PNS_ITEM::SOLID );
|
|
|
|
optimizer.Optimize( &walkSolids );
|
|
|
|
#if 0
|
|
|
|
optimizer.SetCollisionMask( -1 );
|
|
|
|
optimizer.Optimize( &walkFull );
|
|
|
|
#endif
|
|
|
|
cost_orig.Add( initTrack );
|
|
|
|
cost_walk.Add( walkFull );
|
|
|
|
|
|
|
|
if( m_mode == RM_Smart || m_mode == RM_Shove )
|
|
|
|
{
|
|
|
|
PNS_LINE l2;
|
|
|
|
|
|
|
|
bool walk_better = cost_orig.IsBetter( cost_walk, 1.5, 10.0 );
|
|
|
|
walk_better = false;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
|
|
|
#if 0
|
2013-09-26 21:53:54 +00:00
|
|
|
printf( "RtTrk width %d %d %d", initTrack.GetWidth(),
|
|
|
|
walkFull.GetWidth(), walkSolids.GetWidth() );
|
|
|
|
printf( "init-coll %d\n", m_currentNode->CheckColliding( &initTrack ) ? 1 : 0 );
|
|
|
|
printf( "total cost: walk cor %.0f len %.0f orig cor %.0f len %.0f walk-better %d\n",
|
|
|
|
cost_walk.GetCornerCost(), cost_walk.GetLengthCost(),
|
|
|
|
cost_orig.GetCornerCost(), cost_orig.GetLengthCost(),
|
|
|
|
walk_better );
|
2013-09-18 17:55:16 +00:00
|
|
|
#endif
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( m_mode == RM_Smart && wf == PNS_WALKAROUND::DONE && walk_better
|
|
|
|
&& walkFull.GetCLine().CPoint( -1 ) == initTrack.GetCLine().CPoint( -1 ) )
|
|
|
|
l2 = walkFull;
|
|
|
|
else if( stat_solids == PNS_WALKAROUND::DONE )
|
|
|
|
l2 = walkSolids;
|
|
|
|
else
|
|
|
|
l2 = initTrack.ClipToNearestObstacle( m_shove->GetCurrentNode() );
|
|
|
|
|
|
|
|
PNS_LINE l( m_tail );
|
|
|
|
l.GetLine().Append( l2.GetCLine() );
|
|
|
|
l.GetLine().Simplify();
|
|
|
|
|
|
|
|
if( m_placingVia )
|
|
|
|
{
|
|
|
|
PNS_LAYERSET allLayers( 0, 15 );
|
|
|
|
PNS_VIA v1( l.GetCLine().CPoint( -1 ), allLayers, m_viaDiameter );
|
|
|
|
PNS_VIA v2( l2.GetCLine().CPoint( -1 ), allLayers, m_viaDiameter );
|
|
|
|
v1.SetDrill( m_viaDrill );
|
|
|
|
v2.SetDrill( m_viaDrill );
|
|
|
|
|
|
|
|
l.AppendVia( v1 );
|
|
|
|
l2.AppendVia( v2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
PNS_SHOVE::ShoveStatus status = m_shove->ShoveLines( &l );
|
|
|
|
m_currentNode = m_shove->GetCurrentNode();
|
|
|
|
|
|
|
|
if( status == PNS_SHOVE::SH_OK )
|
|
|
|
{
|
|
|
|
optimizer.SetWorld( m_currentNode );
|
|
|
|
optimizer.ClearCache();
|
|
|
|
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS );
|
|
|
|
optimizer.SetCollisionMask( -1 );
|
|
|
|
optimizer.Optimize( &l2 );
|
|
|
|
|
|
|
|
aNewHead = l2;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
walkaround.SetWorld( m_currentNode );
|
|
|
|
walkaround.SetSolidsOnly( false );
|
|
|
|
walkaround.SetIterationLimit( 10 );
|
|
|
|
walkaround.SetApproachCursor( true, aP );
|
|
|
|
walkaround.Route( initTrack, l2 );
|
|
|
|
aNewHead = l2.ClipToNearestObstacle( m_shove->GetCurrentNode() );
|
|
|
|
// aNewHead = l2;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::optimizeTailHeadTransition()
|
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
SHAPE_LINE_CHAIN& head = m_head.GetLine();
|
|
|
|
SHAPE_LINE_CHAIN& tail = m_tail.GetLine();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
const int TailLookbackSegments = 5;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-12-13 16:27:30 +00:00
|
|
|
int threshold = std::min( tail.PointCount(), TailLookbackSegments + 1 );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( tail.SegmentCount() < 3 )
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
// assemble TailLookbackSegments tail segments with the current head
|
|
|
|
SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
opt_line.Append( head );
|
|
|
|
// opt_line.Simplify();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
PNS_LINE new_head( m_tail, opt_line );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
// and see if it could be made simpler by merging obtuse/collnear segments.
|
2013-10-14 11:43:57 +00:00
|
|
|
// If so, replace the (threshold) last tail points and the head with
|
2013-09-26 21:53:54 +00:00
|
|
|
// the optimized line
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
// if(PNS_OPTIMIZER::Optimize(&new_head, PNS_OPTIMIZER::MERGE_SEGMENTS))
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( new_head.MergeSegments() )
|
|
|
|
{
|
|
|
|
PNS_LINE tmp( m_tail, opt_line );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
TRACE( 0, "Placer: optimize tail-head [%d]", threshold );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
head.Clear();
|
|
|
|
tail.Replace( -threshold, -1, new_head.GetCLine() );
|
|
|
|
tail.Simplify();
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
m_p_start = new_head.GetCLine().CPoint( -1 );
|
|
|
|
m_direction = DIRECTION_45( new_head.GetCLine().CSegment( -1 ) );
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
void PNS_LINE_PLACER::routeStep( const VECTOR2I& aP )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
bool fail = false;
|
|
|
|
bool go_back = false;
|
|
|
|
|
|
|
|
int i, n_iter = 1;
|
|
|
|
|
|
|
|
PNS_LINE new_head;
|
|
|
|
|
|
|
|
m_follow_mouse = true;
|
|
|
|
|
|
|
|
TRACE( 2, "INIT-DIR: %s head: %d, tail: %d segs\n",
|
|
|
|
m_initial_direction.Format().c_str() % m_head.GetCLine().SegmentCount() %
|
|
|
|
m_tail.GetCLine().SegmentCount() );
|
|
|
|
|
|
|
|
for( i = 0; i < n_iter; i++ )
|
|
|
|
{
|
|
|
|
if( !go_back && m_follow_mouse )
|
|
|
|
reduceTail( aP );
|
|
|
|
|
|
|
|
go_back = false;
|
|
|
|
|
|
|
|
if( !routeHead( aP, new_head, true ) )
|
|
|
|
fail = true;
|
|
|
|
|
|
|
|
if( !new_head.Is45Degree() )
|
|
|
|
fail = true;
|
|
|
|
|
|
|
|
if( !m_follow_mouse )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_head = new_head;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( handleSelfIntersections() )
|
|
|
|
{
|
|
|
|
n_iter++;
|
|
|
|
go_back = true;
|
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( !go_back && handlePullback() )
|
|
|
|
{
|
|
|
|
n_iter++;
|
|
|
|
go_back = true;
|
|
|
|
}
|
|
|
|
}
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
if( !fail )
|
|
|
|
{
|
|
|
|
if( optimizeTailHeadTransition() )
|
|
|
|
return;
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
mergeHead();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PNS_LINE_PLACER::Route( const VECTOR2I& aP )
|
|
|
|
{
|
|
|
|
if( m_smooth_mouse )
|
|
|
|
{
|
|
|
|
VECTOR2I p_cur = m_p_start;
|
|
|
|
VECTOR2I step = (aP - m_p_start).Resize( m_smoothing_step );
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if( (p_cur - aP).EuclideanNorm() <= m_smoothing_step )
|
|
|
|
p_cur = aP;
|
|
|
|
else
|
|
|
|
p_cur += step;
|
|
|
|
|
|
|
|
routeStep( p_cur );
|
|
|
|
} while( p_cur != aP );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
routeStep( aP );
|
|
|
|
|
|
|
|
return CurrentEnd() == aP;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const PNS_LINE PNS_LINE_PLACER::GetTrace() const
|
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
PNS_LINE tmp( m_head );
|
|
|
|
|
|
|
|
tmp.SetShape( m_tail.GetCLine() );
|
|
|
|
tmp.GetLine().Append( m_head.GetCLine() );
|
|
|
|
tmp.GetLine().Simplify();
|
|
|
|
return tmp;
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
2013-09-18 17:55:16 +00:00
|
|
|
void PNS_LINE_PLACER::FlipPosture()
|
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
m_initial_direction = m_initial_direction.Right();
|
|
|
|
m_direction = m_direction.Right();
|
|
|
|
}
|
|
|
|
|
2013-09-18 17:55:16 +00:00
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
void PNS_LINE_PLACER::GetUpdatedItems( PNS_NODE::ItemVector& aRemoved,
|
|
|
|
PNS_NODE::ItemVector& aAdded )
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
return m_shove->GetCurrentNode()->GetUpdatedItems( aRemoved, aAdded );
|
2013-09-18 17:55:16 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:53:54 +00:00
|
|
|
|
|
|
|
PNS_NODE* PNS_LINE_PLACER::GetCurrentNode() const
|
2013-09-18 17:55:16 +00:00
|
|
|
{
|
2013-09-26 21:53:54 +00:00
|
|
|
return m_shove->GetCurrentNode();
|
|
|
|
}
|