From 9b9e379aa0e975960cb4a8c85559b169057e0115 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Sat, 17 Jul 2021 20:56:18 +0100
Subject: [PATCH] Overhaul arc internal model to not over-specify information.

---
 .../3d_canvas/create_3Dgraphic_brd_items.cpp  |   2 +-
 common/eda_shape.cpp                          | 333 +++++-----
 common/hash_eda.cpp                           |  45 +-
 eeschema/lib_arc.cpp                          | 216 +++----
 eeschema/lib_arc.h                            |  25 +-
 eeschema/lib_shape.cpp                        | 570 ++++++++++++++++++
 eeschema/lib_shape.h                          | 108 ++++
 eeschema/sch_item.cpp                         |   2 +-
 eeschema/sch_painter.cpp                      |  35 +-
 .../sch_plugins/altium/sch_altium_plugin.cpp  |  28 +-
 .../cadstar/cadstar_sch_archive_loader.cpp    |  13 +-
 .../sch_plugins/eagle/sch_eagle_plugin.cpp    |  34 +-
 .../sch_plugins/kicad/sch_sexpr_parser.cpp    |  39 +-
 .../sch_plugins/kicad/sch_sexpr_plugin.cpp    |   7 +-
 .../sch_plugins/legacy/sch_legacy_plugin.cpp  |  44 +-
 include/eda_shape.h                           |  83 +--
 include/macros.h                              |   3 +
 libs/kimath/include/trigo.h                   |  14 +-
 libs/kimath/src/geometry/circle.cpp           |   4 +-
 libs/kimath/src/geometry/shape_arc.cpp        |   4 +-
 libs/kimath/src/geometry/shape_line_chain.cpp |   2 +-
 libs/kimath/src/trigo.cpp                     |  20 +-
 pcbnew/autorouter/ar_matrix.cpp               |  39 +-
 pcbnew/autorouter/ar_matrix.h                 |   2 +-
 pcbnew/board_item.cpp                         |   3 +-
 ...board_items_to_polygon_shape_transform.cpp |  14 +-
 pcbnew/convert_shape_list_to_polygon.cpp      |  89 +--
 .../dialog_graphic_item_properties.cpp        |  48 +-
 .../dialog_pad_basicshapes_properties.cpp     |  20 +-
 pcbnew/dialogs/dialog_pad_properties.cpp      |   5 +-
 pcbnew/exporters/export_gencad.cpp            |  17 +-
 pcbnew/exporters/export_idf.cpp               |  21 +-
 pcbnew/fp_shape.cpp                           |  89 ++-
 pcbnew/fp_shape.h                             |  13 +-
 pcbnew/graphics_cleaner.cpp                   |  12 +-
 .../import_gfx/graphics_importer_pcbnew.cpp   |  10 +-
 pcbnew/microwave/microwave_inductor.cpp       |   3 +-
 pcbnew/pad_custom_shape_functions.cpp         |   6 +-
 pcbnew/pcb_painter.cpp                        |  28 +-
 pcbnew/pcb_shape.cpp                          |   2 +-
 pcbnew/pcb_shape.h                            |   4 +-
 pcbnew/pcb_track.cpp                          |   4 +-
 pcbnew/plot_brditems_plotter.cpp              |  77 +--
 pcbnew/plugins/altium/altium_pcb.cpp          |  50 +-
 .../cadstar/cadstar_pcb_archive_loader.cpp    |  20 +-
 pcbnew/plugins/eagle/eagle_plugin.cpp         |  13 +-
 pcbnew/plugins/fabmaster/import_fabmaster.cpp |  24 +-
 pcbnew/plugins/geda/gpcb_plugin.cpp           |  14 +-
 pcbnew/plugins/kicad/kicad_plugin.cpp         |  18 +-
 pcbnew/plugins/kicad/pcb_parser.cpp           |  29 +-
 pcbnew/plugins/legacy/legacy_plugin.cpp       |  19 +-
 pcbnew/plugins/pcad/pcb_arc.cpp               |  15 +-
 pcbnew/plugins/pcad/pcb_line.cpp              |   2 +-
 .../specctra_export.cpp                       |  12 +-
 pcbnew/tools/convert_tool.cpp                 |  35 +-
 pcbnew/tools/drawing_tool.cpp                 |  15 +-
 pcbnew/tools/edit_tool.cpp                    |   2 +-
 pcbnew/tools/pad_tool.cpp                     |  77 ++-
 pcbnew/tools/pcb_grid_helper.cpp              |   6 +-
 pcbnew/tools/pcb_point_editor.cpp             |  38 +-
 qa/eeschema/CMakeLists.txt                    |   1 -
 qa/eeschema/test_lib_arc.cpp                  | 120 ----
 qa/pcbnew_utils/board_construction_utils.cpp  |   6 +-
 63 files changed, 1546 insertions(+), 1107 deletions(-)
 create mode 100644 eeschema/lib_shape.cpp
 create mode 100644 eeschema/lib_shape.h
 delete mode 100644 qa/eeschema/test_lib_arc.cpp

diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp
index f59b3a2295..4ddf4f4516 100644
--- a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp
+++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp
@@ -689,7 +689,7 @@ void BOARD_ADAPTER::addShapeWithClearance( const PCB_SHAPE* aShape,
     {
         unsigned int segCount = GetCircleSegmentCount( aShape->GetBoundingBox().GetSizeMax() );
 
-        transformArcToSegments( aShape->GetCenter(), aShape->GetArcStart(), aShape->GetAngle(),
+        transformArcToSegments( aShape->GetCenter(), aShape->GetStart(), aShape->GetArcAngle(),
                                 segCount, linewidth, aDstContainer, *aShape );
     }
     break;
diff --git a/common/eda_shape.cpp b/common/eda_shape.cpp
index 0114b28b1e..bc677b6fee 100644
--- a/common/eda_shape.cpp
+++ b/common/eda_shape.cpp
@@ -39,7 +39,7 @@ EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aDefaultLineWidth ) :
     m_shape( aType ),
     m_width( aDefaultLineWidth )
 {
-    m_angle = 0;
+    m_arcAngle = 0;
     m_filled = false;
 }
 
@@ -82,15 +82,17 @@ wxString EDA_SHAPE::SHAPE_T_asString() const
 }
 
 
-void EDA_SHAPE::SetShapePos( const wxPoint& aPos )
+void EDA_SHAPE::setPosition( const wxPoint& aPos )
 {
-    m_start = aPos;
+    move( aPos - getPosition() );
 }
 
 
-wxPoint EDA_SHAPE::GetShapePos() const
+wxPoint EDA_SHAPE::getPosition() const
 {
-    if( m_shape == SHAPE_T::POLY )
+    if( m_shape == SHAPE_T::ARC )
+        return getCenter();
+    else if( m_shape == SHAPE_T::POLY )
         return (wxPoint) m_poly.CVertex( 0 );
     else
         return m_start;
@@ -119,10 +121,10 @@ double EDA_SHAPE::GetLength() const
         return length;
 
     case SHAPE_T::ARC:
-        return 2 * M_PI * GetRadius() * ( GetAngle() / 3600.0 );
+        return 2 * M_PI * GetRadius() * ( GetArcAngle() / 3600.0 );
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::GetLength not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         return 0.0;
     }
 }
@@ -130,25 +132,24 @@ double EDA_SHAPE::GetLength() const
 
 void EDA_SHAPE::move( const wxPoint& aMoveVector )
 {
-    // Move vector should not affect start/end for polygon since it will
-    // be applied directly to polygon outline.
-    if( m_shape != SHAPE_T::POLY )
-    {
-        m_start += aMoveVector;
-        m_end += aMoveVector;
-    }
-
     switch ( m_shape )
     {
+    case SHAPE_T::ARC:
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::RECT:
+    case SHAPE_T::CIRCLE:
+        m_start += aMoveVector;
+        m_end += aMoveVector;
+        m_arcCenter += aMoveVector;
+        break;
+
     case SHAPE_T::POLY:
         m_poly.Move( VECTOR2I( aMoveVector ) );
         break;
 
-    case SHAPE_T::ARC:
-        m_thirdPoint += aMoveVector;
-        break;
-
     case SHAPE_T::BEZIER:
+        m_start += aMoveVector;
+        m_end += aMoveVector;
         m_bezierC1 += aMoveVector;
         m_bezierC2 += aMoveVector;
 
@@ -158,7 +159,7 @@ void EDA_SHAPE::move( const wxPoint& aMoveVector )
         break;
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::ShapeMove not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 }
@@ -174,22 +175,19 @@ void EDA_SHAPE::scale( double aScale )
 
     int radius = GetRadius();
 
-    scalePt( m_start );
-    scalePt( m_end );
-
     // specific parameters:
     switch( m_shape )
     {
-    case SHAPE_T::BEZIER:
-        scalePt( m_bezierC1 );
-        scalePt( m_bezierC2 );
-        break;
-
     case SHAPE_T::ARC:
-        scalePt( m_thirdPoint );
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::RECT:
+        scalePt( m_start );
+        scalePt( m_end );
+        scalePt( m_arcCenter );
         break;
 
     case SHAPE_T::CIRCLE: //  ring or circle
+        scalePt( m_start );
         m_end.x = m_start.x + KiROUND( radius * aScale );
         m_end.y = m_start.y;
         break;
@@ -208,8 +206,15 @@ void EDA_SHAPE::scale( double aScale )
     }
         break;
 
+    case SHAPE_T::BEZIER:
+        scalePt( m_start );
+        scalePt( m_end );
+        scalePt( m_bezierC1 );
+        scalePt( m_bezierC2 );
+        break;
+
     default:
-        wxFAIL_MSG( "EDA_SHAPE::ShapeScale not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 }
@@ -225,7 +230,7 @@ void EDA_SHAPE::rotate( const wxPoint& aRotCentre, double aAngle )
         // these can all be done by just rotating the constituent points
         RotatePoint( &m_start, aRotCentre, aAngle );
         RotatePoint( &m_end, aRotCentre, aAngle );
-        RotatePoint( &m_thirdPoint, aRotCentre, aAngle );
+        RotatePoint( &m_arcCenter, aRotCentre, aAngle );
         break;
 
     case SHAPE_T::RECT:
@@ -263,7 +268,7 @@ void EDA_SHAPE::rotate( const wxPoint& aRotCentre, double aAngle )
         break;
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::ShapeRotate not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 }
@@ -271,26 +276,24 @@ void EDA_SHAPE::rotate( const wxPoint& aRotCentre, double aAngle )
 
 void EDA_SHAPE::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 );
-    }
-    else
-    {
-        m_start.y = aCentre.y - ( m_start.y - aCentre.y );
-        m_end.y   = aCentre.y - ( m_end.y - aCentre.y );
-    }
-
     switch ( m_shape )
     {
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::RECT:
+    case SHAPE_T::CIRCLE:
     case SHAPE_T::ARC:
         if( aFlipLeftRight )
-            m_thirdPoint.x   = aCentre.x - ( m_thirdPoint.x - aCentre.x );
+        {
+            m_start.x      = aCentre.x - ( m_start.x - aCentre.x );
+            m_end.x        = aCentre.x - ( m_end.x - aCentre.x );
+            m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
+        }
         else
-            m_thirdPoint.y   = aCentre.y - ( m_thirdPoint.y - aCentre.y );
-
-        m_angle = -m_angle;
+        {
+            m_start.y      = aCentre.y - ( m_start.y - aCentre.y );
+            m_end.y        = aCentre.y - ( m_end.y - aCentre.y );
+            m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
+        }
         break;
 
     case SHAPE_T::POLY:
@@ -301,11 +304,15 @@ void EDA_SHAPE::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_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
             m_bezierC2.x = aCentre.x - ( m_bezierC2.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_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
             m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
         }
@@ -317,13 +324,8 @@ void EDA_SHAPE::flip( const wxPoint& aCentre, bool aFlipLeftRight )
     }
         break;
 
-    case SHAPE_T::SEGMENT:
-    case SHAPE_T::RECT:
-    case SHAPE_T::CIRCLE:
-        break;
-
     default:
-        wxFAIL_MSG( "EDA_SHAPE::ShapeFlip not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 }
@@ -358,78 +360,59 @@ const std::vector<wxPoint> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinS
 
 wxPoint EDA_SHAPE::getCenter() const
 {
-    wxPoint c;
-
     switch( m_shape )
     {
     case SHAPE_T::ARC:
+        return m_arcCenter;
+
     case SHAPE_T::CIRCLE:
-        c = m_start;
-        break;
+        return m_start;
 
     case SHAPE_T::SEGMENT:
         // Midpoint of the line
-        c = ( GetStart() + GetEnd() ) / 2;
-        break;
+        return ( m_start + m_end ) / 2;
 
     case SHAPE_T::POLY:
     case SHAPE_T::RECT:
     case SHAPE_T::BEZIER:
-        c = getBoundingBox().Centre();
-        break;
+        return getBoundingBox().Centre();
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::GetCentre not implemented for " + SHAPE_T_asString() );
-        break;
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
+        return wxPoint();
     }
-
-    return c;
 }
 
 
-wxPoint EDA_SHAPE::GetArcEnd() const
+void EDA_SHAPE::SetCenter( const wxPoint& aCenter )
 {
-    wxPoint endPoint( m_end );         // start of arc
-
     switch( m_shape )
     {
     case SHAPE_T::ARC:
-        endPoint = m_thirdPoint;
+        m_arcCenter = aCenter;
+        break;
+
+    case SHAPE_T::CIRCLE:
+        m_start = aCenter;
         break;
 
     default:
-        break;
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
     }
-
-    return endPoint;   // after rotation, the end of the arc.
 }
 
 
 wxPoint EDA_SHAPE::GetArcMid() const
 {
-    wxPoint endPoint( m_end );
-
-    switch( m_shape )
-    {
-    case SHAPE_T::ARC:
-        // rotate the starting point of the arc, given by m_End, through half
-        // the angle m_Angle to get the middle of the arc.
-        // m_Start is the arc centre
-        endPoint  = m_end;         // m_End = start point of arc
-        RotatePoint( &endPoint, m_start, -m_angle / 2.0 );
-        break;
-
-    default:
-        break;
-    }
-
-    return endPoint;   // after rotation, the end of the arc.
+    wxPoint mid = m_start;
+    RotatePoint( &mid, m_arcCenter, -m_arcAngle / 2.0 );
+    return mid;
 }
 
 
 double EDA_SHAPE::GetArcAngleStart() const
 {
-    wxPoint arcStart = GetArcStart();
+    wxPoint arcStart = GetStart();
     wxPoint center = getCenter();
     double  angleStart = ArcTangente( arcStart.y - center.y, arcStart.x - center.x );
 
@@ -441,9 +424,10 @@ double EDA_SHAPE::GetArcAngleStart() const
     return angleStart;
 }
 
+
 double EDA_SHAPE::GetArcAngleEnd() const
 {
-    wxPoint arcEnd = GetArcEnd();
+    wxPoint arcEnd = GetEnd();
     wxPoint center = getCenter();
     double  angleStart = ArcTangente( arcEnd.y - center.y, arcEnd.x - center.x );
 
@@ -458,46 +442,60 @@ double EDA_SHAPE::GetArcAngleEnd() const
 
 int EDA_SHAPE::GetRadius() const
 {
-    double radius = GetLineLength( m_start, m_end );
+    double radius = 0.0;
 
-    // don't allow degenerate arcs
+    switch( m_shape )
+    {
+    case SHAPE_T::ARC:
+        radius = GetLineLength( m_arcCenter, m_start );
+        break;
+
+    case SHAPE_T::CIRCLE:
+        radius = GetLineLength( m_start, m_end );
+        break;
+
+    default:
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
+    }
+
+    // don't allow degenerate circles/arcs
     return std::max( 1, KiROUND( radius ) );
 }
 
 
 void EDA_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd )
 {
-    SetArcStart( aStart );
-    SetArcEnd( aEnd );
+    m_start = aStart;
+    m_end = aEnd;
+    m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
 
-    // Sadly we currently store center and angle rather than mid.  So we have to calculate
-    // those.
-    wxPoint  center = GetArcCenter( aStart, aMid, aEnd );
-    VECTOR2D startLine = aStart - center;
-    VECTOR2D endLine   = aEnd - center;
-    bool     clockwise = GetAngle() > 0;
-    double   angle  = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
+    VECTOR2D startLine = m_start - m_arcCenter;
+    VECTOR2D endLine   = aEnd - m_arcCenter;
+    bool     clockwise = m_arcAngle > 0;
 
-    if( clockwise && angle < 0.0 )
-        angle += 3600.0;
-    else if( !clockwise && angle > 0.0 )
-        angle -= 3600.0;
+    m_arcAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
 
-    SetAngle( angle, false );
-    SetArcCenter( center );
+    if( clockwise && m_arcAngle < 0.0 )
+        m_arcAngle += 3600.0;
+    else if( !clockwise && m_arcAngle > 0.0 )
+        m_arcAngle -= 3600.0;
 }
 
 
-void EDA_SHAPE::SetAngle( double aAngle, bool aUpdateEnd )
+void EDA_SHAPE::SetArcAngle( double aAngle )
 {
     // m_Angle must be >= -360 and <= +360 degrees
-    m_angle = NormalizeAngle360Max( aAngle );
+    m_arcAngle = NormalizeAngle360Max( aAngle );
+}
 
-    if( aUpdateEnd )
-    {
-        m_thirdPoint = m_end;
-        RotatePoint( &m_thirdPoint, m_start, -m_angle );
-    }
+
+void EDA_SHAPE::SetArcAngleAndEnd( double aAngle )
+{
+    // m_Angle must be >= -360 and <= +360 degrees
+    m_arcAngle = NormalizeAngle360Max( aAngle );
+
+    m_end = m_start;
+    RotatePoint( &m_end, m_arcCenter, -m_arcAngle );
 }
 
 
@@ -514,17 +512,17 @@ void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
     case SHAPE_T::CIRCLE:
         aList.emplace_back( shape, _( "Circle" ) );
 
-        msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) );
+        msg = MessageTextFromValue( units, GetLineLength( GetStart(), GetEnd() ) );
         aList.emplace_back( _( "Radius" ), msg );
         break;
 
     case SHAPE_T::ARC:
         aList.emplace_back( shape, _( "Arc" ) );
 
-        msg.Printf( wxT( "%.1f" ), m_angle / 10.0 );
+        msg.Printf( wxT( "%.1f" ), m_arcAngle / 10.0 );
         aList.emplace_back( _( "Angle" ), msg );
 
-        msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) );
+        msg = MessageTextFromValue( units, GetLineLength( getCenter(), GetStart() ) );
         aList.emplace_back( _( "Radius" ), msg );
         break;
 
@@ -545,10 +543,10 @@ void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
     case SHAPE_T::RECT:
         aList.emplace_back( shape, _( "Rectangle" ) );
 
-        msg = MessageTextFromValue( units, std::abs( m_end.x - m_start.x ) );
+        msg = MessageTextFromValue( units, std::abs( GetEnd().x - GetStart().x ) );
         aList.emplace_back( _( "Width" ), msg );
 
-        msg = MessageTextFromValue( units, std::abs( m_end.y - m_start.y ) );
+        msg = MessageTextFromValue( units, std::abs( GetEnd().y - GetStart().y ) );
         aList.emplace_back( _( "Height" ), msg );
         break;
 
@@ -556,12 +554,12 @@ void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
     {
         aList.emplace_back( shape, _( "Segment" ) );
 
-        msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) );
+        msg = MessageTextFromValue( units, GetLineLength( GetStart(), GetEnd() ) );
         aList.emplace_back( _( "Length" ), msg );
 
         // angle counter-clockwise from 3'o-clock
-        const double deg = RAD2DEG( atan2( (double)( m_start.y - m_end.y ),
-                                           (double)( m_end.x - m_start.x ) ) );
+        const double deg = RAD2DEG( atan2( (double)( GetStart().y - GetEnd().y ),
+                                           (double)( GetEnd().x - GetStart().x ) ) );
         aList.emplace_back( _( "Angle" ), wxString::Format( "%.1f", deg ) );
     }
         break;
@@ -579,23 +577,21 @@ const EDA_RECT EDA_SHAPE::getBoundingBox() const
 {
     EDA_RECT bbox;
 
-    bbox.SetOrigin( m_start );
-
     switch( m_shape )
     {
     case SHAPE_T::RECT:
-        bbox = EDA_RECT();  // re-init for merging
-
         for( wxPoint& pt : GetRectCorners() )
             bbox.Merge( pt );
 
         break;
 
     case SHAPE_T::SEGMENT:
-        bbox.SetEnd( m_end );
+        bbox.SetOrigin( GetStart() );
+        bbox.SetEnd( GetEnd() );
         break;
 
     case SHAPE_T::CIRCLE:
+        bbox.SetOrigin( GetStart() );
         bbox.Inflate( GetRadius() );
         break;
 
@@ -607,8 +603,6 @@ const EDA_RECT EDA_SHAPE::getBoundingBox() const
         if( m_poly.IsEmpty() )
             break;
 
-        bbox = EDA_RECT();  // re-init for merging
-
         for( auto iter = m_poly.CIterate(); iter; iter++ )
         {
             wxPoint pt( iter->x, iter->y );
@@ -622,13 +616,14 @@ const EDA_RECT EDA_SHAPE::getBoundingBox() const
         break;
 
     case SHAPE_T::BEZIER:
-        bbox.Merge( m_bezierC1 );
-        bbox.Merge( m_bezierC2 );
-        bbox.Merge( m_end );
+        bbox.SetOrigin( GetStart() );
+        bbox.Merge( GetBezierC1() );
+        bbox.Merge( GetBezierC2() );
+        bbox.Merge( GetEnd() );
         break;
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::getBoundingBox not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 
@@ -682,10 +677,10 @@ bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const
 
             // Check angle: inside the arc angle when it is > 0 and outside the not drawn arc when
             // it is < 0
-            if( GetAngle() >= 0.0 )
-                return arc_hittest <= GetAngle();
+            if( GetArcAngle() >= 0.0 )
+                return arc_hittest <= GetArcAngle();
             else
-                return arc_hittest >= ( 3600.0 + GetAngle() );
+                return arc_hittest >= ( 3600.0 + GetArcAngle() );
         }
         else
         {
@@ -705,7 +700,7 @@ bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const
         return false;
 
     case SHAPE_T::SEGMENT:
-        return TestSegmentHit( aPosition, m_start, m_end, maxdist );
+        return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
 
     case SHAPE_T::RECT:
         if( IsFilled() )            // Filled rect hit-test
@@ -903,7 +898,7 @@ bool EDA_SHAPE::hitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
         }
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::hitTest(rect) not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         return false;
     }
 }
@@ -949,11 +944,8 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
 {
     // Do not include the center, which is not necessarily
     // inside the BB of a arc with a small angle
-    aBBox.SetOrigin( m_end );
-
-    wxPoint end = m_end;
-    RotatePoint( &end, m_start, -m_angle );
-    aBBox.Merge( end );
+    aBBox.SetOrigin( m_start );
+    aBBox.Merge( m_end );
 
     // Determine the starting quarter
     // 0 right-bottom
@@ -962,24 +954,24 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
     // 3 right-top
     unsigned int quarter = 0;       // assume right-bottom
 
-    if( m_end.x < m_start.x )
+    if( m_start.x < m_arcCenter.x )
     {
-        if( m_end.y <= m_start.y )
+        if( m_start.y <= m_arcCenter.y )
             quarter = 2;
-        else // ( m_End.y > m_Start.y )
+        else // ( m_start.y > m_arcCenter.y )
             quarter = 1;
     }
-    else if( m_end.x >= m_start.x )
+    else if( m_start.x >= m_arcCenter.x )
     {
-        if( m_end.y < m_start.y )
+        if( m_start.y < m_arcCenter.y )
             quarter = 3;
-        else if( m_end.x == m_start.x )
+        else if( m_start.x == m_arcCenter.x )
             quarter = 1;
     }
 
     int radius = GetRadius();
-    int angle = (int) GetArcAngleStart() % 900 + m_angle;
-    bool directionCW = ( m_angle > 0 );      // Is the direction of arc clockwise?
+    int angle = (int) GetArcAngleStart() % 900 + m_arcAngle;
+    bool directionCW = ( m_arcAngle > 0 );      // Is the direction of arc clockwise?
 
     // Make the angle positive, so we go clockwise and merge points belonging to the arc
     if( !directionCW )
@@ -992,10 +984,10 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
     {
         switch( quarter )
         {
-        case 0: aBBox.Merge( wxPoint( m_start.x,          m_start.y + radius ) ); break;  // down
-        case 1: aBBox.Merge( wxPoint( m_start.x - radius, m_start.y          ) ); break;  // left
-        case 2: aBBox.Merge( wxPoint( m_start.x,          m_start.y - radius ) ); break;  // up
-        case 3: aBBox.Merge( wxPoint( m_start.x + radius, m_start.y          ) ); break;  // right
+        case 0: aBBox.Merge( wxPoint( m_arcCenter.x,          m_arcCenter.y + radius ) ); break;  // down
+        case 1: aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y          ) ); break;  // left
+        case 2: aBBox.Merge( wxPoint( m_arcCenter.x,          m_arcCenter.y - radius ) ); break;  // up
+        case 3: aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y          ) ); break;  // right
         }
 
         if( directionCW )
@@ -1030,12 +1022,12 @@ std::vector<SHAPE*> EDA_SHAPE::MakeEffectiveShapes() const
     switch( m_shape )
     {
     case SHAPE_T::ARC:
-        effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetArcStart(),
-                                                     GetAngle() / 10.0, m_width ) );
+        effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, m_arcAngle / 10.0,
+                                                     m_width ) );
         break;
 
     case SHAPE_T::SEGMENT:
-        effectiveShapes.emplace_back( new SHAPE_SEGMENT( GetStart(), GetEnd(), m_width ) );
+        effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, m_width ) );
         break;
 
     case SHAPE_T::RECT:
@@ -1068,8 +1060,8 @@ std::vector<SHAPE*> EDA_SHAPE::MakeEffectiveShapes() const
 
             for( int i = 0; i < l.SegmentCount(); i++ )
             {
-                effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A,
-                                                                 l.Segment( i ).B, m_width ) );
+                effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, l.Segment( i ).B,
+                                                                 m_width ) );
             }
         }
 
