diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp
index 1b5149b589..58d71e4c0e 100644
--- a/common/advanced_config.cpp
+++ b/common/advanced_config.cpp
@@ -104,7 +104,7 @@ static const wxChar EnableLibWithText[] = wxT( "EnableLibWithText" );
 static const wxChar EnableEeschemaPrintCairo[] = wxT( "EnableEeschemaPrintCairo" );
 static const wxChar DisambiguationTime[] = wxT( "DisambiguationTime" );
 static const wxChar PcbSelectionVisibilityRatio[] = wxT( "PcbSelectionVisibilityRatio" );
-static const wxChar MinimumSegmentLength[] = wxT( "MinimumSegmentLength" );
+static const wxChar FontErrorSize[] = wxT( "FontErrorSize" );
 static const wxChar OcePluginLinearDeflection[] = wxT( "OcePluginLinearDeflection" );
 static const wxChar OcePluginAngularDeflection[] = wxT( "OcePluginAngularDeflection" );
 static const wxChar TriangulateSimplificationLevel[] = wxT( "TriangulateSimplificationLevel" );
@@ -257,7 +257,7 @@ ADVANCED_CFG::ADVANCED_CFG()
 
     m_PcbSelectionVisibilityRatio = 1.0;
 
-    m_MinimumSegmentLength      = 50;
+    m_FontErrorSize             = 16;
 
     m_OcePluginLinearDeflection = 0.14;
     m_OcePluginAngularDeflection = 30;
@@ -469,9 +469,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
                                                   &m_PcbSelectionVisibilityRatio,
                                                   m_PcbSelectionVisibilityRatio, 0.0, 1.0 ) );
 
-    configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::MinimumSegmentLength,
-                                                  &m_MinimumSegmentLength,
-                                                  m_MinimumSegmentLength, 10, 1000 ) );
+    configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::FontErrorSize,
+                                                  &m_FontErrorSize,
+                                                  m_FontErrorSize, 0.01, 100 ) );
 
     configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::OcePluginLinearDeflection,
                                                     &m_OcePluginLinearDeflection,
diff --git a/common/eda_shape.cpp b/common/eda_shape.cpp
index b0e7a793fd..919c4d0515 100644
--- a/common/eda_shape.cpp
+++ b/common/eda_shape.cpp
@@ -352,6 +352,7 @@ void EDA_SHAPE::scale( double aScale )
         scalePt( m_end );
         scalePt( m_bezierC1 );
         scalePt( m_bezierC2 );
+        RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
         break;
 
     default:
@@ -488,12 +489,7 @@ void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
             m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
         }
 
-        // Rebuild the poly points shape
-        {
-            std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
-            BEZIER_POLY converter( ctrlPoints );
-            converter.GetPoly( m_bezierPoints, m_stroke.GetWidth() );
-        }
+        RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
         break;
 
     default:
@@ -503,7 +499,7 @@ void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
 }
 
 
-void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen )
+void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMaxError )
 {
     // Has meaning only for SHAPE_T::BEZIER
     if( m_shape != SHAPE_T::BEZIER )
@@ -513,30 +509,18 @@ void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen )
     }
 
     // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
-    m_bezierPoints = buildBezierToSegmentsPointsList( aMinSegLen );
-
-    // Ensure last point respects aMinSegLen parameter
-    if( m_bezierPoints.size() > 2 )
-    {
-        int idx = m_bezierPoints.size() - 1;
-
-        if( VECTOR2I( m_bezierPoints[idx] - m_bezierPoints[idx] - 1 ).EuclideanNorm() < aMinSegLen )
-        {
-            m_bezierPoints[idx - 1] = m_bezierPoints[idx];
-            m_bezierPoints.pop_back();
-        }
-    }
+    m_bezierPoints = buildBezierToSegmentsPointsList( aMaxError );
 }
 
 
-const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
+const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
 {
     std::vector<VECTOR2I> bezierPoints;
 
     // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
     std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
     BEZIER_POLY converter( ctrlPoints );
-    converter.GetPoly( bezierPoints, aMinSegLen );
+    converter.GetPoly( bezierPoints, aMaxError );
 
     return bezierPoints;
 }
@@ -930,16 +914,25 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
     }
 
     case SHAPE_T::BEZIER:
-        const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( GetWidth() );
+    {
+        const std::vector<VECTOR2I>* pts = &m_bezierPoints;
+        std::vector<VECTOR2I> updatedBezierPoints;
 
-        for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
+        if( m_bezierPoints.empty() )
         {
-            if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
+            BEZIER_POLY converter( m_start, m_bezierC1, m_bezierC2, m_end );
+            converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
+            pts = &updatedBezierPoints;
+        }
+
+        for( unsigned int i = 1; i < pts->size(); i++ )
+        {
+            if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
                 return true;
         }
 
         return false;
-
+    }
     case SHAPE_T::SEGMENT:
         return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
 
@@ -1132,12 +1125,20 @@ bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) co
 
             // Account for the width of the line
             arect.Inflate( GetWidth() / 2 );
-            unsigned count = m_bezierPoints.size();
+            const std::vector<VECTOR2I>* pts = &m_bezierPoints;
+            std::vector<VECTOR2I> updatedBezierPoints;
 
-            for( unsigned ii = 1; ii < count; ii++ )
+            if( m_bezierPoints.empty() )
             {
-                VECTOR2I vertex = m_bezierPoints[ii - 1];
-                VECTOR2I vertexNext = m_bezierPoints[ii];
+                BEZIER_POLY converter( m_start, m_bezierC1, m_bezierC2, m_end );
+                converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
+                pts = &updatedBezierPoints;
+            }
+
+            for( unsigned ii = 1; ii < pts->size(); ii++ )
+            {
+                VECTOR2I vertex = ( *pts )[ii - 1];
+                VECTOR2I vertexNext = ( *pts )[ii];
 
                 // Test if the point is within aRect
                 if( arect.Contains( vertex ) )
@@ -1278,7 +1279,7 @@ std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineCh
 
     case SHAPE_T::BEZIER:
     {
-        std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width );
+        std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
         VECTOR2I              start_pt = bezierPoints[0];
 
         for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
@@ -1381,7 +1382,7 @@ void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
         SetBezierC2( aPosition );
         m_editState = 1;
 
-        RebuildBezierToSegmentsPointsList( GetWidth() );
+        RebuildBezierToSegmentsPointsList( GetWidth() / 2 );
         break;
 
     case SHAPE_T::POLY:
@@ -1463,7 +1464,7 @@ void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
         case 3: SetBezierC2( aPosition ); break;
         }
 
-        RebuildBezierToSegmentsPointsList( GetWidth() );
+        RebuildBezierToSegmentsPointsList( GetWidth() / 2 );
     }
     break;
 
@@ -1792,7 +1793,7 @@ void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance
         std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
         BEZIER_POLY converter( ctrlPts );
         std::vector<VECTOR2I> poly;
-        converter.GetPoly( poly, GetWidth() );
+        converter.GetPoly( poly, aError );
 
         for( unsigned ii = 1; ii < poly.size(); ii++ )
             TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
diff --git a/common/font/outline_decomposer.cpp b/common/font/outline_decomposer.cpp
index 7dd1de483a..880eb80f57 100644
--- a/common/font/outline_decomposer.cpp
+++ b/common/font/outline_decomposer.cpp
@@ -111,7 +111,8 @@ int OUTLINE_DECOMPOSER::cubicTo( const FT_Vector* aFirstControlPoint,
     bezier.push_back( toVector2D( aEndPoint ) );
 
     std::vector<VECTOR2D> result;
-    decomposer->approximateBezierCurve( result, bezier );
+    BEZIER_POLY           converter( bezier );
+    converter.GetPoly( result, ADVANCED_CFG::GetCfg().m_FontErrorSize );
 
     for( const VECTOR2D& p : result )
         decomposer->addContourPoint( p );
@@ -147,63 +148,6 @@ bool OUTLINE_DECOMPOSER::OutlineToSegments( std::vector<CONTOUR>* aContours )
 }
 
 
