@@ -11,8 +11,8 @@ typedef struct _twin_spline {
11
11
} twin_spline_t ;
12
12
13
13
/*
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'.
16
16
* The result is stored in 'result'.
17
17
*/
18
18
static void _lerp (const twin_spoint_t * a ,
@@ -25,17 +25,15 @@ static void _lerp(const twin_spoint_t *a,
25
25
}
26
26
27
27
/*
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'
29
29
* factor. The spline is split into two new splines 's1' and 's2'.
30
30
*/
31
31
static void _de_casteljau (twin_spline_t * spline ,
32
32
int shift ,
33
33
twin_spline_t * s1 ,
34
34
twin_spline_t * s2 )
35
35
{
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 ;
39
37
40
38
_lerp (& spline -> a , & spline -> b , shift , & ab );
41
39
_lerp (& spline -> b , & spline -> c , shift , & bc );
@@ -56,28 +54,31 @@ static void _de_casteljau(twin_spline_t *spline,
56
54
}
57
55
58
56
/*
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.
61
62
*/
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 )
63
64
{
64
- twin_dfixed_t berr , cerr ;
65
+ twin_dfixed_t bdist , cdist ;
65
66
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 );
68
69
69
- if (berr > cerr )
70
- return berr ;
71
- return cerr ;
70
+ if (bdist > cdist )
71
+ return bdist ;
72
+ return cdist ;
72
73
}
73
74
74
75
/*
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
76
77
* tolerance.
77
78
*/
78
79
static bool is_flat (twin_spline_t * spline , twin_dfixed_t tolerance_squared )
79
80
{
80
- return _twin_spline_error_squared (spline ) <= tolerance_squared ;
81
+ return _twin_spline_distance_squared (spline ) <= tolerance_squared ;
81
82
}
82
83
83
84
/*
@@ -91,16 +92,30 @@ static void _twin_spline_decompose(twin_path_t *path,
91
92
{
92
93
/* Draw starting point */
93
94
_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 ;
95
104
while (!is_flat (spline , tolerance_squared )) {
96
- int shift = 1 ;
97
105
twin_spline_t left , right ;
98
106
99
- /* FIXME: Find the optimal shift value to decompose the spline */
100
- do {
107
+ while (true) {
101
108
_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
+ }
102
117
shift ++ ;
103
- } while (! is_flat ( & left , tolerance_squared ));
118
+ }
104
119
105
120
/* Draw the left segment */
106
121
_twin_path_sdraw (path , left .d .x , left .d .y );
0 commit comments