@@ -1110,7 +1102,7 @@ std::vector<SHAPE*> EDA_SHAPE::MakeEffectiveShapes() const
         break;
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::MakeEffectiveShapes not implemented for " + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 
@@ -1166,9 +1158,9 @@ void EDA_SHAPE::SwapShape( EDA_SHAPE* aImage )
     std::swap( m_width, image->m_width );
     std::swap( m_start, image->m_start );
     std::swap( m_end, image->m_end );
-    std::swap( m_thirdPoint, image->m_thirdPoint );
+    std::swap( m_arcCenter, image->m_arcCenter );
     std::swap( m_shape, image->m_shape );
-    std::swap( m_angle, image->m_angle );
+    std::swap( m_arcAngle, image->m_arcAngle );
     std::swap( m_bezierC1, image->m_bezierC1 );
     std::swap( m_bezierC2, image->m_bezierC2 );
     std::swap( m_bezierPoints, image->m_bezierPoints );
@@ -1184,11 +1176,11 @@ int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
     TEST_PT( m_start, aOther->m_start );
     TEST_PT( m_end, aOther->m_end );
 
-    TEST( (int) m_shape, (int) aOther->m_shape );
+    TEST( m_shape, aOther->m_shape );
 
     if( m_shape == SHAPE_T::ARC )
     {
-        TEST_PT( m_thirdPoint, aOther->m_thirdPoint );
+        TEST_PT( m_arcCenter, aOther->m_arcCenter );
     }
     else if( m_shape == SHAPE_T::BEZIER )
     {
@@ -1198,11 +1190,14 @@ int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
     else if( m_shape == SHAPE_T::POLY )
     {
         TEST( m_poly.TotalVertices(), aOther->m_poly.TotalVertices() );
-
-        for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
-            TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
     }
 
+    for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
+        TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
+
+    for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
+        TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
+
     TEST( m_width, aOther->m_width );
 
     return 0;
diff --git a/common/hash_eda.cpp b/common/hash_eda.cpp
index 09345a8a99..69c00351b0 100644
--- a/common/hash_eda.cpp
+++ b/common/hash_eda.cpp
@@ -138,33 +138,44 @@ size_t hash_fp_item( const EDA_ITEM* aItem, int aFlags )
 
     case PCB_FP_SHAPE_T:
     {
-        const FP_SHAPE* segment = static_cast<const FP_SHAPE*>( aItem );
-        ret = hash_board_item( segment, aFlags );
-        hash_combine( ret, segment->GetShape() );
-        hash_combine( ret, segment->GetWidth() );
-        hash_combine( ret, segment->IsFilled() );
-        hash_combine( ret, segment->GetRadius() );
+        const FP_SHAPE* shape = static_cast<const FP_SHAPE*>( aItem );
+        ret = hash_board_item( shape, aFlags );
+        hash_combine( ret, shape->GetShape() );
+        hash_combine( ret, shape->GetWidth() );
+        hash_combine( ret, shape->IsFilled() );
+        hash_combine( ret, shape->GetRadius() );
 
         if( aFlags & HASH_POS )
         {
             if( aFlags & REL_COORD )
             {
-                hash_combine( ret, segment->GetStart0().x );
-                hash_combine( ret, segment->GetStart0().y );
-                hash_combine( ret, segment->GetEnd0().x );
-                hash_combine( ret, segment->GetEnd0().y );
+                hash_combine( ret, shape->GetStart0().x );
+                hash_combine( ret, shape->GetStart0().y );
+                hash_combine( ret, shape->GetEnd0().x );
+                hash_combine( ret, shape->GetEnd0().y );
+
+                if( shape->GetShape() == SHAPE_T::ARC )
+                {
+                    hash_combine( ret, shape->GetCenter0().x );
+                    hash_combine( ret, shape->GetCenter0().y );
+                    hash_combine( ret, shape->GetArcAngle() );
+                }
             }
             else
             {
-                hash_combine( ret, segment->GetStart().x );
-                hash_combine( ret, segment->GetStart().y );
-                hash_combine( ret, segment->GetEnd().x );
-                hash_combine( ret, segment->GetEnd().y );
+                hash_combine( ret, shape->GetStart().x );
+                hash_combine( ret, shape->GetStart().y );
+                hash_combine( ret, shape->GetEnd().x );
+                hash_combine( ret, shape->GetEnd().y );
+
+                if( shape->GetShape() == SHAPE_T::ARC )
+                {
+                    hash_combine( ret, shape->GetCenter().x );
+                    hash_combine( ret, shape->GetCenter().y );
+                    hash_combine( ret, shape->GetArcAngle() );
+                }
             }
         }
-
-        if( aFlags & HASH_ROT )
-            hash_combine( ret, segment->GetAngle() );
     }
         break;
 
diff --git a/eeschema/lib_arc.cpp b/eeschema/lib_arc.cpp
index b38f6a0755..c4abdee87f 100644
--- a/eeschema/lib_arc.cpp
+++ b/eeschema/lib_arc.cpp
@@ -46,9 +46,6 @@ static inline wxPoint twoPointVector( const wxPoint &startPoint, const wxPoint &
 
 LIB_ARC::LIB_ARC( LIB_SYMBOL* aParent ) : LIB_ITEM( LIB_ARC_T, aParent )
 {
-    m_Radius        = 0;
-    m_t1            = 0;
-    m_t2            = 0;
     m_Width         = 0;
     m_fill          = FILL_TYPE::NO_FILL;
     m_isFillable    = true;
@@ -56,6 +53,12 @@ LIB_ARC::LIB_ARC( LIB_SYMBOL* aParent ) : LIB_ITEM( LIB_ARC_T, aParent )
 }
 
 
+int LIB_ARC::GetRadius() const
+{
+    return KiROUND( GetLineLength( GetCenter(), GetStart() ) );
+}
+
+
 bool LIB_ARC::HitTest( const wxPoint& aRefPoint, int aAccuracy ) const
 {
     int     mindist = std::max( aAccuracy + GetPenWidth() / 2,
@@ -66,7 +69,7 @@ bool LIB_ARC::HitTest( const wxPoint& aRefPoint, int aAccuracy ) const
 
     int distance = KiROUND( GetLineLength( m_Pos, relativePosition ) );
 
-    if( abs( distance - m_Radius ) > mindist )
+    if( abs( distance - GetRadius() ) > mindist )
         return false;
 
     // We are on the circle, ensure we are only on the arc, i.e. between
@@ -147,12 +150,6 @@ int LIB_ARC::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFl
     if( m_Pos.y != tmp->m_Pos.y )
         return m_Pos.y - tmp->m_Pos.y;
 
-    if( m_t1 != tmp->m_t1 )
-        return m_t1 - tmp->m_t1;
-
-    if( m_t2 != tmp->m_t2 )
-        return m_t2 - tmp->m_t2;
-
     return 0;
 }
 
@@ -186,20 +183,6 @@ void LIB_ARC::MirrorHorizontal( const wxPoint& aCenter )
     m_ArcEnd.x *= -1;
     m_ArcEnd.x += aCenter.x;
     std::swap( m_ArcStart, m_ArcEnd );
-    std::swap( m_t1, m_t2 );
-    m_t1 = 1800 - m_t1;
-    m_t2 = 1800 - m_t2;
-
-    if( m_t1 > 3600 || m_t2 > 3600 )
-    {
-        m_t1 -= 3600;
-        m_t2 -= 3600;
-    }
-    else if( m_t1 < -3600 || m_t2 < -3600 )
-    {
-        m_t1 += 3600;
-        m_t2 += 3600;
-    }
 }
 
 
@@ -215,20 +198,6 @@ void LIB_ARC::MirrorVertical( const wxPoint& aCenter )
     m_ArcEnd.y *= -1;
     m_ArcEnd.y += aCenter.y;
     std::swap( m_ArcStart, m_ArcEnd );
-    std::swap( m_t1, m_t2 );
-    m_t1 = - m_t1;
-    m_t2 = - m_t2;
-
-    if( m_t1 > 3600 || m_t2 > 3600 )
-    {
-        m_t1 -= 3600;
-        m_t2 -= 3600;
-    }
-    else if( m_t1 < -3600 || m_t2 < -3600 )
-    {
-        m_t1 += 3600;
-        m_t2 += 3600;
-    }
 }
 
 
@@ -238,19 +207,6 @@ void LIB_ARC::Rotate( const wxPoint& aCenter, bool aRotateCCW )
     RotatePoint( &m_Pos, aCenter, rot_angle );
     RotatePoint( &m_ArcStart, aCenter, rot_angle );
     RotatePoint( &m_ArcEnd, aCenter, rot_angle );
-    m_t1 -= rot_angle;
-    m_t2 -= rot_angle;
-
-    if( m_t1 > 3600 || m_t2 > 3600 )
-    {
-        m_t1 -= 3600;
-        m_t2 -= 3600;
-    }
-    else if( m_t1 < -3600 || m_t2 < -3600 )
-    {
-        m_t1 += 3600;
-        m_t2 += 3600;
-    }
 }
 
 
@@ -259,27 +215,30 @@ void LIB_ARC::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
 {
     wxASSERT( aPlotter != nullptr );
 
-    int t1 = m_t1;
-    int t2 = m_t2;
-    wxPoint pos = aTransform.TransformCoordinate( m_Pos ) + aOffset;
+    wxPoint   center = aTransform.TransformCoordinate( GetCenter() ) + aOffset;
+    int       startAngle;
+    int       endAngle;
+    int       pen_size = GetEffectivePenWidth( aPlotter->RenderSettings() );
+    FILL_TYPE fill = aFill ? m_fill : FILL_TYPE::NO_FILL;
 
-    aTransform.MapAngles( &t1, &t2 );
+    CalcAngles( startAngle, endAngle );
+    aTransform.MapAngles( &startAngle, &endAngle );
 
-    if( aFill && m_fill == FILL_TYPE::FILLED_WITH_BG_BODYCOLOR )
+    if( fill == FILL_TYPE::FILLED_WITH_BG_BODYCOLOR )
     {
         aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
-        aPlotter->Arc( pos, -t2, -t1, m_Radius, FILL_TYPE::FILLED_WITH_BG_BODYCOLOR, 0 );
+        aPlotter->Arc( center, -endAngle, -startAngle, GetRadius(), fill, 0 );
+
+        if( pen_size <= 0 )
+            return;
+        else
+            fill = FILL_TYPE::NO_FILL;
     }
 
-    bool already_filled = m_fill == FILL_TYPE::FILLED_WITH_BG_BODYCOLOR;
-    int  pen_size = GetEffectivePenWidth( aPlotter->RenderSettings() );
+    pen_size = std::max( pen_size, aPlotter->RenderSettings()->GetMinPenWidth() );
 
-    if( !already_filled || pen_size > 0 )
-    {
-        aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_DEVICE ) );
-        aPlotter->Arc( pos, -t2, -t1, m_Radius, already_filled ? FILL_TYPE::NO_FILL : m_fill,
-                       pen_size );
-    }
+    aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_DEVICE ) );
+    aPlotter->Arc( center, -endAngle, -startAngle, GetRadius(), fill, pen_size );
 }
 
 
@@ -305,15 +264,12 @@ void LIB_ARC::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset, v
     pos1 = aTransform.TransformCoordinate( m_ArcEnd ) + aOffset;
     pos2 = aTransform.TransformCoordinate( m_ArcStart ) + aOffset;
     posc = aTransform.TransformCoordinate( m_Pos ) + aOffset;
-    int  pt1  = m_t1;
-    int  pt2  = m_t2;
-    bool swap = aTransform.MapAngles( &pt1, &pt2 );
 
-    if( swap )
-    {
-        std::swap( pos1.x, pos2.x );
-        std::swap( pos1.y, pos2.y );
-    }
+    int t1;
+    int t2;
+
+    CalcAngles( t1, t2 );
+    aTransform.MapAngles( &t1, &t2 );
 
     if( forceNoFill || m_fill == FILL_TYPE::NO_FILL )
     {
@@ -324,33 +280,29 @@ void LIB_ARC::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset, v
         if( m_fill == FILL_TYPE::FILLED_WITH_BG_BODYCOLOR )
             color = aSettings->GetLayerColor( LAYER_DEVICE_BACKGROUND );
 
-        GRFilledArc( nullptr, DC, posc.x, posc.y, pt1, pt2, m_Radius, penWidth, color, color );
+        GRFilledArc( nullptr, DC, posc.x, posc.y, t1, t2, GetRadius(), penWidth, color, color );
     }
 }
 
 
 const EDA_RECT LIB_ARC::GetBoundingBox() const
 {
+    int      radius = GetRadius();
     int      minX, minY, maxX, maxY, angleStart, angleEnd;
     EDA_RECT rect;
     wxPoint  nullPoint, startPos, endPos, centerPos;
     wxPoint  normStart = m_ArcStart - m_Pos;
     wxPoint  normEnd   = m_ArcEnd - m_Pos;
 
-    if( ( normStart == nullPoint ) || ( normEnd == nullPoint ) || ( m_Radius == 0 ) )
+    if( normStart == nullPoint || normEnd == nullPoint || radius == 0 )
         return rect;
 
     endPos     = DefaultTransform.TransformCoordinate( m_ArcEnd );
     startPos   = DefaultTransform.TransformCoordinate( m_ArcStart );
     centerPos  = DefaultTransform.TransformCoordinate( m_Pos );
-    angleStart = m_t1;
-    angleEnd   = m_t2;
 
-    if( DefaultTransform.MapAngles( &angleStart, &angleEnd ) )
-    {
-        std::swap( endPos.x, startPos.x );
-        std::swap( endPos.y, startPos.y );
-    }
+    CalcAngles( angleStart, angleEnd );
+    DefaultTransform.MapAngles( &angleStart, &angleEnd );
 
     /* Start with the start and end point of the arc. */
     minX = std::min( startPos.x, endPos.x );
@@ -360,23 +312,23 @@ const EDA_RECT LIB_ARC::GetBoundingBox() const
 
     /* Zero degrees is a special case. */
     if( angleStart == 0 )
-        maxX = centerPos.x + m_Radius;
+        maxX = centerPos.x + radius;
 
     /* Arc end angle wrapped passed 360. */
     if( angleStart > angleEnd )
         angleEnd += 3600;
 
     if( angleStart <= 900 && angleEnd >= 900 )          /* 90 deg */
-        maxY = centerPos.y + m_Radius;
+        maxY = centerPos.y + radius;
 
     if( angleStart <= 1800 && angleEnd >= 1800 )        /* 180 deg */
-        minX = centerPos.x - m_Radius;
+        minX = centerPos.x - radius;
 
     if( angleStart <= 2700 && angleEnd >= 2700 )        /* 270 deg */
-        minY = centerPos.y - m_Radius;
+        minY = centerPos.y - radius;
 
     if( angleStart <= 3600 && angleEnd >= 3600 )        /* 0 deg   */
-        maxX = centerPos.x + m_Radius;
+        maxX = centerPos.x + radius;
 
     rect.SetOrigin( minX, minY );
     rect.SetEnd( maxX, maxY );
@@ -407,7 +359,7 @@ void LIB_ARC::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITE
 wxString LIB_ARC::GetSelectMenuText( EDA_UNITS aUnits ) const
 {
     return wxString::Format( _( "Arc, radius %s" ),
-                             MessageTextFromValue( aUnits, m_Radius ) );
+                             MessageTextFromValue( aUnits, GetRadius() ) );
 }
 
 
@@ -428,6 +380,8 @@ void LIB_ARC::CalcEdit( const wxPoint& aPosition )
 {
 #define sq( x ) pow( x, 2 )
 
+    int radius = GetRadius();
+
     // Edit state 0: drawing: place ArcStart
     // Edit state 1: drawing: place ArcEnd (center calculated for 90-degree subtended angle)
     // Edit state 2: point editing: move ArcStart (center calculated for invariant subtended angle)
@@ -440,14 +394,12 @@ void LIB_ARC::CalcEdit( const wxPoint& aPosition )
         m_ArcStart = aPosition;
         m_ArcEnd = aPosition;
         m_Pos = aPosition;
-        m_Radius = 0;
-        m_t1 = 0;
-        m_t2 = 0;
+        radius = 0;
         return;
 
     case 1:
         m_ArcEnd = aPosition;
-        m_Radius = KiROUND( sqrt( pow( GetLineLength( m_ArcStart, m_ArcEnd ), 2 ) / 2.0 ) );
+        radius = KiROUND( sqrt( sq( GetLineLength( m_ArcStart, m_ArcEnd ) ) / 2.0 ) );
         break;
 
     case 2:
@@ -465,10 +417,10 @@ void LIB_ARC::CalcEdit( const wxPoint& aPosition )
         double chordAfter = sq( v.x ) + sq( v.y );
         double ratio = chordAfter / chordBefore;
 
-        if( ratio > 0 )
+        if( ratio != 0 )
         {
-            m_Radius = int( sqrt( m_Radius * m_Radius * ratio ) ) + 1;
-            m_Radius = std::max( m_Radius, int( sqrt( chordAfter ) / 2 ) + 1 );
+            radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
+                               int( sqrt( chordAfter ) / 2 ) + 1 );
         }
 
         break;
@@ -478,7 +430,7 @@ void LIB_ARC::CalcEdit( const wxPoint& aPosition )
     {
         double chordA = GetLineLength( m_ArcStart, aPosition );
         double chordB = GetLineLength( m_ArcEnd, aPosition );
-        m_Radius = int( ( chordA + chordB ) / 2.0 ) + 1;
+        radius = int( ( chordA + chordB ) / 2.0 ) + 1;
         break;
     }
     }
@@ -491,8 +443,8 @@ void LIB_ARC::CalcEdit( const wxPoint& aPosition )
 
     // Calculate 'd', the vector from the chord midpoint to the center
     wxPoint d;
-    d.x = KiROUND( sqrt( sq( m_Radius ) - sq( l/2 ) ) * ( m_ArcStart.y - m_ArcEnd.y ) / l );
-    d.y = KiROUND( sqrt( sq( m_Radius ) - sq( l/2 ) ) * ( m_ArcEnd.x - m_ArcStart.x ) / l );
+    d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_ArcStart.y - m_ArcEnd.y ) / l );
+    d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_ArcEnd.x - m_ArcStart.x ) / l );
 
     wxPoint c1 = m + d;
     wxPoint c2 = m - d;
@@ -525,63 +477,65 @@ void LIB_ARC::CalcEdit( const wxPoint& aPosition )
         m_Pos = ( GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ) ? c1 : c2;
         break;
     }
-
-    CalcRadiusAngles();
 }
 
 
-void LIB_ARC::CalcRadiusAngles()
+void LIB_ARC::CalcAngles( int& aStartAngle, int& aEndAngle ) const
 {
-    wxPoint centerStartVector = twoPointVector( m_Pos, m_ArcStart );
-    wxPoint centerEndVector   = twoPointVector( m_Pos, m_ArcEnd );
-
-    m_Radius = KiROUND( EuclideanNorm( centerStartVector ) );
+    wxPoint centerStartVector = twoPointVector( GetCenter(), GetStart() );
+    wxPoint centerEndVector   = twoPointVector( GetCenter(), GetEnd() );
 
     // Angles in Eeschema are still integers
-    m_t1 = KiROUND( ArcTangente( centerStartVector.y, centerStartVector.x ) );
-    m_t2 = KiROUND( ArcTangente( centerEndVector.y, centerEndVector.x ) );
+    aStartAngle = KiROUND( ArcTangente( centerStartVector.y, centerStartVector.x ) );
+    aEndAngle = KiROUND( ArcTangente( centerEndVector.y, centerEndVector.x ) );
 
-    NORMALIZE_ANGLE_POS( m_t1 );
-    NORMALIZE_ANGLE_POS( m_t2 );  // angles = 0 .. 3600
+    NORMALIZE_ANGLE_POS( aStartAngle );
+    NORMALIZE_ANGLE_POS( aEndAngle );  // angles = 0 .. 3600
 
     // Restrict angle to less than 180 to avoid PBS display mirror Trace because it is
     // assumed that the arc is less than 180 deg to find orientation after rotate or mirror.
-    if( ( m_t2 - m_t1 ) > 1800 )
-        m_t2 -= 3600;
-    else if( ( m_t2 - m_t1 ) <= -1800 )
-        m_t2 += 3600;
+    if( ( aEndAngle - aStartAngle ) > 1800 )
+        aEndAngle -= 3600;
+    else if( ( aEndAngle - aStartAngle ) <= -1800 )
+        aEndAngle += 3600;
 
-    while( ( m_t2 - m_t1 ) >= 1800 )
+    while( ( aEndAngle - aStartAngle ) >= 1800 )
     {
-        m_t2--;
-        m_t1++;
+        aEndAngle--;
+        aStartAngle++;
     }
 
-    while( ( m_t1 - m_t2 ) >= 1800 )
+    while( ( aStartAngle - aEndAngle ) >= 1800 )
     {
-        m_t2++;
-        m_t1--;
+        aEndAngle++;
+        aStartAngle--;
     }
 
-    NORMALIZE_ANGLE_POS( m_t1 );
+    NORMALIZE_ANGLE_POS( aStartAngle );
 
     if( !IsMoving() )
-        NORMALIZE_ANGLE_POS( m_t2 );
+        NORMALIZE_ANGLE_POS( aEndAngle );
 }
 
 
 VECTOR2I LIB_ARC::CalcMidPoint() const
 {
     VECTOR2D midPoint;
-    double startAngle = static_cast<double>( m_t1 ) / 10.0;
-    double endAngle = static_cast<double>( m_t2 ) / 10.0;
+    int      radius = GetRadius();
+    int      t1;
+    int      t2;
+
+    CalcAngles( t1, t2 );
+
+    double startAngle = static_cast<double>( t1 ) / 10.0;
+    double endAngle = static_cast<double>( t2 ) / 10.0;
 
     if( endAngle < startAngle )
         endAngle -= 360.0;
 
     double midPointAngle = ( ( endAngle - startAngle ) / 2.0 ) + startAngle;
-    double x = cos( DEG2RAD( midPointAngle ) ) * m_Radius;
-    double y = sin( DEG2RAD( midPointAngle ) ) * m_Radius;
+    double x = cos( DEG2RAD( midPointAngle ) ) * radius;
+    double y = sin( DEG2RAD( midPointAngle ) ) * radius;
 
     midPoint.x = KiROUND( x ) + m_Pos.x;
     midPoint.y = KiROUND( y ) + m_Pos.y;
@@ -589,15 +543,3 @@ VECTOR2I LIB_ARC::CalcMidPoint() const
     return midPoint;
 }
 
-
-void LIB_ARC::CalcEndPoints()
-{
-    double startAngle = DEG2RAD( static_cast<double>( m_t1 ) / 10.0 );
-    double endAngle = DEG2RAD( static_cast<double>( m_t2 ) / 10.0 );
-
-    m_ArcStart.x = KiROUND( cos( startAngle ) * m_Radius ) + m_Pos.x;
-    m_ArcStart.y = KiROUND( sin( startAngle ) * m_Radius ) + m_Pos.y;
-
-    m_ArcEnd.x = KiROUND( cos( endAngle ) * m_Radius ) + m_Pos.x;
-    m_ArcEnd.y = KiROUND( sin( endAngle ) * m_Radius ) + m_Pos.y;
-}
diff --git a/eeschema/lib_arc.h b/eeschema/lib_arc.h
index cb74aad590..07f7521f2f 100644
--- a/eeschema/lib_arc.h
+++ b/eeschema/lib_arc.h
@@ -80,14 +80,7 @@ public:
     int GetWidth() const override { return m_Width; }
     void SetWidth( int aWidth ) override { m_Width = aWidth; }
 
-    void SetRadius( int aRadius ) { m_Radius = aRadius; }
-    int GetRadius() const { return m_Radius; }
-
-    void SetFirstRadiusAngle( int aAngle ) { m_t1 = aAngle; }
-    int GetFirstRadiusAngle() const { return m_t1; }
-
-    void SetSecondRadiusAngle( int aAngle ) { m_t2 = aAngle; }
-    int GetSecondRadiusAngle() const { return m_t2; }
+    int GetRadius() const;
 
     wxPoint GetStart() const { return m_ArcStart; }
     void SetStart( const wxPoint& aPoint ) { m_ArcStart = aPoint; }
@@ -95,6 +88,10 @@ public:
     wxPoint GetEnd() const { return m_ArcEnd; }
     void SetEnd( const wxPoint& aPoint ) { m_ArcEnd = aPoint; }
 
+    wxPoint GetCenter() const { return m_Pos; }
+    void SetCenter( const wxPoint& aPoint ) { m_Pos = aPoint; }
+
+
     /**
      * Calculate the arc mid point using the arc start and end angles and radius length.
      *
@@ -106,14 +103,9 @@ public:
     VECTOR2I CalcMidPoint() const;
 
     /**
-     * Calculate the start and end points using the center point and the two angles.
+     * Calculate the start and end angles of an arc using the start, end, and center points.
      */
-    void CalcEndPoints();
-
-    /**
-     * Calculate the radius and angle of an arc using the start, end, and center points.
-     */
-    void CalcRadiusAngles();
+    void CalcAngles( int& aStartAngle, int& aEndAngle ) const;
 
 
     wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
@@ -145,9 +137,6 @@ private:
         ARC_STATUS_OUTLINE,
     };
 
-    int      m_Radius;
-    int      m_t1;              // First radius angle of the arc in 0.1 degrees.
-    int      m_t2;              /* Second radius angle of the arc in 0.1 degrees. */
     wxPoint  m_ArcStart;
     wxPoint  m_ArcEnd;          /* Arc end position. */
     wxPoint  m_Pos;             /* Radius center point. */
