common: minimum version of the shape library

This commit is contained in:
tomasz.wlostowski@cern.ch 2013-09-10 13:43:09 +02:00
parent b0357ce9a7
commit 6ac8188f7f
10 changed files with 2372 additions and 0 deletions

View File

@ -162,6 +162,10 @@ set(COMMON_SRCS
tool/tool_event.cpp
tool/tool_interactive.cpp
tool/context_menu.cpp
geometry/seg.cpp
geometry/shape_line_chain.cpp
geometry/shape_collisions.cpp
)
add_library(common STATIC ${COMMON_SRCS})

150
common/geometry/seg.cpp Normal file
View File

@ -0,0 +1,150 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <geometry/seg.h>
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
bool SEG::PointCloserThan (const VECTOR2I& aP, int dist) const
{
VECTOR2I d = b - a;
ecoord dist_sq = (ecoord) dist * dist;
SEG::ecoord l_squared = d.Dot(d);
SEG::ecoord t = d.Dot(aP - a);
if( t <= 0 || !l_squared )
return (aP - a).SquaredEuclideanNorm() < dist_sq;
else if( t >= l_squared )
return (aP - b).SquaredEuclideanNorm() < dist_sq;
int dxdy = abs(d.x) - abs(d.y);
if( (dxdy >= -1 && dxdy <= 1) || abs(d.x) <= 1 || abs(d.y) <= 1)
{
int ca = -sgn(d.y);
int cb = sgn(d.x);
int cc = -ca * a.x - cb * a.y;
ecoord num = ca * aP.x + cb * aP.y + cc;
num *= num;
if(ca && cb)
num >>= 1;
if(num > (dist_sq + 100))
return false;
else if(num < (dist_sq - 100))
return true;
}
VECTOR2I nearest;
nearest.x = a.x + rescale(t, (ecoord)d.x, l_squared);
nearest.y = a.y + rescale(t, (ecoord)d.y, l_squared);
return (nearest - aP).SquaredEuclideanNorm() <= dist_sq;
}
SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const
{
// fixme: rather inefficient....
if(Intersect(aSeg))
return 0;
const VECTOR2I pts[4] =
{
aSeg.NearestPoint(a) - a,
aSeg.NearestPoint(b) - b,
NearestPoint(aSeg.a) - aSeg.a,
NearestPoint(aSeg.b) - aSeg.b
};
ecoord m = VECTOR2I::ECOORD_MAX;
for (int i = 0; i<4 ; i++)
m = std::min(m, pts[i].SquaredEuclideanNorm());
return m;
}
OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const
{
const VECTOR2I e (b - a);
const VECTOR2I f (aSeg.b - aSeg.a);
const VECTOR2I ac (aSeg.a - a);
ecoord d = f.Cross(e);
ecoord p = f.Cross(ac);
ecoord q = e.Cross(ac);
if(d == 0)
return OPT_VECTOR2I();
if (!aLines && d > 0 && (q < 0 || q > d || p < 0 || p > d))
return OPT_VECTOR2I();
if (!aLines && d < 0 && (q < d || p < d || p > 0 || q > 0))
return OPT_VECTOR2I();
if (!aLines && aIgnoreEndpoints && (q == 0 || q == d) && (p == 0 || p == d))
return OPT_VECTOR2I();
VECTOR2I ip ( aSeg.a.x + rescale(q, (ecoord)f.x, d),
aSeg.a.y + rescale(q, (ecoord)f.y, d) );
return ip;
}
bool SEG::ccw ( const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I &c ) const
{
return (ecoord)(c.y - a.y) * (b.x - a.x) > (ecoord)(b.y - a.y) * (c.x - a.x);
}
bool SEG::Collide( const SEG& aSeg, int aClearance ) const
{
// check for intersection
// fixme: move to a method
if( ccw(a,aSeg.a,aSeg.b) != ccw(b,aSeg.a,aSeg.b) && ccw(a,b,aSeg.a) != ccw(a,b,aSeg.b) )
return true;
#define CHK(_seg, _pt) \
if( (_seg).PointCloserThan (_pt, aClearance ) ) return true;
CHK(*this, aSeg.a);
CHK(*this, aSeg.b);
CHK(aSeg, a);
CHK(aSeg, b);
#undef CHK
return false;
}
bool SEG::Contains(const VECTOR2I& aP) const
{
return PointCloserThan(aP, 1);
}

View File

