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 <dick@softplc.com>
  * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
- * 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<ARC*>( 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<const ARC*>( 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> 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<STATUS_FLAGS>( 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 <unordered_map>
 
 
+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 );