kicad/pcbnew/router/pns_line.cpp

705 lines
15 KiB
C++

/*
* KiRouter - a push-and-(sometimes-)shove PCB router
*
* Copyright (C) 2013 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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.
*
* 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, see <http://www.gnu.or/licenses/>.
*/
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <math/vector2d.h>
#include "pns_line.h"
#include "pns_node.h"
#include "pns_via.h"
#include "pns_utils.h"
#include "pns_router.h"
using namespace std;
using boost::optional;
PNS_LINE *PNS_LINE::Clone() const
{
PNS_LINE *l = new PNS_LINE();
l->m_line = m_line;
l->m_width = m_width;
l->m_layers = m_layers;
l->m_net = m_net;
l->m_movable = m_movable;
l->m_segmentRefs = NULL;
l->m_hasVia = m_hasVia;
l->m_via = m_via;
return l;
}
PNS_LINE *PNS_LINE::CloneProperties() const
{
PNS_LINE *l = new PNS_LINE();
l->m_width = m_width;
l->m_layers = m_layers;
l->m_net = m_net;
l->m_movable = m_movable;
return l;
}
PNS_SEGMENT *PNS_SEGMENT::Clone() const
{
PNS_SEGMENT *s = new PNS_SEGMENT;
s->m_width = m_width;
s->m_net = m_net;
s->m_shape = m_shape;
s->m_layers = m_layers;
return s; //assert(false);
}
#if 1
bool PNS_LINE::MergeObtuseSegments( )
{
int step = m_line.PointCount() - 3;
int iter = 0;
int segs_pre = m_line.SegmentCount();
if(step < 0)
return false;
SHAPE_LINE_CHAIN current_path (m_line);
while(1)
{
iter++;
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if(step > max_step)
step = max_step;
if(step < 2)
{
m_line = current_path;
return current_path.SegmentCount() < segs_pre;
}
bool found_anything = false;
int n = 0;
while (n < n_segs - step)
{
const SEG s1 = current_path.CSegment(n);
const SEG s2 = current_path.CSegment(n + step);
SEG s1opt, s2opt;
if (DIRECTION_45(s1).IsObtuse(DIRECTION_45(s2)))
{
VECTOR2I ip = *s1.IntersectLines(s2);
if(s1.Distance(ip) <= 1 || s2.Distance(ip) <= 1)
{
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
} else {
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
}
if (DIRECTION_45(s1opt).IsObtuse(DIRECTION_45(s2opt)))
{
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1opt.a);
opt_path.Append(s1opt.b);
opt_path.Append(s2opt.b);
PNS_LINE opt_track (*this, opt_path);
if(!m_world->CheckColliding(&opt_track, PNS_ITEM::ANY))
{
current_path.Replace(s1.Index() + 1, s2.Index(), ip);
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++;
}
if(!found_anything)
{
if( step <= 2 )
{
m_line = current_path;
return m_line.SegmentCount() < segs_pre;
}
step --;
}
}
return m_line.SegmentCount() < segs_pre;
}
bool PNS_LINE::MergeSegments( )
{
int step = m_line.PointCount() - 3;
int iter = 0;
int segs_pre = m_line.SegmentCount();
if(step < 0)
return false;
SHAPE_LINE_CHAIN current_path (m_line);
while(1)
{
iter++;
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if(step > max_step)
step = max_step;
if(step < 2)
{
m_line = current_path;
return current_path.SegmentCount() < segs_pre;
}
bool found_anything = false;
int n = 0;
while (n < n_segs - step)
{
const SEG s1 = current_path.CSegment(n);
const SEG s2 = current_path.CSegment(n + step);
SEG s1opt, s2opt;
if(n > 0)
{
SHAPE_LINE_CHAIN path_straight = DIRECTION_45().BuildInitialTrace(s1.a, s2.a, false);
SHAPE_LINE_CHAIN path_diagonal = DIRECTION_45().BuildInitialTrace(s1.a, s2.a, true);
}
if (DIRECTION_45(s1) == DIRECTION_45(s2))
{
if(s1.Collinear(s2))
{
//printf("Colinear: np %d step %d n1 %d n2 %d\n", n_segs, step, n, n+step);
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1.a);
opt_path.Append(s2.b);
PNS_LINE tmp (*this, opt_path);
if(!m_world->CheckColliding(&tmp, PNS_ITEM::ANY))
{
current_path.Remove(s1.Index() + 1, s2.Index());
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
else if (DIRECTION_45(s1).IsObtuse(DIRECTION_45(s2)))
{
VECTOR2I ip = *s1.IntersectLines(s2);
if(s1.Distance(ip) <= 1 || s2.Distance(ip) <= 1)
{
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
} else {
s1opt = SEG(s1.a, ip);
s2opt = SEG(ip, s2.b);
}
if (DIRECTION_45(s1opt).IsObtuse(DIRECTION_45(s2opt)))
{
SHAPE_LINE_CHAIN opt_path;
opt_path.Append(s1opt.a);
opt_path.Append(s1opt.b);
opt_path.Append(s2opt.b);
PNS_LINE opt_track (*this, opt_path);
if(!m_world->CheckColliding(&opt_track, PNS_ITEM::ANY))
{
current_path.Replace(s1.Index() + 1, s2.Index(), ip);
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++;
}
if(!found_anything)
{
if( step <= 2 )
{
m_line = current_path;
return m_line.SegmentCount() < segs_pre;
}
step --;
}
}
return m_line.SegmentCount() < segs_pre;
}
#endif
int PNS_LINE::CountCorners(int aAngles)
{
int count = 0;
for(int i = 0; i < m_line.SegmentCount() - 1; i ++)
{
const SEG seg1 = m_line.CSegment(i);
const SEG seg2 = m_line.CSegment(i + 1);
const DIRECTION_45 dir1(seg1);
const DIRECTION_45 dir2(seg2);
DIRECTION_45::AngleType a = dir1.Angle(dir2);
if(a & aAngles)
count++;
}
return count;
}
//#define DUMP_TEST_CASES
// fixme: damn f*****g inefficient and incredibly crappily written
void PNS_LINE::NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPrePath,
SHAPE_LINE_CHAIN& aWalkaroundPath,
SHAPE_LINE_CHAIN& aPostPath,
bool aCw ) const
{
typedef SHAPE_LINE_CHAIN::Intersection Intersection;
SHAPE_LINE_CHAIN l_orig(m_line);
SHAPE_LINE_CHAIN l_hull;
vector<bool> outside, on_edge, inside;
SHAPE_LINE_CHAIN path;
vector<Intersection> isects;
// don't calculate walkaround for empty lines
if(m_line.PointCount() < 2)
return;
#ifdef DUMP_TEST_CASES
printf("%s\n", m_line.Format().c_str());
printf("%s\n", aObstacle.Format().c_str());
#endif
aObstacle.Intersect(m_line, isects);
//printf("NewWalk intersectiosn :%d\n" ,isects.size());
if( !aCw )
l_hull = aObstacle.Reverse();
else
l_hull = aObstacle;
BOOST_FOREACH( Intersection isect, isects )
{
l_orig.Split(isect.p);
l_hull.Split(isect.p);
}
#ifdef DUMP_TEST_CASES
printf("%s\n", m_line.Format().c_str());
printf("%s\n", aObstacle.Format().c_str());
printf("%s\n", l_orig.Format().c_str());
printf("%s\n", l_hull.Format().c_str());
#endif
//printf("Pts: line %d hull %d\n", l_orig.PointCount(), l_hull.PointCount());
int first_post = -1;
int last_pre = -1;
for (int i = 0; i < l_orig.PointCount(); i++)
{
int ei = l_hull.Find(l_orig.CPoint(i)) ;
bool edge = ei >= 0;
bool in = l_hull.PointInside( l_orig.CPoint(i)) && !edge;
bool out = !( in || edge);
outside.push_back( out );
on_edge.push_back( edge );
inside.push_back( in );
}
for(int i = l_orig.PointCount() - 1; i >= 1; i --)
if(inside[i] && outside[i-1])
{
SHAPE_LINE_CHAIN::Intersections ips;
l_hull.Intersect( SEG( l_orig.CPoint(i), l_orig.CPoint(i - 1) ), ips );
l_orig.Remove(i, -1);
l_orig.Append(ips[0].p);
break;
}
else if (inside[i] && on_edge[i-1])
{
l_orig.Remove(i, -1);
//n = i;
} else if(!inside[i])
break;
if(!outside.size() && on_edge.size() < 2)
return;
for (int i = 0; i < l_orig.PointCount(); i++)
{
const VECTOR2I p = l_orig.Point(i);
if( outside[i] || (on_edge[i] && i == (l_orig.PointCount() - 1)))
{
if(last_pre < 0)
aPrePath.Append(p);
path.Append(p);
}
else if ( on_edge[i] )
{
int li = -1;
if(last_pre < 0)
{
aPrePath.Append(p);
last_pre = path.PointCount();
}
if( i == l_orig.PointCount() - 1 || outside[i+1])
{
path.Append(p);
}
else
{
int vi2 = l_hull.Find( l_orig.CPoint(i) );
path.Append(l_hull.CPoint(vi2));
for(int j = (vi2 + 1) % l_hull.PointCount(); j != vi2; j = (j + 1) % l_hull.PointCount())
{
path.Append(l_hull.CPoint(j));
li = l_orig.Find(l_hull.CPoint(j));
if(li >= 0 && (li == (l_orig.PointCount() - 1) || outside[li+1]))
break;
}
if(li >= 0) {
if(i >= li)
break;
else {
i = li;
}
}
}
first_post = path.PointCount() - 1;
}
}
if(last_pre < 0 && first_post < 0)
return;
aWalkaroundPath = path.Slice( last_pre, first_post );
if(first_post >= 0)
aPostPath = path.Slice( first_post, -1 );
}
bool PNS_LINE::onEdge(const SHAPE_LINE_CHAIN &obstacle, VECTOR2I p, int& ei, bool& is_vertex) const
{
int vtx = obstacle.Find(p);
if(vtx >= 0)
{
ei = vtx;
is_vertex =true;
return true;
}
for(int s = 0; s< obstacle.SegmentCount(); s++)
{
if(obstacle.CSegment(s).Contains(p))
{
ei = s;
is_vertex = false;
return true;
}
}
return false;
}
bool PNS_LINE::walkScan(const SHAPE_LINE_CHAIN &line, const SHAPE_LINE_CHAIN &obstacle, bool reverse, VECTOR2I &ip, int& index_o, int& index_l, bool& is_vertex) const
{
int sc = line.SegmentCount();
for(int i = 0; i < line.SegmentCount(); i++)
{
printf("check-seg rev %d %d/%d %d\n",reverse, i, sc, sc - 1 - i);
SEG tmp = line.CSegment(reverse ? sc - 1 - i : i);
SEG s (tmp.a, tmp.b);
if(reverse)
{
s.a = tmp.b;
s.b = tmp.a;
}
if(onEdge(obstacle, s.a, index_o, is_vertex))
{
index_l = (reverse ? sc-1-i : i);
ip = s.a;
printf("vertex %d on-%s %d\n", index_l, is_vertex?"vertex":"edge",index_o);
return true;
}
if(onEdge(obstacle, s.b, index_o, is_vertex))
{
index_l = (reverse? sc-1-i-1 : i + 1);
ip = s.b;
printf("vertex %d on-%s %d\n", index_l, is_vertex?"vertex":"edge",index_o);
return true;
}
SHAPE_LINE_CHAIN::Intersections ips;
int n_is = obstacle.Intersect ( s, ips );
if (n_is > 0)
{
index_o = ips[0].our.Index();
index_l = reverse ?sc-1-i:i;
printf("segment-%d intersects edge-%d\n", index_l, index_o);
ip = ips[0].p;
return true;
}
}
return false;
}
bool PNS_LINE::Walkaround(SHAPE_LINE_CHAIN obstacle, SHAPE_LINE_CHAIN &pre, SHAPE_LINE_CHAIN &walk, SHAPE_LINE_CHAIN &post, bool cw) const
{
const SHAPE_LINE_CHAIN &line = GetCLine();
VECTOR2I ip_start;
int index_o_start, index_l_start;
VECTOR2I ip_end;
int index_o_end, index_l_end;
bool is_vertex_start, is_vertex_end;
if(line.SegmentCount() < 1)
return false;
if(obstacle.PointInside(line.CPoint(0)) || obstacle.PointInside(line.CPoint(-1)))
return false;
// printf("forward:\n");
bool found = walkScan(line, obstacle, false, ip_start, index_o_start, index_l_start, is_vertex_start);
//printf("reverse:\n");
found |= walkScan(line, obstacle, true, ip_end, index_o_end, index_l_end, is_vertex_end);
if(!found || ip_start == ip_end)
{
pre = line;
return true;
}
pre = line.Slice ( 0, index_l_start );
pre.Append (ip_start);
walk.Clear();
walk.Append(ip_start);
if(cw)
{
int is = (index_o_start + 1) % obstacle.PointCount();
int ie = (is_vertex_end ? index_o_end : index_o_end + 1) % obstacle.PointCount();
while(1)
{
printf("is %d\n", is);
walk.Append(obstacle.CPoint(is));
if(is == ie)
break;
is ++;
if (is == obstacle.PointCount() )
is = 0;
}
} else {
int is = index_o_start;
int ie = (is_vertex_end ? index_o_end : index_o_end) % obstacle.PointCount();
while(1)
{
printf("is %d\n", is);
walk.Append(obstacle.CPoint(is));
if(is == ie)
break;
is --;
if ( is < 0 )
is = obstacle.PointCount() - 1;
}
}
walk.Append(ip_end);
post.Clear();
post.Append(ip_end);
post.Append(line.Slice (is_vertex_end ? index_l_end : index_l_end + 1 , -1));
//for(int i = (index_o_start + 1) % obstacle.PointCount();
// i != (index_o_end + 1) % obstacle.PointCount(); i=(i+1) % obstacle.PointCount())
//{
//printf("append %d\n", i);
//walk.Append(obstacle.CPoint(i));
//}
return true;
}
void PNS_LINE::NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const
{
SHAPE_LINE_CHAIN walk, post;
NewWalkaround(aObstacle, aPath, walk, post, aCw);
aPath.Append(walk);
aPath.Append(post);
aPath.Simplify();
}
void PNS_LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const
{
SHAPE_LINE_CHAIN walk, post;
Walkaround(aObstacle, aPath, walk, post, aCw);
aPath.Append(walk);
aPath.Append(post);
aPath.Simplify();
}
const SHAPE_LINE_CHAIN PNS_SEGMENT::Hull(int aClearance, int aWalkaroundThickness) const
{
int d = aClearance + 10;
int x = (int) (2.0 / (1.0 + M_SQRT2) * d) + 2;
const VECTOR2I a = m_shape.CPoint(0);
const VECTOR2I b = m_shape.CPoint(1);
VECTOR2I dir = b - a;
VECTOR2I p0 = dir.Perpendicular().Resize(d);
VECTOR2I ds = dir.Perpendicular().Resize(x / 2);
VECTOR2I pd = dir.Resize(x / 2);
VECTOR2I dp = dir.Resize(d);
SHAPE_LINE_CHAIN s;
s.SetClosed( true );
s.Append(b + p0 + pd);
s.Append(b + dp + ds);
s.Append(b + dp - ds);
s.Append(b - p0 + pd);
s.Append(a - p0 - pd);
s.Append(a - dp - ds);
s.Append(a - dp + ds);
s.Append(a + p0 - pd);
// make sure the hull outline is always clockwise
if(s.CSegment(0).Side(a) < 0)
return s.Reverse();
else
return s;
}
bool PNS_LINE::Is45Degree()
{
for(int i = 0; i < m_line.SegmentCount(); i++)
{
const SEG &s = m_line.CSegment(i);
double angle = 180.0 / M_PI * atan2((double)s.b.y - (double)s.a.y, (double)s.b.x - (double)s.a.x);
if(angle < 0)
angle+=360.0;
double angle_a = fabs(fmod(angle, 45.0));
if(angle_a > 1.0 && angle_a < 44.0)
return false;
}
return true;
}
const PNS_LINE PNS_LINE::ClipToNearestObstacle( PNS_NODE *aNode ) const
{
PNS_LINE l (*this);
PNS_NODE::OptObstacle obs = aNode->NearestObstacle ( &l );
if(obs)
{
l.RemoveVia();
int p = l.GetLine().Split(obs -> ip_first);
l.GetLine().Remove(p + 1, -1);
}
return l;
}
void PNS_LINE::ShowLinks()
{
if(!m_segmentRefs)
{
printf("line %p: no links\n", this);
return;
}
printf("line %p: %d linked segs\n", this, m_segmentRefs->size());
for (int i= 0; i<(int)m_segmentRefs->size(); i++) printf("seg %d: %p\n", i, (*m_segmentRefs)[i]) ;
}