@ -0,0 +1,210 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <math/vector2d.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_rect.h>
typedef typename VECTOR2I::extended_type ecoord;
static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_CIRCLE& b, int clearance, bool needMTV, VECTOR2I& aMTV )
{
ecoord min_dist = clearance + a.GetRadius() + b.GetRadius();
ecoord min_dist_sq = min_dist * min_dist;
const VECTOR2I delta = b.GetCenter() - a.GetCenter();
ecoord dist_sq = delta.SquaredEuclideanNorm();
if ( dist_sq >= min_dist_sq )
return false;
if ( needMTV )
aMTV = delta.Resize( sqrt (abs(min_dist_sq - dist_sq)) + 1);
return true;
}
static inline bool Collide( const SHAPE_RECT& a, const SHAPE_CIRCLE& b, int clearance, bool needMTV, VECTOR2I& aMTV )
{
const VECTOR2I c = b.GetCenter();
const VECTOR2I p0 = a.GetPosition();
const VECTOR2I size = a.GetSize();
const ecoord r = b.GetRadius();
const ecoord min_dist = clearance + r;
const ecoord min_dist_sq = min_dist * min_dist;
if (a.BBox(0).Contains(c))
return true;
const VECTOR2I vts[] = {
VECTOR2I(p0.x, p0.y),
VECTOR2I(p0.x, p0.y + size.y),
VECTOR2I(p0.x + size.x, p0.y + size.y),
VECTOR2I(p0.x + size.x, p0.y),
VECTOR2I(p0.x, p0.y) };
ecoord nearest_seg_dist_sq = VECTOR2I::ECOORD_MAX;
VECTOR2I nearest;
bool inside = c.x >= p0.x && c.x <= (p0.x + size.x)
&& c.y >= p0.y && c.y <= (p0.y + size.y);
if(!inside)
{
for (int i = 0; i < 4; i++)
{
const SEG seg (vts[i], vts[i+1]);
ecoord dist_sq = seg.SquaredDistance ( c );
if(dist_sq < min_dist_sq)
{
if(!needMTV)
return true;
else
{
nearest = seg.NearestPoint ( c );
nearest_seg_dist_sq = dist_sq;
}
}
}
}
if(nearest_seg_dist_sq >= min_dist_sq && !inside)
return false;
VECTOR2I delta = c - nearest;
if(!needMTV)
return true;
if(inside)
aMTV = -delta.Resize(sqrt(abs(r * r + nearest_seg_dist_sq) + 1));
else
aMTV = delta.Resize(sqrt(abs(r * r - nearest_seg_dist_sq) + 1));
return true;
}
static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV )
{
for (int s = 0; s < b.SegmentCount(); s++)
{
if ( a.Collide (b.CSegment(s), clearance))
return true;
}
return false;
}
static inline bool Collide( const SHAPE_LINE_CHAIN& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV )
{
for( int i = 0; i < b.SegmentCount() ;i++)
if(a.Collide(b.CSegment(i), clearance))
return true;
return false;
}
static inline bool Collide( const SHAPE_RECT& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV )
{
for (int s = 0; s < b.SegmentCount(); s++)
{
SEG seg = b.CSegment(s);
if ( a.Collide (seg, clearance))
return true;
}
return false;
}
bool CollideShapes ( const SHAPE *a, const SHAPE *b, int clearance, bool needMTV, VECTOR2I& aMTV )
{
switch(a->Type())
{
case SH_RECT:
switch(b->Type())
{
case SH_CIRCLE:
return Collide( *static_cast<const SHAPE_RECT *> (a), *static_cast<const SHAPE_CIRCLE *> (b), clearance, needMTV, aMTV );
case SH_LINE_CHAIN:
return Collide( *static_cast<const SHAPE_RECT *> (a), *static_cast<const SHAPE_LINE_CHAIN *> (b), clearance, needMTV, aMTV );
default:
break;
}
case SH_CIRCLE:
switch(b->Type())
{
case SH_RECT:
return Collide( *static_cast<const SHAPE_RECT *> (b), *static_cast<const SHAPE_CIRCLE *> (a), clearance, needMTV, aMTV );
case SH_CIRCLE:
return Collide( *static_cast<const SHAPE_CIRCLE *> (a), *static_cast<const SHAPE_CIRCLE *> (b), clearance, needMTV, aMTV );
case SH_LINE_CHAIN:
return Collide( *static_cast<const SHAPE_CIRCLE *> (a), *static_cast<const SHAPE_LINE_CHAIN *> (b), clearance, needMTV, aMTV );
default:
break;
}
case SH_LINE_CHAIN:
switch(b->Type())
{
case SH_RECT:
return Collide( *static_cast<const SHAPE_RECT *> (b), *static_cast<const SHAPE_LINE_CHAIN *> (a), clearance, needMTV, aMTV );
case SH_CIRCLE:
return Collide( *static_cast<const SHAPE_CIRCLE *> (b), *static_cast<const SHAPE_LINE_CHAIN *> (a), clearance, needMTV, aMTV );
case SH_LINE_CHAIN:
return Collide( *static_cast<const SHAPE_LINE_CHAIN *> (a), *static_cast<const SHAPE_LINE_CHAIN *> (b), clearance, needMTV, aMTV );
default:
break;
}
default:
break;
}
bool unsupported_collision = true;
assert(unsupported_collision == false);
return false;
}
bool SHAPE::Collide ( const SHAPE *aShape, int aClerance, VECTOR2I& aMTV ) const
{
return CollideShapes( this, aShape, aClerance, true, aMTV);
}
bool SHAPE::Collide ( const SHAPE *aShape, int aClerance ) const
{
VECTOR2I dummy;
return CollideShapes( this, aShape, aClerance, false, dummy);
}

View File