-// use converter in kimath
-bool OUTLINE_DECOMPOSER::approximateQuadraticBezierCurve( std::vector<VECTOR2D>&       aResult,
-                                                          const std::vector<VECTOR2D>& aBezier ) const
-{
-    wxASSERT( aBezier.size() == 3 );
-
-    // BEZIER_POLY only handles cubic Bezier curves, even though the
-    // comments say otherwise...
-    //
-    // Quadratic to cubic Bezier conversion:
-    // cpn = Cubic Bezier control points (n = 0..3, 4 in total)
-    // qpn = Quadratic Bezier control points (n = 0..2, 3 in total)
-    // cp0 = qp0, cp1 = qp0 + 2/3 * (qp1 - qp0), cp2 = qp2 + 2/3 * (qp1 - qp2), cp3 = qp2
-
-    std::vector<VECTOR2D> cubic;
-    cubic.reserve( 4 );
-
-    cubic.push_back( aBezier[0] );                                           // cp0
-    cubic.push_back( aBezier[0] + ( ( aBezier[1] - aBezier[0] ) * 2 / 3 ) ); // cp1
-    cubic.push_back( aBezier[2] + ( ( aBezier[1] - aBezier[2] ) * 2 / 3 ) ); // cp2
-    cubic.push_back( aBezier[2] );                                           // cp3
-
-    return approximateCubicBezierCurve( aResult, cubic );
-}
-
-
-bool OUTLINE_DECOMPOSER::approximateCubicBezierCurve( std::vector<VECTOR2D>&       aResult,
-                                                      const std::vector<VECTOR2D>& aCubicBezier ) const
-{
-    wxASSERT( aCubicBezier.size() == 4 );
-
-    static int minimumSegmentLength = ADVANCED_CFG::GetCfg().m_MinimumSegmentLength;
-    BEZIER_POLY   converter( aCubicBezier );
-    converter.GetPoly( aResult, minimumSegmentLength );
-
-    return true;
-}
-
-
-bool OUTLINE_DECOMPOSER::approximateBezierCurve( std::vector<VECTOR2D>&       aResult,
-                                                 const std::vector<VECTOR2D>& aBezier ) const
-{
-    switch( aBezier.size() )
-    {
-    case 4: // cubic
-        return approximateCubicBezierCurve( aResult, aBezier );
-
-    case 3: // quadratic
-        return approximateQuadraticBezierCurve( aResult, aBezier );
-
-    default:
-        // error, only 3 and 4 are acceptable values
-        return false;
-    }
-}
-
-
 int OUTLINE_DECOMPOSER::winding( const std::vector<VECTOR2D>& aContour ) const
 {
     // -1 == counterclockwise, 1 == clockwise
diff --git a/common/font/outline_font.cpp b/common/font/outline_font.cpp
index d11c590eba..85a8337481 100644
--- a/common/font/outline_font.cpp
+++ b/common/font/outline_font.cpp
@@ -95,7 +95,7 @@ FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName, int aFaceIndex )
         // m_face = handle to face object
         // 0 = char width in 1/64th of points ( 0 = same as char height )
         // faceSize() = char height in 1/64th of points
-        // GLYPH_RESOLUTION = horizontal device resolution (288dpi, 4x default)
+        // GLYPH_RESOLUTION = horizontal device resolution (1152dpi, 16x default)
         // 0 = vertical device resolution ( 0 = same as horizontal )
         FT_Set_Char_Size( m_face, 0, faceSize(), GLYPH_RESOLUTION, 0 );
     }
diff --git a/common/io/easyeda/easyeda_parser_base.cpp b/common/io/easyeda/easyeda_parser_base.cpp
index 7e800d1bc1..c2af30d8d3 100644
--- a/common/io/easyeda/easyeda_parser_base.cpp
+++ b/common/io/easyeda/easyeda_parser_base.cpp
@@ -266,7 +266,7 @@ EASYEDA_PARSER_BASE::ParseLineChains( const wxString& data, int aArcMinSegLen, b
             BEZIER_POLY           converter( ctrlPoints );
 
             std::vector<VECTOR2I> bezierPoints;
-            converter.GetPoly( bezierPoints, aArcMinSegLen, 16 );
+            converter.GetPoly( bezierPoints, aArcMinSegLen );
 
             chain.Append( bezierPoints );
 
diff --git a/common/plotters/plotter.cpp b/common/plotters/plotter.cpp
index 5085d1cc48..a9a50466a7 100644
--- a/common/plotters/plotter.cpp
+++ b/common/plotters/plotter.cpp
@@ -244,7 +244,7 @@ void PLOTTER::BezierCurve( const VECTOR2I& aStart, const VECTOR2I& aControl1,
     BEZIER_POLY bezier_converter( ctrlPoints );
 
     std::vector<VECTOR2I> approxPoints;
-    bezier_converter.GetPoly( approxPoints, minSegLen );
+    bezier_converter.GetPoly( approxPoints, aTolerance );
 
     SetCurrentLineWidth( aLineThickness );
     MoveTo( aStart );
diff --git a/eeschema/import_gfx/graphics_importer_lib_symbol.cpp b/eeschema/import_gfx/graphics_importer_lib_symbol.cpp
index 54bd14cba2..adf95516bc 100644
--- a/eeschema/import_gfx/graphics_importer_lib_symbol.cpp
+++ b/eeschema/import_gfx/graphics_importer_lib_symbol.cpp
@@ -217,7 +217,7 @@ void GRAPHICS_IMPORTER_LIB_SYMBOL::AddSpline( const VECTOR2D& aStart,
     spline->SetBezierC1( MapCoordinate( aBezierControl1 ) );
     spline->SetBezierC2( MapCoordinate( aBezierControl2 ) );
     spline->SetEnd( MapCoordinate( aEnd ) );
-    spline->RebuildBezierToSegmentsPointsList( aStroke.GetWidth() );
+    spline->RebuildBezierToSegmentsPointsList( aStroke.GetWidth() / 2 );
 
     // If the spline is degenerated (i.e. a segment) add it as segment or discard it if
     // null (i.e. very small) length
diff --git a/eeschema/import_gfx/graphics_importer_sch.cpp b/eeschema/import_gfx/graphics_importer_sch.cpp
index f5fa2d8830..85c172fb36 100644
--- a/eeschema/import_gfx/graphics_importer_sch.cpp
+++ b/eeschema/import_gfx/graphics_importer_sch.cpp
@@ -207,7 +207,7 @@ void GRAPHICS_IMPORTER_SCH::AddSpline( const VECTOR2D& aStart,
     spline->SetBezierC1( MapCoordinate( aBezierControl1 ) );
     spline->SetBezierC2( MapCoordinate( aBezierControl2 ) );
     spline->SetEnd( MapCoordinate( aEnd ) );
-    spline->RebuildBezierToSegmentsPointsList( aStroke.GetWidth() );
+    spline->RebuildBezierToSegmentsPointsList( aStroke.GetWidth() / 2 );
 
     // If the spline is degenerated (i.e. a segment) add it as segment or discard it if
     // null (i.e. very small) length
diff --git a/eeschema/sch_io/altium/sch_io_altium.cpp b/eeschema/sch_io/altium/sch_io_altium.cpp
index 67b997686d..146300d55b 100644
--- a/eeschema/sch_io/altium/sch_io_altium.cpp
+++ b/eeschema/sch_io/altium/sch_io_altium.cpp
@@ -1947,6 +1947,7 @@ void SCH_IO_ALTIUM::ParseBezier( const std::map<wxString, wxString>& aProperties
                 }
 
                 bezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
+                bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() / 2 );
             }
         }
     }
@@ -2350,7 +2351,7 @@ void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aPro
             schbezier->SetBezierC2( bezier.C2 );
             schbezier->SetEnd( bezier.End );
             schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
-            schbezier->RebuildBezierToSegmentsPointsList( elem.LineWidth );
+            schbezier->RebuildBezierToSegmentsPointsList( elem.LineWidth / 2 );
 
             currentScreen->Append( schbezier );
         }
@@ -2413,7 +2414,7 @@ void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aPro
             }
 
             SetLibShapeLine( elem, schbezier, ALTIUM_SCH_RECORD::ELLIPTICAL_ARC );
-            schbezier->RebuildBezierToSegmentsPointsList( elem.LineWidth );
+            schbezier->RebuildBezierToSegmentsPointsList( elem.LineWidth / 2 );
         }
     }
 }
diff --git a/eeschema/sch_io/kicad_legacy/sch_io_kicad_legacy_lib_cache.cpp b/eeschema/sch_io/kicad_legacy/sch_io_kicad_legacy_lib_cache.cpp
index b030d203fa..f76a5e2a3c 100644
--- a/eeschema/sch_io/kicad_legacy/sch_io_kicad_legacy_lib_cache.cpp
+++ b/eeschema/sch_io/kicad_legacy/sch_io_kicad_legacy_lib_cache.cpp
@@ -1381,7 +1381,7 @@ SCH_SHAPE* SCH_IO_KICAD_LEGACY_LIB_CACHE::loadBezier( LINE_READER& aReader )
     pt.y = -schIUScale.MilsToIU( parseInt( aReader, line, &line ) );
     bezier->SetEnd( pt );
 
-    bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() );
+    bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() / 2 );
 
     if( *line != 0 )
         bezier->SetFillMode( parseFillMode( aReader, line, &line ) );
diff --git a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp
index bcab5f2b65..8c6e51dba3 100644
--- a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp
+++ b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp
@@ -1315,7 +1315,7 @@ SCH_SHAPE* SCH_IO_KICAD_SEXPR_PARSER::parseSymbolBezier()
         }
     }
 
-    bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() );
+    bezier->RebuildBezierToSegmentsPointsList( bezier->GetPenWidth() / 2 );
 
     return bezier.release();
 }
diff --git a/eeschema/tools/ee_point_editor.cpp b/eeschema/tools/ee_point_editor.cpp
index 4b55474d09..b59d33eada 100644
--- a/eeschema/tools/ee_point_editor.cpp
+++ b/eeschema/tools/ee_point_editor.cpp
@@ -739,7 +739,7 @@ void EE_POINT_EDITOR::updateParentItem( bool aSnapToGrid ) const
             shape->SetBezierC2( m_editPoints->Point( BEZIER_CTRL_PT2 ).GetPosition() );
             shape->SetEnd( m_editPoints->Point( BEZIER_END ).GetPosition() );
 
-            shape->RebuildBezierToSegmentsPointsList( shape->GetWidth() );
+            shape->RebuildBezierToSegmentsPointsList( shape->GetWidth() / 2 );
             break;
 
         default:
diff --git a/include/advanced_config.h b/include/advanced_config.h
index 644ea8e47b..a54009e464 100644
--- a/include/advanced_config.h
+++ b/include/advanced_config.h
@@ -509,14 +509,14 @@ public:
     double m_PcbSelectionVisibilityRatio;
 
     /**
-     * Length of the minimum segment for the outline decomposer.  This is in IU, so
-     * it is nm in pcbnew and 100nm in eeschema.
+     * Deviation between font's bezier curve ideal and the poligonized curve.  This
+     * is 1/16 of the font's internal units.
      *
-     * Setting name: "MinimumSegmentLength"
-     * Valid values: 10 to 1000
-     * Default value: 50
+     * Setting name: "FontErrorSize"
+     * Valid values: 0.01 to 100
+     * Default value: 8
      */
-    int m_MinimumSegmentLength;
+    double m_FontErrorSize;
 
     /**
      * OCE (STEP/IGES) 3D Plugin Tesselation Linear Deflection
diff --git a/include/eda_shape.h b/include/eda_shape.h
index 7b866892d0..9d9f6dac4f 100644
--- a/include/eda_shape.h
+++ b/include/eda_shape.h
@@ -306,11 +306,9 @@ public:
      *
      * Has meaning only for BEZIER shape.
      *
-     * @param aMinSegLen is the min length of segments approximating the bezier. The shape's last
-     *                   segment can be shorter.  This parameter avoids having too many very short
-     *                   segment in list. Good values are between m_width/2 and m_width.
+     * @param aMinSegLen is the max deviation between the polyline and the curve
      */
-    void RebuildBezierToSegmentsPointsList( int aMinSegLen );
+    void RebuildBezierToSegmentsPointsList( int aMaxError );
 
     /**
      * Make a set of SHAPE objects representing the EDA_SHAPE.  Caller owns the objects.
@@ -387,7 +385,7 @@ protected:
     bool hitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const;
     bool hitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const;
 
-    const std::vector<VECTOR2I> buildBezierToSegmentsPointsList( int aMinSegLen ) const;
+    const std::vector<VECTOR2I> buildBezierToSegmentsPointsList( int aMaxError ) const;
 
     void beginEdit( const VECTOR2I& aStartPoint );
     bool continueEdit( const VECTOR2I& aPosition );
diff --git a/include/font/glyph.h b/include/font/glyph.h
index 7e756ecd13..caea128c3e 100644
--- a/include/font/glyph.h
+++ b/include/font/glyph.h
@@ -40,12 +40,6 @@
 namespace KIFONT
 {
 
-constexpr int GLYPH_DEFAULT_DPI = 72;  ///< FreeType default
-// The FreeType default of 72 DPI is not enough for outline decomposition;
-// so we'll use something larger than that.
-constexpr int GLYPH_RESOLUTION  = 288;
-constexpr double GLYPH_SIZE_SCALER = GLYPH_DEFAULT_DPI / (double) GLYPH_RESOLUTION;
-
 
 class GAL_API GLYPH
 {
diff --git a/include/font/outline_decomposer.h b/include/font/outline_decomposer.h
index 8a8aacefba..2d988f071a 100644
--- a/include/font/outline_decomposer.h
+++ b/include/font/outline_decomposer.h
@@ -41,6 +41,13 @@
 
 namespace KIFONT
 {
+
+constexpr int GLYPH_DEFAULT_DPI = 72;  ///< FreeType default
+// The FreeType default of 72 DPI is not enough for outline decomposition;
+// so we'll use something larger than that.
+constexpr int GLYPH_RESOLUTION  = 1152;
+constexpr double GLYPH_SIZE_SCALER = GLYPH_DEFAULT_DPI / (double) GLYPH_RESOLUTION;
+
 struct CONTOUR
 {
     std::vector<VECTOR2D> m_Points;
@@ -70,13 +77,6 @@ private:
 
     void addContourPoint( const VECTOR2D& p );
 
-    bool approximateBezierCurve( std::vector<VECTOR2D>& result,
-                                 const std::vector<VECTOR2D>& bezier ) const;
-    bool approximateQuadraticBezierCurve( std::vector<VECTOR2D>& result,
-                                          const std::vector<VECTOR2D>& bezier ) const;
-    bool approximateCubicBezierCurve( std::vector<VECTOR2D>& result,
-                                      const std::vector<VECTOR2D>& bezier ) const;
-
     /**
      * @return 1 if aContour is in clockwise order, -1 if it is in counterclockwise order,
      *         or 0 if the winding can't be determined.
diff --git a/libs/kimath/include/bezier_curves.h b/libs/kimath/include/bezier_curves.h
index 6101d13423..b5e1fcbe3c 100644
--- a/libs/kimath/include/bezier_curves.h
+++ b/libs/kimath/include/bezier_curves.h
@@ -52,15 +52,32 @@ public:
      * Convert a Bezier curve to a polygon.
      *
      * @param aOutput will be used as an output vector storing polygon points.
-     * @param aMinSegLen is the min dist between 2 successive points.
-     * It can be used to reduce the number of points.
-     * (the last point is always generated)
-     * aMaxSegCount is the max number of segments created
+     * @param aMaxError maximum error in IU between the curve and the polygon.
      */
-    void GetPoly( std::vector<VECTOR2I>& aOutput, int aMinSegLen = 0, int aMaxSegCount = 32 );
-    void GetPoly( std::vector<VECTOR2D>& aOutput, double aMinSegLen = 0.0, int aMaxSegCount = 32 );
+    void GetPoly( std::vector<VECTOR2I>& aOutput, int aMaxError = 10 );
+    void GetPoly( std::vector<VECTOR2D>& aOutput, double aMaxError = 10.0 );
 
 private:
+
+    void getQuadPoly( std::vector<VECTOR2D>& aOutput, double aMaxError );
+    void getCubicPoly( std::vector<VECTOR2D>& aOutput, double aMaxError );
+
+    int findInflectionPoints( double& aT1, double& aT2 );
+    int numberOfInflectionPoints();
+
+    double thirdControlPointDeviation();
+
+    void subdivide( double aT, BEZIER_POLY& aLeft, BEZIER_POLY& aRight );
+    void recursiveSegmentation( std::vector<VECTOR2D>& aOutput, double aMaxError );
+
+    void cubicParabolicApprox( std::vector<VECTOR2D>& aOutput, double aMaxError );
+
+    bool isNaN() const;
+
+    bool isFlat( double aMaxError ) const;
+
+    VECTOR2D eval( double t );
+
     double m_minSegLen;
 
     ///< Control points
