diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 3c3630b85a..7ccb9764c2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -163,6 +163,10 @@ set(COMMON_SRCS tool/tool_event.cpp tool/tool_interactive.cpp tool/context_menu.cpp + + geometry/seg.cpp + geometry/shape_line_chain.cpp + geometry/shape_collisions.cpp ) add_library(common STATIC ${COMMON_SRCS}) diff --git a/common/drawpanel_gal.cpp b/common/drawpanel_gal.cpp index 3b354506bf..fa0b004e49 100644 --- a/common/drawpanel_gal.cpp +++ b/common/drawpanel_gal.cpp @@ -87,8 +87,15 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin Connect( wxEVT_MIDDLE_UP, wxEventHandler( EDA_DRAW_PANEL_GAL::onEvent ), NULL, this ); Connect( wxEVT_MIDDLE_DOWN, wxEventHandler( EDA_DRAW_PANEL_GAL::onEvent ), NULL, this ); Connect( wxEVT_MOUSEWHEEL, wxEventHandler( EDA_DRAW_PANEL_GAL::onEvent ), NULL, this ); +// Connect( wxEVT_CHAR_HOOK, wxEventHandler( EDA_DRAW_PANEL_GAL::skipEvent ) ); Connect( wxEVT_KEY_UP, wxEventHandler( EDA_DRAW_PANEL_GAL::onEvent ), NULL, this ); Connect( wxEVT_KEY_DOWN, wxEventHandler( EDA_DRAW_PANEL_GAL::onEvent ), NULL, this ); +// Connect( wxEVT_ENTER_WINDOW, wxEventHandler( EDA_DRAW_PANEL_GAL::onEnter ), NULL, this ); + + m_refreshTimer.SetOwner( this ); + Connect( wxEVT_TIMER, wxTimerEventHandler( EDA_DRAW_PANEL_GAL::onRefreshTimer ), NULL, this ); + + this->SetFocus(); } @@ -110,6 +117,9 @@ EDA_DRAW_PANEL_GAL::~EDA_DRAW_PANEL_GAL() void EDA_DRAW_PANEL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) ) { + m_pendingRefresh = false; + m_lastRefresh = wxGetLocalTimeMillis(); + #ifdef __WXDEBUG__ prof_counter time; prof_start( &time, false ); @@ -144,13 +154,36 @@ void EDA_DRAW_PANEL_GAL::onSize( wxSizeEvent& aEvent ) } -void EDA_DRAW_PANEL_GAL::Refresh( bool eraseBackground, const wxRect* rect ) +void EDA_DRAW_PANEL_GAL::onRefreshTimer( wxTimerEvent& aEvent ) { wxPaintEvent redrawEvent; wxPostEvent( this, redrawEvent ); } +void EDA_DRAW_PANEL_GAL::Refresh( bool eraseBackground, const wxRect* rect ) +{ + if( m_pendingRefresh ) + return; + + wxLongLong t = wxGetLocalTimeMillis(); + wxLongLong delta = t - m_lastRefresh; + + if( delta >= MinRefreshPeriod ) + { + wxPaintEvent redrawEvent; + wxPostEvent( this, redrawEvent ); + m_pendingRefresh = true; + } + else + { + // One shot timer + m_refreshTimer.Start( ( MinRefreshPeriod - delta ).ToLong(), true ); + m_pendingRefresh = true; + } +} + + void EDA_DRAW_PANEL_GAL::SwitchBackend( GalType aGalType ) { // Do not do anything if the currently used GAL is correct @@ -205,5 +238,19 @@ void EDA_DRAW_PANEL_GAL::onEvent( wxEvent& aEvent ) m_eventDispatcher->DispatchWxEvent( aEvent ); } - Refresh(); +// if( m_view->IsDirty() ) + Refresh(); +} + + +void EDA_DRAW_PANEL_GAL::onEnter ( wxEvent& aEvent ) +{ + SetFocus(); +} + + +void EDA_DRAW_PANEL_GAL::skipEvent( wxEvent& aEvent ) +{ + // This is necessary for CHAR_HOOK event to generate KEY_UP and KEY_DOWN events + aEvent.Skip(); } diff --git a/common/gal/color4d.cpp b/common/gal/color4d.cpp index e2cd0af05e..b9f6557204 100644 --- a/common/gal/color4d.cpp +++ b/common/gal/color4d.cpp @@ -58,3 +58,117 @@ const bool COLOR4D::operator!=( const COLOR4D& aColor ) { return a != aColor.a || r != aColor.r || g != aColor.g || b != aColor.b; } + + +void COLOR4D::ToHSV( double& aOutH, double& aOutS, double& aOutV ) const +{ + double min, max, delta; + + min = r < g ? r : g; + min = min < b ? min : b; + + max = r > g ? r : g; + max = max > b ? max : b; + + aOutV = max; // v + delta = max - min; + + if( max > 0.0 ) + { + aOutS = ( delta / max ); // s + } + else + { + // r = g = b = 0 // s = 0, v is undefined + aOutS = 0.0; + aOutH = NAN; // its now undefined + return; + } + + if( r >= max ) // > is bogus, just keeps compiler happy + aOutH = ( g - b ) / delta; // between yellow & magenta + else if( g >= max ) + aOutH = 2.0 + ( b - r ) / delta; // between cyan & yellow + else + aOutH = 4.0 + ( r - g ) / delta; // between magenta & cyan + + aOutH *= 60.0; // degrees + + if( aOutH < 0.0 ) + aOutH += 360.0; +} + + +void COLOR4D::FromHSV( double aInH, double aInS, double aInV ) +{ + double hh, p, q, t, ff; + long i; + + if( aInS <= 0.0 ) // < is bogus, just shuts up warnings + { + r = aInV; + g = aInV; + b = aInV; + return; + } + + hh = aInH; + if( hh >= 360.0 ) + hh = 0.0; + hh /= 60.0; + + i = (long) hh; + ff = hh - i; + + p = aInV * ( 1.0 - aInS ); + q = aInV * ( 1.0 - ( aInS * ff ) ); + t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) ); + + switch (i) + { + case 0: + r = aInV; + g = t; + b = p; + break; + case 1: + r = q; + g = aInV; + b = p; + break; + case 2: + r = p; + g = aInV; + b = t; + break; + + case 3: + r = p; + g = q; + b = aInV; + break; + case 4: + r = t; + g = p; + b = aInV; + break; + case 5: + default: + r = aInV; + g = p; + b = q; + break; + } + +} + + +COLOR4D& COLOR4D::Saturate( double aFactor ) +{ + double h, s, v; + ToHSV( h, s, v ); + FromHSV( h, aFactor, 1.0 ); + + return *this; +} + diff --git a/common/geometry/seg.cpp b/common/geometry/seg.cpp new file mode 100644 index 0000000000..abc80688ca --- /dev/null +++ b/common/geometry/seg.cpp @@ -0,0 +1,150 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include + +template int sgn(T val) { + return (T(0) < val) - (val < T(0)); +} + +bool SEG::PointCloserThan (const VECTOR2I& aP, int dist) const +{ + VECTOR2I d = b - a; + ecoord dist_sq = (ecoord) dist * dist; + + SEG::ecoord l_squared = d.Dot(d); + SEG::ecoord t = d.Dot(aP - a); + + + if( t <= 0 || !l_squared ) + return (aP - a).SquaredEuclideanNorm() < dist_sq; + else if( t >= l_squared ) + return (aP - b).SquaredEuclideanNorm() < dist_sq; + + + int dxdy = abs(d.x) - abs(d.y); + + if( (dxdy >= -1 && dxdy <= 1) || abs(d.x) <= 1 || abs(d.y) <= 1) + { + int ca = -sgn(d.y); + int cb = sgn(d.x); + int cc = -ca * a.x - cb * a.y; + + ecoord num = ca * aP.x + cb * aP.y + cc; + num *= num; + + if(ca && cb) + num >>= 1; + + if(num > (dist_sq + 100)) + return false; + else if(num < (dist_sq - 100)) + return true; + } + + VECTOR2I nearest; + nearest.x = a.x + rescale(t, (ecoord)d.x, l_squared); + nearest.y = a.y + rescale(t, (ecoord)d.y, l_squared); + + return (nearest - aP).SquaredEuclideanNorm() <= dist_sq; +} + +SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const +{ + // fixme: rather inefficient.... + if(Intersect(aSeg)) + return 0; + + const VECTOR2I pts[4] = + { + aSeg.NearestPoint(a) - a, + aSeg.NearestPoint(b) - b, + NearestPoint(aSeg.a) - aSeg.a, + NearestPoint(aSeg.b) - aSeg.b + }; + + ecoord m = VECTOR2I::ECOORD_MAX; + for (int i = 0; i<4 ; i++) + m = std::min(m, pts[i].SquaredEuclideanNorm()); + return m; +} + +OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const +{ + const VECTOR2I e (b - a); + const VECTOR2I f (aSeg.b - aSeg.a); + const VECTOR2I ac (aSeg.a - a); + + ecoord d = f.Cross(e); + ecoord p = f.Cross(ac); + ecoord q = e.Cross(ac); + + if(d == 0) + return OPT_VECTOR2I(); + if (!aLines && d > 0 && (q < 0 || q > d || p < 0 || p > d)) + return OPT_VECTOR2I(); + if (!aLines && d < 0 && (q < d || p < d || p > 0 || q > 0)) + return OPT_VECTOR2I(); + if (!aLines && aIgnoreEndpoints && (q == 0 || q == d) && (p == 0 || p == d)) + return OPT_VECTOR2I(); + + + VECTOR2I ip ( aSeg.a.x + rescale(q, (ecoord)f.x, d), + aSeg.a.y + rescale(q, (ecoord)f.y, d) ); + + return ip; +} + + +bool SEG::ccw ( const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I &c ) const +{ + return (ecoord)(c.y - a.y) * (b.x - a.x) > (ecoord)(b.y - a.y) * (c.x - a.x); +} + +bool SEG::Collide( const SEG& aSeg, int aClearance ) const +{ + // check for intersection + // fixme: move to a method + if( ccw(a,aSeg.a,aSeg.b) != ccw(b,aSeg.a,aSeg.b) && ccw(a,b,aSeg.a) != ccw(a,b,aSeg.b) ) + return true; + +#define CHK(_seg, _pt) \ + if( (_seg).PointCloserThan (_pt, aClearance ) ) return true; + + CHK(*this, aSeg.a); + CHK(*this, aSeg.b); + CHK(aSeg, a); + CHK(aSeg, b); + +#undef CHK + + return false; +} + +bool SEG::Contains(const VECTOR2I& aP) const +{ + return PointCloserThan(aP, 1); +} + diff --git a/common/geometry/shape_collisions.cpp b/common/geometry/shape_collisions.cpp new file mode 100644 index 0000000000..cf2c348ee5 --- /dev/null +++ b/common/geometry/shape_collisions.cpp @@ -0,0 +1,210 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include +#include +#include +#include + +typedef typename VECTOR2I::extended_type ecoord; + +static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_CIRCLE& b, int clearance, bool needMTV, VECTOR2I& aMTV ) +{ + ecoord min_dist = clearance + a.GetRadius() + b.GetRadius(); + ecoord min_dist_sq = min_dist * min_dist; + + const VECTOR2I delta = b.GetCenter() - a.GetCenter(); + + ecoord dist_sq = delta.SquaredEuclideanNorm(); + + if ( dist_sq >= min_dist_sq ) + return false; + + if ( needMTV ) + aMTV = delta.Resize( sqrt (abs(min_dist_sq - dist_sq)) + 1); + + return true; +} + +static inline bool Collide( const SHAPE_RECT& a, const SHAPE_CIRCLE& b, int clearance, bool needMTV, VECTOR2I& aMTV ) +{ + const VECTOR2I c = b.GetCenter(); + const VECTOR2I p0 = a.GetPosition(); + const VECTOR2I size = a.GetSize(); + const ecoord r = b.GetRadius(); + const ecoord min_dist = clearance + r; + const ecoord min_dist_sq = min_dist * min_dist; + + if (a.BBox(0).Contains(c)) + return true; + + const VECTOR2I vts[] = { + VECTOR2I(p0.x, p0.y), + VECTOR2I(p0.x, p0.y + size.y), + VECTOR2I(p0.x + size.x, p0.y + size.y), + VECTOR2I(p0.x + size.x, p0.y), + VECTOR2I(p0.x, p0.y) }; + + ecoord nearest_seg_dist_sq = VECTOR2I::ECOORD_MAX; + VECTOR2I nearest; + + bool inside = c.x >= p0.x && c.x <= (p0.x + size.x) + && c.y >= p0.y && c.y <= (p0.y + size.y); + + if(!inside) + { + + for (int i = 0; i < 4; i++) + { + const SEG seg (vts[i], vts[i+1]); + ecoord dist_sq = seg.SquaredDistance ( c ); + + + if(dist_sq < min_dist_sq) + { + if(!needMTV) + return true; + else + { + nearest = seg.NearestPoint ( c ); + nearest_seg_dist_sq = dist_sq; + } + } + } + } + + if(nearest_seg_dist_sq >= min_dist_sq && !inside) + return false; + + VECTOR2I delta = c - nearest; + + if(!needMTV) + return true; + + if(inside) + aMTV = -delta.Resize(sqrt(abs(r * r + nearest_seg_dist_sq) + 1)); + else + aMTV = delta.Resize(sqrt(abs(r * r - nearest_seg_dist_sq) + 1)); + + return true; +} + +static inline bool Collide( const SHAPE_CIRCLE& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV ) +{ + for (int s = 0; s < b.SegmentCount(); s++) + { + if ( a.Collide (b.CSegment(s), clearance)) + return true; + } + + return false; +} + +static inline bool Collide( const SHAPE_LINE_CHAIN& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV ) +{ + for( int i = 0; i < b.SegmentCount() ;i++) + if(a.Collide(b.CSegment(i), clearance)) + return true; + return false; +} + + +static inline bool Collide( const SHAPE_RECT& a, const SHAPE_LINE_CHAIN& b, int clearance, bool needMTV, VECTOR2I& aMTV ) +{ + for (int s = 0; s < b.SegmentCount(); s++) + { + SEG seg = b.CSegment(s); + if ( a.Collide (seg, clearance)) + return true; + } + + return false; +} + + + +bool CollideShapes ( const SHAPE *a, const SHAPE *b, int clearance, bool needMTV, VECTOR2I& aMTV ) +{ + switch(a->Type()) + { + case SH_RECT: + switch(b->Type()) + { + case SH_CIRCLE: + return Collide( *static_cast (a), *static_cast (b), clearance, needMTV, aMTV ); + case SH_LINE_CHAIN: + return Collide( *static_cast (a), *static_cast (b), clearance, needMTV, aMTV ); + default: + break; + } + + case SH_CIRCLE: + switch(b->Type()) + { + case SH_RECT: + return Collide( *static_cast (b), *static_cast (a), clearance, needMTV, aMTV ); + case SH_CIRCLE: + return Collide( *static_cast (a), *static_cast (b), clearance, needMTV, aMTV ); + case SH_LINE_CHAIN: + return Collide( *static_cast (a), *static_cast (b), clearance, needMTV, aMTV ); + default: + break; + } + + case SH_LINE_CHAIN: + switch(b->Type()) + { + case SH_RECT: + return Collide( *static_cast (b), *static_cast (a), clearance, needMTV, aMTV ); + case SH_CIRCLE: + return Collide( *static_cast (b), *static_cast (a), clearance, needMTV, aMTV ); + case SH_LINE_CHAIN: + return Collide( *static_cast (a), *static_cast (b), clearance, needMTV, aMTV ); + default: + break; + } + default: + break; + } + + bool unsupported_collision = true; + + assert(unsupported_collision == false); + return false; +} + + +bool SHAPE::Collide ( const SHAPE *aShape, int aClerance, VECTOR2I& aMTV ) const +{ + return CollideShapes( this, aShape, aClerance, true, aMTV); +} + +bool SHAPE::Collide ( const SHAPE *aShape, int aClerance ) const +{ + VECTOR2I dummy; + return CollideShapes( this, aShape, aClerance, false, dummy); +} + \ No newline at end of file diff --git a/common/geometry/shape_line_chain.cpp b/common/geometry/shape_line_chain.cpp new file mode 100644 index 0000000000..44c7bad7f4 --- /dev/null +++ b/common/geometry/shape_line_chain.cpp @@ -0,0 +1,465 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +using namespace std; +using boost::optional; + +bool SHAPE_LINE_CHAIN::Collide ( const VECTOR2I& aP, int aClearance ) const +{ + assert(false); + return false; +} + +bool SHAPE_LINE_CHAIN::Collide ( const BOX2I& aBox, int aClearance ) const +{ + assert(false); + return false; +} + +bool SHAPE_LINE_CHAIN::Collide ( const SEG& aSeg, int aClearance ) const +{ + BOX2I box_a(aSeg.a, aSeg.b - aSeg.a); + BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance; + + for( int i = 0; i < SegmentCount() ;i++) + { + const SEG& s = CSegment(i); + BOX2I box_b(s.a, s.b - s.a); + + BOX2I::ecoord_type d = box_a.SquaredDistance ( box_b ); + + if(d < dist_sq) + { + if(s.Collide(aSeg, aClearance)) + return true; + } + } + return false; +} + +const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const +{ + SHAPE_LINE_CHAIN a (*this); + reverse(a.m_points.begin(), a.m_points.end()); + a.m_closed = m_closed; + + return a; +} + + +int SHAPE_LINE_CHAIN::Length() const +{ + int l = 0; + for (int i = 0; i < SegmentCount(); i++) + l += CSegment(i).Length(); + return l; +} + +void SHAPE_LINE_CHAIN::Replace( int start_index, int end_index, const VECTOR2I& aP) +{ + if(end_index < 0) + end_index += PointCount(); + if(start_index < 0) + start_index += PointCount(); + + if (start_index == end_index) + m_points [start_index] = aP; + else { + m_points.erase (m_points.begin() + start_index + 1, m_points.begin() + end_index + 1); + m_points [start_index] = aP; + } +} + +void SHAPE_LINE_CHAIN::Replace( int start_index, int end_index, const SHAPE_LINE_CHAIN& aLine) +{ + if(end_index < 0) + end_index += PointCount(); + if(start_index < 0) + start_index += PointCount(); + + m_points.erase (m_points.begin() + start_index, m_points.begin() + end_index + 1); + m_points.insert (m_points.begin() + start_index, aLine.m_points.begin(), aLine.m_points.end()); +} + +void SHAPE_LINE_CHAIN::Remove( int start_index, int end_index) +{ + if(end_index < 0) + end_index += PointCount(); + if(start_index < 0) + start_index += PointCount(); + + m_points.erase (m_points.begin() + start_index, m_points.begin() + end_index + 1); +} + +int SHAPE_LINE_CHAIN::Distance( const VECTOR2I & aP ) const +{ + int d = INT_MAX; + for (int s = 0; s < SegmentCount(); s++) + d = min (d, CSegment(s).Distance(aP)); + return d; +} + +int SHAPE_LINE_CHAIN::Split( const VECTOR2I & aP ) +{ + int ii = -1; + int min_dist = 2; + + ii = Find(aP); + + if(ii >= 0) + return ii; + + for (int s = 0; s < SegmentCount(); s++) + { + const SEG seg = CSegment(s); + int dist = seg.Distance(aP); + + // make sure we are not producing a 'slightly concave' primitive. This might happen + // if aP lies very close to one of already existing points. + if(dist < min_dist && seg.a != aP && seg.b != aP) + { + min_dist = dist; + ii = s; + } + } + + if(ii >= 0) + { + m_points.insert(m_points.begin() + ii + 1, aP); + return ii + 1; + } + + return -1; +} + +int SHAPE_LINE_CHAIN::Find ( const VECTOR2I& aP ) const +{ + for (int s = 0; s< PointCount(); s++) + if(CPoint(s) == aP) + return s; + return -1; +} + +const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int start_index, int end_index ) const +{ + SHAPE_LINE_CHAIN rv; + + if(end_index < 0) + end_index += PointCount(); + if(start_index < 0) + start_index += PointCount(); + + for(int i = start_index; i<= end_index; i++) + rv.Append(m_points[i]); + return rv; +} + +struct compareOriginDistance { + compareOriginDistance( VECTOR2I& aOrigin ): + m_origin(aOrigin) {}; + + bool operator()(const SHAPE_LINE_CHAIN::Intersection &a, const SHAPE_LINE_CHAIN::Intersection& b) + { + return (m_origin - a.p).EuclideanNorm() < (m_origin - b.p).EuclideanNorm(); + } + + VECTOR2I m_origin; +}; + + +int SHAPE_LINE_CHAIN::Intersect ( const SEG& aSeg, Intersections& aIp ) const +{ + for (int s = 0; s < SegmentCount(); s++) + { + OPT_VECTOR2I p = CSegment(s).Intersect(aSeg); + if(p) + { + Intersection is; + is.our = CSegment(s); + is.their = aSeg; + is.p = *p; + aIp.push_back(is); + } + } + + compareOriginDistance comp(aSeg.a); + sort(aIp.begin(), aIp.end(), comp); + return aIp.size(); +}; + +int SHAPE_LINE_CHAIN::Intersect( const SHAPE_LINE_CHAIN &aChain, Intersections& aIp ) const +{ +BOX2I bb_other = aChain.BBox(); + + for (int s1 = 0; s1 < SegmentCount(); s1++) + { + const SEG& a = CSegment(s1); + const BOX2I bb_cur (a.a, a.b - a.a); + + if(! bb_other.Intersects( bb_cur )) + continue; + + for (int s2 = 0; s2 < aChain.SegmentCount(); s2++) + { + const SEG& b = aChain.CSegment(s2); + Intersection is; + + + if(a.Collinear(b)) + { + if(a.Contains(b.a)) { is.p = b.a; aIp.push_back(is); } + if(a.Contains(b.b)) { is.p = b.b; aIp.push_back(is); } + if(b.Contains(a.a)) { is.p = a.a; aIp.push_back(is); } + if(b.Contains(a.b)) { is.p = a.b; aIp.push_back(is); } + } else { + OPT_VECTOR2I p = a.Intersect(b); + + if(p) + { + is.p = *p; + is.our = a; + is.their = b; + aIp.push_back(is); + } + } + } + } + + return aIp.size(); + + for (int s1 = 0; s1 < SegmentCount(); s1++) + for (int s2 = 0; s2 < aChain.SegmentCount(); s2++) + { + const SEG& a = CSegment(s1); + const SEG& b = aChain.CSegment(s2); + OPT_VECTOR2I p = a.Intersect(b); + Intersection is; + + if(p) + { + is.p = *p; + is.our = a; + is.their = b; + aIp.push_back(is); + } else if (a.Collinear(b)) + { + if(a.a != b.a && a.a != b.b && b.Contains(a.a) ) + { + is.p = a.a; + is.our = a; + is.their = b; + aIp.push_back(is); + } + else if(a.b != b.a && a.b != b.b && b.Contains(a.b) ) + { + is.p = a.b; + is.our = a; + is.their = b; + aIp.push_back(is); + } + + } + } + return aIp.size(); +} + +int SHAPE_LINE_CHAIN::PathLength (const VECTOR2I& aP ) const +{ + int sum = 0; + for (int i = 0; i < SegmentCount(); i++) + { + const SEG seg = CSegment(i); + int d = seg.Distance(aP); + if (d <= 1) + { + sum += (aP - seg.a).EuclideanNorm(); + return sum; + } + else + sum += seg.Length(); + } + return -1; +} + +bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aP) const +{ + if(!m_closed || SegmentCount() < 3) + return false; + + int cur = CSegment(0).Side(aP); + if(cur == 0) + return false; + + for( int i = 1; i < SegmentCount(); i++) + { + const SEG s = CSegment(i); + if(aP == s.a || aP == s.b) // edge does not belong to the interior! + return false; + if (s.Side(aP) != cur) + return false; + } + return true; +} + +bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aP) const +{ + if(SegmentCount() < 1) + return m_points[0] == aP; + + for( int i = 1; i < SegmentCount(); i++) + { + const SEG s = CSegment(i); + if(s.a == aP || s.b == aP) + return true; + + if(s.Distance(aP) <= 1) + return true; + } + return false; +} + +const optional SHAPE_LINE_CHAIN::SelfIntersecting() const +{ + for (int s1 = 0; s1 < SegmentCount(); s1++) + for (int s2 = s1 + 1; s2 < SegmentCount(); s2++) + { + const VECTOR2I s2a = CSegment(s2).a, s2b = CSegment(s2).b; + if(s1 + 1 != s2 && CSegment(s1).Contains(s2a)) + { + Intersection is; + is.our = CSegment(s1); + is.their = CSegment(s2); + is.p = s2a; + return is; + } else if (CSegment(s1).Contains(s2b)) { + Intersection is; + is.our = CSegment(s1); + is.their = CSegment(s2); + is.p = s2b; + return is; + + } else { + OPT_VECTOR2I p = CSegment(s1).Intersect(CSegment(s2), true); + + if(p) + { + Intersection is; + is.our = CSegment(s1); + is.their = CSegment(s2); + is.p = *p; + return is; + } + } + } + return optional(); +} + + +SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() +{ + vector pts_unique; + + if (PointCount() < 2) + { + return *this; + } else if (PointCount() == 2) { + if(m_points[0] == m_points[1]) + m_points.erase(m_points.end()); + return *this; + } + + int i = 0; + int np = PointCount(); + + // stage 1: eliminate duplicate vertices + while ( i < np ) + { + int j = i+1; + while(j < np && CPoint(i) == CPoint(j)) + j ++; + pts_unique.push_back(CPoint(i)); + i = j; + } + + m_points.clear(); + np = pts_unique.size(); + + i = 0; + // stage 1: eliminate collinear segments + while (i < np - 2) + { + const VECTOR2I p0 = pts_unique[i]; + const VECTOR2I p1 = pts_unique[i+1]; + int n = i; + while(n < np - 2 && SEG(p0, p1).LineDistance(pts_unique[n + 2]) <= 1) + n++; + + m_points.push_back(p0); + if (n > i) + i = n; + if (n == np) + { + m_points.push_back(pts_unique[n-1]); + return *this; + } + i ++; + } + + if(np > 1) + m_points.push_back(pts_unique[np-2]); + m_points.push_back(pts_unique[np-1]); + + return *this; +} + +const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint(const VECTOR2I& aP) const +{ + int min_d = INT_MAX; + int nearest; + for ( int i = 0; i < SegmentCount() ; i++ ) + { + int d = CSegment(i).Distance(aP); + if( d < min_d ) + { + min_d = d; + nearest = i; + } + } + return CSegment(nearest).NearestPoint(aP); +} + +const string SHAPE_LINE_CHAIN::Format() const +{ + stringstream ss; + + ss << m_points.size() << " " << (m_closed ? 1 : 0) << " " ; + + for(int i = 0; im_tool->GetManager()->ProcessEvent( evt ); + if( m_menu->m_tool ) + m_menu->m_tool->GetManager()->ProcessEvent( evt ); } private: diff --git a/common/tool/tool_dispatcher.cpp b/common/tool/tool_dispatcher.cpp index 7138f14774..018e54b566 100644 --- a/common/tool/tool_dispatcher.cpp +++ b/common/tool/tool_dispatcher.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include @@ -56,6 +55,8 @@ struct TOOL_DISPATCHER::ButtonState bool pressed; VECTOR2D dragOrigin; + VECTOR2D downPosition; + double dragMaxDelta; TOOL_MouseButtons button; @@ -119,12 +120,22 @@ int TOOL_DISPATCHER::decodeModifiers( const wxKeyboardState* aState ) const } +wxPoint TOOL_DISPATCHER::getCurrentMousePos() const +{ + wxPoint msp = wxGetMousePosition(); + wxPoint winp = m_editFrame->GetGalCanvas()->GetScreenPosition(); + + return wxPoint( msp.x - winp.x, msp.y - winp.y ); +} + + bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ) { ButtonState* st = m_buttons[aIndex]; wxEventType type = aEvent.GetEventType(); optional evt; - + bool isClick = false; + bool up = type == st->upEvent; bool down = type == st->downEvent; @@ -135,13 +146,13 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti { st->downTimestamp = wxGetLocalTimeMillis(); st->dragOrigin = m_lastMousePos; + st->downPosition = m_lastMousePos; st->dragMaxDelta = 0; st->pressed = true; evt = TOOL_EVENT( TC_Mouse, TA_MouseDown, args ); } else if( up ) { - bool isClick = false; st->pressed = false; if( st->dragging ) @@ -156,15 +167,10 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti } else isClick = true; + if( isClick ) - { - if( st->triggerContextMenu && !mods ) - {} - // evt = TOOL_EVENT( TC_Command, TA_ContextMenu ); - else - evt = TOOL_EVENT( TC_Mouse, TA_MouseClick, args ); - } + evt = TOOL_EVENT( TC_Mouse, TA_MouseClick, args ); st->dragging = false; } @@ -187,7 +193,7 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti if( evt ) { - evt->SetMousePosition( m_lastMousePos ); + evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos ); m_toolMgr->ProcessEvent( *evt ); return true; @@ -200,7 +206,7 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) { bool motion = false, buttonEvents = false; - VECTOR2D pos, screenPos; + VECTOR2D pos; optional evt; int type = aEvent.GetEventType(); @@ -211,9 +217,9 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP ) { - screenPos = m_toolMgr->GetViewControls()->GetCursorPosition(); - pos = getView()->ToWorld( screenPos ); + wxMouseEvent* me = static_cast( &aEvent ); + pos = getView()->ToWorld ( getCurrentMousePos() ); if( pos != m_lastMousePos ) { motion = true; @@ -264,6 +270,10 @@ void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent ) switch( aEvent.GetId() ) { + case ID_PNS_ROUTER_TOOL: + toolName = "pcbnew.InteractiveRouter"; + activateTool = true; + break; case ID_SELECTION_TOOL: toolName = "pcbnew.InteractiveSelection"; activateTool = true; @@ -273,4 +283,3 @@ void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent ) if( activateTool && m_editFrame->IsGalCanvasActive() ) m_toolMgr->InvokeTool( toolName ); } - diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index 0c7a97538c..9e0b4ac621 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -81,8 +81,10 @@ struct TOOL_MANAGER::TOOL_STATE }; -TOOL_MANAGER::TOOL_MANAGER() +TOOL_MANAGER::TOOL_MANAGER() : + m_model( NULL ), m_view( NULL ) { + } @@ -290,11 +292,17 @@ bool TOOL_MANAGER::ProcessEvent( TOOL_EVENT& aEvent ) { TOOL_STATE* st = m_toolIdIndex[toolId]; - if( st->contextMenuTrigger == CMENU_NOW ) + if( st->contextMenuTrigger != CMENU_OFF ) { + if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( MB_Right ) ) + break; + st->pendingWait = true; st->waitEvents = TOOL_EVENT( TC_Any, TA_Any ); - st->contextMenuTrigger = CMENU_OFF; + + if( st->contextMenuTrigger == CMENU_NOW ) + st->contextMenuTrigger = CMENU_OFF; + GetEditFrame()->PopupMenu( st->contextMenu->GetMenu() ); TOOL_EVENT evt( TC_Command, TA_ContextMenuChoice ); diff --git a/common/view/view_group.cpp b/common/view/view_group.cpp index d6f90bb0ba..dfaf595c5c 100644 --- a/common/view/view_group.cpp +++ b/common/view/view_group.cpp @@ -71,6 +71,16 @@ void VIEW_GROUP::Clear() } +void VIEW_GROUP::FreeItems() +{ + BOOST_FOREACH( VIEW_ITEM* item, m_items ) + { + delete item; + } + m_items.clear(); +} + + unsigned int VIEW_GROUP::GetSize() const { return m_items.size(); diff --git a/include/class_drawpanel_gal.h b/include/class_drawpanel_gal.h index 9206c69338..8d6a789db2 100644 --- a/include/class_drawpanel_gal.h +++ b/include/class_drawpanel_gal.h @@ -114,7 +114,16 @@ public: protected: void onPaint( wxPaintEvent& WXUNUSED( aEvent ) ); void onSize( wxSizeEvent& aEvent ); - void onEvent( wxEvent& aEvent ); + void onEvent( wxEvent& aEvent ); + void onEnter( wxEvent& aEvent ); + void onRefreshTimer ( wxTimerEvent& aEvent ); + void skipEvent( wxEvent& aEvent ); + + static const int MinRefreshPeriod = 17; ///< 60 FPS. + + wxLongLong m_lastRefresh; ///< Last time the panel was refreshed + bool m_pendingRefresh; + wxTimer m_refreshTimer; KiGfx::GAL* m_gal; ///< Interface for drawing objects on a 2D-surface KiGfx::VIEW* m_view; ///< Stores view settings (scale, center, etc.) diff --git a/include/gal/color4d.h b/include/gal/color4d.h index 7398d796e8..5d0ca571a3 100644 --- a/include/gal/color4d.h +++ b/include/gal/color4d.h @@ -127,6 +127,11 @@ public: return *this; } + /** + * Saturates the color to a given factor (in HSV model) + */ + COLOR4D& Saturate( double aFactor ); + /** * Function Brightened * Returns a color that is brighter by a given factor, without modifying object. @@ -180,6 +185,26 @@ public: return ( r * 0.299 + g * 0.587 + b * 0.117 ); } + /** + * Function ToHSV() + * Converts current color (stored in RGB) to HSV format. + * + * @param aOutH is conversion result for hue component. + * @param aOutS is conversion result for saturation component. + * @param aOutV is conversion result for value component. + */ + void ToHSV( double& aOutH, double& aOutS, double& aOutV ) const; + + /** + * Function FromHSV() + * Changes currently used color to the one given by hue, saturation and value parameters. + * + * @param aOutH is hue component. + * @param aOutS is saturation component. + * @param aOutV is value component. + */ + void FromHSV( double aInH, double aInS, double aInV ); + /// @brief Equality operator, are two colors equal const bool operator==( const COLOR4D& aColor ); diff --git a/include/geometry/seg.h b/include/geometry/seg.h new file mode 100644 index 0000000000..9989c76be7 --- /dev/null +++ b/include/geometry/seg.h @@ -0,0 +1,361 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __SEG_H +#define __SEG_H + +#include +#include + +#include + +#include + +typedef boost::optional OPT_VECTOR2I; + +class SEG { + + private: + typedef VECTOR2I::extended_type ecoord; + + public: + + friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg ); + + /* Start and the of the segment. Public, to make access simpler. These are references + * to an object the segment belongs to (e.g. a line chain) or references to locally stored points + * (m_a, m_b). + */ + VECTOR2I& a, b; + + /** Default constructor + * Creates an empty (0, 0) segment, locally-referenced + */ + SEG(): a(m_a), b(m_b) + { + a = m_a; + b = m_b; + m_is_local = true; + m_index = -1; + } + + /** + * Constructor + * Creates a segment between (x1, y1) and (x2, y2), locally referenced + */ + SEG ( int x1, int y1, int x2, int y2 ) : a(m_a), b(m_b) + { + m_a = VECTOR2I(x1, y1); + m_b = VECTOR2I(x2, y2); + a = m_a; + b = m_b; + m_is_local = true; + m_index = -1; + } + + /** + * Constructor + * Creates a segment between (aA) and (aB), locally referenced + */ + SEG ( const VECTOR2I& aA, const VECTOR2I& aB ): a(m_a), b(m_b), m_a(aA), m_b(aB) + { + a = m_a; + b = m_b; + m_is_local = true; + m_index = -1; + } + + /** + * Constructor + * Creates a segment between (aA) and (aB), referenced to a multi-segment shape + * @param aA reference to the start point in the parent shape + * @param aB reference to the end point in the parent shape + * @param aIndex index of the segment within the parent shape + */ + SEG ( VECTOR2I& aA, VECTOR2I& aB, int aIndex ): a(aA), b(aB) + { + m_is_local = false; + m_index = aIndex; + } + + /** + * Copy constructor + */ + SEG ( const SEG& seg ): a(m_a), b(m_b) + { + if (seg.m_is_local) + { + m_a = seg.m_a; + m_b = seg.m_b; + a = m_a; + b = m_b; + m_is_local = true; + m_index = -1; + } else { + a = seg.a; + b = seg.b; + m_index = seg.m_index; + m_is_local = false; + } + } + + SEG& operator=(const SEG& seg) + { + a = seg.a; + b = seg.b; + m_a = seg.m_a; + m_b = seg.m_b; + m_index = seg.m_index; + m_is_local = seg.m_is_local; + return *this; + } + + /** + * Function LineProject() + * + * Computes the perpendicular projection point of aP on a line passing through + * ends of the segment. + * @param aP point to project + * @return projected point + */ + VECTOR2I LineProject( const VECTOR2I& aP ) const; + + /** + * Function Side() + * + * Determines on which side of directed line passing via segment ends point aP lies. + * @param aP point to determine the orientation wrs to self + * @return: < 0: left, 0 : on the line, > 0 : right + */ + int Side( const VECTOR2I& aP ) const + { + const ecoord det = (b - a).Cross(aP - a); + return det < 0 ? -1 : (det > 0 ? 1 : 0); + } + + /** + * Function LineDistance() + * + * Returns the closest Euclidean distance between point aP and the line defined by + * the ends of segment (this). + * @param aDetermineSide: when true, the sign of the returned value indicates + * the side of the line at which we are (negative = left) + * @return the distance + */ + int LineDistance( const VECTOR2I& aP, bool aDetermineSide = false ) const; + + /** + * Function NearestPoint() + * + * Computes a point on the segment (this) that is closest to point aP. + * @return: nearest point + */ + const VECTOR2I NearestPoint( const VECTOR2I &aP ) const; + + + /** + * Function Intersect() + * + * Computes intersection point of segment (this) with segment aSeg. + * @param aSeg: segment to intersect with + * @param aIgnoreEndpoints: don't treat corner cases (i.e. end of one segment touching the other) + * as intersections. + * @param aLines: treat segments as infinite lines + * @return intersection point, if exists + */ + OPT_VECTOR2I Intersect( const SEG& aSeg, bool aIgnoreEndpoints = false, bool aLines = false ) const; + + + + /** + * Function IntersectLines() + * + * Computes the intersection point of lines passing through ends of (this) and aSeg + * @param aSeg segment defining the line to intersect with + * @return intersection point, if exists + */ + OPT_VECTOR2I IntersectLines( const SEG& aSeg ) const + { + return Intersect ( aSeg, false, true ); + } + + bool Collide( const SEG& aSeg, int aClearance ) const; + + /** + * Function Distance() + * + * Computes minimum Euclidean distance to segment aSeg. + * @param aSeg other segment + * @return minimum distance + */ + + ecoord SquaredDistance( const SEG& aSeg ) const ; + + int Distance( const SEG& aSeg ) const + { + return sqrt ( SquaredDistance(aSeg) ); + } + + + /** + * Function Distance() + * + * Computes minimum Euclidean distance to point aP. + * @param aP the point + * @return minimum distance + */ + + ecoord SquaredDistance( const VECTOR2I& aP ) const + { + return (NearestPoint(aP) - aP).SquaredEuclideanNorm(); + } + + int Distance( const VECTOR2I& aP ) const + { + return sqrt ( SquaredDistance( aP) ); + } + + + /** + * Function Collinear() + * + * Checks if segment aSeg lies on the same line as (this). + * @param aSeg the segment to chech colinearity with + * @return true, when segments are collinear. + */ + + bool Collinear( const SEG& aSeg ) const + { + ecoord qa1 = a.y - b.y; + ecoord qb1 = b.x - a.x; + ecoord qc1 = -qa1 * a.x - qb1 * a.y; + ecoord qa2 = aSeg.a.y - aSeg.b.y; + ecoord qb2 = aSeg.b.x - aSeg.a.x; + ecoord qc2 = -qa2 * aSeg.a.x - qb2 * aSeg.a.y; + + return (qa1 == qa2) && (qb1 == qb2) && (qc1 == qc2); + } + + /** + * Function Length() + * + * Returns the length (this) + * @return length + */ + int Length() const + { + return (a - b).EuclideanNorm(); + } + + /** + * Function Index() + * + * Return the index of this segment in its parent shape (applicable only to non-local segments) + * @return index value + */ + int Index() const + { + return m_index; + } + + + bool Contains(const VECTOR2I& aP) const; + + bool PointCloserThan ( const VECTOR2I& aP, int dist) const; + + // friend std::ostream& operator<<( std::ostream& stream, const SEG& aSeg ); + private: + + bool ccw ( const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I &c ) const; + + ///> locally stored start/end coordinates (used when m_is_local == true) + VECTOR2I m_a, m_b; + ///> index withing the parent shape (used when m_is_local == false) + int m_index; + ///> locality flag + bool m_is_local; +}; + +inline VECTOR2I SEG::LineProject( const VECTOR2I& aP ) const +{ + // fixme: numerical errors for large integers + assert(false); + /*const VECTOR2I d = aB - aA; + ecoord det = d.Dot(d); + ecoord dxdy = (ecoord) d.x * d.y; + + ecoord qx = + ( (extended_type) aA.x * d.y * d.y + (extended_type) d.x * d.x * x - dxdy * + (aA.y - y) ) / det; + extended_type qy = + ( (extended_type) aA.y * d.x * d.x + (extended_type) d.y * d.y * y - dxdy * + (aA.x - x) ) / det; + + return VECTOR2 ( (T) qx, (T) qy );*/ +} + + + +inline int SEG::LineDistance( const VECTOR2I& aP, bool aDetermineSide ) const +{ + ecoord p = a.y - b.y; + ecoord q = b.x - a.x; + ecoord r = -p * a.x - q * a.y; + + ecoord dist = ( p * aP.x + q * aP.y + r ) / sqrt( p * p + q * q ); + return aDetermineSide ? dist : abs(dist); +} + + + +inline const VECTOR2I SEG::NearestPoint(const VECTOR2I& aP) const +{ + VECTOR2I d = b - a; + ecoord l_squared = d.Dot(d); + + if( l_squared == 0 ) + return a; + + ecoord t = d.Dot(aP - a); + + if( t < 0 ) + return a; + else if( t > l_squared ) + return b; + + int xp = rescale(t, (ecoord)d.x, l_squared); + int yp = rescale(t, (ecoord)d.y, l_squared); + + return a + VECTOR2I(xp, yp); +} + +inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg ) +{ + if(aSeg.m_is_local) + aStream << "[ local " << aSeg.a << " - " << aSeg.b << " ]"; + return aStream; +} + +#endif // __SEG_H + diff --git a/include/geometry/shape.h b/include/geometry/shape.h new file mode 100644 index 0000000000..6d56fef79a --- /dev/null +++ b/include/geometry/shape.h @@ -0,0 +1,141 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __SHAPE_H +#define __SHAPE_H + +#include +#include + +#include + +/** + * Enum ShapeType + * Lists all supported shapes + */ + +enum ShapeType { + SH_RECT = 0, ///> axis-aligned rectangle + SH_SEGMENT, ///> line segment + SH_LINE_CHAIN, ///> line chain (polyline) + SH_CIRCLE ///> circle +}; + +/** + * Class SHAPE + * + * Represents an abstract shape on 2D plane. All SHAPEs implement SHAPE interface. + */ +class SHAPE { + protected: + typedef typename VECTOR2I::extended_type ecoord; + + public: + /** + * Constructor + * + * Creates an empty shape of type aType + */ + + SHAPE ( ShapeType aType ): m_type( aType ) { }; + + // Destructor + virtual ~SHAPE() {}; + + /** + * Function Type() + * + * Returns the type of the shape. + * @retval the type + */ + ShapeType Type() const { return m_type; } + + /** + * Function Clone() + * + * Returns a dynamically allocated copy of the shape + * @retval copy of the shape + */ + virtual SHAPE* Clone() const { assert(false); }; + + /** + * Function Collide() + * + * Checks if the boundary of shape (this) lies closer to the point aP than aClearance, indicating + * a collision. + * @return true, if there is a collision. + */ + virtual bool Collide ( const VECTOR2I& aP, int aClearance = 0 ) const + { + return Collide(SEG(aP, aP), aClearance); + } + + /** + * Function Collide() + * + * Checks if the boundary of shape (this) lies closer to the shape aShape than aClearance, indicating + * a collision. + * @return true, if there is a collision. + */ + virtual bool Collide ( const SHAPE *aShape, int aClerance, VECTOR2I& aMTV ) const; + virtual bool Collide ( const SHAPE *aShape, int aClerance = 0 ) const; + /** + * Function Collide() + * + * Checks if the boundary of shape (this) lies closer to the segment aSeg than aClearance, indicating + * a collision. + * @return true, if there is a collision. + */ + virtual bool Collide ( const SEG& aSeg, int aClearance = 0) const = 0; + + /** + * Function Collide() + * + * Computes a bounding box of the shape, with a margin of aClearance + * a collision. + * @aClearance how much the bounding box is expanded wrs to the minimum enclosing rectangle for the shape. + * @return the bounding box. + */ + virtual const BOX2I BBox ( int aClearance = 0 ) const = 0; + + /** + * Function Centre() + * + * Computes a center-of-mass of the shape + * @return the center-of-mass point + */ + virtual VECTOR2I Centre() const + { + return BBox(0).Centre(); // if nothing better is available.... + } + + private: + ///> type of our shape + ShapeType m_type; + +}; + +bool CollideShapes ( const SHAPE *a, const SHAPE *b, int clearance, bool needMTV, VECTOR2I& aMTV ); + +#endif // __SHAPE_H diff --git a/include/geometry/shape_circle.h b/include/geometry/shape_circle.h new file mode 100644 index 0000000000..3e5a7ba89b --- /dev/null +++ b/include/geometry/shape_circle.h @@ -0,0 +1,78 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __SHAPE_CIRCLE_H +#define __SHAPE_CIRCLE_H + +#include "shape.h" + +class SHAPE_CIRCLE : public SHAPE { + +public: + SHAPE_CIRCLE(): + SHAPE( SH_CIRCLE ), m_radius (0) {}; + + SHAPE_CIRCLE( const VECTOR2I& aCenter, int aRadius ): + SHAPE( SH_CIRCLE ), m_radius (aRadius), m_center(aCenter) {}; + + ~SHAPE_CIRCLE() {}; + + const BOX2I BBox(int aClearance = 0) const + { + const VECTOR2I rc (m_radius + aClearance, m_radius + aClearance); + return BOX2I (m_center - rc, rc * 2); + } + + + bool Collide(const SEG& aSeg, int aClearance = 0) const + { + int rc = aClearance + m_radius; + return aSeg.Distance(m_center) <= rc; + } + + void SetRadius(int aRadius) + { + m_radius = aRadius; + } + + void SetCenter (const VECTOR2I& aCenter) + { + m_center = aCenter; + } + + int GetRadius() const + { + return m_radius; + } + + const VECTOR2I GetCenter() const + { + return m_center; + } +private: + int m_radius; + VECTOR2I m_center; +}; + +#endif diff --git a/include/geometry/shape_index.h b/include/geometry/shape_index.h new file mode 100644 index 0000000000..4040cc2549 --- /dev/null +++ b/include/geometry/shape_index.h @@ -0,0 +1,290 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __SHAPE_INDEX_H +#define __SHAPE_INDEX_H + +#include + +template const SHAPE *defaultShapeFunctor( const T aItem ) +{ + return aItem->GetShape(); +} + +template > + +class SHAPE_INDEX_LIST { + + struct ShapeEntry { + ShapeEntry(T aParent) + { + shape = ShapeFunctor(aParent); + bbox = shape->BBox(0); + parent = aParent; + } + + ~ShapeEntry() + { + + } + + T parent; + const SHAPE *shape; + BOX2I bbox; + }; + + typedef std::vector ShapeVec; + typedef typename std::vector::iterator ShapeVecIter; + +public: + +// "Normal" iterator interface, for STL algorithms. + class iterator { + + public: + iterator() {}; + + iterator( ShapeVecIter aCurrent) + : m_current(aCurrent) {}; + + iterator(const iterator &b) : + m_current(b.m_current) {}; + + T operator*() const + { + return (*m_current).parent; + } + + void operator++() + { + ++m_current; + } + + iterator& operator++(int dummy) + { + ++m_current; + return *this; + } + + bool operator ==( const iterator& rhs ) const + { + return m_current == rhs.m_current; + } + + bool operator !=( const iterator& rhs ) const + { + return m_current != rhs.m_current; + } + + const iterator& operator=(const iterator& rhs) + { + m_current = rhs.m_current; + return *this; + } + + private: + ShapeVecIter m_current; + }; + +// "Query" iterator, for iterating over a set of spatially matching shapes. + class query_iterator { + public: + + query_iterator() + { + + } + + query_iterator( ShapeVecIter aCurrent, ShapeVecIter aEnd, SHAPE *aShape, int aMinDistance, bool aExact) + : m_end(aEnd), + m_current(aCurrent), + m_shape(aShape), + m_minDistance(aMinDistance), + m_exact(aExact) + { + if(aShape) + { + m_refBBox = aShape->BBox(); + next(); + } + } + + query_iterator(const query_iterator &b) + : m_end(b.m_end), + m_current(b.m_current), + m_shape(b.m_shape), + m_minDistance(b.m_minDistance), + m_exact(b.m_exact), + m_refBBox(b.m_refBBox) + { + + } + + + T operator*() const + { + return (*m_current).parent; + } + + query_iterator& operator++() + { + ++m_current; + next(); + return *this; + } + + query_iterator& operator++(int dummy) + { + ++m_current; + next(); + return *this; + } + + bool operator ==( const query_iterator& rhs ) const + { + return m_current == rhs.m_current; + } + + bool operator !=( const query_iterator& rhs ) const + { + return m_current != rhs.m_current; + } + + const query_iterator& operator=(const query_iterator& rhs) + { + m_end = rhs.m_end; + m_current = rhs.m_current; + m_shape = rhs.m_shape; + m_minDistance = rhs.m_minDistance; + m_exact = rhs.m_exact; + m_refBBox = rhs.m_refBBox; + return *this; + } + + private: + + void next() + { + while(m_current != m_end) + { + if (m_refBBox.Distance(m_current->bbox) <= m_minDistance) + { + if(!m_exact || m_current->shape->Collide(m_shape, m_minDistance)) + return; + } + ++m_current; + } + } + + ShapeVecIter m_end; + ShapeVecIter m_current; + BOX2I m_refBBox; + bool m_exact; + SHAPE *m_shape; + int m_minDistance; + }; + + void Add(T aItem) + { + ShapeEntry s (aItem); + + m_shapes.push_back(s); + } + + void Remove(const T aItem) + { + ShapeVecIter i; + + for(i=m_shapes.begin(); i!=m_shapes.end();++i) + { + if(i->parent == aItem) + break; + } + + if(i == m_shapes.end()) + return; + + m_shapes.erase(i); + } + + int Size() const + { + return m_shapes.size(); + } + + template + int Query( const SHAPE *aShape, int aMinDistance, Visitor &v, bool aExact = true) //const + { + ShapeVecIter i; + int n = 0; + VECTOR2I::extended_type minDistSq = (VECTOR2I::extended_type) aMinDistance * aMinDistance; + + BOX2I refBBox = aShape->BBox(); + + for(i = m_shapes.begin(); i!=m_shapes.end(); ++i) + { + if (refBBox.SquaredDistance(i->bbox) <= minDistSq) + { + if(!aExact || i->shape->Collide(aShape, aMinDistance)) + { + n++; + if(!v( i->parent )) + return n; + } + } + } + return n; + } + + void Clear() + { + m_shapes.clear(); + } + + query_iterator qbegin( SHAPE *aShape, int aMinDistance, bool aExact ) + { + return query_iterator( m_shapes.begin(), m_shapes.end(), aShape, aMinDistance, aExact); + } + + const query_iterator qend() + { + return query_iterator( m_shapes.end(), m_shapes.end(), NULL, 0, false ); + } + + iterator begin() + { + return iterator( m_shapes.begin() ); + } + + iterator end() + { + return iterator( m_shapes.end() ); + } + +private: + + ShapeVec m_shapes; +}; + +#endif diff --git a/include/geometry/shape_line_chain.h b/include/geometry/shape_line_chain.h new file mode 100644 index 0000000000..54e4662b23 --- /dev/null +++ b/include/geometry/shape_line_chain.h @@ -0,0 +1,531 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __SHAPE_LINE_CHAIN +#define __SHAPE_LINE_CHAIN + +#include +#include + +#include + +#include +#include +#include + +/** + * Class SHAPE_LINE_CHAIN + * + * Represents a polyline (an zero-thickness chain of connected line segments). + * I purposedly didn't name it "polyline" to avoid confusion with the existing CPolyLine class in pcbnew. + * + * SHAPE_LINE_CHAIN class shall not be used for polygons! + */ +class SHAPE_LINE_CHAIN : public SHAPE { + + private: + typedef std::vector::iterator point_iter; + typedef std::vector::const_iterator point_citer; + + public: + + /** + * Struct Intersection + * + * Represents an intersection between two line segments + */ + struct Intersection { + /// segment belonging from the (this) argument of Intersect() + SEG our; + /// segment belonging from the aOther argument of Intersect() + SEG their; + /// point of intersection between our and their. + VECTOR2I p; + }; + + typedef std::vector Intersections; + + /** + * Constructor + * Initializes an empty line chain. + */ + SHAPE_LINE_CHAIN(): + SHAPE (SH_LINE_CHAIN), m_closed(false) {}; + + /** + * Copy Constructor + */ + SHAPE_LINE_CHAIN(const SHAPE_LINE_CHAIN& aShape): + SHAPE (SH_LINE_CHAIN), m_points(aShape.m_points), m_closed(aShape.m_closed) {}; + + /** + * Constructor + * Initializes a 2-point line chain (a single segment) + */ + SHAPE_LINE_CHAIN(const VECTOR2I& a, const VECTOR2I& b): + SHAPE (SH_LINE_CHAIN), + m_closed(false) + { + m_points.resize(2); + m_points[0] = a; + m_points[1] = b; + } + + SHAPE_LINE_CHAIN(const VECTOR2I& a, const VECTOR2I& b, const VECTOR2I& c): + SHAPE (SH_LINE_CHAIN), + m_closed(false) + { + m_points.resize(3); + m_points[0] = a; + m_points[1] = b; + m_points[2] = c; + } + + + SHAPE_LINE_CHAIN(const VECTOR2I *v, int count): + SHAPE (SH_LINE_CHAIN), + m_closed(false) + { + m_points.resize(count); + for(int i = 0; i < count ; i++) + m_points[i] = *v++; + } + + ~SHAPE_LINE_CHAIN() {}; + + /** + * Function Clear() + * Removes all points from the line chain. + */ + void Clear() { + m_points.clear(); + m_closed = false; + } + + /** + * Function SetClosed() + * + * Marks the line chain as closed (i.e. with a segment connecting the last point with the first point). + * @param aClosed: whether the line chain is to be closed or not. + */ + void SetClosed(bool aClosed) + { + m_closed = aClosed; + } + + /** + * Function IsClosed() + * + * @return aClosed: true, when our line is closed. + */ + bool IsClosed() const + { + return m_closed; + } + + /** + * Function SegmentCount() + * + * Returns number of segments in this line chain. + * @return number of segments + */ + int SegmentCount() const + { + int c = m_points.size() - 1; + if(m_closed) + c++; + return std::max(0, c); + } + + /** + * Function PointCount() + * + * Returns the number of points (vertices) in this line chain + * @return number of points + */ + int PointCount() const + { + return m_points.size(); + }; + + /** + * Function Segment() + * + * Returns a segment referencing to the segment (index) in the line chain. + * Modifying ends of the returned segment will modify corresponding points in the line chain. + * @param index: index of the segment in the line chain. Negative values are counted from the end (i.e. -1 means + * the last segment in the line chain) + * @return SEG referenced to given segment in the line chain + */ + SEG Segment ( int index ) + { + if(index < 0) + index += SegmentCount(); + + if(index == (m_points.size() - 1) && m_closed ) + return SEG ( m_points[index], m_points[0], index ); + else + return SEG ( m_points[index], m_points[index + 1], index ); + } + + /** + * Function CSegment() + * + * Returns a read-only segment referencing to the segment (index) in the line chain. + * @param index: index of the segment in the line chain. Negative values are counted from the end (i.e. -1 means + * the last segment in the line chain) + * @return SEG referenced to given segment in the line chain + */ + const SEG CSegment ( int index ) const + { + if(index < 0) + index += SegmentCount(); + + if(index == (m_points.size() - 1) && m_closed ) + return SEG ( const_cast(m_points[index]), + const_cast(m_points[0]), index ); + else + return SEG ( const_cast(m_points[index]), + const_cast(m_points[index + 1]), index ); + } + + /** + * Function Point() + * + * Returns a reference to a given point in the line chain. + * @param index index of the point + * @return reference to the point + */ + VECTOR2I& Point ( int index ) + { + if(index < 0) + index += PointCount(); + return m_points[index]; + } + + /** + * Function CPoint() + * + * Returns a const reference to a given point in the line chain. + * @param index index of the point + * @return const reference to the point + */ + const VECTOR2I& CPoint ( int index ) const + { + if(index < 0) + index += PointCount(); + return m_points[index]; + } + + /// @copydoc SHAPE::BBox() + const BOX2I BBox ( int aClearance = 0 ) const + { + BOX2I bbox; + bbox.Compute(m_points); + return bbox; + } + + + /** + * Function Collide() + * + * Checks if point aP lies closer to us than aClearance. + * @param aP the point to check for collisions with + * @param aClearance minimum distance that does not qualify as a collision. + * @return true, when a collision has been found + */ + bool Collide ( const VECTOR2I& aP, int aClearance = 0 ) const; + + /** + * Function Collide() + * + * Checks if box aBox lies closer to us than aClearance. + * @param aP the box to check for collisions with + * @param aClearance minimum distance that does not qualify as a collision. + * @return true, when a collision has been found + */ + bool Collide ( const BOX2I& aBox, int aClearance = 0 ) const; + + /** + * Function Collide() + * + * Checks if segment aSeg lies closer to us than aClearance. + * @param aSeg the segment to check for collisions with + * @param aClearance minimum distance that does not qualify as a collision. + * @return true, when a collision has been found + */ + bool Collide ( const SEG& aSeg, int aClearance = 0 ) const; + + + /** + * Function Distance() + * + * Computes the minimum distance between the line chain and a point aP. + * @param aP the point + * @return minimum distance. + */ + int Distance( const VECTOR2I & aP ) const; + + /** + * Function Reverse() + * + * Reverses point order in the line chain. + * @return line chain with reversed point order (original A-B-C-D: returned D-C-B-A) + */ + const SHAPE_LINE_CHAIN Reverse() const; + + /** + * Function Length() + * + * Returns length of the line chain in Euclidean metric. + * @return length of the line chain + */ + int Length() const; + + /** + * Function Append() + * + * Appends a new point at the end of the line chain. + * @param x X coordinate of the new point + * @param y Y coordinate of the new point + */ + void Append(int x, int y) + { + VECTOR2I v(x, y); + Append(v); + } + + /** + * Function Append() + * + * Appends a new point at the end of the line chain. + * @param aP the new point + */ + void Append(const VECTOR2I& aP) + { + if(m_points.size() == 0) + m_bbox = BOX2I(aP, VECTOR2I(0, 0)); + + if (m_points.size() == 0 || CPoint(-1) != aP) + { + m_points.push_back(aP); + m_bbox.Merge(aP); + } + } + + /** + * Function Append() + * + * Appends another line chain at the end. + * @param aOtherLine the line chain to be appended. + */ + void Append(const SHAPE_LINE_CHAIN& aOtherLine) + { + if(aOtherLine.PointCount() == 0) + return; + else if(PointCount() == 0 || aOtherLine.CPoint(0) != CPoint(-1)) + { + const VECTOR2I p = aOtherLine.CPoint(0); + m_points.push_back(p); + m_bbox.Merge(p); + } + + for(int i = 1; i SelfIntersecting() const; + + /** + * Function Simplify() + * + * Simplifies the line chain by removing colinear adjacent segments and duplicate vertices. + * @return reference to self. + */ + SHAPE_LINE_CHAIN& Simplify(); + + /** + * Function NearestPoint() + * + * Finds a point on the line chain that is closest to point aP. + * @return the nearest point. + */ + const VECTOR2I NearestPoint(const VECTOR2I& aP) const; + + /// @copydoc SHAPE::Format() + const std::string Format() const; + + bool operator!=(const SHAPE_LINE_CHAIN& rhs) const + { + if(PointCount() != rhs.PointCount()) + return true; + for(int i = 0; i < PointCount(); i++) + if( CPoint(i) != rhs.CPoint(i) ) + return true; + return false; + } + + private: + /// array of vertices + std::vector m_points; + /// is the line chain closed? + bool m_closed; + /// cached bounding box + BOX2I m_bbox; +}; + +#endif // __SHAPE_LINE_CHAIN diff --git a/include/geometry/shape_rect.h b/include/geometry/shape_rect.h new file mode 100644 index 0000000000..eee04ae956 --- /dev/null +++ b/include/geometry/shape_rect.h @@ -0,0 +1,142 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __SHAPE_RECT_H +#define __SHAPE_RECT_H + +#include +#include +#include +#include + +class SHAPE_RECT : public SHAPE { + public: + /** + * Constructor + * Creates an empty (0-sized) rectangle + */ + SHAPE_RECT(): + SHAPE( SH_RECT ), m_w (0), m_h(0) {}; + + /** + * Constructor + * Creates a rectangle defined by top-left corner (x0, y0), width w and height h. + */ + SHAPE_RECT( int x0, int y0, int w, int h ): + SHAPE(SH_RECT), m_p0(x0, y0), m_w(w), m_h(h) {}; + + /** + * Constructor + * Creates a rectangle defined by top-left corner p0, width w and height h. + */ + SHAPE_RECT( const VECTOR2I &p0, int w, int h ): + SHAPE(SH_RECT), m_p0(p0), m_w(w), m_h(h) {}; + + /// @copydoc SHAPE::BBox() + const BOX2I BBox(int aClearance = 0) const + { + BOX2I bbox( VECTOR2I (m_p0.x - aClearance, m_p0.y - aClearance ), + VECTOR2I (m_w + 2 * aClearance, m_h + 2 * aClearance )); + //printf("bb : %s\n",bbox.Format().c_str()); + return bbox; + } + + /** + * Function Diagonal() + * + * Returns length of the diagonal of the rectangle + * @return diagonal length + */ + int Diagonal() const + { + return VECTOR2I(m_w, m_h).EuclideanNorm(); + } + + /// @copydoc SHAPE::Collide() + bool Collide(const SEG& aSeg, int aClearance = 0) const + { + //VECTOR2I pmin = VECTOR2I(std::min(aSeg.a.x, aSeg.b.x), std::min(aSeg.a.y, aSeg.b.y)); + //VECTOR2I pmax = VECTOR2I(std::max(aSeg.a.x, aSeg.b.x), std::max(aSeg.a.y, aSeg.b.y)); + //BOX2I r(pmin, VECTOR2I(pmax.x - pmin.x, pmax.y - pmin.y)); + + //if (BBox(0).SquaredDistance(r) > aClearance * aClearance) + // return false; + + if(BBox(0).Contains(aSeg.a) || BBox(0).Contains(aSeg.b)) + return true; + + VECTOR2I vts[] = { VECTOR2I(m_p0.x, m_p0.y), + VECTOR2I(m_p0.x, m_p0.y + m_h), + VECTOR2I(m_p0.x + m_w, m_p0.y + m_h), + VECTOR2I(m_p0.x + m_w, m_p0.y), + VECTOR2I(m_p0.x, m_p0.y) }; + + for (int i = 0; i < 4; i++) + { + SEG s(vts[i], vts[i+1], i); + if(s.Distance(aSeg) <= aClearance) + return true; + } + + return false; + }; + + /** + * Function GetPosition() + * + * @return top-left corner of the rectangle + */ + const VECTOR2I& GetPosition() const { return m_p0; } + + /** + * Function GetSize() + * + * @return size of the rectangle + */ + const VECTOR2I GetSize() const { return VECTOR2I(m_w, m_h); } + + /** + * Function GetWidth() + * + * @return width of the rectangle + */ + const int GetWidth() const { return m_w; } + + /** + * Function GetHeight() + * + * @return height of the rectangle + */ + const int GetHeight() const { return m_h; } + + private: + ///> Top-left corner + VECTOR2I m_p0; + ///> Width + int m_w; + ///> Height + int m_h; + }; + +#endif // __SHAPE_RECT_H diff --git a/include/math/box2.h b/include/math/box2.h index 3a6368590e..4390b8e7f4 100644 --- a/include/math/box2.h +++ b/include/math/box2.h @@ -66,7 +66,9 @@ public: BOX2( const Vec& aPos, const Vec& aSize ) : m_Pos( aPos ), m_Size( aSize ) - { } + { + Normalize(); + } void SetMaximum() { @@ -416,9 +418,9 @@ public: { ecoord_type x2 = m_Pos.x + m_Size.x; ecoord_type y2 = m_Pos.y + m_Size.y; - ecoord_type xdiff = std::max( aP.x < m_Pos.x ? m_Pos.x - aP.x : m_Pos.x - x2, 0 ); - ecoord_type ydiff = std::max( aP.y < m_Pos.y ? m_Pos.y - aP.y : m_Pos.y - y2, 0 ); - return xdiff * xdiff + ydiff * ydiff; + ecoord_type xdiff = std::max( aP.x < m_Pos.x ? m_Pos.x - aP.x : m_Pos.x - x2, (ecoord_type)0 ); + ecoord_type ydiff = std::max( aP.y < m_Pos.y ? m_Pos.y - aP.y : m_Pos.y - y2, (ecoord_type)0 ); + return xdiff * xdiff + ydiff * ydiff; } ecoord_type Distance( const Vec& aP ) const diff --git a/include/math/math_util.h b/include/math/math_util.h index 2a3ff3fa17..9145b7fa89 100644 --- a/include/math/math_util.h +++ b/include/math/math_util.h @@ -50,12 +50,12 @@ template<> int rescale( int numerator, int value, int denominator ) template<> int64_t rescale( int64_t numerator, int64_t value, int64_t denominator ) { - uint64_t r = 0; + int64_t r = 0; int64_t sign = ( ( numerator < 0) ? -1 : 1 ) * ( denominator < 0 ? - 1: 1 ) * (value < 0 ? - 1 : 1); - uint64_t a = abs( numerator ); - uint64_t b = abs( value ); - uint64_t c = abs( denominator ); + int64_t a = std::abs( numerator ); + int64_t b = std::abs( value ); + int64_t c = std::abs( denominator ); r = c / 2; @@ -77,14 +77,14 @@ template<> int64_t rescale( int64_t numerator, int64_t value, int64_t denominato a0 = a0 * b0 + t1a; a1 = a1 * b1 + (t1 >> 32) + (a0 < t1a); a0 += r; - a1 += a0 < r; + a1 += ((uint64_t)a0) < r; for( i = 63; i >= 0; i-- ) { a1 += a1 + ( (a0 >> i) & 1 ); t1 += t1; - if( c <= a1 ) + if( (uint64_t)c <= a1 ) { a1 -= c; t1++; diff --git a/include/math/vector2d.h b/include/math/vector2d.h index a5eaa604e0..44b72c515a 100644 --- a/include/math/vector2d.h +++ b/include/math/vector2d.h @@ -54,6 +54,8 @@ template <> struct VECTOR2_TRAITS { typedef int64_t extended_type; + static const extended_type ECOORD_MAX = 0x7fffffffffffffffULL; + static const extended_type ECOORD_MIN = 0x8000000000000000ULL; }; // Forward declarations for template friends @@ -123,6 +125,15 @@ public: */ T EuclideanNorm() const; + /** + * Function Squared Euclidean Norm + * computes the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2). + * It is used to calculate the length of the vector. + * @return Scalar, the euclidean norm + */ + extended_type SquaredEuclideanNorm() const; + + /** * Function Perpendicular * computes the perpendicular vector @@ -130,40 +141,6 @@ public: */ VECTOR2 Perpendicular() const; - /** - * Function LineProjection - * computes the perpendicular projection point of self on a line - * going through aA and aB points. - * @return Projected point - */ - VECTOR2 LineProjection( const VECTOR2& aA, const VECTOR2& aB ) const; - - /** - * Function LineSide - * determines on which side of directed line passing via points aEnd - * and a start aStart we are. - * @return: < 0: left, 0 : on the line, > 0 : right - */ - int LineSide( const VECTOR2& aStart, const VECTOR2& aEnd ) const; - - /** - * Function LineDistance - * returns the closest Euclidean distance to a line defined by points - * aStart and aEnd. - * @param aDetermineSide: when true, the sign of the returned value indicates - * the side of the line at which we are (negative = left) - * @return the distance - */ - T LineDistance( const VECTOR2& aStart, const VECTOR2& aEnd, - bool aDetermineSide = false ) const; - - /** - * Function ClosestSegmentPoint - * returns the closest point on a line segment defined by aStart and aEnd. - * @return: our point - */ - VECTOR2 ClosestSegmentPoint( const VECTOR2& aStart, const VECTOR2& aEnd ) const; - /** * Function Resize * returns a vector of the same direction, but length specified in aNewLength @@ -308,6 +285,13 @@ T VECTOR2::EuclideanNorm() const return sqrt( (extended_type) x * x + (extended_type) y * y ); } +template +typename VECTOR2::extended_type VECTOR2::SquaredEuclideanNorm() const +{ + return (extended_type)x * x + (extended_type) y * y ; +} + + template double VECTOR2::Angle() const @@ -367,89 +351,6 @@ VECTOR2& VECTOR2::operator-=( const T& aScalar ) y -= aScalar; return *this; } - - -template -int VECTOR2::LineSide( const VECTOR2& aStart, const VECTOR2& aEnd ) const -{ - VECTOR2 d = aEnd - aStart; - VECTOR2 ap = *this - aStart; - - extended_type det = (extended_type) d.x * (extended_type) ap.y - - (extended_type) d.y * (extended_type) ap.x; - - return det < 0 ? -1 : (det > 0 ? 1 : 0); -} - - -template -VECTOR2 VECTOR2::LineProjection( const VECTOR2& aA, const VECTOR2& aB ) const -{ - const VECTOR2 d = aB - aA; - extended_type det = (extended_type) d.x * d.x + d.y * (extended_type) d.y; - extended_type dxdy = (extended_type) d.x * d.y; - extended_type qx = - ( (extended_type) aA.x * d.y * d.y + (extended_type) d.x * d.x * x - dxdy * - (aA.y - y) ) / det; - extended_type qy = - ( (extended_type) aA.y * d.x * d.x + (extended_type) d.y * d.y * y - dxdy * - (aA.x - x) ) / det; - - return VECTOR2 ( (T) qx, (T) qy ); -} - - -template -T VECTOR2::LineDistance( const VECTOR2& aStart, const VECTOR2& aEnd, - bool aDetermineSide ) const -{ - extended_type a = aStart.y - aEnd.y; - extended_type b = aEnd.x - aStart.x; - extended_type c = -a * aStart.x - b * aStart.y; - - T dist = ( a * x + b * y + c ) / sqrt( a * a + b * b ); - return aDetermineSide ? dist : abs( dist ); -} - - -template -VECTOR2 VECTOR2::ClosestSegmentPoint( const VECTOR2& aStart, - const VECTOR2& aEnd ) const -{ - VECTOR2 d = (aEnd - aStart); - extended_type l_squared = (extended_type) d.x * d.x + (extended_type) d.y * d.y; - - - if( l_squared == 0 ) - { - return aStart; - } - - extended_type t = - (extended_type) (x - aStart.x) * (extended_type) d.x + - (extended_type) (y - aStart.y) * (extended_type) d.y; - - if( t < 0 ) - { - return aStart; - } - else if( t > l_squared ) - { - return aEnd; - } - - double xp = (double) t * (double) d.x / (double) l_squared; - double yp = (double) t * (double) d.y / (double) l_squared; - - /*VECTOR2 proj = aStart + VECTOR2 ( ( t * (extended_type) d.x / l_squared ), - ( t * ( extended_type) d.y / l_squared ) );*/ - - VECTOR2 proj = aStart + VECTOR2 ( (T) xp, (T) yp ); - - return proj; -} - - template VECTOR2 VECTOR2::Rotate( double aAngle ) const { @@ -464,14 +365,15 @@ VECTOR2 VECTOR2::Rotate( double aAngle ) const template VECTOR2 VECTOR2::Resize( T aNewLength ) const { - if( x == 0 && y == 0 ) - return VECTOR2 ( 0, 0 ); + if(x == 0 && y == 0) + return VECTOR2 (0, 0); - T l = this->EuclideanNorm(); + extended_type l_sq_current = (extended_type)this->x * this->x + (extended_type)this->y * this->y; + extended_type l_sq_new = (extended_type) aNewLength * aNewLength; - return VECTOR2 ( - rescale( aNewLength, x, l ), - rescale( aNewLength, y, l ) ); + return VECTOR2 ( + (this->x < 0 ? -1 : 1 ) * sqrt(rescale(l_sq_new, (extended_type) x * x, l_sq_current)), + (this->y < 0 ? -1 : 1 ) * sqrt(rescale(l_sq_new, (extended_type) y * y, l_sq_current))); } diff --git a/include/tool/tool_dispatcher.h b/include/tool/tool_dispatcher.h index d4057c68b0..ae1db0c4d2 100644 --- a/include/tool/tool_dispatcher.h +++ b/include/tool/tool_dispatcher.h @@ -73,6 +73,8 @@ private: bool handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ); bool handlePopupMenu( wxEvent& aEvent ); + wxPoint getCurrentMousePos() const; + int decodeModifiers( const wxKeyboardState* aState ) const; struct ButtonState; diff --git a/include/view/view.h b/include/view/view.h index 29cb5ecb0e..c5146d3540 100644 --- a/include/view/view.h +++ b/include/view/view.h @@ -443,6 +443,15 @@ public: return ( m_layers.at( aLayer ).target == TARGET_CACHED ); } + /** + * Function MarkDirty() + * Forces redraw of view on the next rendering. + */ + void MarkDirty() + { + for( int i = 0; i < TARGETS_NUMBER; ++i ) + m_dirtyTargets[i] = true; + } static const int VIEW_MAX_LAYERS = 128; ///* maximum number of layers that may be shown diff --git a/include/view/view_group.h b/include/view/view_group.h index 514000bac3..18f5f5a9b6 100644 --- a/include/view/view_group.h +++ b/include/view/view_group.h @@ -130,6 +130,23 @@ public: m_layer = aLayer; } + /** + * Function FreeItems() + * Frees all the items that were added to the group. + */ + void FreeItems(); + + /** + * Function GetView() + * Returns pointer to the VIEW instance used by items. + * + * @return Pointer to the VIEW instance. + */ + KiGfx::VIEW *GetView() const + { + return m_view; + } + protected: /// These functions cannot be used with VIEW_GROUP as they are intended only to work with /// singular VIEW_ITEMs (there is only one-to-one relation between item/layer combination and diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index d86f9dbf54..02c943d0d9 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -1,4 +1,5 @@ add_definitions(-DPCBNEW) +add_subdirectory(router) if (KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/scripting) @@ -117,6 +118,7 @@ set(PCBNEW_AUTOROUTER_SRCS autorouter/work.cpp ) + set(PCBNEW_CLASS_SRCS tool_modview.cpp modview.cpp @@ -325,6 +327,7 @@ if (KICAD_SCRIPTING_MODULES) swig_link_libraries(pcbnew 3d-viewer pcbcommon + pnsrouter common pcad2kicadpcb polygon @@ -421,6 +424,7 @@ endif(APPLE) target_link_libraries(pcbnew 3d-viewer pcbcommon + pnsrouter common pcad2kicadpcb polygon diff --git a/pcbnew/menubar_pcbframe.cpp b/pcbnew/menubar_pcbframe.cpp index 5eb3b28a96..03793ca68b 100644 --- a/pcbnew/menubar_pcbframe.cpp +++ b/pcbnew/menubar_pcbframe.cpp @@ -305,6 +305,11 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Interactive selection and drag&drop tool." ), KiBitmap( tools_xpm ) ); + AddMenuItem( editMenu, ID_PNS_ROUTER_TOOL, + _( "Interactive router" ), + _( "Interactive router drag&drop tool." ), + KiBitmap( tools_xpm ) ); + /** Create View menu **/ wxMenu* viewMenu = new wxMenu; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 9fccb84562..6810f5049f 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -189,6 +189,12 @@ void PCB_RENDER_SETTINGS::update() } +const COLOR4D& PCB_RENDER_SETTINGS::GetLayerColor( int aLayer ) const +{ + return m_layerColors[aLayer]; +} + + PCB_PAINTER::PCB_PAINTER( GAL* aGal ) : PAINTER( aGal ) { diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h index d7f922e623..2e106f2b93 100644 --- a/pcbnew/pcb_painter.h +++ b/pcbnew/pcb_painter.h @@ -89,6 +89,8 @@ public: /// @copydoc RENDER_SETTINGS::GetColor() virtual const COLOR4D& GetColor( const VIEW_ITEM* aItem, int aLayer ) const; + const COLOR4D& GetLayerColor( int aLayer ) const; + protected: /// @copydoc RENDER_SETTINGS::Update() void update(); diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 8904b35e81..bc257d29cc 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -120,8 +120,10 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME ) // menu Config /* Tom's hacks start */ - EVT_MENU ( ID_SELECTION_TOOL, PCB_EDIT_FRAME::onGenericCommand ) + EVT_MENU ( ID_SELECTION_TOOL, PCB_EDIT_FRAME::onGenericCommand ) EVT_TOOL ( ID_SELECTION_TOOL, PCB_EDIT_FRAME::onGenericCommand ) + EVT_MENU ( ID_PNS_ROUTER_TOOL, PCB_EDIT_FRAME::onGenericCommand ) + EVT_TOOL ( ID_PNS_ROUTER_TOOL, PCB_EDIT_FRAME::onGenericCommand ) /* Tom's hacks end */ EVT_MENU( ID_PCB_DRAWINGS_WIDTHS_SETUP, PCB_EDIT_FRAME::OnConfigurePcbOptions ) @@ -752,7 +754,7 @@ void PCB_EDIT_FRAME::setHighContrastLayer( LAYER_NUM aLayer ) KiGfx::VIEW* view = m_galCanvas->GetView(); KiGfx::RENDER_SETTINGS* rSettings = view->GetPainter()->GetSettings(); - if( DisplayOpt.ContrastModeDisplay ) +// if( DisplayOpt.ContrastModeDisplay ) { view->ClearTopLayers(); view->SetTopLayer( aLayer ); diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index ba479ffe65..ff4bb6062f 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -366,7 +366,8 @@ enum pcbnew_ids ID_FOOTPRINT_WIZARD_SELECT_WIZARD, ID_FOOTPRINT_WIZARD_EXPORT_TO_BOARD, - ID_SELECTION_TOOL + ID_SELECTION_TOOL, + ID_PNS_ROUTER_TOOL }; #endif // PCBNEW_ID_H_ diff --git a/pcbnew/router/readme.txt b/pcbnew/router/readme.txt new file mode 100644 index 0000000000..933b93a817 --- /dev/null +++ b/pcbnew/router/readme.txt @@ -0,0 +1,4 @@ +You'll see the P&S router sources here, but just not right now. +We are still dealing with some non-technical issues that should be solved by the next week. + +Tom \ No newline at end of file diff --git a/pcbnew/tools/pcb_tools.cpp b/pcbnew/tools/pcb_tools.cpp index 30692560d8..9d2457c5ec 100644 --- a/pcbnew/tools/pcb_tools.cpp +++ b/pcbnew/tools/pcb_tools.cpp @@ -36,16 +36,18 @@ #include "selection_tool.h" #include "move_tool.h" +#include void PCB_EDIT_FRAME::setupTools() { - // Create the manager and dispatcher. Route draw panel events to the dispatcher + // Create the manager and dispatcher & route draw panel events to the dispatcher m_toolManager = new TOOL_MANAGER; m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager, this ); m_galCanvas->SetEventDispatcher( m_toolDispatcher ); - // Register tools + // Register tools. m_toolManager->RegisterTool( new SELECTION_TOOL ); + m_toolManager->RegisterTool( new ROUTER_TOOL ); m_toolManager->RegisterTool( new MOVE_TOOL ); }