@ -0,0 +1,465 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h>
using namespace std;
using boost::optional;
bool SHAPE_LINE_CHAIN::Collide ( const VECTOR2I& aP, int aClearance ) const
{
assert(false);
return false;
}
bool SHAPE_LINE_CHAIN::Collide ( const BOX2I& aBox, int aClearance ) const
{
assert(false);
return false;
}
bool SHAPE_LINE_CHAIN::Collide ( const SEG& aSeg, int aClearance ) const
{
BOX2I box_a(aSeg.a, aSeg.b - aSeg.a);
BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance;
for( int i = 0; i < SegmentCount() ;i++)
{
const SEG& s = CSegment(i);
BOX2I box_b(s.a, s.b - s.a);
BOX2I::ecoord_type d = box_a.SquaredDistance ( box_b );
if(d < dist_sq)
{
if(s.Collide(aSeg, aClearance))
return true;
}
}
return false;
}
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
{
SHAPE_LINE_CHAIN a (*this);
reverse(a.m_points.begin(), a.m_points.end());
a.m_closed = m_closed;
return a;
}
int SHAPE_LINE_CHAIN::Length() const
{
int l = 0;
for (int i = 0; i < SegmentCount(); i++)
l += CSegment(i).Length();
return l;
}
void SHAPE_LINE_CHAIN::Replace( int start_index, int end_index, const VECTOR2I& aP)
{
if(end_index < 0)
end_index += PointCount();
if(start_index < 0)
start_index += PointCount();
if (start_index == end_index)
m_points [start_index] = aP;
else {
m_points.erase (m_points.begin() + start_index + 1, m_points.begin() + end_index + 1);
m_points [start_index] = aP;
}
}
void SHAPE_LINE_CHAIN::Replace( int start_index, int end_index, const SHAPE_LINE_CHAIN& aLine)
{
if(end_index < 0)
end_index += PointCount();
if(start_index < 0)
start_index += PointCount();
m_points.erase (m_points.begin() + start_index, m_points.begin() + end_index + 1);
m_points.insert (m_points.begin() + start_index, aLine.m_points.begin(), aLine.m_points.end());
}
void SHAPE_LINE_CHAIN::Remove( int start_index, int end_index)
{
if(end_index < 0)
end_index += PointCount();
if(start_index < 0)
start_index += PointCount();
m_points.erase (m_points.begin() + start_index, m_points.begin() + end_index + 1);
}
int SHAPE_LINE_CHAIN::Distance( const VECTOR2I & aP ) const
{
int d = INT_MAX;
for (int s = 0; s < SegmentCount(); s++)
d = min (d, CSegment(s).Distance(aP));
return d;
}
int SHAPE_LINE_CHAIN::Split( const VECTOR2I & aP )
{
int ii = -1;
int min_dist = 2;
ii = Find(aP);
if(ii >= 0)
return ii;
for (int s = 0; s < SegmentCount(); s++)
{
const SEG seg = CSegment(s);
int dist = seg.Distance(aP);
// make sure we are not producing a 'slightly concave' primitive. This might happen
// if aP lies very close to one of already existing points.
if(dist < min_dist && seg.a != aP && seg.b != aP)
{
min_dist = dist;
ii = s;
}
}
if(ii >= 0)
{
m_points.insert(m_points.begin() + ii + 1, aP);
return ii + 1;
}
return -1;
}
int SHAPE_LINE_CHAIN::Find ( const VECTOR2I& aP ) const
{
for (int s = 0; s< PointCount(); s++)
if(CPoint(s) == aP)
return s;
return -1;
}
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int start_index, int end_index ) const
{
SHAPE_LINE_CHAIN rv;
if(end_index < 0)
end_index += PointCount();
if(start_index < 0)
start_index += PointCount();
for(int i = start_index; i<= end_index; i++)
rv.Append(m_points[i]);
return rv;
}
struct compareOriginDistance {
compareOriginDistance( VECTOR2I& aOrigin ):
m_origin(aOrigin) {};
bool operator()(const SHAPE_LINE_CHAIN::Intersection &a, const SHAPE_LINE_CHAIN::Intersection& b)
{
return (m_origin - a.p).EuclideanNorm() < (m_origin - b.p).EuclideanNorm();
}
VECTOR2I m_origin;
};
int SHAPE_LINE_CHAIN::Intersect ( const SEG& aSeg, Intersections& aIp ) const
{
for (int s = 0; s < SegmentCount(); s++)
{
OPT_VECTOR2I p = CSegment(s).Intersect(aSeg);
if(p)
{
Intersection is;
is.our = CSegment(s);
is.their = aSeg;
is.p = *p;
aIp.push_back(is);
}
}
compareOriginDistance comp(aSeg.a);
sort(aIp.begin(), aIp.end(), comp);
return aIp.size();
};
int SHAPE_LINE_CHAIN::Intersect( const SHAPE_LINE_CHAIN &aChain, Intersections& aIp ) const
{
BOX2I bb_other = aChain.BBox();
for (int s1 = 0; s1 < SegmentCount(); s1++)
{
const SEG& a = CSegment(s1);
const BOX2I bb_cur (a.a, a.b - a.a);
if(! bb_other.Intersects( bb_cur ))
continue;
for (int s2 = 0; s2 < aChain.SegmentCount(); s2++)
{
const SEG& b = aChain.CSegment(s2);
Intersection is;
if(a.Collinear(b))
{
if(a.Contains(b.a)) { is.p = b.a; aIp.push_back(is); }
if(a.Contains(b.b)) { is.p = b.b; aIp.push_back(is); }
if(b.Contains(a.a)) { is.p = a.a; aIp.push_back(is); }
if(b.Contains(a.b)) { is.p = a.b; aIp.push_back(is); }
} else {
OPT_VECTOR2I p = a.Intersect(b);
if(p)
{
is.p = *p;
is.our = a;
is.their = b;
aIp.push_back(is);
}
}
}
}
return aIp.size();
for (int s1 = 0; s1 < SegmentCount(); s1++)
for (int s2 = 0; s2 < aChain.SegmentCount(); s2++)
{
const SEG& a = CSegment(s1);
const SEG& b = aChain.CSegment(s2);
OPT_VECTOR2I p = a.Intersect(b);
Intersection is;
if(p)
{
is.p = *p;
is.our = a;
is.their = b;
aIp.push_back(is);
} else if (a.Collinear(b))
{
if(a.a != b.a && a.a != b.b && b.Contains(a.a) )
{
is.p = a.a;
is.our = a;
is.their = b;
aIp.push_back(is);
}
else if(a.b != b.a && a.b != b.b && b.Contains(a.b) )
{
is.p = a.b;
is.our = a;
is.their = b;
aIp.push_back(is);
}
}
}
return aIp.size();
}
int SHAPE_LINE_CHAIN::PathLength (const VECTOR2I& aP ) const
{
int sum = 0;
for (int i = 0; i < SegmentCount(); i++)
{
const SEG seg = CSegment(i);
int d = seg.Distance(aP);
if (d <= 1)
{
sum += (aP - seg.a).EuclideanNorm();
return sum;
}
else
sum += seg.Length();
}
return -1;
}
bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aP) const
{
if(!m_closed || SegmentCount() < 3)
return false;
int cur = CSegment(0).Side(aP);
if(cur == 0)
return false;
for( int i = 1; i < SegmentCount(); i++)
{
const SEG s = CSegment(i);
if(aP == s.a || aP == s.b) // edge does not belong to the interior!
return false;
if (s.Side(aP) != cur)
return false;
}
return true;
}
bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aP) const
{
if(SegmentCount() < 1)
return m_points[0] == aP;
for( int i = 1; i < SegmentCount(); i++)
{
const SEG s = CSegment(i);
if(s.a == aP || s.b == aP)
return true;
if(s.Distance(aP) <= 1)
return true;
}
return false;
}
const optional<SHAPE_LINE_CHAIN::Intersection> SHAPE_LINE_CHAIN::SelfIntersecting() const
{
for (int s1 = 0; s1 < SegmentCount(); s1++)
for (int s2 = s1 + 1; s2 < SegmentCount(); s2++)
{
const VECTOR2I s2a = CSegment(s2).a, s2b = CSegment(s2).b;
if(s1 + 1 != s2 && CSegment(s1).Contains(s2a))
{
Intersection is;
is.our = CSegment(s1);
is.their = CSegment(s2);
is.p = s2a;
return is;
} else if (CSegment(s1).Contains(s2b)) {
Intersection is;
is.our = CSegment(s1);
is.their = CSegment(s2);
is.p = s2b;
return is;
} else {
OPT_VECTOR2I p = CSegment(s1).Intersect(CSegment(s2), true);
if(p)
{
Intersection is;
is.our = CSegment(s1);
is.their = CSegment(s2);
is.p = *p;
return is;
}
}
}
return optional<Intersection>();
}
SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
{
vector<VECTOR2I> pts_unique;
if (PointCount() < 2)
{
return *this;
} else if (PointCount() == 2) {
if(m_points[0] == m_points[1])
m_points.erase(m_points.end());
return *this;
}
int i = 0;
int np = PointCount();
// stage 1: eliminate duplicate vertices
while ( i < np )
{
int j = i+1;
while(j < np && CPoint(i) == CPoint(j))
j ++;
pts_unique.push_back(CPoint(i));
i = j;
}
m_points.clear();
np = pts_unique.size();
i = 0;
// stage 1: eliminate collinear segments
while (i < np - 2)
{
const VECTOR2I p0 = pts_unique[i];
const VECTOR2I p1 = pts_unique[i+1];
int n = i;
while(n < np - 2 && SEG(p0, p1).LineDistance(pts_unique[n + 2]) <= 1)
n++;
m_points.push_back(p0);
if (n > i)
i = n;
if (n == np)
{
m_points.push_back(pts_unique[n-1]);
return *this;
}
i ++;
}
if(np > 1)
m_points.push_back(pts_unique[np-2]);
m_points.push_back(pts_unique[np-1]);
return *this;
}
const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint(const VECTOR2I& aP) const
{
int min_d = INT_MAX;
int nearest;
for ( int i = 0; i < SegmentCount() ; i++ )
{
int d = CSegment(i).Distance(aP);
if( d < min_d )
{
min_d = d;
nearest = i;
}
}
return CSegment(nearest).NearestPoint(aP);
}
const string SHAPE_LINE_CHAIN::Format() const
{
stringstream ss;
ss << m_points.size() << " " << (m_closed ? 1 : 0) << " " ;
for(int i = 0; i<PointCount(); i++)
ss << m_points[i].x << " " << m_points[i].y<<" ";// Format() << " ";
return ss.str();
}