diff --git a/libs/kimath/src/bezier_curves.cpp b/libs/kimath/src/bezier_curves.cpp
index a52304c162..e151a8f427 100644
--- a/libs/kimath/src/bezier_curves.cpp
+++ b/libs/kimath/src/bezier_curves.cpp
@@ -21,16 +21,69 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-/************************************/
-/* routines to handle bezier curves */
-/************************************/
+/**********************************************************************************************/
+/* routines to handle bezier curves                                                           */
+/* Based on "Fast, Precise Flattening of Cubic Bezier segments offset Curves" by Hain, et. al.*/
+/**********************************************************************************************/
+
+// Portions of this code are based draw2d
+// Copyright (c) 2010, Laurent Le Goff
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// 	* Redistributions of source code must retain the above copyright notice,
+// 		this list of conditions and the following disclaimer.
+// 	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+//  		disclaimer in the documentation and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Portions of this code are base on BezierKit
+// Copyright (c) 2017 Holmes Futrell
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// Portions of this code are based on the spline-research project
+// Copyright 2018 Raph Levien
+
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 #include <bezier_curves.h>
 #include <geometry/ellipse.h>
 #include <trigo.h>
 #include <math/vector2d.h>  // for VECTOR2D, operator*, VECTOR2
 #include <wx/debug.h>       // for wxASSERT
+#include <wx/log.h>         // for wxLogTrace
 
+#define BEZIER_DBG "bezier"
 
 BEZIER_POLY::BEZIER_POLY( const VECTOR2I& aStart, const VECTOR2I& aCtrl1,
                           const VECTOR2I& aCtrl2, const VECTOR2I& aEnd )
@@ -55,62 +108,491 @@ BEZIER_POLY::BEZIER_POLY( const std::vector<VECTOR2I>& aControlPoints )
 }
 
 
-void BEZIER_POLY::GetPoly( std::vector<VECTOR2I>& aOutput, int aMinSegLen, int aMaxSegCount )
+bool BEZIER_POLY::isNaN() const
+{
+    for( const VECTOR2D& pt : m_ctrlPts )
+    {
+        if( std::isnan( pt.x ) || std::isnan( pt.y ) )
+            return true;
+    }
+
+    return false;
+}
+
+
+bool BEZIER_POLY::isFlat( double aMaxError ) const
+{
+    if( m_ctrlPts.size() == 3 )
+    {
+        VECTOR2D D21 = m_ctrlPts[1] - m_ctrlPts[0];
+        VECTOR2D D31 = m_ctrlPts[2] - m_ctrlPts[0];
+
+        double   t = D21.Dot( D31 ) / D31.SquaredEuclideanNorm();
+        double   u = std::min( std::max( t, 0.0 ), 1.0 );
+        VECTOR2D p = m_ctrlPts[0] + u * D31;
+        VECTOR2D delta = m_ctrlPts[1] - p;
+
+        return 0.5 * delta.EuclideanNorm() <= aMaxError;
+    }
+    else if( m_ctrlPts.size() == 4 )
+    {
+        VECTOR2D delta = m_ctrlPts[3] - m_ctrlPts[0];
+
+        VECTOR2D D21 = m_ctrlPts[1] - m_ctrlPts[0];
+        VECTOR2D D31 = m_ctrlPts[2] - m_ctrlPts[0];
+
+        double cross1 = delta.Cross( D21 );
+        double cross2 = delta.Cross( D31 );
+
+        double inv_delta_sq = 1.0 / delta.SquaredEuclideanNorm();
+        double d1 = ( cross1 * cross1 ) * inv_delta_sq;
+        double d2 = ( cross2 * cross2 ) * inv_delta_sq;
+
+        double factor = ( cross1 * cross2 > 0.0 ) ? 3.0 / 4.0 : 4.0 / 9.0;
+        double f2 = factor * factor;
+        double tol = aMaxError * aMaxError;
+
+        return d1 * f2 <= tol && d2 * f2 <= tol;
+    }
+
+    wxASSERT( false );
+    return false;
+
+}
+
+
+void BEZIER_POLY::GetPoly( std::vector<VECTOR2I>& aOutput, int aMaxError )
 {
     aOutput.clear();
     std::vector<VECTOR2D> buffer;
-    GetPoly( buffer, double( aMinSegLen ), aMaxSegCount );
+    GetPoly( buffer, aMaxError );
 
     aOutput.reserve( buffer.size() );
 
     for( const VECTOR2D& pt : buffer )
-        aOutput.emplace_back( VECTOR2I( (int) pt.x, (int) pt.y ) );
+        aOutput.emplace_back( KiROUND( pt.x ), KiROUND( pt.y ) );
+}
+
+static double approx_int( double x )
+{
+    const double d = 0.6744897501960817;
+    const double d4 = d * d * d * d;
+    return x / ( 1.0 - d + std::pow( d4 + x * x * 0.25, 0.25 ) );
+}
+
+static constexpr double approx_inv_int( double x )
+{
+    const double p = 0.39538816;
+    return x * ( 1.0 - p + std::sqrt( p * p + 0.25 * x * x ) );
 }
 
 