diff --git a/eeschema/lib_shape.cpp b/eeschema/lib_shape.cpp
new file mode 100644
index 0000000000..0ef902e5b6
--- /dev/null
+++ b/eeschema/lib_shape.cpp
@@ -0,0 +1,570 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2004-2021 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 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 <sch_draw_panel.h>
+#include <plotter.h>
+#include <base_units.h>
+#include <widgets/msgpanel.h>
+#include <bitmaps.h>
+#include <eda_draw_frame.h>
+#include <general.h>
+#include <lib_shape.h>
+
+
+LIB_SHAPE::LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape, int aDefaultLineWidth,
+                      FILL_T aFillType ) :
+    LIB_ITEM( LIB_SHAPE_T, aParent ),
+    EDA_SHAPE( aShape, aDefaultLineWidth, aFillType )
+{
+}
+
+
+bool LIB_SHAPE::HitTest( const wxPoint& aPosRef, int aAccuracy ) const
+{
+    if( aAccuracy < Mils2iu( MINIMUM_SELECTION_DISTANCE ) )
+        aAccuracy = Mils2iu( MINIMUM_SELECTION_DISTANCE );
+
+    return hitTest( DefaultTransform.TransformCoordinate( aPosRef ), aAccuracy );
+}
+
+
+bool LIB_SHAPE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
+{
+    if( m_flags & (STRUCT_DELETED | SKIP_STRUCT ) )
+        return false;
+
+    return hitTest( aRect, aContained, aAccuracy );
+}
+
+
+EDA_ITEM* LIB_SHAPE::Clone() const
+{
+    return new LIB_SHAPE( *this );
+}
+
+
+int LIB_SHAPE::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const
+{
+    int retv = LIB_ITEM::compare( aOther, aCompareFlags );
+
+    if( retv )
+        return retv;
+
+    return EDA_SHAPE::Compare( &static_cast<const LIB_SHAPE&>( aOther ) );
+}
+
+
+void LIB_SHAPE::Offset( const wxPoint& aOffset )
+{
+    move( aOffset );
+}
+
+
+void LIB_SHAPE::MoveTo( const wxPoint& aPosition )
+{
+    setPosition( aPosition );
+}
+
+
+void LIB_SHAPE::MirrorHorizontal( const wxPoint& aCenter )
+{
+    flip( aCenter, true );
+}
+
+
+void LIB_SHAPE::MirrorVertical( const wxPoint& aCenter )
+{
+    flip( aCenter, false );
+}
+
+
+void LIB_SHAPE::Rotate( const wxPoint& aCenter, bool aRotateCCW )
+{
+    int rot_angle = aRotateCCW ? -900 : 900;
+
+    rotate( aCenter, rot_angle );
+}
+
+
+void LIB_SHAPE::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
+                      const TRANSFORM& aTransform ) const
+{
+    wxPoint start = aTransform.TransformCoordinate( m_start ) + aOffset;
+    wxPoint end = aTransform.TransformCoordinate( m_end ) + aOffset;
+    wxPoint center;
+    int     startAngle;
+    int     endAngle;
+    int     pen_size = GetPenWidth();
+    FILL_T fill = aFill ? m_fill : FILL_TYPE::NO_FILL;
+
+    static std::vector<wxPoint> cornerList;
+
+    if( GetShape() == SHAPE_T::POLYGON )
+    {
+        cornerList.clear();
+
+        for( const VECTOR2I& pt : m_poly.Outline( 0 ).CPoints() )
+            cornerList.push_back( aTransform.TransformCoordinate( (wxPoint) pt ) + aOffset );
+    }
+    else if( GetShape() == SHAPE_T::CURVE )
+    {
+        cornerList.clear();
+
+        for( const wxPoint& pt : m_bezierPoints )
+            cornerList.push_back( aTransform.TransformCoordinate( pt ) + aOffset );
+    }
+    else if( GetShape() == SHAPE_T::ARC )
+    {
+        center = aTransform.TransformCoordinate( getCenter() ) + aOffset;
+        startAngle = GetArcAngleStart();
+        endAngle = GetArcAngleEnd();
+        aTransform.MapAngles( &startAngle, &endAngle );
+    }
+
+    if( fill == FILL_T::FILLED_WITH_BG_BODYCOLOR )
+    {
+        aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
+
+        switch( GetShape() )
+        {
+        case SHAPE_T::ARC:
+            aPlotter->Arc( center, -endAngle, -startAngle, GetRadius(), fill, 0 );
+            break;
+
+        case SHAPE_T::CIRCLE:
+            aPlotter->Circle( start, GetRadius() * 2, fill, 0 );
+            break;
+
+        case SHAPE_T::RECT:
+            aPlotter->Rect( start, end, fill, 0 );
+            break;
+
+        case SHAPE_T::POLYGON:
+        case SHAPE_T::CURVE:
+            aPlotter->PlotPoly( cornerList, fill, 0 );
+            break;
+
+        default:
+            wxFAIL_MSG( "LIB_SHAPE::Plot not implemented for " + SHAPE_T_asString() );
+        }
+
+        if( pen_size <= 0 )
+            return;
+        else
+            fill = FILL_T::NO_FILL;
+    }
+
+    pen_size = std::max( pen_size, aPlotter->RenderSettings()->GetMinPenWidth() );
+
+    aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_DEVICE ) );
+
+    switch( GetShape() )
+    {
+    case SHAPE_T::ARC:
+        aPlotter->Arc( center, -endAngle, -startAngle, GetRadius(), fill, pen_size );
+        break;
+
+    case SHAPE_T::CIRCLE:
+        aPlotter->Circle( start, GetRadius() * 2, fill, pen_size );
+        break;
+
+    case SHAPE_T::RECT:
+        aPlotter->Rect( start, end, fill, pen_size );
+        break;
+
+    case SHAPE_T::POLYGON:
+    case SHAPE_T::CURVE:
+        aPlotter->PlotPoly( cornerList, fill, pen_size );
+        break;
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::Plot not implemented for " + SHAPE_T_asString() );
+    }
+}
+
+
+int LIB_SHAPE::GetPenWidth() const
+{
+    // Historically 0 meant "default width" and negative numbers meant "don't stroke".
+    if( GetWidth() < 0 && GetFillType() != FILL_T::NO_FILL )
+        return 0;
+    else
+        return std::max( GetWidth(), 1 );
+}
+
+
+void LIB_SHAPE::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset,
+                       void* aData, const TRANSFORM& aTransform )
+{
+    bool forceNoFill = static_cast<bool>( aData );
+    int  penWidth = GetPenWidth();
+
+    if( forceNoFill && IsFilled() && penWidth == 0 )
+        return;
+
+    wxDC*    DC = aSettings->GetPrintDC();
+    wxPoint  pt1 = aTransform.TransformCoordinate( m_start ) + aOffset;
+    wxPoint  pt2 = aTransform.TransformCoordinate( m_end ) + aOffset;
+    wxPoint  c;
+    int      t1;
+    int      t2;
+    COLOR4D  color = aSettings->GetLayerColor( LAYER_DEVICE );
+
+    unsigned ptCount = 0;
+    wxPoint* buffer = nullptr;
+
+    if( GetShape() == SHAPE_T::POLYGON )
+    {
+        SHAPE_LINE_CHAIN poly = m_poly.Outline( 0 );
+
+        ptCount = poly.GetPointCount();
+        buffer = new wxPoint[ ptCount ];
+
+        for( unsigned ii = 0; ii < ptCount; ++ii )
+            buffer[ii] = aTransform.TransformCoordinate( (wxPoint) poly.CPoint( ii ) ) + aOffset;
+    }
+    else if( GetShape() == SHAPE_T::CURVE )
+    {
+        ptCount = m_bezierPoints.size();
+        buffer = new wxPoint[ ptCount ];
+
+        for( size_t ii = 0; ii < ptCount; ++ii )
+            buffer[ii] = aTransform.TransformCoordinate( m_bezierPoints[ii] ) + aOffset;
+    }
+    else if( GetShape() == SHAPE_T::ARC )
+    {
+        c = aTransform.TransformCoordinate( getCenter() ) + aOffset;
+        t1 = GetArcAngleStart();
+        t2 = GetArcAngleEnd();
+        aTransform.MapAngles( &t1, &t2 );
+    }
+
+    if( forceNoFill || GetFillType() == FILL_T::NO_FILL )
+    {
+        penWidth = std::max( penWidth, aSettings->GetDefaultPenWidth() );
+
+        switch( GetShape() )
+        {
+        case SHAPE_T::ARC:
+            GRArc1( nullptr, DC, pt2.x, pt2.y, pt1.x, pt1.y, c.x, c.y, penWidth, color );
+            break;
+
+        case SHAPE_T::CIRCLE:
+            GRCircle( nullptr, DC, pt1.x, pt1.y, GetRadius(), penWidth, color );
+            break;
+
+        case SHAPE_T::RECT:
+            GRRect( nullptr, DC, pt1.x, pt1.y, pt2.x, pt2.y, penWidth, color );
+            break;
+
+        case SHAPE_T::POLYGON:
+            GRPoly( nullptr, DC, ptCount, buffer, false, penWidth, color, color );
+            break;
+
+        case SHAPE_T::CURVE:
+            GRPoly( nullptr, DC, ptCount, buffer, false, penWidth, color, color );
+            break;
+
+        default:
+            wxFAIL_MSG( "LIB_SHAPE::print not implemented for " + SHAPE_T_asString() );
+        }
+    }
+    else
+    {
+        if( GetFillType() == FILL_T::FILLED_WITH_BG_BODYCOLOR )
+            color = aSettings->GetLayerColor( LAYER_DEVICE_BACKGROUND );
+
+        switch( GetShape() )
+        {
+        case SHAPE_T::ARC:
+            GRFilledArc( nullptr, DC, c.x, c.y, t1, t2, GetRadius(), penWidth, color, color );
+            break;
+
+        case SHAPE_T::CIRCLE:
+            GRFilledCircle( nullptr, DC, pt1.x, pt1.y, GetRadius(), 0, color, color );
+            break;
+
+        case SHAPE_T::RECT:
+            GRFilledRect( nullptr, DC, pt1.x, pt1.y, pt2.x, pt2.y, penWidth, color, color );
+            break;
+
+        case SHAPE_T::POLYGON:
+            GRPoly( nullptr, DC, ptCount, buffer, true, penWidth, color, color );
+            break;
+
+        case SHAPE_T::CURVE:
+            GRPoly( nullptr, DC, ptCount, buffer, true, penWidth, color, color );
+            break;
+
+        default:
+            wxFAIL_MSG( "LIB_SHAPE::print not implemented for " + SHAPE_T_asString() );
+        }
+    }
+
+    delete[] buffer;
+}
+
+
+const EDA_RECT LIB_SHAPE::GetBoundingBox() const
+{
+    EDA_RECT rect = getBoundingBox();
+
+    rect.RevertYAxis();
+
+    return rect;
+}
+
+
+void LIB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, MSG_PANEL_ITEMS& aList )
+{
+    LIB_ITEM::GetMsgPanelInfo( aFrame, aList );
+
+    ShapeGetMsgPanelInfo( aFrame, aList );
+}
+
+
+wxString LIB_SHAPE::GetSelectMenuText( EDA_UNITS aUnits ) const
+{
+    switch( GetShape() )
+    {
+    case SHAPE_T::ARC:
+        return wxString::Format( _( "Arc, radius %s" ),
+                                 MessageTextFromValue( aUnits, GetRadius() ) );
+
+    case SHAPE_T::CIRCLE:
+        return wxString::Format( _( "Circle, radius %s" ),
+                                 MessageTextFromValue( aUnits, GetRadius() ) );
+
+    case SHAPE_T::RECT:
+        return wxString::Format( _( "Rectangle, width %s height %s" ),
+                                 MessageTextFromValue( aUnits, std::abs( m_start.x - m_end.x ) ),
+                                 MessageTextFromValue( aUnits, std::abs( m_start.y - m_end.y ) ) );
+
+    case SHAPE_T::POLYGON:
+        return wxString::Format( _( "Polyline, %d points" ),
+                                 int( m_poly.Outline( 0 ).GetPointCount() ) );
+
+    case SHAPE_T::CURVE:
+        return wxString::Format( _( "Bezier Curve, %d points" ),
+                                 int( m_bezierPoints.size() ) );
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::GetSelectMenuText unimplemented for " + SHAPE_T_asString() );
+        return wxEmptyString;
+    }
+}
+
+
+BITMAPS LIB_SHAPE::GetMenuImage() const
+{
+    switch( GetShape() )
+    {
+    case SHAPE_T::SEGMENT:  return BITMAPS::add_line;
+    case SHAPE_T::ARC:      return BITMAPS::add_arc;
+    case SHAPE_T::CIRCLE:   return BITMAPS::add_circle;
+    case SHAPE_T::RECT:     return BITMAPS::add_rectangle;
+    case SHAPE_T::POLYGON:  return BITMAPS::add_graphical_segments;
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::GetMenuImage unimplemented for " + SHAPE_T_asString() );
+        return BITMAPS::question_mark;
+    }
+}
+
+
+void LIB_SHAPE::BeginEdit( const wxPoint& aPosition )
+{
+    switch( GetShape() )
+    {
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::CIRCLE:
+    case SHAPE_T::RECT:
+        SetPosition( aPosition );
+        SetEnd( aPosition );
+        break;
+
+    case SHAPE_T::ARC:
+        SetStart( aPosition );
+        SetEnd( aPosition );
+        SetEditState( 1 );
+        break;
+
+    case SHAPE_T::POLYGON:
+        // Start and end of the first segment (co-located for now)
+        m_poly.Outline( 0 ).Append( aPosition );
+        m_poly.Outline( 0 ).Append( aPosition, true );
+        break;
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::BeginEdit not implemented for " + SHAPE_T_asString() );
+    }
+}
+
+
+bool LIB_SHAPE::ContinueEdit( const wxPoint& aPosition )
+{
+    switch( GetShape() )
+    {
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::CIRCLE:
+    case SHAPE_T::RECT:
+        return false;
+
+    case SHAPE_T::POLYGON:
+    {
+        SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
+
+        // do not add zero-length segments
+        if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
+            poly.Append( aPosition );
+    }
+        return true;
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::ContinueEdit not implemented for " + SHAPE_T_asString() );
+        return false;
+    }
+}
+
+
+void LIB_SHAPE::CalcEdit( const wxPoint& aPosition )
+{
+#define sq( x ) pow( x, 2 )
+
+    switch( GetShape() )
+    {
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::CIRCLE:
+    case SHAPE_T::RECT:
+        SetEnd( aPosition );
+        break;
+
+    case SHAPE_T::POLYGON:
+        m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
+        break;
+
+    case SHAPE_T::ARC:
+        // Edit state 0: drawing: place start
+        // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
+        // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
+        // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
+        // Edit state 4: point edit: move center
+        // Edit state 5: point edit: move arc-mid-point
+
+        switch( m_editState )
+        {
+        case 0:
+            SetArcGeometry( aPosition, aPosition, aPosition );
+            return;
+
+        case 1:
+        {
+            wxPoint start = GetStart();
+            wxPoint end = aPosition;
+            wxPoint center = CalcArcCenter( start, end, 90.0 );
+            wxPoint mid = (wxPoint) CalcArcMid( start, end, center, true );
+
+            SetArcGeometry( start, mid, aPosition );
+        }
+            break;
+
+        case 2:
+        {
+            wxPoint delta = aPosition - GetStart();
+
+            SetArcGeometry( aPosition, GetArcMid() + delta/2, GetEnd() );
+        }
+            break;
+
+        case 3:
+        {
+            wxPoint delta = aPosition - GetEnd();
+
+            SetArcGeometry( GetStart(), GetArcMid() + delta/2, aPosition );
+        }
+            break;
+
+        case 4:
+            MoveTo( aPosition );
+            break;
+
+        case 5:
+            SetArcGeometry( GetStart(), aPosition, GetEnd() );
+            break;
+
+        }
+
+        break;
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::CalcEdit not implemented for " + SHAPE_T_asString() );
+    }
+}
+
+
+void LIB_SHAPE::EndEdit()
+{
+    switch( GetShape() )
+    {
+    case SHAPE_T::ARC:
+    case SHAPE_T::SEGMENT:
+    case SHAPE_T::CIRCLE:
+    case SHAPE_T::RECT:
+        break;
+
+    case SHAPE_T::POLYGON:
+    {
+        SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
+
+        // do not include last point twice
+        if( poly.GetPointCount() > 2 )
+        {
+            if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
+                poly.Remove( poly.GetPointCount() - 1 );
+        }
+    }
+        break;
+
+    default:
+        wxFAIL_MSG( "LIB_SHAPE::EndEdit not implemented for " + SHAPE_T_asString() );
+    }
+}
+
+
+void LIB_SHAPE::AddPoint( const wxPoint& aPosition )
+{
+    if( GetShape() == SHAPE_T::POLYGON )
+    {
+        if( m_poly.IsEmpty() )
+            m_poly.NewOutline();
+
+        m_poly.Outline( 0 ).Append( aPosition, true );
+    }
+    else
+    {
+        wxFAIL_MSG( "LIB_SHAPE::AddPoint not implemented for " + SHAPE_T_asString() );
+    }
+}
+
+
diff --git a/eeschema/lib_shape.h b/eeschema/lib_shape.h
new file mode 100644
index 0000000000..197e769857
--- /dev/null
+++ b/eeschema/lib_shape.h
@@ -0,0 +1,108 @@
+/*
+ * 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) 2004-2021 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef LIB_SHAPE_H
+#define LIB_SHAPE_H
+
+#include <lib_item.h>
+#include <eda_shape.h>
+
+
+class LIB_SHAPE : public LIB_ITEM, public EDA_SHAPE
+{
+public:
+    LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape, int aLineWidth = 0,
+               FILL_T aFillType = FILL_T::NO_FILL );
+
+    // Do not create a copy constructor.  The one generated by the compiler is adequate.
+
+    ~LIB_SHAPE() { }
+
+    wxString GetClass() const override
+    {
+        return wxT( "LIB_SHAPE" );
+    }
+
+    wxString GetTypeName() const override
+    {
+        return ShowShape();
+    }
+
+    bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override;
+    bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override;
+
+    int GetPenWidth() const override;
+
+    const EDA_RECT GetBoundingBox() const override;
+
+    void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
+
+    void BeginEdit( const wxPoint& aStartPoint ) override;
+    bool ContinueEdit( const wxPoint& aPosition ) override;
+    void CalcEdit( const wxPoint& aPosition ) override;
+    void EndEdit() override;
+
+    void AddPoint( const wxPoint& aPosition );
+
+    void Offset( const wxPoint& aOffset ) override;
+
+    void MoveTo( const wxPoint& aPosition ) override;
+
+    wxPoint GetPosition() const override { return getPosition(); }
+    void SetPosition( const wxPoint& aPosition ) override { setPosition( aPosition ); }
+
+    void MirrorHorizontal( const wxPoint& aCenter ) override;
+    void MirrorVertical( const wxPoint& aCenter ) override;
+    void Rotate( const wxPoint& aCenter, bool aRotateCCW = true ) override;
+
+    void Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
+               const TRANSFORM& aTransform ) const override;
+
+    wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
+
+    BITMAPS GetMenuImage() const override;
+
+    EDA_ITEM* Clone() const override;
+
+private:
+    /**
+     * @copydoc LIB_ITEM::compare()
+     *
+     * The circle specific sort order is as follows:
+     *      - Circle horizontal (X) position.
+     *      - Circle vertical (Y) position.
+     *      - Circle radius.
+     */
+    int compare( const LIB_ITEM& aOther,
+            LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override;
+
+    void print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset, void* aData,
+                const TRANSFORM& aTransform ) override;
+
+    double getParentOrientation() const override { return 0.0; }
+    wxPoint getParentPosition() const override { return wxPoint(); }
+};
+
+
+#endif    // LIB_SHAPE_H
diff --git a/eeschema/sch_item.cpp b/eeschema/sch_item.cpp
index 38d89e4824..166c5e10be 100644
--- a/eeschema/sch_item.cpp
+++ b/eeschema/sch_item.cpp
@@ -196,7 +196,7 @@ SCH_CONNECTION* SCH_ITEM::InitializeConnection( const SCH_SHEET_PATH& aSheet,
 
 void SCH_ITEM::SwapData( SCH_ITEM* aItem )
 {
-    wxFAIL_MSG( wxT( "SwapData() method not implemented for class " ) + GetClass() );
+    UNIMPLEMENTED_FOR( GetClass() );
 }
 
 
diff --git a/eeschema/sch_painter.cpp b/eeschema/sch_painter.cpp
index 77e5557251..a5b188ae84 100644
--- a/eeschema/sch_painter.cpp
+++ b/eeschema/sch_painter.cpp
@@ -579,37 +579,14 @@ void SCH_PAINTER::draw( const LIB_ARC *aArc, int aLayer )
 
     if( setDeviceColors( aArc, aLayer ) )
     {
-        int sai = aArc->GetFirstRadiusAngle();
-        int eai = aArc->GetSecondRadiusAngle();
+        int startAngle;
+        int endAngle;
+        aArc->CalcAngles( startAngle, endAngle );
 
-        /**
-         * This accounts for an oddity in the old library format, where the symbol
-         * is overdefined.  The previous draw (based on wxwidgets) used start point and end
-         * point and always drew counter-clockwise.  The new GAL draw takes center, radius and
-         * start/end angles.  All of these points were stored in the file, so we need to mimic the
-         * swapping of start/end points rather than using the stored angles in order to properly map
-         * edge cases.
-         *
-         * todo(v6): Remove this hack when we update the file format and do translation on loading.
-         */
-        if( !TRANSFORM().MapAngles( &sai, &eai ) )
-        {
-            LIB_ARC new_arc( *aArc );
+        TRANSFORM().MapAngles( &startAngle, &endAngle );
 
-            new_arc.SetStart( aArc->GetEnd() );
-            new_arc.SetEnd( aArc->GetStart() );
-            new_arc.CalcRadiusAngles();
-            sai = new_arc.GetFirstRadiusAngle();
-            eai = new_arc.GetSecondRadiusAngle();
-            TRANSFORM().MapAngles( &sai, &eai );
-        }
-
-        double sa = (double) sai * M_PI / 1800.0;
-        double ea = (double) eai * M_PI / 1800.0 ;
-
-        VECTOR2D pos = mapCoords( aArc->GetPosition() );
-
-        m_gal->DrawArc( pos, aArc->GetRadius(), sa, ea );
+        m_gal->DrawArc( mapCoords( aArc->GetCenter() ), aArc->GetRadius(),
+                        DECIDEG2RAD( startAngle ), DECIDEG2RAD( endAngle ) );
     }
 }
 
diff --git a/eeschema/sch_plugins/altium/sch_altium_plugin.cpp b/eeschema/sch_plugins/altium/sch_altium_plugin.cpp
index 4dc5733bdb..c14328d707 100644
--- a/eeschema/sch_plugins/altium/sch_altium_plugin.cpp
+++ b/eeschema/sch_plugins/altium/sch_altium_plugin.cpp
@@ -62,7 +62,7 @@
 #include <wx/log.h>
 #include <wx/zstream.h>
 #include <wx/wfstream.h>
-
+#include <trigo.h>
 
 const wxPoint GetRelativePosition( const wxPoint& aPosition, const SCH_SYMBOL* aSymbol )
 {
@@ -1350,21 +1350,23 @@ void SCH_ALTIUM_PLUGIN::ParseArc( const std::map<wxString, wxString>& aPropertie
         }
         else
         {
-            if( fmod( 360.0 + elem.endAngle - elem.startAngle, 360.0 ) > 180.0 )
-            {
-                m_reporter->Report( _( "Arcs in symbols cannot exceed 180 degrees." ),
-                                    RPT_SEVERITY_ERROR );
-                return;
-            }
-
             LIB_ARC* arc = new LIB_ARC( libSymbolIt->second );
             libSymbolIt->second->AddDrawItem( arc );
             arc->SetUnit( elem.ownerpartid );
-            arc->SetPosition( GetRelativePosition( elem.center + m_sheetOffset, symbol ) );
-            arc->SetRadius( elem.radius );
-            arc->SetFirstRadiusAngle( elem.startAngle * 10. );
-            arc->SetSecondRadiusAngle( elem.endAngle * 10. );
-            arc->CalcEndPoints();
+
+            arc->SetCenter( GetRelativePosition( elem.center + m_sheetOffset, symbol ) );
+
+            wxPoint arcStart( elem.radius, 0 );
+            RotatePoint( &arcStart.x, &arcStart.y, -elem.startAngle * 10.0 );
+            arcStart += arc->GetCenter();
+            arc->SetStart( arcStart );
+
+            wxPoint arcEnd( elem.radius, 0 );
+            RotatePoint( &arcEnd.x, &arcEnd.y, -elem.endAngle * 10.0 );
+            arcEnd += arc->GetCenter();
+            arc->SetEnd( arcEnd );
+
+            arc->SetWidth( elem.lineWidth );
         }
     }
 }
diff --git a/eeschema/sch_plugins/cadstar/cadstar_sch_archive_loader.cpp b/eeschema/sch_plugins/cadstar/cadstar_sch_archive_loader.cpp
index 7528ef7023..aa870f5370 100644
--- a/eeschema/sch_plugins/cadstar/cadstar_sch_archive_loader.cpp
+++ b/eeschema/sch_plugins/cadstar/cadstar_sch_archive_loader.cpp
@@ -1590,15 +1590,16 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadSymDefIntoLibrary( const SYMDEF_ID& aSymdef
 }
 
 