361
include/geometry/seg.h Normal file
View File

@ -0,0 +1,361 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SEG_H
#define __SEG_H
#include <cstdio>
#include <climits>
#include <math/vector2d.h>
#include <boost/optional/optional.hpp>
typedef boost::optional<VECTOR2I> OPT_VECTOR2I;
class SEG {
private:
typedef VECTOR2I::extended_type ecoord;
public:
friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg );
/* Start and the of the segment. Public, to make access simpler. These are references
* to an object the segment belongs to (e.g. a line chain) or references to locally stored points
* (m_a, m_b).
*/
VECTOR2I& a, b;
/** Default constructor
* Creates an empty (0, 0) segment, locally-referenced
*/
SEG(): a(m_a), b(m_b)
{
a = m_a;
b = m_b;
m_is_local = true;
m_index = -1;
}
/**
* Constructor
* Creates a segment between (x1, y1) and (x2, y2), locally referenced
*/
SEG ( int x1, int y1, int x2, int y2 ) : a(m_a), b(m_b)
{
m_a = VECTOR2I(x1, y1);
m_b = VECTOR2I(x2, y2);
a = m_a;
b = m_b;
m_is_local = true;
m_index = -1;
}
/**
* Constructor
* Creates a segment between (aA) and (aB), locally referenced
*/
SEG ( const VECTOR2I& aA, const VECTOR2I& aB ): a(m_a), b(m_b), m_a(aA), m_b(aB)
{
a = m_a;
b = m_b;
m_is_local = true;
m_index = -1;
}
/**
* Constructor
* Creates a segment between (aA) and (aB), referenced to a multi-segment shape
* @param aA reference to the start point in the parent shape
* @param aB reference to the end point in the parent shape
* @param aIndex index of the segment within the parent shape
*/
SEG ( VECTOR2I& aA, VECTOR2I& aB, int aIndex ): a(aA), b(aB)
{
m_is_local = false;
m_index = aIndex;
}
/**
* Copy constructor
*/
SEG ( const SEG& seg ): a(m_a), b(m_b)
{
if (seg.m_is_local)
{
m_a = seg.m_a;
m_b = seg.m_b;
a = m_a;
b = m_b;
m_is_local = true;
m_index = -1;
} else {
a = seg.a;
b = seg.b;
m_index = seg.m_index;
m_is_local = false;
}
}
SEG& operator=(const SEG& seg)
{
a = seg.a;
b = seg.b;
m_a = seg.m_a;
m_b = seg.m_b;
m_index = seg.m_index;
m_is_local = seg.m_is_local;
return *this;
}
/**
* Function LineProject()
*
* Computes the perpendicular projection point of aP on a line passing through
* ends of the segment.
* @param aP point to project
* @return projected point
*/
VECTOR2I LineProject( const VECTOR2I& aP ) const;
/**
* Function Side()
*
* Determines on which side of directed line passing via segment ends point aP lies.
* @param aP point to determine the orientation wrs to self
* @return: < 0: left, 0 : on the line, > 0 : right
*/
int Side( const VECTOR2I& aP ) const
{
const ecoord det = (b - a).Cross(aP - a);
return det < 0 ? -1 : (det > 0 ? 1 : 0);
}
/**
* Function LineDistance()
*
* Returns the closest Euclidean distance between point aP and the line defined by
* the ends of segment (this).
* @param aDetermineSide: when true, the sign of the returned value indicates
* the side of the line at which we are (negative = left)
* @return the distance
*/
int LineDistance( const VECTOR2I& aP, bool aDetermineSide = false ) const;
/**
* Function NearestPoint()
*
* Computes a point on the segment (this) that is closest to point aP.
* @return: nearest point
*/
const VECTOR2I NearestPoint( const VECTOR2I &aP ) const;
/**
* Function Intersect()
*
* Computes intersection point of segment (this) with segment aSeg.
* @param aSeg: segment to intersect with
* @param aIgnoreEndpoints: don't treat corner cases (i.e. end of one segment touching the other)
* as intersections.
* @param aLines: treat segments as infinite lines
* @return intersection point, if exists
*/
OPT_VECTOR2I Intersect( const SEG& aSeg, bool aIgnoreEndpoints = false, bool aLines = false ) const;
/**
* Function IntersectLines()
*
* Computes the intersection point of lines passing through ends of (this) and aSeg
* @param aSeg segment defining the line to intersect with
* @return intersection point, if exists
*/
OPT_VECTOR2I IntersectLines( const SEG& aSeg ) const
{
return Intersect ( aSeg, false, true );
}
bool Collide( const SEG& aSeg, int aClearance ) const;
/**
* Function Distance()
*
* Computes minimum Euclidean distance to segment aSeg.
* @param aSeg other segment
* @return minimum distance
*/
ecoord SquaredDistance( const SEG& aSeg ) const ;
int Distance( const SEG& aSeg ) const
{
return sqrt ( SquaredDistance(aSeg) );
}
/**
* Function Distance()
*
* Computes minimum Euclidean distance to point aP.
* @param aP the point
* @return minimum distance
*/
ecoord SquaredDistance( const VECTOR2I& aP ) const
{
return (NearestPoint(aP) - aP).SquaredEuclideanNorm();
}
int Distance( const VECTOR2I& aP ) const
{
return sqrt ( SquaredDistance( aP) );
}
/**
* Function Collinear()
*
* Checks if segment aSeg lies on the same line as (this).
* @param aSeg the segment to chech colinearity with
* @return true, when segments are collinear.
*/
bool Collinear( const SEG& aSeg ) const
{
ecoord qa1 = a.y - b.y;
ecoord qb1 = b.x - a.x;
ecoord qc1 = -qa1 * a.x - qb1 * a.y;
ecoord qa2 = aSeg.a.y - aSeg.b.y;
ecoord qb2 = aSeg.b.x - aSeg.a.x;
ecoord qc2 = -qa2 * aSeg.a.x - qb2 * aSeg.a.y;
return (qa1 == qa2) && (qb1 == qb2) && (qc1 == qc2);
}
/**
* Function Length()
*
* Returns the length (this)
* @return length
*/
int Length() const
{
return (a - b).EuclideanNorm();
}
/**
* Function Index()
*
* Return the index of this segment in its parent shape (applicable only to non-local segments)
* @return index value
*/
int Index() const
{
return m_index;
}
bool Contains(const VECTOR2I& aP) const;
bool PointCloserThan ( const VECTOR2I& aP, int dist) const;
// friend std::ostream& operator<<( std::ostream& stream, const SEG& aSeg );
private:
bool ccw ( const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I &c ) const;
///> locally stored start/end coordinates (used when m_is_local == true)
VECTOR2I m_a, m_b;
///> index withing the parent shape (used when m_is_local == false)
int m_index;
///> locality flag
bool m_is_local;
};
inline VECTOR2I SEG::LineProject( const VECTOR2I& aP ) const
{
// fixme: numerical errors for large integers
assert(false);
/*const VECTOR2I d = aB - aA;
ecoord det = d.Dot(d);
ecoord dxdy = (ecoord) d.x * d.y;
ecoord qx =
( (extended_type) aA.x * d.y * d.y + (extended_type) d.x * d.x * x - dxdy *
(aA.y - y) ) / det;
extended_type qy =
( (extended_type) aA.y * d.x * d.x + (extended_type) d.y * d.y * y - dxdy *
(aA.x - x) ) / det;
return VECTOR2<T> ( (T) qx, (T) qy );*/
}
inline int SEG::LineDistance( const VECTOR2I& aP, bool aDetermineSide ) const
{
ecoord p = a.y - b.y;
ecoord q = b.x - a.x;
ecoord r = -p * a.x - q * a.y;
ecoord dist = ( p * aP.x + q * aP.y + r ) / sqrt( p * p + q * q );
return aDetermineSide ? dist : abs(dist);
}
inline const VECTOR2I SEG::NearestPoint(const VECTOR2I& aP) const
{
VECTOR2I d = b - a;
ecoord l_squared = d.Dot(d);
if( l_squared == 0 )
return a;
ecoord t = d.Dot(aP - a);
if( t < 0 )
return a;
else if( t > l_squared )
return b;
int xp = rescale(t, (ecoord)d.x, l_squared);
int yp = rescale(t, (ecoord)d.y, l_squared);
return a + VECTOR2I(xp, yp);
}
inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg )
{
if(aSeg.m_is_local)
aStream << "[ local " << aSeg.a << " - " << aSeg.b << " ]";
return aStream;
}
#endif // __SEG_H

