Skip to content

Commit 5433792

Browse files
committed
Implement quadratic bezier curve
Add quadratic bezier curve support by converting quadratic spline to cubic spline. The conversion follows the formula: CP1 = P0 + 2/3 * (P1 - P0) CP2 = P2 + 2/3 * (P1 - P2) where: - P0, P1, P2 are quadratic control points - CP1, CP2 are the resulting cubic control points Ref: https://fontforge.org/docs/techref/bezier.html#converting-truetype-to-postscript
1 parent f8e868b commit 5433792

File tree

3 files changed

+146
-39
lines changed

3 files changed

+146
-39
lines changed

apps/spline.c

Lines changed: 111 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,86 @@
1515

1616
#define _apps_spline_pixmap(spline) ((spline)->widget.window->pixmap)
1717

18-
#define NPT 4
19-
2018
typedef struct _apps_spline {
2119
twin_widget_t widget;
22-
twin_point_t points[NPT];
20+
int n_points;
21+
twin_point_t *points;
2322
int which;
2423
twin_fixed_t line_width;
2524
twin_cap_t cap_style;
25+
twin_matrix_t transition;
26+
twin_matrix_t inverse_transition;
2627
} apps_spline_t;
2728

29+
static void _init_control_point(apps_spline_t *spline)
30+
{
31+
const int init_point_quad[3][2] = {
32+
{100, 100},
33+
{200, 100},
34+
{300, 100},
35+
};
36+
const int init_point_cubic[4][2] = {
37+
{100, 100},
38+
{300, 300},
39+
{100, 300},
40+
{300, 100},
41+
};
42+
const int(*init_point)[2];
43+
if (spline->n_points == 4) {
44+
init_point = init_point_cubic;
45+
} else if (spline->n_points == 3) {
46+
init_point = init_point_quad;
47+
}
48+
for (int i = 0; i < spline->n_points; i++) {
49+
spline->points[i].x = twin_int_to_fixed(init_point[i][0]);
50+
spline->points[i].y = twin_int_to_fixed(init_point[i][1]);
51+
}
52+
}
53+
54+
static void _draw_aux_line(twin_path_t *path,
55+
apps_spline_t *spline,
56+
int idx1,
57+
int idx2)
58+
{
59+
twin_path_move(path, spline->points[idx1].x, spline->points[idx1].y);
60+
twin_path_draw(path, spline->points[idx2].x, spline->points[idx2].y);
61+
twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path,
62+
twin_int_to_fixed(2));
63+
twin_path_empty(path);
64+
}
65+
2866
static void _apps_spline_paint(apps_spline_t *spline)
2967
{
3068
twin_path_t *path;
31-
int i;
32-
3369
path = twin_path_create();
3470
twin_path_set_cap_style(path, spline->cap_style);
71+
twin_path_set_matrix(path, spline->transition);
72+
3573
twin_path_move(path, spline->points[0].x, spline->points[0].y);
36-
twin_path_curve(path, spline->points[1].x, spline->points[1].y,
37-
spline->points[2].x, spline->points[2].y,
38-
spline->points[3].x, spline->points[3].y);
74+
if (spline->n_points == 4) {
75+
twin_path_curve(path, spline->points[1].x, spline->points[1].y,
76+
spline->points[2].x, spline->points[2].y,
77+
spline->points[3].x, spline->points[3].y);
78+
} else if (spline->n_points == 3) {
79+
twin_path_quadratic_curve(path, spline->points[1].x,
80+
spline->points[1].y, spline->points[2].x,
81+
spline->points[2].y);
82+
}
3983
twin_paint_stroke(_apps_spline_pixmap(spline), 0xff404040, path,
4084
spline->line_width);
4185
twin_path_set_cap_style(path, TwinCapButt);
4286
twin_paint_stroke(_apps_spline_pixmap(spline), 0xffffff00, path,
4387
twin_int_to_fixed(2));
44-
4588
twin_path_empty(path);
46-
twin_path_move(path, spline->points[0].x, spline->points[0].y);
47-
twin_path_draw(path, spline->points[1].x, spline->points[1].y);
48-
twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path,
49-
twin_int_to_fixed(2));
50-
twin_path_empty(path);
51-
twin_path_move(path, spline->points[3].x, spline->points[3].y);
52-
twin_path_draw(path, spline->points[2].x, spline->points[2].y);
53-
twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path,
54-
twin_int_to_fixed(2));
55-
twin_path_empty(path);
56-
for (i = 0; i < NPT; i++) {
89+
if (spline->n_points == 4) {
90+
_draw_aux_line(path, spline, 0, 1);
91+
_draw_aux_line(path, spline, 3, 2);
92+
} else if (spline->n_points == 3) {
93+
_draw_aux_line(path, spline, 0, 1);
94+
_draw_aux_line(path, spline, 1, 2);
95+
}
96+
97+
for (int i = 0; i < spline->n_points; i++) {
5798
twin_path_empty(path);
5899
twin_path_circle(path, spline->points[i].x, spline->points[i].y,
59100
twin_int_to_fixed(10));
@@ -62,13 +103,32 @@ static void _apps_spline_paint(apps_spline_t *spline)
62103
twin_path_destroy(path);
63104
}
64105

106+
static void _apps_spline_button_signal(maybe_unused twin_button_t *button,
107+
twin_button_signal_t signal,
108+
void *closure)
109+
{
110+
if (signal != TwinButtonSignalDown)
111+
return;
112+
113+
apps_spline_t *spline = closure;
114+
spline->n_points = (spline->n_points == 3) ? 4 : 3;
115+
_init_control_point(spline);
116+
_twin_widget_queue_paint(&spline->widget);
117+
}
118+
119+
#define D(x) twin_double_to_fixed(x)
65120
static twin_dispatch_result_t _apps_spline_update_pos(apps_spline_t *spline,
66121
twin_event_t *event)
67122
{
68123
if (spline->which < 0)
69124
return TwinDispatchContinue;
70-
spline->points[spline->which].x = twin_int_to_fixed(event->u.pointer.x);
71-
spline->points[spline->which].y = twin_int_to_fixed(event->u.pointer.y);
125+
twin_fixed_t x = twin_int_to_fixed(event->u.pointer.x);
126+
twin_fixed_t y = twin_int_to_fixed(event->u.pointer.y);
127+
128+
spline->points[spline->which].x = twin_sfixed_to_fixed(
129+
_twin_matrix_x(&(spline->inverse_transition), x, y));
130+
spline->points[spline->which].y = twin_sfixed_to_fixed(
131+
_twin_matrix_y(&(spline->inverse_transition), x, y));
72132
_twin_widget_queue_paint(&spline->widget);
73133
return TwinDispatchDone;
74134
}
@@ -80,11 +140,15 @@ static int _apps_spline_hit(apps_spline_t *spline,
80140
twin_fixed_t y)
81141
{
82142
int i;
83-
84-
for (i = 0; i < NPT; i++)
85-
if (twin_fixed_abs(x - spline->points[i].x) < spline->line_width / 2 &&
86-
twin_fixed_abs(y - spline->points[i].y) < spline->line_width / 2)
143+
for (i = 0; i < spline->n_points; i++) {
144+
twin_fixed_t px = twin_sfixed_to_fixed(_twin_matrix_x(
145+
&(spline->transition), spline->points[i].x, spline->points[i].y));
146+
twin_fixed_t py = twin_sfixed_to_fixed(_twin_matrix_y(
147+
&(spline->transition), spline->points[i].x, spline->points[i].y));
148+
if (twin_fixed_abs(x - px) < spline->line_width / 2 &&
149+
twin_fixed_abs(y - py) < spline->line_width / 2)
87150
return i;
151+
}
88152
return -1;
89153
}
90154

@@ -123,28 +187,36 @@ static twin_dispatch_result_t _apps_spline_dispatch(twin_widget_t *widget,
123187

124188
static void _apps_spline_init(apps_spline_t *spline,
125189
twin_box_t *parent,
126-
twin_dispatch_proc_t dispatch)
190+
twin_dispatch_proc_t dispatch,
191+
int n_points)
127192
{
128-
static const twin_widget_layout_t preferred = {0, 0, 1, 1};
193+
static twin_widget_layout_t preferred = {0, 0, 1, 1};
194+
preferred.height = parent->widget.window->screen->height * 2 / 3;
129195
_twin_widget_init(&spline->widget, parent, 0, preferred, dispatch);
130196
twin_widget_set(&spline->widget, 0xffffffff);
131197
spline->line_width = twin_int_to_fixed(100);
132198
spline->cap_style = TwinCapRound;
133-
spline->points[0].x = twin_int_to_fixed(100);
134-
spline->points[0].y = twin_int_to_fixed(100);
135-
spline->points[1].x = twin_int_to_fixed(300);
136-
spline->points[1].y = twin_int_to_fixed(300);
137-
spline->points[2].x = twin_int_to_fixed(100);
138-
spline->points[2].y = twin_int_to_fixed(300);
139-
spline->points[3].x = twin_int_to_fixed(300);
140-
spline->points[3].y = twin_int_to_fixed(100);
199+
twin_matrix_identity(&spline->transition);
200+
twin_matrix_rotate(&spline->transition, TWIN_ANGLE_11_25);
201+
twin_matrix_identity(&spline->inverse_transition);
202+
twin_matrix_rotate(&spline->inverse_transition, -TWIN_ANGLE_11_25);
203+
spline->points = calloc(n_points, sizeof(twin_point_t));
204+
spline->n_points = n_points;
205+
_init_control_point(spline);
206+
twin_button_t *button =
207+
twin_button_create(parent, "SwitchCurve", 0xffae0000, D(10),
208+
TwinStyleBold | TwinStyleOblique);
209+
twin_widget_set(&button->label.widget, 0xc0808080);
210+
button->signal = _apps_spline_button_signal;
211+
button->closure = spline;
212+
button->label.widget.shape = TwinShapeRectangle;
141213
}
142214

143-
static apps_spline_t *apps_spline_create(twin_box_t *parent)
215+
static apps_spline_t *apps_spline_create(twin_box_t *parent, int n_points)
144216
{
145217
apps_spline_t *spline = malloc(sizeof(apps_spline_t));
146218

147-
_apps_spline_init(spline, parent, _apps_spline_dispatch);
219+
_apps_spline_init(spline, parent, _apps_spline_dispatch, n_points);
148220
return spline;
149221
}
150222

@@ -157,7 +229,7 @@ void apps_spline_start(twin_screen_t *screen,
157229
{
158230
twin_toplevel_t *toplevel = twin_toplevel_create(
159231
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, name);
160-
apps_spline_t *spline = apps_spline_create(&toplevel->box);
232+
apps_spline_t *spline = apps_spline_create(&toplevel->box, 4);
161233
(void) spline;
162234
twin_toplevel_show(toplevel);
163235
}

include/twin.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,12 @@ void twin_path_curve(twin_path_t *path,
10611061
twin_fixed_t x3,
10621062
twin_fixed_t y3);
10631063

1064+
void twin_path_quadratic_curve(twin_path_t *path,
1065+
twin_fixed_t x1,
1066+
twin_fixed_t y1,
1067+
twin_fixed_t x2,
1068+
twin_fixed_t y2);
1069+
10641070
/*
10651071
* timeout.c
10661072
*/

src/spline.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,32 @@ void twin_path_curve(twin_path_t *path,
164164
_twin_matrix_x(&path->state.matrix, x3, y3),
165165
_twin_matrix_y(&path->state.matrix, x3, y3));
166166
}
167+
168+
void twin_path_quadratic_curve(twin_path_t *path,
169+
twin_fixed_t x1,
170+
twin_fixed_t y1,
171+
twin_fixed_t x2,
172+
twin_fixed_t y2)
173+
{
174+
/* Convert quadratic to cubic control point */
175+
twin_spoint_t p0 = _twin_path_current_spoint(path);
176+
twin_sfixed_t x1s = _twin_matrix_x(&path->state.matrix, x1, y1);
177+
twin_sfixed_t y1s = _twin_matrix_y(&path->state.matrix, x1, y1);
178+
twin_sfixed_t x2s = _twin_matrix_x(&path->state.matrix, x2, y2);
179+
twin_sfixed_t y2s = _twin_matrix_y(&path->state.matrix, x2, y2);
180+
/* CP1 = P0 + 2/3 * (P1 - P0) */
181+
twin_sfixed_t dx1 = x1s - p0.x;
182+
twin_sfixed_t dy1 = y1s - p0.y;
183+
twin_sfixed_t cx1 =
184+
p0.x + twin_sfixed_mul(twin_double_to_sfixed(2.0 / 3.0), dx1);
185+
twin_sfixed_t cy1 =
186+
p0.y + twin_sfixed_mul(twin_double_to_sfixed(2.0 / 3.0), dy1);
187+
/* CP2 = P2 + 2/3 * (P1 - P2) */
188+
twin_sfixed_t dx2 = x1s - x2s;
189+
twin_sfixed_t dy2 = y1s - y2s;
190+
twin_sfixed_t cx2 =
191+
x2s + twin_sfixed_mul(twin_double_to_sfixed(2.0 / 3.0), dx2);
192+
twin_sfixed_t cy2 =
193+
y2s + twin_sfixed_mul(twin_double_to_sfixed(2.0 / 3.0), dy2);
194+
_twin_path_scurve(path, cx1, cy1, cx2, cy2, x2s, y2s);
195+
}

0 commit comments

Comments
 (0)