-void CADSTAR_SCH_ARCHIVE_LOADER::loadLibrarySymbolShapeVertices(
-        const std::vector<VERTEX>& aCadstarVertices, wxPoint aSymbolOrigin, LIB_SYMBOL* aSymbol,
-        int aGateNumber, int aLineThickness )
+void CADSTAR_SCH_ARCHIVE_LOADER::loadLibrarySymbolShapeVertices( const std::vector<VERTEX>& aCadstarVertices,
+                                                                 wxPoint aSymbolOrigin,
+                                                                 LIB_SYMBOL* aSymbol,
+                                                                 int aGateNumber,
+                                                                 int aLineThickness )
 {
     const VERTEX* prev = &aCadstarVertices.at( 0 );
     const VERTEX* cur;
 
-    wxASSERT_MSG(
-            prev->Type == VERTEX_TYPE::POINT, "First vertex should always be a point vertex" );
+    wxASSERT_MSG( prev->Type == VERTEX_TYPE::POINT, "First vertex should always be a point." );
 
     for( size_t i = 1; i < aCadstarVertices.size(); i++ )
     {
@@ -1651,7 +1652,6 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadLibrarySymbolShapeVertices(
                 ( (LIB_ARC*) segment )->SetEnd( endPoint );
             }
 
-            ( (LIB_ARC*) segment )->CalcRadiusAngles();
             break;
         }
 
@@ -2778,7 +2778,6 @@ LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::getScaledLibPart( const LIB_SYMBOL* aSym
             arc.SetPosition( scalePt( arc.GetPosition() ) );
             arc.SetStart( scalePt( arc.GetStart() ) );
             arc.SetEnd( scalePt( arc.GetEnd() ) );
-            arc.CalcRadiusAngles(); // Maybe not needed?
         }
         break;
 
diff --git a/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp b/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp
index 96dee2ce36..30658457a7 100644
--- a/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp
+++ b/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp
@@ -1581,7 +1581,7 @@ EAGLE_LIBRARY* SCH_EAGLE_PLUGIN::loadLibrary( wxXmlNode* aLibraryNode,
 
 
 bool SCH_EAGLE_PLUGIN::loadSymbol( wxXmlNode* aSymbolNode, std::unique_ptr<LIB_SYMBOL>& aSymbol,
-        EDEVICE* aDevice, int aGateNumber, const wxString& aGateName )
+                                   EDEVICE* aDevice, int aGateNumber, const wxString& aGateName )
 {
     wxString               symbolName = aSymbolNode->GetAttribute( "name" );
     std::vector<LIB_ITEM*> items;
@@ -1831,8 +1831,6 @@ LIB_ITEM* SCH_EAGLE_PLUGIN::loadSymbolWire( std::unique_ptr<LIB_SYMBOL>& aSymbol
             arc->SetEnd( begin );
         }
 
-        arc->SetRadius( radius );
-        arc->CalcRadiusAngles();
         arc->SetUnit( aGateNumber );
 
         return (LIB_ITEM*) arc.release();
@@ -1891,25 +1889,11 @@ LIB_PIN* SCH_EAGLE_PLUGIN::loadPin( std::unique_ptr<LIB_SYMBOL>& aSymbol, wxXmlN
 
     switch( roti )
     {
-    default:
-        wxASSERT_MSG( false, wxString::Format( "Unhandled orientation (%d degrees)", roti ) );
-        KI_FALLTHROUGH;
-
-    case 0:
-        pin->SetOrientation( 'R' );
-        break;
-
-    case 90:
-        pin->SetOrientation( 'U' );
-        break;
-
-    case 180:
-        pin->SetOrientation( 'L' );
-        break;
-
-    case 270:
-        pin->SetOrientation( 'D' );
-        break;
+    case 0:   pin->SetOrientation( 'R' ); break;
+    case 90:  pin->SetOrientation( 'U' ); break;
+    case 180: pin->SetOrientation( 'L' ); break;
+    case 270: pin->SetOrientation( 'D' ); break;
+    default:  wxFAIL_MSG( wxString::Format( "Unhandled orientation (%d degrees).", roti ) );
     }
 
     pin->SetLength( Mils2iu( 300 ) );  // Default pin length when not defined.
@@ -1959,17 +1943,11 @@ LIB_PIN* SCH_EAGLE_PLUGIN::loadPin( std::unique_ptr<LIB_SYMBOL>& aSymbol, wxXmlN
         wxString function = aEPin->function.Get();
 
         if( function == "dot" )
-        {
             pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
-        }
         else if( function == "clk" )
-        {
             pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
-        }
         else if( function == "dotclk" )
-        {
             pin->SetShape( GRAPHIC_PINSHAPE::INVERTED_CLOCK );
-        }
     }
 
     return pin.release();
diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp
index d9df85c580..f62c7530f9 100644
--- a/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp
+++ b/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp
@@ -916,6 +916,8 @@ LIB_ARC* SCH_SEXPR_PARSER::parseArc()
     wxPoint midPoint;
     wxPoint endPoint;
     wxPoint pos;
+    int startAngle;
+    int endAngle;
     FILL_PARAMS fill;
     bool hasMidPoint = false;
     std::unique_ptr<LIB_ARC> arc = std::make_unique<LIB_ARC>( nullptr );
@@ -964,19 +966,16 @@ LIB_ARC* SCH_SEXPR_PARSER::parseArc()
                     break;
 
                 case T_length:
-                    arc->SetRadius( parseInternalUnits( "radius length" ) );
+                    parseInternalUnits( "radius length" );
                     NeedRIGHT();
                     break;
 
                 case T_angles:
                 {
-                    int angle1 = KiROUND( parseDouble( "start radius angle" ) * 10.0 );
-                    int angle2 = KiROUND( parseDouble( "end radius angle" ) * 10.0 );
-
-                    NORMALIZE_ANGLE_POS( angle1 );
-                    NORMALIZE_ANGLE_POS( angle2 );
-                    arc->SetFirstRadiusAngle( angle1 );
-                    arc->SetSecondRadiusAngle( angle2 );
+                    startAngle = KiROUND( parseDouble( "start radius angle" ) * 10.0 );
+                    endAngle = KiROUND( parseDouble( "end radius angle" ) * 10.0 );
+                    NORMALIZE_ANGLE_POS( startAngle );
+                    NORMALIZE_ANGLE_POS( endAngle );
                     NeedRIGHT();
                     break;
                 }
@@ -1016,13 +1015,25 @@ LIB_ARC* SCH_SEXPR_PARSER::parseArc()
 
     if( hasMidPoint )
     {
-        VECTOR2I center = GetArcCenter( arc->GetStart(), midPoint, arc->GetEnd() );
+        VECTOR2I center = CalcArcCenter( arc->GetStart(), midPoint, arc->GetEnd());
 
-        arc->SetPosition( wxPoint( center.x, center.y ) );
-
-        // @todo Calculate the radius.
-
-        arc->CalcRadiusAngles();
+        arc->SetCenter( (wxPoint) center );
+    }
+    else
+    {
+        /**
+         * This accounts for an oddity in the old library format, where the symbol is overdefined.
+         * The previous draw (based on wxwidgets) used start point and end point and always drew
+         * counter-clockwise.  The new GAL draw takes center, radius and start/end angles.  All of
+         * these points were stored in the file, so we need to mimic the swapping of start/end
+         * points rather than using the stored angles in order to properly map edge cases.
+         */
+        if( !TRANSFORM().MapAngles( &startAngle, &endAngle ) )
+        {
+            wxPoint temp = arc->GetStart();
+            arc->SetStart( arc->GetEnd() );
+            arc->SetEnd( temp );
+        }
     }
 
     return arc.release();
diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp
index d3b24822dc..6bed63dac6 100644
--- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp
+++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp
@@ -1810,13 +1810,14 @@ void SCH_SEXPR_PLUGIN_CACHE::saveArc( LIB_ARC* aArc, OUTPUTFORMATTER& aFormatter
 {
     wxCHECK_RET( aArc && aArc->Type() == LIB_ARC_T, "Invalid LIB_ARC object." );
 
-    int x1 = aArc->GetFirstRadiusAngle();
+    int x1;
+    int x2;
+
+    aArc->CalcAngles( x1, x2 );
 
     if( x1 > 1800 )
         x1 -= 3600;
 
-    int x2 = aArc->GetSecondRadiusAngle();
-
     if( x2 > 1800 )
         x2 -= 3600;
 
diff --git a/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp b/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp
index 5e74497c72..bf4f4b6db1 100644
--- a/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp
+++ b/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp
@@ -3280,15 +3280,13 @@ LIB_ARC* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbol,
     center.y = Mils2Iu( parseInt( aReader, line, &line ) );
 
     arc->SetPosition( center );
-    arc->SetRadius( Mils2Iu( parseInt( aReader, line, &line ) ) );
 
+    int radius = Mils2Iu( parseInt( aReader, line, &line ) );
     int angle1 = parseInt( aReader, line, &line );
     int angle2 = parseInt( aReader, line, &line );
 
     NORMALIZE_ANGLE_POS( angle1 );
     NORMALIZE_ANGLE_POS( angle2 );
-    arc->SetFirstRadiusAngle( angle1 );
-    arc->SetSecondRadiusAngle( angle2 );
 
     arc->SetUnit( parseInt( aReader, line, &line ) );
     arc->SetConvert( parseInt( aReader, line, &line ) );
@@ -3316,17 +3314,31 @@ LIB_ARC* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbol,
     {
         // Actual Coordinates of arc ends are not read from file
         // (old library), calculate them
-        wxPoint arcStart( arc->GetRadius(), 0 );
-        wxPoint arcEnd( arc->GetRadius(), 0 );
+        wxPoint arcStart( radius, 0 );
+        wxPoint arcEnd( radius, 0 );
 
         RotatePoint( &arcStart.x, &arcStart.y, -angle1 );
-        arcStart += arc->GetPosition();
+        arcStart += arc->GetCenter();
         arc->SetStart( arcStart );
         RotatePoint( &arcEnd.x, &arcEnd.y, -angle2 );
-        arcEnd += arc->GetPosition();
+        arcEnd += arc->GetCenter();
         arc->SetEnd( arcEnd );
     }
 
+    /**
+     * This accounts for an oddity in the old library format, where the symbol is overdefined.
+     * The previous draw (based on wxwidgets) used start point and end point and always drew
+     * counter-clockwise.  The new GAL draw takes center, radius and start/end angles.  All of
+     * these points were stored in the file, so we need to mimic the swapping of start/end
+     * points rather than using the stored angles in order to properly map edge cases.
+     */
+    if( !TRANSFORM().MapAngles( &angle1, &angle2 ) )
+    {
+        wxPoint temp = arc->GetStart();
+        arc->SetStart( arc->GetEnd() );
+        arc->SetEnd( temp );
+    }
+
     return arc;
 }
 
@@ -3955,43 +3967,41 @@ void SCH_LEGACY_PLUGIN_CACHE::saveArc( LIB_ARC* aArc, OUTPUTFORMATTER& aFormatte
 {
     wxCHECK_RET( aArc && aArc->Type() == LIB_ARC_T, "Invalid LIB_ARC object." );
 
-    int x1 = aArc->GetFirstRadiusAngle();
+    int x1;
+    int x2;
+
+    aArc->CalcAngles( x1, x2 );
 
     if( x1 > 1800 )
         x1 -= 3600;
 
-    int x2 = aArc->GetSecondRadiusAngle();
-
     if( x2 > 1800 )
         x2 -= 3600;
 
     aFormatter.Print( 0, "A %d %d %d %d %d %d %d %d %c %d %d %d %d\n",
                       Iu2Mils( aArc->GetPosition().x ), Iu2Mils( aArc->GetPosition().y ),
                       Iu2Mils( aArc->GetRadius() ), x1, x2, aArc->GetUnit(), aArc->GetConvert(),
-                      Iu2Mils( aArc->GetWidth() ),
-                               fill_tab[ static_cast<int>( aArc->GetFillMode() ) ],
+                      Iu2Mils( aArc->GetWidth() ), fill_tab[ (int) aArc->GetFillMode() ],
                       Iu2Mils( aArc->GetStart().x ), Iu2Mils( aArc->GetStart().y ),
                       Iu2Mils( aArc->GetEnd().x ), Iu2Mils( aArc->GetEnd().y ) );
 }
 
 
-void SCH_LEGACY_PLUGIN_CACHE::saveBezier( LIB_BEZIER* aBezier,
-                                          OUTPUTFORMATTER& aFormatter )
+void SCH_LEGACY_PLUGIN_CACHE::saveBezier( LIB_BEZIER* aBezier, OUTPUTFORMATTER& aFormatter )
 {
     wxCHECK_RET( aBezier && aBezier->Type() == LIB_BEZIER_T, "Invalid LIB_BEZIER object." );
 
     aFormatter.Print( 0, "B %u %d %d %d", (unsigned)aBezier->GetPoints().size(),
                       aBezier->GetUnit(), aBezier->GetConvert(), Iu2Mils( aBezier->GetWidth() ) );
 
-    for( const auto& pt : aBezier->GetPoints() )
+    for( const wxPoint& pt : aBezier->GetPoints() )
         aFormatter.Print( 0, " %d %d", Iu2Mils( pt.x ), Iu2Mils( pt.y ) );
 
     aFormatter.Print( 0, " %c\n", fill_tab[static_cast<int>( aBezier->GetFillMode() )] );
 }
 
 
-void SCH_LEGACY_PLUGIN_CACHE::saveCircle( LIB_CIRCLE* aCircle,
-                                          OUTPUTFORMATTER& aFormatter )
+void SCH_LEGACY_PLUGIN_CACHE::saveCircle( LIB_CIRCLE* aCircle, OUTPUTFORMATTER& aFormatter )
 {
     wxCHECK_RET( aCircle && aCircle->Type() == LIB_CIRCLE_T, "Invalid LIB_CIRCLE object." );
 
diff --git a/include/eda_shape.h b/include/eda_shape.h
index 2a4a6f33ae..1f3acf460f 100644
--- a/include/eda_shape.h
+++ b/include/eda_shape.h
@@ -92,28 +92,9 @@ public:
     void SetWidth( int aWidth ) { m_width = aWidth; }
     int GetWidth() const        { return m_width; }
 
-    /**
-     * Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
-     *
-     * @param aAngle is tenths of degrees, but will soon be degrees.
-     * @param aUpdateEnd set to true to update also arc end coordinates m_thirdPoint, so must
-     *                   be called after setting m_Start and m_End.
-     */
-    virtual void SetAngle( double aAngle, bool aUpdateEnd = true );
-    double GetAngle() const { return m_angle; }
-
     void SetShape( SHAPE_T aShape )                 { m_shape = aShape; }
     SHAPE_T GetShape() const                        { return m_shape; }
 
-    void SetBezierC1( const wxPoint& aPoint )    { m_bezierC1 = aPoint; }
-    const wxPoint& GetBezierC1() const           { return m_bezierC1; }
-
-    void SetBezierC2( const wxPoint& aPoint )    { m_bezierC2 = aPoint; }
-    const wxPoint& GetBezierC2() const           { return m_bezierC2; }
-
-    void SetShapePos( const wxPoint& aPos );
-    wxPoint GetShapePos() const;
-
     /**
      * Return the starting point of the graphic.
      */
@@ -135,22 +116,26 @@ public:
     void SetEndX( int x )                   { m_end.x = x; }
 
     /**
-     * Function GetThirdPoint
-     * returns the third point point of the graphic
+     * Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
+     *
+     * @param aAngle is tenths of degrees, but will soon be degrees.
      */
-    const wxPoint& GetThirdPoint() const           { return m_thirdPoint; }
-    int GetThirdPointY()                           { return m_thirdPoint.y; }
-    int GetThirdPointX()                           { return m_thirdPoint.x; }
-    void SetThirdPoint( const wxPoint& aPoint )    { m_thirdPoint = aPoint; }
-    void SetThirdPointY( int y )                   { m_thirdPoint.y = y; }
-    void SetThirdPointX( int x )                   { m_thirdPoint.x = x; }
+    virtual void SetArcAngle( double aAngle );
+    virtual void SetArcAngleAndEnd( double aAngle );
+    double GetArcAngle() const { return m_arcAngle; }
+
+    void SetBezierC1( const wxPoint& aPt )  { m_bezierC1 = aPt; }
+    const wxPoint& GetBezierC1() const      { return m_bezierC1; }
+
+    void SetBezierC2( const wxPoint& aPt )  { m_bezierC2 = aPt; }
+    const wxPoint& GetBezierC2() const      { return m_bezierC2; }
+
+    wxPoint getCenter() const;
+    void SetCenter( const wxPoint& aCenter );
 
     // Some attributes are read only, since they are derived from m_Start, m_End, and m_Angle.
     // No Set...() function for these attributes.
 
-    wxPoint getCenter() const;
-    wxPoint GetArcStart() const      { return m_end; }
-    wxPoint GetArcEnd() const;
     wxPoint GetArcMid() const;
     std::vector<wxPoint> GetRectCorners() const;
 
@@ -164,35 +149,8 @@ public:
      */
     double GetArcAngleEnd() const;
 
-    /**
-     * Return the radius of this item.
-     *
-     * Has meaning only for arcs and circles.
-     */
     int GetRadius() const;
 
-    /**
-     * Initialize the start arc point.
-     *
-     * Can be used for circles to initialize one point of the cicumference.
-     */
-    void SetArcStart( const wxPoint& aArcStartPoint )
-    {
-        m_end = aArcStartPoint;
-    }
-
-    /**
-     * Initialize the end arc point.
-     *
-     * Can be used for circles to initialize one point of the cicumference.
-     */
-    void SetArcEnd( const wxPoint& aArcEndPoint )
-    {
-        m_thirdPoint = aArcEndPoint;
-    }
-
-    void SetArcCenter( const wxPoint& aCenterPoint ) { m_start = aCenterPoint; }
-
     /**
      * Set the three controlling points for an arc.
      *
@@ -278,6 +236,9 @@ public:
     int Compare( const EDA_SHAPE* aOther ) const;
 
 protected:
+    void setPosition( const wxPoint& aPos );
+    wxPoint getPosition() const;
+
     void move( const wxPoint& aMoveVector );
     void rotate( const wxPoint& aRotCentre, double aAngle );
     void flip( const wxPoint& aCentre, bool aFlipLeftRight );
@@ -300,11 +261,11 @@ protected:
     SHAPE_T              m_shape;        // Shape: line, Circle, Arc
     int                  m_width;        // thickness of lines ...
     bool                 m_filled;       // Pretty much what it says on the tin...
-    wxPoint              m_start;        // Line start point or Circle and Arc center
-    wxPoint              m_end;          // Line end point or circle and arc start point
+    wxPoint              m_start;        // Line start point or Circle center
+    wxPoint              m_end;          // Line end point or Circle 3 o'clock point
 
-    wxPoint              m_thirdPoint;   // Used only for Arcs: arc end point
-    double               m_angle;        // Used only for Arcs: Arc angle in 1/10 deg
+    wxPoint              m_arcCenter;    // Used only for Arcs: arc end point
+    double               m_arcAngle;     // Used only for Arcs: Arc angle in 1/10 deg
 
     wxPoint              m_bezierC1;     // Bezier Control Point 1
     wxPoint              m_bezierC2;     // Bezier Control Point 2
diff --git a/include/macros.h b/include/macros.h
index ff888f02f2..bc1d6d3d80 100644
--- a/include/macros.h
+++ b/include/macros.h
@@ -117,4 +117,7 @@ static inline wxString FROM_UTF8( const char* cstring )
     return line;
 }
 
+#define UNIMPLEMENTED_FOR( type ) \
+        wxFAIL_MSG( wxString::Format( "%s: unimplemented for %s", __FUNCTION__, type ) )
+
 #endif // MACROS_H
diff --git a/libs/kimath/include/trigo.h b/libs/kimath/include/trigo.h
index 95f904085a..4d33746ce8 100644
--- a/libs/kimath/include/trigo.h
+++ b/libs/kimath/include/trigo.h
@@ -106,15 +106,15 @@ void RotatePoint( double *pX, double *pY, double cx, double cy, double angle );
  * @param aEnd The ending point of the circle (equivalent to aStart)
  * @return The center of the circle
  */
-const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
-const VECTOR2D GetArcCenter( const VECTOR2D& aStart, const VECTOR2D& aMid, const VECTOR2D& aEnd );
-const wxPoint GetArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd );
-const wxPoint GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle );
+const VECTOR2I CalcArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
+const VECTOR2D CalcArcCenter( const VECTOR2D& aStart, const VECTOR2D& aMid, const VECTOR2D& aEnd );
+const wxPoint CalcArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd );
+const wxPoint CalcArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle );
 
 /**
  * Return the subtended angle for a given arc.
  */
-double GetArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
+double CalcArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
 
 /**
  * Return the middle point of an arc, half-way between aStart and aEnd. There are two possible
@@ -127,8 +127,8 @@ double GetArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I
  * @param aMinArcAngle If true, returns the point that results in the smallest arc angle.
  * @return The middle point of the arc
 */
-const VECTOR2I GetArcMid( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
-                          bool aMinArcAngle = true );
+const VECTOR2I CalcArcMid( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
+                           bool aMinArcAngle = true );
 
 /* Return the arc tangent of 0.1 degrees coord vector dx, dy
  * between -1800 and 1800
diff --git a/libs/kimath/src/geometry/circle.cpp b/libs/kimath/src/geometry/circle.cpp
index 291ac1fb72..cd5b52bd6f 100644
--- a/libs/kimath/src/geometry/circle.cpp
+++ b/libs/kimath/src/geometry/circle.cpp
@@ -24,7 +24,7 @@
 #include <math/util.h>          // for KiROUND
 #include <math/vector2d.h>      // for VECTOR2I
 #include <math.h>               // for sqrt
-#include <trigo.h>              // for GetArcMid
+#include <trigo.h>              // for CalcArcMid
 
 
 CIRCLE::CIRCLE()
@@ -126,7 +126,7 @@ CIRCLE& CIRCLE::ConstructFromTanTanPt( const SEG& aLineA, const SEG& aLineB, con
         // Calculate bisector
         VECTOR2I lineApt = furthestFromIntersect( aLineA.A, aLineA.B );
         VECTOR2I lineBpt = furthestFromIntersect( aLineB.A, aLineB.B );
-        VECTOR2I bisectorPt = GetArcMid( lineApt, lineBpt, intersectPoint, true );
+        VECTOR2I bisectorPt = CalcArcMid( lineApt, lineBpt, intersectPoint, true );
 
         anglebisector.A = intersectPoint;
         anglebisector.B = bisectorPt;
diff --git a/libs/kimath/src/geometry/shape_arc.cpp b/libs/kimath/src/geometry/shape_arc.cpp
index e1f1dd5026..5587a1fa8e 100644
--- a/libs/kimath/src/geometry/shape_arc.cpp
+++ b/libs/kimath/src/geometry/shape_arc.cpp
@@ -188,7 +188,7 @@ SHAPE_ARC& SHAPE_ARC::ConstructFromStartEndAngle( const VECTOR2I& aStart, const
     m_end   = aEnd;
     m_width = aWidth;
 
-    VECTOR2I center( GetArcCenter( aStart, aEnd, aAngle ) );
+    VECTOR2I center( CalcArcCenter( aStart, aEnd, aAngle ) );
 
     RotatePoint( m_mid, center, -aAngle * 10.0 / 2.0 );
 
@@ -463,7 +463,7 @@ double SHAPE_ARC::GetEndAngle() const
 
 VECTOR2I SHAPE_ARC::GetCenter() const
 {
-    return GetArcCenter( m_start, m_mid, m_end );
+    return CalcArcCenter( m_start, m_mid, m_end );
 }
 
 
diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp
index ce4f303d6a..77d7cb4f95 100644
--- a/libs/kimath/src/geometry/shape_line_chain.cpp
+++ b/libs/kimath/src/geometry/shape_line_chain.cpp
@@ -38,7 +38,7 @@
 #include <math/box2.h>       // for BOX2I
 #include <math/util.h>       // for rescale
 #include <math/vector2d.h>   // for VECTOR2, VECTOR2I
-#include <trigo.h>  // for RAD2DECIDEG, GetArcAngle
+#include <trigo.h>  // for RAD2DECIDEG, CalcArcAngle
 
 class SHAPE;
 
diff --git a/libs/kimath/src/trigo.cpp b/libs/kimath/src/trigo.cpp
index c58bcb97aa..31043dfc9b 100644
--- a/libs/kimath/src/trigo.cpp
+++ b/libs/kimath/src/trigo.cpp
@@ -160,8 +160,8 @@ bool TestSegmentHit( const wxPoint& aRefPoint, const wxPoint& aStart, const wxPo
 }
 
 
-const VECTOR2I GetArcMid( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
-                          bool aMinArcAngle )
+const VECTOR2I CalcArcMid( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
+                           bool aMinArcAngle )
 {
     VECTOR2I startVector = aStart - aCenter;
     VECTOR2I endVector = aEnd - aCenter;
@@ -359,7 +359,7 @@ void RotatePoint( double* pX, double* pY, double angle )
 }
 
 
-const wxPoint GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle )
+const wxPoint CalcArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle )
 {
     VECTOR2I start = aStart;
     VECTOR2I end = aEnd;
@@ -387,7 +387,7 @@ const wxPoint GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double
 }
 
 
-const VECTOR2D GetArcCenter( const VECTOR2D& aStart, const VECTOR2D& aMid, const VECTOR2D& aEnd )
+const VECTOR2D CalcArcCenter( const VECTOR2D& aStart, const VECTOR2D& aMid, const VECTOR2D& aEnd )
 {
     VECTOR2D center;
     double yDelta_21 = aMid.y - aStart.y;
@@ -451,12 +451,12 @@ const VECTOR2D GetArcCenter( const VECTOR2D& aStart, const VECTOR2D& aMid, const
 }
 
 
-const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
+const VECTOR2I CalcArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
 {
     VECTOR2D dStart( static_cast<double>( aStart.x ), static_cast<double>( aStart.y ) );
     VECTOR2D dMid( static_cast<double>( aMid.x ), static_cast<double>( aMid.y ) );
     VECTOR2D dEnd( static_cast<double>( aEnd.x ), static_cast<double>( aEnd.y ) );
-    VECTOR2D dCenter =  GetArcCenter( dStart, dMid, dEnd );
+    VECTOR2D dCenter = CalcArcCenter( dStart, dMid, dEnd );
 
     VECTOR2I iCenter;
 
@@ -472,12 +472,12 @@ const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const
 }
 
 
-const wxPoint GetArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd )
+const wxPoint CalcArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd )
 {
     VECTOR2D dStart( static_cast<double>( aStart.x ), static_cast<double>( aStart.y ) );
     VECTOR2D dMid( static_cast<double>( aMid.x ), static_cast<double>( aMid.y ) );
     VECTOR2D dEnd( static_cast<double>( aEnd.x ), static_cast<double>( aEnd.y ) );
-    VECTOR2D dCenter =  GetArcCenter( dStart, dMid, dEnd );
+    VECTOR2D dCenter = CalcArcCenter( dStart, dMid, dEnd );
 
     wxPoint iCenter;
 
@@ -493,9 +493,9 @@ const wxPoint GetArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wx
 }
 
 
-double GetArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
+double CalcArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
 {
-    VECTOR2I center = GetArcCenter( aStart, aMid, aEnd );
+    VECTOR2I center = CalcArcCenter( aStart, aMid, aEnd );
 
     // Check if the new arc is CW or CCW
     VECTOR2D startLine = aStart - center;
diff --git a/pcbnew/autorouter/ar_matrix.cpp b/pcbnew/autorouter/ar_matrix.cpp
index 3bdcc1f44a..675aeae274 100644
--- a/pcbnew/autorouter/ar_matrix.cpp
+++ b/pcbnew/autorouter/ar_matrix.cpp
@@ -762,35 +762,34 @@ void AR_MATRIX::TraceFilledRectangle( int ux0, int uy0, int ux1, int uy1, LSET a
 }
 
 
-void AR_MATRIX::TraceSegmentPcb( PCB_SHAPE* pt_segm, int color, int marge,
+void AR_MATRIX::TraceSegmentPcb( PCB_SHAPE* aShape, int aColor, int aMargin,
                                  AR_MATRIX::CELL_OP op_logic )
 {
-    int half_width = ( pt_segm->GetWidth() / 2 ) + marge;
+    int half_width = ( aShape->GetWidth() / 2 ) + aMargin;
 
     // Calculate the bounding rectangle of the segment (if H, V or Via)
-    int ux0 = pt_segm->GetStart().x - GetBrdCoordOrigin().x;
-    int uy0 = pt_segm->GetStart().y - GetBrdCoordOrigin().y;
-    int ux1 = pt_segm->GetEnd().x - GetBrdCoordOrigin().x;
-    int uy1 = pt_segm->GetEnd().y - GetBrdCoordOrigin().y;
-
     LAYER_NUM layer = UNDEFINED_LAYER;    // Draw on all layers
 
-    switch( pt_segm->GetShape() )
+    if( aShape->GetShape() == SHAPE_T::CIRCLE || aShape->GetShape() == SHAPE_T::SEGMENT )
     {
-    case SHAPE_T::CIRCLE:
-        traceCircle( ux0, uy0, ux1, uy1, half_width, layer, color, op_logic );
-        break;
+        int ux0 = aShape->GetStart().x - GetBrdCoordOrigin().x;
+        int uy0 = aShape->GetStart().y - GetBrdCoordOrigin().y;
+        int ux1 = aShape->GetEnd().x - GetBrdCoordOrigin().x;
+        int uy1 = aShape->GetEnd().y - GetBrdCoordOrigin().y;
 
-    case SHAPE_T::ARC:
-        traceArc( ux0, uy0, ux1, uy1, pt_segm->GetAngle(), half_width, layer, color, op_logic );
-        break;
+        if( aShape->GetShape() == SHAPE_T::CIRCLE )
+            traceCircle( ux0, uy0, ux1, uy1, half_width, layer, aColor, op_logic );
+        else
+            drawSegmentQcq( ux0, uy0, ux1, uy1, half_width, layer, aColor, op_logic );
+    }
+    else if( aShape->GetShape() == SHAPE_T::ARC )
+    {
+        int ux0 = aShape->GetCenter().x - GetBrdCoordOrigin().x;
+        int uy0 = aShape->GetCenter().y - GetBrdCoordOrigin().y;
+        int ux1 = aShape->GetStart().x - GetBrdCoordOrigin().x;
+        int uy1 = aShape->GetStart().y - GetBrdCoordOrigin().y;
 
-    case SHAPE_T::SEGMENT:
-        drawSegmentQcq( ux0, uy0, ux1, uy1, half_width, layer, color, op_logic );
-        break;
-
-    default:
-        break;
+        traceArc( ux0, uy0, ux1, uy1, aShape->GetArcAngle(), half_width, layer, aColor, op_logic );
     }
 }
 
diff --git a/pcbnew/autorouter/ar_matrix.h b/pcbnew/autorouter/ar_matrix.h
index 9f932b174a..3071441b35 100644
--- a/pcbnew/autorouter/ar_matrix.h
+++ b/pcbnew/autorouter/ar_matrix.h
@@ -108,7 +108,7 @@ public:
     DIST_CELL   GetDist( int aRow, int aCol, int aSide );
     void        SetDist( int aRow, int aCol, int aSide, DIST_CELL );
 
-    void TraceSegmentPcb( PCB_SHAPE* pt_segm, int color, int marge, AR_MATRIX::CELL_OP op_logic );
+    void TraceSegmentPcb( PCB_SHAPE* aShape, int aColor, int aMargin, AR_MATRIX::CELL_OP op_logic );
 
     void CreateKeepOutRectangle( int ux0, int uy0, int ux1, int uy1, int marge, int aKeepOut,
                                  LSET aLayerMask );
diff --git a/pcbnew/board_item.cpp b/pcbnew/board_item.cpp
index 8625b27a42..8a24f9fa84 100644
--- a/pcbnew/board_item.cpp
+++ b/pcbnew/board_item.cpp
@@ -28,6 +28,7 @@
 #include <wx/debug.h>
 #include <wx/msgdlg.h>
 #include <i18n_utility.h>
+#include <macros.h>
 #include <board.h>
 #include <pcb_group.h>
 
@@ -167,7 +168,7 @@ std::shared_ptr<SHAPE> BOARD_ITEM::GetEffectiveShape( PCB_LAYER_ID aLayer ) cons
 {
     std::shared_ptr<SHAPE> shape;
 
-    wxFAIL_MSG( "GetEffectiveShape() not implemented for " + GetClass() );
+    UNIMPLEMENTED_FOR( GetClass() );
 
     return shape;
 }
diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp
index b8adc8501d..5baaab5187 100644
--- a/pcbnew/board_items_to_polygon_shape_transform.cpp
+++ b/pcbnew/board_items_to_polygon_shape_transform.cpp
@@ -23,6 +23,7 @@
  */
 
 #include <vector>
+#include <macros.h>
 #include <bezier_curves.h>
 #include <board_design_settings.h>
 #include <trigo.h>
@@ -465,12 +466,12 @@ void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf
     }
 
     case SHAPE_T::ARC:
-        TransformArcToPolygon( aCornerBuffer, GetArcStart(), GetArcMid(), GetArcEnd(), width,
-                               aError, aErrorLoc );
+        TransformArcToPolygon( aCornerBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError,
+                               aErrorLoc );
         break;
 
     case SHAPE_T::SEGMENT:
-        TransformOvalToPolygon( aCornerBuffer, m_start, m_end, width, aError, aErrorLoc );
+        TransformOvalToPolygon( aCornerBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
         break;
 
     case SHAPE_T::POLY:
@@ -518,8 +519,8 @@ void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf
 
     case SHAPE_T::BEZIER:
     {
-        std::vector<wxPoint> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
-        BEZIER_POLY converter( ctrlPoints );
+        std::vector<wxPoint> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
+        BEZIER_POLY converter( ctrlPts );
         std::vector< wxPoint> poly;
         converter.GetPoly( poly, m_width );
 
@@ -533,8 +534,7 @@ void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf
     }
 
     default:
-        wxFAIL_MSG( "EDA_SHAPE::TransformShapeWithClearanceToPolygon not implemented for "
-                    + SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
         break;
     }
 }
diff --git a/pcbnew/convert_shape_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp
index d622963b98..707cdcafbc 100644
--- a/pcbnew/convert_shape_list_to_polygon.cpp
+++ b/pcbnew/convert_shape_list_to_polygon.cpp
@@ -95,18 +95,8 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint,
         if( graphic == aShape || ( graphic->GetFlags() & SKIP_STRUCT ) != 0 )
             continue;
 
-        switch( graphic->GetShape() )
-        {
-        case SHAPE_T::ARC:
-            if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
-                return graphic;
-
-            break;
-
-        default:
-            if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
-                return graphic;
-        }
+        if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
+            return graphic;
     }
 
     // Search again for anything that's close, even something already used.  (The latter is
@@ -121,42 +111,20 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint,
         if( graphic == aShape )
             continue;
 
-        switch( graphic->GetShape() )
+        d_sq = ( pt - graphic->GetStart() ).SquaredEuclideanNorm();
+
+        if( d_sq < closest_dist_sq )
         {
-        case SHAPE_T::ARC:
-            d_sq = ( pt - graphic->GetArcStart() ).SquaredEuclideanNorm();
+            closest_dist_sq = d_sq;
+            closest_graphic = graphic;
+        }
 
-            if( d_sq < closest_dist_sq )
-            {
-                closest_dist_sq = d_sq;
-                closest_graphic = graphic;
-            }
+        d_sq = ( pt - graphic->GetEnd() ).SquaredEuclideanNorm();
 
-            d_sq = ( pt - graphic->GetArcEnd() ).SquaredEuclideanNorm();
-
-            if( d_sq < closest_dist_sq )
-            {
-                closest_dist_sq = d_sq;
-                closest_graphic = graphic;
-            }
-            break;
-
-        default:
-            d_sq = ( pt - graphic->GetStart() ).SquaredEuclideanNorm();
-
-            if( d_sq < closest_dist_sq )
-            {
-                closest_dist_sq = d_sq;
-                closest_graphic = graphic;
-            }
-
-            d_sq = ( pt - graphic->GetEnd() ).SquaredEuclideanNorm();
-
-            if( d_sq < closest_dist_sq )
-            {
-                closest_dist_sq = d_sq;
-                closest_graphic = graphic;
-            }
+        if( d_sq < closest_dist_sq )
+        {
+            closest_dist_sq = d_sq;
+            closest_graphic = graphic;
         }
     }
 
@@ -221,9 +189,9 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
 
         case SHAPE_T::ARC:
             {
-                wxPoint  pstart = graphic->GetArcStart();
+                wxPoint  pstart = graphic->GetStart();
                 wxPoint  center = graphic->GetCenter();
-                double   angle  = -graphic->GetAngle();
+                double   angle  = -graphic->GetArcAngle();
                 double   radius = graphic->GetRadius();
                 int      steps  = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
                 wxPoint  pt;
@@ -389,8 +357,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
         // Polygon start point. Arbitrarily chosen end of the
         // segment and build the poly from here.
 
-        wxPoint startPt = graphic->GetShape() == SHAPE_T::ARC ? graphic->GetArcEnd()
-                                                              : graphic->GetEnd();
+        wxPoint startPt = graphic->GetEnd();
 
         prevPt = startPt;
         aPolygons.NewOutline();
@@ -443,16 +410,16 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
                 // We do not support arcs in polygons, so approximate an arc with a series of
                 // short lines and put those line segments into the !same! PATH.
 
-                wxPoint pstart  = graphic->GetArcStart();
-                wxPoint pend    = graphic->GetArcEnd();
+                wxPoint pstart  = graphic->GetStart();
+                wxPoint pend    = graphic->GetEnd();
                 wxPoint pcenter = graphic->GetCenter();
-                double  angle   = -graphic->GetAngle();
+                double  angle   = -graphic->GetArcAngle();
                 double  radius  = graphic->GetRadius();
                 int     steps   = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
 
                 if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
                 {
-                    wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aChainingEpsilon ) );
+                    wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
 
                     angle = -angle;
                     std::swap( pstart, pend );
@@ -535,8 +502,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
                 break;
 
             default:
-                wxFAIL_MSG( "ConvertOutlineToPolygon not implemented for "
-                            + graphic->SHAPE_T_asString() );
+                UNIMPLEMENTED_FOR( graphic->SHAPE_T_asString() );
                 return false;
             }
 
@@ -668,8 +634,8 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
             // Polygon start point. Arbitrarily chosen end of the segment and build the poly
             // from here.
 
-            wxPoint startPt( graphic->GetEnd() );
-            prevPt = graphic->GetEnd();
+            wxPoint startPt = graphic->GetEnd();
+            prevPt = startPt;
             aPolygons.Append( prevPt, -1, hole );
 
             // do not append the other end point yet, this first 'graphic' might be an arc