141
include/geometry/shape.h Normal file
View File

@ -0,0 +1,141 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SHAPE_H
#define __SHAPE_H
#include <math/vector2d.h>
#include <math/box2.h>
#include <geometry/seg.h>
/**
* Enum ShapeType
* Lists all supported shapes
*/
enum ShapeType {
SH_RECT = 0, ///> axis-aligned rectangle
SH_SEGMENT, ///> line segment
SH_LINE_CHAIN, ///> line chain (polyline)
SH_CIRCLE ///> circle
};
/**
* Class SHAPE
*
* Represents an abstract shape on 2D plane. All SHAPEs implement SHAPE interface.
*/
class SHAPE {
protected:
typedef typename VECTOR2I::extended_type ecoord;
public:
/**
* Constructor
*
* Creates an empty shape of type aType
*/
SHAPE ( ShapeType aType ): m_type( aType ) { };
// Destructor
virtual ~SHAPE() {};
/**
* Function Type()
*
* Returns the type of the shape.
* @retval the type
*/
ShapeType Type() const { return m_type; }
/**
* Function Clone()
*
* Returns a dynamically allocated copy of the shape
* @retval copy of the shape
*/
virtual SHAPE* Clone() const { assert(false); };
/**
* Function Collide()
*
* Checks if the boundary of shape (this) lies closer to the point aP than aClearance, indicating
* a collision.
* @return true, if there is a collision.
*/
virtual bool Collide ( const VECTOR2I& aP, int aClearance = 0 ) const
{
return Collide(SEG(aP, aP), aClearance);
}
/**
* Function Collide()
*
* Checks if the boundary of shape (this) lies closer to the shape aShape than aClearance, indicating
* a collision.
* @return true, if there is a collision.
*/
virtual bool Collide ( const SHAPE *aShape, int aClerance, VECTOR2I& aMTV ) const;
virtual bool Collide ( const SHAPE *aShape, int aClerance = 0 ) const;
/**
* Function Collide()
*
* Checks if the boundary of shape (this) lies closer to the segment aSeg than aClearance, indicating
* a collision.
* @return true, if there is a collision.
*/
virtual bool Collide ( const SEG& aSeg, int aClearance = 0) const = 0;
/**
* Function Collide()
*
* Computes a bounding box of the shape, with a margin of aClearance
* a collision.
* @aClearance how much the bounding box is expanded wrs to the minimum enclosing rectangle for the shape.
* @return the bounding box.
*/
virtual const BOX2I BBox ( int aClearance = 0 ) const = 0;
/**
* Function Centre()
*
* Computes a center-of-mass of the shape
* @return the center-of-mass point
*/
virtual VECTOR2I Centre() const
{
return BBox(0).Centre(); // if nothing better is available....
}
private:
///> type of our shape
ShapeType m_type;
};
bool CollideShapes ( const SHAPE *a, const SHAPE *b, int clearance, bool needMTV, VECTOR2I& aMTV );
#endif // __SHAPE_H

View File

@ -0,0 +1,78 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SHAPE_CIRCLE_H
#define __SHAPE_CIRCLE_H
#include "shape.h"
class SHAPE_CIRCLE : public SHAPE {
public:
SHAPE_CIRCLE():
SHAPE( SH_CIRCLE ), m_radius (0) {};
SHAPE_CIRCLE( const VECTOR2I& aCenter, int aRadius ):
SHAPE( SH_CIRCLE ), m_radius (aRadius), m_center(aCenter) {};
~SHAPE_CIRCLE() {};
const BOX2I BBox(int aClearance = 0) const
{
const VECTOR2I rc (m_radius + aClearance, m_radius + aClearance);
return BOX2I (m_center - rc, rc * 2);
}
bool Collide(const SEG& aSeg, int aClearance = 0) const
{
int rc = aClearance + m_radius;
return aSeg.Distance(m_center) <= rc;
}
void SetRadius(int aRadius)
{
m_radius = aRadius;
}
void SetCenter (const VECTOR2I& aCenter)
{
m_center = aCenter;
}
int GetRadius() const
{
return m_radius;
}
const VECTOR2I GetCenter() const
{
return m_center;
}
private:
int m_radius;
VECTOR2I m_center;
};
#endif

View File