-void BEZIER_POLY::GetPoly( std::vector<VECTOR2D>& aOutput, double aMinSegLen, int aMaxSegCount )
+VECTOR2D BEZIER_POLY::eval( double t )
 {
-    wxASSERT( m_ctrlPts.size() == 4 );
-    // FIXME Brute force method, use a better (recursive?) algorithm
-    // with a max error value.
-    // to optimize the number of segments
-    double                  dt = 1.0 / aMaxSegCount;
-    VECTOR2D::extended_type minSegLen_sq = aMinSegLen * aMinSegLen;
+    double omt = 1.0 - t;
+    double omt2 = omt * omt;
 
-    aOutput.clear();
-    aOutput.push_back( m_ctrlPts[0] );
-
-    // If the Bezier curve is degenerated (straight line), skip intermediate points:
-    bool degenerated = m_ctrlPts[0] == m_ctrlPts[1] && m_ctrlPts[2] == m_ctrlPts[3];
-
-    if( !degenerated )
+    if( m_ctrlPts.size() == 3 )
     {
-        for( int ii = 1; ii < aMaxSegCount; ii++ )
+        return omt2 * m_ctrlPts[0] + 2.0 * omt * t * m_ctrlPts[1] + t * t * m_ctrlPts[2];
+    }
+    else if( m_ctrlPts.size() == 4 )
+    {
+        double omt3 = omt * omt2;
+        double t2 = t * t;
+        double t3 = t * t2;
+        return omt3 * m_ctrlPts[0] + 3.0 * t * omt2 * m_ctrlPts[1]
+               + 3.0 * t2 * omt * m_ctrlPts[2] + t3 * m_ctrlPts[3];
+    }
+    else
+    {
+        wxASSERT( false );
+        return VECTOR2D( 0, 0 );
+    }
+}
+
+void BEZIER_POLY::getQuadPoly( std::vector<VECTOR2D>& aOutput, double aMaxError )
+{
+    double ddx = 2 * m_ctrlPts[1].x - m_ctrlPts[0].x - m_ctrlPts[2].x;
+    double ddy = 2 * m_ctrlPts[1].y - m_ctrlPts[0].y - m_ctrlPts[2].y;
+    double u0 =
+            ( m_ctrlPts[1].x - m_ctrlPts[0].x ) * ddx + ( m_ctrlPts[1].y - m_ctrlPts[0].y ) * ddy;
+    double u2 =
+            ( m_ctrlPts[2].x - m_ctrlPts[1].x ) * ddx + ( m_ctrlPts[2].y - m_ctrlPts[1].y ) * ddy;
+    double cross =
+            ( m_ctrlPts[2].x - m_ctrlPts[0].x ) * ddy - ( m_ctrlPts[2].y - m_ctrlPts[0].y ) * ddx;
+    double x0 = u0 / cross;
+    double x2 = u2 / cross;
+    double scale = std::abs( cross ) / ( std::hypot( ddx, ddy ) * std::abs( x2 - x0 ) );
+
+    double a0 = approx_int( x0 );
+    double a2 = approx_int( x2 );
+
+    int n = std::ceil( 0.5 * std::abs( a2 - a0 ) * std::sqrt( scale / aMaxError ) );
+
+    double v0 = approx_inv_int( a0 );
+    double v2 = approx_inv_int( a2 );
+
+    aOutput.emplace_back( m_ctrlPts[0] );
+
+    for( int ii = 0; ii < n; ++ii )
+    {
+        double u = approx_inv_int( a0 + ( a2 - a0 ) * ii / n );
+        double t = ( u - v0 ) / ( v2 - v0 );
+        aOutput.emplace_back( eval( t ) );
+    }
+
+    aOutput.emplace_back( m_ctrlPts[2] );
+}
+
+
+int BEZIER_POLY::numberOfInflectionPoints()
+{
+    VECTOR2D D21 = m_ctrlPts[1] - m_ctrlPts[0];
+    VECTOR2D D32 = m_ctrlPts[2] - m_ctrlPts[1];
+    VECTOR2D D43 = m_ctrlPts[3] - m_ctrlPts[2];
+
+    double cross1 = D21.Cross( D32 ) * D32.Cross( D43 );
+    double cross2 = D21.Cross( D32 ) * D21.Cross( D43 );
+
+    if( cross1 < 0.0 )
+        return 1;
+    else if( cross2 > 0.0 )
+        return 0;
+
+    bool b1 = D21.Dot( D32 ) > 0.0;
+    bool b2 = D32.Dot( D43 ) > 0.0;
+
+    if( b1 ^ b2 )
+        return 0;
+
+    wxLogTrace( BEZIER_DBG, "numberOfInflectionPoints: rare case" );
+    // These are rare cases where there are potentially 2 or 0 inflection points.
+    return -1;
+}
+
+
+double BEZIER_POLY::thirdControlPointDeviation()
+{
+    VECTOR2D delta = m_ctrlPts[1] - m_ctrlPts[0];
+    double len_sq = delta.SquaredEuclideanNorm();
+
+    if( len_sq < 1e-6 )
+        return 0.0;
+
+    double len = std::sqrt( len_sq );
+    double r = ( m_ctrlPts[1].y - m_ctrlPts[0].y ) / len;
+    double s = ( m_ctrlPts[0].x - m_ctrlPts[1].x ) / len;
+    double u = ( m_ctrlPts[1].x * m_ctrlPts[0].y - m_ctrlPts[0].x * m_ctrlPts[1].y ) / len;
+
+    return std::abs( r * m_ctrlPts[2].x + s * m_ctrlPts[2].y + u );
+}
+
+
+void BEZIER_POLY::subdivide( double aT, BEZIER_POLY& aLeft, BEZIER_POLY& aRight )
+{
+    if( m_ctrlPts.size() == 3 )
+    {
+        aLeft.m_ctrlPts[0] = m_ctrlPts[0];
+        aLeft.m_ctrlPts[1] = m_ctrlPts[0] + aT * ( m_ctrlPts[1] - m_ctrlPts[0] );
+        aLeft.m_ctrlPts[2] = eval( aT );
+
+        aRight.m_ctrlPts[2] = m_ctrlPts[2];
+        aRight.m_ctrlPts[1] = m_ctrlPts[1] + aT * ( m_ctrlPts[2] - m_ctrlPts[1] );
+        aRight.m_ctrlPts[0] = aLeft.m_ctrlPts[2];
+    }
+    else if( m_ctrlPts.size() == 4 )
+    {
+        VECTOR2D left_ctrl1 = m_ctrlPts[0] + aT * ( m_ctrlPts[1] - m_ctrlPts[0] );
+        VECTOR2D tmp = m_ctrlPts[1] + aT * ( m_ctrlPts[2] - m_ctrlPts[1] );
+        VECTOR2D left_ctrl2 = left_ctrl1 + aT * ( tmp - left_ctrl1 );
+        VECTOR2D right_ctrl2 = m_ctrlPts[2] + aT * ( m_ctrlPts[3] - m_ctrlPts[2] );
+        VECTOR2D right_ctrl1 = tmp + aT * ( right_ctrl2 - tmp );
+        VECTOR2D shared = left_ctrl2 + aT * ( right_ctrl1 - left_ctrl2 );
+
+        aLeft.m_ctrlPts[0] = m_ctrlPts[0];
+        aLeft.m_ctrlPts[1] = left_ctrl1;
+        aLeft.m_ctrlPts[2] = left_ctrl2;
+        aLeft.m_ctrlPts[3] = shared;
+
+        aRight.m_ctrlPts[3] = m_ctrlPts[3];
+        aRight.m_ctrlPts[2] = right_ctrl2;
+        aRight.m_ctrlPts[1] = right_ctrl1;
+        aRight.m_ctrlPts[0] = shared;
+    }
+    else
+    {
+        wxASSERT( false );
+    }
+}
+
+
+void BEZIER_POLY::recursiveSegmentation( std::vector<VECTOR2D>& aOutput, double aThreshhold )
+{
+    wxLogTrace( BEZIER_DBG, "recursiveSegmentation with threshold %f", aThreshhold );
+    std::vector<BEZIER_POLY> stack;
+
+    BEZIER_POLY* bezier = nullptr;
+    BEZIER_POLY left( std::vector<VECTOR2D>(4) );
+    BEZIER_POLY right( std::vector<VECTOR2D>(4) );
+
+    stack.push_back( *this );
+
+    while( !stack.empty() )
+    {
+        bezier = &stack.back();
+
+        if( bezier->m_ctrlPts[3] == bezier->m_ctrlPts[0] )
         {
-            double t = dt * ii;
-            double omt  = 1.0 - t;
-            double omt2 = omt * omt;
-            double omt3 = omt * omt2;
-            double t2   = t * t;
-            double t3   = t * t2;
-
-            VECTOR2D vertex = omt3 * m_ctrlPts[0]
-                              + 3.0 * t * omt2 * m_ctrlPts[1]
-                              + 3.0 * t2 * omt * m_ctrlPts[2]
-                              + t3 * m_ctrlPts[3];
-
-            // a minimal filter on the length of the segment being created:
-            // The offset from last point:
-            VECTOR2D                delta = vertex - aOutput.back();
-            VECTOR2D::extended_type dist_sq = delta.SquaredEuclideanNorm();
-
-            if( dist_sq > minSegLen_sq )
-                aOutput.push_back( vertex );
+            wxLogTrace( BEZIER_DBG, "recursiveSegmentation dropping zero length segment" );
+            stack.pop_back();
+        }
+        else if( bezier->isFlat( aThreshhold ) )
+        {
+            aOutput.push_back( bezier->m_ctrlPts[3] );
+            stack.pop_back();
+        }
+        else
+        {
+            bezier->subdivide( 0.5, left, right );
+            *bezier = right;
+            stack.push_back( left );
         }
     }
 
-    if( aOutput.back() != m_ctrlPts[3] )
-        aOutput.push_back( m_ctrlPts[3] );
+    wxLogTrace( BEZIER_DBG, "recursiveSegmentation generated %zu points", aOutput.size() );
+}
+
+
+int BEZIER_POLY::findInflectionPoints( double& aT1, double& aT2 )
+{
+    VECTOR2D A{ ( -m_ctrlPts[0].x + 3 * m_ctrlPts[1].x - 3 * m_ctrlPts[2].x + m_ctrlPts[3].x ),
+                ( -m_ctrlPts[0].y + 3 * m_ctrlPts[1].y - 3 * m_ctrlPts[2].y + m_ctrlPts[3].y ) };
+    VECTOR2D B{ ( 3 * m_ctrlPts[0].x - 6 * m_ctrlPts[1].x + 3 * m_ctrlPts[2].x ),
+                ( 3 * m_ctrlPts[0].y - 6 * m_ctrlPts[1].y + 3 * m_ctrlPts[2].y ) };
+    VECTOR2D C{ ( -3 * m_ctrlPts[0].x + 3 * m_ctrlPts[1].x ),
+                ( -3 * m_ctrlPts[0].y + 3 * m_ctrlPts[1].y ) };
+
+    double a = 3 * A.Cross( B );
+    double b = 3 * A.Cross( C );
+    double c = B.Cross( C );
+
+    // Solve the quadratic equation a*t^2 + b*t + c = 0
+    double r2 = ( b * b - 4 * a * c );
+
+    aT1 = 0.0;
+    aT2 = 0.0;
+
+    if( r2 >= 0.0 && a != 0.0 )
+    {
+        double r = std::sqrt( r2 );
+
+        double t1 = ( ( -b + r ) / ( 2 * a ) );
+        double t2 = ( ( -b - r ) / ( 2 * a ) );
+
+        if( ( t1 > 0.0 && t1 < 1.0 ) && ( t2 > 0.0 && t2 < 1.0 ) )
+        {
+            if( t1 > t2 )
+            {
+                std::swap( t1, t2 );
+            }
+
+            aT1 = t1;
+            aT2 = t2;
+
+            if( t2 - t1 > 0.00001 )
+            {
+                wxLogTrace( BEZIER_DBG, "BEZIER_POLY Found 2 inflection points at t1 = %f, t2 = %f", t1, t2 );
+                return 2;
+            }
+            else
+            {
+                wxLogTrace( BEZIER_DBG, "BEZIER_POLY Found 1 inflection point at t = %f", t1 );
+                return 1;
+            }
+        }
+        else if( t1 > 0.0 && t1 < 1.0 )
+        {
+            aT1 = t1;
+            wxLogTrace( BEZIER_DBG, "BEZIER_POLY Found 1 inflection point at t = %f", t1 );
+            return 1;
+        }
+        else if( t2 > 0.0 && t2 < 1.0 )
+        {
+            aT1 = t2;
+            wxLogTrace( BEZIER_DBG, "BEZIER_POLY Found 1 inflection point at t = %f", t2 );
+            return 1;
+        }
+
+        wxLogTrace( BEZIER_DBG, "BEZIER_POLY Found no inflection points" );
+        return 0;
+    }
+
+    wxLogTrace( BEZIER_DBG, "BEZIER_POLY Found no inflection points" );
+    return 0;
+}
+
+
+void BEZIER_POLY::cubicParabolicApprox( std::vector<VECTOR2D>& aOutput, double aMaxError )
+{
+    std::vector<BEZIER_POLY> stack;
+    stack.push_back( std::vector<VECTOR2D>(4) );
+    stack.push_back( std::vector<VECTOR2D>(4) );
+    stack.push_back( std::vector<VECTOR2D>(4) );
+    stack.push_back( std::vector<VECTOR2D>(4) );
+
+    BEZIER_POLY* c = this;
+    BEZIER_POLY* b1 = &stack[0];
+    BEZIER_POLY* b2 = &stack[1];
+
+    for( ;; )
+    {
+        if( c->isNaN() )
+        {
+            wxLogDebug( "cubicParabolicApprox: NaN detected" );
+            break;
+        }
+
+        if( c->isFlat( aMaxError ) )
+        {
+            wxLogTrace( BEZIER_DBG, "cubicParabolicApprox: General Flatness detected, adding %f %f", c->m_ctrlPts[3].x, c->m_ctrlPts[3].y );
+            // If the subsegment deviation satisfies the flatness criterion, store the last point and stop
+            aOutput.push_back( c->m_ctrlPts[3] );
+            break;
+        }
+
+        // Find the third control point deviation and the t values for subdivision
+        double d = c->thirdControlPointDeviation();
+        double t = 2 * std::sqrt( aMaxError / ( 3.0 * d ) ); // Forumla 2 in Hain et al.
+
+        wxLogTrace( BEZIER_DBG, "cubicParabolicApprox: split point t = %f", t );
+
+        if( t > 1.0 )
+        {
+            wxLogTrace( BEZIER_DBG, "cubicParabolicApprox: Invalid t value detected" );
+            // Case where the t value calculated is invalid, so use recursive subdivision
+            c->recursiveSegmentation( aOutput, aMaxError );
+            break;
+        }
+
+        // Valid t value to subdivide at that calculated value
+        c->subdivide( t, *b1, *b2 );
+
+        // First subsegment should have its deviation equal to flatness
+        if( b1->isFlat( aMaxError ) )
+        {
+            wxLogTrace( BEZIER_DBG, "cubicParabolicApprox: Flatness detected, adding %f %f", b1->m_ctrlPts[3].x, b1->m_ctrlPts[3].y );
+            aOutput.push_back( b1->m_ctrlPts[3] );
+        }
+        else
+        {
+            // if not then use segment to handle any mathematical errors
+            b1->recursiveSegmentation( aOutput, aMaxError );
+        }
+
+        // Repeat the process for the left over subsegment
+        c = b2;
+
+        if( b1 == &stack.front() )
+        {
+            b1 = &stack[2];
+            b2 = &stack[3];
+        }
+        else
+        {
+            b1 = &stack[0];
+            b2 = &stack[1];
+        }
+    }
+}
+
+
+void BEZIER_POLY::getCubicPoly( std::vector<VECTOR2D>& aOutput, double aMaxError )
+{
+    aOutput.push_back( m_ctrlPts[0] );
+
+    if( numberOfInflectionPoints() == 0 )
+    {
+        wxLogTrace( BEZIER_DBG, "getCubicPoly Short circuit to PA" );
+        // If no inflection points then apply PA on the full Bezier segment.
+        cubicParabolicApprox( aOutput, aMaxError );
+        return;
+    }
+
+    // If one or more inflection points then we will have to subdivide the curve
+    double t1, t2;
+    int    numOfIfP = findInflectionPoints( t1, t2 );
+
+    if( numOfIfP == 2 )
+    {
+        wxLogTrace( BEZIER_DBG, "getCubicPoly: 2 inflection points" );
+        // Case when 2 inflection points then divide at the smallest one first
+        BEZIER_POLY sub1( std::vector<VECTOR2D>( 4 ) );
+        BEZIER_POLY tmp1( std::vector<VECTOR2D>( 4 ) );
+        BEZIER_POLY sub2( std::vector<VECTOR2D>( 4 ) );
+        BEZIER_POLY sub3( std::vector<VECTOR2D>( 4 ) );
+
+        subdivide( t1, sub1, tmp1 );
+
+        // Now find the second inflection point in the second curve and subdivide
+        numOfIfP = tmp1.findInflectionPoints( t1, t2 );
+        if( numOfIfP == 2 )
+            tmp1.subdivide( t1, sub2, sub3 );
+        else if( numOfIfP == 1 )
+            tmp1.subdivide( t1, sub2, sub3 );
+        else
+        {
+            wxLogTrace( BEZIER_DBG, "getCubicPoly: 2nd inflection point not found" );
+            return;
+        }
+
+        // Use PA for first subsegment
+        sub1.cubicParabolicApprox( aOutput, aMaxError );
+
+        // Use Segment for the second (middle) subsegment
+        sub2.recursiveSegmentation( aOutput, aMaxError );
+
+        // Use PA for the third curve
+        sub3.cubicParabolicApprox( aOutput, aMaxError );
+    }
+    else if( numOfIfP == 1 )
+    {
+        wxLogTrace( BEZIER_DBG, "getCubicPoly: 1 inflection point" );
+        // Case where there is one inflection point, subdivide once and use PA on both subsegments
+        BEZIER_POLY sub1( std::vector<VECTOR2D>( 4 ) );
+        BEZIER_POLY sub2( std::vector<VECTOR2D>( 4 ) );
+        subdivide( t1, sub1, sub2 );
+        sub1.cubicParabolicApprox( aOutput, aMaxError );
+        sub2.cubicParabolicApprox( aOutput, aMaxError );
+    }
+    else
+    {
+        wxLogTrace( BEZIER_DBG, "getCubicPoly: Unknown inflection points" );
+        // Case where there is no inflection, use PA directly
+        cubicParabolicApprox( aOutput, aMaxError );
+    }
+}
+
+
+void BEZIER_POLY::GetPoly( std::vector<VECTOR2D>& aOutput, double aMaxError )
+{
+    if( aMaxError <= 0.0 )
+        aMaxError = 10.0;
+
+    if( m_ctrlPts.size() == 3 )
+    {
+        getQuadPoly( aOutput, aMaxError );
+    }
+    else if( m_ctrlPts.size() == 4 )
+    {
+        getCubicPoly( aOutput, aMaxError );
+    }
+    else
+    {
+        wxASSERT( false );
+    }
+
+    wxLogTrace( BEZIER_DBG, "GetPoly generated %zu points", aOutput.size() );
 }
 
 