@@ -700,17 +666,16 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
                     // We do not support arcs in polygons, so approximate an arc with a series of
                     // short lines and put those line segments into the !same! PATH.
                     {
-                        wxPoint pstart  = graphic->GetArcStart();
-                        wxPoint pend    = graphic->GetArcEnd();
+                        wxPoint pstart  = graphic->GetStart();
+                        wxPoint pend    = graphic->GetEnd();
                         wxPoint pcenter = graphic->GetCenter();
-                        double  angle   = -graphic->GetAngle();
+                        double  angle   = -graphic->GetArcAngle();
                         int     radius  = graphic->GetRadius();
                         int     steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
 
                         if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
                         {
-                            wxASSERT( close_enough( prevPt, graphic->GetArcEnd(),
-                                                    aChainingEpsilon ) );
+                            wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
 
                             angle = -angle;
                             std::swap( pstart, pend );
diff --git a/pcbnew/dialogs/dialog_graphic_item_properties.cpp b/pcbnew/dialogs/dialog_graphic_item_properties.cpp
index 85d0ab1a60..d5b544f3bd 100644
--- a/pcbnew/dialogs/dialog_graphic_item_properties.cpp
+++ b/pcbnew/dialogs/dialog_graphic_item_properties.cpp
@@ -189,7 +189,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataToWindow()
 
     case SHAPE_T::ARC:
         SetTitle( _( "Arc Properties" ) );
-        m_AngleValue = m_item->GetAngle() / 10.0;
+        m_AngleValue = m_item->GetArcAngle() / 10.0;
         m_filledCtrl->Show( false );
         break;
 
@@ -220,12 +220,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataToWindow()
         break;
     }
 
-    if( m_item->GetShape() == SHAPE_T::ARC )
-    {
-        m_startX.SetValue( m_item->GetArcStart().x );
-        m_startY.SetValue( m_item->GetArcStart().y );
-    }
-    else if( m_flipStartEnd )
+    if( m_flipStartEnd && m_item->GetShape() != SHAPE_T::ARC )
     {
         m_startX.SetValue( m_item->GetEnd().x );
         m_startY.SetValue( m_item->GetEnd().y );
@@ -240,12 +235,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataToWindow()
     {
         m_endX.SetValue( m_item->GetRadius() );
     }
-    else if( m_item->GetShape() == SHAPE_T::ARC )
-    {
-        m_endX.SetValue( m_item->GetArcEnd().x );
-        m_endY.SetValue( m_item->GetArcEnd().y );
-    }
-    else if( m_flipStartEnd )
+    else if( m_flipStartEnd && m_item->GetShape() != SHAPE_T::ARC )
     {
         m_endX.SetValue( m_item->GetStart().x );
         m_endY.SetValue( m_item->GetStart().y );
@@ -292,11 +282,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow()
     BOARD_COMMIT commit( m_parent );
     commit.Modify( m_item );
 
-    if( m_item->GetShape() == SHAPE_T::ARC )
-    {
-        m_item->SetArcStart( wxPoint( m_startX.GetValue(), m_startY.GetValue() ) );
-    }
-    else if( m_flipStartEnd )
+    if( m_flipStartEnd && m_item->GetShape() != SHAPE_T::ARC )
     {
         m_item->SetEndX( m_startX.GetValue() );
         m_item->SetEndY( m_startY.GetValue() );
@@ -311,11 +297,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow()
     {
         m_item->SetEnd( m_item->GetStart() + wxPoint( m_endX.GetValue(), 0 ) );
     }
-    else if( m_item->GetShape() == SHAPE_T::ARC )
-    {
-        m_item->SetArcEnd( wxPoint( m_endX.GetValue(), m_endY.GetValue() ) );
-    }
-    else if( m_flipStartEnd )
+    else if( m_flipStartEnd && m_item->GetShape() != SHAPE_T::ARC )
     {
         m_item->SetStartX( m_endX.GetValue() );
         m_item->SetStartY( m_endY.GetValue() );
@@ -329,14 +311,14 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow()
     // For Bezier curve: Set the two control points
     if( m_item->GetShape() == SHAPE_T::BEZIER )
     {
-        m_item->SetBezierC1( wxPoint( m_bezierCtrl1X.GetValue(), m_bezierCtrl1Y.GetValue()));
-        m_item->SetBezierC2( wxPoint( m_bezierCtrl2X.GetValue(), m_bezierCtrl2Y.GetValue()));
+        m_item->SetBezierC1( wxPoint( m_bezierCtrl1X.GetValue(), m_bezierCtrl1Y.GetValue() ) );
+        m_item->SetBezierC2( wxPoint( m_bezierCtrl2X.GetValue(), m_bezierCtrl2Y.GetValue() ) );
     }
 
     if( m_item->GetShape() == SHAPE_T::ARC )
     {
-        m_item->SetArcCenter( GetArcCenter( m_item->GetArcStart(), m_item->GetArcEnd(), m_AngleValue ));
-        m_item->SetAngle( m_AngleValue * 10.0, false );
+        m_item->SetCenter( CalcArcCenter( m_item->GetStart(), m_item->GetEnd(), m_AngleValue ) );
+        m_item->SetArcAngle( m_AngleValue * 10.0 );
     }
 
     if( m_fp_item )
@@ -345,10 +327,13 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow()
         m_fp_item->SetStart0( m_fp_item->GetStart() );
         m_fp_item->SetEnd0( m_fp_item->GetEnd() );
 
-        if( m_fp_item->GetShape() == SHAPE_T::BEZIER )
+        if( m_item->GetShape() == SHAPE_T::ARC )
+            m_fp_item->SetCenter0( m_fp_item->GetCenter() );
+
+        if( m_item->GetShape() == SHAPE_T::BEZIER )
         {
-            m_fp_item->SetBezierC1_0( wxPoint( m_bezierCtrl1X.GetValue(), m_bezierCtrl1Y.GetValue()));
-            m_fp_item->SetBezierC2_0( wxPoint( m_bezierCtrl2X.GetValue(), m_bezierCtrl2Y.GetValue()));
+            m_fp_item->SetBezierC1_0( m_fp_item->GetBezierC1() );
+            m_fp_item->SetBezierC2_0( m_fp_item->GetBezierC2() );
         }
     }
 
@@ -408,8 +393,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate()
         break;
 
     default:
-        wxFAIL_MSG( "DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate not implemented for shape "
-                    + m_item->SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( m_item->SHAPE_T_asString() );
         break;
     }
 
diff --git a/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp b/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp
index 0b1e2c5230..bb6422d3f6 100644
--- a/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp
+++ b/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp
@@ -112,14 +112,14 @@ bool DIALOG_PAD_PRIMITIVES_PROPERTIES::TransferDataToWindow()
 
     case SHAPE_T::ARC:
         SetTitle( _( "Arc" ) );
-        m_startX.SetValue( m_shape->GetEnd().x );     // confusingly, the start point of the arc
-        m_startY.SetValue( m_shape->GetEnd().y );
+        m_startX.SetValue( m_shape->GetStart().x );
+        m_startY.SetValue( m_shape->GetStart().y );
         m_staticTextPosEnd->SetLabel( _( "Center" ) );
-        m_endX.SetValue( m_shape->GetStart().x );     // arc center
-        m_endY.SetValue( m_shape->GetStart().y );
+        m_endX.SetValue( m_shape->GetCenter().x );
+        m_endY.SetValue( m_shape->GetCenter().y );
         m_radiusLabel->SetLabel( _( "Angle:" ) );
         m_radius.SetUnits( EDA_UNITS::DEGREES );
-        m_radius.SetValue( m_shape->GetAngle() );
+        m_radius.SetValue( m_shape->GetArcAngle() );
         m_ctrl1X.Show( false, true );
         m_ctrl1Y.Show( false, true );
         m_ctrl2X.Show( false, true );
@@ -199,13 +199,9 @@ bool DIALOG_PAD_PRIMITIVES_PROPERTIES::TransferDataFromWindow()
         break;
 
     case SHAPE_T::ARC:
-        // NB: we store the center of the arc in m_Start, and, confusingly,
-        // the start point in m_End
-        m_shape->SetStart( wxPoint( m_endX.GetValue(), m_endY.GetValue() ) );
-        m_shape->SetEnd( wxPoint( m_startX.GetValue(), m_startY.GetValue() ) );
-
-        // arc angle
-        m_shape->SetAngle( m_radius.GetValue() );
+        m_shape->SetCenter( wxPoint( m_endX.GetValue(), m_endY.GetValue() ) );
+        m_shape->SetStart( wxPoint( m_startX.GetValue(), m_startY.GetValue() ) );
+        m_shape->SetArcAngleAndEnd( m_radius.GetValue() );
         break;
 
     case SHAPE_T::CIRCLE:
diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp
index 68a4963bb0..32853684a1 100644
--- a/pcbnew/dialogs/dialog_pad_properties.cpp
+++ b/pcbnew/dialogs/dialog_pad_properties.cpp
@@ -711,9 +711,8 @@ void DIALOG_PAD_PROPERTIES::displayPrimitivesList()
         case SHAPE_T::ARC:
             bs_info[0] = _( "Arc" );
             bs_info[1] = _( "center" ) + wxS( " " )+ formatCoord( m_units, primitive->GetCenter() );
-            bs_info[2] = _( "start" ) + wxS( " " )+ formatCoord( m_units,
-                                                                 primitive->GetArcStart() );
-            bs_info[3] = _( "angle" ) + wxS( " " )+ FormatAngle( primitive->GetAngle() );
+            bs_info[2] = _( "start" ) + wxS( " " )+ formatCoord( m_units, primitive->GetStart() );
+            bs_info[3] = _( "angle" ) + wxS( " " )+ FormatAngle( primitive->GetArcAngle() );
             break;
 
         case SHAPE_T::CIRCLE:
diff --git a/pcbnew/exporters/export_gencad.cpp b/pcbnew/exporters/export_gencad.cpp
index 04013c156e..0dd2ea9b99 100644
--- a/pcbnew/exporters/export_gencad.cpp
+++ b/pcbnew/exporters/export_gencad.cpp
@@ -1255,23 +1255,14 @@ static void FootprintWriteShape( FILE* aFile, FOOTPRINT* aFootprint, const wxStr
                 }
 
                 case SHAPE_T::ARC:
-                {
-                    int arcendx, arcendy;
-                    arcendx = shape->GetEnd0().x - shape->GetStart0().x;
-                    arcendy = shape->GetEnd0().y - shape->GetStart0().y;
-                    RotatePoint( &arcendx, &arcendy, -shape->GetAngle() );
-                    arcendx += shape->GetStart0().x;
-                    arcendy += shape->GetStart0().y;
-
                     fprintf( aFile, "ARC %g %g %g %g %g %g\n",
+                             shape->GetStart0().x / SCALE_FACTOR,
+                             -shape->GetStart0().y / SCALE_FACTOR,
                              shape->GetEnd0().x / SCALE_FACTOR,
                              -shape->GetEnd0().y / SCALE_FACTOR,
-                             arcendx / SCALE_FACTOR,
-                             -arcendy / SCALE_FACTOR,
-                             shape->GetStart0().x / SCALE_FACTOR,
-                             -shape->GetStart0().y / SCALE_FACTOR );
+                             shape->GetCenter0().x / SCALE_FACTOR,
+                             -shape->GetCenter0().y / SCALE_FACTOR );
                     break;
-                }
 
                 case SHAPE_T::POLY:
                     // Not exported (TODO)
diff --git a/pcbnew/exporters/export_idf.cpp b/pcbnew/exporters/export_idf.cpp
index 6490de3125..540738264e 100644
--- a/pcbnew/exporters/export_idf.cpp
+++ b/pcbnew/exporters/export_idf.cpp
@@ -91,11 +91,8 @@ static void idf_export_outline( BOARD* aPcb, IDF3_BOARD& aIDFBoard )
         {
         case SHAPE_T::SEGMENT:
         {
-            if( ( graphic->GetStart().x == graphic->GetEnd().x )
-                && ( graphic->GetStart().y == graphic->GetEnd().y ) )
-            {
+            if( graphic->GetStart() == graphic->GetEnd() )
                 break;
-            }
 
             sp.x = graphic->GetStart().x * scale + offX;
             sp.y = -graphic->GetStart().y * scale + offY;
@@ -111,11 +108,8 @@ static void idf_export_outline( BOARD* aPcb, IDF3_BOARD& aIDFBoard )
 
         case SHAPE_T::RECT:
         {
-            if( ( graphic->GetStart().x == graphic->GetEnd().x )
-                && ( graphic->GetStart().y == graphic->GetEnd().y ) )
-            {
+            if( graphic->GetStart() == graphic->GetEnd() )
                 break;
-            }
 
             double top = graphic->GetStart().y * scale + offY;
             double left = graphic->GetStart().x * scale + offX;
@@ -137,17 +131,14 @@ static void idf_export_outline( BOARD* aPcb, IDF3_BOARD& aIDFBoard )
 
         case SHAPE_T::ARC:
         {
-            if( ( graphic->GetCenter().x == graphic->GetArcStart().x )
-                && ( graphic->GetCenter().y == graphic->GetArcStart().y ) )
-            {
+            if( graphic->GetCenter() == graphic->GetStart() )
                 break;
-            }
 
             sp.x = graphic->GetCenter().x * scale + offX;
             sp.y = -graphic->GetCenter().y * scale + offY;
-            ep.x = graphic->GetArcStart().x * scale + offX;
-            ep.y = -graphic->GetArcStart().y * scale + offY;
-            IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetAngle() / 10.0, true );
+            ep.x = graphic->GetStart().x * scale + offX;
+            ep.y = -graphic->GetStart().y * scale + offY;
+            IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetArcAngle() / 10.0, true );
 
             if( seg )
                 lines.push_back( seg );
diff --git a/pcbnew/fp_shape.cpp b/pcbnew/fp_shape.cpp
index 1f2fbd140e..a4c6090103 100644
--- a/pcbnew/fp_shape.cpp
+++ b/pcbnew/fp_shape.cpp
@@ -57,7 +57,7 @@ void FP_SHAPE::SetLocalCoord()
     {
         m_start0 = m_start;
         m_end0 = m_end;
-        m_thirdPoint0 = m_thirdPoint;
+        m_arcCenter0 = m_arcCenter;
         m_bezierC1_0 = m_bezierC1;
         m_bezierC2_0 = m_bezierC2;
         return;
@@ -65,13 +65,13 @@ void FP_SHAPE::SetLocalCoord()
 
     m_start0 = m_start - fp->GetPosition();
     m_end0 = m_end - fp->GetPosition();
-    m_thirdPoint0 = m_thirdPoint - fp->GetPosition();
+    m_arcCenter0 = m_arcCenter - fp->GetPosition();
     m_bezierC1_0 = m_bezierC1 - fp->GetPosition();
     m_bezierC2_0 = m_bezierC2 - fp->GetPosition();
     double angle = fp->GetOrientation();
     RotatePoint( &m_start0.x, &m_start0.y, -angle );
     RotatePoint( &m_end0.x, &m_end0.y, -angle );
-    RotatePoint( &m_thirdPoint0.x, &m_thirdPoint0.y, -angle );
+    RotatePoint( &m_arcCenter0.x, &m_arcCenter0.y, -angle );
     RotatePoint( &m_bezierC1_0.x, &m_bezierC1_0.y, -angle );
     RotatePoint( &m_bezierC2_0.x, &m_bezierC2_0.y, -angle );
 }
@@ -83,7 +83,7 @@ void FP_SHAPE::SetDrawCoord()
 
     m_start      = m_start0;
     m_end        = m_end0;
-    m_thirdPoint = m_thirdPoint0;
+    m_arcCenter = m_arcCenter0;
     m_bezierC1   = m_bezierC1_0;
     m_bezierC2   = m_bezierC2_0;
 
@@ -91,13 +91,13 @@ void FP_SHAPE::SetDrawCoord()
     {
         RotatePoint( &m_start.x, &m_start.y, fp->GetOrientation() );
         RotatePoint( &m_end.x, &m_end.y, fp->GetOrientation() );
-        RotatePoint( &m_thirdPoint.x, &m_thirdPoint.y, fp->GetOrientation() );
+        RotatePoint( &m_arcCenter.x, &m_arcCenter.y, fp->GetOrientation() );
         RotatePoint( &m_bezierC1.x, &m_bezierC1.y, fp->GetOrientation() );
         RotatePoint( &m_bezierC2.x, &m_bezierC2.y, fp->GetOrientation() );
 
         m_start      += fp->GetPosition();
         m_end        += fp->GetPosition();
-        m_thirdPoint += fp->GetPosition();
+        m_arcCenter += fp->GetPosition();
         m_bezierC1   += fp->GetPosition();
         m_bezierC2   += fp->GetPosition();
     }
@@ -137,22 +137,57 @@ EDA_ITEM* FP_SHAPE::Clone() const
 }
 
 
-void FP_SHAPE::SetAngle( double aAngle, bool aUpdateEnd )
+wxPoint FP_SHAPE::GetCenter0() const
 {
-    // Mark as depreciated.
-    // m_Angle does not define the arc anymore
-    // Update the parent class (updates the global m_ThirdPoint)
-    PCB_SHAPE::SetAngle( aAngle, aUpdateEnd );
-
-    // Also update the local m_thirdPoint0 if requested
-    if( aUpdateEnd )
+    switch( m_shape )
     {
-        m_thirdPoint0 = m_end0;
-        RotatePoint( &m_thirdPoint0, m_start0, -m_angle );
+    case SHAPE_T::ARC:
+        return m_arcCenter0;
+
+    case SHAPE_T::CIRCLE:
+        return m_start0;
+
+    default:
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
+        return wxPoint();
     }
 }
 
 
+void FP_SHAPE::SetCenter0( const wxPoint& aCenter )
+{
+    switch( m_shape )
+    {
+    case SHAPE_T::ARC:
+        m_arcCenter0 = aCenter;
+        break;
+
+    case SHAPE_T::CIRCLE:
+        m_start0 = aCenter;
+        break;
+
+    default:
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
+    }
+}
+
+
+void FP_SHAPE::SetArcAngle( double aAngle )
+{
+    PCB_SHAPE::SetArcAngle( aAngle );
+}
+
+
+void FP_SHAPE::SetArcAngleAndEnd0( double aAngle )
+{
+    PCB_SHAPE::SetArcAngle( aAngle );
+
+    wxPoint end = GetStart0();
+    RotatePoint( &end, GetCenter0(), -m_arcAngle );
+    SetEnd0( end );
+}
+
+
 void FP_SHAPE::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
 {
     wxPoint pt( 0, 0 );
@@ -160,9 +195,9 @@ void FP_SHAPE::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
     switch( GetShape() )
     {
     case SHAPE_T::ARC:
-        // Update arc angle but do not yet update m_ThirdPoint0 and m_thirdPoint,
+        // Update arc angle but do not yet update m_arcCenter0 and m_arcCenter,
         // arc center and start point must be updated before calculation arc end.
-        SetAngle( -GetAngle(), false );
+        SetArcAngle( -GetArcAngle() );
         KI_FALLTHROUGH;
 
     default:
@@ -177,12 +212,12 @@ void FP_SHAPE::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
         {
             MIRROR( m_start.x, aCentre.x );
             MIRROR( m_end.x, aCentre.x );
-            MIRROR( m_thirdPoint.x, aCentre.x );
+            MIRROR( m_arcCenter.x, aCentre.x );
             MIRROR( m_bezierC1.x, aCentre.x );
             MIRROR( m_bezierC2.x, aCentre.x );
             MIRROR( m_start0.x, pt.x );
             MIRROR( m_end0.x, pt.x );
-            MIRROR( m_thirdPoint0.x, pt.x );
+            MIRROR( m_arcCenter0.x, pt.x );
             MIRROR( m_bezierC1_0.x, pt.x );
             MIRROR( m_bezierC2_0.x, pt.x );
         }
@@ -190,12 +225,12 @@ void FP_SHAPE::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
         {
             MIRROR( m_start.y, aCentre.y );
             MIRROR( m_end.y, aCentre.y );
-            MIRROR( m_thirdPoint.y, aCentre.y );
+            MIRROR( m_arcCenter.y, aCentre.y );
             MIRROR( m_bezierC1.y, aCentre.y );
             MIRROR( m_bezierC2.y, aCentre.y );
             MIRROR( m_start0.y, pt.y );
             MIRROR( m_end0.y, pt.y );
-            MIRROR( m_thirdPoint0.y, pt.y );
+            MIRROR( m_arcCenter0.y, pt.y );
             MIRROR( m_bezierC1_0.y, pt.y );
             MIRROR( m_bezierC2_0.y, pt.y );
         }
@@ -227,9 +262,9 @@ void FP_SHAPE::Mirror( const wxPoint& aCentre, bool aMirrorAroundXAxis )
     switch( GetShape() )
     {
     case SHAPE_T::ARC:
-        // Update arc angle but do not yet update m_ThirdPoint0 and m_thirdPoint,
+        // Update arc angle but do not yet update m_arcCenter0 and m_arcCenter,
         // arc center and start point must be updated before calculation arc end.
-        SetAngle( -GetAngle(), false );
+        SetArcAngle( -GetArcAngle() );
         KI_FALLTHROUGH;
 
     default:
@@ -239,7 +274,7 @@ void FP_SHAPE::Mirror( const wxPoint& aCentre, bool aMirrorAroundXAxis )
         {
             MIRROR( m_start0.y, aCentre.y );
             MIRROR( m_end0.y, aCentre.y );
-            MIRROR( m_thirdPoint0.y, aCentre.y );
+            MIRROR( m_arcCenter0.y, aCentre.y );
             MIRROR( m_bezierC1_0.y, aCentre.y );
             MIRROR( m_bezierC2_0.y, aCentre.y );
         }
@@ -247,7 +282,7 @@ void FP_SHAPE::Mirror( const wxPoint& aCentre, bool aMirrorAroundXAxis )
         {
             MIRROR( m_start0.x, aCentre.x );
             MIRROR( m_end0.x, aCentre.x );
-            MIRROR( m_thirdPoint0.x, aCentre.x );
+            MIRROR( m_arcCenter0.x, aCentre.x );
             MIRROR( m_bezierC1_0.x, aCentre.x );
             MIRROR( m_bezierC2_0.x, aCentre.x );
         }
@@ -290,7 +325,7 @@ void FP_SHAPE::Move( const wxPoint& aMoveVector )
     // This is a footprint shape modification.
     m_start0      += aMoveVector;
     m_end0        += aMoveVector;
-    m_thirdPoint0 += aMoveVector;
+    m_arcCenter0 += aMoveVector;
     m_bezierC1_0  += aMoveVector;
     m_bezierC2_0  += aMoveVector;
 
diff --git a/pcbnew/fp_shape.h b/pcbnew/fp_shape.h
index 8b1200e8cf..3e89087e9a 100644
--- a/pcbnew/fp_shape.h
+++ b/pcbnew/fp_shape.h
@@ -70,10 +70,9 @@ public:
     /**
      * Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
      * @param aAngle is tenths of degrees, but will soon be degrees.
-     * @param aUpdateEnd = true to update also arc end coordinates m_thirdPoint and
-     * m_thirdPoint0, so must be called after setting m_Start, m_start0, m_End and m_end0
      */
-    void SetAngle( double aAngle, bool aUpdateEnd = true ) override;
+    void SetArcAngle( double aAngle ) override;
+    void SetArcAngleAndEnd0( double aAngle );
 
     /**
      * Move an edge of the footprint.
@@ -114,15 +113,15 @@ public:
     void SetEnd0( const wxPoint& aPoint )       { m_end0 = aPoint; }
     const wxPoint& GetEnd0() const              { return m_end0; }
 
-    void SetThirdPoint0( const wxPoint& aPoint ){ m_thirdPoint0 = aPoint; }
-    const wxPoint& GetThirdPoint0() const       { return m_thirdPoint0; }
-
     void SetBezierC1_0( const wxPoint& aPoint ) { m_bezierC1_0 = aPoint; }
     const wxPoint& GetBezierC1_0() const        { return m_bezierC1_0; }
 
     void SetBezierC2_0( const wxPoint& aPoint ) { m_bezierC2_0 = aPoint; }
     const wxPoint& GetBezierC2_0() const        { return m_bezierC2_0; }
 
+    wxPoint GetCenter0() const;
+    void SetCenter0( const wxPoint& aPt );
+
     /**
      * Set relative coordinates from draw coordinates.
      * Call in only when the geometry or the footprint is modified and therefore the relative
@@ -160,7 +159,7 @@ public:
 protected:
     wxPoint m_start0;       ///< Start point or circle center, relative to footprint origin, orient 0.
     wxPoint m_end0;         ///< End point or circle edge, relative to footprint origin, orient 0.
-    wxPoint m_thirdPoint0;  ///< End point for an arc.
+    wxPoint m_arcCenter0;   ///< Center of arc, relative to footprint origin, orient 0.
     wxPoint m_bezierC1_0;   ///< Bezier Control Point 1, relative to footprint origin, orient 0.
     wxPoint m_bezierC2_0;   ///< Bezier Control Point 2, relative to footprint origin, orient 0.
 };
diff --git a/pcbnew/graphics_cleaner.cpp b/pcbnew/graphics_cleaner.cpp
index 0f552d7c30..53a63c947b 100644
--- a/pcbnew/graphics_cleaner.cpp
+++ b/pcbnew/graphics_cleaner.cpp
@@ -24,6 +24,7 @@
  */
 
 #include <reporter.h>
+#include <macros.h>
 #include <board_commit.h>
 #include <cleanup_item.h>
 #include <pcb_shape.h>
@@ -71,14 +72,12 @@ bool GRAPHICS_CLEANER::isNullShape( PCB_SHAPE* aShape )
     {
     case SHAPE_T::SEGMENT:
     case SHAPE_T::RECT:
+    case SHAPE_T::ARC:
         return aShape->GetStart() == aShape->GetEnd();
 
     case SHAPE_T::CIRCLE:
         return aShape->GetRadius() == 0;
 
-    case SHAPE_T::ARC:
-        return aShape->GetCenter() == aShape->GetArcStart();
-
     case SHAPE_T::POLY:
         return aShape->GetPointCount() == 0;
 
@@ -87,8 +86,7 @@ bool GRAPHICS_CLEANER::isNullShape( PCB_SHAPE* aShape )
         return aShape->GetBezierPoints().empty();
 
     default:
-        wxFAIL_MSG( "GRAPHICS_CLEANER::isNullSegment unsupported PCB_SHAPE shape: "
-                    + aShape->SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( aShape->SHAPE_T_asString() );
         return false;
     }
 }
@@ -113,8 +111,8 @@ bool GRAPHICS_CLEANER::areEquivalent( PCB_SHAPE* aShape1, PCB_SHAPE* aShape2 )
 
     case SHAPE_T::ARC:
         return aShape1->GetCenter() == aShape2->GetCenter()
-                && aShape1->GetArcStart() == aShape2->GetArcStart()
-                && aShape1->GetAngle() == aShape2->GetAngle();
+                && aShape1->GetStart() == aShape2->GetStart()
+                && aShape1->GetArcAngle() == aShape2->GetArcAngle();
 
     case SHAPE_T::POLY:
         // TODO
diff --git a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp
index b0d9f206cf..a0646ac435 100644
--- a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp
+++ b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp
@@ -82,8 +82,8 @@ void GRAPHICS_IMPORTER_PCBNEW::AddCircle( const VECTOR2D& aCenter, double aRadiu
     circle->SetFilled( aFilled );
     circle->SetLayer( GetLayer() );
     circle->SetWidth( MapLineWidth( aWidth ) );
-    circle->SetArcCenter( MapCoordinate( aCenter ));
-    circle->SetArcStart( MapCoordinate( VECTOR2D( aCenter.x + aRadius, aCenter.y ) ) );
+    circle->SetStart( MapCoordinate( aCenter ));
+    circle->SetEnd( MapCoordinate( VECTOR2D( aCenter.x + aRadius, aCenter.y ) ) );
 
     if( circle->Type() == PCB_FP_SHAPE_T )
         static_cast<FP_SHAPE*>( circle.get() )->SetLocalCoord();
@@ -99,9 +99,9 @@ void GRAPHICS_IMPORTER_PCBNEW::AddArc( const VECTOR2D& aCenter, const VECTOR2D&
     arc->SetShape( SHAPE_T::ARC );
     arc->SetLayer( GetLayer() );
     arc->SetWidth( MapLineWidth( aWidth ) );
-    arc->SetArcCenter( MapCoordinate( aCenter ));
-    arc->SetArcStart( MapCoordinate( aStart ) );
-    arc->SetAngle( aAngle * 10.0 );     // Pcbnew uses the decidegree
+    arc->SetCenter( MapCoordinate( aCenter ));
+    arc->SetStart( MapCoordinate( aStart ) );
+    arc->SetArcAngleAndEnd( aAngle * 10.0 );     // Pcbnew uses the decidegree
 
     if( arc->Type() == PCB_FP_SHAPE_T )
         static_cast<FP_SHAPE*>( arc.get() )->SetLocalCoord();
diff --git a/pcbnew/microwave/microwave_inductor.cpp b/pcbnew/microwave/microwave_inductor.cpp
index 954a686acf..6e3dcf24fa 100644
--- a/pcbnew/microwave/microwave_inductor.cpp
+++ b/pcbnew/microwave/microwave_inductor.cpp
@@ -424,8 +424,7 @@ FOOTPRINT* MICROWAVE_TOOL::createMicrowaveInductor( MICROWAVE_INDUCTOR_PATTERN&
     // Generate segments
     for( unsigned jj = 1; jj < buffer.size(); jj++ )
     {
-        FP_SHAPE* seg;
-        seg = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
+        FP_SHAPE* seg = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
         seg->SetStart( buffer[jj - 1] );
         seg->SetEnd( buffer[jj] );
         seg->SetWidth( aInductorPattern.m_Width );
diff --git a/pcbnew/pad_custom_shape_functions.cpp b/pcbnew/pad_custom_shape_functions.cpp
index ccf18842b4..713a9add6d 100644
--- a/pcbnew/pad_custom_shape_functions.cpp
+++ b/pcbnew/pad_custom_shape_functions.cpp
@@ -85,9 +85,9 @@ void PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aA
 {
     PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::ARC );
     item->SetFilled( false );
-    item->SetArcCenter( aCenter );
-    item->SetArcStart( aStart );
-    item->SetAngle( aArcAngle );
+    item->SetCenter( aCenter );
+    item->SetStart( aStart );
+    item->SetArcAngleAndEnd( aArcAngle );
     item->SetWidth( aThickness );
     item->SetParent( this );
     m_editPrimitives.emplace_back( item );
diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp
index 09a7e92847..4443be547c 100644
--- a/pcbnew/pcb_painter.cpp
+++ b/pcbnew/pcb_painter.cpp
@@ -1340,8 +1340,6 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
     const COLOR4D& color = m_pcbSettings.GetColor( aShape, aShape->GetLayer() );
     bool           sketch = m_pcbSettings.m_sketchGraphics;
     int            thickness = getLineThickness( aShape->GetWidth() );
-    VECTOR2D       start( aShape->GetStart() );
-    VECTOR2D       end( aShape->GetEnd() );
 
     if( sketch )
     {
@@ -1358,14 +1356,14 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
     case SHAPE_T::SEGMENT:
         if( sketch )
         {
-            m_gal->DrawSegment( start, end, thickness );
+            m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
         }
         else
         {
             m_gal->SetIsFill( true );
             m_gal->SetIsStroke( false );
 
-            m_gal->DrawSegment( start, end, thickness );
+            m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
         }
 
         break;
@@ -1412,28 +1410,28 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
     case SHAPE_T::ARC:
         if( sketch )
         {
-            m_gal->DrawArcSegment( start, aShape->GetRadius(),
-                    DECIDEG2RAD( aShape->GetArcAngleStart() ),
-                    DECIDEG2RAD( aShape->GetArcAngleStart() + aShape->GetAngle() ), // Change this
-                    thickness, m_maxError );
+            m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(),
+                                   DECIDEG2RAD( aShape->GetArcAngleStart() ),
+                                   DECIDEG2RAD( aShape->GetArcAngleStart() + aShape->GetArcAngle() ), // Change this
+                                   thickness, m_maxError );
         }
         else
         {
             m_gal->SetIsFill( true );
             m_gal->SetIsStroke( false );
 
-            m_gal->DrawArcSegment( start, aShape->GetRadius(),
-                    DECIDEG2RAD( aShape->GetArcAngleStart() ),
-                    DECIDEG2RAD( aShape->GetArcAngleStart() + aShape->GetAngle() ), // Change this
-                    thickness, m_maxError );
+            m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(),
+                                   DECIDEG2RAD( aShape->GetArcAngleStart() ),
+                                   DECIDEG2RAD( aShape->GetArcAngleStart() + aShape->GetArcAngle() ), // Change this
+                                   thickness, m_maxError );
         }
         break;
 
     case SHAPE_T::CIRCLE:
         if( sketch )
         {
-            m_gal->DrawCircle( start, aShape->GetRadius() - thickness / 2 );
-            m_gal->DrawCircle( start, aShape->GetRadius() + thickness / 2 );
+            m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() - thickness / 2 );
+            m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() + thickness / 2 );
         }
         else
         {
@@ -1441,7 +1439,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
             m_gal->SetIsStroke( thickness > 0 );
             m_gal->SetLineWidth( thickness );
 
-            m_gal->DrawCircle( start, aShape->GetRadius() );
+            m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() );
         }
         break;
 
diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp
index 7c1d10de00..f5bb53f6d8 100644
--- a/pcbnew/pcb_shape.cpp
+++ b/pcbnew/pcb_shape.cpp
@@ -255,7 +255,7 @@ static struct PCB_SHAPE_DESC
                     &EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth, PROPERTY_DISPLAY::DISTANCE ) );
         // TODO show certain properties depending on the shape
         //propMgr.AddProperty( new PROPERTY<PCB_SHAPE, double>( _HKI( "Angle" ),
-        //            &PCB_SHAPE::SetAngle, &PCB_SHAPE::GetAngle, PROPERTY_DISPLAY::DECIDEGREE ) );
+        //            &PCB_SHAPE::SetArcAngle, &PCB_SHAPE::GetAngle, PROPERTY_DISPLAY::DECIDEGREE ) );
         // TODO or may have different names (arcs)
         // TODO type?
         propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
diff --git a/pcbnew/pcb_shape.h b/pcbnew/pcb_shape.h
index 3f3ff47163..da89061256 100644
--- a/pcbnew/pcb_shape.h
+++ b/pcbnew/pcb_shape.h
@@ -73,8 +73,8 @@ public:
         return false;
     }
 
-    void SetPosition( const wxPoint& aPos ) override { SetShapePos( aPos ); }
-    wxPoint GetPosition() const override { return GetShapePos(); }
+    void SetPosition( const wxPoint& aPos ) override { setPosition( aPos ); }
+    wxPoint GetPosition() const override { return getPosition(); }
 
     wxPoint GetCenter() const override { return getCenter(); }
 
diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp
index 31ab30d6fe..8a2aa37596 100644
--- a/pcbnew/pcb_track.cpp
+++ b/pcbnew/pcb_track.cpp
@@ -956,13 +956,13 @@ void PCB_VIA::SwapData( BOARD_ITEM* aImage )
 
 wxPoint PCB_ARC::GetPosition() const
 {
-    auto center = GetArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ) );
+    auto center = CalcArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ));
     return wxPoint( center.x, center.y );
 }
 
 double PCB_ARC::GetRadius() const
 {
-    auto center = GetArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ) );
+    auto center = CalcArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ));
     return GetLineLength( wxPoint( center ), m_Start );
 }
 
diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp
index 34c49965c4..024aada574 100644
--- a/pcbnew/plot_brditems_plotter.cpp
+++ b/pcbnew/plot_brditems_plotter.cpp
@@ -541,10 +541,8 @@ void BRDITEMS_PLOTTER::PlotFootprintGraphicItem( const FP_SHAPE* aShape )
 
     m_plotter->SetColor( getColor( aShape->GetLayer() ) );
 
-    bool    sketch = GetPlotMode() == SKETCH;
-    int     thickness = aShape->GetWidth();
-    wxPoint pos( aShape->GetStart() );
-    wxPoint end( aShape->GetEnd() );
+    bool sketch = GetPlotMode() == SKETCH;
+    int  thickness = aShape->GetWidth();
 
     GBR_METADATA gbr_metadata;
     gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
@@ -568,7 +566,8 @@ void BRDITEMS_PLOTTER::PlotFootprintGraphicItem( const FP_SHAPE* aShape )
     switch( aShape->GetShape() )
     {
     case SHAPE_T::SEGMENT:
-        m_plotter->ThickSegment( pos, end, thickness, GetPlotMode(), &gbr_metadata );
+        m_plotter->ThickSegment( aShape->GetStart(), aShape->GetEnd(), thickness, GetPlotMode(),
+                                 &gbr_metadata );
         break;
 
     case SHAPE_T::RECT:
@@ -596,30 +595,38 @@ void BRDITEMS_PLOTTER::PlotFootprintGraphicItem( const FP_SHAPE* aShape )
         break;
 
     case SHAPE_T::CIRCLE:
-        radius = KiROUND( GetLineLength( end, pos ) );
+        radius = KiROUND( GetLineLength( aShape->GetStart(), aShape->GetEnd() ) );
 
         if( aShape->IsFilled() )
-            m_plotter->FilledCircle( pos, radius * 2 + thickness, GetPlotMode(), &gbr_metadata );
+        {
+            m_plotter->FilledCircle( aShape->GetStart(), radius * 2 + thickness, GetPlotMode(),
+                                     &gbr_metadata );
+        }
         else
-            m_plotter->ThickCircle( pos, radius * 2, thickness, GetPlotMode(), &gbr_metadata );
+        {
+            m_plotter->ThickCircle( aShape->GetStart(), radius * 2, thickness, GetPlotMode(),
+                                    &gbr_metadata );
+        }
 
         break;
 
     case SHAPE_T::ARC:
     {
-        radius = KiROUND( GetLineLength( end, pos ) );
-        double startAngle  = ArcTangente( end.y - pos.y, end.x - pos.x );
-        double endAngle = startAngle + aShape->GetAngle();
+        radius = KiROUND( GetLineLength( aShape->GetCenter(), aShape->GetStart() ) );
+        double startAngle  = ArcTangente( aShape->GetStart().y - aShape->GetCenter().y,
+                                          aShape->GetStart().x - aShape->GetCenter().x );
+        double endAngle = startAngle + aShape->GetArcAngle();
 
         // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg
-        if( std::abs( aShape->GetAngle() ) == 3600.0 )
+        if( std::abs( aShape->GetArcAngle() ) == 3600.0 )
         {
-            m_plotter->ThickCircle( pos, radius * 2, thickness, GetPlotMode(), &gbr_metadata );
+            m_plotter->ThickCircle( aShape->GetCenter(), radius * 2, thickness, GetPlotMode(),
+                                    &gbr_metadata );
         }
         else
         {
-            m_plotter->ThickArc( pos, -endAngle, -startAngle, radius, thickness, GetPlotMode(),
-                                 &gbr_metadata );
+            m_plotter->ThickArc( aShape->GetCenter(), -endAngle, -startAngle, radius, thickness,
+                                 GetPlotMode(), &gbr_metadata );
         }
     }
         break;
@@ -865,16 +872,11 @@ void BRDITEMS_PLOTTER::PlotPcbShape( const PCB_SHAPE* aShape )
     if( !m_layerMask[aShape->GetLayer()] )
         return;
 
-    int     radius = 0;
-    double  StAngle = 0, EndAngle = 0;
     bool    sketch = GetPlotMode() == SKETCH;
     int     thickness = aShape->GetWidth();
 
     m_plotter->SetColor( getColor( aShape->GetLayer() ) );
 
-    wxPoint start( aShape->GetStart() );
-    wxPoint end( aShape->GetEnd() );
-
     GBR_METADATA gbr_metadata;
 
     if( aShape->GetLayer() == Edge_Cuts )
@@ -889,34 +891,42 @@ void BRDITEMS_PLOTTER::PlotPcbShape( const PCB_SHAPE* aShape )
     switch( aShape->GetShape() )
     {
     case SHAPE_T::SEGMENT:
-        m_plotter->ThickSegment( start, end, thickness, GetPlotMode(), &gbr_metadata );
+        m_plotter->ThickSegment( aShape->GetStart(), aShape->GetEnd(), thickness, GetPlotMode(),
+                                 &gbr_metadata );
         break;
 
     case SHAPE_T::CIRCLE:
-        radius = KiROUND( GetLineLength( end, start ) );
-
         if( aShape->IsFilled() )
-            m_plotter->FilledCircle( start, radius * 2 + thickness, GetPlotMode(), &gbr_metadata );
+        {
+            m_plotter->FilledCircle( aShape->GetStart(), aShape->GetRadius() * 2 + thickness,
+                                     GetPlotMode(), &gbr_metadata );
+        }
         else
-            m_plotter->ThickCircle( start, radius * 2, thickness, GetPlotMode(), &gbr_metadata );
+        {
+            m_plotter->ThickCircle( aShape->GetStart(), aShape->GetRadius() * 2, thickness,
+                                    GetPlotMode(), &gbr_metadata );
+        }
 
         break;
 
     case SHAPE_T::ARC:
-        radius = KiROUND( GetLineLength( end, start ) );
-        StAngle  = ArcTangente( end.y - start.y, end.x - start.x );
-        EndAngle = StAngle + aShape->GetAngle();
+    {
+        double startAngle  = ArcTangente( aShape->GetStart().y - aShape->GetCenter().y,
+                                          aShape->GetStart().x - aShape->GetCenter().x );
+        double endAngle = startAngle + aShape->GetArcAngle();
 
         // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg
-        if( std::abs( aShape->GetAngle() ) == 3600.0 )
+        if( std::abs( aShape->GetArcAngle() ) == 3600.0 )
         {
-            m_plotter->ThickCircle( start, radius * 2, thickness, GetPlotMode(), &gbr_metadata );
+            m_plotter->ThickCircle( aShape->GetCenter(), aShape->GetRadius() * 2, thickness,
+                                    GetPlotMode(), &gbr_metadata );
         }
         else
         {
-            m_plotter->ThickArc( start, -EndAngle, -StAngle, radius, thickness, GetPlotMode(),
-                                 &gbr_metadata );
+            m_plotter->ThickArc( aShape->GetCenter(), -endAngle, -startAngle, aShape->GetRadius(),
+                                 thickness, GetPlotMode(), &gbr_metadata );
         }
+    }
         break;
 
     case SHAPE_T::BEZIER:
@@ -984,8 +994,7 @@ void BRDITEMS_PLOTTER::PlotPcbShape( const PCB_SHAPE* aShape )
     }
 
     default:
-        wxASSERT_MSG( false, "Unhandled PCB_SHAPE shape" );
-        m_plotter->ThickSegment( start, end, thickness, GetPlotMode(), &gbr_metadata );
+        UNIMPLEMENTED_FOR( aShape->SHAPE_T_asString() );
     }
 }
 
