diff --git a/include/board_design_settings.h b/include/board_design_settings.h index 734e126e98..3540f5d599 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2009-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,7 +26,6 @@ #define BOARD_DESIGN_SETTINGS_H_ #include -#include #include #include #include @@ -167,6 +166,8 @@ enum LAYER_CLASS_COUNT }; +// forward declaration from class_track.h +enum class VIATYPE : int; /** * BOARD_DESIGN_SETTINGS diff --git a/libs/kimath/CMakeLists.txt b/libs/kimath/CMakeLists.txt index a3b3ffc0c3..bf065f5dad 100644 --- a/libs/kimath/CMakeLists.txt +++ b/libs/kimath/CMakeLists.txt @@ -5,6 +5,7 @@ set( KIMATH_SRCS src/trigo.cpp src/geometry/convex_hull.cpp + src/geometry/direction_45.cpp src/geometry/geometry_utils.cpp src/geometry/polygon_test_point_inside.cpp src/geometry/seg.cpp diff --git a/libs/kimath/include/geometry/direction45.h b/libs/kimath/include/geometry/direction45.h index 360f75f19f..f25f81d803 100644 --- a/libs/kimath/include/geometry/direction45.h +++ b/libs/kimath/include/geometry/direction45.h @@ -3,7 +3,7 @@ * * Copyright (C) 2013-2015 CERN * Author: Tomasz Wlostowski - * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017-2019 KiCad Developers, see AUTHORS.txt for contributors. * * 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 @@ -43,7 +43,7 @@ public: * Represents available directions - there are 8 of them, as on a rectilinear map (north = up) + * an extra undefined direction, reserved for traces that don't respect 45-degree routing regime. */ - enum Directions + enum Directions : int { N = 0, NE = 1, @@ -53,6 +53,7 @@ public: SW = 5, W = 6, NW = 7, + LAST = 8, UNDEFINED = -1 }; @@ -70,13 +71,14 @@ public: ANG_UNDEFINED = 0x20 }; - DIRECTION_45( Directions aDir = UNDEFINED ) : m_dir( aDir ) {} + DIRECTION_45( Directions aDir = UNDEFINED ) : m_dir( aDir ), m_90deg( false ) {} /** * Constructor * @param aVec vector, whose direction will be translated into a DIRECTION_45. */ - DIRECTION_45( const VECTOR2I& aVec ) + DIRECTION_45( const VECTOR2I &aVec, bool a90 = false ) : + m_90deg( a90 ) { construct_( aVec ); } @@ -85,7 +87,8 @@ public: * Constructor * @param aSeg segment, whose direction will be translated into a DIRECTION_45. */ - DIRECTION_45( const SEG& aSeg ) + DIRECTION_45( const SEG& aSeg, bool a90 = false ) : + m_90deg( a90 ) { construct_( aSeg.B - aSeg.A ); } @@ -198,51 +201,13 @@ public: * @param aP0 starting point * @param aP1 ending point * @param aStartDiagonal whether the first segment has to be diagonal + * @param aRadius is the radius of curvature for rounding. If =0, do not insert arcs * @return the trace */ const SHAPE_LINE_CHAIN BuildInitialTrace( const VECTOR2I& aP0, const VECTOR2I& aP1, - bool aStartDiagonal = false ) const - { - int w = abs( aP1.x - aP0.x ); - int h = abs( aP1.y - aP0.y ); - int sw = sign( aP1.x - aP0.x ); - int sh = sign( aP1.y - aP0.y ); - - VECTOR2I mp0, mp1; - - // we are more horizontal than vertical? - if( w > h ) - { - mp0 = VECTOR2I( ( w - h ) * sw, 0 ); // direction: E - mp1 = VECTOR2I( h * sw, h * sh ); // direction: NE - } - else - { - mp0 = VECTOR2I( 0, sh * ( h - w ) ); // direction: N - mp1 = VECTOR2I( sw * w, sh * w ); // direction: NE - } - - bool start_diagonal; - - if( m_dir == UNDEFINED ) - start_diagonal = aStartDiagonal; - else - start_diagonal = IsDiagonal(); - - SHAPE_LINE_CHAIN pl; - - pl.Append( aP0 ); - - if( start_diagonal ) - pl.Append( aP0 + mp1 ); - else - pl.Append( aP0 + mp0 ); - - pl.Append( aP1 ); - pl.Simplify(); - return pl; - } + bool aStartDiagonal = false, + int aMaxRadius = 0 ) const; bool operator==( const DIRECTION_45& aOther ) const { @@ -258,14 +223,19 @@ public: * Function Right() * * Returns the direction on the right side of this (i.e. turns right - * by 45 deg) + * by 45 or 90 deg) */ const DIRECTION_45 Right() const { DIRECTION_45 r; if ( m_dir != UNDEFINED ) - r.m_dir = static_cast( ( m_dir + 1 ) % 8 ); + { + if( m_90deg ) + r.m_dir = static_cast( ( m_dir + 2 ) % LAST ); + else + r.m_dir = static_cast( ( m_dir + 1 ) % LAST ); + } return r; } @@ -274,19 +244,19 @@ public: * Function Left() * * Returns the direction on the left side of this (i.e. turns left - * by 45 deg) + * by 45 or 90 deg) */ const DIRECTION_45 Left() const { DIRECTION_45 l; - if ( m_dir == UNDEFINED ) - return l; - - if( m_dir == N ) - l.m_dir = NW; - else - l.m_dir = static_cast( m_dir - 1 ); + if ( m_dir != UNDEFINED ) + { + if( m_90deg ) + l.m_dir = static_cast( ( m_dir + LAST - 2 ) % LAST ); + else + l.m_dir = static_cast( ( m_dir + LAST - 1 ) % LAST ); + } return l; } @@ -344,11 +314,11 @@ private: int dir = ( mag + 22.5 ) / 45.0; - if( dir >= 8 ) - dir = dir - 8; + if( dir >= LAST ) + dir -= LAST; if( dir < 0 ) - dir = dir + 8; + dir += LAST; m_dir = (Directions) dir; @@ -357,6 +327,9 @@ private: ///> our actual direction Directions m_dir; + + ///> Are we routing on 45 or 90 degree increments + bool m_90deg; }; #endif // DIRECTION45_H diff --git a/libs/kimath/include/geometry/shape_arc.h b/libs/kimath/include/geometry/shape_arc.h index 9b9ee7f494..6c17bf7c5a 100644 --- a/libs/kimath/include/geometry/shape_arc.h +++ b/libs/kimath/include/geometry/shape_arc.h @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 CERN + * Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors. * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or @@ -50,6 +51,7 @@ public: SHAPE( SH_ARC ), m_p0( aArcStartPoint ), m_pc( aArcCenter ), m_centralAngle( aCenterAngle ), m_width( aWidth ) { + update_bbox(); } SHAPE_ARC( const SHAPE_ARC& aOther ) @@ -59,6 +61,7 @@ public: m_pc = aOther.m_pc; m_centralAngle = aOther.m_centralAngle; m_width = aOther.m_width; + m_bbox = aOther.m_bbox; } ~SHAPE_ARC() {} @@ -70,6 +73,7 @@ public: const VECTOR2I& GetP0() const { return m_p0; } const VECTOR2I GetP1() const; + const VECTOR2I GetArcMid() const; const VECTOR2I& GetCenter() const { return m_pc; } const BOX2I BBox( int aClearance = 0 ) const override; @@ -96,6 +100,7 @@ public: { m_p0 += aVector; m_pc += aVector; + update_bbox(); } /** @@ -114,6 +119,7 @@ public: m_pc += aCenter; m_p0 += aCenter; + update_bbox(); } void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aVector = { 0, 0 } ) @@ -131,6 +137,8 @@ public: m_pc.y = -m_pc.y + 2 * aVector.y; m_centralAngle = - m_centralAngle; } + + update_bbox(); } int GetRadius() const; @@ -176,11 +184,14 @@ private: (ecoord) ( aB.y - aA.y ) * ( aC.x - aA.x ); } + void update_bbox(); + VECTOR2I m_p0, m_pc; double m_centralAngle; int m_width; + BOX2I m_bbox; }; #endif diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index 9707f855e3..d4bdb31288 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -3,7 +3,7 @@ * * Copyright (C) 2013 CERN * @author Tomasz Wlostowski - * Copyright (C) 2013-2019 + * Copyright (C) 2013-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -93,8 +93,8 @@ public: m_shapes( aShape.m_shapes ), m_arcs( aShape.m_arcs ), m_closed( aShape.m_closed ), - m_bbox( aShape.m_bbox ), - m_width( aShape.m_width ) + m_width( aShape.m_width ), + m_bbox( aShape.m_bbox ) {} SHAPE_LINE_CHAIN( const std::vector& aV, bool aClosed = false ) @@ -115,8 +115,20 @@ public: m_shapes = std::vector( aV.size(), ssize_t( SHAPE_IS_PT ) ); } - SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) - : SHAPE( SH_LINE_CHAIN ), m_closed( true ), m_width( 0 ) + SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed = false ) + : SHAPE( SH_LINE_CHAIN ), + m_closed( aClosed ), + m_width( 0 ) + { + m_points = aArc.ConvertToPolyline().CPoints(); + m_arcs.emplace_back( aArc ); + m_shapes = std::vector( m_points.size(), 0 ); + } + + SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) : + SHAPE( SH_LINE_CHAIN ), + m_closed( true ), + m_width( 0 ) { m_points.reserve( aPath.size() ); m_shapes = std::vector( aPath.size(), ssize_t( SHAPE_IS_PT ) ); @@ -298,6 +310,22 @@ public: return m_points[PointCount() - 1]; } + /** + * @return the vector of stored arcs + */ + const std::vector& CArcs() const + { + return m_arcs; + } + + /** + * @return the vector of values indicating shape type and location + */ + const std::vector& CShapes() const + { + return m_shapes; + } + /// @copydoc SHAPE::BBox() const BOX2I BBox( int aClearance = 0 ) const override { @@ -695,7 +723,33 @@ public: double Area() const; + size_t ArcCount() const + { + return m_arcs.size(); + } + + ssize_t ArcIndex( size_t aSegment ) const + { + if( aSegment >= m_shapes.size() ) + return SHAPE_IS_PT; + + return m_shapes[aSegment]; + } + + const SHAPE_ARC& Arc( size_t aArc ) const + { + return m_arcs[aArc]; + } + + bool isArc( size_t aSegment ) const + { + return aSegment < m_shapes.size() && m_shapes[aSegment] != SHAPE_IS_PT; + } + private: + + constexpr static ssize_t SHAPE_IS_PT = -1; + /// array of vertices std::vector m_points; @@ -705,7 +759,6 @@ private: * If the value is -1, the point is just a point */ std::vector m_shapes; - constexpr static ssize_t SHAPE_IS_PT = -1; std::vector m_arcs; diff --git a/libs/kimath/src/geometry/direction_45.cpp b/libs/kimath/src/geometry/direction_45.cpp new file mode 100644 index 0000000000..abcc56b421 --- /dev/null +++ b/libs/kimath/src/geometry/direction_45.cpp @@ -0,0 +1,102 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +const SHAPE_LINE_CHAIN DIRECTION_45::BuildInitialTrace( const VECTOR2I& aP0, const VECTOR2I& aP1, + bool aStartDiagonal, int aMaxRadius ) const +{ + bool start_diagonal; + + if( m_dir == UNDEFINED ) + start_diagonal = aStartDiagonal; + else + start_diagonal = IsDiagonal(); + + int w = abs( aP1.x - aP0.x ); + int h = abs( aP1.y - aP0.y ); + int sw = sign( aP1.x - aP0.x ); + int sh = sign( aP1.y - aP0.y ); + + int radius = std::min( aMaxRadius, std::min( w, h ) ); + bool use_rounded = aMaxRadius > 0; + int dist90 = use_rounded ? KiROUND( ( M_SQRT2 - 1.0 ) * radius ) : 0; + int dist45 = use_rounded ? KiROUND( radius * ( 1.0 - M_SQRT1_2 ) ) : 0; + + VECTOR2I mp0, mp1, arc_offset_90, arc_offset_45; + + // we are more horizontal than vertical? +// if( m_90deg ) +// { +// if( m_dir == N || m_dir == S ) +// +// } + + if( w > h ) + { + mp0 = VECTOR2I( ( w - h - dist90 ) * sw, 0 ); // direction: E + mp1 = VECTOR2I( ( h - dist45 ) * sw, ( h - dist45 ) * sh ); // direction: NE + arc_offset_90 = VECTOR2I( 0, radius * sh ); + arc_offset_45 = VECTOR2I( sw * radius * M_SQRT1_2, -sh * radius * M_SQRT1_2 ); + } + else + { + mp0 = VECTOR2I( 0, sh * ( h - w - dist90 ) ); // direction: N + mp1 = VECTOR2I( sw * ( w - dist45 ), sh * ( w - dist45 ) ); // direction: NE + arc_offset_90 = VECTOR2I( radius * sw, 0 ); + arc_offset_45 = VECTOR2I( -sw * radius * M_SQRT1_2, sh * radius * M_SQRT1_2 ); + } + + SHAPE_LINE_CHAIN pl; + VECTOR2I arc_center; + + pl.Append( aP0 ); + VECTOR2I next_point; + + if( start_diagonal ) + { + next_point = aP0 + mp1; + arc_center = aP0 + mp1 + arc_offset_45; + } + else + { + next_point = aP0 + mp0; + arc_center = aP0 + mp0 + arc_offset_90; + } + + if( use_rounded ) + { + int sa = start_diagonal ? -sw * sh : sw * sh; + + if( h > w ) + sa = -sa; + + SHAPE_ARC new_arc( arc_center, next_point, sa * 45.0 ); + pl.Append( new_arc ); + } + else + { + pl.Append( next_point ); + } + + pl.Append( aP1 ); + + pl.Simplify(); + return pl; +} diff --git a/libs/kimath/src/geometry/shape_arc.cpp b/libs/kimath/src/geometry/shape_arc.cpp index af6fd1f67a..4ba8fdbe6a 100644 --- a/libs/kimath/src/geometry/shape_arc.cpp +++ b/libs/kimath/src/geometry/shape_arc.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 CERN + * Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors. * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or @@ -22,17 +23,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include // for assert -#include // for cos, sin, M_PI, atan2, ceil -#include // for swap +#include +#include #include #include #include // for SEG #include #include +#include // for cos, sin, M_PI, atan2, ceil #include // for BOX2I #include // for VECTOR2I, VECTOR2D, VECTOR2 +#include // for swap bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance ) const @@ -150,16 +152,28 @@ const VECTOR2I SHAPE_ARC::GetP1() const auto ca = m_centralAngle * M_PI / 180.0; VECTOR2I p1; - p1.x = (int) ( m_pc.x + rvec.x * cos( ca ) - rvec.y * sin( ca ) ); - p1.y = (int) ( m_pc.y + rvec.x * sin( ca ) + rvec.y * cos( ca ) ); + p1.x = KiROUND( m_pc.x + rvec.x * cos( ca ) - rvec.y * sin( ca ) ); + p1.y = KiROUND( m_pc.y + rvec.x * sin( ca ) + rvec.y * cos( ca ) ); return p1; } -const BOX2I SHAPE_ARC::BBox( int aClearance ) const +const VECTOR2I SHAPE_ARC::GetArcMid() const +{ + VECTOR2D rvec = m_p0 - m_pc; + auto ca = m_centralAngle / 2.0 * M_PI / 180.0; + VECTOR2I p1; + + p1.x = KiROUND( m_pc.x + rvec.x * cos( ca ) - rvec.y * sin( ca ) ); + p1.y = KiROUND( m_pc.y + rvec.x * sin( ca ) + rvec.y * cos( ca ) ); + + return p1; +} + + +void SHAPE_ARC::update_bbox() { - BOX2I bbox; std::vector points; // Put start and end points in the point list points.push_back( m_p0 ); @@ -197,7 +211,13 @@ const BOX2I SHAPE_ARC::BBox( int aClearance ) const points.push_back( quad_pt ); } - bbox.Compute( points ); + m_bbox.Compute( points ); +} + + +const BOX2I SHAPE_ARC::BBox( int aClearance ) const +{ + BOX2I bbox( m_bbox ); if( aClearance != 0 ) bbox.Inflate( aClearance ); @@ -208,8 +228,15 @@ const BOX2I SHAPE_ARC::BBox( int aClearance ) const bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance ) const { - assert( false ); - return false; + int minDist = aClearance + m_width / 2; + auto bbox = BBox( minDist ); + + if( !bbox.Contains( aP ) ) + return false; + + auto dist = ( aP - GetCenter() ).SquaredEuclideanNorm(); + + return dist <= ( GetRadius() + minDist ) && dist >= ( GetRadius() - minDist ); } diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index 059fe0d0a0..cb79fa4d45 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -192,7 +192,11 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& { m_points.erase( m_points.begin() + aStartIndex + 1, m_points.begin() + aEndIndex + 1 ); m_points[aStartIndex] = aP; + + m_shapes.erase( m_shapes.begin() + aStartIndex + 1, m_shapes.begin() + aEndIndex + 1 ); } + + assert( m_shapes.size() == m_points.size() ); } @@ -204,47 +208,26 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE if( aStartIndex < 0 ) aStartIndex += PointCount(); - aEndIndex = std::min( aEndIndex, PointCount() - 1 ); - ssize_t arc_index = -1; + Remove( aStartIndex, aEndIndex ); - // N.B. This works because convertArc changes m_shapes on the first run - for( int ind = aStartIndex; ind <= aEndIndex; ind++ ) - { - if( m_shapes[ind] != SHAPE_IS_PT ) - { - if( arc_index == -1 ) - arc_index = m_shapes[ind]; + // The total new arcs index is added to the new arc indices + size_t prev_arc_count = m_arcs.size(); + auto new_shapes = aLine.m_shapes; - convertArc( ind ); - } - } + for( auto& shape : new_shapes ) + shape += prev_arc_count; - for( auto remaining_it = m_shapes.erase( m_shapes.begin() + aStartIndex, - m_shapes.begin() + aEndIndex + 1 ); - remaining_it != m_shapes.end(); remaining_it++ ) - { - if( *remaining_it != SHAPE_IS_PT ) - *remaining_it += aLine.m_arcs.size(); - } - - m_shapes.insert( m_shapes.begin() + aStartIndex, aLine.m_shapes.begin(), aLine.m_shapes.end() ); - - for( auto new_it = m_shapes.begin() + aStartIndex; - new_it != m_shapes.begin() + aStartIndex + aLine.m_shapes.size() + 1; new_it++ ) - { - if( *new_it != SHAPE_IS_PT ) - *new_it += arc_index; - } - - m_arcs.insert( m_arcs.begin() + arc_index, aLine.m_arcs.begin(), aLine.m_arcs.end() ); - - m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 ); + m_shapes.insert( m_shapes.begin() + aStartIndex, new_shapes.begin(), new_shapes.end() ); m_points.insert( m_points.begin() + aStartIndex, aLine.m_points.begin(), aLine.m_points.end() ); + m_arcs.insert( m_arcs.end(), aLine.m_arcs.begin(), aLine.m_arcs.end() ); + + assert( m_shapes.size() == m_points.size() ); } void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex ) { + assert( m_shapes.size() == m_points.size() ); if( aEndIndex < 0 ) aEndIndex += PointCount(); @@ -255,23 +238,21 @@ void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex ) return; aEndIndex = std::min( aEndIndex, PointCount() ); - std::vector extra_arcs; - ssize_t last_arc = -1; + std::set extra_arcs; - for( ssize_t i = aStartIndex; i < aEndIndex; i++) + // Remove any overlapping arcs in the point range + for( int i = aStartIndex; i < aEndIndex; i++ ) { - if( m_shapes[i] != SHAPE_IS_PT && m_shapes[i] != last_arc ) - extra_arcs.emplace_back( m_shapes[i] ); + if( m_shapes[i] != SHAPE_IS_PT ) + extra_arcs.insert( m_shapes[i] ); } - // Reverse the sort order to ensure we maintain valid indices - std::sort( extra_arcs.begin(), extra_arcs.end(), std::greater() ); - for( auto arc : extra_arcs ) convertArc( arc ); m_shapes.erase( m_shapes.begin() + aStartIndex, m_shapes.begin() + aEndIndex + 1 ); m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 ); + assert( m_shapes.size() == m_points.size() ); } @@ -358,7 +339,7 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) if( aStartIndex < 0 ) aStartIndex += PointCount(); - for( int i = aStartIndex; i <= aEndIndex; i++ ) + for( int i = aStartIndex; i <= aEndIndex && static_cast( i ) < m_points.size(); i++ ) rv.Append( m_points[i] ); return rv; @@ -367,6 +348,8 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine ) { + assert( m_shapes.size() == m_points.size() ); + if( aOtherLine.PointCount() == 0 ) return; @@ -378,26 +361,41 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine ) m_bbox.Merge( p ); } + size_t num_arcs = m_arcs.size(); + m_arcs.insert( m_arcs.end(), aOtherLine.m_arcs.begin(), aOtherLine.m_arcs.end() ); + for( int i = 1; i < aOtherLine.PointCount(); i++ ) { const VECTOR2I p = aOtherLine.CPoint( i ); m_points.push_back( p ); - m_shapes.push_back( ssize_t( SHAPE_IS_PT ) ); + + ssize_t arcIndex = aOtherLine.ArcIndex( i ); + + if( arcIndex != ssize_t( SHAPE_IS_PT ) ) + m_shapes.push_back( num_arcs + arcIndex ); + else + m_shapes.push_back( ssize_t( SHAPE_IS_PT ) ); + m_bbox.Merge( p ); } + + assert( m_shapes.size() == m_points.size() ); } void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc ) { auto& chain = aArc.ConvertToPolyline(); - m_arcs.push_back( aArc ); for( auto& pt : chain.CPoints() ) { m_points.push_back( pt ); m_shapes.push_back( m_arcs.size() ); } + + m_arcs.push_back( aArc ); + + assert( m_shapes.size() == m_points.size() ); } @@ -408,6 +406,8 @@ void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP ) m_points.insert( m_points.begin() + aVertex, aP ); m_shapes.insert( m_shapes.begin() + aVertex, ssize_t( SHAPE_IS_PT ) ); + + assert( m_shapes.size() == m_points.size() ); } @@ -437,6 +437,7 @@ void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc ) /// Step 3: Add the vector of indices to the shape vector std::vector new_points( chain.PointCount(), arc_pos ); m_shapes.insert( m_shapes.begin() + aVertex, new_points.begin(), new_points.end() ); + assert( m_shapes.size() == m_points.size() ); } @@ -707,6 +708,7 @@ const OPT SHAPE_LINE_CHAIN::SelfIntersecting() c SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() { std::vector pts_unique; + std::vector shapes_unique; if( PointCount() < 2 ) { @@ -728,20 +730,22 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() { int j = i + 1; - while( j < np && CPoint( i ) == CPoint( j ) ) + while( j < np && m_points[i] == m_points[j] && m_shapes[i] == m_shapes[j] ) j++; pts_unique.push_back( CPoint( i ) ); + shapes_unique.push_back( m_shapes[i] ); + i = j; } m_points.clear(); + m_shapes.clear(); np = pts_unique.size(); i = 0; // stage 1: eliminate collinear segments - // TODO(sh): handle arcs Maybe segment-wise? while( i < np - 2 ) { const VECTOR2I p0 = pts_unique[i]; @@ -754,7 +758,7 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() n++; m_points.push_back( p0 ); - m_shapes.push_back( ssize_t( SHAPE_IS_PT ) ); + m_shapes.push_back( shapes_unique[i] ); if( n > i ) i = n; @@ -762,7 +766,7 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() if( n == np ) { m_points.push_back( pts_unique[n - 1] ); - m_shapes.push_back( ssize_t( SHAPE_IS_PT ) ); + m_shapes.push_back( shapes_unique[n - 1] ); return *this; } @@ -772,11 +776,13 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() if( np > 1 ) { m_points.push_back( pts_unique[np - 2] ); - m_shapes.push_back( ssize_t( SHAPE_IS_PT ) ); + m_shapes.push_back( shapes_unique[np - 2] ); } m_points.push_back( pts_unique[np - 1] ); - m_shapes.push_back( ssize_t( SHAPE_IS_PT ) ); + m_shapes.push_back( shapes_unique[np - 1] ); + + assert( m_points.size() == m_shapes.size() ); return *this; } @@ -929,7 +935,7 @@ bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream ) aStream >> p0.y; aStream >> angle; - m_arcs.emplace_back( pc, p0, angle ); + m_arcs.emplace_back( pc, p0, angle ); } return true; diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index f341955663..e2093a7ad2 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -212,6 +212,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a case PCB_LINE_T: // a segment not on copper layers case PCB_TEXT_T: // a text on a layer case PCB_TRACE_T: // a track segment (segment on a copper layer) + case PCB_ARC_T: // an arced track segment (segment on a copper layer) case PCB_VIA_T: // a via (like track segment on a copper layer) case PCB_DIMENSION_T: // a dimension (graphic item) case PCB_TARGET_T: // a target (graphic item) diff --git a/pcbnew/board_connected_item.h b/pcbnew/board_connected_item.h index d7edaf303a..89120af0f3 100644 --- a/pcbnew/board_connected_item.h +++ b/pcbnew/board_connected_item.h @@ -58,6 +58,7 @@ public: { case PCB_PAD_T: case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: case PCB_ZONE_AREA_T: return true; diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 7c059e6e2a..aaa17705cf 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 5d4c145952..5755709cd7 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -203,6 +203,7 @@ void BOARD::Move( const wxPoint& aMoveVector ) // overload PCB_TARGET_T, PCB_VIA_T, PCB_TRACE_T, + PCB_ARC_T, // PCB_PAD_T, Can't be at board level // PCB_MODULE_TEXT_T, Can't be at board level PCB_MODULE_T, @@ -549,6 +550,7 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode ) case PCB_TRACE_T: case PCB_VIA_T: + case PCB_ARC_T: // N.B. This inserts a small memory leak as we lose the if( !IsCopperLayer( aBoardItem->GetLayer() ) ) @@ -647,6 +649,7 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem ) break; case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: m_tracks.erase( std::remove_if( m_tracks.begin(), m_tracks.end(), [aBoardItem]( BOARD_ITEM* aItem ) { return aItem == aBoardItem; } ) ); @@ -954,6 +957,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s // consuming) search is made, but this case is statistically rare. case PCB_VIA_T: case PCB_TRACE_T: + case PCB_ARC_T: result = IterateForward( m_Track, inspector, testData, p ); // skip over any types handled in the above call. @@ -963,6 +967,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s { case PCB_VIA_T: case PCB_TRACE_T: + case PCB_ARC_T: continue; default: @@ -981,6 +986,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s break; case PCB_TRACE_T: + case PCB_ARC_T: result = IterateForward( m_tracks, inspector, testData, p ); ++p; break; @@ -1415,7 +1421,7 @@ std::tuple BOARD::GetTrackLength( const TRACK& aTrack ) con double length = 0.0; double package_length = 0.0; - constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, PCB_PAD_T, EOT }; + constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT }; auto connectivity = GetBoard()->GetConnectivity(); for( auto item : connectivity->GetConnectedItems( diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index b309306d93..ca85caac68 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -58,6 +58,9 @@ class SHAPE_POLY_SET; class CONNECTIVITY_DATA; class COMPONENT; +// Forward declare endpoint from class_track.h +enum ENDPOINT_T : int; + /** * Enum LAYER_T * gives the allowed types of layers, same as Specctra DSN spec. diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 529fdb8c74..c94a33f7fb 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -1449,6 +1449,7 @@ double MODULE::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const case PCB_TEXT_T: case PCB_MODULE_TEXT_T: case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: addRect( holes, item->GetBoundingBox() ); break; diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index cc3c794317..57bf181788 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -977,16 +977,28 @@ bool TRACK::HitTest( const wxPoint& aPosition, int aAccuracy ) const bool ARC::HitTest( const wxPoint& aPosition, int aAccuracy ) const { int max_dist = aAccuracy + ( m_Width / 2 ); + wxPoint center = GetPosition(); + wxPoint relpos = aPosition - center; + double dist = EuclideanNorm( relpos ); + double radius = GetRadius(); - auto rel_start = EuclideanNorm( aPosition - m_Start ); - auto rel_mid = EuclideanNorm( aPosition - m_Mid ); - auto rel_end = EuclideanNorm( aPosition - m_End ); + if( std::abs( dist - radius ) > max_dist ) + return false; - if( rel_start <= max_dist || rel_mid <= max_dist || rel_end <= max_dist ) - return true; + double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg + double arc_hittest = ArcTangente( relpos.y, relpos.x ); - //TODO: Calculate along arc - return false; + // Calculate relative angle between the starting point of the arc, and the test point + arc_hittest -= arc_angle_start; + + // Normalise arc_hittest between 0 ... 360 deg + NORMALIZE_ANGLE_POS( arc_hittest ); + double arc_angle = GetAngle(); + + if( arc_angle < 0 ) + return arc_hittest >= 3600 + arc_angle; + + return arc_hittest <= GetAngle(); } @@ -1090,6 +1102,50 @@ void VIA::SwapData( BOARD_ITEM* aImage ) std::swap( *((VIA*) this), *((VIA*) aImage) ); } + +const wxPoint ARC::GetPosition() const +{ + auto center = GetArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ) ); + return wxPoint( center.x, center.y ); +} + +double ARC::GetRadius() const +{ + auto center = GetArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ) ); + return GetLineLength( wxPoint( center ), m_Start ); +} + +double ARC::GetAngle() const +{ + wxPoint center = GetPosition(); + wxPoint p0 = m_Start - center; + wxPoint p1 = m_Mid - center; + wxPoint p2 = m_End - center; + double angle1 = ArcTangente( p1.y, p1.x ) - ArcTangente( p0.y, p0.x ); + double angle2 = ArcTangente( p2.y, p2.x ) - ArcTangente( p1.y, p1.x ); + + return NormalizeAngle180( angle1 ) + NormalizeAngle180( angle2 ); +} + +double ARC::GetArcAngleStart() const +{ + wxPoint center = GetPosition(); + + double angleStart = ArcTangente( m_Start.y - center.y, + m_Start.x - center.x ); + return NormalizeAnglePos( angleStart ); +} + +double ARC::GetArcAngleEnd() const +{ + wxPoint center = GetPosition(); + + double angleEnd = ArcTangente( m_End.y - center.y, + m_End.x - center.x ); + return NormalizeAnglePos( angleEnd ); +} + + #if defined(DEBUG) wxString TRACK::ShowState( int stateBits ) diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index fe0704b281..d608c3113a 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,6 +38,7 @@ #include #include +#include #include @@ -50,14 +51,15 @@ class SHAPE_POLY_SET; // Flag used in locate routines (from which endpoint work) -enum ENDPOINT_T { +enum ENDPOINT_T : int +{ ENDPOINT_START = 0, ENDPOINT_END = 1 }; // Via types // Note that this enum must be synchronized to GAL_LAYER_ID -enum class VIATYPE +enum class VIATYPE : int { THROUGH = 3, /* Always a through hole via */ BLIND_BURIED = 2, /* this via can be on internal layers */ @@ -262,6 +264,14 @@ class ARC : public TRACK public: ARC( BOARD_ITEM* aParent ) : TRACK( aParent, PCB_ARC_T ){}; + ARC( BOARD_ITEM* aParent, const SHAPE_ARC* aArc ) : + TRACK( aParent, PCB_ARC_T ) + { + m_Start = wxPoint( aArc->GetP0() ); + m_End = wxPoint( aArc->GetP1() ); + m_Mid = wxPoint( aArc->GetArcMid() ); + } + static inline bool ClassOf( const EDA_ITEM *aItem ) { return aItem && PCB_ARC_T == aItem->Type(); @@ -281,6 +291,19 @@ public: void SetMid( const wxPoint& aMid ) { m_Mid = aMid; } const wxPoint& GetMid() const { return m_Mid; } + void SetPosition( const wxPoint& aPos ) override + { + printf("Setting the arc position\n"); + m_Start = aPos; + } + + virtual const wxPoint GetPosition() const override; + + double GetRadius() const; + double GetAngle() const; + double GetArcAngleStart() const; + double GetArcAngleEnd() const; + virtual bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override; virtual bool HitTest( const EDA_RECT& aRect, bool aContained = true, int aAccuracy = 0 ) const override; @@ -294,7 +317,7 @@ public: /** * Function GetLength - * returns the length of the track using the hypotenuse calculation. + * returns the length of the arc track using a series of segment approximations. * @return double - the length of the track */ virtual double GetLength() const override diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp index 6bb8ce107d..448d4e156c 100644 --- a/pcbnew/collectors.cpp +++ b/pcbnew/collectors.cpp @@ -51,6 +51,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = { PCB_TARGET_T, // in m_Drawings PCB_VIA_T, // in m_Tracks PCB_TRACE_T, // in m_Tracks + PCB_ARC_T, // in m_Tracks PCB_PAD_T, // in modules PCB_MODULE_TEXT_T, // in modules PCB_MODULE_T, // in m_Modules @@ -66,6 +67,7 @@ const KICAD_T GENERAL_COLLECTOR::BoardLevelItems[] = { PCB_DIMENSION_T, PCB_TARGET_T, PCB_VIA_T, + PCB_ARC_T, PCB_TRACE_T, PCB_MODULE_T, PCB_ZONE_AREA_T, @@ -81,6 +83,7 @@ const KICAD_T GENERAL_COLLECTOR::AllButZones[] = { PCB_TARGET_T, PCB_VIA_T, PCB_TRACE_T, + PCB_ARC_T, PCB_PAD_T, PCB_MODULE_TEXT_T, PCB_MODULE_T, @@ -106,6 +109,7 @@ const KICAD_T GENERAL_COLLECTOR::PadsOrTracks[] = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, + PCB_ARC_T, EOT }; @@ -131,6 +135,7 @@ const KICAD_T GENERAL_COLLECTOR::ModuleItems[] = { const KICAD_T GENERAL_COLLECTOR::Tracks[] = { PCB_TRACE_T, + PCB_ARC_T, PCB_VIA_T, EOT }; @@ -139,6 +144,7 @@ const KICAD_T GENERAL_COLLECTOR::Tracks[] = { const KICAD_T GENERAL_COLLECTOR::LockableItems[] = { PCB_MODULE_T, PCB_TRACE_T, + PCB_ARC_T, PCB_VIA_T, EOT }; @@ -184,6 +190,7 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData ) break; case PCB_TRACE_T: + case PCB_ARC_T: breakhere++; break; @@ -263,6 +270,7 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData ) break; case PCB_TRACE_T: + case PCB_ARC_T: if( m_Guide->IgnoreTracks() ) goto exit; break; diff --git a/pcbnew/connectivity/connectivity_algo.cpp b/pcbnew/connectivity/connectivity_algo.cpp index 989df45585..b957b1b316 100644 --- a/pcbnew/connectivity/connectivity_algo.cpp +++ b/pcbnew/connectivity/connectivity_algo.cpp @@ -60,6 +60,7 @@ bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem ) break; case PCB_TRACE_T: + case PCB_ARC_T: m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); m_itemMap.erase( static_cast( aItem ) ); m_itemList.SetDirty( true ); @@ -153,6 +154,16 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem ) break; } + case PCB_ARC_T: + { + if( m_itemMap.find( static_cast( aItem ) ) != m_itemMap.end() ) + return false; + + add( m_itemList, static_cast( aItem ) ); + + break; + } + case PCB_VIA_T: if( m_itemMap.find( static_cast( aItem ) ) != m_itemMap.end() ) return false; @@ -280,8 +291,10 @@ void CN_CONNECTIVITY_ALGO::searchConnections() const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode ) { - constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_AREA_T, PCB_MODULE_T, EOT }; - constexpr KICAD_T no_zones[] = { PCB_TRACE_T, PCB_PAD_T, PCB_VIA_T, PCB_MODULE_T, EOT }; + constexpr KICAD_T types[] = + { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_AREA_T, PCB_MODULE_T, EOT }; + constexpr KICAD_T no_zones[] = + { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_MODULE_T, EOT }; if( aMode == CSM_PROPAGATE ) return SearchClusters( aMode, no_zones, -1 ); @@ -423,6 +436,7 @@ void CN_CONNECTIVITY_ALGO::Build( const std::vector& aItems ) switch( item->Type() ) { case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: case PCB_PAD_T: Add( item ); diff --git a/pcbnew/connectivity/connectivity_data.cpp b/pcbnew/connectivity/connectivity_data.cpp index 11e6002526..3005923ba3 100644 --- a/pcbnew/connectivity/connectivity_data.cpp +++ b/pcbnew/connectivity/connectivity_data.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2017 CERN - * Copyright (C) 2018-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors. * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or @@ -248,8 +248,8 @@ void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector& if( std::none_of( aItems.begin(), aItems.end(), []( const BOARD_ITEM* aItem ) { return( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_PAD_T || - aItem->Type() == PCB_ZONE_AREA_T || aItem->Type() == PCB_MODULE_T || - aItem->Type() == PCB_VIA_T ); } ) ) + aItem->Type() == PCB_ARC_T || aItem->Type() == PCB_ZONE_AREA_T || + aItem->Type() == PCB_MODULE_T || aItem->Type() == PCB_VIA_T ); } ) ) { return ; } @@ -448,7 +448,10 @@ const { for( auto connected : citem->ConnectedItems() ) { - if( connected->Valid() && ( connected->Parent()->Type() == PCB_TRACE_T || connected->Parent()->Type() == PCB_VIA_T ) ) + if( connected->Valid() && + ( connected->Parent()->Type() == PCB_TRACE_T || + connected->Parent()->Type() == PCB_VIA_T || + connected->Parent()->Type() == PCB_ARC_T ) ) tracks.insert( static_cast ( connected->Parent() ) ); } } diff --git a/pcbnew/connectivity/connectivity_items.cpp b/pcbnew/connectivity/connectivity_items.cpp index e96be8dd5f..f45aad97be 100644 --- a/pcbnew/connectivity/connectivity_items.cpp +++ b/pcbnew/connectivity/connectivity_items.cpp @@ -36,7 +36,8 @@ int CN_ITEM::AnchorCount() const case PCB_PAD_T: return 5; // center, north, south, east and west case PCB_TRACE_T: - return 2; // stard and end + case PCB_ARC_T: + return 2; // start and end default: return 1; } @@ -125,6 +126,7 @@ const VECTOR2I CN_ITEM::GetAnchor( int n ) const break; } case PCB_TRACE_T: + case PCB_ARC_T: if( n == 0 ) return static_cast( m_parent )->GetStart(); else @@ -236,17 +238,29 @@ CN_ITEM* CN_LIST::Add( D_PAD* pad ) return item; } - CN_ITEM* CN_LIST::Add( TRACK* track ) - { - auto item = new CN_ITEM( track, true ); - m_items.push_back( item ); - item->AddAnchor( track->GetStart() ); - item->AddAnchor( track->GetEnd() ); - item->SetLayer( track->GetLayer() ); - addItemtoTree( item ); - SetDirty(); - return item; - } +CN_ITEM* CN_LIST::Add( TRACK* track ) +{ + auto item = new CN_ITEM( track, true ); + m_items.push_back( item ); + item->AddAnchor( track->GetStart() ); + item->AddAnchor( track->GetEnd() ); + item->SetLayer( track->GetLayer() ); + addItemtoTree( item ); + SetDirty(); + return item; +} + +CN_ITEM* CN_LIST::Add( ARC* aArc ) +{ + auto item = new CN_ITEM( aArc, true ); + m_items.push_back( item ); + item->AddAnchor( aArc->GetStart() ); + item->AddAnchor( aArc->GetEnd() ); + item->SetLayer( aArc->GetLayer() ); + addItemtoTree( item ); + SetDirty(); + return item; +} CN_ITEM* CN_LIST::Add( VIA* via ) { diff --git a/pcbnew/connectivity/connectivity_items.h b/pcbnew/connectivity/connectivity_items.h index a59a3c010f..bd3b5f62cd 100644 --- a/pcbnew/connectivity/connectivity_items.h +++ b/pcbnew/connectivity/connectivity_items.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -476,6 +477,8 @@ public: CN_ITEM* Add( TRACK* track ); + CN_ITEM* Add( ARC* track ); + CN_ITEM* Add( VIA* via ); const std::vector Add( ZONE_CONTAINER* zone ); diff --git a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp index d0cf967247..20b4116864 100644 --- a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp +++ b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/pcbnew/dialogs/dialog_select_net_from_list.cpp b/pcbnew/dialogs/dialog_select_net_from_list.cpp index ba6a24e2a3..a922117108 100644 --- a/pcbnew/dialogs/dialog_select_net_from_list.cpp +++ b/pcbnew/dialogs/dialog_select_net_from_list.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/pcbnew/dialogs/dialog_track_via_properties.cpp b/pcbnew/dialogs/dialog_track_via_properties.cpp index da5fb0c00a..e38f17782c 100644 --- a/pcbnew/dialogs/dialog_track_via_properties.cpp +++ b/pcbnew/dialogs/dialog_track_via_properties.cpp @@ -93,6 +93,7 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_FRAME* aParen switch( item->Type() ) { case PCB_TRACE_T: + case PCB_ARC_T: { const TRACK* t = static_cast( item ); @@ -410,6 +411,7 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow() switch( item->Type() ) { case PCB_TRACE_T: + case PCB_ARC_T: { wxASSERT( m_tracks ); TRACK* t = static_cast( item ); diff --git a/pcbnew/exporters/gendrill_file_writer_base.cpp b/pcbnew/exporters/gendrill_file_writer_base.cpp index 8225ed7385..c7ccb66326 100644 --- a/pcbnew/exporters/gendrill_file_writer_base.cpp +++ b/pcbnew/exporters/gendrill_file_writer_base.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include diff --git a/pcbnew/exporters/gendrill_gerber_writer.cpp b/pcbnew/exporters/gendrill_gerber_writer.cpp index 85ae95b6e5..3e66f025ed 100644 --- a/pcbnew/exporters/gendrill_gerber_writer.cpp +++ b/pcbnew/exporters/gendrill_gerber_writer.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include diff --git a/pcbnew/exporters/gerber_jobfile_writer.cpp b/pcbnew/exporters/gerber_jobfile_writer.cpp index cdcdac860d..3c97153d1a 100644 --- a/pcbnew/exporters/gerber_jobfile_writer.cpp +++ b/pcbnew/exporters/gerber_jobfile_writer.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Jean_Pierre Charras - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,6 +39,7 @@ #include #include +#include #include #include diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 882ad7abe7..d7763c2ef6 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -442,6 +442,7 @@ void PCB_IO::Format( BOARD_ITEM* aItem, int aNestLevel ) const break; case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: format( static_cast( aItem ), aNestLevel ); break; diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 3c80a2e7ec..b6030cf650 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 CERN. - * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 9db48e8d4d..b6b430c2d2 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -348,6 +348,10 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer ) draw( static_cast( item ), aLayer ); break; + case PCB_ARC_T: + draw( static_cast( item ), aLayer ); + break; + case PCB_VIA_T: draw( static_cast( item ), aLayer ); break; @@ -475,6 +479,45 @@ void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer ) } +void PCB_PAINTER::draw( const ARC* aArc, int aLayer ) +{ + VECTOR2D center( aArc->GetCenter() ); + int width = aArc->GetWidth(); + + if( IsCopperLayer( aLayer ) ) + { + // Draw a regular track + const COLOR4D& color = m_pcbSettings.GetColor( aArc, aLayer ); + bool outline_mode = m_pcbSettings.m_sketchMode[LAYER_TRACKS]; + m_gal->SetStrokeColor( color ); + m_gal->SetFillColor( color ); + m_gal->SetIsStroke( outline_mode ); + m_gal->SetIsFill( not outline_mode ); + m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); + + auto radius = aArc->GetRadius(); + auto start_angle = DECIDEG2RAD( aArc->GetArcAngleStart() ); + auto angle = DECIDEG2RAD( aArc->GetAngle() ); + + m_gal->DrawArcSegment( center, radius, start_angle, start_angle + angle, width ); + + // Clearance lines + constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_EXISTING | PCB_RENDER_SETTINGS::CL_TRACKS; + + if( ( m_pcbSettings.m_clearance & clearanceFlags ) == clearanceFlags ) + { + m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); + m_gal->SetIsFill( false ); + m_gal->SetIsStroke( true ); + m_gal->SetStrokeColor( color ); + + m_gal->DrawArcSegment( center, radius, start_angle, start_angle + angle, + width + aArc->GetClearance() * 2 ); + } + } +} + + void PCB_PAINTER::draw( const VIA* aVia, int aLayer ) { VECTOR2D center( aVia->GetStart() ); diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h index 067acccb30..e873232f14 100644 --- a/pcbnew/pcb_painter.h +++ b/pcbnew/pcb_painter.h @@ -37,6 +37,7 @@ class COLORS_DESIGN_SETTINGS; class PCB_DISPLAY_OPTIONS; class BOARD_ITEM; +class ARC; class BOARD; class VIA; class TRACK; @@ -247,6 +248,7 @@ protected: // Drawing functions for various types of PCB-specific items void draw( const TRACK* aTrack, int aLayer ); + void draw( const ARC* aArc, int aLayer ); void draw( const VIA* aVia, int aLayer ); void draw( const D_PAD* aPad, int aLayer ); void draw( const DRAWSEGMENT* aSegment, int aLayer ); diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 8a4ed45c25..8e8ddf76a3 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -587,7 +587,7 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() break; case T_arc: - m_board->Add( parseARC(), ADD_INSERT ); + m_board->Add( parseARC(), ADD_MODE::INSERT ); break; case T_via: @@ -3406,7 +3406,7 @@ ARC* PCB_PARSER::parseARC() break; case T_tstamp: - arc->SetTimeStamp( parseHex() ); + const_cast( arc->m_Uuid ) = KIID( CurStr() ); break; case T_status: diff --git a/pcbnew/router/CMakeLists.txt b/pcbnew/router/CMakeLists.txt index 078c4f7a13..37d4132503 100644 --- a/pcbnew/router/CMakeLists.txt +++ b/pcbnew/router/CMakeLists.txt @@ -13,6 +13,7 @@ set( PCBNEW_PNS_SRCS time_limit.cpp pns_kicad_iface.cpp pns_algo_base.cpp + pns_arc.cpp pns_diff_pair.cpp pns_diff_pair_placer.cpp pns_dp_meander_placer.cpp diff --git a/pcbnew/router/pns_arc.cpp b/pcbnew/router/pns_arc.cpp new file mode 100644 index 0000000000..0b1dcb58b4 --- /dev/null +++ b/pcbnew/router/pns_arc.cpp @@ -0,0 +1,53 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2019 CERN + * Author: Seth Hillbrand + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_arc.h" +#include "pns_node.h" +#include "pns_router.h" +#include "pns_utils.h" +#include "pns_via.h" + +#include + +namespace PNS { + + +const SHAPE_LINE_CHAIN ARC::Hull( int aClearance, int aWalkaroundThickness ) const +{ + return ArcHull( m_arc, aClearance, aWalkaroundThickness ); +} + + +ARC* ARC::Clone() const +{ + ARC* a = new ARC( m_arc, m_net ); + + return a; +} + + +OPT_BOX2I ARC::ChangedArea( const ARC* aOther ) const +{ + BOX2I tmp = Shape()->BBox(); + tmp.Merge( aOther->Shape()->BBox() ); + return tmp; +} + +} diff --git a/pcbnew/router/pns_arc.h b/pcbnew/router/pns_arc.h new file mode 100644 index 0000000000..27b7690cc2 --- /dev/null +++ b/pcbnew/router/pns_arc.h @@ -0,0 +1,118 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2019 CERN + * Author: Seth Hillbrand + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_ARC_H +#define __PNS_ARC_H + +#include + +#include +#include +#include + +#include "pns_line.h" +#include "pns_linked_item.h" + +namespace PNS { + +class NODE; + +class ARC : public LINKED_ITEM +{ +public: + ARC() : LINKED_ITEM( ARC_T ) + {} + + ARC( const SHAPE_ARC& aArc, int aNet ) : LINKED_ITEM( ARC_T ), m_arc( aArc ) + { + m_net = aNet; + } + + ARC( const ARC& aParentArc, const SHAPE_ARC& aArc ) + : LINKED_ITEM( ARC_T ), + m_arc( aArc ) + { + m_net = aParentArc.Net(); + m_layers = aParentArc.Layers(); + m_marker = aParentArc.Marker(); + m_rank = aParentArc.Rank(); + } + + ARC( const LINE& aParentLine, const SHAPE_ARC& aArc ) : + LINKED_ITEM( ARC_T ), + m_arc( aArc.GetCenter(), aArc.GetP0(), aArc.GetCentralAngle(), aParentLine.Width() ) + { + m_net = aParentLine.Net(); + m_layers = aParentLine.Layers(); + m_marker = aParentLine.Marker(); + m_rank = aParentLine.Rank(); + } + + static inline bool ClassOf( const ITEM* aItem ) + { + return aItem && ARC_T == aItem->Kind(); + } + + ARC* Clone() const override; + + const SHAPE* Shape() const override + { + return static_cast( &m_arc ); + } + + void SetWidth( int aWidth ) override + { + m_arc.SetWidth(aWidth); + } + + int Width() const override + { + return m_arc.GetWidth(); + } + + const SHAPE_LINE_CHAIN CLine() const + { + return SHAPE_LINE_CHAIN( m_arc ); + } + + const SHAPE_LINE_CHAIN Hull( int aClearance, int aWalkaroundThickness ) const override; + + virtual VECTOR2I Anchor( int n ) const override + { + if( n == 0 ) + return m_arc.GetP0(); + else + return m_arc.GetP1(); + } + + virtual int AnchorCount() const override + { + return 2; + } + + OPT_BOX2I ChangedArea( const ARC* aOther ) const; + +private: + SHAPE_ARC m_arc; +}; + +} + +#endif diff --git a/pcbnew/router/pns_diff_pair.cpp b/pcbnew/router/pns_diff_pair.cpp index 31fa9b88f3..46f5004112 100644 --- a/pcbnew/router/pns_diff_pair.cpp +++ b/pcbnew/router/pns_diff_pair.cpp @@ -111,17 +111,15 @@ bool DP_PRIMITIVE_PAIR::Directional() const } -DIRECTION_45 DP_PRIMITIVE_PAIR::anchorDirection( ITEM* aItem, const VECTOR2I& aP ) const +DIRECTION_45 DP_PRIMITIVE_PAIR::anchorDirection( const ITEM* aItem, const VECTOR2I& aP ) const { - if( !aItem->OfKind ( ITEM::SEGMENT_T ) ) + if( !aItem->OfKind ( ITEM::SEGMENT_T | ITEM::ARC_T ) ) return DIRECTION_45(); - SEGMENT* s = static_cast( aItem ); - - if( s->Seg().A == aP ) - return DIRECTION_45( s->Seg().A - s->Seg().B ); + if( aItem->Anchor( 0 ) == aP ) + return DIRECTION_45( aItem->Anchor( 0 ) - aItem->Anchor( 1 ) ); else - return DIRECTION_45( s->Seg().B - s->Seg().A ); + return DIRECTION_45( aItem->Anchor( 1 ) - aItem->Anchor( 0 ) ); } void DP_PRIMITIVE_PAIR::CursorOrientation( const VECTOR2I& aCursorPos, VECTOR2I& aMidpoint, VECTOR2I& aDirection ) const diff --git a/pcbnew/router/pns_diff_pair.h b/pcbnew/router/pns_diff_pair.h index ccd2cec766..f4f3f06b20 100644 --- a/pcbnew/router/pns_diff_pair.h +++ b/pcbnew/router/pns_diff_pair.h @@ -170,7 +170,7 @@ public: } private: - DIRECTION_45 anchorDirection( ITEM* aItem, const VECTOR2I& aP ) const; + DIRECTION_45 anchorDirection( const ITEM* aItem, const VECTOR2I& aP ) const; ITEM* m_primP; ITEM* m_primN; diff --git a/pcbnew/router/pns_diff_pair_placer.cpp b/pcbnew/router/pns_diff_pair_placer.cpp index ebdf861365..7d9b7850f2 100644 --- a/pcbnew/router/pns_diff_pair_placer.cpp +++ b/pcbnew/router/pns_diff_pair_placer.cpp @@ -411,16 +411,17 @@ OPT_VECTOR2I DIFF_PAIR_PLACER::getDanglingAnchor( NODE* aNode, ITEM* aItem ) case ITEM::SOLID_T: return aItem->Anchor( 0 ); + case ITEM::ARC_T: case ITEM::SEGMENT_T: { - SEGMENT* s =static_cast( aItem ); + SEGMENT* s = static_cast( aItem ); - JOINT* jA = aNode->FindJoint( s->Seg().A, s ); - JOINT* jB = aNode->FindJoint( s->Seg().B, s ); + JOINT* jA = aNode->FindJoint( aItem->Anchor( 0 ), aItem ); + JOINT* jB = aNode->FindJoint( aItem->Anchor( 1 ), aItem ); - if( jA->LinkCount() == 1 ) + if( jA && jA->LinkCount() == 1 ) return s->Seg().A; - else if( jB->LinkCount() == 1 ) + else if( jB && jB->LinkCount() == 1 ) return s->Seg().B; else return OPT_VECTOR2I(); diff --git a/pcbnew/router/pns_dragger.cpp b/pcbnew/router/pns_dragger.cpp index b8d4c586ef..299c163d2c 100644 --- a/pcbnew/router/pns_dragger.cpp +++ b/pcbnew/router/pns_dragger.cpp @@ -2,7 +2,7 @@ * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2014 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -19,6 +19,8 @@ * with this program. If not, see . */ +#include "pns_arc.h" + #include "pns_dragger.h" #include "pns_shove.h" #include "pns_router.h" @@ -69,6 +71,7 @@ bool DRAGGER::startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg ) } else if( distB <= w2 ) { + //todo (snh) Adjust segment for arcs m_draggedSegmentIndex++; m_mode = DM_CORNER; } @@ -89,6 +92,17 @@ bool DRAGGER::startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg ) } + +bool DRAGGER::startDragArc( const VECTOR2D& aP, ARC* aArc ) +{ + m_draggedLine = m_world->AssembleLine( aArc, &m_draggedSegmentIndex ); + m_shove->SetInitialLine( m_draggedLine ); + m_mode = DM_ARC; + + return true; +} + + bool DRAGGER::startDragVia( VIA* aVia ) { m_initialVia = aVia->MakeHandle(); @@ -110,10 +124,10 @@ const ITEM_SET DRAGGER::findViaFanoutByHandle ( NODE *aNode, const VIA_HANDLE& h for( ITEM* item : jt->LinkList() ) { - if( item->OfKind( ITEM::SEGMENT_T ) ) + if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) ) { int segIndex; - SEGMENT* seg = ( SEGMENT*) item; + LINKED_ITEM* seg = ( LINKED_ITEM*) item; LINE l = aNode->AssembleLine( seg, &segIndex ); if( segIndex != 0 ) @@ -151,6 +165,9 @@ bool DRAGGER::Start( const VECTOR2I& aP, ITEM* aStartItem ) case ITEM::VIA_T: return startDragVia( static_cast( aStartItem ) ); + case ITEM::ARC_T: + return startDragArc( aP, static_cast( aStartItem ) ); + default: return false; } @@ -179,15 +196,16 @@ bool DRAGGER::dragMarkObstacles( const VECTOR2I& aP ) case DM_SEGMENT: case DM_CORNER: { - int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0; + //TODO: Make threshhold configurable + int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0; LINE origLine( m_draggedLine ); LINE dragged( m_draggedLine ); - dragged.ClearSegmentLinks(); + dragged.SetSnapThreshhold( thresh ); if( m_mode == DM_SEGMENT ) - dragged.DragSegment( aP, m_draggedSegmentIndex, thresh ); + dragged.DragSegment( aP, m_draggedSegmentIndex ); else - dragged.DragCorner( aP, m_draggedSegmentIndex, thresh, m_freeAngleMode ); + dragged.DragCorner( aP, m_draggedSegmentIndex, m_freeAngleMode ); m_lastNode->Remove( origLine ); m_lastNode->Add( dragged ); @@ -233,7 +251,7 @@ void DRAGGER::dumbDragVia( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2 LINE origLine( *l ); LINE draggedLine( *l ); - draggedLine.DragCorner( aP, origLine.CLine().Find( aHandle.pos ), 0, m_freeAngleMode ); + draggedLine.DragCorner( aP, origLine.CLine().Find( aHandle.pos ), m_freeAngleMode ); draggedLine.ClearSegmentLinks(); m_draggedItems.Add( draggedLine ); @@ -270,13 +288,15 @@ bool DRAGGER::dragShove( const VECTOR2I& aP ) case DM_SEGMENT: case DM_CORNER: { - int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0; + //TODO: Make threshhold configurable + int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 2 : 0; LINE dragged( m_draggedLine ); + dragged.SetSnapThreshhold( thresh ); if( m_mode == DM_SEGMENT ) - dragged.DragSegment( aP, m_draggedSegmentIndex, thresh ); + dragged.DragSegment( aP, m_draggedSegmentIndex ); else - dragged.DragCorner( aP, m_draggedSegmentIndex, thresh ); + dragged.DragCorner( aP, m_draggedSegmentIndex ); SHOVE::SHOVE_STATUS st = m_shove->ShoveLines( dragged ); diff --git a/pcbnew/router/pns_dragger.h b/pcbnew/router/pns_dragger.h index 4459fb2288..1d71dcfa2c 100644 --- a/pcbnew/router/pns_dragger.h +++ b/pcbnew/router/pns_dragger.h @@ -109,6 +109,7 @@ private: bool dragMarkObstacles( const VECTOR2I& aP ); bool dragShove(const VECTOR2I& aP ); bool startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg ); + bool startDragArc( const VECTOR2D& aP, ARC* aArc ); bool startDragVia( VIA* aVia ); void dumbDragVia( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP ); @@ -126,7 +127,11 @@ private: bool m_dragStatus; PNS_MODE m_currentMode; ITEM_SET m_origViaConnections; + + ///< Contains the list of items that are currently modified by the dragger ITEM_SET m_draggedItems; + + ///< If true, moves the connection lines without maintaining 45° corners bool m_freeAngleMode; }; diff --git a/pcbnew/router/pns_index.cpp b/pcbnew/router/pns_index.cpp index a047c441f1..97885b0c35 100644 --- a/pcbnew/router/pns_index.cpp +++ b/pcbnew/router/pns_index.cpp @@ -60,6 +60,7 @@ INDEX::ITEM_SHAPE_INDEX* INDEX::getSubindex( const ITEM* aItem ) } break; + case ITEM::ARC_T: case ITEM::SEGMENT_T: case ITEM::LINE_T: idx_n = SI_Traces + 2 * l.Start() + SI_SegStraight; diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp index 42230207a2..19f5bbb7dc 100644 --- a/pcbnew/router/pns_item.cpp +++ b/pcbnew/router/pns_item.cpp @@ -72,6 +72,7 @@ std::string ITEM::KindStr() const { switch( m_kind ) { + case ARC_T: return "arc"; case LINE_T: return "line"; case SEGMENT_T: return "segment"; case VIA_T: return "via"; diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index f48425e701..dd879e4288 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -62,8 +62,9 @@ public: LINE_T = 2, JOINT_T = 4, SEGMENT_T = 8, - VIA_T = 16, - DIFF_PAIR_T = 32, + ARC_T = 16, + VIA_T = 32, + DIFF_PAIR_T = 64, ANY_T = 0xff }; diff --git a/pcbnew/router/pns_joint.h b/pcbnew/router/pns_joint.h index 0ebdf43a59..f0156dd687 100644 --- a/pcbnew/router/pns_joint.h +++ b/pcbnew/router/pns_joint.h @@ -100,11 +100,11 @@ public: /// segments of the same net, on the same layer. bool IsLineCorner() const { - if( m_linkedItems.Size() != 2 || m_linkedItems.Count( SEGMENT_T ) != 2 ) + if( m_linkedItems.Size() != 2 || m_linkedItems.Count( SEGMENT_T | ARC_T ) != 2 ) return false; - SEGMENT* seg1 = static_cast( m_linkedItems[0] ); - SEGMENT* seg2 = static_cast( m_linkedItems[1] ); + auto seg1 = static_cast( m_linkedItems[0] ); + auto seg2 = static_cast( m_linkedItems[1] ); // joints between segments of different widths are not considered trivial. return seg1->Width() == seg2->Width(); @@ -114,6 +114,7 @@ public: { int vias = m_linkedItems.Count( VIA_T ); int segs = m_linkedItems.Count( SEGMENT_T ); + segs += m_linkedItems.Count( ARC_T ); return ( m_linkedItems.Size() == 3 && vias == 1 && segs == 2 ); } @@ -156,12 +157,12 @@ public: ///> For trivial joints, returns the segment adjacent to (aCurrent). For non-trival ones, returns ///> NULL, indicating the end of line. - SEGMENT* NextSegment( SEGMENT* aCurrent ) const + LINKED_ITEM* NextSegment( ITEM* aCurrent ) const { if( !IsLineCorner() ) return NULL; - return static_cast( m_linkedItems[m_linkedItems[0] == aCurrent ? 1 : 0] ); + return static_cast( m_linkedItems[m_linkedItems[0] == aCurrent ? 1 : 0] ); } VIA* Via() diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index a7d86fcef0..aaadc6f319 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -2,7 +2,7 @@ * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2016 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2020 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -52,6 +52,8 @@ #include "tools/pcb_tool_base.h" #include "pns_kicad_iface.h" + +#include "pns_arc.h" #include "pns_routing_settings.h" #include "pns_item.h" #include "pns_solid.h" @@ -815,6 +817,24 @@ std::unique_ptr PNS_KICAD_IFACE::syncTrack( TRACK* aTrack ) } +std::unique_ptr PNS_KICAD_IFACE::syncArc( ARC* aArc ) +{ + std::unique_ptr< PNS::ARC > arc( + new PNS::ARC( SHAPE_ARC( aArc->GetCenter(), aArc->GetStart(), + aArc->GetAngle(), aArc->GetWidth() ), + aArc->GetNetCode() ) + ); + + arc->SetLayers( LAYER_RANGE( aArc->GetLayer() ) ); + arc->SetParent( aArc ); + + if( aArc->IsLocked() ) + arc->Mark( PNS::MK_LOCKED ); + + return arc; +} + + std::unique_ptr PNS_KICAD_IFACE::syncVia( VIA* aVia ) { PCB_LAYER_ID top, bottom; @@ -1180,6 +1200,11 @@ void PNS_KICAD_IFACE::SyncWorld( PNS::NODE *aWorld ) if( auto segment = syncTrack( t ) ) aWorld->Add( std::move( segment ) ); } + else if( type == PCB_ARC_T ) + { + if( auto arc = syncArc( static_cast( t ) ) ) + aWorld->Add( std::move( arc ) ); + } else if( type == PCB_VIA_T ) { if( auto via = syncVia( static_cast( t ) ) ) @@ -1290,6 +1315,17 @@ void PNS_KICAD_IFACE::AddItem( PNS::ITEM* aItem ) switch( aItem->Kind() ) { + case PNS::ITEM::ARC_T: + { + auto arc = static_cast( aItem ); + ARC* new_arc = new ARC( m_board, static_cast( arc->Shape() ) ); + new_arc->SetWidth( arc->Width() ); + new_arc->SetLayer( ToLAYER_ID( arc->Layers().Start() ) ); + new_arc->SetNetCode( std::max( 0, arc->Net() ) ); + newBI = new_arc; + break; + } + case PNS::ITEM::SEGMENT_T: { PNS::SEGMENT* seg = static_cast( aItem ); diff --git a/pcbnew/router/pns_kicad_iface.h b/pcbnew/router/pns_kicad_iface.h index 01d2aef339..61c7ec6162 100644 --- a/pcbnew/router/pns_kicad_iface.h +++ b/pcbnew/router/pns_kicad_iface.h @@ -71,6 +71,7 @@ private: std::unique_ptr syncPad( D_PAD* aPad ); std::unique_ptr syncTrack( TRACK* aTrack ); + std::unique_ptr syncArc( ARC* aArc ); std::unique_ptr syncVia( VIA* aVia ); bool syncTextItem( PNS::NODE* aWorld, EDA_TEXT* aText, PCB_LAYER_ID aLayer ); bool syncGraphicalItem( PNS::NODE* aWorld, DRAWSEGMENT* aItem ); diff --git a/pcbnew/router/pns_line.cpp b/pcbnew/router/pns_line.cpp index 4cc00f129a..cdf2968948 100644 --- a/pcbnew/router/pns_line.cpp +++ b/pcbnew/router/pns_line.cpp @@ -2,7 +2,7 @@ * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2017 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2020 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -34,10 +34,11 @@ namespace PNS { -LINE::LINE( const LINE& aOther ) : - ITEM( aOther ), - m_line( aOther.m_line ), - m_width( aOther.m_width ) +LINE::LINE( const LINE& aOther ) + : ITEM( aOther ), + m_line( aOther.m_line ), + m_width( aOther.m_width ), + m_snapThreshhold( aOther.m_snapThreshhold ) { m_net = aOther.m_net; m_movable = aOther.m_movable; @@ -68,6 +69,7 @@ LINE& LINE::operator=( const LINE& aOther ) m_marker = aOther.m_marker; m_rank = aOther.m_rank; m_owner = aOther.m_owner; + m_snapThreshhold = aOther.m_snapThreshhold; copyLinks( &aOther ); @@ -87,7 +89,7 @@ void LINE::Mark( int aMarker ) { m_marker = aMarker; - for( SEGMENT* s : m_segmentRefs ) + for( auto s : m_segmentRefs ) s->Mark( aMarker ); } @@ -95,7 +97,7 @@ void LINE::Mark( int aMarker ) void LINE::Unmark( int aMarker ) { - for( SEGMENT* s : m_segmentRefs ) + for( auto s : m_segmentRefs ) s->Unmark( aMarker ); m_marker = 0; @@ -106,7 +108,7 @@ int LINE::Marker() const { int marker = m_marker; - for( SEGMENT* s : m_segmentRefs ) + for( auto s : m_segmentRefs ) { marker |= s->Marker(); } @@ -306,6 +308,9 @@ bool LINE::Is45Degree() const { const SEG& s = m_line.CSegment( i ); + if( m_line.isArc( i ) ) + continue; + if( s.Length() < 10 ) continue; @@ -443,12 +448,11 @@ SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECT return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() ); } - -void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ) +void LINE::dragCorner45( const VECTOR2I& aP, int aIndex ) { SHAPE_LINE_CHAIN path; - VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex, aSnappingThreshold ); + VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex ); if( aIndex == 0 ) path = dragCornerInternal( m_line.Reverse(), snapped ).Reverse(); @@ -458,8 +462,8 @@ void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold { // fixme: awkward behaviour for "outwards" drags path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped ); - SHAPE_LINE_CHAIN path_rev = dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(), - snapped ).Reverse(); + SHAPE_LINE_CHAIN path_rev = + dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(), snapped ).Reverse(); path.Append( path_rev ); } @@ -467,26 +471,25 @@ void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold m_line = path; } - -void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ) +void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex ) { m_line.SetPoint( aIndex, aP ); m_line.Simplify(); } -void LINE::DragCorner( const VECTOR2I& aP, int aIndex, int aSnappingThreshold, bool aFreeAngle ) +void LINE::DragCorner( const VECTOR2I& aP, int aIndex, bool aFreeAngle ) { if( aFreeAngle ) { - dragCornerFree ( aP, aIndex, aSnappingThreshold ); + dragCornerFree( aP, aIndex ); } else { - dragCorner45 ( aP, aIndex, aSnappingThreshold ); + dragCorner45( aP, aIndex ); } } -void LINE::DragSegment( const VECTOR2I& aP, int aIndex, int aSnappingThreshold, bool aFreeAngle ) +void LINE::DragSegment( const VECTOR2I& aP, int aIndex, bool aFreeAngle ) { if( aFreeAngle ) { @@ -494,22 +497,21 @@ void LINE::DragSegment( const VECTOR2I& aP, int aIndex, int aSnappingThreshold, } else { - dragSegment45 ( aP, aIndex, aSnappingThreshold ); + dragSegment45( aP, aIndex ); } } - -VECTOR2I LINE::snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, - int aIndex, int aThreshold ) const +VECTOR2I LINE::snapDraggedCorner( + const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const { int s_start = std::max( aIndex - 2, 0 ); int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 ); - int i, j; - int best_dist = INT_MAX; + int i, j; + int best_dist = INT_MAX; VECTOR2I best_snap = aP; - if( aThreshold <= 0 ) + if( m_snapThreshhold <= 0 ) return aP; for( i = s_start; i <= s_end; i++ ) @@ -520,16 +522,16 @@ VECTOR2I LINE::snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& { const SEG& b = aPath.CSegment( j ); - if( !( DIRECTION_45( a ).IsObtuse(DIRECTION_45( b ) ) ) ) + if( !( DIRECTION_45( a ).IsObtuse( DIRECTION_45( b ) ) ) ) continue; - OPT_VECTOR2I ip = a.IntersectLines(b); + OPT_VECTOR2I ip = a.IntersectLines( b ); if( ip ) { int dist = ( *ip - aP ).EuclideanNorm(); - if( dist < aThreshold && dist < best_dist ) + if( dist < m_snapThreshhold && dist < best_dist ) { best_dist = dist; best_snap = *ip; @@ -541,14 +543,14 @@ VECTOR2I LINE::snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& return best_snap; } -VECTOR2I LINE::snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, - int aIndex, int aThreshold ) const +VECTOR2I LINE::snapToNeighbourSegments( + const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const { - VECTOR2I snap_p[2]; + VECTOR2I snap_p[2]; DIRECTION_45 dragDir( aPath.CSegment( aIndex ) ); - int snap_d[2] = { -1, -1 }; + int snap_d[2] = { -1, -1 }; - if( aThreshold == 0 ) + if( m_snapThreshhold == 0 ) return aP; if( aIndex >= 2 ) @@ -566,17 +568,17 @@ VECTOR2I LINE::snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VEC SEG s = aPath.CSegment( aIndex + 2 ); if( DIRECTION_45( s ) == dragDir ) - snap_d[1] = s.LineDistance(aP); + snap_d[1] = s.LineDistance( aP ); snap_p[1] = s.A; } VECTOR2I best = aP; - int minDist = INT_MAX; + int minDist = INT_MAX; for( int i = 0; i < 2; i++ ) { - if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= aThreshold ) + if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= m_snapThreshhold ) { minDist = snap_d[i]; best = snap_p[i]; @@ -586,16 +588,15 @@ VECTOR2I LINE::snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VEC return best; } - -void LINE::dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ) +void LINE::dragSegment45( const VECTOR2I& aP, int aIndex ) { SHAPE_LINE_CHAIN path( m_line ); - VECTOR2I target( aP ); + VECTOR2I target( aP ); SEG guideA[2], guideB[2]; int index = aIndex; - target = snapToNeighbourSegments( path, aP, aIndex, aSnappingThreshold ); + target = snapToNeighbourSegments( path, aP, aIndex ); if( index == 0 ) { @@ -608,7 +609,7 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold path.Insert( path.PointCount() - 1, path.CPoint( -1 ) ); } - SEG dragged = path.CSegment( index ); + SEG dragged = path.CSegment( index ); DIRECTION_45 drag_dir( dragged ); SEG s_prev = path.CSegment( index - 1 ); @@ -634,25 +635,15 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold s_next = path.CSegment( index + 1 ); dragged = path.CSegment( index ); - const bool lockEndpointA = true; - const bool lockEndpointB = true; - if( aIndex == 0 ) { - if( !lockEndpointA ) - { - guideA[0] = guideA[1] = SEG( dragged.A, - dragged.A + drag_dir.Right().Right().ToVector() ); - } - else - { - guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() ); - guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() ); - } + guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() ); + guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() ); } else { - if( dir_prev.Angle( drag_dir ) & (DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL) ) + if( dir_prev.Angle( drag_dir ) + & ( DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL ) ) { guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() ); guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() ); @@ -663,32 +654,24 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold if( aIndex == m_line.SegmentCount() - 1 ) { - if( !lockEndpointB ) - { - guideB[0] = guideB[1] = SEG( dragged.B, - dragged.B + drag_dir.Right().Right().ToVector() ); - } - else - { - guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() ); - guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() ); - } + guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() ); + guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() ); } else { - if( dir_next.Angle( drag_dir ) & (DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL) ) + if( dir_next.Angle( drag_dir ) + & ( DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL ) ) { guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() ); guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() ); } else - guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() ); - + guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() ); } SEG s_current( target, target + drag_dir.ToVector() ); - int best_len = INT_MAX; + int best_len = INT_MAX; SHAPE_LINE_CHAIN best; for( int i = 0; i < 2; i++ ) @@ -709,19 +692,19 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold OPT_VECTOR2I ip; - if( (ip = s1.Intersect( s_next )) ) + if( ( ip = s1.Intersect( s_next ) ) ) { np.Append( s1.A ); np.Append( *ip ); np.Append( s_next.B ); } - else if( (ip = s3.Intersect( s_prev )) ) + else if( ( ip = s3.Intersect( s_prev ) ) ) { np.Append( s_prev.A ); np.Append( *ip ); np.Append( s3.B ); } - else if( (ip = s1.Intersect( s3 )) ) + else if( ( ip = s1.Intersect( s3 ) ) ) { np.Append( s_prev.A ); np.Append( *ip ); @@ -743,11 +726,6 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold } } - if( !lockEndpointA && aIndex == 0 ) - best.Remove( 0, 0 ); - if( !lockEndpointB && aIndex == m_line.SegmentCount() - 1 ) - best.Remove( -1, -1 ); - if( m_line.PointCount() == 1 ) m_line = best; else if( aIndex == 0 ) @@ -792,7 +770,7 @@ void LINE::SetRank( int aRank ) { m_rank = aRank; - for( SEGMENT* s : m_segmentRefs ) + for( auto s : m_segmentRefs ) s->SetRank( aRank ); } @@ -803,7 +781,7 @@ int LINE::Rank() const int min_rank = INT_MAX; if( IsLinked() ) { - for( SEGMENT *s : m_segmentRefs ) + for( auto s : m_segmentRefs ) { min_rank = std::min( min_rank, s->Rank() ); } @@ -955,7 +933,7 @@ OPT_BOX2I LINE::ChangedArea( const LINE* aOther ) const bool LINE::HasLockedSegments() const { - for( const SEGMENT* seg : m_segmentRefs ) + for( const auto seg : m_segmentRefs ) { if( seg->Marker() & MK_LOCKED ) return true; diff --git a/pcbnew/router/pns_line.h b/pcbnew/router/pns_line.h index 452f12a617..63f7d189f7 100644 --- a/pcbnew/router/pns_line.h +++ b/pcbnew/router/pns_line.h @@ -35,8 +35,8 @@ namespace PNS { +class LINKED_ITEM; class NODE; -class SEGMENT; class VIA; /** @@ -61,7 +61,7 @@ class VIA; class LINE : public ITEM { public: - typedef std::vector SEGMENT_REFS; + typedef std::vector SEGMENT_REFS; /** * Constructor @@ -71,6 +71,7 @@ public: { m_hasVia = false; m_width = 1; // Dummy value + m_snapThreshhold = 0; } LINE( const LINE& aOther ); @@ -80,10 +81,11 @@ public: * Copies properties (net, layers, etc.) from a base line and replaces the shape * by another **/ - LINE( const LINE& aBase, const SHAPE_LINE_CHAIN& aLine ) : - ITEM( aBase ), - m_line( aLine ), - m_width( aBase.m_width ) + LINE( const LINE& aBase, const SHAPE_LINE_CHAIN& aLine ) + : ITEM( aBase ), + m_line( aLine ), + m_width( aBase.m_width ), + m_snapThreshhold( aBase.m_snapThreshhold ) { m_net = aBase.m_net; m_layers = aBase.m_layers; @@ -104,6 +106,7 @@ public: m_net = aVia.Net(); m_layers = aVia.Layers(); m_rank = aVia.Rank(); + m_snapThreshhold = 0; } ~LINE(); @@ -155,6 +158,12 @@ public: return m_line.PointCount(); } + ///> Returns the number of arcs in the line + int ArcCount() const + { + return m_line.ArcCount(); + } + ///> Returns the aIdx-th point of the line const VECTOR2I& CPoint( int aIdx ) const { @@ -190,7 +199,7 @@ public: /* Linking functions */ ///> Adds a reference to a segment registered in a NODE that is a part of this line. - void LinkSegment( SEGMENT* aSeg ) + void LinkSegment( LINKED_ITEM* aSeg ) { m_segmentRefs.push_back( aSeg ); } @@ -213,13 +222,13 @@ public: } ///> Checks if the segment aSeg is a part of the line. - bool ContainsSegment( SEGMENT* aSeg ) const + bool ContainsSegment( LINKED_ITEM* aSeg ) const { return std::find( m_segmentRefs.begin(), m_segmentRefs.end(), aSeg ) != m_segmentRefs.end(); } - SEGMENT* GetLink( int aIndex ) const + LINKED_ITEM* GetLink( int aIndex ) const { return m_segmentRefs[aIndex]; } @@ -275,8 +284,8 @@ public: virtual void Unmark( int aMarker = -1 ) override; virtual int Marker() const override; - void DragSegment( const VECTOR2I& aP, int aIndex, int aSnappingThreshold = 0, bool aFreeAngle = false ); - void DragCorner( const VECTOR2I& aP, int aIndex, int aSnappingThreshold = 0, bool aFreeAngle = false ); + void DragSegment( const VECTOR2I& aP, int aIndex, bool aFreeAngle = false ); + void DragCorner( const VECTOR2I& aP, int aIndex, bool aFreeAngle = false ); void SetRank( int aRank ) override; int Rank() const override; @@ -286,18 +295,27 @@ public: OPT_BOX2I ChangedArea( const LINE* aOther ) const; + void SetSnapThreshhold( int aThreshhold ) + { + m_snapThreshhold = aThreshhold; + } + + int GetSnapThreshhold() const + { + return m_snapThreshhold; + } + private: + void dragSegment45( const VECTOR2I& aP, int aIndex ); + void dragCorner45( const VECTOR2I& aP, int aIndex ); + void dragSegmentFree( const VECTOR2I& aP, int aIndex ); + void dragCornerFree( const VECTOR2I& aP, int aIndex ); - void dragSegment45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ); - void dragCorner45( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ); - void dragSegmentFree( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ); - void dragCornerFree( const VECTOR2I& aP, int aIndex, int aSnappingThreshold ); + VECTOR2I snapToNeighbourSegments( + const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const; - VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, - int aIndex, int aThreshold) const; - - VECTOR2I snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, - int aIndex, int aThreshold ) const; + VECTOR2I snapDraggedCorner( + const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const; ///> Copies m_segmentRefs from the line aParent. void copyLinks( const LINE* aParent ) ; @@ -315,6 +333,9 @@ private: ///> If true, the line ends with a via bool m_hasVia; + ///> Width to smooth out jagged segments + int m_snapThreshhold; + ///> Via at the end point, if m_hasVia == true VIA m_via; }; diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp index cd5f720718..f3fb2d785a 100644 --- a/pcbnew/router/pns_line_placer.cpp +++ b/pcbnew/router/pns_line_placer.cpp @@ -21,14 +21,15 @@ #include -#include "pns_node.h" -#include "pns_line_placer.h" -#include "pns_walkaround.h" -#include "pns_shove.h" -#include "pns_utils.h" -#include "pns_router.h" -#include "pns_topology.h" +#include "pns_arc.h" #include "pns_debug_decorator.h" +#include "pns_line_placer.h" +#include "pns_node.h" +#include "pns_router.h" +#include "pns_shove.h" +#include "pns_topology.h" +#include "pns_utils.h" +#include "pns_walkaround.h" #include @@ -344,13 +345,8 @@ bool LINE_PLACER::mergeHead() return false; } - if( !n_tail ) - tail.Append( head.CSegment( 0 ).A ); - - for( int i = 0; i < n_head - 2; i++ ) - { - tail.Append( head.CSegment( i ).B ); - } + tail.Append( head ); + tail.Remove( -1 ); tail.Simplify(); @@ -359,7 +355,7 @@ bool LINE_PLACER::mergeHead() m_p_start = last.B; m_direction = DIRECTION_45( last ).Right(); - head.Remove( 0, n_head - 2 ); + head.Remove( 0, -1 ); wxLogTrace( "PNS", "Placer: merge %d, new direction: %s", n_head, m_direction.Format().c_str() ); @@ -1064,17 +1060,32 @@ bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinis lastV = std::max( 1, l.SegmentCount() - 1 ); SEGMENT* lastSeg = nullptr; + int lastArc = -1; for( int i = 0; i < lastV; i++ ) { - const SEG& s = pl.CSegment( i ); - lastSeg = new SEGMENT( s, m_currentNet ); - std::unique_ptr< SEGMENT > seg( lastSeg ); - seg->SetWidth( pl.Width() ); - seg->SetLayer( m_currentLayer ); - if( ! m_lastNode->Add( std::move( seg ) ) ) + ssize_t arcIndex = l.ArcIndex( i ); + + if( arcIndex < 0 ) { + const SEG& s = pl.CSegment( i ); + auto seg = std::make_unique( s, m_currentNet ); + seg->SetWidth( pl.Width() ); + seg->SetLayer( m_currentLayer ); + if( !m_lastNode->Add( std::move( seg ) ) ) + lastSeg = nullptr; + } + else + { + if( arcIndex == lastArc ) + continue; + + auto arc = std::make_unique( l.Arc( arcIndex ), m_currentNet ); + arc->SetWidth( pl.Width() ); + arc->SetLayer( m_currentLayer ); + m_lastNode->Add( std::move( arc ) ); lastSeg = nullptr; + lastArc = arcIndex; } } @@ -1115,12 +1126,12 @@ void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest ) if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CPoint( -1 ) ) return; - std::set toErase; + std::set toErase; aNode->Add( aLatest, true ); for( int s = 0; s < aLatest.LinkCount(); s++ ) { - SEGMENT* seg = aLatest.GetLink(s); + auto seg = aLatest.GetLink(s); LINE ourLine = aNode->AssembleLine( seg ); JOINT a, b; std::vector lines; @@ -1143,7 +1154,7 @@ void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest ) if( !( line.ContainsSegment( seg ) ) && line.SegmentCount() ) { - for( SEGMENT *ss : line.LinkedSegments() ) + for( auto ss : line.LinkedSegments() ) toErase.insert( ss ); removedCount++; @@ -1153,7 +1164,7 @@ void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest ) wxLogTrace( "PNS", "total segs removed: %d/%d", removedCount, total ); } - for( SEGMENT *s : toErase ) + for( auto s : toErase ) aNode->Remove( s ); aNode->Remove( aLatest ); @@ -1207,6 +1218,7 @@ void LINE_PLACER::SetOrthoMode( bool aOrthoMode ) bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aInvertPosture ) { SHAPE_LINE_CHAIN l; + int initial_radius = 0; if( m_p_start == aP ) { @@ -1220,10 +1232,14 @@ bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aInver } else { + // Rounded corners don't make sense when routing orthogonally (single track at a time) + if( Settings().GetRounded() && !m_orthoMode ) + initial_radius = Settings().GetMaxRadius(); + if ( aInvertPosture ) - l = m_direction.Right().BuildInitialTrace( m_p_start, aP ); + l = m_direction.Right().BuildInitialTrace( m_p_start, aP, false, initial_radius ); else - l = m_direction.BuildInitialTrace( m_p_start, aP ); + l = m_direction.BuildInitialTrace( m_p_start, aP, false, initial_radius ); } if( l.SegmentCount() > 1 && m_orthoMode ) @@ -1256,7 +1272,7 @@ bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aInver if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) ) { - SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP + force ); + SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP + force, initial_radius ); aHead = LINE( aHead, line ); v.SetPos( v.Pos() + force ); diff --git a/pcbnew/router/pns_linked_item.h b/pcbnew/router/pns_linked_item.h new file mode 100644 index 0000000000..fd8ad8990a --- /dev/null +++ b/pcbnew/router/pns_linked_item.h @@ -0,0 +1,46 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2019 CERN + * Author: Seth Hillbrand + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef PCBNEW_ROUTER_PNS_LINKED_ITEM_H_ +#define PCBNEW_ROUTER_PNS_LINKED_ITEM_H_ + +#include "pns_item.h" + + +namespace PNS +{ +class LINKED_ITEM : public ITEM +{ +public: + + LINKED_ITEM( PnsKind aKind ) : ITEM( aKind ) + {} + + virtual void SetWidth( int aWidth ) + {}; + + virtual int Width() const + { + return 0; + } +}; + +} // namespace PNS +#endif /* PCBNEW_ROUTER_PNS_LINKED_ITEM_H_ */ diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index 512e26afbf..a2daae7bb8 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -1,7 +1,7 @@ /* * KiRouter - a push-and-(sometimes-)shove PCB router * - * Copyright (C) 2013-2014 CERN + * Copyright (C) 2013-2019 CERN * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * @@ -21,12 +21,14 @@ #include #include +#include #include #include #include +#include "pns_arc.h" #include "pns_item.h" #include "pns_line.h" #include "pns_node.h" @@ -564,8 +566,26 @@ void NODE::Add( LINE& aLine, bool aAllowRedundant ) SHAPE_LINE_CHAIN& l = aLine.Line(); + for( size_t i = 0; i < l.ArcCount(); i++ ) + { + auto s = l.Arc( i ); + ARC* rarc; + + if( !aAllowRedundant && ( rarc = findRedundantArc( s.GetP0(), s.GetP1(), aLine.Layers(), aLine.Net() ) ) ) + aLine.LinkSegment( rarc ); + else + { + auto newarc = std::make_unique< ARC >( aLine, s ); + aLine.LinkSegment( newarc.get() ); + Add( std::move( newarc ), true ); + } + } + for( int i = 0; i < l.SegmentCount(); i++ ) { + if( l.isArc( i ) ) + continue; + SEG s = l.CSegment( i ); if( s.A != s.B ) @@ -612,6 +632,20 @@ bool NODE::Add( std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant ) return true; } +void NODE::addArc( ARC* aArc ) +{ + linkJoint( aArc->Anchor( 0 ), aArc->Layers(), aArc->Net(), aArc ); + linkJoint( aArc->Anchor( 1 ), aArc->Layers(), aArc->Net(), aArc ); + + m_index->Add( aArc ); +} + +void NODE::Add( std::unique_ptr< ARC > aArc ) +{ + aArc->SetOwner( this ); + addArc( aArc.release() ); +} + void NODE::Add( std::unique_ptr< ITEM > aItem, bool aAllowRedundant ) { switch( aItem->Kind() ) @@ -620,6 +654,11 @@ void NODE::Add( std::unique_ptr< ITEM > aItem, bool aAllowRedundant ) case ITEM::SEGMENT_T: Add( ItemCast( std::move( aItem ) ), aAllowRedundant ); break; case ITEM::VIA_T: Add( ItemCast( std::move( aItem ) ) ); break; + case ITEM::ARC_T: + //todo(snh): Add redundant search + Add( ItemCast( std::move( aItem ) ) ); + break; + case ITEM::LINE_T: default: assert( false ); @@ -654,6 +693,14 @@ void NODE::removeSegmentIndex( SEGMENT* aSeg ) unlinkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg ); } + +void NODE::removeArcIndex( ARC* aArc ) +{ + unlinkJoint( aArc->Anchor( 0 ), aArc->Layers(), aArc->Net(), aArc ); + unlinkJoint( aArc->Anchor( 1 ), aArc->Layers(), aArc->Net(), aArc ); +} + + void NODE::removeViaIndex( VIA* aVia ) { // We have to split a single joint (associated with a via, binding together multiple layers) @@ -738,10 +785,20 @@ void NODE::Remove( SEGMENT* aSegment ) doRemove( aSegment ); } +void NODE::Remove( ARC* aArc ) +{ + removeArcIndex( aArc ); + doRemove( aArc ); +} + void NODE::Remove( ITEM* aItem ) { switch( aItem->Kind() ) { + case ITEM::ARC_T: + Remove( static_cast( aItem ) ); + break; + case ITEM::SOLID_T: Remove( static_cast( aItem ) ); break; @@ -773,11 +830,14 @@ void NODE::Remove( ITEM* aItem ) void NODE::Remove( LINE& aLine ) { // LINE does not have a seperate remover, as LINEs are never truly a member of the tree - std::vector& segRefs = aLine.LinkedSegments(); + std::vector& segRefs = aLine.LinkedSegments(); - for( SEGMENT* seg : segRefs ) + for( auto li : segRefs ) { - Remove( seg ); + if( li->OfKind( ITEM::SEGMENT_T ) ) + Remove( static_cast( li ) ); + else if( li->OfKind( ITEM::ARC_T ) ) + Remove( static_cast( li ) ); } aLine.SetOwner( nullptr ); @@ -785,18 +845,16 @@ void NODE::Remove( LINE& aLine ) } -void NODE::followLine( SEGMENT* aCurrent, bool aScanDirection, int& aPos, - int aLimit, VECTOR2I* aCorners, SEGMENT** aSegments, bool& aGuardHit, - bool aStopAtLockedJoints ) +void NODE::followLine( LINKED_ITEM* aCurrent, int aScanDirection, int& aPos, int aLimit, VECTOR2I* aCorners, + LINKED_ITEM** aSegments, bool& aGuardHit, bool aStopAtLockedJoints ) { bool prevReversed = false; - const VECTOR2I guard = aScanDirection ? aCurrent->Seg().B : aCurrent->Seg().A; + const VECTOR2I guard = aCurrent->Anchor( aScanDirection ); for( int count = 0 ; ; ++count ) { - const VECTOR2I p = - ( aScanDirection ^ prevReversed ) ? aCurrent->Seg().B : aCurrent->Seg().A; + const VECTOR2I p = aCurrent->Anchor( aScanDirection ^ prevReversed ); const JOINT* jt = FindJoint( p, aCurrent ); assert( jt ); @@ -819,18 +877,17 @@ void NODE::followLine( SEGMENT* aCurrent, bool aScanDirection, int& aPos, aCurrent = jt->NextSegment( aCurrent ); - prevReversed = - ( jt->Pos() == ( aScanDirection ? aCurrent->Seg().B : aCurrent->Seg().A ) ); + prevReversed = ( jt->Pos() == aCurrent->Anchor( aScanDirection ) ); } } -const LINE NODE::AssembleLine( SEGMENT* aSeg, int* aOriginSegmentIndex, bool aStopAtLockedJoints ) +const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex, bool aStopAtLockedJoints ) { const int MaxVerts = 1024 * 16; VECTOR2I corners[MaxVerts + 1]; - SEGMENT* segs[MaxVerts + 1]; + LINKED_ITEM* segs[MaxVerts + 1]; LINE pl; bool guardHit = false; @@ -849,7 +906,7 @@ const LINE NODE::AssembleLine( SEGMENT* aSeg, int* aOriginSegmentIndex, bool aSt int n = 0; - SEGMENT* prev_seg = NULL; + LINKED_ITEM* prev_seg = NULL; bool originSet = false; for( int i = i_start + 1; i < i_end; i++ ) @@ -1301,6 +1358,37 @@ SEGMENT* NODE::findRedundantSegment( SEGMENT* aSeg ) return findRedundantSegment( aSeg->Seg().A, aSeg->Seg().B, aSeg->Layers(), aSeg->Net() ); } +ARC* NODE::findRedundantArc( const VECTOR2I& A, const VECTOR2I& B, const LAYER_RANGE& lr, + int aNet ) +{ + JOINT* jtStart = FindJoint( A, lr.Start(), aNet ); + + if( !jtStart ) + return nullptr; + + for( ITEM* item : jtStart->LinkList() ) + { + if( item->OfKind( ITEM::ARC_T ) ) + { + ARC* seg2 = static_cast( item ); + + const VECTOR2I a2( seg2->Anchor( 0 ) ); + const VECTOR2I b2( seg2->Anchor( 1 ) ); + + if( seg2->Layers().Start() == lr.Start() && + ((A == a2 && B == b2) || (A == b2 && B == a2)) ) + return seg2; + } + } + + return nullptr; +} + +ARC* NODE::findRedundantArc( ARC* aArc ) +{ + return findRedundantArc( aArc->Anchor( 0 ), aArc->Anchor( 1 ), aArc->Layers(), aArc->Net() ); +} + ITEM *NODE::FindItemByParent( const BOARD_CONNECTED_ITEM* aParent ) { diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index 4ee01f94c2..7f5dd3cab3 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -39,6 +39,7 @@ namespace PNS { +class ARC; class SEGMENT; class LINE; class SOLID; @@ -285,6 +286,7 @@ public: bool Add( std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant = false ); void Add( std::unique_ptr< SOLID > aSolid ); void Add( std::unique_ptr< VIA > aVia ); + void Add( std::unique_ptr< ARC > aArc ); void Add( LINE& aLine, bool aAllowRedundant = false ); @@ -297,6 +299,7 @@ public: * * Just as the name says, removes an item from this branch. */ + void Remove( ARC* aArc ); void Remove( SOLID* aSolid ); void Remove( VIA* aVia ); void Remove( SEGMENT* aSegment ); @@ -340,7 +343,7 @@ public: * @param aOriginSegmentIndex index of aSeg in the resulting line * @return the line */ - const LINE AssembleLine( SEGMENT* aSeg, int* aOriginSegmentIndex = NULL, + const LINE AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex = NULL, bool aStopAtLockedJoints = false ); ///> Prints the contents and joints structure @@ -449,11 +452,13 @@ private: void addSolid( SOLID* aSeg ); void addSegment( SEGMENT* aSeg ); void addVia( VIA* aVia ); + void addArc( ARC* aVia ); void removeLine( LINE& aLine ); void removeSolidIndex( SOLID* aSeg ); void removeSegmentIndex( SEGMENT* aSeg ); void removeViaIndex( VIA* aVia ); + void removeArcIndex( ARC* aVia ); void doRemove( ITEM* aItem ); void unlinkParent(); @@ -469,15 +474,13 @@ private: const LAYER_RANGE & lr, int aNet ); SEGMENT* findRedundantSegment( SEGMENT* aSeg ); + ARC* findRedundantArc( const VECTOR2I& A, const VECTOR2I& B, + const LAYER_RANGE & lr, int aNet ); + ARC* findRedundantArc( ARC* aSeg ); + ///> scans the joint map, forming a line starting from segment (current). - void followLine( SEGMENT* aCurrent, - bool aScanDirection, - int& aPos, - int aLimit, - VECTOR2I* aCorners, - SEGMENT** aSegments, - bool& aGuardHit, - bool aStopAtLockedJoints ); + void followLine( LINKED_ITEM* aCurrent, int aScanDirection, int& aPos, int aLimit, VECTOR2I* aCorners, + LINKED_ITEM** aSegments, bool& aGuardHit, bool aStopAtLockedJoints ); ///> hash table with the joints, linking the items. Joints are hashed by ///> their position, layer set and net. diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp index 93fc3caf0f..fc41d48473 100644 --- a/pcbnew/router/pns_optimizer.cpp +++ b/pcbnew/router/pns_optimizer.cpp @@ -24,6 +24,7 @@ #include #include +#include "pns_arc.h" #include "pns_line.h" #include "pns_diff_pair.h" #include "pns_node.h" @@ -190,7 +191,7 @@ void OPTIMIZER::removeCachedSegments( LINE* aLine, int aStartVertex, int aEndVer for( int i = aStartVertex; i < aEndVertex - 1; i++ ) { - SEGMENT* s = segs[i]; + LINKED_ITEM* s = segs[i]; m_cacheTags.erase( s ); m_cache.Remove( s ); } @@ -273,17 +274,19 @@ int LINE_RESTRICTIONS::allowedAngles( NODE* aWorld, const LINE* aLine, const VEC for( const ITEM* item : jt->Links().CItems() ) { - if( item->OfKind( ITEM::VIA_T ) || item->OfKind( ITEM::SOLID_T ) ) + if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) ) return 0xff; - else if( const SEGMENT* seg = dyn_cast( item ) ) + else if( auto segment = dynamic_cast( item ) ) { - SEG s = seg->Seg(); + SEG s( segment->Seg() ); if( s.A != aP ) s.Reverse(); - if( n_dirs < 8 ) - dirs[n_dirs++] = aFirst ? DIRECTION_45( s ) : DIRECTION_45( s ).Opposite(); + dirs[n_dirs] = aFirst ? DIRECTION_45( s ) : DIRECTION_45( s ).Opposite(); } + + if( ++n_dirs >= 8 ) + break; } const int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_STRAIGHT; @@ -452,10 +455,13 @@ bool OPTIMIZER::mergeObtuse( LINE* aLine ) } bool found_anything = false; - int n = 0; - while( n < n_segs - step ) + for( int n = 0; n < n_segs - step; n++ ) { + // Don't try to optimize the arc segments + if( current_path.isArc( n ) || current_path.isArc( n + step ) ) + continue; + const SEG s1 = current_path.CSegment( n ); const SEG s2 = current_path.CSegment( n + step ); SEG s1opt, s2opt; @@ -494,8 +500,6 @@ bool OPTIMIZER::mergeObtuse( LINE* aLine ) } } } - - n++; } if( !found_anything ) @@ -580,7 +584,6 @@ bool OPTIMIZER::Optimize( LINE* aLine, LINE* aResult ) bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step ) { - int n = 0; int n_segs = aCurrentPath.SegmentCount(); int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath ); @@ -595,8 +598,12 @@ bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive ); - while( n < n_segs - step ) + for( int n = 0; n < n_segs - step; n++ ) { + // Do not attempt to merge false segments that are part of an arc + if( aCurrentPath.isArc( n ) || aCurrentPath.isArc( n + step ) ) + continue; + const SEG s1 = aCurrentPath.CSegment( n ); const SEG s2 = aCurrentPath.CSegment( n + step ); @@ -637,8 +644,6 @@ bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step aCurrentPath = *picked; return true; } - - n++; } return false; diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index 3c3f6a1eba..8b6884b983 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -509,6 +509,12 @@ bool ROUTER::IsPlacingVia() const } +void ROUTER::ToggleRounded() +{ + m_settings->SetRounded( !m_settings->GetRounded() ); +} + + void ROUTER::SetOrthoMode( bool aEnable ) { if( !m_placer ) diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h index 0e2bb13823..bce84a6bef 100644 --- a/pcbnew/router/pns_router.h +++ b/pcbnew/router/pns_router.h @@ -53,6 +53,7 @@ class DIFF_PAIR_PLACER; class PLACEMENT_ALGO; class LINE_PLACER; class ITEM; +class ARC; class LINE; class SOLID; class SEGMENT; @@ -76,7 +77,8 @@ enum DRAG_MODE DM_SEGMENT = 0x2, DM_VIA = 0x4, DM_FREE_ANGLE = 0x8, - DM_ANY = 0x7 + DM_ARC = 0x10, + DM_ANY = 0x17 }; /** * ROUTER @@ -158,6 +160,8 @@ public: void ToggleViaPlacement(); void SetOrthoMode( bool aEnable ); + void ToggleRounded(); + int GetCurrentLayer() const; const std::vector GetCurrentNets() const; diff --git a/pcbnew/router/pns_routing_settings.cpp b/pcbnew/router/pns_routing_settings.cpp index 1d7d3359bd..f06768115f 100644 --- a/pcbnew/router/pns_routing_settings.cpp +++ b/pcbnew/router/pns_routing_settings.cpp @@ -50,6 +50,9 @@ ROUTING_SETTINGS::ROUTING_SETTINGS( JSON_SETTINGS* aParent, const std::string& a m_inlineDragEnabled = false; m_snapToTracks = false; m_snapToPads = false; + m_minRadius = 0; + m_maxRadius = 1000000; + m_roundedCorners = false; m_params.emplace_back( new PARAM( "mode", reinterpret_cast( &m_routingMode ), static_cast( RM_Walkaround ) ) ); @@ -83,6 +86,11 @@ ROUTING_SETTINGS::ROUTING_SETTINGS( JSON_SETTINGS* aParent, const std::string& a m_params.emplace_back( new PARAM( "inline_drag", &m_inlineDragEnabled, false ) ); m_params.emplace_back( new PARAM( "snap_to_tracks", &m_snapToTracks, false ) ); m_params.emplace_back( new PARAM( "snap_to_pads", &m_snapToPads, false ) ); + + m_params.emplace_back( new PARAM( "min_radius", &m_minRadius, 0 ) ); + m_params.emplace_back( new PARAM( "max_radius", &m_maxRadius, 1000000 ) ); + m_params.emplace_back( new PARAM( "use_rounded", &m_roundedCorners, false ) ); + } diff --git a/pcbnew/router/pns_routing_settings.h b/pcbnew/router/pns_routing_settings.h index 72ed2af396..6e4c29570f 100644 --- a/pcbnew/router/pns_routing_settings.h +++ b/pcbnew/router/pns_routing_settings.h @@ -141,6 +141,28 @@ public: bool GetSnapToTracks() const { return m_snapToTracks; } bool GetSnapToPads() const { return m_snapToPads; } + bool GetRounded() const { return m_roundedCorners; } + void SetRounded( bool aRound ) { m_roundedCorners = aRound; } + + void SetMinRadius( int aRadius ) + { + m_minRadius = aRadius; + + if( m_maxRadius < m_minRadius ) + m_maxRadius = m_minRadius; + } + + void SetMaxRadius( int aRadius ) + { + m_maxRadius = aRadius; + + if( m_maxRadius < m_minRadius ) + m_minRadius = m_maxRadius; + } + + int GetMinRadius() const { return m_minRadius; } + int GetMaxRadius() const { return m_maxRadius; } + private: bool m_shoveVias; bool m_startDiagonal; @@ -155,6 +177,10 @@ private: bool m_inlineDragEnabled; bool m_snapToTracks; bool m_snapToPads; + bool m_roundedCorners; + + int m_minRadius; + int m_maxRadius; PNS_MODE m_routingMode; PNS_OPTIMIZATION_EFFORT m_optimizerEffort; diff --git a/pcbnew/router/pns_segment.h b/pcbnew/router/pns_segment.h index 36f92e0354..90bfd500f1 100644 --- a/pcbnew/router/pns_segment.h +++ b/pcbnew/router/pns_segment.h @@ -28,28 +28,28 @@ #include #include -#include "pns_item.h" #include "pns_line.h" +#include "pns_linked_item.h" namespace PNS { class NODE; -class SEGMENT : public ITEM +class SEGMENT : public LINKED_ITEM { public: SEGMENT() : - ITEM( SEGMENT_T ) + LINKED_ITEM( SEGMENT_T ) {} SEGMENT( const SEG& aSeg, int aNet ) : - ITEM( SEGMENT_T ), m_seg( aSeg, 0 ) + LINKED_ITEM( SEGMENT_T ), m_seg( aSeg, 0 ) { m_net = aNet; } SEGMENT( const LINE& aParentLine, const SEG& aSeg ) : - ITEM( SEGMENT_T ), + LINKED_ITEM( SEGMENT_T ), m_seg( aSeg, aParentLine.Width() ) { m_net = aParentLine.Net(); @@ -70,22 +70,12 @@ public: return static_cast( &m_seg ); } - void SetLayer( int aLayer ) - { - SetLayers( LAYER_RANGE( aLayer ) ); - } - - int Layer() const override - { - return Layers().Start(); - } - - void SetWidth( int aWidth ) + void SetWidth( int aWidth ) override { m_seg.SetWidth(aWidth); } - int Width() const + int Width() const override { return m_seg.GetWidth(); } diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp index bc6e9acf00..2185d2803e 100644 --- a/pcbnew/router/pns_shove.cpp +++ b/pcbnew/router/pns_shove.cpp @@ -23,6 +23,7 @@ #include #include +#include "pns_arc.h" #include "pns_line.h" #include "pns_node.h" #include "pns_debug_decorator.h" @@ -98,9 +99,9 @@ SHOVE::~SHOVE() } -LINE SHOVE::assembleLine( const SEGMENT* aSeg, int* aIndex ) +LINE SHOVE::assembleLine( const LINKED_ITEM* aSeg, int* aIndex ) { - return m_currentNode->AssembleLine( const_cast( aSeg ), aIndex, true ); + return m_currentNode->AssembleLine( const_cast( aSeg ), aIndex, true ); } // A dumb function that checks if the shoved line is shoved the right way, e.g. @@ -267,7 +268,7 @@ SHOVE::SHOVE_STATUS SHOVE::ProcessSingleLine( LINE& aCurrent, LINE& aObstacle, L bool obstacleIsHead = false; - for( SEGMENT* s : aObstacle.LinkedSegments() ) + for( auto s : aObstacle.LinkedSegments() ) { if( s->Marker() & MK_HEAD ) { @@ -377,6 +378,67 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSegment( LINE& aCurrent, SEGMENT* aObstacl } +/* + * TODO describe.... + */ +SHOVE::SHOVE_STATUS SHOVE::onCollidingArc( LINE& aCurrent, ARC* aObstacleArc ) +{ + int segIndex; + LINE obstacleLine = assembleLine( aObstacleArc, &segIndex ); + LINE shovedLine( obstacleLine ); + ARC tmp( *aObstacleArc ); + + if( obstacleLine.HasLockedSegments() ) + return SH_TRY_WALK; + + SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine ); + + const double extensionWalkThreshold = 1.0; + + double obsLen = obstacleLine.CLine().Length(); + double shovedLen = shovedLine.CLine().Length(); + double extensionFactor = 0.0; + + if( obsLen != 0.0f ) + extensionFactor = shovedLen / obsLen - 1.0; + + if( extensionFactor > extensionWalkThreshold ) + return SH_TRY_WALK; + + assert( obstacleLine.LayersOverlap( &shovedLine ) ); + +#ifdef DEBUG + m_logger.NewGroup( "on-colliding-segment", m_iter ); + m_logger.Log( &tmp, 0, "obstacle-segment" ); + m_logger.Log( &aCurrent, 1, "current-line" ); + m_logger.Log( &obstacleLine, 2, "obstacle-line" ); + m_logger.Log( &shovedLine, 3, "shoved-line" ); +#endif + + if( rv == SH_OK ) + { + if( shovedLine.Marker() & MK_HEAD ) + { + if( m_multiLineMode ) + return SH_INCOMPLETE; + + m_newHead = shovedLine; + } + + int rank = aCurrent.Rank(); + shovedLine.SetRank( rank - 1 ); + + sanityCheck( &obstacleLine, &shovedLine ); + replaceLine( obstacleLine, shovedLine ); + + if( !pushLineStack( shovedLine ) ) + rv = SH_INCOMPLETE; + } + + return rv; +} + + /* * TODO describe.... */ @@ -658,12 +720,13 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in for( ITEM* item : jt->LinkList() ) { - if( SEGMENT* seg = dyn_cast( item ) ) + if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) ) { + LINKED_ITEM* li = static_cast( item ); LINE_PAIR lp; int segIndex; - lp.first = assembleLine( seg, &segIndex ); + lp.first = assembleLine( li, &segIndex ); if( lp.first.HasLockedSegments() ) return SH_TRY_WALK; @@ -835,10 +898,10 @@ SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacle for( ITEM* item : jt->LinkList() ) { - if( item->OfKind( ITEM::SEGMENT_T ) && item->LayersOverlap( &aCurrent ) ) + if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->LayersOverlap( &aCurrent ) ) { - SEGMENT* seg = (SEGMENT*) item; - LINE head = assembleLine( seg ); + LINKED_ITEM* li = static_cast( item ); + LINE head = assembleLine( li ); head.AppendVia( *aObstacleVia ); @@ -903,7 +966,7 @@ SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacle } -void SHOVE::unwindLineStack( SEGMENT* aSeg ) +void SHOVE::unwindLineStack( LINKED_ITEM* aSeg ) { for( std::vector::iterator i = m_lineStack.begin(); i != m_lineStack.end() ; ) { @@ -925,13 +988,13 @@ void SHOVE::unwindLineStack( SEGMENT* aSeg ) void SHOVE::unwindLineStack( ITEM* aItem ) { - if( aItem->OfKind( ITEM::SEGMENT_T ) ) - unwindLineStack( static_cast( aItem )); + if( aItem->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) ) + unwindLineStack( static_cast( aItem ) ); else if( aItem->OfKind( ITEM::LINE_T ) ) { LINE* l = static_cast( aItem ); - for( SEGMENT* seg : l->LinkedSegments() ) + for( auto seg : l->LinkedSegments() ) unwindLineStack( seg ); } } @@ -964,7 +1027,7 @@ void SHOVE::popLineStack( ) { bool found = false; - for( SEGMENT *s : l.LinkedSegments() ) + for( auto s : l.LinkedSegments() ) { if( i->ContainsSegment( s ) ) { @@ -1035,7 +1098,21 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter ) case ITEM::SEGMENT_T: { wxLogTrace( "PNS", "iter %d: reverse-collide-segment ", aIter ); - LINE revLine = assembleLine( (SEGMENT*) ni ); + LINE revLine = assembleLine( static_cast( ni ) ); + + popLineStack(); + st = onCollidingLine( revLine, currentLine ); + if( !pushLineStack( revLine ) ) + return SH_INCOMPLETE; + + break; + } + + case ITEM::ARC_T: + { + //TODO(snh): Handle Arc shove separate from track + wxLogTrace( "PNS", "iter %d: reverse-collide-arc ", aIter ); + LINE revLine = assembleLine( static_cast( ni ) ); popLineStack(); st = onCollidingLine( revLine, currentLine ); @@ -1065,6 +1142,17 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter ) break; + //TODO(snh): Customize Arc collide + case ITEM::ARC_T: + wxLogTrace( "PNS", "iter %d: collide-arc ", aIter ); + + st = onCollidingArc( currentLine, static_cast( ni ) ); + + if( st == SH_TRY_WALK ) + st = onCollidingSolid( currentLine, ni ); + + break; + case ITEM::VIA_T: wxLogTrace( "PNS", "iter %d: shove-via ", aIter ); st = onCollidingVia( ¤tLine, (VIA*) ni ); diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h index 420e9b2f6d..8bb744c64b 100644 --- a/pcbnew/router/pns_shove.h +++ b/pcbnew/router/pns_shove.h @@ -111,6 +111,7 @@ private: SHOVE_STATUS walkaroundLoneVia( LINE& aCurrent, LINE& aObstacle, LINE& aShoved ); bool checkBumpDirection( const LINE& aCurrent, const LINE& aShoved ) const; + SHOVE_STATUS onCollidingArc( LINE& aCurrent, ARC* aObstacleArc ); SHOVE_STATUS onCollidingLine( LINE& aCurrent, LINE& aObstacle ); SHOVE_STATUS onCollidingSegment( LINE& aCurrent, SEGMENT* aObstacleSeg ); SHOVE_STATUS onCollidingSolid( LINE& aCurrent, ITEM* aObstacle ); @@ -120,7 +121,7 @@ private: OPT_BOX2I totalAffectedArea() const; - void unwindLineStack( SEGMENT* aSeg ); + void unwindLineStack( LINKED_ITEM* aSeg ); void unwindLineStack( ITEM* aItem ); void runOptimizer( NODE* aNode ); @@ -128,7 +129,7 @@ private: bool pushLineStack( const LINE& aL, bool aKeepCurrentOnTop = false ); void popLineStack(); - LINE assembleLine( const SEGMENT* aSeg, int* aIndex = NULL ); + LINE assembleLine( const LINKED_ITEM* aSeg, int* aIndex = NULL ); void replaceItems( ITEM* aOld, std::unique_ptr< ITEM > aNew ); void replaceLine( LINE& aOld, LINE& aNew ); diff --git a/pcbnew/router/pns_tool_base.cpp b/pcbnew/router/pns_tool_base.cpp index 5342d870eb..0d1a1b5b20 100644 --- a/pcbnew/router/pns_tool_base.cpp +++ b/pcbnew/router/pns_tool_base.cpp @@ -44,6 +44,7 @@ using namespace std::placeholders; #include #include +#include "pns_arc.h" #include "pns_kicad_iface.h" #include "pns_tool_base.h" #include "pns_segment.h" @@ -67,6 +68,7 @@ TOOL_BASE::TOOL_BASE( const std::string& aToolName ) : m_gridHelper = nullptr; m_iface = nullptr; m_router = nullptr; + m_cancelled = false; m_startItem = nullptr; m_startLayer = 0; @@ -256,7 +258,7 @@ bool TOOL_BASE::checkSnap( ITEM *aItem ) if( aItem ) { - if( aItem->OfKind( ITEM::VIA_T ) || aItem->OfKind( ITEM::SEGMENT_T ) ) + if( aItem->OfKind( ITEM::VIA_T | ITEM::SEGMENT_T | ITEM::ARC_T ) ) return pnss.GetSnapToTracks(); else if( aItem->OfKind( ITEM::SOLID_T ) ) return pnss.GetSnapToPads(); @@ -406,18 +408,23 @@ const VECTOR2I TOOL_BASE::snapToItem( bool aEnabled, ITEM* aItem, VECTOR2I aP) break; case ITEM::SEGMENT_T: + case ITEM::ARC_T: { - SEGMENT* seg = static_cast( aItem ); - const SEG& s = seg->Seg(); - int w = seg->Width(); + LINKED_ITEM* li = static_cast( aItem ); + int w = li->Width(); + auto A = li->Anchor( 0 ); + auto B = li->Anchor( 1 ); - - if( ( aP - s.A ).EuclideanNorm() < w / 2 ) - anchor = s.A; - else if( ( aP - s.B ).EuclideanNorm() < w / 2 ) - anchor = s.B; - else - anchor = m_gridHelper->AlignToSegment( aP, s ); + if( ( aP - A ).EuclideanNorm() < w / 2 ) + anchor = A; + else if( ( aP - B ).EuclideanNorm() < w / 2 ) + anchor = B; + else // TODO(snh): Clean this up + if( aItem->Kind() == ITEM::SEGMENT_T ) + anchor = m_gridHelper->AlignToSegment( aP, static_cast( li )->Seg() ); + else if( aItem->Kind() == ITEM::ARC_T ) + anchor = m_gridHelper->AlignToArc( aP, + *static_cast( static_cast( li )->Shape() ) ); break; } diff --git a/pcbnew/router/pns_topology.cpp b/pcbnew/router/pns_topology.cpp index e671aca79c..5ed5c46276 100644 --- a/pcbnew/router/pns_topology.cpp +++ b/pcbnew/router/pns_topology.cpp @@ -39,7 +39,7 @@ bool TOPOLOGY::SimplifyLine( LINE* aLine ) if( !aLine->IsLinked() || !aLine->SegmentCount() ) return false; - SEGMENT* root = aLine->GetLink(0); + LINKED_ITEM* root = aLine->GetLink( 0 ); LINE l = m_world->AssembleLine( root ); SHAPE_LINE_CHAIN simplified( l.CLine() ); @@ -180,9 +180,10 @@ bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet, std:: { assert( aLine->IsLinked() ); - VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 ); - SEGMENT* last = aLeft ? aLine->LinkedSegments().front() : aLine->LinkedSegments().back(); - JOINT* jt = m_world->FindJoint( anchor, aLine ); + VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 ); + LINKED_ITEM* last = + aLeft ? aLine->LinkedSegments().front() : aLine->LinkedSegments().back(); + JOINT* jt = m_world->FindJoint( anchor, aLine ); assert( jt != NULL ); diff --git a/pcbnew/router/pns_utils.cpp b/pcbnew/router/pns_utils.cpp index 1ee7779cbc..3593e5f5ce 100644 --- a/pcbnew/router/pns_utils.cpp +++ b/pcbnew/router/pns_utils.cpp @@ -24,6 +24,7 @@ #include "pns_via.h" #include "pns_router.h" +#include #include #include @@ -51,6 +52,63 @@ const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize } +const SHAPE_LINE_CHAIN ArcHull( const SHAPE_ARC& aSeg, int aClearance, + int aWalkaroundThickness ) +{ + int d = aSeg.GetWidth() / 2 + aClearance + aWalkaroundThickness / 2 + HULL_MARGIN; + int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d ) / 2; + + auto line = aSeg.ConvertToPolyline(); + + SHAPE_LINE_CHAIN s; + s.SetClosed( true ); + std::vector reverse_line; + + auto seg = line.Segment( 0 ); + VECTOR2I dir = seg.B - seg.A; + VECTOR2I p0 = dir.Perpendicular().Resize( d ); + VECTOR2I ds = dir.Perpendicular().Resize( x ); + VECTOR2I pd = dir.Resize( x ); + VECTOR2I dp = dir.Resize( d ); + + // Append the first curve + s.Append( seg.A + p0 - pd ); + s.Append( seg.A - dp + ds ); + s.Append( seg.A - dp - ds ); + s.Append( seg.A - p0 - pd ); + + for( int i = 1; i < line.SegmentCount(); i++ ) + { + auto old_seg = seg; + auto endpt = ( old_seg.A - old_seg.B ).Resize( seg.Length() ); + old_seg.A = old_seg.B + endpt; + + seg = line.Segment( i ); + auto dir2 = old_seg.A - seg.B; + + p0 = dir2.Perpendicular().Resize( d ); + s.Append( seg.A - p0 ); + reverse_line.push_back( seg.A + p0 ); + } + + pd = dir.Resize( x ); + dp = dir.Resize( d ); + s.Append( seg.B - p0 + pd ); + s.Append( seg.B + dp - ds ); + s.Append( seg.B + dp + ds ); + s.Append( seg.B + p0 + pd ); + + for( int i = reverse_line.size() - 1; i >= 0; i-- ) + s.Append( reverse_line[i] ); + + // make sure the hull outline is always clockwise + if( s.CSegment( 0 ).Side( line.Segment( 0 ).A ) < 0 ) + return s.Reverse(); + else + return s; +} + + const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance, int aWalkaroundThickness ) { diff --git a/pcbnew/router/pns_utils.h b/pcbnew/router/pns_utils.h index 55d7d333f8..c68f75b1a2 100644 --- a/pcbnew/router/pns_utils.h +++ b/pcbnew/router/pns_utils.h @@ -38,6 +38,8 @@ class LINE; /** Various utility functions */ +const SHAPE_LINE_CHAIN ArcHull( const SHAPE_ARC& aSeg, int aClearance, int aWalkaroundThickness ); + const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize, int aClearance, int aChamfer ); diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp index b70e35a862..3b001a4d1c 100644 --- a/pcbnew/router/router_preview_item.cpp +++ b/pcbnew/router/router_preview_item.cpp @@ -30,6 +30,7 @@ #include "router_preview_item.h" +#include "pns_arc.h" #include "pns_line.h" #include "pns_segment.h" #include "pns_via.h" @@ -90,16 +91,18 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem ) { case PNS::ITEM::LINE_T: m_type = PR_SHAPE; - m_width = ( (PNS::LINE*) aItem )->Width(); + m_width = static_cast( aItem )->Width(); + break; + + case PNS::ITEM::ARC_T: + m_type = PR_SHAPE; + m_width = static_cast( aItem )->Width(); break; case PNS::ITEM::SEGMENT_T: - { - PNS::SEGMENT* seg = (PNS::SEGMENT*) aItem; m_type = PR_SHAPE; - m_width = seg->Width(); + m_width = static_cast( aItem )->Width(); break; - } case PNS::ITEM::VIA_T: m_originLayer = m_layer = LAYER_VIAS; diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index dd48306fbc..8091325675 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -136,6 +136,12 @@ static const TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPost _( "Switches posture of the currently routed track." ), change_entry_orient_xpm ); +static const TOOL_ACTION ACT_SwitchRounding( "pcbnew.InteractiveRouter.SwitchRounding", + AS_CONTEXT, + 0, LEGACY_HK_NAME( "Switch Rounding" ), + _( "Switch Rounding" ), + _( "Switches the corner type of the currently routed track." ) ); + #undef _ #define _(s) wxGetTranslation((s)) @@ -787,6 +793,12 @@ void ROUTER_TOOL::performRouting() m_router->Move( m_endSnapPoint, m_endItem ); m_startItem = nullptr; } + else if( evt->IsAction( &ACT_SwitchRounding ) ) + { + m_router->ToggleRounded(); + updateEndItem( *evt ); + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + } else if( evt->IsAction( &ACT_SwitchPosture ) ) { m_router->FlipPosture(); @@ -1078,8 +1090,9 @@ void ROUTER_TOOL::NeighboringSegmentFilter( const VECTOR2I& aPt, GENERAL_COLLECT // First make sure we've got something that *might* match. int vias = aCollector.CountType( PCB_VIA_T ); int traces = aCollector.CountType( PCB_TRACE_T ); + int arcs = aCollector.CountType( PCB_ARC_T ); - if( vias > 1 || traces > 2 || vias + traces < 1 ) + if( arcs > 0 || vias > 1 || traces > 2 || vias + traces < 1 ) return; // Fetch first TRACK (via or trace) as our reference diff --git a/pcbnew/swig/board_connected_item.i b/pcbnew/swig/board_connected_item.i index ec9d5cc132..9a8327823f 100644 --- a/pcbnew/swig/board_connected_item.i +++ b/pcbnew/swig/board_connected_item.i @@ -47,6 +47,7 @@ break; case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: obj = SWIG_NewPointerObj( SWIG_as_voidptr(aItem), SWIGTYPE_p_TRACK, diff --git a/pcbnew/tools/global_edit_tool.cpp b/pcbnew/tools/global_edit_tool.cpp index 018668ca6c..90e02b7410 100644 --- a/pcbnew/tools/global_edit_tool.cpp +++ b/pcbnew/tools/global_edit_tool.cpp @@ -22,6 +22,7 @@ */ #include +#include #include #include #include diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp index a924b3425c..147314eda9 100644 --- a/pcbnew/tools/grid_helper.cpp +++ b/pcbnew/tools/grid_helper.cpp @@ -33,6 +33,7 @@ using namespace std::placeholders; #include #include #include +#include #include #include @@ -177,6 +178,39 @@ VECTOR2I GRID_HELPER::AlignToSegment( const VECTOR2I& aPoint, const SEG& aSeg ) } +VECTOR2I GRID_HELPER::AlignToArc( const VECTOR2I& aPoint, const SHAPE_ARC& aArc ) +{ + OPT_VECTOR2I pts[6]; + + if( !m_enableSnap ) + return aPoint; + + const VECTOR2D gridOffset( GetOrigin() ); + const VECTOR2D gridSize( GetGrid() ); + + VECTOR2I nearest( KiROUND( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x, + KiROUND( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y ); + + auto line = aArc.ConvertToPolyline(); + int min_d = std::numeric_limits::max(); + + for( auto pt : line.CPoints() ) + { + int d = ( pt - aPoint ).EuclideanNorm(); + + if( d < min_d ) + { + min_d = d; + nearest = pt; + } + else + break; + } + + return nearest; +} + + VECTOR2I GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, std::vector& aItems ) { clearAnchors(); @@ -460,6 +494,7 @@ void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bo } case PCB_TRACE_T: + case PCB_ARC_T: { if( aFrom || m_frame->Settings().m_MagneticTracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS ) { diff --git a/pcbnew/tools/grid_helper.h b/pcbnew/tools/grid_helper.h index afc212bf97..451a519108 100644 --- a/pcbnew/tools/grid_helper.h +++ b/pcbnew/tools/grid_helper.h @@ -32,6 +32,7 @@ #include #include #include +#include class PCB_BASE_FRAME; @@ -60,6 +61,9 @@ public: VECTOR2I AlignToSegment ( const VECTOR2I& aPoint, const SEG& aSeg ); VECTOR2I BestDragOrigin( const VECTOR2I& aMousePos, std::vector& aItem ); + + VECTOR2I AlignToArc ( const VECTOR2I& aPoint, const SHAPE_ARC& aSeg ); + VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDraggedItem ); VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aLayers, const std::vector& aSkip = {} ); diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index a4079ce7fa..319593524d 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 242279f7c1..263cc1fa4d 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -822,7 +822,7 @@ int SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent ) void SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem, KICAD_T aStopCondition ) { - constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_VIA_T, PCB_PAD_T, EOT }; + constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT }; auto connectivity = board()->GetConnectivity(); auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types ); @@ -1210,6 +1210,7 @@ static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard break; } case PCB_TRACE_T: + case PCB_ARC_T: { include = aFilterOptions.includeTracks; break; @@ -1525,6 +1526,7 @@ bool SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOn break; case PCB_TRACE_T: + case PCB_ARC_T: { if( !board()->IsElementVisible( LAYER_TRACKS ) ) return false; @@ -2038,6 +2040,7 @@ void SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector, switch( item->Type() ) { case PCB_TRACE_T: + case PCB_ARC_T: case PCB_PAD_T: case PCB_LINE_T: case PCB_VIA_T: diff --git a/pcbnew/undo_redo.cpp b/pcbnew/undo_redo.cpp index 9e241c652b..4cbdf37c62 100644 --- a/pcbnew/undo_redo.cpp +++ b/pcbnew/undo_redo.cpp @@ -445,6 +445,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool // Fall through case PCB_ZONE_AREA_T: case PCB_TRACE_T: + case PCB_ARC_T: case PCB_VIA_T: reBuild_ratsnest = true; break; diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 2606efad74..bf123eff2f 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/qa/common/CMakeLists.txt b/qa/common/CMakeLists.txt index 1c3f4457da..c4cffa9422 100644 --- a/qa/common/CMakeLists.txt +++ b/qa/common/CMakeLists.txt @@ -60,6 +60,7 @@ set( common_srcs geometry/test_shape_poly_set_collision.cpp geometry/test_shape_poly_set_distance.cpp geometry/test_shape_poly_set_iterator.cpp + geometry/test_shape_line_chain.cpp view/test_zoom_controller.cpp ) diff --git a/qa/common/geometry/test_shape_line_chain.cpp b/qa/common/geometry/test_shape_line_chain.cpp new file mode 100644 index 0000000000..9b425af0aa --- /dev/null +++ b/qa/common/geometry/test_shape_line_chain.cpp @@ -0,0 +1,62 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * 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 + +#include "geom_test_utils.h" + +BOOST_AUTO_TEST_SUITE( ShapeLineChain ) + +BOOST_AUTO_TEST_CASE( ArcToPolyline ) +{ + SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 1000 ), VECTOR2I( 1000, 0 ) } ); + + SHAPE_LINE_CHAIN chain_insert( { + VECTOR2I( 0, 1500 ), + VECTOR2I( 1500, 1500 ), + VECTOR2I( 1500, 0 ), + } ); + + SHAPE_LINE_CHAIN arc_insert1( SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 1800 ) ); + + SHAPE_LINE_CHAIN arc_insert2( SHAPE_ARC( VECTOR2I( 0, 500 ), VECTOR2I( 0, 400 ), 1800 ) ); + + BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() ); + BOOST_CHECK_EQUAL( arc_insert1.CShapes().size(), arc_insert1.CPoints().size() ); + BOOST_CHECK_EQUAL( arc_insert2.CShapes().size(), arc_insert2.CPoints().size() ); + + base_chain.Insert( 0, SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 1800 ) ); + + BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() ); + + base_chain.Replace( 0, 2, chain_insert ); + BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() ); +} + + +BOOST_AUTO_TEST_SUITE_END()