@ -0,0 +1,290 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SHAPE_INDEX_H
#define __SHAPE_INDEX_H
#include <boost/unordered_map.hpp>
template <class T> const SHAPE *defaultShapeFunctor( const T aItem )
{
return aItem->GetShape();
}
template <class T, const SHAPE *(ShapeFunctor)(const T) = defaultShapeFunctor<T> >
class SHAPE_INDEX_LIST {
struct ShapeEntry {
ShapeEntry(T aParent)
{
shape = ShapeFunctor(aParent);
bbox = shape->BBox(0);
parent = aParent;
}
~ShapeEntry()
{
}
T parent;
const SHAPE *shape;
BOX2I bbox;
};
typedef std::vector<ShapeEntry> ShapeVec;
typedef typename std::vector<ShapeEntry>::iterator ShapeVecIter;
public:
// "Normal" iterator interface, for STL algorithms.
class iterator {
public:
iterator() {};
iterator( ShapeVecIter aCurrent)
: m_current(aCurrent) {};
iterator(const iterator &b) :
m_current(b.m_current) {};
T operator*() const
{
return (*m_current).parent;
}
void operator++()
{
++m_current;
}
iterator& operator++(int dummy)
{
++m_current;
return *this;
}
bool operator ==( const iterator& rhs ) const
{
return m_current == rhs.m_current;
}
bool operator !=( const iterator& rhs ) const
{
return m_current != rhs.m_current;
}
const iterator& operator=(const iterator& rhs)
{
m_current = rhs.m_current;
return *this;
}
private:
ShapeVecIter m_current;
};
// "Query" iterator, for iterating over a set of spatially matching shapes.
class query_iterator {
public:
query_iterator()
{
}
query_iterator( ShapeVecIter aCurrent, ShapeVecIter aEnd, SHAPE *aShape, int aMinDistance, bool aExact)
: m_end(aEnd),
m_current(aCurrent),
m_shape(aShape),
m_minDistance(aMinDistance),
m_exact(aExact)
{
if(aShape)
{
m_refBBox = aShape->BBox();
next();
}
}
query_iterator(const query_iterator &b)
: m_end(b.m_end),
m_current(b.m_current),
m_shape(b.m_shape),
m_minDistance(b.m_minDistance),
m_exact(b.m_exact),
m_refBBox(b.m_refBBox)
{
}
T operator*() const
{
return (*m_current).parent;
}
query_iterator& operator++()
{
++m_current;
next();
return *this;
}
query_iterator& operator++(int dummy)
{
++m_current;
next();
return *this;
}
bool operator ==( const query_iterator& rhs ) const
{
return m_current == rhs.m_current;
}
bool operator !=( const query_iterator& rhs ) const
{
return m_current != rhs.m_current;
}
const query_iterator& operator=(const query_iterator& rhs)
{
m_end = rhs.m_end;
m_current = rhs.m_current;
m_shape = rhs.m_shape;
m_minDistance = rhs.m_minDistance;
m_exact = rhs.m_exact;
m_refBBox = rhs.m_refBBox;
return *this;
}
private:
void next()
{
while(m_current != m_end)
{
if (m_refBBox.Distance(m_current->bbox) <= m_minDistance)
{
if(!m_exact || m_current->shape->Collide(m_shape, m_minDistance))
return;
}
++m_current;
}
}
ShapeVecIter m_end;
ShapeVecIter m_current;
BOX2I m_refBBox;
bool m_exact;
SHAPE *m_shape;
int m_minDistance;
};
void Add(T aItem)
{
ShapeEntry s (aItem);
m_shapes.push_back(s);
}
void Remove(const T aItem)
{
ShapeVecIter i;
for(i=m_shapes.begin(); i!=m_shapes.end();++i)
{
if(i->parent == aItem)
break;
}
if(i == m_shapes.end())
return;
m_shapes.erase(i);
}
int Size() const
{
return m_shapes.size();
}
template<class Visitor>
int Query( const SHAPE *aShape, int aMinDistance, Visitor &v, bool aExact = true) //const
{
ShapeVecIter i;
int n = 0;
VECTOR2I::extended_type minDistSq = (VECTOR2I::extended_type) aMinDistance * aMinDistance;
BOX2I refBBox = aShape->BBox();
for(i = m_shapes.begin(); i!=m_shapes.end(); ++i)
{
if (refBBox.SquaredDistance(i->bbox) <= minDistSq)
{
if(!aExact || i->shape->Collide(aShape, aMinDistance))
{
n++;
if(!v( i->parent ))
return n;
}
}
}
return n;
}
void Clear()
{
m_shapes.clear();
}
query_iterator qbegin( SHAPE *aShape, int aMinDistance, bool aExact )
{
return query_iterator( m_shapes.begin(), m_shapes.end(), aShape, aMinDistance, aExact);
}
const query_iterator qend()
{
return query_iterator( m_shapes.end(), m_shapes.end(), NULL, 0, false );
}
iterator begin()
{
return iterator( m_shapes.begin() );
}
iterator end()
{
return iterator( m_shapes.end() );
}
private:
ShapeVec m_shapes;
};
#endif

View File

