Skip to content

Commit 37cab7f

Browse files
authored
Merge pull request #31 from weihsinyeh/main
Decompose splines with t flexibly updated
2 parents b75d5ca + 7e25ecd commit 37cab7f

File tree

1 file changed

+37
-22
lines changed

1 file changed

+37
-22
lines changed

src/spline.c

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
1818
static 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
*/
3131
static 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
*/
7879
static 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

Comments
 (0)