diff --git a/pcbnew/plugins/altium/altium_pcb.cpp b/pcbnew/plugins/altium/altium_pcb.cpp
index 8572bd5dbe..522849cdc5 100644
--- a/pcbnew/plugins/altium/altium_pcb.cpp
+++ b/pcbnew/plugins/altium/altium_pcb.cpp
@@ -862,24 +862,25 @@ void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aV
             else if( cur->isRound )
             {
                 shape->SetShape( SHAPE_T::ARC );
-                shape->SetAngle( -NormalizeAngleDegreesPos( cur->endangle - cur->startangle ) * 10. );
 
+                double  includedAngle  = cur->endangle - cur->startangle;
                 double  startradiant   = DEG2RAD( cur->startangle );
                 wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * cur->radius ),
-                        -KiROUND( std::sin( startradiant ) * cur->radius ) );
+                                                 -KiROUND( std::sin( startradiant ) * cur->radius ) );
                 wxPoint arcStart       = cur->center + arcStartOffset;
-                shape->SetArcCenter( cur->center );
-                shape->SetArcStart( arcStart );
+
+                shape->SetCenter( cur->center );
+                shape->SetStart( arcStart );
+                shape->SetArcAngleAndEnd( -NormalizeAngleDegreesPos( includedAngle ) * 10.0 );
 
                 if( !last->isRound )
                 {
                     double  endradiant   = DEG2RAD( cur->endangle );
                     wxPoint arcEndOffset = wxPoint( KiROUND( std::cos( endradiant ) * cur->radius ),
-                            -KiROUND( std::sin( endradiant ) * cur->radius ) );
+                                                   -KiROUND( std::sin( endradiant ) * cur->radius ) );
                     wxPoint arcEnd       = cur->center + arcEndOffset;
 
-                    PCB_SHAPE* shape2 = new PCB_SHAPE( m_board );
-                    shape2->SetShape( SHAPE_T::SEGMENT );
+                    PCB_SHAPE* shape2 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
                     m_board->Add( shape2, ADD_MODE::APPEND );
                     shape2->SetWidth( m_board->GetDesignSettings().GetLineThickness( Edge_Cuts ) );
                     shape2->SetLayer( Edge_Cuts );
@@ -888,14 +889,11 @@ void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aV
                     // TODO: this is more of a hack than the real solution
                     double lineLengthStart = GetLineLength( last->position, arcStart );
                     double lineLengthEnd   = GetLineLength( last->position, arcEnd );
+
                     if( lineLengthStart > lineLengthEnd )
-                    {
                         shape2->SetEnd( cur->center + arcEndOffset );
-                    }
                     else
-                    {
                         shape2->SetEnd( cur->center + arcStartOffset );
-                    }
                 }
             }
             last = cur;
@@ -1922,22 +1920,25 @@ void ALTIUM_PCB::ParseArcs6Data( const CFB::CompoundFileReader& aReader,
         {
             PCB_SHAPE shape( nullptr ); // just a helper to get the graphic
             shape.SetWidth( elem.width );
-            shape.SetArcCenter( elem.center );
 
             if( elem.startangle == 0. && elem.endangle == 360. )
             { // TODO: other variants to define circle?
                 shape.SetShape( SHAPE_T::CIRCLE );
-                shape.SetArcStart( elem.center - wxPoint( 0, elem.radius ) );
+                shape.SetStart( elem.center );
+                shape.SetEnd( elem.center - wxPoint( 0, elem.radius ) );
             }
             else
             {
                 shape.SetShape( SHAPE_T::ARC );
 
+                double  includedAngle  = elem.endangle - elem.startangle;
                 double  startradiant   = DEG2RAD( elem.startangle );
                 wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
                                                  -KiROUND( std::sin( startradiant ) * elem.radius ) );
-                shape.SetArcStart( elem.center + arcStartOffset );
-                shape.SetAngle( -NormalizeAngleDegreesPos( elem.endangle - elem.startangle ) * 10. );
+
+                shape.SetCenter( elem.center );
+                shape.SetStart( elem.center + arcStartOffset );
+                shape.SetArcAngleAndEnd( -NormalizeAngleDegreesPos( includedAngle ) * 10.0 );
             }
 
             ZONE* zone = new ZONE( m_board );
@@ -2023,24 +2024,27 @@ void ALTIUM_PCB::ParseArcs6Data( const CFB::CompoundFileReader& aReader,
         else
         {
             PCB_SHAPE* shape = HelperCreateAndAddShape( elem.component );
-            shape->SetArcCenter( elem.center );
             shape->SetWidth( elem.width );
             shape->SetLayer( klayer );
 
             if( elem.startangle == 0. && elem.endangle == 360. )
             { // TODO: other variants to define circle?
                 shape->SetShape( SHAPE_T::CIRCLE );
-                shape->SetArcStart( elem.center - wxPoint( 0, elem.radius ) );
+                shape->SetStart( elem.center );
+                shape->SetEnd( elem.center - wxPoint( 0, elem.radius ) );
             }
             else
             {
                 shape->SetShape( SHAPE_T::ARC );
-                shape->SetAngle( -NormalizeAngleDegreesPos( elem.endangle - elem.startangle ) * 10. );
 
+                double  includedAngle  = elem.endangle - elem.startangle;
                 double  startradiant   = DEG2RAD( elem.startangle );
                 wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
                                                   -KiROUND( std::sin( startradiant ) * elem.radius ) );
-                shape->SetArcStart( elem.center + arcStartOffset );
+
+                shape->SetCenter( elem.center );
+                shape->SetStart( elem.center + arcStartOffset );
+                shape->SetArcAngleAndEnd( -NormalizeAngleDegreesPos( includedAngle ) * 10.0 );
             }
 
             HelperShapeSetLocalCoord( shape, elem.component );
@@ -2363,9 +2367,9 @@ void ALTIUM_PCB::HelperParsePad6NonCopper( const APAD6& aElem )
                 // circle
                 shape->SetShape( SHAPE_T::CIRCLE );
                 shape->SetFilled( true );
-                shape->SetArcCenter( aElem.position );
+                shape->SetStart( aElem.position );
+                shape->SetEnd( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
                 shape->SetWidth( aElem.topsize.x / 2 );
-                shape->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
             }
             else if( aElem.topsize.x < aElem.topsize.y )
             {
@@ -2396,9 +2400,9 @@ void ALTIUM_PCB::HelperParsePad6NonCopper( const APAD6& aElem )
             shape->SetShape( SHAPE_T::CIRCLE );
             shape->SetFilled( true );
             shape->SetLayer( klayer );
-            shape->SetArcCenter( aElem.position );
+            shape->SetStart( aElem.position );
+            shape->SetEnd( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
             shape->SetWidth( aElem.topsize.x / 2 );
-            shape->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
             HelperShapeSetLocalCoord( shape, aElem.component );
         }
         else
diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp
index acde795e56..1abcdf7363 100644
--- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp
+++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp
@@ -2818,8 +2818,8 @@ PCB_SHAPE* CADSTAR_PCB_ARCHIVE_LOADER::getShapeFromVertex( const POINT& aCadstar
         else
             shape = new PCB_SHAPE( aContainer, SHAPE_T::ARC );
 
-        shape->SetArcStart( startPoint );
-        shape->SetArcCenter( centerPoint );
+        shape->SetCenter( centerPoint );
+        shape->SetStart( startPoint );
 
         arcStartAngle = getPolarAngle( startPoint - centerPoint );
         arcEndAngle   = getPolarAngle( endPoint - centerPoint );
@@ -2828,9 +2828,9 @@ PCB_SHAPE* CADSTAR_PCB_ARCHIVE_LOADER::getShapeFromVertex( const POINT& aCadstar
         // with opposite start/end points and same centre point)
 
         if( cw )
-            shape->SetAngle( NormalizeAnglePos( arcAngle ) );
+            shape->SetArcAngleAndEnd( NormalizeAnglePos( arcAngle ) );
         else
-            shape->SetAngle( NormalizeAngleNeg( arcAngle ) );
+            shape->SetArcAngleAndEnd( NormalizeAngleNeg( arcAngle ) );
 
         break;
     }
@@ -2960,12 +2960,12 @@ SHAPE_LINE_CHAIN CADSTAR_PCB_ARCHIVE_LOADER::getLineChainFromShapes( const std::
             if( shape->GetClass() == wxT( "MGRAPHIC" ) )
             {
                 FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
-                SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetEnd0(), (double) fp_shape->GetAngle() / 10.0 );
+                SHAPE_ARC arc( fp_shape->GetCenter0(), fp_shape->GetStart0(), fp_shape->GetArcAngle() / 10.0 );
                 lineChain.Append( arc );
             }
             else
             {
-                SHAPE_ARC arc( shape->GetCenter(), shape->GetArcStart(), (double) shape->GetAngle() / 10.0 );
+                SHAPE_ARC arc( shape->GetCenter(), shape->GetStart(), shape->GetArcAngle() / 10.0 );
                 lineChain.Append( arc );
             }
         }
@@ -3041,15 +3041,13 @@ std::vector<PCB_TRACK*> CADSTAR_PCB_ARCHIVE_LOADER::makeTracksFromShapes(
             if( shape->GetClass() == wxT( "MGRAPHIC" ) )
             {
                 FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
-                SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetEnd0(),
-                               (double) fp_shape->GetAngle() / 10.0 );
-                SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetEnd0(),
-                               fp_shape->GetAngle() / 10.0 );
+                SHAPE_ARC arc( fp_shape->GetCenter0(), fp_shape->GetStart0(),
+                               fp_shape->GetArcAngle() / 10.0 );
                 track = new PCB_ARC( aParentContainer, &arc );
             }
             else
             {
-                SHAPE_ARC arc( shape->GetCenter(), shape->GetArcStart(), (double) shape->GetAngle() / 10.0 );
+                SHAPE_ARC arc( shape->GetCenter(), shape->GetStart(), shape->GetArcAngle() / 10.0 );
                 track = new PCB_ARC( aParentContainer, &arc );
             }
             break;
diff --git a/pcbnew/plugins/eagle/eagle_plugin.cpp b/pcbnew/plugins/eagle/eagle_plugin.cpp
index 8d0f0cdcb5..7462cb5dc1 100644
--- a/pcbnew/plugins/eagle/eagle_plugin.cpp
+++ b/pcbnew/plugins/eagle/eagle_plugin.cpp
@@ -715,6 +715,7 @@ void EAGLE_PLUGIN::loadPlain( wxXmlNode* aGraphics )
 
                 if( !w.curve )
                 {
+                    shape->SetShape( SHAPE_T::SEGMENT );
                     shape->SetStart( start );
                     shape->SetEnd( end );
                 }
@@ -723,9 +724,9 @@ void EAGLE_PLUGIN::loadPlain( wxXmlNode* aGraphics )
                     wxPoint center = ConvertArcCenter( start, end, *w.curve );
 
                     shape->SetShape( SHAPE_T::ARC );
-                    shape->SetStart( center );
-                    shape->SetEnd( start );
-                    shape->SetAngle( *w.curve * -10.0 ); // KiCad rotates the other way
+                    shape->SetCenter( center );
+                    shape->SetStart( start );
+                    shape->SetArcAngleAndEnd( *w.curve * -10.0 ); // KiCad rotates the other way
                 }
 
                 shape->SetLayer( layer );
@@ -1773,9 +1774,9 @@ void EAGLE_PLUGIN::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
         dwg = new FP_SHAPE( aFootprint, SHAPE_T::ARC );
         wxPoint center = ConvertArcCenter( start, end, *w.curve );
 
-        dwg->SetStart0( center );
-        dwg->SetEnd0( start );
-        dwg->SetAngle( *w.curve * -10.0 ); // KiCad rotates the other way
+        dwg->SetCenter0( center );
+        dwg->SetStart0( start );
+        dwg->SetArcAngleAndEnd0( *w.curve * -10.0 ); // KiCad rotates the other way
     }
 
     dwg->SetLayer( layer );
diff --git a/pcbnew/plugins/fabmaster/import_fabmaster.cpp b/pcbnew/plugins/fabmaster/import_fabmaster.cpp
index 255e2d838f..f1d3111372 100644
--- a/pcbnew/plugins/fabmaster/import_fabmaster.cpp
+++ b/pcbnew/plugins/fabmaster/import_fabmaster.cpp
@@ -2120,16 +2120,16 @@ bool FABMASTER::loadFootprints( BOARD* aBoard )
                         if( src->mirror )
                         {
                             arc->SetLayer( FlipLayer( layer ) );
-                            arc->SetArcCenter( wxPoint( lsrc->center_x, 2 * src->y - lsrc->center_y ));
-                            arc->SetArcStart( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
-                            arc->SetAngle( lsrc->result.GetCentralAngle() * 10.0 );
+                            arc->SetCenter( wxPoint( lsrc->center_x, 2 * src->y - lsrc->center_y ) );
+                            arc->SetStart( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
+                            arc->SetArcAngleAndEnd0( lsrc->result.GetCentralAngle() * 10.0 );
                         }
                         else
                         {
                             arc->SetLayer( layer );
-                            arc->SetArcCenter( wxPoint( lsrc->center_x, lsrc->center_y ));
-                            arc->SetArcStart( wxPoint( lsrc->end_x, lsrc->end_y ) );
-                            arc->SetAngle( -lsrc->result.GetCentralAngle() * 10.0 );
+                            arc->SetCenter( wxPoint( lsrc->center_x, lsrc->center_y ) );
+                            arc->SetStart( wxPoint( lsrc->end_x, lsrc->end_y ) );
+                            arc->SetArcAngleAndEnd0( -lsrc->result.GetCentralAngle() * 10.0 );
                         }
 
                         arc->SetWidth( lsrc->width );
@@ -2773,9 +2773,9 @@ bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRA
 
             PCB_SHAPE* arc = new PCB_SHAPE( aBoard, SHAPE_T::ARC );
             arc->SetLayer( layer );
-            arc->SetArcCenter( wxPoint( src->center_x, src->center_y ));
-            arc->SetArcStart( wxPoint( src->start_x, src->start_y ) );
-            arc->SetAngle( src->result.GetCentralAngle() * 10.0 );
+            arc->SetCenter( wxPoint( src->center_x, src->center_y ) );
+            arc->SetStart( wxPoint( src->start_x, src->start_y ) );
+            arc->SetArcAngleAndEnd( src->result.GetCentralAngle() * 10.0 );
             arc->SetWidth( src->width );
 
             if( arc->GetWidth() == 0 )
@@ -2888,9 +2888,9 @@ bool FABMASTER::loadGraphics( BOARD* aBoard )
 
                 PCB_SHAPE* arc = new PCB_SHAPE( aBoard, SHAPE_T::ARC );
                 arc->SetLayer( layer );
-                arc->SetArcCenter( wxPoint( src->center_x, src->center_y ));
-                arc->SetArcStart( wxPoint( src->start_x, src->start_y ) );
-                arc->SetAngle( src->result.GetCentralAngle() * 10.0 );
+                arc->SetCenter( wxPoint( src->center_x, src->center_y ) );
+                arc->SetStart( wxPoint( src->start_x, src->start_y ) );
+                arc->SetArcAngleAndEnd( src->result.GetCentralAngle() * 10.0 );
                 arc->SetWidth( src->width );
 
                 aBoard->Add( arc, ADD_MODE::APPEND );
diff --git a/pcbnew/plugins/geda/gpcb_plugin.cpp b/pcbnew/plugins/geda/gpcb_plugin.cpp
index f5684a2710..f898dfda90 100644
--- a/pcbnew/plugins/geda/gpcb_plugin.cpp
+++ b/pcbnew/plugins/geda/gpcb_plugin.cpp
@@ -497,7 +497,7 @@ FOOTPRINT* GPCB_FPL_CACHE::parseFOOTPRINT( LINE_READER* aLineReader )
             wxPoint centre( parseInt( parameters[2], conv_unit ),
                             parseInt( parameters[3], conv_unit ) );
 
-            shape->SetStart0( centre );
+            shape->SetCenter0( centre );
 
             // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
             double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
@@ -509,14 +509,14 @@ FOOTPRINT* GPCB_FPL_CACHE::parseFOOTPRINT( LINE_READER* aLineReader )
             if( sweep_angle == -3600.0 )
                 shape->SetShape( SHAPE_T::CIRCLE );
 
-            // Angle value is clockwise in gpcb and Pcbnew.
-            shape->SetAngle( sweep_angle );
-            shape->SetEnd0( wxPoint( radius, 0 ) );
-
             // Calculate start point coordinate of arc
-            wxPoint arcStart( shape->GetEnd0() );
+            wxPoint arcStart( radius, 0 );
             RotatePoint( &arcStart, -start_angle );
-            shape->SetEnd0( centre + arcStart );
+            shape->SetStart0( arcStart + centre );
+
+            // Angle value is clockwise in gpcb and Pcbnew.
+            shape->SetArcAngleAndEnd0( sweep_angle );
+
             shape->SetWidth( parseInt( parameters[8], conv_unit ) );
             shape->SetDrawCoord();
             continue;
diff --git a/pcbnew/plugins/kicad/kicad_plugin.cpp b/pcbnew/plugins/kicad/kicad_plugin.cpp
index a518c34caa..bbe2ee5014 100644
--- a/pcbnew/plugins/kicad/kicad_plugin.cpp
+++ b/pcbnew/plugins/kicad/kicad_plugin.cpp
@@ -803,10 +803,6 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const
                      locked.c_str(),
                       FormatInternalUnits( aShape->GetStart() ).c_str(),
                       FormatInternalUnits( aShape->GetEnd() ).c_str() );
-
-        if( aShape->GetAngle() != 0.0 )
-            m_out->Print( 0, " (angle %s)", FormatAngle( aShape->GetAngle() ).c_str() );
-
         break;
 
     case SHAPE_T::RECT:
@@ -826,9 +822,9 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const
     case SHAPE_T::ARC:
         m_out->Print( aNestLevel, "(gr_arc%s (start %s) (end %s) (angle %s)",
                       locked.c_str(),
+                      FormatInternalUnits( aShape->GetCenter() ).c_str(),
                       FormatInternalUnits( aShape->GetStart() ).c_str(),
-                      FormatInternalUnits( aShape->GetEnd() ).c_str(),
-                      FormatAngle( aShape->GetAngle() ).c_str() );
+                      FormatAngle( aShape->GetArcAngle() ).c_str() );
         break;
 
     case SHAPE_T::POLY:
@@ -905,7 +901,7 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const
         break;
 
     default:
-        wxFAIL_MSG( "PCB_IO::format not implemented for " + aShape->SHAPE_T_asString() );
+        UNIMPLEMENTED_FOR( aShape->SHAPE_T_asString() );
         return;
     };
 
@@ -960,9 +956,9 @@ void PCB_IO::format( const FP_SHAPE* aFPShape, int aNestLevel ) const
     case SHAPE_T::ARC:
         m_out->Print( aNestLevel, "(fp_arc%s (start %s) (end %s) (angle %s)",
                       locked.c_str(),
+                      FormatInternalUnits( aFPShape->GetCenter0() ).c_str(),
                       FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
-                      FormatInternalUnits( aFPShape->GetEnd0() ).c_str(),
-                      FormatAngle( aFPShape->GetAngle() ).c_str() );
+                      FormatAngle( aFPShape->GetArcAngle() ).c_str() );
         break;
 
     case SHAPE_T::POLY:
@@ -1635,9 +1631,9 @@ void PCB_IO::format( const PAD* aPad, int aNestLevel ) const
 
             case SHAPE_T::ARC:
                 m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s)",
+                              FormatInternalUnits( primitive->GetCenter() ).c_str(),
                               FormatInternalUnits( primitive->GetStart() ).c_str(),
-                              FormatInternalUnits( primitive->GetEnd() ).c_str(),
-                              FormatAngle( primitive->GetAngle() ).c_str() );
+                              FormatAngle( primitive->GetArcAngle() ).c_str() );
                 break;
 
             case SHAPE_T::CIRCLE:
diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp
index 117d0380e5..b809dcb4d8 100644
--- a/pcbnew/plugins/kicad/pcb_parser.cpp
+++ b/pcbnew/plugins/kicad/pcb_parser.cpp
@@ -2392,7 +2392,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
 
         pt.x = parseBoardUnits( "X coordinate" );
         pt.y = parseBoardUnits( "Y coordinate" );
-        shape->SetArcCenter( pt );
+        shape->SetCenter( pt );
         NeedRIGHT();
         NeedLEFT();
         token = NextTok();
@@ -2402,7 +2402,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
 
         pt.x = parseBoardUnits( "X coordinate" );
         pt.y = parseBoardUnits( "Y coordinate" );
-        shape->SetArcStart( pt );
+        shape->SetStart( pt );
         NeedRIGHT();
         break;
 
@@ -2426,7 +2426,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
 
         pt.x = parseBoardUnits( "X coordinate" );
         pt.y = parseBoardUnits( "Y coordinate" );
-        shape->SetArcCenter( pt );
+        shape->SetStart( pt );
         NeedRIGHT();
         NeedLEFT();
 
@@ -2570,7 +2570,8 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
         Expecting( "gr_arc, gr_circle, gr_curve, gr_line, gr_poly, or gp_rect" );
     }
 
