@@ -11,8 +11,8 @@ typedef struct _twin_spline {
1111} twin_spline_t ;
1212
1313/*
14- * Linearly interpolate between points 'a' and 'b' with a shift factor.
15- * The shift factor determines the position between 'a' and 'b'.
14+ * Linearly interpolate between points 'a' and 'b' with a ' shift' factor.
15+ * The ' shift' factor determines the position between 'a' and 'b'.
1616 * The result is stored in 'result'.
1717 */
1818static void _lerp (const twin_spoint_t * a ,
@@ -25,17 +25,15 @@ static void _lerp(const twin_spoint_t *a,
2525}
2626
2727/*
28- * Perform the de Casteljau algorithm to split a spline at a given shift
28+ * Perform the de Casteljau algorithm to split a spline at a given ' shift'
2929 * factor. The spline is split into two new splines 's1' and 's2'.
3030 */
3131static void _de_casteljau (twin_spline_t * spline ,
3232 int shift ,
3333 twin_spline_t * s1 ,
3434 twin_spline_t * s2 )
3535{
36- twin_spoint_t ab , bc , cd ;
37- twin_spoint_t abbc , bccd ;
38- twin_spoint_t final ;
36+ twin_spoint_t ab , bc , cd , abbc , bccd , final ;
3937
4038 _lerp (& spline -> a , & spline -> b , shift , & ab );
4139 _lerp (& spline -> b , & spline -> c , shift , & bc );
@@ -56,28 +54,31 @@ static void _de_casteljau(twin_spline_t *spline,
5654}
5755
5856/*
59- * Return an upper bound on the error (squared) that could result from
60- * approximating a spline with a line segment connecting the two endpoints.
57+ * Return an upper bound on the distance (squared) that could result from
58+ * approximating a spline with a line segment connecting the two endpoints,
59+ * which is based on the Convex Hull Property of Bézier Curves: The Bézier Curve
60+ * lies completely in the convex hull of the given control points. Therefore, we
61+ * can use control points B and C to approximate the actual spline.
6162 */
62- static twin_dfixed_t _twin_spline_error_squared (twin_spline_t * spline )
63+ static twin_dfixed_t _twin_spline_distance_squared (twin_spline_t * spline )
6364{
64- twin_dfixed_t berr , cerr ;
65+ twin_dfixed_t bdist , cdist ;
6566
66- berr = _twin_distance_to_line_squared (& spline -> b , & spline -> a , & spline -> d );
67- cerr = _twin_distance_to_line_squared (& spline -> c , & spline -> a , & spline -> d );
67+ bdist = _twin_distance_to_line_squared (& spline -> b , & spline -> a , & spline -> d );
68+ cdist = _twin_distance_to_line_squared (& spline -> c , & spline -> a , & spline -> d );
6869
69- if (berr > cerr )
70- return berr ;
71- return cerr ;
70+ if (bdist > cdist )
71+ return bdist ;
72+ return cdist ;
7273}
7374
7475/*
75- * Check if a spline is flat enough by comparing the error against the
76+ * Check if a spline is flat enough by comparing the distance against the
7677 * tolerance.
7778 */
7879static bool is_flat (twin_spline_t * spline , twin_dfixed_t tolerance_squared )
7980{
80- return _twin_spline_error_squared (spline ) <= tolerance_squared ;
81+ return _twin_spline_distance_squared (spline ) <= tolerance_squared ;
8182}
8283
8384/*
@@ -91,16 +92,30 @@ static void _twin_spline_decompose(twin_path_t *path,
9192{
9293 /* Draw starting point */
9394 _twin_path_sdraw (path , spline -> a .x , spline -> a .y );
94-
95+ /*
96+ * It on average requires over two shift attempts per iteration to find the
97+ * optimal value. To reduce redundancy in shift 1, adjust the initial 't'
98+ * value from 0.5 to 0.25 by applying an initial shift of 2. As spline
99+ * rendering progresses, the shift amount decreases. Store the last shift
100+ * value as a global variable to use directly in the next iteration,
101+ * avoiding a reset to an initial shift of 2.
102+ */
103+ int shift = 2 ;
95104 while (!is_flat (spline , tolerance_squared )) {
96- int shift = 1 ;
97105 twin_spline_t left , right ;
98106
99- /* FIXME: Find the optimal shift value to decompose the spline */
100- do {
107+ while (true) {
101108 _de_casteljau (spline , shift , & left , & right );
109+ if (is_flat (& left , tolerance_squared )) {
110+ /* Limiting the scope of 't' may overlook optimal points with
111+ * maximum curvature. Therefore, dynamically reduce the shift
112+ * amount to a minimum of 1. */
113+ if (shift > 1 )
114+ shift -- ;
115+ break ;
116+ }
102117 shift ++ ;
103- } while (! is_flat ( & left , tolerance_squared ));
118+ }
104119
105120 /* Draw the left segment */
106121 _twin_path_sdraw (path , left .d .x , left .d .y );
0 commit comments