diff --git a/pcbnew/convert_shape_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp
index 98303d26da..ea9834c17a 100644
--- a/pcbnew/convert_shape_list_to_polygon.cpp
+++ b/pcbnew/convert_shape_list_to_polygon.cpp
@@ -395,11 +395,7 @@ bool doConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_
                     }
 
                     // Ensure the approximated Bezier shape is built
-                    // a good value is between (Bezier curve width / 2) and (Bezier curve width)
-                    // ( and at least 0.05 mm to avoid very small segments)
-                    int min_segm_length = std::max( pcbIUScale.mmToIU( 0.05 ),
-                                                    graphic->GetWidth() );
-                    graphic->RebuildBezierToSegmentsPointsList( min_segm_length );
+                    graphic->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
 
                     if( reverse )
                     {
diff --git a/pcbnew/dialogs/dialog_shape_properties.cpp b/pcbnew/dialogs/dialog_shape_properties.cpp
index 042ea1cbc8..3f4ee3de18 100644
--- a/pcbnew/dialogs/dialog_shape_properties.cpp
+++ b/pcbnew/dialogs/dialog_shape_properties.cpp
@@ -513,7 +513,7 @@ bool DIALOG_SHAPE_PROPERTIES::TransferDataFromWindow()
 
     m_item->SetLayer( ToLAYER_ID( layer ) );
 
-    m_item->RebuildBezierToSegmentsPointsList( m_item->GetWidth() );
+    m_item->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
 
     if( m_item->IsOnCopperLayer() )
         m_item->SetNetCode( m_netSelector->GetSelectedNetcode() );
diff --git a/pcbnew/drc/drc_test_provider_physical_clearance.cpp b/pcbnew/drc/drc_test_provider_physical_clearance.cpp
index 5a8f70e903..22997d9904 100644
--- a/pcbnew/drc/drc_test_provider_physical_clearance.cpp
+++ b/pcbnew/drc/drc_test_provider_physical_clearance.cpp
@@ -310,7 +310,7 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
                             {
                                 SHAPE_LINE_CHAIN asPoly;
 
-                                shape->RebuildBezierToSegmentsPointsList( shape->GetWidth() );
+                                shape->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
 
                                 for( const VECTOR2I& pt : shape->GetBezierPoints() )
                                     asPoly.Append( pt );
diff --git a/pcbnew/graphics_cleaner.cpp b/pcbnew/graphics_cleaner.cpp
index 5b13067c5e..a680134b1a 100644
--- a/pcbnew/graphics_cleaner.cpp
+++ b/pcbnew/graphics_cleaner.cpp
@@ -105,7 +105,7 @@ bool GRAPHICS_CLEANER::isNullShape( PCB_SHAPE* aShape )
         return aShape->GetPointCount() == 0;
 
     case SHAPE_T::BEZIER:
-        aShape->RebuildBezierToSegmentsPointsList( aShape->GetWidth() );
+        aShape->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
 
         // If the Bezier points list contains 2 points, it is equivalent to a segment
         if( aShape->GetBezierPoints().size() == 2 )
diff --git a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp
index d69c5a6e33..1acee726cf 100644
--- a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp
+++ b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp
@@ -208,7 +208,7 @@ void GRAPHICS_IMPORTER_PCBNEW::AddSpline( const VECTOR2D& aStart, const VECTOR2D
     spline->SetBezierC1( MapCoordinate( aBezierControl1 ));
     spline->SetBezierC2( MapCoordinate( aBezierControl2 ));
     spline->SetEnd( MapCoordinate( aEnd ) );
-    spline->RebuildBezierToSegmentsPointsList( aStroke.GetWidth() );
+    spline->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
 
     // If the spline is degenerated (i.e. a segment) add it as segment or discard it if
     // null (i.e. very small) length
diff --git a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp
index f68ca75388..7dd822552f 100644
--- a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp
+++ b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp
@@ -462,8 +462,6 @@ PCB_IO_EASYEDAPRO_PARSER::ParseContour( nlohmann::json polyData, bool aInFill,
     SHAPE_LINE_CHAIN result;
     VECTOR2D         prevPt;
 
-    double bezierMinSegLen = polyData.size() < 300 ? aArcAccuracy : aArcAccuracy * 10;
-
     for( int i = 0; i < polyData.size(); i++ )
     {
         nlohmann::json val = polyData.at( i );
@@ -554,7 +552,7 @@ PCB_IO_EASYEDAPRO_PARSER::ParseContour( nlohmann::json polyData, bool aInFill,
                 BEZIER_POLY           converter( ctrlPoints );
 
                 std::vector<VECTOR2I> bezierPoints;
-                converter.GetPoly( bezierPoints, bezierMinSegLen, 16 );
+                converter.GetPoly( bezierPoints, aArcAccuracy );
 
                 result.Append( bezierPoints );
 
diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
index 3eec41b4a0..99c4a52d19 100644
--- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
+++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
@@ -1078,7 +1078,7 @@ void PCB_IO_IPC2581::addShape( wxXmlNode* aContentNode, const PCB_SHAPE& aShape
                                              aShape.GetBezierC2(), aShape.GetEnd() };
         BEZIER_POLY converter( ctrlPoints );
         std::vector<VECTOR2I> points;
-        converter.GetPoly( points, aShape.GetStroke().GetWidth() );
+        converter.GetPoly( points, ARC_HIGH_DEF );
 
         wxXmlNode* point_node = appendNode( polyline_node, "PolyBegin" );
         addXY( point_node, points[0] );
diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp
index a6e7fa462e..74e6644523 100644
--- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp
+++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp
@@ -2835,6 +2835,7 @@ PCB_SHAPE* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_SHAPE( BOARD_ITEM* aParent )
         shape->SetBezierC1( parseXY());
         shape->SetBezierC2( parseXY());
         shape->SetEnd( parseXY() );
+        shape->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
         NeedRIGHT();
         break;
 
diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp
index 772e353f12..db8bda5fa7 100644
--- a/pcbnew/pcb_painter.cpp
+++ b/pcbnew/pcb_painter.cpp
@@ -1937,22 +1937,27 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
                 pointCtrl.push_back( aShape->GetEnd() );
 
                 BEZIER_POLY converter( pointCtrl );
-                converter.GetPoly( output, thickness );
+                converter.GetPoly( output, m_maxError );
 
-                m_gal->DrawSegmentChain( output, thickness );
+                m_gal->DrawSegmentChain( aShape->GetBezierPoints(), thickness );
             }
             else
             {
-            m_gal->SetIsFill( aShape->IsFilled() );
-            m_gal->SetIsStroke( thickness > 0 );
-            m_gal->SetLineWidth( thickness );
+                m_gal->SetIsFill( aShape->IsFilled() );
+                m_gal->SetIsStroke( thickness > 0 );
+                m_gal->SetLineWidth( thickness );
 
-                // Use thickness as filter value to convert the curve to polyline when the curve
-                // is not supported
-                m_gal->DrawCurve( VECTOR2D( aShape->GetStart() ),
-                                  VECTOR2D( aShape->GetBezierC1() ),
-                                  VECTOR2D( aShape->GetBezierC2() ),
-                                  VECTOR2D( aShape->GetEnd() ), thickness );
+                if( aShape->GetBezierPoints().size() > 2 )
+                {
+                    m_gal->DrawPolygon( aShape->GetBezierPoints() );
+                }
+                else
+                {
+                    m_gal->DrawCurve( VECTOR2D( aShape->GetStart() ),
+                                      VECTOR2D( aShape->GetBezierC1() ),
+                                      VECTOR2D( aShape->GetBezierC2() ),
+                                      VECTOR2D( aShape->GetEnd() ), m_maxError );
+                }
             }
 
             break;
diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp
index ec4a1afdbb..c1352c496c 100644
--- a/pcbnew/pcb_shape.cpp
+++ b/pcbnew/pcb_shape.cpp
@@ -267,6 +267,7 @@ bool PCB_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
         SetBezierC1( kiapi::common::UnpackVector2( msg.bezier().control1() ) );
         SetBezierC2( kiapi::common::UnpackVector2( msg.bezier().control2() ) );
         SetEnd( kiapi::common::UnpackVector2( msg.bezier().end() ) );
+        RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
     }
 
     return true;
@@ -568,7 +569,7 @@ void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
             std::swap( m_start, m_end );
 
         if( GetShape() == SHAPE_T::BEZIER )
-            RebuildBezierToSegmentsPointsList( GetWidth() );
+            RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
 
         break;
 
diff --git a/pcbnew/teardrop/teardrop_utils.cpp b/pcbnew/teardrop/teardrop_utils.cpp
index 4f69a4bdf0..c9430d7632 100644
--- a/pcbnew/teardrop/teardrop_utils.cpp
+++ b/pcbnew/teardrop/teardrop_utils.cpp
@@ -250,7 +250,7 @@ void TEARDROP_MANAGER::computeCurvedForRoundShape( const TEARDROP_PARAMETERS& aP
 
     std::vector<VECTOR2I> curve_pts;
     curve_pts.reserve( aParams.m_CurveSegCount );
-    BEZIER_POLY( pts[1], tangentB, tangentC, pts[2] ).GetPoly( curve_pts, 0, aParams.m_CurveSegCount );
+    BEZIER_POLY( pts[1], tangentB, tangentC, pts[2] ).GetPoly( curve_pts, ARC_HIGH_DEF );
 
     for( VECTOR2I& corner: curve_pts )
         aPoly.push_back( corner );
@@ -258,7 +258,7 @@ void TEARDROP_MANAGER::computeCurvedForRoundShape( const TEARDROP_PARAMETERS& aP
     aPoly.push_back( pts[3] );
 
     curve_pts.clear();
-    BEZIER_POLY( pts[4], tangentE, tangentA, pts[0] ).GetPoly( curve_pts, 0, aParams.m_CurveSegCount );
+    BEZIER_POLY( pts[4], tangentE, tangentA, pts[0] ).GetPoly( curve_pts, ARC_HIGH_DEF );
 
     for( VECTOR2I& corner: curve_pts )
         aPoly.push_back( corner );
@@ -319,7 +319,7 @@ void TEARDROP_MANAGER::computeCurvedForRectShape( const TEARDROP_PARAMETERS& aPa
     ctrl2.x += bias.x;
     ctrl2.y += bias.y;
 
-    BEZIER_POLY( aPts[1], ctrl1, ctrl2, aPts[2] ).GetPoly( curve_pts, 0, aParams.m_CurveSegCount );
+    BEZIER_POLY( aPts[1], ctrl1, ctrl2, aPts[2] ).GetPoly( curve_pts, ARC_HIGH_DEF );
 
     for( VECTOR2I& corner: curve_pts )
         aPoly.push_back( corner );
@@ -347,7 +347,7 @@ void TEARDROP_MANAGER::computeCurvedForRectShape( const TEARDROP_PARAMETERS& aPa
     ctrl2.x += bias.x;
     ctrl2.y += bias.y;
 
-    BEZIER_POLY( aPts[4], ctrl1, ctrl2, aPts[0] ).GetPoly( curve_pts, 0, aParams.m_CurveSegCount );
+    BEZIER_POLY( aPts[4], ctrl1, ctrl2, aPts[0] ).GetPoly( curve_pts, ARC_HIGH_DEF );
 
     for( VECTOR2I& corner: curve_pts )
         aPoly.push_back( corner );
diff --git a/pcbnew/tools/pcb_point_editor.cpp b/pcbnew/tools/pcb_point_editor.cpp
index 89ca90f681..9d2c2797fe 100644
--- a/pcbnew/tools/pcb_point_editor.cpp
+++ b/pcbnew/tools/pcb_point_editor.cpp
@@ -1407,7 +1407,7 @@ void PCB_POINT_EDITOR::updateItem( BOARD_COMMIT* aCommit )
             else if( isModified( m_editPoints->Point( BEZIER_END ) ) )
                 shape->SetEnd( m_editPoints->Point( BEZIER_END ).GetPosition() );
 
-            shape->RebuildBezierToSegmentsPointsList( shape->GetWidth() );
+            shape->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
             break;
 
         default:        // suppress warnings
diff --git a/qa/data/pcbnew/issue6039.kicad_pro b/qa/data/pcbnew/issue6039.kicad_pro
index 2a30c45658..1054539c6b 100644
--- a/qa/data/pcbnew/issue6039.kicad_pro
+++ b/qa/data/pcbnew/issue6039.kicad_pro
@@ -3,14 +3,17 @@
     "3dviewports": [],
     "design_settings": {
       "defaults": {
-        "board_outline_line_width": 0.049999999999999996,
-        "copper_line_width": 0.19999999999999998,
+        "apply_defaults_to_fp_fields": false,
+        "apply_defaults_to_fp_shapes": false,
+        "apply_defaults_to_fp_text": false,
+        "board_outline_line_width": 0.05,
+        "copper_line_width": 0.2,
         "copper_text_italic": false,
         "copper_text_size_h": 1.5,
         "copper_text_size_v": 1.5,
         "copper_text_thickness": 0.3,
         "copper_text_upright": true,
-        "courtyard_line_width": 0.049999999999999996,
+        "courtyard_line_width": 0.05,
         "dimension_precision": 1,
         "dimension_units": 2,
         "dimensions": {
@@ -21,13 +24,13 @@
           "text_position": 0,
           "units_format": 1
         },
-        "fab_line_width": 0.09999999999999999,
+        "fab_line_width": 0.1,
         "fab_text_italic": false,
         "fab_text_size_h": 1.0,
         "fab_text_size_v": 1.0,
         "fab_text_thickness": 0.15,
         "fab_text_upright": true,
-        "other_line_width": 0.09999999999999999,
+        "other_line_width": 0.1,
         "other_text_italic": false,
         "other_text_size_h": 1.0,
         "other_text_size_v": 1.0,
@@ -46,7 +49,7 @@
         "silk_text_upright": true,
         "zones": {
           "45_degree_only": false,
-          "min_clearance": 0.19999999999999998
+          "min_clearance": 0.2
         }
       },
       "diff_pair_dimensions": [
@@ -73,9 +76,12 @@
         "duplicate_footprints": "warning",
         "extra_footprint": "warning",
         "footprint": "error",
+        "footprint_symbol_mismatch": "warning",
         "footprint_type_mismatch": "ignore",
         "hole_clearance": "error",
         "hole_near_hole": "error",
+        "hole_to_hole": "warning",
+        "holes_co_located": "warning",
         "invalid_outline": "error",
         "isolated_copper": "warning",
         "item_on_disabled_layer": "error",
@@ -120,17 +126,17 @@
         "min_copper_edge_clearance": 0.01,
         "min_hole_clearance": 0.0,
         "min_hole_to_hole": 0.25,
-        "min_microvia_diameter": 0.19999999999999998,
-        "min_microvia_drill": 0.09999999999999999,
+        "min_microvia_diameter": 0.2,
+        "min_microvia_drill": 0.1,
         "min_resolved_spokes": 2,
         "min_silk_clearance": 0.0,
-        "min_text_height": 0.7999999999999999,
+        "min_text_height": 0.8,
         "min_text_thickness": 0.08,
         "min_through_hole_diameter": 0.3,
-        "min_track_width": 0.19999999999999998,
-        "min_via_annular_width": 0.049999999999999996,
+        "min_track_width": 0.2,
+        "min_via_annular_width": 0.05,
         "min_via_annulus": 0.049999999999999996,
-        "min_via_diameter": 0.39999999999999997,
+        "min_via_diameter": 0.4,
         "solder_mask_to_copper_clearance": 0.0,
         "use_height_for_length_calcs": true
       },
@@ -184,6 +190,32 @@
         1.0,
         2.0
       ],
+      "tuning_pattern_settings": {
+        "diff_pair_defaults": {
+          "corner_radius_percentage": 80,
+          "corner_style": 1,
+          "max_amplitude": 1.0,
+          "min_amplitude": 0.2,
+          "single_sided": false,
+          "spacing": 1.0
+        },
+        "diff_pair_skew_defaults": {
+          "corner_radius_percentage": 80,
+          "corner_style": 1,
+          "max_amplitude": 1.0,
+          "min_amplitude": 0.2,
+          "single_sided": false,
+          "spacing": 0.6
+        },
+        "single_track_defaults": {
+          "corner_radius_percentage": 80,
+          "corner_style": 1,
+          "max_amplitude": 1.0,
+          "min_amplitude": 0.2,
+          "single_sided": false,
+          "spacing": 0.6
+        }
+      },
       "via_dimensions": [
         {
           "diameter": 0.85,
@@ -193,6 +225,13 @@
       "zones_allow_external_fillets": false,
       "zones_use_no_outline": true
     },
+    "ipc2581": {
+      "dist": "",
+      "distpn": "",
+      "internal_id": "",
+      "mfg": "",
+      "mpn": ""
+    },
     "layer_presets": [
       {
         "activeLayer": -2,