-    bool foundFill = false;
+    bool   foundFill = false;
+    double angle;
 
     for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
     {
@@ -2582,7 +2583,11 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
         switch( token )
         {
         case T_angle:
-            shape->SetAngle( parseDouble( "segment angle" ) * 10.0 );
+            angle = parseDouble( "segment angle" ) * 10.0;
+
+            if( shape->GetShape() == SHAPE_T::ARC )
+                shape->SetArcAngleAndEnd( angle );
+
             NeedRIGHT();
             break;
 
@@ -3419,7 +3424,7 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
             FP_SHAPE* shape = parseFP_SHAPE();
 
             // Drop 0 and NaN angles as these can corrupt/crash the schematic
-            if( std::isnormal( shape->GetAngle() ) )
+            if( std::isnormal( shape->GetArcAngle() ) )
             {
                 shape->SetParent( footprint.get() );
                 shape->SetDrawCoord();
@@ -3654,7 +3659,7 @@ FP_SHAPE* PCB_PARSER::parseFP_SHAPE()
 
         pt.x = parseBoardUnits( "X coordinate" );
         pt.y = parseBoardUnits( "Y coordinate" );
-        shape->SetStart0( pt );
+        shape->SetCenter0( pt );
         NeedRIGHT();
         NeedLEFT();
         token = NextTok();
@@ -3664,7 +3669,7 @@ FP_SHAPE* PCB_PARSER::parseFP_SHAPE()
 
         pt.x = parseBoardUnits( "X coordinate" );
         pt.y = parseBoardUnits( "Y coordinate" );
-        shape->SetEnd0( pt );
+        shape->SetStart0( pt );
         NeedRIGHT();
         NeedLEFT();
         token = NextTok();
@@ -3672,9 +3677,7 @@ FP_SHAPE* PCB_PARSER::parseFP_SHAPE()
         if( token != T_angle )
             Expecting( T_angle );
 
-        // Setting angle will set m_thirdPoint0, so must be done after setting
-        // m_start0 and m_end0
-        shape->SetAngle( parseDouble( "segment angle" ) * 10.0 );
+        shape->SetArcAngleAndEnd0( parseDouble( "segment angle" ) * 10.0 );
         NeedRIGHT();
         break;
 
@@ -4346,8 +4349,8 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
                 {
                 case T_gr_arc:
                     dummyShape = parsePCB_SHAPE();
-                    pad->AddPrimitiveArc( dummyShape->GetCenter(), dummyShape->GetArcStart(),
-                                          dummyShape->GetAngle(), dummyShape->GetWidth() );
+                    pad->AddPrimitiveArc( dummyShape->GetCenter(), dummyShape->GetStart(),
+                                          dummyShape->GetArcAngle(), dummyShape->GetWidth() );
                     break;
 
                 case T_gr_line:
diff --git a/pcbnew/plugins/legacy/legacy_plugin.cpp b/pcbnew/plugins/legacy/legacy_plugin.cpp
index 9af1eda3cf..129cb8797f 100644
--- a/pcbnew/plugins/legacy/legacy_plugin.cpp
+++ b/pcbnew/plugins/legacy/legacy_plugin.cpp
@@ -1582,21 +1582,18 @@ void LEGACY_PLUGIN::loadFP_SHAPE( FOOTPRINT* aFootprint )
     {
     case SHAPE_T::ARC:
     {
-        BIU    start0_x = biuParse( line + SZ( "DA" ), &data );
-        BIU    start0_y = biuParse( data, &data );
-        BIU    end0_x = biuParse( data, &data );
-        BIU    end0_y = biuParse( data, &data );
-        double angle = degParse( data, &data );
+        BIU     center0_x = biuParse( line + SZ( "DA" ), &data );
+        BIU     center0_y = biuParse( data, &data );
+        BIU     start0_x = biuParse( data, &data );
+        BIU     start0_y = biuParse( data, &data );
+        double  angle = degParse( data, &data );
 
         width = biuParse( data, &data );
         layer = layerParse( data );
 
+        dwg->SetCenter0( wxPoint( center0_x, center0_y ) );
         dwg->SetStart0( wxPoint( start0_x, start0_y ) );
-        dwg->SetEnd0( wxPoint( end0_x, end0_y ) );
-
-        // Setting angle will set m_thirdPoint0, so must be done after setting
-        // m_start0 and m_end0
-        dwg->SetAngle( angle );
+        dwg->SetArcAngleAndEnd0( angle );
         break;
     }
 
@@ -1882,7 +1879,7 @@ void LEGACY_PLUGIN::loadPCB_LINE()
                 case 2:
                     double angle;
                     angle = degParse( data );
-                    dseg->SetAngle( angle );    // m_Angle
+                    dseg->SetArcAngleAndEnd( angle );    // m_Angle
                     break;
                 case 3:
                     const_cast<KIID&>( dseg->m_Uuid ) = KIID( data );
diff --git a/pcbnew/plugins/pcad/pcb_arc.cpp b/pcbnew/plugins/pcad/pcb_arc.cpp
index 27addbaa59..18208f7f41 100644
--- a/pcbnew/plugins/pcad/pcb_arc.cpp
+++ b/pcbnew/plugins/pcad/pcb_arc.cpp
@@ -174,12 +174,9 @@ void PCB_ARC::AddToFootprint( FOOTPRINT* aFootprint )
         FP_SHAPE* arc = new FP_SHAPE( aFootprint, IsCircle() ? SHAPE_T::CIRCLE : SHAPE_T::ARC );
         aFootprint->Add( arc );
 
-        arc->SetStart0( wxPoint( m_positionX, m_positionY ) );
-        arc->SetEnd0( wxPoint( m_StartX, m_StartY ) );
-
-        // Setting angle will set m_thirdPoint0, so must be done after setting
-        // m_start0 and m_end0
-        arc->SetAngle( -m_Angle );
+        arc->SetCenter0( wxPoint( m_positionX, m_positionY ) );
+        arc->SetStart0( wxPoint( m_StartX, m_StartY ) );
+        arc->SetArcAngleAndEnd0( -m_Angle );
 
         arc->SetWidth( m_Width );
         arc->SetLayer( m_KiCadLayer );
@@ -197,9 +194,9 @@ void PCB_ARC::AddToBoard()
 
     arc->SetFilled( false );
     arc->SetLayer( m_KiCadLayer );
-    arc->SetStart( wxPoint( m_positionX, m_positionY ) );
-    arc->SetEnd( wxPoint( m_StartX, m_StartY ) );
-    arc->SetAngle( -m_Angle );
+    arc->SetCenter( wxPoint( m_positionX, m_positionY ) );
+    arc->SetStart( wxPoint( m_StartX, m_StartY ) );
+    arc->SetArcAngleAndEnd( -m_Angle );
     arc->SetWidth( m_Width );
 }
 
diff --git a/pcbnew/plugins/pcad/pcb_line.cpp b/pcbnew/plugins/pcad/pcb_line.cpp
index b6a112d9c1..cf143df363 100644
--- a/pcbnew/plugins/pcad/pcb_line.cpp
+++ b/pcbnew/plugins/pcad/pcb_line.cpp
@@ -150,7 +150,7 @@ void PCB_LINE::AddToBoard()
     }
     else
     {
-        PCB_SHAPE* segment = new PCB_SHAPE( m_board );
+        PCB_SHAPE* segment = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
         m_board->Add( segment, ADD_MODE::APPEND );
 
         segment->SetLayer( m_KiCadLayer );
diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp
index 6d222d2835..ef399c22f0 100644
--- a/pcbnew/specctra_import_export/specctra_export.cpp
+++ b/pcbnew/specctra_import_export/specctra_export.cpp
@@ -802,10 +802,10 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint )
             path->SetAperture( 0 );//scale( graphic->GetWidth() ) );
             path->SetLayerId( "signal" );
 
-            wxPoint arc_centre = graphic->GetStart0();
-            double  radius = graphic->GetRadius() + graphic->GetWidth() / 2;
-            double  arcStartDeg = graphic->GetArcAngleStart() / 10.0;
-            double  arcAngleDeg = graphic->GetAngle() / 10.0;
+            wxPoint arc_centre = graphic->GetCenter0();
+            double radius = graphic->GetRadius() + graphic->GetWidth()/2;
+            double arcStartDeg = graphic->GetArcAngleStart() / 10.0;
+            double arcAngleDeg = graphic->GetArcAngle() / 10.0;
 
             // For some obscure reason, FreeRouter does not show the same polygonal
             // shape for polygons CW and CCW. So used only the order of corners
@@ -841,10 +841,10 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint )
 
             wxPoint move = graphic->GetCenter() - arc_centre;
 
-            TransformCircleToPolygon( polyBuffer, graphic->GetArcStart() - move,
+            TransformCircleToPolygon( polyBuffer, graphic->GetStart() - move,
                                       graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
 
-            TransformCircleToPolygon( polyBuffer, graphic->GetArcEnd() - move,
+            TransformCircleToPolygon( polyBuffer, graphic->GetEnd() - move,
                                       graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
 
             polyBuffer.Simplify( SHAPE_POLY_SET::PM_FAST );
diff --git a/pcbnew/tools/convert_tool.cpp b/pcbnew/tools/convert_tool.cpp
index c150818465..9bb1dc6895 100644
--- a/pcbnew/tools/convert_tool.cpp
+++ b/pcbnew/tools/convert_tool.cpp
@@ -321,7 +321,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
                         else
                         {
                             PCB_SHAPE* ps = static_cast<PCB_SHAPE*>( aItem );
-                            arc = SHAPE_ARC( ps->GetArcStart(), ps->GetArcMid(), ps->GetArcEnd(),
+                            arc = SHAPE_ARC( ps->GetStart(), ps->GetArcMid(), ps->GetEnd(),
                                              ps->GetWidth() );
                         }
 
@@ -653,8 +653,8 @@ int CONVERT_TOOL::CreateLines( const TOOL_EVENT& aEvent )
                     PCB_ARC* arc = new PCB_ARC( parent );
 
                     arc->SetLayer( targetLayer );
-                    arc->SetStart( graphic->GetArcStart() );
-                    arc->SetEnd( graphic->GetArcEnd() );
+                    arc->SetStart( graphic->GetStart() );
+                    arc->SetEnd( graphic->GetEnd() );
                     arc->SetMid( graphic->GetArcMid() );
                     commit.Add( arc );
 
@@ -804,20 +804,19 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
     if( source->Type() == PCB_SHAPE_T || source->Type() == PCB_FP_SHAPE_T )
     {
         PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
-        PCB_SHAPE* arc  = new PCB_SHAPE( parent );
+        PCB_SHAPE* arc  = new PCB_SHAPE( parent, SHAPE_T::ARC );
 
-        VECTOR2I center = GetArcCenter( start, mid, end );
+        VECTOR2I center = CalcArcCenter( start, mid, end );
 
-        arc->SetShape( SHAPE_T::ARC );
         arc->SetFilled( false );
         arc->SetLayer( layer );
         arc->SetWidth( line->GetWidth() );
 
-        arc->SetArcCenter( wxPoint( center ));
-        arc->SetArcStart( wxPoint( start ) );
-        arc->SetAngle( GetArcAngle( start, mid, end ) );
+        arc->SetCenter( wxPoint( center ) );
+        arc->SetStart( wxPoint( start ) );
+        arc->SetEnd( wxPoint( end ) );
+        arc->SetArcAngle( CalcArcAngle( start, mid, end ) );
 
-        arc->SetArcEnd( wxPoint( end ) );
         commit.Add( arc );
     }
     else
@@ -848,21 +847,13 @@ OPT<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth )
     case PCB_SHAPE_T:
     case PCB_FP_SHAPE_T:
     {
-        PCB_SHAPE* line = static_cast<PCB_SHAPE*>( aItem );
+        PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
 
         if( aWidth )
-            *aWidth = line->GetWidth();
+            *aWidth = shape->GetWidth();
 
-        if( line->GetShape() == SHAPE_T::SEGMENT )
-        {
-            return boost::make_optional<SEG>( { VECTOR2I( line->GetStart() ),
-                                                VECTOR2I( line->GetEnd() ) } );
-        }
-        else
-        {
-            return boost::make_optional<SEG>( { VECTOR2I( line->GetArcStart() ),
-                                                VECTOR2I( line->GetArcEnd() ) } );
-        }
+        return boost::make_optional<SEG>( { VECTOR2I( shape->GetStart() ),
+                                            VECTOR2I( shape->GetEnd() ) } );
     }
 
     case PCB_TRACE_T:
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
index cdce6f07ab..78df65bd1e 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -1713,17 +1713,16 @@ bool DRAWING_TOOL::drawSegment( const std::string& aTool, PCB_SHAPE** aGraphic,
 static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER& aMgr,
                                           PCB_SHAPE& aArc )
 {
-    auto vec = aMgr.GetOrigin();
+    VECTOR2I vec = aMgr.GetOrigin();
 
-    aArc.SetArcCenter( { vec.x, vec.y } );
+    aArc.SetCenter( (wxPoint) vec );
 
     vec = aMgr.GetStartRadiusEnd();
-    aArc.SetArcStart( { vec.x, vec.y } );
-
-    aArc.SetAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
-
+    aArc.SetStart( (wxPoint) vec );
     vec = aMgr.GetEndRadiusEnd();
-    aArc.SetArcEnd( { vec.x, vec.y } );
+    aArc.SetEnd( (wxPoint) vec );
+
+    aArc.SetArcAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
 }
 
 
@@ -1892,7 +1891,7 @@ bool DRAWING_TOOL::drawArc( const std::string& aTool, PCB_SHAPE** aGraphic, bool
         {
             if( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_START )
             {
-                graphic->SetAngle( 900, true );
+                graphic->SetArcAngleAndEnd( 900 );
                 frame()->OnEditItemRequest( graphic );
                 m_view->Update( &preview );
                 frame()->SetMsgPanel( graphic );
diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
index e4be983355..5b976f2a17 100644
--- a/pcbnew/tools/edit_tool.cpp
+++ b/pcbnew/tools/edit_tool.cpp
@@ -555,7 +555,7 @@ int EDIT_TOOL::DragArcTrack( const TOOL_EVENT& aEvent )
         VECTOR2I newCenter = circlehelper.Center;
         VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
         VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
-        VECTOR2I newMid = GetArcMid( newStart, newEnd, newCenter );
+        VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
 
         // Update objects
         theArc->SetStart( (wxPoint) newStart );
diff --git a/pcbnew/tools/pad_tool.cpp b/pcbnew/tools/pad_tool.cpp
index e65645f4ce..8dd753d5c3 100644
--- a/pcbnew/tools/pad_tool.cpp
+++ b/pcbnew/tools/pad_tool.cpp
@@ -23,6 +23,7 @@
 
 
 #include "pad_tool.h"
+#include <macros.h>
 #include <class_draw_panel_gal.h>
 #include <view/view_controls.h>
 #include <view/view.h>
@@ -612,12 +613,38 @@ PCB_LAYER_ID PAD_TOOL::explodePad( PAD* aPad )
             shape->SetShape( primitive->GetShape() );
             shape->SetFilled( primitive->IsFilled() );
             shape->SetWidth( primitive->GetWidth() );
-            shape->SetStart( primitive->GetStart() );
-            shape->SetEnd( primitive->GetEnd() );
-            shape->SetBezierC1( primitive->GetBezierC1());
-            shape->SetBezierC2( primitive->GetBezierC2());
-            shape->SetAngle( primitive->GetAngle() );
-            shape->SetPolyShape( primitive->GetPolyShape() );
+
+            switch( shape->GetShape() )
+            {
+            case SHAPE_T::SEGMENT:
+            case SHAPE_T::RECT:
+            case SHAPE_T::CIRCLE:
+                shape->SetStart( primitive->GetStart() );
+                shape->SetEnd( primitive->GetEnd() );
+                break;
+
+            case SHAPE_T::ARC:
+                shape->SetStart( primitive->GetStart() );
+                shape->SetEnd( primitive->GetEnd() );
+                shape->SetCenter( primitive->GetCenter() );
+                shape->SetArcAngle( primitive->GetArcAngle() );
+                break;
+
+            case SHAPE_T::BEZIER:
+                shape->SetStart( primitive->GetStart() );
+                shape->SetEnd( primitive->GetEnd() );
+                shape->SetBezierC1( primitive->GetBezierC1() );
+                shape->SetBezierC2( primitive->GetBezierC2() );
+                break;
+
+            case SHAPE_T::POLY:
+                shape->SetPolyShape( primitive->GetPolyShape() );
+                break;
+
+            default:
+                UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
+            }
+
             shape->SetLocalCoord();
             shape->Move( aPad->GetPosition() );
             shape->Rotate( aPad->GetPosition(), aPad->GetOrientation() );
@@ -724,12 +751,38 @@ void PAD_TOOL::recombinePad( PAD* aPad )
         pcbShape->SetShape( fpShape->GetShape() );
         pcbShape->SetFilled( fpShape->IsFilled() );
         pcbShape->SetWidth( fpShape->GetWidth() );
-        pcbShape->SetStart( fpShape->GetStart() );
-        pcbShape->SetEnd( fpShape->GetEnd() );
-        pcbShape->SetBezierC1( fpShape->GetBezierC1());
-        pcbShape->SetBezierC2( fpShape->GetBezierC2());
-        pcbShape->SetAngle( fpShape->GetAngle() );
-        pcbShape->SetPolyShape( fpShape->GetPolyShape() );
+
+
+        switch( pcbShape->GetShape() )
+        {
+        case SHAPE_T::SEGMENT:
+        case SHAPE_T::RECT:
+        case SHAPE_T::CIRCLE:
+            pcbShape->SetStart( fpShape->GetStart() );
+            pcbShape->SetEnd( fpShape->GetEnd() );
+            break;
+
+        case SHAPE_T::ARC:
+            pcbShape->SetStart( fpShape->GetStart() );
+            pcbShape->SetEnd( fpShape->GetEnd() );
+            pcbShape->SetCenter( fpShape->GetCenter() );
+            pcbShape->SetArcAngle( fpShape->GetArcAngle() );
+            break;
+
+        case SHAPE_T::BEZIER:
+            pcbShape->SetStart( fpShape->GetStart() );
+            pcbShape->SetEnd( fpShape->GetEnd() );
+            pcbShape->SetBezierC1( fpShape->GetBezierC1() );
+            pcbShape->SetBezierC2( fpShape->GetBezierC2() );
+            break;
+
+        case SHAPE_T::POLY:
+            pcbShape->SetPolyShape( fpShape->GetPolyShape() );
+            break;
+
+        default:
+            UNIMPLEMENTED_FOR( pcbShape->SHAPE_T_asString() );
+        }
 
         pcbShape->Move( - aPad->GetPosition() );
         pcbShape->Rotate( wxPoint( 0, 0 ), - aPad->GetOrientation() );
diff --git a/pcbnew/tools/pcb_grid_helper.cpp b/pcbnew/tools/pcb_grid_helper.cpp
index d2456045f0..dd4472b83a 100644
--- a/pcbnew/tools/pcb_grid_helper.cpp
+++ b/pcbnew/tools/pcb_grid_helper.cpp
@@ -543,8 +543,8 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos
                 }
 
                 case SHAPE_T::ARC:
-                    addAnchor( shape->GetArcStart(), CORNER | SNAPPABLE, shape );
-                    addAnchor( shape->GetArcEnd(), CORNER | SNAPPABLE, shape );
+                    addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape );
+                    addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape );
                     addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape );
                     addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape );
                     break;
@@ -598,7 +598,7 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos
                     KI_FALLTHROUGH;
 
                 default:
-                    addAnchor( shape->GetStart(), ORIGIN | SNAPPABLE, shape );
+                    addAnchor( shape->GetPosition(), ORIGIN | SNAPPABLE, shape );
                     break;
             }
             break;
diff --git a/pcbnew/tools/pcb_point_editor.cpp b/pcbnew/tools/pcb_point_editor.cpp
index 9e39bb5bbe..0f8b12e3d6 100644
--- a/pcbnew/tools/pcb_point_editor.cpp
+++ b/pcbnew/tools/pcb_point_editor.cpp
@@ -215,9 +215,9 @@ std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
 
         case SHAPE_T::ARC:
             points->AddPoint( shape->GetCenter() );
-            points->AddPoint( shape->GetArcStart() );
+            points->AddPoint( shape->GetStart() );
             points->AddPoint( shape->GetArcMid() );
-            points->AddPoint( shape->GetArcEnd() );
+            points->AddPoint( shape->GetEnd() );
 
             points->Point( ARC_MID ).SetGridConstraint( IGNORE_GRID );
             points->Point( ARC_START ).SetGridConstraint( SNAP_TO_GRID );
@@ -613,7 +613,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
     VECTOR2I p1, p2, p3;
     // p1 does not move, p2 does.
 
-    if( aStart != aArc->GetArcStart() )
+    if( aStart != aArc->GetStart() )
     {
         start       = aCursor;
         p1          = aEnd;
@@ -621,7 +621,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
         p3          = aMid;
         movingStart = true;
     }
-    else if( aEnd != aArc->GetArcEnd() )
+    else if( aEnd != aArc->GetEnd() )
     {
         end         = aCursor;
         p1          = aStart;
@@ -736,7 +736,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
         // v4 is the new center
         v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
 
-        clockwise = aArc->GetAngle() > 0;
+        clockwise = aArc->GetArcAngle() > 0;
 
         if( transformCircle )
             clockwise = !clockwise;
@@ -759,13 +759,13 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
 
         if( arcValid )
         {
-            aArc->SetAngle( newAngle, false );
-            aArc->SetArcCenter( ( wxPoint ) center );
+            aArc->SetCenter( (wxPoint) aCenter );
+            aArc->SetArcAngle( newAngle );
 
             if( movingStart )
-                aArc->SetArcStart( ( wxPoint ) start );
+                aArc->SetStart( (wxPoint) aStart );
             else
-                aArc->SetArcEnd( ( wxPoint ) end );
+                aArc->SetEnd( (wxPoint) aEnd );
         }
     }
 }
@@ -886,7 +886,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepCenter( PCB_SHAPE* aArc, const VECTOR2
 
     // p1 does not move, p2 does.
 
-    if( aStart != aArc->GetArcStart() )
+    if( aStart != aArc->GetStart() )
     {
         p1          = aEnd;
         p2          = aStart;
@@ -932,7 +932,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepCenter( PCB_SHAPE* aArc, const VECTOR2
     p1 = p1 + aCenter;
     p2 = p2 + aCenter;
 
-    clockwise = aArc->GetAngle() > 0;
+    clockwise = aArc->GetArcAngle() > 0;
 
     VECTOR2D startLine = aStart - aCenter;
     VECTOR2D endLine   = aEnd - aCenter;
@@ -943,13 +943,13 @@ void PCB_POINT_EDITOR::editArcEndpointKeepCenter( PCB_SHAPE* aArc, const VECTOR2
     else if( !clockwise && newAngle > 0.0 )
         newAngle -= 3600.0;
 
-    aArc->SetAngle( newAngle, false );
-    aArc->SetArcCenter((wxPoint) aCenter );
+    aArc->SetCenter( (wxPoint) aCenter );
+    aArc->SetArcAngle( newAngle );
 
     if( movingStart )
-        aArc->SetArcStart( (wxPoint) aStart );
+        aArc->SetStart( (wxPoint) aStart );
     else
-        aArc->SetArcEnd( (wxPoint) aEnd );
+        aArc->SetEnd( (wxPoint) aEnd );
 }
 
 
@@ -1009,8 +1009,8 @@ void PCB_POINT_EDITOR::editArcMidKeepCenter( PCB_SHAPE* aArc, const VECTOR2I& aC
     start = start + aCenter;
     end   = end + aCenter;
 
-    aArc->SetArcStart( (wxPoint) start );
-    aArc->SetArcEnd( (wxPoint) end );
+    aArc->SetStart( (wxPoint) start );
+    aArc->SetEnd( (wxPoint) end );
 }
 
 
@@ -1577,9 +1577,9 @@ void PCB_POINT_EDITOR::updatePoints()
 
         case SHAPE_T::ARC:
             m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
-            m_editPoints->Point( ARC_START ).SetPosition( shape->GetArcStart() );
+            m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
             m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
-            m_editPoints->Point( ARC_END ).SetPosition( shape->GetArcEnd() );
+            m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
             break;
 
         case SHAPE_T::CIRCLE:
diff --git a/qa/eeschema/CMakeLists.txt b/qa/eeschema/CMakeLists.txt
index 254e6fbce2..552097d214 100644
--- a/qa/eeschema/CMakeLists.txt
+++ b/qa/eeschema/CMakeLists.txt
@@ -51,7 +51,6 @@ set( QA_EESCHEMA_SRCS
     sch_plugins/altium/test_altium_parser_sch.cpp
 
     test_eagle_plugin.cpp
-    test_lib_arc.cpp
     test_lib_part.cpp
     test_netlists.cpp
     test_sch_pin.cpp
diff --git a/qa/eeschema/test_lib_arc.cpp b/qa/eeschema/test_lib_arc.cpp
deleted file mode 100644
index 6dfc256159..0000000000
--- a/qa/eeschema/test_lib_arc.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright (C) 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
- * 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
- */
-
-/**
- * @file
- * Test suite for LIB_ARC
- */
-
-#include <qa_utils/wx_utils/unit_test_utils.h>
-#include <trigo.h>
-#include <convert_to_biu.h>
-
-// Code under test
-#include <lib_arc.h>
-
-class TEST_LIB_ARC_FIXTURE
-{
-public:
-    TEST_LIB_ARC_FIXTURE() :
-        m_arc( nullptr )
-    {
-    }
-
-    ///> Part with no extra data set
-    LIB_ARC m_arc;
-};
-
-
-/**
- * Declare the test suite
- */
-BOOST_FIXTURE_TEST_SUITE( LibArc, TEST_LIB_ARC_FIXTURE )
-
-
-/**
- * Check that we can get the default properties out as expected
- */
-BOOST_AUTO_TEST_CASE( DefaultProperties )
-{
-    BOOST_CHECK_EQUAL( m_arc.Type(), LIB_ARC_T );
-    BOOST_CHECK_EQUAL( m_arc.GetClass(), "LIB_ARC" );
-    BOOST_CHECK_EQUAL( m_arc.GetPosition(), wxPoint( 0, 0 ) );
-}
-
-
-/**
- * Test the function that calculates the radius angles based on the center, start, and end points.
- */
-BOOST_AUTO_TEST_CASE( TestCalcRadiusAngles )
-{
-    double radius = 5.0;   // Use millimeters and convert to internal units.
-    int startX = Millimeter2iu( radius * cos( DEG2RAD( 10.0 ) ) );
-    int startY = Millimeter2iu( radius * sin( DEG2RAD( 10.0 ) ) );
-    int endX = Millimeter2iu( radius * cos( DEG2RAD( 45.0 ) ) );
-    int endY = Millimeter2iu( radius * sin( DEG2RAD( 45.0 ) ) );
-
-    m_arc.SetStart( wxPoint( startX, startY ) );
-    m_arc.SetEnd( wxPoint( endX, endY ) );
-
-    m_arc.CalcRadiusAngles();
-    BOOST_CHECK_EQUAL( m_arc.GetFirstRadiusAngle(), 100 );
-    BOOST_CHECK_EQUAL( m_arc.GetSecondRadiusAngle(), 450 );
-
-    // Set arc end point in the second quadrant.
-    endX = Millimeter2iu( radius * cos( DEG2RAD( 145.0 ) ) );
-    endY = Millimeter2iu( radius * sin( DEG2RAD( 145.0 ) ) );
-    m_arc.SetEnd( wxPoint( endX, endY ) );
-    m_arc.CalcRadiusAngles();
-    BOOST_CHECK_EQUAL( m_arc.GetFirstRadiusAngle(), 100 );
-    BOOST_CHECK_EQUAL( m_arc.GetSecondRadiusAngle(), 1450 );
-}
-
-
-/**
- * Test the function that calculates the mid point based on the start and end angles and
- * radius length.
- */
-BOOST_AUTO_TEST_CASE( TestCalcMidPoint )
-{
-    // Midpoint angle is 77.5 degrees.
-    m_arc.SetRadius( Millimeter2iu( 5.0 ) );
-    m_arc.SetFirstRadiusAngle( 100 );
-    m_arc.SetSecondRadiusAngle( 1450 );
-    BOOST_CHECK_EQUAL( m_arc.CalcMidPoint(), VECTOR2I( 10822, 48815 ) );
-    m_arc.SetFirstRadiusAngle( 850 );
-    m_arc.SetSecondRadiusAngle( 950 );
-    BOOST_CHECK_EQUAL( m_arc.CalcMidPoint(), VECTOR2I( 0, 50000 ) );
-    m_arc.SetFirstRadiusAngle( 1700 );
-    m_arc.SetSecondRadiusAngle( 1900 );
-    BOOST_CHECK_EQUAL( m_arc.CalcMidPoint(), VECTOR2I( -50000, 0 ) );
-    m_arc.SetFirstRadiusAngle( 2500 );
-    m_arc.SetSecondRadiusAngle( 2900 );
-    BOOST_CHECK_EQUAL( m_arc.CalcMidPoint(), VECTOR2I( 0, -50000 ) );
-    m_arc.SetFirstRadiusAngle( 3500 );
-    m_arc.SetSecondRadiusAngle( 100 );
-    BOOST_CHECK_EQUAL( m_arc.CalcMidPoint(), VECTOR2I( 50000, 0 ) );
-}
-
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/qa/pcbnew_utils/board_construction_utils.cpp b/qa/pcbnew_utils/board_construction_utils.cpp
index 78e65c1fee..98d1bf5298 100644
--- a/qa/pcbnew_utils/board_construction_utils.cpp
+++ b/qa/pcbnew_utils/board_construction_utils.cpp
@@ -61,9 +61,9 @@ void DrawArc( FOOTPRINT& aFootprint, const VECTOR2I& aCentre, const VECTOR2I& aS
 {
     auto seg = std::make_unique<FP_SHAPE>( &aFootprint, SHAPE_T::ARC );
 
-    seg->SetStart0( (wxPoint) aCentre );
-    seg->SetEnd0( (wxPoint) aStart );
-    seg->SetAngle( aAngle * 10 );
+    seg->SetCenter0( (wxPoint) aCentre );
+    seg->SetStart0( (wxPoint) aStart );
+    seg->SetArcAngleAndEnd0( aAngle * 10 );
 
     seg->SetWidth( aWidth );
     seg->SetLayer( aLayer );