Some more comments and code formatting.
This commit is contained in:
parent
6e0c7a938c
commit
3f320e4d68
|
@ -121,11 +121,6 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
|
||||||
m_pendingRefresh = false;
|
m_pendingRefresh = false;
|
||||||
m_lastRefresh = wxGetLocalTimeMillis();
|
m_lastRefresh = wxGetLocalTimeMillis();
|
||||||
|
|
||||||
#ifdef __WXDEBUG__
|
|
||||||
prof_counter time;
|
|
||||||
prof_start( &time, false );
|
|
||||||
#endif /* __WXDEBUG__ */
|
|
||||||
|
|
||||||
m_gal->BeginDrawing();
|
m_gal->BeginDrawing();
|
||||||
m_gal->SetBackgroundColor( KiGfx::COLOR4D( 0.0, 0.0, 0.0, 1.0 ) );
|
m_gal->SetBackgroundColor( KiGfx::COLOR4D( 0.0, 0.0, 0.0, 1.0 ) );
|
||||||
m_gal->ClearScreen();
|
m_gal->ClearScreen();
|
||||||
|
@ -138,12 +133,6 @@ void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
|
||||||
m_gal->DrawCursor( m_viewControls->GetCursorPosition() );
|
m_gal->DrawCursor( m_viewControls->GetCursorPosition() );
|
||||||
|
|
||||||
m_gal->EndDrawing();
|
m_gal->EndDrawing();
|
||||||
|
|
||||||
#ifdef __WXDEBUG__
|
|
||||||
prof_end( &time );
|
|
||||||
wxLogDebug( wxT( "EDA_DRAW_PANEL_GAL::Refresh: %.0f ms (%.0f fps)" ),
|
|
||||||
static_cast<double>( time.value ) / 1000.0, 1000000.0 / static_cast<double>( time.value ) );
|
|
||||||
#endif /* __WXDEBUG__ */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,126 +25,128 @@
|
||||||
|
|
||||||
#include <geometry/seg.h>
|
#include <geometry/seg.h>
|
||||||
|
|
||||||
template <typename T> int sgn(T val) {
|
template<typename T> int sgn( T val ) {
|
||||||
return (T(0) < val) - (val < T(0));
|
return ( T( 0 ) < val ) - ( val < T( 0 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SEG::PointCloserThan (const VECTOR2I& aP, int dist) const
|
|
||||||
|
bool SEG::PointCloserThan( const VECTOR2I& aP, int dist ) const
|
||||||
{
|
{
|
||||||
VECTOR2I d = b - a;
|
VECTOR2I d = b - a;
|
||||||
ecoord dist_sq = (ecoord) dist * dist;
|
ecoord dist_sq = (ecoord) dist * dist;
|
||||||
|
|
||||||
SEG::ecoord l_squared = d.Dot(d);
|
SEG::ecoord l_squared = d.Dot( d );
|
||||||
SEG::ecoord t = d.Dot(aP - a);
|
SEG::ecoord t = d.Dot( aP - a );
|
||||||
|
|
||||||
|
|
||||||
if( t <= 0 || !l_squared )
|
if( t <= 0 || !l_squared )
|
||||||
return (aP - a).SquaredEuclideanNorm() < dist_sq;
|
return ( aP - a ).SquaredEuclideanNorm() < dist_sq;
|
||||||
else if( t >= l_squared )
|
else if( t >= l_squared )
|
||||||
return (aP - b).SquaredEuclideanNorm() < dist_sq;
|
return ( aP - b ).SquaredEuclideanNorm() < dist_sq;
|
||||||
|
|
||||||
|
int dxdy = abs( d.x ) - abs( d.y );
|
||||||
|
|
||||||
int dxdy = abs(d.x) - abs(d.y);
|
if( ( dxdy >= -1 && dxdy <= 1 ) || abs( d.x ) <= 1 || abs( d.y ) <= 1 )
|
||||||
|
|
||||||
if( (dxdy >= -1 && dxdy <= 1) || abs(d.x) <= 1 || abs(d.y) <= 1)
|
|
||||||
{
|
{
|
||||||
int ca = -sgn(d.y);
|
int ca = -sgn( d.y );
|
||||||
int cb = sgn(d.x);
|
int cb = sgn( d.x );
|
||||||
int cc = -ca * a.x - cb * a.y;
|
int cc = -ca * a.x - cb * a.y;
|
||||||
|
|
||||||
ecoord num = ca * aP.x + cb * aP.y + cc;
|
ecoord num = ca * aP.x + cb * aP.y + cc;
|
||||||
num *= num;
|
num *= num;
|
||||||
|
|
||||||
if(ca && cb)
|
if( ca && cb )
|
||||||
num >>= 1;
|
num >>= 1;
|
||||||
|
|
||||||
if(num > (dist_sq + 100))
|
if( num > ( dist_sq + 100 ) )
|
||||||
return false;
|
return false;
|
||||||
else if(num < (dist_sq - 100))
|
else if( num < ( dist_sq - 100 ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VECTOR2I nearest;
|
VECTOR2I nearest;
|
||||||
nearest.x = a.x + rescale(t, (ecoord)d.x, l_squared);
|
nearest.x = a.x + rescale( t, (ecoord)d.x, l_squared );
|
||||||
nearest.y = a.y + rescale(t, (ecoord)d.y, l_squared);
|
nearest.y = a.y + rescale( t, (ecoord)d.y, l_squared );
|
||||||
|
|
||||||
return (nearest - aP).SquaredEuclideanNorm() <= dist_sq;
|
return ( nearest - aP ).SquaredEuclideanNorm() <= dist_sq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const
|
SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const
|
||||||
{
|
{
|
||||||
// fixme: rather inefficient....
|
// fixme: rather inefficient....
|
||||||
if(Intersect(aSeg))
|
if( Intersect( aSeg ) )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
const VECTOR2I pts[4] =
|
const VECTOR2I pts[4] =
|
||||||
{
|
{
|
||||||
aSeg.NearestPoint(a) - a,
|
aSeg.NearestPoint( a ) - a,
|
||||||
aSeg.NearestPoint(b) - b,
|
aSeg.NearestPoint( b ) - b,
|
||||||
NearestPoint(aSeg.a) - aSeg.a,
|
NearestPoint( aSeg.a ) - aSeg.a,
|
||||||
NearestPoint(aSeg.b) - aSeg.b
|
NearestPoint( aSeg.b ) - aSeg.b
|
||||||
};
|
};
|
||||||
|
|
||||||
ecoord m = VECTOR2I::ECOORD_MAX;
|
ecoord m = VECTOR2I::ECOORD_MAX;
|
||||||
for (int i = 0; i<4 ; i++)
|
for( int i = 0; i < 4; i++ )
|
||||||
m = std::min(m, pts[i].SquaredEuclideanNorm());
|
m = std::min( m, pts[i].SquaredEuclideanNorm() );
|
||||||
return m;
|
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const
|
OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const
|
||||||
{
|
{
|
||||||
const VECTOR2I e (b - a);
|
const VECTOR2I e ( b - a );
|
||||||
const VECTOR2I f (aSeg.b - aSeg.a);
|
const VECTOR2I f ( aSeg.b - aSeg.a );
|
||||||
const VECTOR2I ac (aSeg.a - a);
|
const VECTOR2I ac ( aSeg.a - a );
|
||||||
|
|
||||||
ecoord d = f.Cross(e);
|
ecoord d = f.Cross( e );
|
||||||
ecoord p = f.Cross(ac);
|
ecoord p = f.Cross( ac );
|
||||||
ecoord q = e.Cross(ac);
|
ecoord q = e.Cross( ac );
|
||||||
|
|
||||||
if(d == 0)
|
if( d == 0 )
|
||||||
return OPT_VECTOR2I();
|
return OPT_VECTOR2I();
|
||||||
if (!aLines && d > 0 && (q < 0 || q > d || p < 0 || p > d))
|
if ( !aLines && d > 0 && ( q < 0 || q > d || p < 0 || p > d ) )
|
||||||
return OPT_VECTOR2I();
|
return OPT_VECTOR2I();
|
||||||
if (!aLines && d < 0 && (q < d || p < d || p > 0 || q > 0))
|
if ( !aLines && d < 0 && ( q < d || p < d || p > 0 || q > 0 ) )
|
||||||
return OPT_VECTOR2I();
|
return OPT_VECTOR2I();
|
||||||
if (!aLines && aIgnoreEndpoints && (q == 0 || q == d) && (p == 0 || p == d))
|
if ( !aLines && aIgnoreEndpoints && ( q == 0 || q == d ) && ( p == 0 || p == d ) )
|
||||||
return OPT_VECTOR2I();
|
return OPT_VECTOR2I();
|
||||||
|
|
||||||
|
|
||||||
VECTOR2I ip ( aSeg.a.x + rescale(q, (ecoord)f.x, d),
|
VECTOR2I ip( aSeg.a.x + rescale( q, (ecoord)f.x, d ),
|
||||||
aSeg.a.y + rescale(q, (ecoord)f.y, d) );
|
aSeg.a.y + rescale( q, (ecoord)f.y, d ) );
|
||||||
|
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SEG::ccw ( const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I &c ) const
|
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);
|
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
|
bool SEG::Collide( const SEG& aSeg, int aClearance ) const
|
||||||
{
|
{
|
||||||
// check for intersection
|
// check for intersection
|
||||||
// fixme: move to a method
|
// 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) )
|
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;
|
return true;
|
||||||
|
|
||||||
#define CHK(_seg, _pt) \
|
#define CHK(_seg, _pt) \
|
||||||
if( (_seg).PointCloserThan (_pt, aClearance ) ) return true;
|
if( (_seg).PointCloserThan (_pt, aClearance ) ) return true;
|
||||||
|
|
||||||
CHK(*this, aSeg.a);
|
CHK( *this, aSeg.a );
|
||||||
CHK(*this, aSeg.b);
|
CHK( *this, aSeg.b );
|
||||||
CHK(aSeg, a);
|
CHK( aSeg, a );
|
||||||
CHK(aSeg, b);
|
CHK( aSeg, b );
|
||||||
|
|
||||||
#undef CHK
|
#undef CHK
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SEG::Contains(const VECTOR2I& aP) const
|
|
||||||
{
|
|
||||||
return PointCloserThan(aP, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
bool SEG::Contains( const VECTOR2I& aP ) const
|
||||||
|
{
|
||||||
|
return PointCloserThan( aP, 1 );
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
|
|
||||||
typedef VECTOR2I::extended_type ecoord;
|
typedef VECTOR2I::extended_type ecoord;
|
||||||
|
|
||||||
static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_CIRCLE& b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
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 = clearance + a.GetRadius() + b.GetRadius();
|
||||||
ecoord min_dist_sq = min_dist * min_dist;
|
ecoord min_dist_sq = min_dist * min_dist;
|
||||||
|
@ -44,12 +45,13 @@ static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_CIRCLE& b, int cl
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ( needMTV )
|
if ( needMTV )
|
||||||
aMTV = delta.Resize( sqrt (abs(min_dist_sq - dist_sq)) + 1);
|
aMTV = delta.Resize( sqrt ( abs( min_dist_sq - dist_sq ) ) + 1 );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool Collide( const SHAPE_RECT& a, const SHAPE_CIRCLE& b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
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 c = b.GetCenter();
|
||||||
const VECTOR2I p0 = a.GetPosition();
|
const VECTOR2I p0 = a.GetPosition();
|
||||||
|
@ -58,7 +60,7 @@ static inline bool Collide( const SHAPE_RECT& a, const SHAPE_CIRCLE& b, int cle
|
||||||
const ecoord min_dist = clearance + r;
|
const ecoord min_dist = clearance + r;
|
||||||
const ecoord min_dist_sq = min_dist * min_dist;
|
const ecoord min_dist_sq = min_dist * min_dist;
|
||||||
|
|
||||||
if (a.BBox(0).Contains(c))
|
if ( a.BBox( 0 ).Contains( c ) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const VECTOR2I vts[] = {
|
const VECTOR2I vts[] = {
|
||||||
|
@ -71,73 +73,77 @@ static inline bool Collide( const SHAPE_RECT& a, const SHAPE_CIRCLE& b, int cle
|
||||||
ecoord nearest_seg_dist_sq = VECTOR2I::ECOORD_MAX;
|
ecoord nearest_seg_dist_sq = VECTOR2I::ECOORD_MAX;
|
||||||
VECTOR2I nearest;
|
VECTOR2I nearest;
|
||||||
|
|
||||||
bool inside = c.x >= p0.x && c.x <= (p0.x + size.x)
|
bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x )
|
||||||
&& c.y >= p0.y && c.y <= (p0.y + size.y);
|
&& c.y >= p0.y && c.y <= ( p0.y + size.y );
|
||||||
|
|
||||||
if(!inside)
|
if( !inside )
|
||||||
{
|
{
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for( int i = 0; i < 4; i++ )
|
||||||
{
|
{
|
||||||
const SEG seg (vts[i], vts[i+1]);
|
const SEG seg( vts[i], vts[i+1] );
|
||||||
ecoord dist_sq = seg.SquaredDistance ( c );
|
ecoord dist_sq = seg.SquaredDistance( c );
|
||||||
|
|
||||||
|
if( dist_sq < min_dist_sq )
|
||||||
if(dist_sq < min_dist_sq)
|
|
||||||
{
|
{
|
||||||
if(!needMTV)
|
if( !needMTV )
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nearest = seg.NearestPoint ( c );
|
nearest = seg.NearestPoint( c );
|
||||||
nearest_seg_dist_sq = dist_sq;
|
nearest_seg_dist_sq = dist_sq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nearest_seg_dist_sq >= min_dist_sq && !inside)
|
if( nearest_seg_dist_sq >= min_dist_sq && !inside )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
VECTOR2I delta = c - nearest;
|
VECTOR2I delta = c - nearest;
|
||||||
|
|
||||||
if(!needMTV)
|
if( !needMTV )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(inside)
|
if( inside )
|
||||||
aMTV = -delta.Resize(sqrt(abs(r * r + nearest_seg_dist_sq) + 1));
|
aMTV = -delta.Resize( sqrt( abs( r * r + nearest_seg_dist_sq ) + 1 ) );
|
||||||
else
|
else
|
||||||
aMTV = delta.Resize(sqrt(abs(r * r - nearest_seg_dist_sq) + 1));
|
aMTV = delta.Resize( sqrt( abs( r * r - nearest_seg_dist_sq ) + 1 ) );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
|
||||||
|
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++)
|
for( int s = 0; s < b.SegmentCount(); s++ )
|
||||||
{
|
{
|
||||||
if ( a.Collide (b.CSegment(s), clearance))
|
if ( a.Collide( b.CSegment( s ), clearance ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool Collide( const SHAPE_LINE_CHAIN& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
|
||||||
|
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++)
|
for( int i = 0; i < b.SegmentCount(); i++ )
|
||||||
if(a.Collide(b.CSegment(i), clearance))
|
if( a.Collide( b.CSegment(i), clearance ) )
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline bool Collide( const SHAPE_RECT& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
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++)
|
for( int s = 0; s < b.SegmentCount(); s++ )
|
||||||
{
|
{
|
||||||
SEG seg = b.CSegment(s);
|
SEG seg = b.CSegment( s );
|
||||||
if ( a.Collide (seg, clearance))
|
if( a.Collide( seg, clearance ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,66 +151,83 @@ static inline bool Collide( const SHAPE_RECT& a, const SHAPE_LINE_CHAIN& b, int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CollideShapes( const SHAPE* a, const SHAPE* b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
||||||
bool CollideShapes ( const SHAPE *a, const SHAPE *b, int clearance, bool needMTV, VECTOR2I& aMTV )
|
|
||||||
{
|
{
|
||||||
switch(a->Type())
|
switch( a->Type() )
|
||||||
{
|
{
|
||||||
case SH_RECT:
|
case SH_RECT:
|
||||||
switch(b->Type())
|
switch( b->Type() )
|
||||||
{
|
{
|
||||||
case SH_CIRCLE:
|
case SH_CIRCLE:
|
||||||
return Collide( *static_cast<const SHAPE_RECT *> (a), *static_cast<const SHAPE_CIRCLE *> (b), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_RECT*>( a ),
|
||||||
|
*static_cast<const SHAPE_CIRCLE*>( b ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
case SH_LINE_CHAIN:
|
case SH_LINE_CHAIN:
|
||||||
return Collide( *static_cast<const SHAPE_RECT *> (a), *static_cast<const SHAPE_LINE_CHAIN *> (b), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_RECT*>( a ),
|
||||||
|
*static_cast<const SHAPE_LINE_CHAIN*>( b ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SH_CIRCLE:
|
case SH_CIRCLE:
|
||||||
switch(b->Type())
|
switch( b->Type() )
|
||||||
{
|
{
|
||||||
case SH_RECT:
|
case SH_RECT:
|
||||||
return Collide( *static_cast<const SHAPE_RECT *> (b), *static_cast<const SHAPE_CIRCLE *> (a), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_RECT*>( b ),
|
||||||
|
*static_cast<const SHAPE_CIRCLE*>( a ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
case SH_CIRCLE:
|
case SH_CIRCLE:
|
||||||
return Collide( *static_cast<const SHAPE_CIRCLE *> (a), *static_cast<const SHAPE_CIRCLE *> (b), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_CIRCLE*>( a ),
|
||||||
|
*static_cast<const SHAPE_CIRCLE*>( b ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
case SH_LINE_CHAIN:
|
case SH_LINE_CHAIN:
|
||||||
return Collide( *static_cast<const SHAPE_CIRCLE *> (a), *static_cast<const SHAPE_LINE_CHAIN *> (b), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_CIRCLE*>( a ),
|
||||||
|
*static_cast<const SHAPE_LINE_CHAIN *>( b ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SH_LINE_CHAIN:
|
case SH_LINE_CHAIN:
|
||||||
switch(b->Type())
|
switch( b->Type() )
|
||||||
{
|
{
|
||||||
case SH_RECT:
|
case SH_RECT:
|
||||||
return Collide( *static_cast<const SHAPE_RECT *> (b), *static_cast<const SHAPE_LINE_CHAIN *> (a), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_RECT*>( b ),
|
||||||
|
*static_cast<const SHAPE_LINE_CHAIN*>( a ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
case SH_CIRCLE:
|
case SH_CIRCLE:
|
||||||
return Collide( *static_cast<const SHAPE_CIRCLE *> (b), *static_cast<const SHAPE_LINE_CHAIN *> (a), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_CIRCLE*>( b ),
|
||||||
|
*static_cast<const SHAPE_LINE_CHAIN*>( a ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
case SH_LINE_CHAIN:
|
case SH_LINE_CHAIN:
|
||||||
return Collide( *static_cast<const SHAPE_LINE_CHAIN *> (a), *static_cast<const SHAPE_LINE_CHAIN *> (b), clearance, needMTV, aMTV );
|
return Collide( *static_cast<const SHAPE_LINE_CHAIN*>( a ),
|
||||||
|
*static_cast<const SHAPE_LINE_CHAIN*>( b ), clearance, needMTV, aMTV );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unsupported_collision = true;
|
bool unsupported_collision = true;
|
||||||
|
|
||||||
assert(unsupported_collision == false);
|
assert( unsupported_collision == false );
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SHAPE::Collide ( const SHAPE *aShape, int aClerance, VECTOR2I& aMTV ) const
|
bool SHAPE::Collide( const SHAPE* aShape, int aClerance, VECTOR2I& aMTV ) const
|
||||||
{
|
{
|
||||||
return CollideShapes( this, aShape, aClerance, true, aMTV);
|
return CollideShapes( this, aShape, aClerance, true, aMTV);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SHAPE::Collide ( const SHAPE *aShape, int aClerance ) const
|
|
||||||
|
bool SHAPE::Collide ( const SHAPE* aShape, int aClerance ) const
|
||||||
{
|
{
|
||||||
VECTOR2I dummy;
|
VECTOR2I dummy;
|
||||||
return CollideShapes( this, aShape, aClerance, false, dummy);
|
return CollideShapes( this, aShape, aClerance, false, dummy );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,4 +30,3 @@ const SHAPE* shapeFunctor( SHAPE* aItem )
|
||||||
{
|
{
|
||||||
return aItem;
|
return aItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,43 +28,46 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using boost::optional;
|
using boost::optional;
|
||||||
|
|
||||||
bool SHAPE_LINE_CHAIN::Collide ( const VECTOR2I& aP, int aClearance ) const
|
bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const
|
||||||
{
|
{
|
||||||
assert(false);
|
assert( false );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SHAPE_LINE_CHAIN::Collide ( const BOX2I& aBox, int aClearance ) const
|
|
||||||
|
bool SHAPE_LINE_CHAIN::Collide( const BOX2I& aBox, int aClearance ) const
|
||||||
{
|
{
|
||||||
assert(false);
|
assert( false );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SHAPE_LINE_CHAIN::Collide ( const SEG& aSeg, int aClearance ) const
|
|
||||||
|
bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance ) const
|
||||||
{
|
{
|
||||||
BOX2I box_a(aSeg.a, aSeg.b - aSeg.a);
|
BOX2I box_a( aSeg.a, aSeg.b - aSeg.a );
|
||||||
BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance;
|
BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance;
|
||||||
|
|
||||||
for( int i = 0; i < SegmentCount() ;i++)
|
for( int i = 0; i < SegmentCount() ;i++ )
|
||||||
{
|
{
|
||||||
const SEG& s = CSegment(i);
|
const SEG& s = CSegment( i );
|
||||||
BOX2I box_b(s.a, s.b - s.a);
|
BOX2I box_b( s.a, s.b - s.a );
|
||||||
|
|
||||||
BOX2I::ecoord_type d = box_a.SquaredDistance ( box_b );
|
BOX2I::ecoord_type d = box_a.SquaredDistance ( box_b );
|
||||||
|
|
||||||
if(d < dist_sq)
|
if( d < dist_sq )
|
||||||
{
|
{
|
||||||
if(s.Collide(aSeg, aClearance))
|
if( s.Collide( aSeg, aClearance ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
|
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
|
||||||
{
|
{
|
||||||
SHAPE_LINE_CHAIN a (*this);
|
SHAPE_LINE_CHAIN a( *this );
|
||||||
reverse(a.m_points.begin(), a.m_points.end());
|
reverse( a.m_points.begin(), a.m_points.end() );
|
||||||
a.m_closed = m_closed;
|
a.m_closed = m_closed;
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
|
@ -74,176 +77,195 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
|
||||||
int SHAPE_LINE_CHAIN::Length() const
|
int SHAPE_LINE_CHAIN::Length() const
|
||||||
{
|
{
|
||||||
int l = 0;
|
int l = 0;
|
||||||
for (int i = 0; i < SegmentCount(); i++)
|
for( int i = 0; i < SegmentCount(); i++ )
|
||||||
l += CSegment(i).Length();
|
l += CSegment( i ).Length();
|
||||||
|
|
||||||
return l;
|
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)
|
void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP )
|
||||||
m_points [start_index] = aP;
|
{
|
||||||
else {
|
if( aEndIndex < 0 )
|
||||||
m_points.erase (m_points.begin() + start_index + 1, m_points.begin() + end_index + 1);
|
aEndIndex += PointCount();
|
||||||
m_points [start_index] = aP;
|
if( aStartIndex < 0 )
|
||||||
|
aStartIndex += PointCount();
|
||||||
|
|
||||||
|
if( aStartIndex == aEndIndex )
|
||||||
|
m_points [aStartIndex] = aP;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_points.erase( m_points.begin() + aStartIndex + 1, m_points.begin() + aEndIndex + 1 );
|
||||||
|
m_points[aStartIndex] = 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);
|
void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE_CHAIN& aLine )
|
||||||
m_points.insert (m_points.begin() + start_index, aLine.m_points.begin(), aLine.m_points.end());
|
{
|
||||||
|
if( aEndIndex < 0 )
|
||||||
|
aEndIndex += PointCount();
|
||||||
|
if( aStartIndex < 0 )
|
||||||
|
aStartIndex += PointCount();
|
||||||
|
|
||||||
|
m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
|
||||||
|
m_points.insert( m_points.begin() + aStartIndex, 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);
|
void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex )
|
||||||
|
{
|
||||||
|
if(aEndIndex < 0)
|
||||||
|
aEndIndex += PointCount();
|
||||||
|
if(aStartIndex < 0)
|
||||||
|
aStartIndex += PointCount();
|
||||||
|
|
||||||
|
m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int SHAPE_LINE_CHAIN::Distance( const VECTOR2I & aP ) const
|
|
||||||
|
int SHAPE_LINE_CHAIN::Distance( const VECTOR2I& aP ) const
|
||||||
{
|
{
|
||||||
int d = INT_MAX;
|
int d = INT_MAX;
|
||||||
for (int s = 0; s < SegmentCount(); s++)
|
for( int s = 0; s < SegmentCount(); s++ )
|
||||||
d = min (d, CSegment(s).Distance(aP));
|
d = min( d, CSegment( s ).Distance( aP ) );
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SHAPE_LINE_CHAIN::Split( const VECTOR2I & aP )
|
|
||||||
|
int SHAPE_LINE_CHAIN::Split( const VECTOR2I& aP )
|
||||||
{
|
{
|
||||||
int ii = -1;
|
int ii = -1;
|
||||||
int min_dist = 2;
|
int min_dist = 2;
|
||||||
|
|
||||||
ii = Find(aP);
|
ii = Find( aP );
|
||||||
|
|
||||||
if(ii >= 0)
|
if( ii >= 0 )
|
||||||
return ii;
|
return ii;
|
||||||
|
|
||||||
for (int s = 0; s < SegmentCount(); s++)
|
for( int s = 0; s < SegmentCount(); s++ )
|
||||||
{
|
{
|
||||||
const SEG seg = CSegment(s);
|
const SEG seg = CSegment( s );
|
||||||
int dist = seg.Distance(aP);
|
int dist = seg.Distance( aP );
|
||||||
|
|
||||||
// make sure we are not producing a 'slightly concave' primitive. This might happen
|
// 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 aP lies very close to one of already existing points.
|
||||||
if(dist < min_dist && seg.a != aP && seg.b != aP)
|
if( dist < min_dist && seg.a != aP && seg.b != aP )
|
||||||
{
|
{
|
||||||
min_dist = dist;
|
min_dist = dist;
|
||||||
ii = s;
|
ii = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ii >= 0)
|
if( ii >= 0 )
|
||||||
{
|
{
|
||||||
m_points.insert(m_points.begin() + ii + 1, aP);
|
m_points.insert( m_points.begin() + ii + 1, aP );
|
||||||
|
|
||||||
return ii + 1;
|
return ii + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SHAPE_LINE_CHAIN::Find ( const VECTOR2I& aP ) const
|
|
||||||
|
int SHAPE_LINE_CHAIN::Find( const VECTOR2I& aP ) const
|
||||||
{
|
{
|
||||||
for (int s = 0; s< PointCount(); s++)
|
for( int s = 0; s< PointCount(); s++ )
|
||||||
if(CPoint(s) == aP)
|
if( CPoint( s ) == aP )
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int start_index, int end_index ) const
|
|
||||||
|
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) const
|
||||||
{
|
{
|
||||||
SHAPE_LINE_CHAIN rv;
|
SHAPE_LINE_CHAIN rv;
|
||||||
|
|
||||||
if(end_index < 0)
|
if( aEndIndex < 0 )
|
||||||
end_index += PointCount();
|
aEndIndex += PointCount();
|
||||||
if(start_index < 0)
|
if( aStartIndex < 0 )
|
||||||
start_index += PointCount();
|
aStartIndex += PointCount();
|
||||||
|
|
||||||
|
for( int i = aStartIndex; i <= aEndIndex; i++ )
|
||||||
|
rv.Append( m_points[i] );
|
||||||
|
|
||||||
for(int i = start_index; i<= end_index; i++)
|
|
||||||
rv.Append(m_points[i]);
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct compareOriginDistance {
|
|
||||||
compareOriginDistance( VECTOR2I& aOrigin ):
|
|
||||||
m_origin(aOrigin) {};
|
|
||||||
|
|
||||||
bool operator()(const SHAPE_LINE_CHAIN::Intersection &a, const SHAPE_LINE_CHAIN::Intersection& b)
|
struct compareOriginDistance
|
||||||
|
{
|
||||||
|
compareOriginDistance( VECTOR2I& aOrigin ):
|
||||||
|
m_origin( aOrigin ) {};
|
||||||
|
|
||||||
|
bool operator()( const SHAPE_LINE_CHAIN::Intersection& aA,
|
||||||
|
const SHAPE_LINE_CHAIN::Intersection& aB )
|
||||||
{
|
{
|
||||||
return (m_origin - a.p).EuclideanNorm() < (m_origin - b.p).EuclideanNorm();
|
return ( m_origin - aA.p ).EuclideanNorm() < ( m_origin - aB.p ).EuclideanNorm();
|
||||||
}
|
}
|
||||||
|
|
||||||
VECTOR2I m_origin;
|
VECTOR2I m_origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int SHAPE_LINE_CHAIN::Intersect ( const SEG& aSeg, Intersections& aIp ) const
|
int SHAPE_LINE_CHAIN::Intersect( const SEG& aSeg, Intersections& aIp ) const
|
||||||
{
|
{
|
||||||
for (int s = 0; s < SegmentCount(); s++)
|
for( int s = 0; s < SegmentCount(); s++ )
|
||||||
{
|
{
|
||||||
OPT_VECTOR2I p = CSegment(s).Intersect(aSeg);
|
OPT_VECTOR2I p = CSegment( s ).Intersect( aSeg );
|
||||||
if(p)
|
if( p )
|
||||||
{
|
{
|
||||||
Intersection is;
|
Intersection is;
|
||||||
is.our = CSegment(s);
|
is.our = CSegment( s );
|
||||||
is.their = aSeg;
|
is.their = aSeg;
|
||||||
is.p = *p;
|
is.p = *p;
|
||||||
aIp.push_back(is);
|
aIp.push_back( is );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compareOriginDistance comp(aSeg.a);
|
compareOriginDistance comp( aSeg.a );
|
||||||
sort(aIp.begin(), aIp.end(), comp);
|
sort( aIp.begin(), aIp.end(), comp );
|
||||||
|
|
||||||
return aIp.size();
|
return aIp.size();
|
||||||
};
|
}
|
||||||
|
|
||||||
int SHAPE_LINE_CHAIN::Intersect( const SHAPE_LINE_CHAIN &aChain, Intersections& aIp ) const
|
|
||||||
|
int SHAPE_LINE_CHAIN::Intersect( const SHAPE_LINE_CHAIN& aChain, Intersections& aIp ) const
|
||||||
{
|
{
|
||||||
BOX2I bb_other = aChain.BBox();
|
BOX2I bb_other = aChain.BBox();
|
||||||
|
|
||||||
for (int s1 = 0; s1 < SegmentCount(); s1++)
|
for ( int s1 = 0; s1 < SegmentCount(); s1++ )
|
||||||
{
|
{
|
||||||
const SEG& a = CSegment(s1);
|
const SEG& a = CSegment( s1 );
|
||||||
const BOX2I bb_cur (a.a, a.b - a.a);
|
const BOX2I bb_cur( a.a, a.b - a.a );
|
||||||
|
|
||||||
if(! bb_other.Intersects( bb_cur ))
|
if( !bb_other.Intersects( bb_cur ) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (int s2 = 0; s2 < aChain.SegmentCount(); s2++)
|
for( int s2 = 0; s2 < aChain.SegmentCount(); s2++ )
|
||||||
{
|
{
|
||||||
const SEG& b = aChain.CSegment(s2);
|
const SEG& b = aChain.CSegment( s2 );
|
||||||
Intersection is;
|
Intersection is;
|
||||||
|
|
||||||
|
if( a.Collinear( b ) )
|
||||||
if(a.Collinear(b))
|
|
||||||
{
|
{
|
||||||
if(a.Contains(b.a)) { is.p = b.a; aIp.push_back(is); }
|
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( 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.a ) ) { is.p = a.a; aIp.push_back( is ); }
|
||||||
if(b.Contains(a.b)) { is.p = a.b; aIp.push_back(is); }
|
if( b.Contains( a.b ) ) { is.p = a.b; aIp.push_back( is ); }
|
||||||
} else {
|
}
|
||||||
OPT_VECTOR2I p = a.Intersect(b);
|
else
|
||||||
|
{
|
||||||
|
OPT_VECTOR2I p = a.Intersect( b );
|
||||||
|
|
||||||
if(p)
|
if( p )
|
||||||
{
|
{
|
||||||
is.p = *p;
|
is.p = *p;
|
||||||
is.our = a;
|
is.our = a;
|
||||||
is.their = b;
|
is.their = b;
|
||||||
aIp.push_back(is);
|
aIp.push_back( is );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,130 +273,152 @@ BOX2I bb_other = aChain.BBox();
|
||||||
|
|
||||||
return aIp.size();
|
return aIp.size();
|
||||||
|
|
||||||
for (int s1 = 0; s1 < SegmentCount(); s1++)
|
for( int s1 = 0; s1 < SegmentCount(); s1++ )
|
||||||
for (int s2 = 0; s2 < aChain.SegmentCount(); s2++)
|
{
|
||||||
|
for( int s2 = 0; s2 < aChain.SegmentCount(); s2++ )
|
||||||
{
|
{
|
||||||
const SEG& a = CSegment(s1);
|
const SEG& a = CSegment( s1 );
|
||||||
const SEG& b = aChain.CSegment(s2);
|
const SEG& b = aChain.CSegment( s2 );
|
||||||
OPT_VECTOR2I p = a.Intersect(b);
|
OPT_VECTOR2I p = a.Intersect( b );
|
||||||
Intersection is;
|
Intersection is;
|
||||||
|
|
||||||
if(p)
|
if( p )
|
||||||
{
|
{
|
||||||
is.p = *p;
|
is.p = *p;
|
||||||
is.our = a;
|
is.our = a;
|
||||||
is.their = b;
|
is.their = b;
|
||||||
aIp.push_back(is);
|
aIp.push_back( is );
|
||||||
} else if (a.Collinear(b))
|
}
|
||||||
|
else if( a.Collinear( b ) )
|
||||||
{
|
{
|
||||||
if(a.a != b.a && a.a != b.b && b.Contains(a.a) )
|
if( a.a != b.a && a.a != b.b && b.Contains( a.a ) )
|
||||||
{
|
{
|
||||||
is.p = a.a;
|
is.p = a.a;
|
||||||
is.our = a;
|
is.our = a;
|
||||||
is.their = b;
|
is.their = b;
|
||||||
aIp.push_back(is);
|
aIp.push_back( is );
|
||||||
}
|
}
|
||||||
else if(a.b != b.a && a.b != b.b && b.Contains(a.b) )
|
else if( a.b != b.a && a.b != b.b && b.Contains( a.b ) )
|
||||||
{
|
{
|
||||||
is.p = a.b;
|
is.p = a.b;
|
||||||
is.our = a;
|
is.our = a;
|
||||||
is.their = b;
|
is.their = b;
|
||||||
aIp.push_back(is);
|
aIp.push_back( is );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return aIp.size();
|
return aIp.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SHAPE_LINE_CHAIN::PathLength (const VECTOR2I& aP ) const
|
|
||||||
|
int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP ) const
|
||||||
{
|
{
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (int i = 0; i < SegmentCount(); i++)
|
for( int i = 0; i < SegmentCount(); i++ )
|
||||||
{
|
{
|
||||||
const SEG seg = CSegment(i);
|
const SEG seg = CSegment( i );
|
||||||
int d = seg.Distance(aP);
|
int d = seg.Distance( aP );
|
||||||
if (d <= 1)
|
|
||||||
|
if( d <= 1 )
|
||||||
{
|
{
|
||||||
sum += (aP - seg.a).EuclideanNorm();
|
sum += ( aP - seg.a ).EuclideanNorm();
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sum += seg.Length();
|
sum += seg.Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aP) const
|
|
||||||
|
bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aP ) const
|
||||||
{
|
{
|
||||||
if(!m_closed || SegmentCount() < 3)
|
if( !m_closed || SegmentCount() < 3 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int cur = CSegment(0).Side(aP);
|
int cur = CSegment(0).Side( aP );
|
||||||
if(cur == 0)
|
|
||||||
|
if( cur == 0 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for( int i = 1; i < SegmentCount(); i++)
|
for( int i = 1; i < SegmentCount(); i++ )
|
||||||
{
|
{
|
||||||
const SEG s = CSegment(i);
|
const SEG s = CSegment( i );
|
||||||
if(aP == s.a || aP == s.b) // edge does not belong to the interior!
|
|
||||||
|
if( aP == s.a || aP == s.b ) // edge does not belong to the interior!
|
||||||
return false;
|
return false;
|
||||||
if (s.Side(aP) != cur)
|
|
||||||
|
if( s.Side(aP) != cur )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aP) const
|
|
||||||
|
bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aP ) const
|
||||||
{
|
{
|
||||||
if(SegmentCount() < 1)
|
if( SegmentCount() < 1 )
|
||||||
return m_points[0] == aP;
|
return m_points[0] == aP;
|
||||||
|
|
||||||
for( int i = 1; i < SegmentCount(); i++)
|
for( int i = 1; i < SegmentCount(); i++ )
|
||||||
{
|
{
|
||||||
const SEG s = CSegment(i);
|
const SEG s = CSegment( i );
|
||||||
if(s.a == aP || s.b == aP)
|
|
||||||
|
if( s.a == aP || s.b == aP )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(s.Distance(aP) <= 1)
|
if( s.Distance(aP) <= 1 )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const optional<SHAPE_LINE_CHAIN::Intersection> SHAPE_LINE_CHAIN::SelfIntersecting() const
|
const optional<SHAPE_LINE_CHAIN::Intersection> SHAPE_LINE_CHAIN::SelfIntersecting() const
|
||||||
{
|
{
|
||||||
for (int s1 = 0; s1 < SegmentCount(); s1++)
|
for( int s1 = 0; s1 < SegmentCount(); s1++ )
|
||||||
for (int s2 = s1 + 1; s2 < SegmentCount(); s2++)
|
{
|
||||||
|
for( int s2 = s1 + 1; s2 < SegmentCount(); s2++ )
|
||||||
{
|
{
|
||||||
const VECTOR2I s2a = CSegment(s2).a, s2b = CSegment(s2).b;
|
const VECTOR2I s2a = CSegment( s2 ).a, s2b = CSegment( s2 ).b;
|
||||||
if(s1 + 1 != s2 && CSegment(s1).Contains(s2a))
|
if( s1 + 1 != s2 && CSegment( s1 ).Contains( s2a ) )
|
||||||
{
|
{
|
||||||
Intersection is;
|
Intersection is;
|
||||||
is.our = CSegment(s1);
|
is.our = CSegment( s1 );
|
||||||
is.their = CSegment(s2);
|
is.their = CSegment( s2 );
|
||||||
is.p = s2a;
|
is.p = s2a;
|
||||||
return is;
|
return is;
|
||||||
} else if (CSegment(s1).Contains(s2b)) {
|
}
|
||||||
|
else if( CSegment( s1 ).Contains(s2b ) )
|
||||||
|
{
|
||||||
Intersection is;
|
Intersection is;
|
||||||
is.our = CSegment(s1);
|
is.our = CSegment( s1 );
|
||||||
is.their = CSegment(s2);
|
is.their = CSegment( s2 );
|
||||||
is.p = s2b;
|
is.p = s2b;
|
||||||
return is;
|
return is;
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
OPT_VECTOR2I p = CSegment(s1).Intersect(CSegment(s2), true);
|
{
|
||||||
|
OPT_VECTOR2I p = CSegment( s1 ).Intersect( CSegment( s2 ), true );
|
||||||
|
|
||||||
if(p)
|
if( p )
|
||||||
{
|
{
|
||||||
Intersection is;
|
Intersection is;
|
||||||
is.our = CSegment(s1);
|
is.our = CSegment( s1 );
|
||||||
is.their = CSegment(s2);
|
is.their = CSegment( s2 );
|
||||||
is.p = *p;
|
is.p = *p;
|
||||||
return is;
|
return is;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return optional<Intersection>();
|
return optional<Intersection>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,12 +427,15 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
|
||||||
{
|
{
|
||||||
vector<VECTOR2I> pts_unique;
|
vector<VECTOR2I> pts_unique;
|
||||||
|
|
||||||
if (PointCount() < 2)
|
if( PointCount() < 2 )
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
} else if (PointCount() == 2) {
|
}
|
||||||
if(m_points[0] == m_points[1])
|
else if( PointCount() == 2 )
|
||||||
m_points.erase(m_points.end());
|
{
|
||||||
|
if( m_points[0] == m_points[1] )
|
||||||
|
m_points.erase( m_points.end() );
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,10 +445,11 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
|
||||||
// stage 1: eliminate duplicate vertices
|
// stage 1: eliminate duplicate vertices
|
||||||
while ( i < np )
|
while ( i < np )
|
||||||
{
|
{
|
||||||
int j = i+1;
|
int j = i + 1;
|
||||||
while(j < np && CPoint(i) == CPoint(j))
|
while( j < np && CPoint(i) == CPoint( j ) )
|
||||||
j ++;
|
j++;
|
||||||
pts_unique.push_back(CPoint(i));
|
|
||||||
|
pts_unique.push_back( CPoint( i ) );
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,56 +458,63 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
// stage 1: eliminate collinear segments
|
// stage 1: eliminate collinear segments
|
||||||
while (i < np - 2)
|
while( i < np - 2 )
|
||||||
{
|
{
|
||||||
const VECTOR2I p0 = pts_unique[i];
|
const VECTOR2I p0 = pts_unique[i];
|
||||||
const VECTOR2I p1 = pts_unique[i+1];
|
const VECTOR2I p1 = pts_unique[i+1];
|
||||||
int n = i;
|
int n = i;
|
||||||
while(n < np - 2 && SEG(p0, p1).LineDistance(pts_unique[n + 2]) <= 1)
|
|
||||||
|
while( n < np - 2 && SEG( p0, p1 ).LineDistance( pts_unique[n + 2] ) <= 1 )
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
m_points.push_back(p0);
|
m_points.push_back( p0 );
|
||||||
if (n > i)
|
if( n > i )
|
||||||
i = n;
|
i = n;
|
||||||
if (n == np)
|
|
||||||
|
if( n == np )
|
||||||
{
|
{
|
||||||
m_points.push_back(pts_unique[n-1]);
|
m_points.push_back( pts_unique[n - 1] );
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
i ++;
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(np > 1)
|
if( np > 1 )
|
||||||
m_points.push_back(pts_unique[np-2]);
|
m_points.push_back( pts_unique[np - 2] );
|
||||||
m_points.push_back(pts_unique[np-1]);
|
|
||||||
|
m_points.push_back( pts_unique[np - 1] );
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint(const VECTOR2I& aP) const
|
|
||||||
|
const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const VECTOR2I& aP ) const
|
||||||
{
|
{
|
||||||
int min_d = INT_MAX;
|
int min_d = INT_MAX;
|
||||||
int nearest = 0;
|
int nearest = 0;
|
||||||
for ( int i = 0; i < SegmentCount() ; i++ )
|
for ( int i = 0; i < SegmentCount(); i++ )
|
||||||
{
|
{
|
||||||
int d = CSegment(i).Distance(aP);
|
int d = CSegment( i ).Distance( aP );
|
||||||
if( d < min_d )
|
if( d < min_d )
|
||||||
{
|
{
|
||||||
min_d = d;
|
min_d = d;
|
||||||
nearest = i;
|
nearest = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CSegment(nearest).NearestPoint(aP);
|
|
||||||
|
return CSegment( nearest ).NearestPoint( aP );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const string SHAPE_LINE_CHAIN::Format() const
|
const string SHAPE_LINE_CHAIN::Format() const
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
|
|
||||||
ss << m_points.size() << " " << (m_closed ? 1 : 0) << " " ;
|
ss << m_points.size() << " " << ( m_closed ? 1 : 0 ) << " " ;
|
||||||
|
|
||||||
for(int i = 0; i<PointCount(); i++)
|
for( int i = 0; i < PointCount(); i++ )
|
||||||
ss << m_points[i].x << " " << m_points[i].y<<" ";// Format() << " ";
|
ss << m_points[i].x << " " << m_points[i].y << " ";// Format() << " ";
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,9 @@ void ACTION_MANAGER::RegisterAction( TOOL_ACTION* aAction )
|
||||||
|
|
||||||
void ACTION_MANAGER::UnregisterAction( TOOL_ACTION* aAction )
|
void ACTION_MANAGER::UnregisterAction( TOOL_ACTION* aAction )
|
||||||
{
|
{
|
||||||
// Indicate that we no longer care about the object
|
// Indicate that the ACTION_MANAGER no longer care about the object
|
||||||
aAction->setActionMgr( NULL );
|
aAction->setActionMgr( NULL );
|
||||||
|
aAction->setId( -1 );
|
||||||
|
|
||||||
m_actionNameIndex.erase( aAction->m_name );
|
m_actionNameIndex.erase( aAction->m_name );
|
||||||
m_actionIdIndex.erase( aAction->m_id );
|
m_actionIdIndex.erase( aAction->m_id );
|
||||||
|
@ -75,7 +76,7 @@ bool ACTION_MANAGER::RunAction( const std::string& aActionName ) const
|
||||||
std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );
|
std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );
|
||||||
|
|
||||||
if( it == m_actionNameIndex.end() )
|
if( it == m_actionNameIndex.end() )
|
||||||
return false;
|
return false; // no action with given name found
|
||||||
|
|
||||||
runAction( it->second );
|
runAction( it->second );
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
|
||||||
std::map<int, TOOL_ACTION*>::const_iterator it = m_actionHotKeys.find( aHotKey );
|
std::map<int, TOOL_ACTION*>::const_iterator it = m_actionHotKeys.find( aHotKey );
|
||||||
|
|
||||||
if( it == m_actionHotKeys.end() )
|
if( it == m_actionHotKeys.end() )
|
||||||
return false;
|
return false; // no appropriate action found for the hotkey
|
||||||
|
|
||||||
runAction( it->second );
|
runAction( it->second );
|
||||||
|
|
||||||
|
|
|
@ -62,13 +62,15 @@ CONTEXT_MENU::CONTEXT_MENU( const CONTEXT_MENU& aMenu ) :
|
||||||
wxEmptyString, wxITEM_NORMAL ) );
|
wxEmptyString, wxITEM_NORMAL ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy tool actions that are available to choose from menu
|
// Copy tool actions that are available to choose from context menu
|
||||||
m_toolActions = aMenu.m_toolActions;
|
m_toolActions = aMenu.m_toolActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CONTEXT_MENU::SetTitle( const wxString& aTitle )
|
void CONTEXT_MENU::SetTitle( const wxString& aTitle )
|
||||||
{
|
{
|
||||||
|
// TODO handle an empty string (remove title and separator)
|
||||||
|
|
||||||
// Unfortunately wxMenu::SetTitle() does nothing..
|
// Unfortunately wxMenu::SetTitle() does nothing..
|
||||||
if( m_titleSet )
|
if( m_titleSet )
|
||||||
{
|
{
|
||||||
|
@ -85,12 +87,18 @@ void CONTEXT_MENU::SetTitle( const wxString& aTitle )
|
||||||
|
|
||||||
void CONTEXT_MENU::Add( const wxString& aLabel, int aId )
|
void CONTEXT_MENU::Add( const wxString& aLabel, int aId )
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
if( m_menu.FindItem( aId ) != NULL )
|
||||||
|
wxLogWarning( wxT( "Adding more than one menu entry with the same ID may result in"
|
||||||
|
"undefined behaviour" ) );
|
||||||
|
#endif
|
||||||
m_menu.Append( new wxMenuItem( &m_menu, aId, aLabel, wxEmptyString, wxITEM_NORMAL ) );
|
m_menu.Append( new wxMenuItem( &m_menu, aId, aLabel, wxEmptyString, wxITEM_NORMAL ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CONTEXT_MENU::Add( const TOOL_ACTION& aAction )
|
void CONTEXT_MENU::Add( const TOOL_ACTION& aAction )
|
||||||
{
|
{
|
||||||
|
/// ID numbers for tool actions need to have a value higher than m_actionId
|
||||||
int id = m_actionId + aAction.GetId();
|
int id = m_actionId + aAction.GetId();
|
||||||
wxString menuEntry;
|
wxString menuEntry;
|
||||||
|
|
||||||
|
@ -110,6 +118,7 @@ void CONTEXT_MENU::Clear()
|
||||||
{
|
{
|
||||||
m_titleSet = false;
|
m_titleSet = false;
|
||||||
|
|
||||||
|
// Remove all the entries from context menu
|
||||||
for( unsigned i = 0; i < m_menu.GetMenuItemCount(); ++i )
|
for( unsigned i = 0; i < m_menu.GetMenuItemCount(); ++i )
|
||||||
m_menu.Destroy( m_menu.FindItemByPosition( 0 ) );
|
m_menu.Destroy( m_menu.FindItemByPosition( 0 ) );
|
||||||
|
|
||||||
|
@ -144,15 +153,18 @@ void CONTEXT_MENU::CMEventHandler::onEvent( wxEvent& aEvent )
|
||||||
|
|
||||||
if( type == wxEVT_MENU_HIGHLIGHT )
|
if( type == wxEVT_MENU_HIGHLIGHT )
|
||||||
evt = TOOL_EVENT( TC_Command, TA_ContextMenuUpdate, aEvent.GetId() );
|
evt = TOOL_EVENT( TC_Command, TA_ContextMenuUpdate, aEvent.GetId() );
|
||||||
|
|
||||||
else if( type == wxEVT_COMMAND_MENU_SELECTED )
|
else if( type == wxEVT_COMMAND_MENU_SELECTED )
|
||||||
{
|
{
|
||||||
if( aEvent.GetId() > m_actionId )
|
if( aEvent.GetId() > m_actionId )
|
||||||
{
|
{
|
||||||
|
// Handling TOOL_ACTIONs
|
||||||
if( m_menu->m_toolActions.count( aEvent.GetId() ) == 1 )
|
if( m_menu->m_toolActions.count( aEvent.GetId() ) == 1 )
|
||||||
evt = m_menu->m_toolActions[aEvent.GetId()]->GetEvent();
|
evt = m_menu->m_toolActions[aEvent.GetId()]->GetEvent();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Handling common menu entries
|
||||||
evt = TOOL_EVENT( TC_Command, TA_ContextMenuChoice, aEvent.GetId() );
|
evt = TOOL_EVENT( TC_Command, TA_ContextMenuChoice, aEvent.GetId() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,28 +42,41 @@ using boost::optional;
|
||||||
struct TOOL_DISPATCHER::ButtonState
|
struct TOOL_DISPATCHER::ButtonState
|
||||||
{
|
{
|
||||||
ButtonState( TOOL_MouseButtons aButton, const wxEventType& aDownEvent,
|
ButtonState( TOOL_MouseButtons aButton, const wxEventType& aDownEvent,
|
||||||
const wxEventType& aUpEvent, bool aTriggerMenu = false ) :
|
const wxEventType& aUpEvent ) :
|
||||||
button( aButton ),
|
button( aButton ),
|
||||||
downEvent( aDownEvent ),
|
downEvent( aDownEvent ),
|
||||||
upEvent( aUpEvent ),
|
upEvent( aUpEvent )
|
||||||
triggerContextMenu( aTriggerMenu )
|
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
///> Flag indicating that dragging is active for the given button.
|
||||||
bool dragging;
|
bool dragging;
|
||||||
|
|
||||||
|
///> Flag indicating that the given button is pressed.
|
||||||
bool pressed;
|
bool pressed;
|
||||||
|
|
||||||
|
///> Point where dragging has started (in world coordinates).
|
||||||
VECTOR2D dragOrigin;
|
VECTOR2D dragOrigin;
|
||||||
|
|
||||||
|
///> Point where click event has occurred.
|
||||||
VECTOR2D downPosition;
|
VECTOR2D downPosition;
|
||||||
|
|
||||||
|
///> Difference between drag origin point and current mouse position (expressed as distance in
|
||||||
|
///> pixels).
|
||||||
double dragMaxDelta;
|
double dragMaxDelta;
|
||||||
|
|
||||||
|
///> Determines the mouse button for which information are stored.
|
||||||
TOOL_MouseButtons button;
|
TOOL_MouseButtons button;
|
||||||
|
|
||||||
|
///> The type of wxEvent that determines mouse button press.
|
||||||
wxEventType downEvent;
|
wxEventType downEvent;
|
||||||
|
|
||||||
|
///> The type of wxEvent that determines mouse button release.
|
||||||
wxEventType upEvent;
|
wxEventType upEvent;
|
||||||
bool triggerContextMenu;
|
|
||||||
|
///> Time stamp for the last mouse button press event.
|
||||||
wxLongLong downTimestamp;
|
wxLongLong downTimestamp;
|
||||||
|
|
||||||
|
///> Restores initial state.
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
dragging = false;
|
dragging = false;
|
||||||
|
@ -76,7 +89,7 @@ TOOL_DISPATCHER::TOOL_DISPATCHER( TOOL_MANAGER* aToolMgr, PCB_BASE_FRAME* aEditF
|
||||||
m_toolMgr( aToolMgr ), m_editFrame( aEditFrame )
|
m_toolMgr( aToolMgr ), m_editFrame( aEditFrame )
|
||||||
{
|
{
|
||||||
m_buttons.push_back( new ButtonState( MB_Left, wxEVT_LEFT_DOWN, wxEVT_LEFT_UP ) );
|
m_buttons.push_back( new ButtonState( MB_Left, wxEVT_LEFT_DOWN, wxEVT_LEFT_UP ) );
|
||||||
m_buttons.push_back( new ButtonState( MB_Right, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_UP, true ) );
|
m_buttons.push_back( new ButtonState( MB_Right, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_UP ) );
|
||||||
m_buttons.push_back( new ButtonState( MB_Middle, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP ) );
|
m_buttons.push_back( new ButtonState( MB_Middle, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP ) );
|
||||||
|
|
||||||
ResetState();
|
ResetState();
|
||||||
|
@ -124,14 +137,14 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti
|
||||||
wxEventType type = aEvent.GetEventType();
|
wxEventType type = aEvent.GetEventType();
|
||||||
optional<TOOL_EVENT> evt;
|
optional<TOOL_EVENT> evt;
|
||||||
bool isClick = false;
|
bool isClick = false;
|
||||||
|
|
||||||
bool up = type == st->upEvent;
|
bool up = type == st->upEvent;
|
||||||
bool down = type == st->downEvent;
|
bool down = type == st->downEvent;
|
||||||
|
|
||||||
int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
|
int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
|
||||||
int args = st->button | mods;
|
int args = st->button | mods;
|
||||||
|
|
||||||
if( down )
|
if( down ) // Handle mouse button press
|
||||||
{
|
{
|
||||||
st->downTimestamp = wxGetLocalTimeMillis();
|
st->downTimestamp = wxGetLocalTimeMillis();
|
||||||
st->dragOrigin = m_lastMousePos;
|
st->dragOrigin = m_lastMousePos;
|
||||||
|
@ -140,7 +153,7 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti
|
||||||
st->pressed = true;
|
st->pressed = true;
|
||||||
evt = TOOL_EVENT( TC_Mouse, TA_MouseDown, args );
|
evt = TOOL_EVENT( TC_Mouse, TA_MouseDown, args );
|
||||||
}
|
}
|
||||||
else if( up )
|
else if( up ) // Handle mouse button release
|
||||||
{
|
{
|
||||||
st->pressed = false;
|
st->pressed = false;
|
||||||
|
|
||||||
|
@ -148,6 +161,7 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti
|
||||||
{
|
{
|
||||||
wxLongLong t = wxGetLocalTimeMillis();
|
wxLongLong t = wxGetLocalTimeMillis();
|
||||||
|
|
||||||
|
// Determine if it was just a single click or beginning of dragging
|
||||||
if( t - st->downTimestamp < DragTimeThreshold &&
|
if( t - st->downTimestamp < DragTimeThreshold &&
|
||||||
st->dragMaxDelta < DragDistanceThreshold )
|
st->dragMaxDelta < DragDistanceThreshold )
|
||||||
isClick = true;
|
isClick = true;
|
||||||
|
@ -156,7 +170,6 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
isClick = true;
|
isClick = true;
|
||||||
|
|
||||||
|
|
||||||
if( isClick )
|
if( isClick )
|
||||||
evt = TOOL_EVENT( TC_Mouse, TA_MouseClick, args );
|
evt = TOOL_EVENT( TC_Mouse, TA_MouseClick, args );
|
||||||
|
@ -208,6 +221,7 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
|
||||||
{
|
{
|
||||||
VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetCursorPosition();
|
VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetCursorPosition();
|
||||||
VECTOR2D pos = getView()->ToWorld( screenPos );
|
VECTOR2D pos = getView()->ToWorld( screenPos );
|
||||||
|
|
||||||
if( pos != m_lastMousePos || type == KiGfx::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE )
|
if( pos != m_lastMousePos || type == KiGfx::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE )
|
||||||
{
|
{
|
||||||
motion = true;
|
motion = true;
|
||||||
|
@ -251,7 +265,7 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent )
|
void TOOL_DISPATCHER::DispatchWxCommand( const wxCommandEvent& aEvent )
|
||||||
{
|
{
|
||||||
bool activateTool = false;
|
bool activateTool = false;
|
||||||
std::string toolName;
|
std::string toolName;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <base_struct.h>
|
//#include <base_struct.h>
|
||||||
|
|
||||||
#include <tool/tool_event.h>
|
#include <tool/tool_event.h>
|
||||||
#include <tool/tool_manager.h>
|
#include <tool/tool_manager.h>
|
||||||
|
|
|
@ -288,10 +288,7 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent )
|
||||||
st->pendingWait = false;
|
st->pendingWait = false;
|
||||||
st->waitEvents.clear();
|
st->waitEvents.clear();
|
||||||
if( st->cofunc && !st->cofunc->Resume() )
|
if( st->cofunc && !st->cofunc->Resume() )
|
||||||
{
|
finishTool( st ); // The couroutine has finished
|
||||||
// The couroutine has finished
|
|
||||||
finishTool( st );
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the tool did not request to propagate
|
// If the tool did not request to propagate
|
||||||
// the event to other tools, we should stop it now
|
// the event to other tools, we should stop it now
|
||||||
|
@ -303,6 +300,7 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent )
|
||||||
|
|
||||||
BOOST_FOREACH( TOOL_STATE* st, m_toolState | boost::adaptors::map_values )
|
BOOST_FOREACH( TOOL_STATE* st, m_toolState | boost::adaptors::map_values )
|
||||||
{
|
{
|
||||||
|
// the tool state handler is waiting for events (i.e. called Wait() method)
|
||||||
if( !st->pendingWait )
|
if( !st->pendingWait )
|
||||||
{
|
{
|
||||||
// no state handler in progress - check if there are any transitions (defined by
|
// no state handler in progress - check if there are any transitions (defined by
|
||||||
|
@ -324,9 +322,7 @@ void TOOL_MANAGER::dispatchInternal( TOOL_EVENT& aEvent )
|
||||||
st->cofunc->Call( aEvent );
|
st->cofunc->Call( aEvent );
|
||||||
|
|
||||||
if( !st->cofunc->Running() )
|
if( !st->cofunc->Running() )
|
||||||
{
|
finishTool( st ); // The couroutine has finished
|
||||||
finishTool( st );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,28 +93,27 @@ void acceptVisitor( T object, V visitor )
|
||||||
* @param minDistance minimum collision distance
|
* @param minDistance minimum collision distance
|
||||||
* @return if object and anotherObject collide
|
* @return if object and anotherObject collide
|
||||||
*/
|
*/
|
||||||
template <class T, class U>
|
template<class T, class U>
|
||||||
bool collide( T object, U anotherObject, int minDistance )
|
bool collide( T object, U anotherObject, int minDistance )
|
||||||
{
|
{
|
||||||
return shapeFunctor(object)->Collide( anotherObject, minDistance );
|
return shapeFunctor( object )->Collide( anotherObject, minDistance );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T, class V>
|
template<class T, class V>
|
||||||
bool queryCallback(T shape, void* context) {
|
bool queryCallback(T shape, void* context)
|
||||||
|
{
|
||||||
V* visitor = (V*) context;
|
V* visitor = (V*) context;
|
||||||
acceptVisitor<T,V>(shape, *visitor);
|
acceptVisitor<T,V>( shape, *visitor );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T = SHAPE*>
|
template<class T = SHAPE*>
|
||||||
class SHAPE_INDEX {
|
class SHAPE_INDEX {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
class Iterator
|
class Iterator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef typename RTree<T, int, 2, float>::Iterator RTreeIterator;
|
typedef typename RTree<T, int, 2, float>::Iterator RTreeIterator;
|
||||||
RTreeIterator iterator;
|
RTreeIterator iterator;
|
||||||
|
|
||||||
|
@ -124,20 +123,21 @@ class SHAPE_INDEX {
|
||||||
* Setup the internal tree iterator.
|
* Setup the internal tree iterator.
|
||||||
* @param tree pointer to a RTREE object
|
* @param tree pointer to a RTREE object
|
||||||
*/
|
*/
|
||||||
void Init(RTree<T, int, 2, float>* tree) {
|
void Init( RTree<T, int, 2, float>* tree )
|
||||||
tree->GetFirst(iterator);
|
{
|
||||||
|
tree->GetFirst( iterator );
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterator constructor
|
* Iterator constructor
|
||||||
*
|
*
|
||||||
* Creates an iterator for the index object
|
* Creates an iterator for the index object
|
||||||
* @param index SHAPE_INDEX object to iterate
|
* @param index SHAPE_INDEX object to iterate
|
||||||
*/
|
*/
|
||||||
Iterator(SHAPE_INDEX* index) {
|
Iterator( SHAPE_INDEX* index )
|
||||||
Init(index->m_tree);
|
{
|
||||||
|
Init( index->m_tree );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,7 +145,8 @@ class SHAPE_INDEX {
|
||||||
*
|
*
|
||||||
* Returns the next data element.
|
* Returns the next data element.
|
||||||
*/
|
*/
|
||||||
T operator*() {
|
T operator*()
|
||||||
|
{
|
||||||
return *iterator;
|
return *iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +155,8 @@ class SHAPE_INDEX {
|
||||||
*
|
*
|
||||||
* Shifts the iterator to the next element.
|
* Shifts the iterator to the next element.
|
||||||
*/
|
*/
|
||||||
bool operator++() {
|
bool operator++()
|
||||||
|
{
|
||||||
return ++iterator;
|
return ++iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +165,8 @@ class SHAPE_INDEX {
|
||||||
*
|
*
|
||||||
* Shifts the iterator to the next element.
|
* Shifts the iterator to the next element.
|
||||||
*/
|
*/
|
||||||
bool operator++(int) {
|
bool operator++( int )
|
||||||
|
{
|
||||||
return ++iterator;
|
return ++iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +176,8 @@ class SHAPE_INDEX {
|
||||||
* Checks if the iterator has reached the end.
|
* Checks if the iterator has reached the end.
|
||||||
* @return true if it is in an invalid position (data finished)
|
* @return true if it is in an invalid position (data finished)
|
||||||
*/
|
*/
|
||||||
bool IsNull() {
|
bool IsNull()
|
||||||
|
{
|
||||||
return iterator.IsNull();
|
return iterator.IsNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +187,8 @@ class SHAPE_INDEX {
|
||||||
* Checks if the iterator has not reached the end.
|
* Checks if the iterator has not reached the end.
|
||||||
* @return true if it is in an valid position (data not finished)
|
* @return true if it is in an valid position (data not finished)
|
||||||
*/
|
*/
|
||||||
bool IsNotNull() {
|
bool IsNotNull()
|
||||||
|
{
|
||||||
return iterator.IsNotNull();
|
return iterator.IsNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,12 +197,13 @@ class SHAPE_INDEX {
|
||||||
*
|
*
|
||||||
* Returns the current element of the iterator and moves to the next
|
* Returns the current element of the iterator and moves to the next
|
||||||
* position.
|
* position.
|
||||||
* @return SHAPE object pointed by the iterator before moving to the
|
* @return SHAPE object pointed by the iterator before moving to the next position.
|
||||||
* next position.
|
|
||||||
*/
|
*/
|
||||||
T Next() {
|
T Next()
|
||||||
|
{
|
||||||
T object = *iterator;
|
T object = *iterator;
|
||||||
++iterator;
|
++iterator;
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -210,17 +216,17 @@ class SHAPE_INDEX {
|
||||||
* Function Add()
|
* Function Add()
|
||||||
*
|
*
|
||||||
* Adds a SHAPE to the index.
|
* Adds a SHAPE to the index.
|
||||||
* @param shape the new SHAPE
|
* @param aShape is the new SHAPE.
|
||||||
*/
|
*/
|
||||||
void Add( T shape );
|
void Add( T aShape );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Remove()
|
* Function Remove()
|
||||||
*
|
*
|
||||||
* Removes a SHAPE to the index.
|
* Removes a SHAPE to the index.
|
||||||
* @param shape the new SHAPE
|
* @param aShape is the new SHAPE.
|
||||||
*/
|
*/
|
||||||
void Remove( T shape );
|
void Remove( T aShape );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function RemoveAll()
|
* Function RemoveAll()
|
||||||
|
@ -236,12 +242,14 @@ class SHAPE_INDEX {
|
||||||
* @param visitor Visitor object to be run
|
* @param visitor Visitor object to be run
|
||||||
*/
|
*/
|
||||||
template<class V>
|
template<class V>
|
||||||
void Accept( V visitor )
|
void Accept( V aVisitor )
|
||||||
{
|
{
|
||||||
Iterator iter = this->Begin();
|
Iterator iter = this->Begin();
|
||||||
while(!iter.IsNull()) {
|
|
||||||
|
while( !iter.IsNull() )
|
||||||
|
{
|
||||||
T shape = *iter;
|
T shape = *iter;
|
||||||
acceptVisitor(shape, visitor);
|
acceptVisitor( shape, aVisitor );
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,17 +270,16 @@ class SHAPE_INDEX {
|
||||||
* @param minDistance distance threshold
|
* @param minDistance distance threshold
|
||||||
* @param visitor object to be invoked on every object contained in the search area.
|
* @param visitor object to be invoked on every object contained in the search area.
|
||||||
*/
|
*/
|
||||||
|
template<class V>
|
||||||
template<class V>
|
int Query( const SHAPE *aShape, int aMinDistance, V& aVisitor, bool aExact )
|
||||||
int Query( const SHAPE *shape, int minDistance, V& visitor, bool aExact )
|
|
||||||
{
|
{
|
||||||
BOX2I box = shape->BBox();
|
BOX2I box = aShape->BBox();
|
||||||
box.Inflate(minDistance);
|
box.Inflate( aMinDistance );
|
||||||
|
|
||||||
int min[2] = {box.GetX(), box.GetY()};
|
int min[2] = { box.GetX(), box.GetY() };
|
||||||
int max[2] = {box.GetRight(), box.GetBottom()};
|
int max[2] = { box.GetRight(), box.GetBottom() };
|
||||||
|
|
||||||
return this->m_tree->Search(min, max, visitor);
|
return this->m_tree->Search( min, max, aVisitor );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -284,7 +291,6 @@ class SHAPE_INDEX {
|
||||||
Iterator Begin();
|
Iterator Begin();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
RTree<T, int, 2, float>* m_tree;
|
RTree<T, int, 2, float>* m_tree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -293,58 +299,68 @@ class SHAPE_INDEX {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
SHAPE_INDEX<T>::SHAPE_INDEX() {
|
SHAPE_INDEX<T>::SHAPE_INDEX()
|
||||||
|
{
|
||||||
this->m_tree = new RTree<T, int, 2, float>();
|
this->m_tree = new RTree<T, int, 2, float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
SHAPE_INDEX<T>::~SHAPE_INDEX() {
|
SHAPE_INDEX<T>::~SHAPE_INDEX()
|
||||||
|
{
|
||||||
delete this->m_tree;
|
delete this->m_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void SHAPE_INDEX<T>::Add(T shape) {
|
void SHAPE_INDEX<T>::Add( T aShape )
|
||||||
BOX2I box = boundingBox(shape);
|
{
|
||||||
int min[2]= {box.GetX(), box.GetY()};
|
BOX2I box = boundingBox( aShape );
|
||||||
int max[2] = {box.GetRight(), box.GetBottom()};
|
int min[2] = { box.GetX(), box.GetY() };
|
||||||
this->m_tree->Insert(min, max, shape);
|
int max[2] = { box.GetRight(), box.GetBottom() };
|
||||||
|
|
||||||
|
this->m_tree->Insert( min, max, aShape );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void SHAPE_INDEX<T>::Remove(T shape) {
|
void SHAPE_INDEX<T>::Remove(T aShape)
|
||||||
BOX2I box = boundingBox(shape);
|
{
|
||||||
int min[2]= {box.GetX(), box.GetY()};
|
BOX2I box = boundingBox( aShape );
|
||||||
int max[2] = {box.GetRight(), box.GetBottom()};
|
int min[2] = { box.GetX(), box.GetY() };
|
||||||
this->m_tree->Remove(min, max, shape);
|
int max[2] = { box.GetRight(), box.GetBottom() };
|
||||||
|
|
||||||
|
this->m_tree->Remove( min, max, aShape );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void SHAPE_INDEX<T>::RemoveAll() {
|
void SHAPE_INDEX<T>::RemoveAll()
|
||||||
|
{
|
||||||
this->m_tree->RemoveAll();
|
this->m_tree->RemoveAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void SHAPE_INDEX<T>::Reindex() {
|
void SHAPE_INDEX<T>::Reindex()
|
||||||
|
{
|
||||||
RTree<T, int, 2, float>* newTree;
|
RTree<T, int, 2, float>* newTree;
|
||||||
newTree = new RTree<T, int, 2, float>();
|
newTree = new RTree<T, int, 2, float>();
|
||||||
|
|
||||||
Iterator iter = this->Begin();
|
Iterator iter = this->Begin();
|
||||||
while(!iter.IsNull()) {
|
while( !iter.IsNull() )
|
||||||
|
{
|
||||||
T shape = *iter;
|
T shape = *iter;
|
||||||
BOX2I box = boundingBox(shape);
|
BOX2I box = boundingBox( shape );
|
||||||
int min[2]= {box.GetX(), box.GetY()};
|
int min[2] = { box.GetX(), box.GetY() };
|
||||||
int max[2] = {box.GetRight(), box.GetBottom()};
|
int max[2] = { box.GetRight(), box.GetBottom() };
|
||||||
newTree->Insert(min, max, shape);
|
newTree->Insert( min, max, shape );
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this->m_tree;
|
delete this->m_tree;
|
||||||
this->m_tree = newTree;
|
this->m_tree = newTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
typename SHAPE_INDEX<T>::Iterator SHAPE_INDEX<T>::Begin() {
|
typename SHAPE_INDEX<T>::Iterator SHAPE_INDEX<T>::Begin()
|
||||||
return Iterator(this);
|
{
|
||||||
|
return Iterator( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
* SHAPE_LINE_CHAIN class shall not be used for polygons!
|
* SHAPE_LINE_CHAIN class shall not be used for polygons!
|
||||||
*/
|
*/
|
||||||
class SHAPE_LINE_CHAIN : public SHAPE {
|
class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector<VECTOR2I>::iterator point_iter;
|
typedef std::vector<VECTOR2I>::iterator point_iter;
|
||||||
typedef std::vector<VECTOR2I>::const_iterator point_citer;
|
typedef std::vector<VECTOR2I>::const_iterator point_citer;
|
||||||
|
@ -55,7 +54,8 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
*
|
*
|
||||||
* Represents an intersection between two line segments
|
* Represents an intersection between two line segments
|
||||||
*/
|
*/
|
||||||
struct Intersection {
|
struct Intersection
|
||||||
|
{
|
||||||
/// segment belonging from the (this) argument of Intersect()
|
/// segment belonging from the (this) argument of Intersect()
|
||||||
SEG our;
|
SEG our;
|
||||||
/// segment belonging from the aOther argument of Intersect()
|
/// segment belonging from the aOther argument of Intersect()
|
||||||
|
@ -71,45 +71,45 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Initializes an empty line chain.
|
* Initializes an empty line chain.
|
||||||
*/
|
*/
|
||||||
SHAPE_LINE_CHAIN():
|
SHAPE_LINE_CHAIN():
|
||||||
SHAPE (SH_LINE_CHAIN), m_closed(false) {};
|
SHAPE( SH_LINE_CHAIN ), m_closed( false ) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy Constructor
|
* Copy Constructor
|
||||||
*/
|
*/
|
||||||
SHAPE_LINE_CHAIN(const SHAPE_LINE_CHAIN& aShape):
|
SHAPE_LINE_CHAIN( const SHAPE_LINE_CHAIN& aShape ) :
|
||||||
SHAPE (SH_LINE_CHAIN), m_points(aShape.m_points), m_closed(aShape.m_closed) {};
|
SHAPE( SH_LINE_CHAIN ), m_points( aShape.m_points ), m_closed( aShape.m_closed ) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* Initializes a 2-point line chain (a single segment)
|
* Initializes a 2-point line chain (a single segment)
|
||||||
*/
|
*/
|
||||||
SHAPE_LINE_CHAIN(const VECTOR2I& a, const VECTOR2I& b):
|
SHAPE_LINE_CHAIN( const VECTOR2I& aA, const VECTOR2I& aB ) :
|
||||||
SHAPE (SH_LINE_CHAIN),
|
SHAPE( SH_LINE_CHAIN ),
|
||||||
m_closed(false)
|
m_closed( false )
|
||||||
{
|
{
|
||||||
m_points.resize(2);
|
m_points.resize( 2 );
|
||||||
m_points[0] = a;
|
m_points[0] = aA;
|
||||||
m_points[1] = b;
|
m_points[1] = aB;
|
||||||
}
|
}
|
||||||
|
|
||||||
SHAPE_LINE_CHAIN(const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I& c):
|
SHAPE_LINE_CHAIN( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ):
|
||||||
SHAPE (SH_LINE_CHAIN),
|
SHAPE( SH_LINE_CHAIN ),
|
||||||
m_closed(false)
|
m_closed( false )
|
||||||
{
|
{
|
||||||
m_points.resize(3);
|
m_points.resize( 3 );
|
||||||
m_points[0] = a;
|
m_points[0] = aA;
|
||||||
m_points[1] = b;
|
m_points[1] = aB;
|
||||||
m_points[2] = c;
|
m_points[2] = aC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SHAPE_LINE_CHAIN(const VECTOR2I* aV, int aCount ) :
|
||||||
SHAPE_LINE_CHAIN(const VECTOR2I *v, int count):
|
SHAPE( SH_LINE_CHAIN ),
|
||||||
SHAPE (SH_LINE_CHAIN),
|
m_closed( false )
|
||||||
m_closed(false)
|
|
||||||
{
|
{
|
||||||
m_points.resize(count);
|
m_points.resize( aCount );
|
||||||
for(int i = 0; i < count ; i++)
|
|
||||||
m_points[i] = *v++;
|
for( int i = 0; i < aCount; i++ )
|
||||||
|
m_points[i] = *aV++;
|
||||||
}
|
}
|
||||||
|
|
||||||
~SHAPE_LINE_CHAIN() {};
|
~SHAPE_LINE_CHAIN() {};
|
||||||
|
@ -118,7 +118,8 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Function Clear()
|
* Function Clear()
|
||||||
* Removes all points from the line chain.
|
* Removes all points from the line chain.
|
||||||
*/
|
*/
|
||||||
void Clear() {
|
void Clear()
|
||||||
|
{
|
||||||
m_points.clear();
|
m_points.clear();
|
||||||
m_closed = false;
|
m_closed = false;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Marks the line chain as closed (i.e. with a segment connecting the last point with the first point).
|
* 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.
|
* @param aClosed: whether the line chain is to be closed or not.
|
||||||
*/
|
*/
|
||||||
void SetClosed(bool aClosed)
|
void SetClosed( bool aClosed )
|
||||||
{
|
{
|
||||||
m_closed = aClosed;
|
m_closed = aClosed;
|
||||||
}
|
}
|
||||||
|
@ -153,9 +154,10 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
int SegmentCount() const
|
int SegmentCount() const
|
||||||
{
|
{
|
||||||
int c = m_points.size() - 1;
|
int c = m_points.size() - 1;
|
||||||
if(m_closed)
|
if( m_closed )
|
||||||
c++;
|
c++;
|
||||||
return std::max(0, c);
|
|
||||||
|
return std::max( 0, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,86 +169,88 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
int PointCount() const
|
int PointCount() const
|
||||||
{
|
{
|
||||||
return m_points.size();
|
return m_points.size();
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Segment()
|
* Function Segment()
|
||||||
*
|
*
|
||||||
* Returns a segment referencing to the segment (index) in the line chain.
|
* 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.
|
* 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
|
* @param aIndex: 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)
|
* the last segment in the line chain)
|
||||||
* @return SEG referenced to given segment in the line chain
|
* @return SEG referenced to given segment in the line chain
|
||||||
*/
|
*/
|
||||||
SEG Segment ( int index )
|
SEG Segment( int aIndex )
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if( aIndex < 0 )
|
||||||
index += SegmentCount();
|
aIndex += SegmentCount();
|
||||||
|
|
||||||
if(index == (m_points.size() - 1) && m_closed )
|
if( aIndex == ( m_points.size() - 1 ) && m_closed )
|
||||||
return SEG ( m_points[index], m_points[0], index );
|
return SEG( m_points[aIndex], m_points[0], aIndex );
|
||||||
else
|
else
|
||||||
return SEG ( m_points[index], m_points[index + 1], index );
|
return SEG( m_points[aIndex], m_points[aIndex + 1], aIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function CSegment()
|
* Function CSegment()
|
||||||
*
|
*
|
||||||
* Returns a read-only segment referencing to the segment (index) in the line chain.
|
* 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
|
* @param aIndex: 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)
|
* the last segment in the line chain)
|
||||||
* @return SEG referenced to given segment in the line chain
|
* @return SEG referenced to given segment in the line chain
|
||||||
*/
|
*/
|
||||||
const SEG CSegment ( int index ) const
|
const SEG CSegment( int aIndex ) const
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if( aIndex < 0 )
|
||||||
index += SegmentCount();
|
aIndex += SegmentCount();
|
||||||
|
|
||||||
if(index == (m_points.size() - 1) && m_closed )
|
if( aIndex == ( m_points.size() - 1 ) && m_closed )
|
||||||
return SEG ( const_cast<VECTOR2I&>(m_points[index]),
|
return SEG( const_cast<VECTOR2I&>( m_points[aIndex] ),
|
||||||
const_cast<VECTOR2I&>(m_points[0]), index );
|
const_cast<VECTOR2I&>( m_points[0] ), aIndex );
|
||||||
else
|
else
|
||||||
return SEG ( const_cast<VECTOR2I&>(m_points[index]),
|
return SEG( const_cast<VECTOR2I&>( m_points[aIndex] ),
|
||||||
const_cast<VECTOR2I&>(m_points[index + 1]), index );
|
const_cast<VECTOR2I&>( m_points[aIndex + 1] ), aIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Point()
|
* Function Point()
|
||||||
*
|
*
|
||||||
* Returns a reference to a given point in the line chain.
|
* Returns a reference to a given point in the line chain.
|
||||||
* @param index index of the point
|
* @param aIndex index of the point
|
||||||
* @return reference to the point
|
* @return reference to the point
|
||||||
*/
|
*/
|
||||||
VECTOR2I& Point ( int index )
|
VECTOR2I& Point( int aIndex )
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if( aIndex < 0 )
|
||||||
index += PointCount();
|
aIndex += PointCount();
|
||||||
return m_points[index];
|
|
||||||
|
return m_points[aIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function CPoint()
|
* Function CPoint()
|
||||||
*
|
*
|
||||||
* Returns a const reference to a given point in the line chain.
|
* Returns a const reference to a given point in the line chain.
|
||||||
* @param index index of the point
|
* @param aIndex index of the point
|
||||||
* @return const reference to the point
|
* @return const reference to the point
|
||||||
*/
|
*/
|
||||||
const VECTOR2I& CPoint ( int index ) const
|
const VECTOR2I& CPoint( int aIndex ) const
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if( aIndex < 0 )
|
||||||
index += PointCount();
|
aIndex += PointCount();
|
||||||
return m_points[index];
|
|
||||||
|
return m_points[aIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @copydoc SHAPE::BBox()
|
/// @copydoc SHAPE::BBox()
|
||||||
const BOX2I BBox ( int aClearance = 0 ) const
|
const BOX2I BBox( int aClearance = 0 ) const
|
||||||
{
|
{
|
||||||
BOX2I bbox;
|
BOX2I bbox;
|
||||||
bbox.Compute(m_points);
|
bbox.Compute( m_points );
|
||||||
|
|
||||||
return bbox;
|
return bbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Collide()
|
* Function Collide()
|
||||||
*
|
*
|
||||||
|
@ -255,7 +259,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aClearance minimum distance that does not qualify as a collision.
|
* @param aClearance minimum distance that does not qualify as a collision.
|
||||||
* @return true, when a collision has been found
|
* @return true, when a collision has been found
|
||||||
*/
|
*/
|
||||||
bool Collide ( const VECTOR2I& aP, int aClearance = 0 ) const;
|
bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Collide()
|
* Function Collide()
|
||||||
|
@ -265,7 +269,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aClearance minimum distance that does not qualify as a collision.
|
* @param aClearance minimum distance that does not qualify as a collision.
|
||||||
* @return true, when a collision has been found
|
* @return true, when a collision has been found
|
||||||
*/
|
*/
|
||||||
bool Collide ( const BOX2I& aBox, int aClearance = 0 ) const;
|
bool Collide( const BOX2I& aBox, int aClearance = 0 ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Collide()
|
* Function Collide()
|
||||||
|
@ -275,9 +279,8 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aClearance minimum distance that does not qualify as a collision.
|
* @param aClearance minimum distance that does not qualify as a collision.
|
||||||
* @return true, when a collision has been found
|
* @return true, when a collision has been found
|
||||||
*/
|
*/
|
||||||
bool Collide ( const SEG& aSeg, int aClearance = 0 ) const;
|
bool Collide( const SEG& aSeg, int aClearance = 0 ) const;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Distance()
|
* Function Distance()
|
||||||
*
|
*
|
||||||
|
@ -285,7 +288,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aP the point
|
* @param aP the point
|
||||||
* @return minimum distance.
|
* @return minimum distance.
|
||||||
*/
|
*/
|
||||||
int Distance( const VECTOR2I & aP ) const;
|
int Distance( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Reverse()
|
* Function Reverse()
|
||||||
|
@ -307,13 +310,13 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Function Append()
|
* Function Append()
|
||||||
*
|
*
|
||||||
* Appends a new point at the end of the line chain.
|
* Appends a new point at the end of the line chain.
|
||||||
* @param x X coordinate of the new point
|
* @param aX is X coordinate of the new point
|
||||||
* @param y Y coordinate of the new point
|
* @param aY is Y coordinate of the new point
|
||||||
*/
|
*/
|
||||||
void Append(int x, int y)
|
void Append( int aX, int aY )
|
||||||
{
|
{
|
||||||
VECTOR2I v(x, y);
|
VECTOR2I v( aX, aY );
|
||||||
Append(v);
|
Append( v );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,15 +325,15 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Appends a new point at the end of the line chain.
|
* Appends a new point at the end of the line chain.
|
||||||
* @param aP the new point
|
* @param aP the new point
|
||||||
*/
|
*/
|
||||||
void Append(const VECTOR2I& aP)
|
void Append( const VECTOR2I& aP )
|
||||||
{
|
{
|
||||||
if(m_points.size() == 0)
|
if( m_points.size() == 0 )
|
||||||
m_bbox = BOX2I(aP, VECTOR2I(0, 0));
|
m_bbox = BOX2I( aP, VECTOR2I( 0, 0 ) );
|
||||||
|
|
||||||
if (m_points.size() == 0 || CPoint(-1) != aP)
|
if( m_points.size() == 0 || CPoint( -1 ) != aP )
|
||||||
{
|
{
|
||||||
m_points.push_back(aP);
|
m_points.push_back( aP );
|
||||||
m_bbox.Merge(aP);
|
m_bbox.Merge( aP );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,22 +343,23 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Appends another line chain at the end.
|
* Appends another line chain at the end.
|
||||||
* @param aOtherLine the line chain to be appended.
|
* @param aOtherLine the line chain to be appended.
|
||||||
*/
|
*/
|
||||||
void Append(const SHAPE_LINE_CHAIN& aOtherLine)
|
void Append( const SHAPE_LINE_CHAIN& aOtherLine )
|
||||||
{
|
{
|
||||||
if(aOtherLine.PointCount() == 0)
|
if( aOtherLine.PointCount() == 0 )
|
||||||
return;
|
return;
|
||||||
else if(PointCount() == 0 || aOtherLine.CPoint(0) != CPoint(-1))
|
|
||||||
|
else if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CPoint( -1 ) )
|
||||||
{
|
{
|
||||||
const VECTOR2I p = aOtherLine.CPoint(0);
|
const VECTOR2I p = aOtherLine.CPoint( 0 );
|
||||||
m_points.push_back(p);
|
m_points.push_back( p );
|
||||||
m_bbox.Merge(p);
|
m_bbox.Merge( p );
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 1; i<aOtherLine.PointCount(); i++)
|
for( int i = 1; i < aOtherLine.PointCount(); i++ )
|
||||||
{
|
{
|
||||||
const VECTOR2I p = aOtherLine.CPoint(i);
|
const VECTOR2I p = aOtherLine.CPoint( i );
|
||||||
m_points.push_back(p);
|
m_points.push_back( p );
|
||||||
m_bbox.Merge(p);
|
m_bbox.Merge( p );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,30 +368,31 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
*
|
*
|
||||||
* Replaces points with indices in range [start_index, end_index] with a single
|
* Replaces points with indices in range [start_index, end_index] with a single
|
||||||
* point aP.
|
* point aP.
|
||||||
* @param start_index start of the point range to be replaced (inclusive)
|
* @param aStartIndex start of the point range to be replaced (inclusive)
|
||||||
* @param end_index end of the point range to be replaced (inclusive)
|
* @param aEndIndex end of the point range to be replaced (inclusive)
|
||||||
* @param aP replacement point
|
* @param aP replacement point
|
||||||
*/
|
*/
|
||||||
void Replace( int start_index, int end_index, const VECTOR2I& aP);
|
void Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Replace()
|
* Function Replace()
|
||||||
*
|
*
|
||||||
* Replaces points with indices in range [start_index, end_index] with the points from line chain aLine.
|
* Replaces points with indices in range [start_index, end_index] with the points from
|
||||||
* @param start_index start of the point range to be replaced (inclusive)
|
* line chain aLine.
|
||||||
* @param end_index end of the point range to be replaced (inclusive)
|
* @param aStartIndex start of the point range to be replaced (inclusive)
|
||||||
|
* @param aEndIndex end of the point range to be replaced (inclusive)
|
||||||
* @param aLine replacement line chain.
|
* @param aLine replacement line chain.
|
||||||
*/
|
*/
|
||||||
void Replace( int start_index, int end_index, const SHAPE_LINE_CHAIN& aLine);
|
void Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE_CHAIN& aLine );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Remove()
|
* Function Remove()
|
||||||
*
|
*
|
||||||
* Removes the range of points [start_index, end_index] from the line chain.
|
* 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 aStartIndex start of the point range to be replaced (inclusive)
|
||||||
* @param end_index end of the point range to be replaced (inclusive)
|
* @param aEndIndex end of the point range to be replaced (inclusive)
|
||||||
*/
|
*/
|
||||||
void Remove( int start_index, int end_index);
|
void Remove( int aStartIndex, int aEndIndex );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Split()
|
* Function Split()
|
||||||
|
@ -397,7 +402,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aP the point to be inserted
|
* @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)
|
* @return index of the newly inserted point (or a negative value if aP does not lie on our line)
|
||||||
*/
|
*/
|
||||||
int Split( const VECTOR2I & aP );
|
int Split( const VECTOR2I& aP );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Find()
|
* Function Find()
|
||||||
|
@ -412,26 +417,25 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Function Slice()
|
* Function Slice()
|
||||||
*
|
*
|
||||||
* Returns a subset of this line chain containing the [start_index, end_index] range of points.
|
* 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 aStartIndex start of the point range to be returned (inclusive)
|
||||||
* @param end_index end of the point range to be returned (inclusive)
|
* @param aEndIndex end of the point range to be returned (inclusive)
|
||||||
* @return cut line chain.
|
* @return cut line chain.
|
||||||
*/
|
*/
|
||||||
const SHAPE_LINE_CHAIN Slice( int start_index, int end_index = -1) const;
|
const SHAPE_LINE_CHAIN Slice( int aStartIndex, int aEndIndex = -1) const;
|
||||||
|
|
||||||
|
|
||||||
struct compareOriginDistance {
|
struct compareOriginDistance
|
||||||
|
{
|
||||||
compareOriginDistance( VECTOR2I& aOrigin ):
|
compareOriginDistance( VECTOR2I& aOrigin ):
|
||||||
m_origin(aOrigin) {};
|
m_origin( aOrigin ) {};
|
||||||
|
|
||||||
bool operator()(const Intersection &a, const Intersection& b)
|
bool operator()( const Intersection& aA, const Intersection& aB )
|
||||||
{
|
{
|
||||||
return (m_origin - a.p).EuclideanNorm() < (m_origin - b.p).EuclideanNorm();
|
return ( m_origin - aA.p ).EuclideanNorm() < ( m_origin - aB.p ).EuclideanNorm();
|
||||||
}
|
}
|
||||||
|
|
||||||
VECTOR2I m_origin;
|
VECTOR2I m_origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Intersect()
|
* Function Intersect()
|
||||||
*
|
*
|
||||||
|
@ -452,7 +456,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* are sorted with increasing path lengths from the starting point of aChain.
|
* are sorted with increasing path lengths from the starting point of aChain.
|
||||||
* @return number of intersections found
|
* @return number of intersections found
|
||||||
*/
|
*/
|
||||||
int Intersect( const SHAPE_LINE_CHAIN &aChain, Intersections& aIp ) const;
|
int Intersect( const SHAPE_LINE_CHAIN& aChain, Intersections& aIp ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function PathLength()
|
* Function PathLength()
|
||||||
|
@ -461,7 +465,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* the point aP belonging to our line.
|
* the point aP belonging to our line.
|
||||||
* @return: path length in Euclidean metric or negative if aP does not belong to the line chain.
|
* @return: path length in Euclidean metric or negative if aP does not belong to the line chain.
|
||||||
*/
|
*/
|
||||||
int PathLength (const VECTOR2I& aP ) const;
|
int PathLength( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function PointInside()
|
* Function PointInside()
|
||||||
|
@ -471,7 +475,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aP point to check
|
* @param aP point to check
|
||||||
* @return true if the point is inside the shape (edge is not treated as being inside).
|
* @return true if the point is inside the shape (edge is not treated as being inside).
|
||||||
*/
|
*/
|
||||||
bool PointInside( const VECTOR2I& aP) const;
|
bool PointInside( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function PointOnEdge()
|
* Function PointOnEdge()
|
||||||
|
@ -480,7 +484,7 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* @param aP point to check
|
* @param aP point to check
|
||||||
* @return true if the point lies on the edge.
|
* @return true if the point lies on the edge.
|
||||||
*/
|
*/
|
||||||
bool PointOnEdge( const VECTOR2I& aP) const;
|
bool PointOnEdge( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function SelfIntersecting()
|
* Function SelfIntersecting()
|
||||||
|
@ -504,26 +508,32 @@ class SHAPE_LINE_CHAIN : public SHAPE {
|
||||||
* Finds a point on the line chain that is closest to point aP.
|
* Finds a point on the line chain that is closest to point aP.
|
||||||
* @return the nearest point.
|
* @return the nearest point.
|
||||||
*/
|
*/
|
||||||
const VECTOR2I NearestPoint(const VECTOR2I& aP) const;
|
const VECTOR2I NearestPoint( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
/// @copydoc SHAPE::Format()
|
/// @copydoc SHAPE::Format()
|
||||||
const std::string Format() const;
|
const std::string Format() const;
|
||||||
|
|
||||||
bool operator!=(const SHAPE_LINE_CHAIN& rhs) const
|
bool operator!=( const SHAPE_LINE_CHAIN& aRhs ) const
|
||||||
{
|
{
|
||||||
if(PointCount() != rhs.PointCount())
|
if( PointCount() != aRhs.PointCount() )
|
||||||
return true;
|
return true;
|
||||||
for(int i = 0; i < PointCount(); i++)
|
|
||||||
if( CPoint(i) != rhs.CPoint(i) )
|
for( int i = 0; i < PointCount(); i++ )
|
||||||
|
{
|
||||||
|
if( CPoint( i ) != aRhs.CPoint( i ) )
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// array of vertices
|
/// array of vertices
|
||||||
std::vector<VECTOR2I> m_points;
|
std::vector<VECTOR2I> m_points;
|
||||||
|
|
||||||
/// is the line chain closed?
|
/// is the line chain closed?
|
||||||
bool m_closed;
|
bool m_closed;
|
||||||
|
|
||||||
/// cached bounding box
|
/// cached bounding box
|
||||||
BOX2I m_bbox;
|
BOX2I m_bbox;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,21 +43,21 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function RegisterAction()
|
* Function RegisterAction()
|
||||||
* Adds a tool action to the manager set and sets it up. After that is is possible to invoke
|
* Adds a tool action to the manager and sets it up. After that is is possible to invoke
|
||||||
* the action using hotkeys or its name.
|
* the action using hotkeys or sending a command event with its name.
|
||||||
* @param aAction: action to be added. Ownership is not transferred.
|
* @param aAction: action to be added. Ownership is not transferred.
|
||||||
*/
|
*/
|
||||||
void RegisterAction( TOOL_ACTION* aAction );
|
void RegisterAction( TOOL_ACTION* aAction );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function UnregisterAction()
|
* Function UnregisterAction()
|
||||||
* Removes a tool action from the manager set and makes it unavailable for further usage.
|
* Removes a tool action from the manager and makes it unavailable for further usage.
|
||||||
* @param aAction: action to be removed.
|
* @param aAction: action to be removed.
|
||||||
*/
|
*/
|
||||||
void UnregisterAction( TOOL_ACTION* aAction );
|
void UnregisterAction( TOOL_ACTION* aAction );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an unique ID from for a tool with given name.
|
* Generates an unique ID from for an action with given name.
|
||||||
*/
|
*/
|
||||||
static int MakeActionId( const std::string& aActionName );
|
static int MakeActionId( const std::string& aActionName );
|
||||||
|
|
||||||
|
@ -82,9 +82,16 @@ public:
|
||||||
bool RunHotKey( int aHotKey ) const;
|
bool RunHotKey( int aHotKey ) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
///> Tool manager needed to run actions
|
||||||
TOOL_MANAGER* m_toolMgr;
|
TOOL_MANAGER* m_toolMgr;
|
||||||
|
|
||||||
|
///> Map for indexing actions by their IDs
|
||||||
std::map<int, TOOL_ACTION*> m_actionIdIndex;
|
std::map<int, TOOL_ACTION*> m_actionIdIndex;
|
||||||
|
|
||||||
|
///> Map for indexing actions by their names
|
||||||
std::map<std::string, TOOL_ACTION*> m_actionNameIndex;
|
std::map<std::string, TOOL_ACTION*> m_actionNameIndex;
|
||||||
|
|
||||||
|
///> Map for indexing actions by their hotkeys
|
||||||
std::map<int, TOOL_ACTION*> m_actionHotKeys;
|
std::map<int, TOOL_ACTION*> m_actionHotKeys;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <tool/tool_action.h>
|
#include <tool/tool_action.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
class wxMenu;
|
|
||||||
class TOOL_INTERACTIVE;
|
class TOOL_INTERACTIVE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,53 +39,102 @@ class TOOL_INTERACTIVE;
|
||||||
*/
|
*/
|
||||||
class CONTEXT_MENU
|
class CONTEXT_MENU
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
///> Default constructor
|
||||||
CONTEXT_MENU();
|
CONTEXT_MENU();
|
||||||
|
|
||||||
|
///> Copy constructor
|
||||||
CONTEXT_MENU( const CONTEXT_MENU& aMenu );
|
CONTEXT_MENU( const CONTEXT_MENU& aMenu );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function SetTitle()
|
||||||
|
* Sets title for the context menu. The title is shown as a text label shown on the top of
|
||||||
|
* the menu.
|
||||||
|
* @param aTitle is the new title.
|
||||||
|
*/
|
||||||
void SetTitle( const wxString& aTitle );
|
void SetTitle( const wxString& aTitle );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Add()
|
||||||
|
* Adds an entry to the menu. After highlighting/selecting the entry, a TOOL_EVENT command is
|
||||||
|
* sent that contains ID of the entry.
|
||||||
|
* @param aLabel is the text label show in the menu.
|
||||||
|
* @param aId is the ID that is sent in the TOOL_EVENT. It should be unique for every entry.
|
||||||
|
*/
|
||||||
void Add( const wxString& aLabel, int aId );
|
void Add( const wxString& aLabel, int aId );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Add()
|
||||||
|
* Adds an entry to the menu, basing on the TOOL_ACTION object. After selecting the entry,
|
||||||
|
* a TOOL_EVENT command containing name of the action is sent.
|
||||||
|
* @param aAction is the action to be added to menu entry.
|
||||||
|
*/
|
||||||
void Add( const TOOL_ACTION& aAction );
|
void Add( const TOOL_ACTION& aAction );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Clear()
|
||||||
|
* Removes all the entries from the menu (as well as its title). It leaves the menu in the
|
||||||
|
* initial state.
|
||||||
|
*/
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function GetMenu()
|
||||||
|
* Returns the instance of wxMenu object used to display the menu.
|
||||||
|
*/
|
||||||
wxMenu* GetMenu() const
|
wxMenu* GetMenu() const
|
||||||
{
|
{
|
||||||
return const_cast<wxMenu*>( &m_menu );
|
return const_cast<wxMenu*>( &m_menu );
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
///> Class CMEventHandler takes care of handling menu events. After reception of particular
|
||||||
|
///> events, it translates them to TOOL_EVENTs that may control tools.
|
||||||
class CMEventHandler : public wxEvtHandler
|
class CMEventHandler : public wxEvtHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
///> Default constructor
|
||||||
|
///> aMenu is the CONTEXT_MENU instance for which it handles events.
|
||||||
CMEventHandler( CONTEXT_MENU* aMenu ) : m_menu( aMenu ) {};
|
CMEventHandler( CONTEXT_MENU* aMenu ) : m_menu( aMenu ) {};
|
||||||
|
|
||||||
|
///> Handler for menu events.
|
||||||
void onEvent( wxEvent& aEvent );
|
void onEvent( wxEvent& aEvent );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
///> CONTEXT_MENU instance for which it handles events.
|
||||||
CONTEXT_MENU* m_menu;
|
CONTEXT_MENU* m_menu;
|
||||||
};
|
};
|
||||||
|
|
||||||
friend class TOOL_INTERACTIVE;
|
friend class TOOL_INTERACTIVE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function setTool()
|
||||||
|
* Sets a tool that is the creator of the menu.
|
||||||
|
* @param aTool is the tool that created the menu.
|
||||||
|
*/
|
||||||
void setTool( TOOL_INTERACTIVE* aTool )
|
void setTool( TOOL_INTERACTIVE* aTool )
|
||||||
{
|
{
|
||||||
m_tool = aTool;
|
m_tool = aTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Function getHotKeyDescription()
|
||||||
* Returns a hot key in the string format accepted by wxMenu.
|
* Returns a hot key in the string format accepted by wxMenu.
|
||||||
*
|
* @param aAction is the action with hot key to be translated..
|
||||||
* @param aAction is the action with hot key to be converted.
|
|
||||||
* @return Hot key in the string format compatible with wxMenu.
|
* @return Hot key in the string format compatible with wxMenu.
|
||||||
*/
|
*/
|
||||||
std::string getHotKeyDescription( const TOOL_ACTION& aAction ) const;
|
std::string getHotKeyDescription( const TOOL_ACTION& aAction ) const;
|
||||||
|
|
||||||
/// Flag indicating that the menu title was set up
|
///> Flag indicating that the menu title was set up.
|
||||||
bool m_titleSet;
|
bool m_titleSet;
|
||||||
|
|
||||||
|
///> Instance of wxMenu used for display of the context menu.
|
||||||
wxMenu m_menu;
|
wxMenu m_menu;
|
||||||
|
|
||||||
|
///> Instance of menu event handler.
|
||||||
CMEventHandler m_handler;
|
CMEventHandler m_handler;
|
||||||
|
|
||||||
|
///> Creator of the menu
|
||||||
TOOL_INTERACTIVE* m_tool;
|
TOOL_INTERACTIVE* m_tool;
|
||||||
|
|
||||||
/// Menu items with ID higher than that are considered TOOL_ACTIONs
|
/// Menu items with ID higher than that are considered TOOL_ACTIONs
|
||||||
|
|
|
@ -42,17 +42,21 @@ class VIEW_CONTROLS;
|
||||||
|
|
||||||
enum TOOL_Type
|
enum TOOL_Type
|
||||||
{
|
{
|
||||||
TOOL_Interactive = 0x1,
|
///> Tool that interacts with the user
|
||||||
TOOL_Batch = 0x2
|
TOOL_Interactive = 0x01,
|
||||||
|
|
||||||
|
///> Tool that runs in the background without any user intervention
|
||||||
|
TOOL_Batch = 0x02
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Unique identifier for tools
|
||||||
typedef int TOOL_ID;
|
typedef int TOOL_ID;
|
||||||
typedef DELEGATE<int, TOOL_EVENT&> TOOL_STATE_FUNC;
|
typedef DELEGATE<int, TOOL_EVENT&> TOOL_STATE_FUNC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class TOOL_BASE
|
* Class TOOL_BASE
|
||||||
*
|
*
|
||||||
* Base abstract interface for all kinds of tools
|
* Base abstract interface for all kinds of tools.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class TOOL_BASE
|
class TOOL_BASE
|
||||||
|
@ -61,25 +65,49 @@ public:
|
||||||
TOOL_BASE( TOOL_Type aType, TOOL_ID aId, const std::string& aName = std::string( "" ) ) :
|
TOOL_BASE( TOOL_Type aType, TOOL_ID aId, const std::string& aName = std::string( "" ) ) :
|
||||||
m_type( aType ),
|
m_type( aType ),
|
||||||
m_toolId( aId ),
|
m_toolId( aId ),
|
||||||
m_toolName( aName ) {};
|
m_toolName( aName ),
|
||||||
|
m_toolMgr( NULL ){};
|
||||||
|
|
||||||
virtual ~TOOL_BASE() {};
|
virtual ~TOOL_BASE() {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function GetType()
|
||||||
|
* Returns the type of the tool.
|
||||||
|
* @return The type of the tool.
|
||||||
|
*/
|
||||||
TOOL_Type GetType() const
|
TOOL_Type GetType() const
|
||||||
{
|
{
|
||||||
return m_type;
|
return m_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function GetId()
|
||||||
|
* Returns the unique identifier of the tool. The identifier is set by an instance of
|
||||||
|
* TOOL_MANAGER.
|
||||||
|
* @return Identifier of the tool.
|
||||||
|
*/
|
||||||
TOOL_ID GetId() const
|
TOOL_ID GetId() const
|
||||||
{
|
{
|
||||||
return m_toolId;
|
return m_toolId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function GetName()
|
||||||
|
* Returns the name of the tool. Tool names are expected to obey the format:
|
||||||
|
* application.ToolName (eg. pcbnew.InteractiveSelection).
|
||||||
|
* @return The name of the tool.
|
||||||
|
*/
|
||||||
const std::string& GetName() const
|
const std::string& GetName() const
|
||||||
{
|
{
|
||||||
return m_toolName;
|
return m_toolName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function GetManager()
|
||||||
|
* Returns the instance of TOOL_MANAGER that takes care of the tool.
|
||||||
|
* @return Instance of the TOOL_MANAGER. If there is no TOOL_MANAGER associated, it returns
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
TOOL_MANAGER* GetManager() const
|
TOOL_MANAGER* GetManager() const
|
||||||
{
|
{
|
||||||
return m_toolMgr;
|
return m_toolMgr;
|
||||||
|
@ -96,14 +124,27 @@ protected:
|
||||||
*/
|
*/
|
||||||
void attachManager( TOOL_MANAGER* aManager );
|
void attachManager( TOOL_MANAGER* aManager );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function getView()
|
||||||
|
*
|
||||||
|
* Returns the instance of VIEW object used in the application. It allows tools to draw.
|
||||||
|
* @return The instance of VIEW.
|
||||||
|
*/
|
||||||
KiGfx::VIEW* getView() const;
|
KiGfx::VIEW* getView() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function getViewControls()
|
||||||
|
*
|
||||||
|
* Returns the instance of VIEW_CONTROLS object used in the application. It allows tools to
|
||||||
|
* read & modify user input and its settings (eg. show cursor, enable snapping to grid, etc.)
|
||||||
|
* @return The instance of VIEW_CONTROLS.
|
||||||
|
*/
|
||||||
KiGfx::VIEW_CONTROLS* getViewControls() const;
|
KiGfx::VIEW_CONTROLS* getViewControls() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function getEditFrame()
|
* Function getEditFrame()
|
||||||
*
|
*
|
||||||
* Returns the application window object, casted to requested user type, possibly with
|
* Returns the application window object, casted to requested user type.
|
||||||
* run-time type check
|
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T* getEditFrame() const
|
T* getEditFrame() const
|
||||||
|
@ -124,8 +165,14 @@ protected:
|
||||||
return static_cast<T*>( m );
|
return static_cast<T*>( m );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Stores the type of the tool.
|
||||||
TOOL_Type m_type;
|
TOOL_Type m_type;
|
||||||
|
|
||||||
|
///> Unique identifier for the tool, assigned by a TOOL_MANAGER instance.
|
||||||
TOOL_ID m_toolId;
|
TOOL_ID m_toolId;
|
||||||
|
|
||||||
|
///> Name of the tool. Names are expected to obey the format application.ToolName
|
||||||
|
///> (eg. pcbnew.InteractiveSelection).
|
||||||
std::string m_toolName;
|
std::string m_toolName;
|
||||||
TOOL_MANAGER* m_toolMgr;
|
TOOL_MANAGER* m_toolMgr;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
|
|
||||||
#include <tool/tool_event.h>
|
#include <tool/tool_event.h>
|
||||||
|
|
||||||
//#include <wx/event.h>
|
|
||||||
#include <wx/kbdstate.h>
|
#include <wx/kbdstate.h>
|
||||||
|
|
||||||
class TOOL_MANAGER;
|
class TOOL_MANAGER;
|
||||||
|
@ -61,26 +60,62 @@ public:
|
||||||
TOOL_DISPATCHER( TOOL_MANAGER* aToolMgr, PCB_BASE_FRAME* aEditFrame );
|
TOOL_DISPATCHER( TOOL_MANAGER* aToolMgr, PCB_BASE_FRAME* aEditFrame );
|
||||||
virtual ~TOOL_DISPATCHER();
|
virtual ~TOOL_DISPATCHER();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function ResetState()
|
||||||
|
* Brings the dispatcher to its initial state.
|
||||||
|
*/
|
||||||
virtual void ResetState();
|
virtual void ResetState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function DispatchWxEvent()
|
||||||
|
* Processes wxEvents (mostly UI events), translates them to TOOL_EVENTs, and makes tools
|
||||||
|
* handle those.
|
||||||
|
* @param aEvent is the wxWidgets event to be processed.
|
||||||
|
*/
|
||||||
virtual void DispatchWxEvent( wxEvent& aEvent );
|
virtual void DispatchWxEvent( wxEvent& aEvent );
|
||||||
virtual void DispatchWxCommand( wxCommandEvent& aEvent );
|
|
||||||
|
/**
|
||||||
|
* Function DispatchWxCommand()
|
||||||
|
* Processes wxCommands (mostly menu related events) and runs appropriate actions (eg. run the
|
||||||
|
* specified tool).
|
||||||
|
* @param aEvent is the wxCommandEvent to be processed.
|
||||||
|
*/
|
||||||
|
virtual void DispatchWxCommand( const wxCommandEvent& aEvent );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
///> Number of mouse buttons that is handled in events.
|
||||||
static const int MouseButtonCount = 3;
|
static const int MouseButtonCount = 3;
|
||||||
|
|
||||||
|
///> The time threshold for a mouse button press that distinguishes between a single mouse
|
||||||
|
///> click and a beginning of drag event (expressed in milliseconds).
|
||||||
static const int DragTimeThreshold = 300;
|
static const int DragTimeThreshold = 300;
|
||||||
|
|
||||||
|
///> The distance threshold for mouse cursor that disinguishes between a single mouse click
|
||||||
|
///> and a beginning of drag event (expressed in screen pixels).
|
||||||
static const int DragDistanceThreshold = 8;
|
static const int DragDistanceThreshold = 8;
|
||||||
|
|
||||||
|
///> Handles mouse related events (click, motion, dragging)
|
||||||
bool handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion );
|
bool handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion );
|
||||||
bool handlePopupMenu( wxEvent& aEvent );
|
|
||||||
|
|
||||||
|
///> Saves the state of key modifiers (Alt, Ctrl and so on).
|
||||||
int decodeModifiers( const wxKeyboardState* aState ) const;
|
int decodeModifiers( const wxKeyboardState* aState ) const;
|
||||||
|
|
||||||
|
///> Stores all the informations regarding a mouse button state.
|
||||||
struct ButtonState;
|
struct ButtonState;
|
||||||
|
|
||||||
|
///> The last mouse cursor position (in world coordinates).
|
||||||
VECTOR2D m_lastMousePos;
|
VECTOR2D m_lastMousePos;
|
||||||
|
|
||||||
|
///> State of mouse buttons.
|
||||||
std::vector<ButtonState*> m_buttons;
|
std::vector<ButtonState*> m_buttons;
|
||||||
|
|
||||||
|
///> Returns the instance of VIEW, used by the application.
|
||||||
KiGfx::VIEW* getView();
|
KiGfx::VIEW* getView();
|
||||||
|
|
||||||
|
///> Instance of tool manager that cooperates with the dispatcher.
|
||||||
TOOL_MANAGER* m_toolMgr;
|
TOOL_MANAGER* m_toolMgr;
|
||||||
|
|
||||||
|
///> Instance of wxFrame that is the source of UI events.
|
||||||
PCB_BASE_FRAME* m_editFrame;
|
PCB_BASE_FRAME* m_editFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
#include <math/vector2d.h>
|
#include <math/vector2d.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
@ -71,17 +72,19 @@ enum TOOL_Actions
|
||||||
TA_ViewDirty = 0x0800,
|
TA_ViewDirty = 0x0800,
|
||||||
TA_ChangeLayer = 0x1000,
|
TA_ChangeLayer = 0x1000,
|
||||||
|
|
||||||
// Tool cancel event. Issued automagically when the user hits escape or selects End Tool from the context menu.
|
// Tool cancel event. Issued automagically when the user hits escape or selects End Tool from
|
||||||
|
// the context menu.
|
||||||
TA_CancelTool = 0x2000,
|
TA_CancelTool = 0x2000,
|
||||||
|
|
||||||
// Context menu update. Issued whenever context menu is open and the user hovers the mouse over one of choices.
|
// Context menu update. Issued whenever context menu is open and the user hovers the mouse
|
||||||
// Used in dynamic highligting in disambiguation menu
|
// over one of choices. Used in dynamic highligting in disambiguation menu
|
||||||
TA_ContextMenuUpdate = 0x4000,
|
TA_ContextMenuUpdate = 0x4000,
|
||||||
|
|
||||||
// Context menu choice. Sent if the user picked something from the context menu or closed it without selecting anything.
|
// Context menu choice. Sent if the user picked something from the context menu or
|
||||||
|
// closed it without selecting anything.
|
||||||
TA_ContextMenuChoice = 0x8000,
|
TA_ContextMenuChoice = 0x8000,
|
||||||
|
|
||||||
// Tool action
|
// Tool action (allows to control tools)
|
||||||
TA_Action = 0x10000,
|
TA_Action = 0x10000,
|
||||||
|
|
||||||
TA_Any = 0xffffffff
|
TA_Any = 0xffffffff
|
||||||
|
@ -129,6 +132,12 @@ enum CONTEXT_MENU_TRIGGER
|
||||||
class TOOL_EVENT
|
class TOOL_EVENT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Function Format()
|
||||||
|
* Returns information about event in form of a human-readable string.
|
||||||
|
*
|
||||||
|
* @return Event information.
|
||||||
|
*/
|
||||||
const std::string Format() const;
|
const std::string Format() const;
|
||||||
|
|
||||||
TOOL_EVENT( TOOL_EventCategory aCategory = TC_None, TOOL_Actions aAction = TA_None,
|
TOOL_EVENT( TOOL_EventCategory aCategory = TC_None, TOOL_Actions aAction = TA_None,
|
||||||
|
@ -175,33 +184,44 @@ public:
|
||||||
m_commandStr = aExtraParam;
|
m_commandStr = aExtraParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns the category (eg. mouse/keyboard/action) of an event..
|
||||||
TOOL_EventCategory Category() const
|
TOOL_EventCategory Category() const
|
||||||
{
|
{
|
||||||
return m_category;
|
return m_category;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns more specific information about the type of an event.
|
||||||
TOOL_Actions Action() const
|
TOOL_Actions Action() const
|
||||||
{
|
{
|
||||||
return m_actions;
|
return m_actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns information about difference between current mouse cursor position and the place
|
||||||
|
///> where dragging has started.
|
||||||
const VECTOR2D Delta() const
|
const VECTOR2D Delta() const
|
||||||
{
|
{
|
||||||
|
assert( m_category == TC_Mouse ); // this should be used only with mouse events
|
||||||
return m_mouseDelta;
|
return m_mouseDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns mouse cursor position in world coordinates.
|
||||||
const VECTOR2D& Position() const
|
const VECTOR2D& Position() const
|
||||||
{
|
{
|
||||||
|
assert( m_category == TC_Mouse ); // this should be used only with mouse events
|
||||||
return m_mousePos;
|
return m_mousePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns the point where dragging has started.
|
||||||
const VECTOR2D& DragOrigin() const
|
const VECTOR2D& DragOrigin() const
|
||||||
{
|
{
|
||||||
|
assert( m_category == TC_Mouse ); // this should be used only with mouse events
|
||||||
return m_mouseDragOrigin;
|
return m_mouseDragOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns information about mouse buttons state.
|
||||||
int Buttons() const
|
int Buttons() const
|
||||||
{
|
{
|
||||||
|
assert( m_category == TC_Mouse ); // this should be used only with mouse events
|
||||||
return m_mouseButtons;
|
return m_mouseButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +251,7 @@ public:
|
||||||
return m_actions == TA_CancelTool;
|
return m_actions == TA_CancelTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///> Returns information about key modifiers state (Ctrl, Alt, etc.)
|
||||||
int Modifier( int aMask = MD_ModifierMask ) const
|
int Modifier( int aMask = MD_ModifierMask ) const
|
||||||
{
|
{
|
||||||
return ( m_modifiers & aMask );
|
return ( m_modifiers & aMask );
|
||||||
|
@ -251,8 +272,6 @@ public:
|
||||||
return m_actions == TA_KeyDown;
|
return m_actions == TA_KeyDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ignore();
|
|
||||||
|
|
||||||
void SetMouseDragOrigin( const VECTOR2D &aP )
|
void SetMouseDragOrigin( const VECTOR2D &aP )
|
||||||
{
|
{
|
||||||
m_mouseDragOrigin = aP;
|
m_mouseDragOrigin = aP;
|
||||||
|
@ -268,6 +287,13 @@ public:
|
||||||
m_mouseDelta = aP;
|
m_mouseDelta = aP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Matches()
|
||||||
|
* Tests whether two events match in terms of category & action or command.
|
||||||
|
*
|
||||||
|
* @param aEvent is the event to test against.
|
||||||
|
* @return True if two events match, false otherwise.
|
||||||
|
*/
|
||||||
bool Matches( const TOOL_EVENT& aEvent ) const
|
bool Matches( const TOOL_EVENT& aEvent ) const
|
||||||
{
|
{
|
||||||
if( !( m_category & aEvent.m_category ) )
|
if( !( m_category & aEvent.m_category ) )
|
||||||
|
@ -299,13 +325,25 @@ private:
|
||||||
TOOL_Actions m_actions;
|
TOOL_Actions m_actions;
|
||||||
TOOL_ActionScope m_scope;
|
TOOL_ActionScope m_scope;
|
||||||
|
|
||||||
|
///> Difference between mouse cursor position and
|
||||||
|
///> the point where dragging event has started
|
||||||
VECTOR2D m_mouseDelta;
|
VECTOR2D m_mouseDelta;
|
||||||
|
|
||||||
|
///> Current mouse cursor position
|
||||||
VECTOR2D m_mousePos;
|
VECTOR2D m_mousePos;
|
||||||
|
|
||||||
|
///> Point where dragging has started
|
||||||
VECTOR2D m_mouseDragOrigin;
|
VECTOR2D m_mouseDragOrigin;
|
||||||
|
|
||||||
|
///> State of mouse buttons
|
||||||
int m_mouseButtons;
|
int m_mouseButtons;
|
||||||
|
|
||||||
|
///> Stores code of pressed/released key
|
||||||
int m_keyCode;
|
int m_keyCode;
|
||||||
|
|
||||||
|
///> State of key modifierts (Ctrl/Alt/etc.)
|
||||||
int m_modifiers;
|
int m_modifiers;
|
||||||
|
|
||||||
boost::optional<int> m_commandId;
|
boost::optional<int> m_commandId;
|
||||||
boost::optional<std::string> m_commandStr;
|
boost::optional<std::string> m_commandStr;
|
||||||
};
|
};
|
||||||
|
@ -324,7 +362,10 @@ public:
|
||||||
typedef std::deque<TOOL_EVENT>::iterator iterator;
|
typedef std::deque<TOOL_EVENT>::iterator iterator;
|
||||||
typedef std::deque<TOOL_EVENT>::const_iterator const_iterator;
|
typedef std::deque<TOOL_EVENT>::const_iterator const_iterator;
|
||||||
|
|
||||||
|
///> Default constructor. Creates an empty list.
|
||||||
TOOL_EVENT_LIST() {};
|
TOOL_EVENT_LIST() {};
|
||||||
|
|
||||||
|
///> Constructor for a list containing only one TOOL_EVENT.
|
||||||
TOOL_EVENT_LIST( const TOOL_EVENT& aSingleEvent )
|
TOOL_EVENT_LIST( const TOOL_EVENT& aSingleEvent )
|
||||||
{
|
{
|
||||||
m_events.push_back( aSingleEvent );
|
m_events.push_back( aSingleEvent );
|
||||||
|
@ -346,6 +387,11 @@ public:
|
||||||
return boost::optional<const TOOL_EVENT&>();
|
return boost::optional<const TOOL_EVENT&>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Add()
|
||||||
|
* Adds a tool event to the list.
|
||||||
|
* @param aEvent is the tool event to be addded.
|
||||||
|
*/
|
||||||
void Add( const TOOL_EVENT& aEvent )
|
void Add( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
m_events.push_back( aEvent );
|
m_events.push_back( aEvent );
|
||||||
|
|
|
@ -35,6 +35,10 @@ class CONTEXT_MENU;
|
||||||
class TOOL_INTERACTIVE : public TOOL_BASE
|
class TOOL_INTERACTIVE : public TOOL_BASE
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* Creates a tool with given id & name. The name must be unique. */
|
||||||
TOOL_INTERACTIVE( TOOL_ID aId, const std::string& aName );
|
TOOL_INTERACTIVE( TOOL_ID aId, const std::string& aName );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,8 +50,8 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Reset()
|
* Function Reset()
|
||||||
* Brings the tool to a known, initial state. If the tool claimed anything from the model or the view,
|
* Brings the tool to a known, initial state. If the tool claimed anything from
|
||||||
* it must release it when its reset.
|
* the model or the view, it must release it when its reset.
|
||||||
*/
|
*/
|
||||||
virtual void Reset() = 0;
|
virtual void Reset() = 0;
|
||||||
|
|
||||||
|
@ -65,7 +69,9 @@ public:
|
||||||
/**
|
/**
|
||||||
* Function SetContextMenu()
|
* Function SetContextMenu()
|
||||||
*
|
*
|
||||||
* Assigns a context menu and tells when it should be activated
|
* Assigns a context menu and tells when it should be activated.
|
||||||
|
* @param aMenu is the menu to be assigned.
|
||||||
|
* @param aTrigger determines conditions upon which the context menu is activated.
|
||||||
*/
|
*/
|
||||||
void SetContextMenu( CONTEXT_MENU* aMenu, CONTEXT_MENU_TRIGGER aTrigger = CMENU_BUTTON );
|
void SetContextMenu( CONTEXT_MENU* aMenu, CONTEXT_MENU_TRIGGER aTrigger = CMENU_BUTTON );
|
||||||
|
|
||||||
|
@ -87,9 +93,8 @@ public:
|
||||||
*/
|
*/
|
||||||
OPT_TOOL_EVENT Wait( const TOOL_EVENT_LIST& aEventList = TOOL_EVENT ( TC_Any, TA_Any ) );
|
OPT_TOOL_EVENT Wait( const TOOL_EVENT_LIST& aEventList = TOOL_EVENT ( TC_Any, TA_Any ) );
|
||||||
|
|
||||||
|
|
||||||
/** functions below are not yet implemented - their interface may change */
|
/** functions below are not yet implemented - their interface may change */
|
||||||
template<class Parameters, class ReturnValue>
|
/*template<class Parameters, class ReturnValue>
|
||||||
bool InvokeTool( const std::string& aToolName, const Parameters& parameters,
|
bool InvokeTool( const std::string& aToolName, const Parameters& parameters,
|
||||||
ReturnValue& returnValue );
|
ReturnValue& returnValue );
|
||||||
|
|
||||||
|
@ -98,11 +103,10 @@ public:
|
||||||
ReturnValue& returnValue );
|
ReturnValue& returnValue );
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void Yield( const T& returnValue );
|
void Yield( const T& returnValue );*/
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* helper functions for constructing events for Wait() and Go() with
|
/* helper functions for constructing events for Wait() and Go() with less typing */
|
||||||
less typing */
|
|
||||||
const TOOL_EVENT evActivate( std::string aToolName = "" );
|
const TOOL_EVENT evActivate( std::string aToolName = "" );
|
||||||
const TOOL_EVENT evCommand( int aCommandId = -1 );
|
const TOOL_EVENT evCommand( int aCommandId = -1 );
|
||||||
const TOOL_EVENT evCommand( std::string aCommandStr = "" );
|
const TOOL_EVENT evCommand( std::string aCommandStr = "" );
|
||||||
|
|
|
@ -36,6 +36,8 @@ using boost::optional;
|
||||||
|
|
||||||
MOVE_TOOL::MOVE_TOOL() :
|
MOVE_TOOL::MOVE_TOOL() :
|
||||||
TOOL_INTERACTIVE( "pcbnew.InteractiveMove" ), m_selectionTool( NULL ),
|
TOOL_INTERACTIVE( "pcbnew.InteractiveMove" ), m_selectionTool( NULL ),
|
||||||
|
|
||||||
|
// Available actions:
|
||||||
m_activate( m_toolName, AS_GLOBAL, 'M', "Move", "Moves the selected item(s)" ),
|
m_activate( m_toolName, AS_GLOBAL, 'M', "Move", "Moves the selected item(s)" ),
|
||||||
m_rotate( m_toolName + ".rotate", AS_CONTEXT, ' ', "Rotate", "Rotates selected item(s)" ),
|
m_rotate( m_toolName + ".rotate", AS_CONTEXT, ' ', "Rotate", "Rotates selected item(s)" ),
|
||||||
m_flip( m_toolName + ".flip", AS_CONTEXT, 'F', "Flip", "Flips selected item(s)" )
|
m_flip( m_toolName + ".flip", AS_CONTEXT, 'F', "Flip", "Flips selected item(s)" )
|
||||||
|
@ -69,7 +71,7 @@ bool MOVE_TOOL::Init()
|
||||||
m_toolMgr->RegisterAction( &m_rotate );
|
m_toolMgr->RegisterAction( &m_rotate );
|
||||||
m_toolMgr->RegisterAction( &m_flip );
|
m_toolMgr->RegisterAction( &m_flip );
|
||||||
|
|
||||||
// Add context menu entries for the selection tool
|
// Add context menu entries that are displayed when selection tool is active
|
||||||
m_selectionTool->AddMenuItem( m_activate );
|
m_selectionTool->AddMenuItem( m_activate );
|
||||||
m_selectionTool->AddMenuItem( m_rotate );
|
m_selectionTool->AddMenuItem( m_rotate );
|
||||||
m_selectionTool->AddMenuItem( m_flip );
|
m_selectionTool->AddMenuItem( m_flip );
|
||||||
|
@ -92,7 +94,9 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
VIEW* view = getView();
|
VIEW* view = getView();
|
||||||
VIEW_CONTROLS* controls = getViewControls();
|
VIEW_CONTROLS* controls = getViewControls();
|
||||||
|
|
||||||
|
// Add a VIEW_GROUP that will hold all modified items
|
||||||
view->Add( &m_items );
|
view->Add( &m_items );
|
||||||
|
|
||||||
controls->ShowCursor( true );
|
controls->ShowCursor( true );
|
||||||
controls->SetSnapping( true );
|
controls->SetSnapping( true );
|
||||||
controls->SetAutoPan( true );
|
controls->SetAutoPan( true );
|
||||||
|
@ -102,7 +106,7 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
if( evt->IsCancel() )
|
if( evt->IsCancel() )
|
||||||
{
|
{
|
||||||
restore = true;
|
restore = true; // Cancelling the tool means that items have to be restored
|
||||||
break; // Finish
|
break; // Finish
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,12 +115,12 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
VECTOR2D cursorPos = getView()->ToWorld( getViewControls()->GetCursorPosition() );
|
VECTOR2D cursorPos = getView()->ToWorld( getViewControls()->GetCursorPosition() );
|
||||||
|
|
||||||
if( evt->Matches( m_rotate.GetEvent() ) )
|
if( evt->Matches( m_rotate.GetEvent() ) ) // got rotation event?
|
||||||
{
|
{
|
||||||
m_state.Rotate( cursorPos, 900.0 );
|
m_state.Rotate( cursorPos, 900.0 );
|
||||||
m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
|
m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
|
||||||
}
|
}
|
||||||
else if( evt->Matches( m_flip.GetEvent() ) )
|
else if( evt->Matches( m_flip.GetEvent() ) ) // got flip event?
|
||||||
{
|
{
|
||||||
m_state.Flip( cursorPos );
|
m_state.Flip( cursorPos );
|
||||||
m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
|
m_items.ViewUpdate( VIEW_ITEM::GEOMETRY );
|
||||||
|
@ -165,20 +169,22 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
// Restore visibility of the original items
|
// Restore visibility of the original items
|
||||||
vgSetVisibility( &m_items, true );
|
vgSetVisibility( &m_items, true );
|
||||||
|
|
||||||
// Movement has to be rollbacked, so restore the previous state of items
|
|
||||||
if( restore )
|
if( restore )
|
||||||
{
|
{
|
||||||
|
// Modifications has to be rollbacked, so restore the previous state of items
|
||||||
vgUpdate( &m_items, VIEW_ITEM::APPEARANCE );
|
vgUpdate( &m_items, VIEW_ITEM::APPEARANCE );
|
||||||
m_state.RestoreAll();
|
m_state.RestoreAll();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Changes are applied, so update the items
|
||||||
vgUpdate( &m_items, m_state.GetUpdateFlag() );
|
vgUpdate( &m_items, m_state.GetUpdateFlag() );
|
||||||
m_state.Apply();
|
m_state.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_items.Clear();
|
m_items.Clear();
|
||||||
view->Remove( &m_items );
|
view->Remove( &m_items );
|
||||||
|
|
||||||
controls->ShowCursor( false );
|
controls->ShowCursor( false );
|
||||||
controls->SetSnapping( false );
|
controls->SetSnapping( false );
|
||||||
controls->SetAutoPan( false );
|
controls->SetAutoPan( false );
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
virtual const BOX2I ViewBBox() const;
|
virtual const BOX2I ViewBBox() const;
|
||||||
|
|
||||||
void ViewDraw( int aLayer, KiGfx::GAL* aGal ) const;
|
void ViewDraw( int aLayer, KiGfx::GAL* aGal ) const;
|
||||||
|
|
||||||
void ViewGetLayers( int aLayers[], int& aCount ) const;
|
void ViewGetLayers( int aLayers[], int& aCount ) const;
|
||||||
|
|
||||||
void SetOrigin ( VECTOR2I aOrigin )
|
void SetOrigin ( VECTOR2I aOrigin )
|
||||||
|
|
|
@ -83,7 +83,6 @@ bool SELECTION_TOOL::Init()
|
||||||
int SELECTION_TOOL::Main( TOOL_EVENT& aEvent )
|
int SELECTION_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
BOARD* board = getModel<BOARD>( PCB_T );
|
BOARD* board = getModel<BOARD>( PCB_T );
|
||||||
|
|
||||||
assert( board != NULL );
|
assert( board != NULL );
|
||||||
|
|
||||||
// Main loop: keep receiving events
|
// Main loop: keep receiving events
|
||||||
|
@ -116,13 +115,16 @@ int SELECTION_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Check if dragging has started within any of selected items bounding box
|
||||||
if( containsSelected( evt->Position() ) )
|
if( containsSelected( evt->Position() ) )
|
||||||
{
|
{
|
||||||
|
// Yes -> run the move tool and wait till it finishes
|
||||||
m_toolMgr->InvokeTool( "pcbnew.InteractiveMove" );
|
m_toolMgr->InvokeTool( "pcbnew.InteractiveMove" );
|
||||||
Wait();
|
Wait();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// No -> clear the selection list
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +137,7 @@ int SELECTION_TOOL::Main( TOOL_EVENT& aEvent )
|
||||||
|
|
||||||
void SELECTION_TOOL::AddMenuItem( const TOOL_ACTION& aAction )
|
void SELECTION_TOOL::AddMenuItem( const TOOL_ACTION& aAction )
|
||||||
{
|
{
|
||||||
assert( aAction.GetId() > 0 ); // Check if the action was registered before
|
assert( aAction.GetId() > 0 ); // Check if the action was registered before in ACTION_MANAGER
|
||||||
|
|
||||||
m_menu.Add( aAction );
|
m_menu.Add( aAction );
|
||||||
}
|
}
|
||||||
|
@ -259,9 +261,9 @@ BOARD_ITEM* SELECTION_TOOL::pickSmallestComponent( GENERAL_COLLECTOR* aCollector
|
||||||
|
|
||||||
bool SELECTION_TOOL::selectMultiple()
|
bool SELECTION_TOOL::selectMultiple()
|
||||||
{
|
{
|
||||||
VIEW* view = getView();
|
bool cancelled = false; // Was the tool cancelled while it was running?
|
||||||
bool cancelled = false;
|
|
||||||
m_multiple = true; // Multiple selection mode is active
|
m_multiple = true; // Multiple selection mode is active
|
||||||
|
VIEW* view = getView();
|
||||||
getViewControls()->SetAutoPan( true );
|
getViewControls()->SetAutoPan( true );
|
||||||
|
|
||||||
// Those 2 lines remove the blink-in-the-random-place effect
|
// Those 2 lines remove the blink-in-the-random-place effect
|
||||||
|
@ -291,7 +293,7 @@ bool SELECTION_TOOL::selectMultiple()
|
||||||
|
|
||||||
if( evt->IsMouseUp( MB_Left ) )
|
if( evt->IsMouseUp( MB_Left ) )
|
||||||
{
|
{
|
||||||
// End drawing a selection box
|
// End drawing the selection box
|
||||||
m_selArea->ViewSetVisible( false );
|
m_selArea->ViewSetVisible( false );
|
||||||
|
|
||||||
// Mark items within the selection box as selected
|
// Mark items within the selection box as selected
|
||||||
|
@ -315,6 +317,7 @@ bool SELECTION_TOOL::selectMultiple()
|
||||||
// Now the context menu should be enabled
|
// Now the context menu should be enabled
|
||||||
if( !m_selectedItems.empty() )
|
if( !m_selectedItems.empty() )
|
||||||
SetContextMenu( &m_menu, CMENU_BUTTON );
|
SetContextMenu( &m_menu, CMENU_BUTTON );
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +370,7 @@ BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector )
|
||||||
{
|
{
|
||||||
optional<int> id = evt->GetCommandId();
|
optional<int> id = evt->GetCommandId();
|
||||||
|
|
||||||
|
// User has selected an item, so this one will be returned
|
||||||
if( id && ( *id >= 0 ) )
|
if( id && ( *id >= 0 ) )
|
||||||
current = ( *aCollector )[*id];
|
current = ( *aCollector )[*id];
|
||||||
|
|
||||||
|
@ -392,11 +396,12 @@ BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector )
|
||||||
|
|
||||||
bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
|
bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
|
||||||
{
|
{
|
||||||
|
// Is high contrast mode enabled?
|
||||||
bool highContrast = getView()->GetPainter()->GetSettings()->GetHighContrast();
|
bool highContrast = getView()->GetPainter()->GetSettings()->GetHighContrast();
|
||||||
|
|
||||||
if( highContrast )
|
if( highContrast )
|
||||||
{
|
{
|
||||||
bool onActive = false;
|
bool onActive = false; // Is the item on any of active layers?
|
||||||
int layers[KiGfx::VIEW::VIEW_MAX_LAYERS], layers_count;
|
int layers[KiGfx::VIEW::VIEW_MAX_LAYERS], layers_count;
|
||||||
|
|
||||||
// Filter out items that do not belong to active layers
|
// Filter out items that do not belong to active layers
|
||||||
|
@ -406,14 +411,14 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
|
||||||
|
|
||||||
for( int i = 0; i < layers_count; ++i )
|
for( int i = 0; i < layers_count; ++i )
|
||||||
{
|
{
|
||||||
if( activeLayers.count( layers[i] ) > 0 ) // Item is on at least one active layer
|
if( activeLayers.count( layers[i] ) > 0 ) // Item is on at least one of active layers
|
||||||
{
|
{
|
||||||
onActive = true;
|
onActive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !onActive )
|
if( !onActive ) // We do not want to select items that are in the background
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,8 +431,7 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
|
||||||
LAYER_NUM top, bottom;
|
LAYER_NUM top, bottom;
|
||||||
static_cast<const SEGVIA*>( aItem )->ReturnLayerPair( &top, &bottom );
|
static_cast<const SEGVIA*>( aItem )->ReturnLayerPair( &top, &bottom );
|
||||||
|
|
||||||
return ( board->IsLayerVisible( top ) ||
|
return ( board->IsLayerVisible( top ) || board->IsLayerVisible( bottom ) );
|
||||||
board->IsLayerVisible( bottom ) );
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -473,8 +477,7 @@ bool SELECTION_TOOL::containsSelected( const VECTOR2I& aPoint ) const
|
||||||
{
|
{
|
||||||
// Check if the point is located within any of the currently selected items bounding boxes
|
// Check if the point is located within any of the currently selected items bounding boxes
|
||||||
std::set<BOARD_ITEM*>::iterator it, it_end;
|
std::set<BOARD_ITEM*>::iterator it, it_end;
|
||||||
for( it = m_selectedItems.begin(), it_end = m_selectedItems.end();
|
for( it = m_selectedItems.begin(), it_end = m_selectedItems.end(); it != it_end; ++it )
|
||||||
it != it_end; ++it )
|
|
||||||
{
|
{
|
||||||
BOX2I itemBox = (*it)->ViewBBox();
|
BOX2I itemBox = (*it)->ViewBBox();
|
||||||
itemBox.Inflate( 500000 ); // Give some margin for gripping an item
|
itemBox.Inflate( 500000 ); // Give some margin for gripping an item
|
||||||
|
|
Loading…
Reference in New Issue