diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index f878c58dec..c31178986a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -353,6 +353,7 @@ set( COMMON_SRCS geometry/seg.cpp geometry/shape.cpp geometry/shape_collisions.cpp + geometry/shape_arc.cpp geometry/shape_file_io.cpp geometry/shape_line_chain.cpp geometry/shape_poly_set.cpp diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index 0286ce76a6..ad6832d86f 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -785,7 +785,7 @@ void OPENGL_GAL::drawTriangulatedPolyset( const SHAPE_POLY_SET& aPolySet ) { auto triPoly = aPolySet.TriangulatedPolygon( j ); - for ( int i = 0; i < triPoly->GetTriangleCount(); i++ ) + for( int i = 0; i < triPoly->GetTriangleCount(); i++ ) { VECTOR2I a, b, c; triPoly->GetTriangle( i ,a,b,c); diff --git a/common/geometry/shape_arc.cpp b/common/geometry/shape_arc.cpp new file mode 100644 index 0000000000..451cefbe8f --- /dev/null +++ b/common/geometry/shape_arc.cpp @@ -0,0 +1,181 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 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 + +bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance ) const +{ + int minDist = aClearance + m_width / 2; + auto centerDist = aSeg.Distance( m_pc ); + + if( centerDist < minDist ) + return true; + + auto ab = (aSeg.B - aSeg.A ); + auto ac = ( m_pc - aSeg.A ); + + auto lenAbSq = ab.SquaredEuclideanNorm(); + + auto lambda = (double) ac.Dot( ab ) / (double) lenAbSq; + + + if( lambda >= 0.0 && lambda <= 1.0 ) + { + VECTOR2I p; + + p.x = (double) aSeg.A.x * lambda + (double) aSeg.B.x * (1.0 - lambda); + p.y = (double) aSeg.A.y * lambda + (double) aSeg.B.y * (1.0 - lambda); + + auto p0pdist = ( m_p0 - p ).EuclideanNorm(); + + if( p0pdist < minDist ) + return true; + + auto p1pdist = ( m_p1 - p ).EuclideanNorm(); + + if( p1pdist < minDist ) + return true; + } + + auto p0dist = aSeg.Distance( m_p0 ); + + if( p0dist > minDist ) + return true; + + auto p1dist = aSeg.Distance( m_p1 ); + + if( p1dist > minDist ) + return false; + + + return true; +} + + +bool SHAPE_ARC::ConstructFromCorners( VECTOR2I aP0, VECTOR2I aP1, double aCenterAngle ) +{ + VECTOR2D mid = ( VECTOR2D( aP0 ) + VECTOR2D( aP1 ) ) * 0.5; + VECTOR2D chord = VECTOR2D( aP1 ) - VECTOR2D( aP0 ); + double c = (aP1 - aP0).EuclideanNorm() / 2; + VECTOR2D d = chord.Rotate( M_PI / 2.0 ).Resize( c ); + + m_pc = mid + d * ( 1.0 / tan( aCenterAngle / 2.0 * M_PI / 180.0 ) ); + m_p0 = aP0; + m_p1 = aP1; + + return true; +} + +bool SHAPE_ARC::ConstructFromCornerAndAngles( VECTOR2I aP0, + double aStartAngle, + double aCenterAngle, + double aRadius ) +{ + m_p0 = aP0; + auto d1 = VECTOR2D( 1.0, 0.0 ).Rotate( aStartAngle * M_PI / 180.0 ) * aRadius; + auto d2 = + VECTOR2D( 1.0, 0.0 ).Rotate( (aStartAngle + aCenterAngle) * M_PI / 180.0 ) * aRadius; + + m_pc = m_p0 - (VECTOR2I) d1; + m_p1 = m_pc + (VECTOR2I) d2; + + if( aCenterAngle < 0 ) + std::swap( m_p0, m_p1 ); + + return true; +} + + +bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance ) const +{ + assert( false ); + return false; +} + + +double SHAPE_ARC::GetStartAngle() const +{ + VECTOR2D d( m_p0 - m_pc ); + + return 180.0 / M_PI * atan2( d.y, d.x ); +} + + +double SHAPE_ARC::GetEndAngle() const +{ + VECTOR2D d( m_p1 - m_pc ); + + return 180.0 / M_PI * atan2( d.y, d.x ); +} + + +double SHAPE_ARC::GetCentralAngle() const +{ + auto ea = GetEndAngle(); + auto sa = GetStartAngle(); + + if( ea < sa ) + ea += 360.0; + + while( sa < 0.0 ) + { + sa += 360.0; + ea += 360.0; + } + + return ea - sa; +} + +const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy ) const +{ + SHAPE_LINE_CHAIN rv; + double ca = GetCentralAngle(); + double r = GetRadius(); + double step; + auto c = GetCenter(); + int n; + + if( r == 0.0 ) + { + ca = 0; + n = 0; + } + else + { + step = 180 / M_PI * acos( r * ( 1 - aAccuracy ) / r ); + n = (int) ceil(ca / step); + } + + for( int i = 0; i <= n ; i++ ) + { + double a = GetStartAngle() + ca * (double) i / (double) n; + double x = c.x + r * cos( a * M_PI / 180.0 ); + double y = c.y + r * sin( a * M_PI / 180.0 ); + + rv.Append( (int) x, (int) y ); + } + + return rv; +} diff --git a/include/geometry/direction45.h b/include/geometry/direction45.h index 50236eaff4..46f4500bb4 100644 --- a/include/geometry/direction45.h +++ b/include/geometry/direction45.h @@ -25,6 +25,9 @@ #include #include +// believe or not, X11 headers have a F****ING macro called Opposite... +#undef Opposite + /** * Class DIRECTION_45. * Represents route directions & corner angles in a 45-degree metric. diff --git a/include/geometry/shape.h b/include/geometry/shape.h index 300de610f8..dfbc0c2316 100644 --- a/include/geometry/shape.h +++ b/include/geometry/shape.h @@ -45,8 +45,9 @@ enum SHAPE_TYPE SH_LINE_CHAIN, ///> line chain (polyline) SH_CIRCLE, ///> circle SH_CONVEX, ///> convex polygon - SH_POLY_SET, ///> any polygon (with holes, etc.) - SH_COMPOUND ///> compound shape, consisting of multiple simple shapes + SH_POLY_SET, ///> set of polygons (with holes, etc.) + SH_COMPOUND, ///> compound shape, consisting of multiple simple shapes + SH_ARC ///> circular arc }; /** diff --git a/include/geometry/shape_arc.h b/include/geometry/shape_arc.h new file mode 100644 index 0000000000..7b36c1014d --- /dev/null +++ b/include/geometry/shape_arc.h @@ -0,0 +1,129 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 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_ARC_H +#define __SHAPE_ARC_H + +#include +#include + +class SHAPE_LINE_CHAIN; + +class SHAPE_ARC : public SHAPE +{ +public: + SHAPE_ARC() : + SHAPE( SH_ARC ), m_width( 0 ) {}; + + SHAPE_ARC( const VECTOR2I& pa, const VECTOR2I& pb, const VECTOR2I& pCenter, int aWidth = 0 ) : + SHAPE( SH_ARC ), m_p0( pa ), m_p1( pb ), m_pc( pCenter ), m_width( aWidth ) {}; + + SHAPE_ARC( const SHAPE_ARC& aOther ) + : SHAPE( SH_ARC ) + { + m_p0 = aOther.m_p0; + m_p1 = aOther.m_p1; + m_pc = aOther.m_pc; + } + + ~SHAPE_ARC() {}; + + SHAPE* Clone() const override + { + return new SHAPE_ARC( *this ); + } + + const VECTOR2I& GetP0() const { return m_p0; } + const VECTOR2I& GetP1() const { return m_p1; } + const VECTOR2I& GetCenter() const { return m_pc; } + + const BOX2I BBox( int aClearance = 0 ) const override + { + assert( false ); + return BOX2I(); // fixme + } + + bool Collide( const SEG& aSeg, int aClearance = 0 ) const override; + bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override; + + void SetWidth( int aWidth ) + { + m_width = aWidth; + } + + int GetWidth() const + { + return m_width; + } + + bool IsSolid() const override + { + return true; + } + + void Move( const VECTOR2I& aVector ) override + { + m_p0 += aVector; + m_p1 += aVector; + m_pc += aVector; + } + + int GetRadius() const + { + return (m_pc - m_p0).EuclideanNorm(); + } + + SEG GetChord() const + { + return SEG( m_p0, m_p1 ); + } + + double GetCentralAngle() const; + double GetStartAngle() const; + double GetEndAngle() const; + + + bool ConstructFromCorners( VECTOR2I aP0, VECTOR2I aP1, double aCenterAngle ); + + + bool ConstructFromCornerAndAngles( VECTOR2I aP0, + double aStartAngle, + double aCenterAngle, + double aRadius ); + + const SHAPE_LINE_CHAIN ConvertToPolyline( double aAccuracy = 0.02f ) const; + +private: + + bool ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ) const + { + return (ecoord) ( aC.y - aA.y ) * ( aB.x - aA.x ) > + (ecoord) ( aB.y - aA.y ) * ( aC.x - aA.x ); + } + + VECTOR2I m_p0, m_p1, m_pc; + int m_width; +}; + +#endif diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index e8b949bf28..f69a261010 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "tools/pcb_tool.h" @@ -802,12 +803,12 @@ bool PNS_KICAD_IFACE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone ) LSET layers = aZone->GetLayerSet(); - for ( int layer = F_Cu; layer <= B_Cu; layer++ ) + for( int layer = F_Cu; layer <= B_Cu; layer++ ) { if ( ! layers[layer] ) continue; - for ( int outline = 0; outline < poly.OutlineCount(); outline++ ) + for( int outline = 0; outline < poly.OutlineCount(); outline++ ) { auto tri = poly.TriangulatedPolygon( outline ); @@ -837,9 +838,59 @@ bool PNS_KICAD_IFACE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone ) return true; } -std::unique_ptr PNS_KICAD_IFACE::syncGraphicalItem( DRAWSEGMENT* aItem ) +bool PNS_KICAD_IFACE::syncGraphicalItem( PNS::NODE* aWorld, DRAWSEGMENT* aItem ) { - return nullptr; + std::vector segs; + + + if( aItem->GetLayer() != Edge_Cuts ) + return false; + + switch( aItem->GetShape() ) + { + case S_ARC: + { + SHAPE_ARC arc( aItem->GetArcStart(), aItem->GetArcEnd(), aItem->GetCenter() ); + auto l = arc.ConvertToPolyline(); + + for( int i = 0; i < l.SegmentCount(); i++ ) + { + SHAPE_SEGMENT *seg = new SHAPE_SEGMENT( l.CSegment(i), aItem->GetWidth() ); + segs.push_back( seg ); + } + + break; + } + case S_SEGMENT: + { + SHAPE_SEGMENT *seg = new SHAPE_SEGMENT( aItem->GetStart(), aItem->GetEnd(), aItem->GetWidth() ); + segs.push_back( seg ); + + break; + } + default: + break; + } + + for( auto seg : segs ) + { + for( int layer = F_Cu; layer <= B_Cu; layer++ ) + { + std::unique_ptr< PNS::SOLID > solid( new PNS::SOLID ); + + solid->SetLayer( layer ); + solid->SetNet( -1 ); + solid->SetParent( nullptr ); + solid->SetShape( seg->Clone() ); + solid->SetRoutable( false ); + + aWorld->Add( std::move( solid ) ); + } + + delete seg; + } + + return true; } void PNS_KICAD_IFACE::SetBoard( BOARD* aBoard ) @@ -861,10 +912,7 @@ void PNS_KICAD_IFACE::SyncWorld( PNS::NODE *aWorld ) for( auto gitem : m_board->Drawings() ) { - auto solid = syncGraphicalItem( static_cast( gitem ) ); - - if ( solid ) - aWorld->Add( std::move( solid ) ); + syncGraphicalItem( aWorld, static_cast( gitem ) ); } for( auto zone : m_board->Zones() ) diff --git a/pcbnew/router/pns_kicad_iface.h b/pcbnew/router/pns_kicad_iface.h index bede5496ce..40ae1ef2e8 100644 --- a/pcbnew/router/pns_kicad_iface.h +++ b/pcbnew/router/pns_kicad_iface.h @@ -70,7 +70,7 @@ private: std::unique_ptr syncPad( D_PAD* aPad ); std::unique_ptr syncTrack( TRACK* aTrack ); std::unique_ptr syncVia( VIA* aVia ); - std::unique_ptr syncGraphicalItem( DRAWSEGMENT* aItem ); + bool syncGraphicalItem( PNS::NODE* aWorld, DRAWSEGMENT* aItem ); bool syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone ); KIGFX::VIEW* m_view; diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index 02acf6c2aa..579ae1dcc4 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -172,7 +172,7 @@ bool ROUTER::StartRouting( const VECTOR2I& aP, ITEM* aStartItem, int aLayer ) if( ! isStartingPointRoutable( aP, aLayer ) ) { - SetFailureReason( _("Cannot start routing inside a keepout area." ) ); + SetFailureReason( _("Cannot start routing inside a keepout area or board outline." ) ); return false; }