@ -0,0 +1,531 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SHAPE_LINE_CHAIN
#define __SHAPE_LINE_CHAIN
#include <vector>
#include <sstream>
#include <boost/optional.hpp>
#include <math/vector2d.h>
#include <geometry/shape.h>
#include <geometry/seg.h>
/**
* Class SHAPE_LINE_CHAIN
*
* Represents a polyline (an zero-thickness chain of connected line segments).
* I purposedly didn't name it "polyline" to avoid confusion with the existing CPolyLine class in pcbnew.
*
* SHAPE_LINE_CHAIN class shall not be used for polygons!
*/
class SHAPE_LINE_CHAIN : public SHAPE {
private:
typedef std::vector<VECTOR2I>::iterator point_iter;
typedef std::vector<VECTOR2I>::const_iterator point_citer;
public:
/**
* Struct Intersection
*
* Represents an intersection between two line segments
*/
struct Intersection {
/// segment belonging from the (this) argument of Intersect()
SEG our;
/// segment belonging from the aOther argument of Intersect()
SEG their;
/// point of intersection between our and their.
VECTOR2I p;
};
typedef std::vector<Intersection> Intersections;
/**
* Constructor
* Initializes an empty line chain.
*/
SHAPE_LINE_CHAIN():
SHAPE (SH_LINE_CHAIN), m_closed(false) {};
/**
* Copy Constructor
*/
SHAPE_LINE_CHAIN(const SHAPE_LINE_CHAIN& aShape):
SHAPE (SH_LINE_CHAIN), m_points(aShape.m_points), m_closed(aShape.m_closed) {};
/**
* Constructor
* Initializes a 2-point line chain (a single segment)
*/
SHAPE_LINE_CHAIN(const VECTOR2I& a, const VECTOR2I& b):
SHAPE (SH_LINE_CHAIN),
m_closed(false)
{
m_points.resize(2);
m_points[0] = a;
m_points[1] = b;
}
SHAPE_LINE_CHAIN(const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I& c):
SHAPE (SH_LINE_CHAIN),
m_closed(false)
{
m_points.resize(3);
m_points[0] = a;
m_points[1] = b;
m_points[2] = c;
}
SHAPE_LINE_CHAIN(const VECTOR2I *v, int count):
SHAPE (SH_LINE_CHAIN),
m_closed(false)
{
m_points.resize(count);
for(int i = 0; i < count ; i++)
m_points[i] = *v++;
}
~SHAPE_LINE_CHAIN() {};
/**
* Function Clear()
* Removes all points from the line chain.
*/
void Clear() {
m_points.clear();
m_closed = false;
}
/**
* Function SetClosed()
*
* Marks the line chain as closed (i.e. with a segment connecting the last point with the first point).
* @param aClosed: whether the line chain is to be closed or not.
*/
void SetClosed(bool aClosed)
{
m_closed = aClosed;
}
/**
* Function IsClosed()
*
* @return aClosed: true, when our line is closed.
*/
bool IsClosed() const
{
return m_closed;
}
/**
* Function SegmentCount()
*
* Returns number of segments in this line chain.
* @return number of segments
*/
int SegmentCount() const
{
int c = m_points.size() - 1;
if(m_closed)
c++;
return std::max(0, c);
}
/**
* Function PointCount()
*
* Returns the number of points (vertices) in this line chain
* @return number of points
*/
int PointCount() const
{
return m_points.size();
};
/**
* Function Segment()
*
* Returns a segment referencing to the segment (index) in the line chain.
* Modifying ends of the returned segment will modify corresponding points in the line chain.
* @param index: index of the segment in the line chain. Negative values are counted from the end (i.e. -1 means
* the last segment in the line chain)
* @return SEG referenced to given segment in the line chain
*/
SEG Segment ( int index )
{
if(index < 0)
index += SegmentCount();
if(index == (m_points.size() - 1) && m_closed )
return SEG ( m_points[index], m_points[0], index );
else
return SEG ( m_points[index], m_points[index + 1], index );
}
/**
* Function CSegment()
*
* Returns a read-only segment referencing to the segment (index) in the line chain.
* @param index: index of the segment in the line chain. Negative values are counted from the end (i.e. -1 means
* the last segment in the line chain)
* @return SEG referenced to given segment in the line chain
*/
const SEG CSegment ( int index ) const
{
if(index < 0)
index += SegmentCount();
if(index == (m_points.size() - 1) && m_closed )
return SEG ( const_cast<VECTOR2I&>(m_points[index]),
const_cast<VECTOR2I&>(m_points[0]), index );
else
return SEG ( const_cast<VECTOR2I&>(m_points[index]),
const_cast<VECTOR2I&>(m_points[index + 1]), index );
}
/**
* Function Point()
*
* Returns a reference to a given point in the line chain.
* @param index index of the point
* @return reference to the point
*/
VECTOR2I& Point ( int index )
{
if(index < 0)
index += PointCount();
return m_points[index];
}
/**
* Function CPoint()
*
* Returns a const reference to a given point in the line chain.
* @param index index of the point
* @return const reference to the point
*/
const VECTOR2I& CPoint ( int index ) const
{
if(index < 0)
index += PointCount();
return m_points[index];
}
/// @copydoc SHAPE::BBox()
const BOX2I BBox ( int aClearance = 0 ) const
{
BOX2I bbox;
bbox.Compute(m_points);
return bbox;
}
/**
* Function Collide()
*
* Checks if point aP lies closer to us than aClearance.
* @param aP the point to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @return true, when a collision has been found
*/
bool Collide ( const VECTOR2I& aP, int aClearance = 0 ) const;
/**
* Function Collide()
*
* Checks if box aBox lies closer to us than aClearance.
* @param aP the box to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @return true, when a collision has been found
*/
bool Collide ( const BOX2I& aBox, int aClearance = 0 ) const;
/**
* Function Collide()
*
* Checks if segment aSeg lies closer to us than aClearance.
* @param aSeg the segment to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @return true, when a collision has been found
*/
bool Collide ( const SEG& aSeg, int aClearance = 0 ) const;
/**
* Function Distance()
*
* Computes the minimum distance between the line chain and a point aP.
* @param aP the point
* @return minimum distance.
*/
int Distance( const VECTOR2I & aP ) const;
/**
* Function Reverse()
*
* Reverses point order in the line chain.
* @return line chain with reversed point order (original A-B-C-D: returned D-C-B-A)
*/
const SHAPE_LINE_CHAIN Reverse() const;
/**
* Function Length()
*
* Returns length of the line chain in Euclidean metric.
* @return length of the line chain
*/
int Length() const;
/**
* Function Append()
*
* Appends a new point at the end of the line chain.
* @param x X coordinate of the new point
* @param y Y coordinate of the new point
*/
void Append(int x, int y)
{
VECTOR2I v(x, y);
Append(v);
}
/**
* Function Append()
*
* Appends a new point at the end of the line chain.
* @param aP the new point
*/
void Append(const VECTOR2I& aP)
{
if(m_points.size() == 0)
m_bbox = BOX2I(aP, VECTOR2I(0, 0));
if (m_points.size() == 0 || CPoint(-1) != aP)
{
m_points.push_back(aP);
m_bbox.Merge(aP);
}
}
/**
* Function Append()
*
* Appends another line chain at the end.
* @param aOtherLine the line chain to be appended.
*/
void Append(const SHAPE_LINE_CHAIN& aOtherLine)
{
if(aOtherLine.PointCount() == 0)
return;
else if(PointCount() == 0 || aOtherLine.CPoint(0) != CPoint(-1))
{
const VECTOR2I p = aOtherLine.CPoint(0);
m_points.push_back(p);
m_bbox.Merge(p);
}
for(int i = 1; i<aOtherLine.PointCount(); i++)
{
const VECTOR2I p = aOtherLine.CPoint(i);
m_points.push_back(p);
m_bbox.Merge(p);
}
}
/**
* Function Replace()
*
* Replaces points with indices in range [start_index, end_index] with a single
* point aP.
* @param start_index start of the point range to be replaced (inclusive)
* @param end_index end of the point range to be replaced (inclusive)
* @param aP replacement point
*/
void Replace( int start_index, int end_index, const VECTOR2I& aP);
/**
* Function Replace()
*
* Replaces points with indices in range [start_index, end_index] with the points from line chain aLine.
* @param start_index start of the point range to be replaced (inclusive)
* @param end_index end of the point range to be replaced (inclusive)
* @param aLine replacement line chain.
*/
void Replace( int start_index, int end_index, const SHAPE_LINE_CHAIN& aLine);
/**
* Function Remove()
*
* Removes the range of points [start_index, end_index] from the line chain.
* @param start_index start of the point range to be replaced (inclusive)
* @param end_index end of the point range to be replaced (inclusive)
*/
void Remove( int start_index, int end_index);
/**
* Function Split()
*
* Inserts the point aP belonging to one of the our segments, splitting the adjacent
* segment in two.
* @param aP the point to be inserted
* @return index of the newly inserted point (or a negative value if aP does not lie on our line)
*/
int Split( const VECTOR2I & aP );
/**
* Function Find()
*
* Searches for point aP.
* @param aP the point to be looked for
* @return index of the correspoinding point in the line chain or negative when not found.
*/
int Find ( const VECTOR2I& aP ) const;
/**
* Function Slice()
*
* Returns a subset of this line chain containing the [start_index, end_index] range of points.
* @param start_index start of the point range to be returned (inclusive)
* @param end_index end of the point range to be returned (inclusive)
* @return cut line chain.
*/
const SHAPE_LINE_CHAIN Slice( int start_index, int end_index = -1) const;
struct compareOriginDistance {
compareOriginDistance( VECTOR2I& aOrigin ):
m_origin(aOrigin) {};
bool operator()(const Intersection &a, const Intersection& b)
{
return (m_origin - a.p).EuclideanNorm() < (m_origin - b.p).EuclideanNorm();
}
VECTOR2I m_origin;
};
/**
* Function Intersect()
*
* Finds all intersection points between our line chain and the segment aSeg.
* @param aSeg the segment chain to find intersections with
* @param aIp reference to a vector to store found intersections. Intersection points
* are sorted with increasing distances from point aSeg.a.
* @return number of intersections found
*/
int Intersect ( const SEG& aSeg, Intersections& aIp ) const;
/**
* Function Intersect()
*
* Finds all intersection points between our line chain and the line chain aChain.
* @param aChain the line chain to find intersections with
* @param aIp reference to a vector to store found intersections. Intersection points
* are sorted with increasing path lengths from the starting point of aChain.
* @return number of intersections found
*/
int Intersect( const SHAPE_LINE_CHAIN &aChain, Intersections& aIp ) const;
/**
* Function PathLength()
*
* Computes the walk path length from the beginning of the line chain and
* the point aP belonging to our line.
* @return: path length in Euclidean metric or negative if aP does not belong to the line chain.
*/
int PathLength (const VECTOR2I& aP ) const;
/**
* Function PointInside()
*
* Checks if point aP lies inside a convex polygon defined by the line chain. For closed
* shapes only.
* @param aP point to check
* @return true if the point is inside the shape (edge is not treated as being inside).
*/
bool PointInside( const VECTOR2I& aP) const;
/**
* Function PointOnEdge()
*
* Checks if point aP lies on an edge or vertex of the line chain.
* @param aP point to check
* @return true if the point lies on the edge.
*/
bool PointOnEdge( const VECTOR2I& aP) const;
/**
* Function SelfIntersecting()
*
* Checks if the line chain is self-intersecting.
* @return (optional) first found self-intersection point.
*/
const boost::optional<Intersection> SelfIntersecting() const;
/**
* Function Simplify()
*
* Simplifies the line chain by removing colinear adjacent segments and duplicate vertices.
* @return reference to self.
*/
SHAPE_LINE_CHAIN& Simplify();
/**
* Function NearestPoint()
*
* Finds a point on the line chain that is closest to point aP.
* @return the nearest point.
*/
const VECTOR2I NearestPoint(const VECTOR2I& aP) const;
/// @copydoc SHAPE::Format()
const std::string Format() const;
bool operator!=(const SHAPE_LINE_CHAIN& rhs) const
{
if(PointCount() != rhs.PointCount())
return true;
for(int i = 0; i < PointCount(); i++)
if( CPoint(i) != rhs.CPoint(i) )
return true;
return false;
}
private:
/// array of vertices
std::vector<VECTOR2I> m_points;
/// is the line chain closed?
bool m_closed;
/// cached bounding box
BOX2I m_bbox;
};
#endif // __SHAPE_LINE_CHAIN

