diff --git a/common/pcb.keywords b/common/pcb.keywords index af41b7c704..d18e0c330d 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -134,6 +134,7 @@ loss_tangent max_error material micro +mid min_thickness mirror mod_edge_width diff --git a/include/core/typeinfo.h b/include/core/typeinfo.h index 96a72cb4d1..69f1bb0851 100644 --- a/include/core/typeinfo.h +++ b/include/core/typeinfo.h @@ -95,6 +95,7 @@ enum KICAD_T PCB_MODULE_ZONE_AREA_T, ///< class ZONE_CONTAINER, managed by a footprint PCB_TRACE_T, ///< class TRACK, a track segment (segment on a copper layer) PCB_VIA_T, ///< class VIA, a via (like a track segment on a copper layer) + PCB_ARC_T, ///< class ARC, an arc track segment on a copper layer PCB_MARKER_T, ///< class MARKER_PCB, a marker used to show something PCB_DIMENSION_T, ///< class DIMENSION, a dimension (graphic item) PCB_TARGET_T, ///< class PCB_TARGET, a target (graphic item) diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index da18893f1d..cc3c794317 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -4,7 +4,7 @@ * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2012 Wayne Stambaugh - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-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 @@ -53,7 +53,8 @@ static bool ShowClearance( const PCB_DISPLAY_OPTIONS& aDisplOpts, const TRACK* a { // maybe return true for tracks and vias, not for zone segments return IsCopperLayer( aTrack->GetLayer() ) - && ( aTrack->Type() == PCB_TRACE_T || aTrack->Type() == PCB_VIA_T ) + && ( aTrack->Type() == PCB_TRACE_T || aTrack->Type() == PCB_VIA_T + || aTrack->Type() == PCB_ARC_T ) && ( ( aDisplOpts.m_ShowTrackClearanceMode == PCB_DISPLAY_OPTIONS::SHOW_CLEARANCE_NEW_AND_EDITED_TRACKS_AND_VIA_AREAS && ( aTrack->IsDragging() || aTrack->IsMoving() || aTrack->IsNew() ) ) || ( aDisplOpts.m_ShowTrackClearanceMode == PCB_DISPLAY_OPTIONS::SHOW_CLEARANCE_ALWAYS ) @@ -75,7 +76,14 @@ EDA_ITEM* TRACK::Clone() const } -VIA::VIA( BOARD_ITEM* aParent ) : TRACK( aParent, PCB_VIA_T ) +EDA_ITEM* ARC::Clone() const +{ + return new ARC( *this ); +} + + +VIA::VIA( BOARD_ITEM* aParent ) : + TRACK( aParent, PCB_VIA_T ) { SetViaType( VIATYPE::THROUGH ); m_BottomLayer = B_Cu; @@ -229,6 +237,14 @@ void TRACK::Rotate( const wxPoint& aRotCentre, double aAngle ) } +void ARC::Rotate( const wxPoint& aRotCentre, double aAngle ) +{ + RotatePoint( &m_Start, aRotCentre, aAngle ); + RotatePoint( &m_End, aRotCentre, aAngle ); + RotatePoint( &m_Mid, aRotCentre, aAngle ); +} + + void TRACK::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) { if( aFlipLeftRight ) @@ -247,6 +263,26 @@ void TRACK::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) } +void ARC::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) +{ + if( aFlipLeftRight ) + { + m_Start.x = aCentre.x - ( m_Start.x - aCentre.x ); + m_End.x = aCentre.x - ( m_End.x - aCentre.x ); + m_Mid.x = aCentre.x - ( m_Mid.x - aCentre.x ); + } + else + { + m_Start.y = aCentre.y - ( m_Start.y - aCentre.y ); + m_End.y = aCentre.y - ( m_End.y - aCentre.y ); + m_Mid.y = aCentre.y - ( m_Mid.y - aCentre.y ); + } + + int copperLayerCount = GetBoard()->GetCopperLayerCount(); + SetLayer( FlipLayer( GetLayer(), copperLayerCount ) ); +} + + void VIA::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) { if( aFlipLeftRight ) @@ -938,6 +974,22 @@ 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 ); + + auto rel_start = EuclideanNorm( aPosition - m_Start ); + auto rel_mid = EuclideanNorm( aPosition - m_Mid ); + auto rel_end = EuclideanNorm( aPosition - m_End ); + + if( rel_start <= max_dist || rel_mid <= max_dist || rel_end <= max_dist ) + return true; + + //TODO: Calculate along arc + return false; +} + + bool VIA::HitTest( const wxPoint& aPosition, int aAccuracy ) const { int max_dist = aAccuracy + ( m_Width / 2 ); @@ -963,6 +1015,25 @@ bool TRACK::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con } +bool ARC::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const +{ + EDA_RECT box; + EDA_RECT arect = aRect; + arect.Inflate( aAccuracy ); + + box.SetOrigin( GetStart() ); + box.Merge( GetMid() ); + box.Merge( GetEnd() ); + + box.Inflate( GetWidth() / 2 ); + + if( aContained ) + return arect.Contains( box ); + else + return arect.Intersects( box ); +} + + bool VIA::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const { EDA_RECT box; @@ -1005,6 +1076,13 @@ void TRACK::SwapData( BOARD_ITEM* aImage ) std::swap( *((TRACK*) this), *((TRACK*) aImage) ); } +void ARC::SwapData( BOARD_ITEM* aImage ) +{ + assert( aImage->Type() == PCB_ARC_T ); + + std::swap( *this, *static_cast( aImage ) ); +} + void VIA::SwapData( BOARD_ITEM* aImage ) { assert( aImage->Type() == PCB_VIA_T ); diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index f3a95cb101..fe0704b281 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -136,7 +136,7 @@ public: * returns the length of the track using the hypotenuse calculation. * @return double - the length of the track */ - double GetLength() const + virtual double GetLength() const { return GetLineLength( m_Start, m_End ); } @@ -257,6 +257,63 @@ protected: }; +class ARC : public TRACK +{ +public: + ARC( BOARD_ITEM* aParent ) : TRACK( aParent, PCB_ARC_T ){}; + + static inline bool ClassOf( const EDA_ITEM *aItem ) + { + return aItem && PCB_ARC_T == aItem->Type(); + } + + virtual void Move( const wxPoint& aMoveVector ) override + { + m_Start += aMoveVector; + m_Mid += aMoveVector; + m_End += aMoveVector; + } + + virtual void Rotate( const wxPoint& aRotCentre, double aAngle ) override; + + virtual void Flip( const wxPoint& aCentre, bool aFlipLeftRight ) override; + + void SetMid( const wxPoint& aMid ) { m_Mid = aMid; } + const wxPoint& GetMid() const { return m_Mid; } + + 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; + + wxString GetClass() const override + { + return wxT( "ARC" ); + } + + //TODO(snh): Add GetSelectMenuText() and GetMsgPanelInfoBase() + + /** + * Function GetLength + * returns the length of the track using the hypotenuse calculation. + * @return double - the length of the track + */ + virtual double GetLength() const override + { + //TODO(snh): Add proper arc length calc + return GetLineLength( m_Start, m_Mid ) + GetLineLength( m_Mid, m_End ); + } + + EDA_ITEM* Clone() const override; + + virtual void SwapData( BOARD_ITEM* aImage ) override; + +protected: + +private: + wxPoint m_Mid; ///< Arc mid point, halfway between start and end +}; + + class VIA : public TRACK { public: diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 44b821f394..882ad7abe7 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -1706,6 +1706,18 @@ void PCB_IO::format( TRACK* aTrack, int aNestLevel ) const m_out->Quotew( m_board->GetLayerName( layer1 ) ).c_str(), m_out->Quotew( m_board->GetLayerName( layer2 ) ).c_str() ); } + else if( aTrack->Type() == PCB_ARC_T ) + { + const ARC* arc = static_cast( aTrack ); + + m_out->Print( aNestLevel, "(arc (start %s) (mid %s) (end %s) (width %s)", + FormatInternalUnits( arc->GetStart() ).c_str(), + FormatInternalUnits( arc->GetMid() ).c_str(), + FormatInternalUnits( arc->GetEnd() ).c_str(), + FormatInternalUnits( arc->GetWidth() ).c_str() ); + + m_out->Print( 0, " (layer %s)", m_out->Quotew( aTrack->GetLayerName() ).c_str() ); + } else { m_out->Print( aNestLevel, "(segment (start %s) (end %s) (width %s)", diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 338fc34a84..3c80a2e7ec 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -65,7 +65,8 @@ class TEXTE_PCB; //#define SEXPR_BOARD_FILE_VERSION 20190905 // Add board physical stackup info in setup section //#define SEXPR_BOARD_FILE_VERSION 20190907 // Keepout areas in footprints //#define SEXPR_BOARD_FILE_VERSION 20191123 // pin function in pads -#define SEXPR_BOARD_FILE_VERSION 20200104 // pad property for fabrication +//#define SEXPR_BOARD_FILE_VERSION 20200104 // pad property for fabrication +#define SEXPR_BOARD_FILE_VERSION 20200119 // arcs in tracks #define CTL_STD_LAYER_NAMES (1 << 0) ///< Use English Standard layer names #define CTL_OMIT_NETS (1 << 1) ///< Omit pads net names (useless in library) diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 2b07302a26..8a4ed45c25 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -586,6 +586,10 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() m_board->Add( parseTRACK(), ADD_MODE::INSERT ); break; + case T_arc: + m_board->Add( parseARC(), ADD_INSERT ); + break; + case T_via: m_board->Add( parseVIA(), ADD_MODE::INSERT ); break; @@ -3349,6 +3353,77 @@ bool PCB_PARSER::parseD_PAD_option( D_PAD* aPad ) } +ARC* PCB_PARSER::parseARC() +{ + wxCHECK_MSG( CurTok() == T_arc, NULL, + wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) ); + + wxPoint pt; + T token; + + std::unique_ptr arc( new ARC( m_board ) ); + + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) + { + if( token != T_LEFT ) + Expecting( T_LEFT ); + + token = NextTok(); + + switch( token ) + { + case T_start: + pt.x = parseBoardUnits( "start x" ); + pt.y = parseBoardUnits( "start y" ); + arc->SetStart( pt ); + break; + + case T_mid: + pt.x = parseBoardUnits( "mid x" ); + pt.y = parseBoardUnits( "mid y" ); + arc->SetMid( pt ); + break; + + case T_end: + pt.x = parseBoardUnits( "end x" ); + pt.y = parseBoardUnits( "end y" ); + arc->SetEnd( pt ); + break; + + case T_width: + arc->SetWidth( parseBoardUnits( "width" ) ); + break; + + case T_layer: + arc->SetLayer( parseBoardItemLayer() ); + break; + + case T_net: + if( !arc->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) ) + THROW_IO_ERROR( wxString::Format( + _( "Invalid net ID in\nfile: \"%s\"\nline: %d\noffset: %d" ), + GetChars( CurSource() ), CurLineNumber(), CurOffset() ) ); + break; + + case T_tstamp: + arc->SetTimeStamp( parseHex() ); + break; + + case T_status: + arc->SetStatus( static_cast( parseHex() ) ); + break; + + default: + Expecting( "start, mid, end, width, layer, net, tstamp, or status" ); + } + + NeedRIGHT(); + } + + return arc.release(); +} + + TRACK* PCB_PARSER::parseTRACK() { wxCHECK_MSG( CurTok() == T_segment, NULL, diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index c531f65330..e23626fed3 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -39,6 +39,7 @@ #include +class ARC; class BOARD; class BOARD_ITEM; class BOARD_ITEM_CONTAINER; @@ -155,6 +156,7 @@ class PCB_PARSER : public PCB_LEXER D_PAD* parseD_PAD( MODULE* aParent = NULL ); // Parse only the (option ...) inside a pad description bool parseD_PAD_option( D_PAD* aPad ); + ARC* parseARC(); TRACK* parseTRACK(); VIA* parseVIA(); ZONE_CONTAINER* parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent );