From c5dd720d017e511d51960498449bd9a4e2a0d8f9 Mon Sep 17 00:00:00 2001 From: ndsl7109256 Date: Tue, 19 Nov 2024 09:39:27 +0800 Subject: [PATCH] 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 --- apps/spline.c | 116 +++++++++++++++++++++++++++++++++---------------- include/twin.h | 6 +++ src/spline.c | 23 ++++++++++ 3 files changed, 107 insertions(+), 38 deletions(-) diff --git a/apps/spline.c b/apps/spline.c index cd99daf..8f3907e 100644 --- a/apps/spline.c +++ b/apps/spline.c @@ -15,11 +15,11 @@ #define _apps_spline_pixmap(spline) ((spline)->widget.window->pixmap) -#define NPT 4 typedef struct _apps_spline { twin_widget_t widget; - twin_point_t points[NPT]; + int npt; + twin_point_t *points; int which; twin_fixed_t line_width; twin_cap_t cap_style; @@ -32,28 +32,51 @@ static void _apps_spline_paint(apps_spline_t *spline) path = twin_path_create(); twin_path_set_cap_style(path, spline->cap_style); - twin_path_move(path, spline->points[0].x, spline->points[0].y); - twin_path_curve(path, spline->points[1].x, spline->points[1].y, - spline->points[2].x, spline->points[2].y, - spline->points[3].x, spline->points[3].y); - twin_paint_stroke(_apps_spline_pixmap(spline), 0xff404040, path, - spline->line_width); - twin_path_set_cap_style(path, TwinCapButt); - twin_paint_stroke(_apps_spline_pixmap(spline), 0xffffff00, path, - twin_int_to_fixed(2)); - - twin_path_empty(path); - twin_path_move(path, spline->points[0].x, spline->points[0].y); - twin_path_draw(path, spline->points[1].x, spline->points[1].y); - twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, - twin_int_to_fixed(2)); - twin_path_empty(path); - twin_path_move(path, spline->points[3].x, spline->points[3].y); - twin_path_draw(path, spline->points[2].x, spline->points[2].y); - twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, - twin_int_to_fixed(2)); - twin_path_empty(path); - for (i = 0; i < NPT; i++) { + if (spline->npt == 4) { + twin_path_move(path, spline->points[0].x, spline->points[0].y); + twin_path_curve(path, spline->points[1].x, spline->points[1].y, + spline->points[2].x, spline->points[2].y, + spline->points[3].x, spline->points[3].y); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xff404040, path, + spline->line_width); + twin_path_set_cap_style(path, TwinCapButt); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xffffff00, path, + twin_int_to_fixed(2)); + + twin_path_empty(path); + twin_path_move(path, spline->points[0].x, spline->points[0].y); + twin_path_draw(path, spline->points[1].x, spline->points[1].y); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, + twin_int_to_fixed(2)); + twin_path_empty(path); + twin_path_move(path, spline->points[3].x, spline->points[3].y); + twin_path_draw(path, spline->points[2].x, spline->points[2].y); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, + twin_int_to_fixed(2)); + twin_path_empty(path); + } else if (spline->npt == 3) { + twin_path_move(path, spline->points[0].x, spline->points[0].y); + twin_path_quadratic_curve(path, spline->points[1].x, + spline->points[1].y, spline->points[2].x, + spline->points[2].y); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xff404040, path, + spline->line_width); + twin_path_set_cap_style(path, TwinCapButt); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xffffff00, path, + twin_int_to_fixed(2)); + twin_path_empty(path); + twin_path_move(path, spline->points[0].x, spline->points[0].y); + twin_path_draw(path, spline->points[1].x, spline->points[1].y); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, + twin_int_to_fixed(2)); + twin_path_empty(path); + twin_path_move(path, spline->points[1].x, spline->points[1].y); + twin_path_draw(path, spline->points[2].x, spline->points[2].y); + twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, + twin_int_to_fixed(2)); + twin_path_empty(path); + } + for (i = 0; i < spline->npt; i++) { twin_path_empty(path); twin_path_circle(path, spline->points[i].x, spline->points[i].y, twin_int_to_fixed(10)); @@ -81,7 +104,7 @@ static int _apps_spline_hit(apps_spline_t *spline, { int i; - for (i = 0; i < NPT; i++) + for (i = 0; i < spline->npt; i++) if (twin_fixed_abs(x - spline->points[i].x) < spline->line_width / 2 && twin_fixed_abs(y - spline->points[i].y) < spline->line_width / 2) return i; @@ -123,28 +146,40 @@ static twin_dispatch_result_t _apps_spline_dispatch(twin_widget_t *widget, static void _apps_spline_init(apps_spline_t *spline, twin_box_t *parent, - twin_dispatch_proc_t dispatch) + twin_dispatch_proc_t dispatch, + int npt) { static const twin_widget_layout_t preferred = {0, 0, 1, 1}; _twin_widget_init(&spline->widget, parent, 0, preferred, dispatch); twin_widget_set(&spline->widget, 0xffffffff); spline->line_width = twin_int_to_fixed(100); spline->cap_style = TwinCapRound; - spline->points[0].x = twin_int_to_fixed(100); - spline->points[0].y = twin_int_to_fixed(100); - spline->points[1].x = twin_int_to_fixed(300); - spline->points[1].y = twin_int_to_fixed(300); - spline->points[2].x = twin_int_to_fixed(100); - spline->points[2].y = twin_int_to_fixed(300); - spline->points[3].x = twin_int_to_fixed(300); - spline->points[3].y = twin_int_to_fixed(100); + spline->points = calloc(npt, sizeof(twin_point_t)); + spline->npt = npt; + if (npt == 4) { + spline->points[0].x = twin_int_to_fixed(100); + spline->points[0].y = twin_int_to_fixed(100); + spline->points[1].x = twin_int_to_fixed(300); + spline->points[1].y = twin_int_to_fixed(300); + spline->points[2].x = twin_int_to_fixed(100); + spline->points[2].y = twin_int_to_fixed(300); + spline->points[3].x = twin_int_to_fixed(300); + spline->points[3].y = twin_int_to_fixed(100); + } else if (npt == 3) { + spline->points[0].x = twin_int_to_fixed(100); + spline->points[0].y = twin_int_to_fixed(100); + spline->points[1].x = twin_int_to_fixed(200); + spline->points[1].y = twin_int_to_fixed(300); + spline->points[2].x = twin_int_to_fixed(300); + spline->points[2].y = twin_int_to_fixed(100); + } } -static apps_spline_t *apps_spline_create(twin_box_t *parent) +static apps_spline_t *apps_spline_create(twin_box_t *parent, int npt) { apps_spline_t *spline = malloc(sizeof(apps_spline_t)); - _apps_spline_init(spline, parent, _apps_spline_dispatch); + _apps_spline_init(spline, parent, _apps_spline_dispatch, npt); return spline; } @@ -157,7 +192,12 @@ void apps_spline_start(twin_screen_t *screen, { twin_toplevel_t *toplevel = twin_toplevel_create( screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, name); - apps_spline_t *spline = apps_spline_create(&toplevel->box); - (void) spline; + apps_spline_t *spline_cubic = apps_spline_create(&toplevel->box, 4); + twin_toplevel_t *toplevel2 = twin_toplevel_create( + screen, TWIN_ARGB32, TwinWindowApplication, x + 20, y + 20, w, h, name); + apps_spline_t *spline_quad = apps_spline_create(&toplevel2->box, 3); + (void) spline_cubic; + (void) spline_quad; twin_toplevel_show(toplevel); + twin_toplevel_show(toplevel2); } diff --git a/include/twin.h b/include/twin.h index 356ccaa..8f7c60f 100644 --- a/include/twin.h +++ b/include/twin.h @@ -1061,6 +1061,12 @@ void twin_path_curve(twin_path_t *path, twin_fixed_t x3, twin_fixed_t y3); +void twin_path_quadratic_curve(twin_path_t *path, + twin_fixed_t x1, + twin_fixed_t y1, + twin_fixed_t x2, + twin_fixed_t y2); + /* * timeout.c */ diff --git a/src/spline.c b/src/spline.c index 6d1d7f8..e716cfc 100644 --- a/src/spline.c +++ b/src/spline.c @@ -164,3 +164,26 @@ void twin_path_curve(twin_path_t *path, _twin_matrix_x(&path->state.matrix, x3, y3), _twin_matrix_y(&path->state.matrix, x3, y3)); } + +void twin_path_quadratic_curve(twin_path_t *path, + twin_fixed_t x1, + twin_fixed_t y1, + twin_fixed_t x2, + twin_fixed_t y2) +{ + twin_spoint_t p0 = _twin_path_current_spoint(path); + /* Convert quadratic to cubic control point */ + /* CP1 = P0 + 2/3 * (P1 - P0) */ + twin_sfixed_t dx1 = twin_fixed_to_sfixed(x1) - p0.x; + twin_sfixed_t dy1 = twin_fixed_to_sfixed(y1) - p0.y; + twin_sfixed_t cx1 = p0.x + twin_sfixed_mul(dx1, twin_int_to_sfixed(2) / 3); + twin_sfixed_t cy1 = p0.y + twin_sfixed_mul(dy1, twin_int_to_sfixed(2) / 3); + /* CP2 = P2 + 2/3 * (P1 - P2) */ + twin_sfixed_t x2s = twin_fixed_to_sfixed(x2); + twin_sfixed_t y2s = twin_fixed_to_sfixed(y2); + twin_sfixed_t dx2 = twin_fixed_to_sfixed(x1) - x2s; + twin_sfixed_t dy2 = twin_fixed_to_sfixed(y1) - y2s; + twin_sfixed_t cx2 = x2s + twin_sfixed_mul(dx2, twin_int_to_sfixed(2) / 3); + twin_sfixed_t cy2 = y2s + twin_sfixed_mul(dy2, twin_int_to_sfixed(2) / 3); + _twin_path_scurve(path, cx1, cy1, cx2, cy2, x2s, y2s); +}