View File

@ -0,0 +1,142 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __SHAPE_RECT_H
#define __SHAPE_RECT_H
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h>
#include <geometry/seg.h>
class SHAPE_RECT : public SHAPE {
public:
/**
* Constructor
* Creates an empty (0-sized) rectangle
*/
SHAPE_RECT():
SHAPE( SH_RECT ), m_w (0), m_h(0) {};
/**
* Constructor
* Creates a rectangle defined by top-left corner (x0, y0), width w and height h.
*/
SHAPE_RECT( int x0, int y0, int w, int h ):
SHAPE(SH_RECT), m_p0(x0, y0), m_w(w), m_h(h) {};
/**
* Constructor
* Creates a rectangle defined by top-left corner p0, width w and height h.
*/
SHAPE_RECT( const VECTOR2I &p0, int w, int h ):
SHAPE(SH_RECT), m_p0(p0), m_w(w), m_h(h) {};
/// @copydoc SHAPE::BBox()
const BOX2I BBox(int aClearance = 0) const
{
BOX2I bbox( VECTOR2I (m_p0.x - aClearance, m_p0.y - aClearance ),
VECTOR2I (m_w + 2 * aClearance, m_h + 2 * aClearance ));
//printf("bb : %s\n",bbox.Format().c_str());
return bbox;
}
/**
* Function Diagonal()
*
* Returns length of the diagonal of the rectangle
* @return diagonal length
*/
int Diagonal() const
{
return VECTOR2I(m_w, m_h).EuclideanNorm();
}
/// @copydoc SHAPE::Collide()
bool Collide(const SEG& aSeg, int aClearance = 0) const
{
//VECTOR2I pmin = VECTOR2I(std::min(aSeg.a.x, aSeg.b.x), std::min(aSeg.a.y, aSeg.b.y));
//VECTOR2I pmax = VECTOR2I(std::max(aSeg.a.x, aSeg.b.x), std::max(aSeg.a.y, aSeg.b.y));
//BOX2I r(pmin, VECTOR2I(pmax.x - pmin.x, pmax.y - pmin.y));
//if (BBox(0).SquaredDistance(r) > aClearance * aClearance)
// return false;
if(BBox(0).Contains(aSeg.a) || BBox(0).Contains(aSeg.b))
return true;
VECTOR2I vts[] = { VECTOR2I(m_p0.x, m_p0.y),
VECTOR2I(m_p0.x, m_p0.y + m_h),
VECTOR2I(m_p0.x + m_w, m_p0.y + m_h),
VECTOR2I(m_p0.x + m_w, m_p0.y),
VECTOR2I(m_p0.x, m_p0.y) };
for (int i = 0; i < 4; i++)
{
SEG s(vts[i], vts[i+1], i);
if(s.Distance(aSeg) <= aClearance)
return true;
}
return false;
};
/**
* Function GetPosition()
*
* @return top-left corner of the rectangle
*/
const VECTOR2I& GetPosition() const { return m_p0; }
/**
* Function GetSize()
*
* @return size of the rectangle
*/
const VECTOR2I GetSize() const { return VECTOR2I(m_w, m_h); }
/**
* Function GetWidth()
*
* @return width of the rectangle
*/
const int GetWidth() const { return m_w; }
/**
* Function GetHeight()
*
* @return height of the rectangle
*/
const int GetHeight() const { return m_h; }
private:
///> Top-left corner
VECTOR2I m_p0;
///> Width
int m_w;
///> Height
int m_h;
};
#endif // __SHAPE_RECT_H