Skip to content

Commit

Permalink
Implement quadratic bezier curve
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ndsl7109256 committed Nov 19, 2024
1 parent c7bbcc7 commit c5dd720
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 38 deletions.
116 changes: 78 additions & 38 deletions apps/spline.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
}
6 changes: 6 additions & 0 deletions include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
23 changes: 23 additions & 0 deletions src/spline.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit c5dd720

Please sign in to comment.