Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support path function to fulfill TinyVG rendering #74

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions apps/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,54 @@ static void apps_jelly_start(twin_screen_t *screen, int x, int y, int w, int h)
twin_window_show(window);
}

static void draw_flower(twin_path_t *path,
twin_fixed_t radius,
int number_of_petals)
{
const twin_angle_t angle_shift = TWIN_ANGLE_360 / number_of_petals;
const twin_angle_t angle_start = angle_shift / 2;
twin_fixed_t p_x = twin_fixed_mul(radius, twin_cos(-angle_start));
twin_fixed_t p_y = twin_fixed_mul(radius, twin_sin(-angle_start));
twin_path_move(path, p_x, p_y);

for (twin_angle_t a = angle_start; a <= TWIN_ANGLE_360; a += angle_shift) {
twin_fixed_t c_x = twin_fixed_mul(radius, twin_cos(a));
twin_fixed_t c_y = twin_fixed_mul(radius, twin_sin(a));
twin_fixed_t rx = radius;
twin_fixed_t ry = radius * 3;
twin_path_arc_ellipse(path, 1, 1, rx, ry, p_x, p_y, c_x, c_y,
a - angle_start);
p_x = c_x;
p_y = c_y;
}

twin_path_close(path);
}

static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h)
{
twin_window_t *window = twin_window_create(
screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h);
twin_pixmap_t *pixmap = window->pixmap;
twin_path_t *stroke = twin_path_create();
twin_path_t *path = twin_path_create();
twin_path_translate(path, D(200), D(200));
twin_path_scale(path, D(10), D(10));
twin_path_translate(stroke, D(200), D(200));
twin_fill(pixmap, 0xffffffff, TWIN_SOURCE, 0, 0, w, h);
twin_window_set_name(window, "Flower");
twin_path_move(stroke, D(-200), D(0));
twin_path_draw(stroke, D(200), D(0));
twin_path_move(stroke, D(0), D(200));
twin_path_draw(stroke, D(0), D(-200));
twin_path_set_cap_style(stroke, TwinCapProjecting);
twin_paint_stroke(pixmap, 0xffcc9999, stroke, D(10));
draw_flower(path, D(3), 5);
twin_paint_path(pixmap, 0xffe2d2d2, path);
twin_path_destroy(stroke);
twin_window_show(window);
}

void apps_multi_start(twin_screen_t *screen,
const char *name,
int x,
Expand All @@ -233,4 +281,5 @@ void apps_multi_start(twin_screen_t *screen,
apps_quickbrown_start(screen, x += 20, y += 20, w, h);
apps_ascii_start(screen, x += 20, y += 20, w, h);
apps_jelly_start(screen, x += 20, y += 20, w / 2, h);
apps_flower_start(screen, x += 20, y += 20, w, h);
}
150 changes: 111 additions & 39 deletions apps/spline.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,86 @@

#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 n_points;
twin_point_t *points;
int which;
twin_fixed_t line_width;
twin_cap_t cap_style;
twin_matrix_t transition;
twin_matrix_t inverse_transition;
} apps_spline_t;

static void _init_control_point(apps_spline_t *spline)
{
const int init_point_quad[3][2] = {
{100, 100},
{200, 100},
{300, 100},
};
const int init_point_cubic[4][2] = {
{100, 100},
{300, 300},
{100, 300},
{300, 100},
};
const int(*init_point)[2];
if (spline->n_points == 4) {
init_point = init_point_cubic;
} else if (spline->n_points == 3) {
init_point = init_point_quad;
}
for (int i = 0; i < spline->n_points; i++) {
spline->points[i].x = twin_int_to_fixed(init_point[i][0]);
spline->points[i].y = twin_int_to_fixed(init_point[i][1]);
}
}

static void _draw_aux_line(twin_path_t *path,
apps_spline_t *spline,
int idx1,
int idx2)
{
twin_path_move(path, spline->points[idx1].x, spline->points[idx1].y);
twin_path_draw(path, spline->points[idx2].x, spline->points[idx2].y);
twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path,
twin_int_to_fixed(2));
twin_path_empty(path);
}

static void _apps_spline_paint(apps_spline_t *spline)
{
twin_path_t *path;
int i;

path = twin_path_create();
twin_path_set_cap_style(path, spline->cap_style);
twin_path_set_matrix(path, spline->transition);

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);
if (spline->n_points == 4) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you generalize the handling for n_points = 3 and 4? That is, share code by avoiding duplication.

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);
} else if (spline->n_points == 3) {
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[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->n_points == 4) {
_draw_aux_line(path, spline, 0, 1);
_draw_aux_line(path, spline, 3, 2);
} else if (spline->n_points == 3) {
_draw_aux_line(path, spline, 0, 1);
_draw_aux_line(path, spline, 1, 2);
}

for (int i = 0; i < spline->n_points; i++) {
twin_path_empty(path);
twin_path_circle(path, spline->points[i].x, spline->points[i].y,
twin_int_to_fixed(10));
Expand All @@ -62,13 +103,32 @@ static void _apps_spline_paint(apps_spline_t *spline)
twin_path_destroy(path);
}

static void _apps_spline_button_signal(maybe_unused twin_button_t *button,
twin_button_signal_t signal,
void *closure)
{
if (signal != TwinButtonSignalDown)
return;

apps_spline_t *spline = closure;
spline->n_points = (spline->n_points == 3) ? 4 : 3;
_init_control_point(spline);
_twin_widget_queue_paint(&spline->widget);
}

#define D(x) twin_double_to_fixed(x)
static twin_dispatch_result_t _apps_spline_update_pos(apps_spline_t *spline,
twin_event_t *event)
{
if (spline->which < 0)
return TwinDispatchContinue;
spline->points[spline->which].x = twin_int_to_fixed(event->u.pointer.x);
spline->points[spline->which].y = twin_int_to_fixed(event->u.pointer.y);
twin_fixed_t x = twin_int_to_fixed(event->u.pointer.x);
twin_fixed_t y = twin_int_to_fixed(event->u.pointer.y);

spline->points[spline->which].x = twin_sfixed_to_fixed(
_twin_matrix_x(&(spline->inverse_transition), x, y));
spline->points[spline->which].y = twin_sfixed_to_fixed(
_twin_matrix_y(&(spline->inverse_transition), x, y));
_twin_widget_queue_paint(&spline->widget);
return TwinDispatchDone;
}
Expand All @@ -80,11 +140,15 @@ static int _apps_spline_hit(apps_spline_t *spline,
twin_fixed_t y)
{
int i;

for (i = 0; i < 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)
for (i = 0; i < spline->n_points; i++) {
twin_fixed_t px = twin_sfixed_to_fixed(_twin_matrix_x(
&(spline->transition), spline->points[i].x, spline->points[i].y));
twin_fixed_t py = twin_sfixed_to_fixed(_twin_matrix_y(
&(spline->transition), spline->points[i].x, spline->points[i].y));
if (twin_fixed_abs(x - px) < spline->line_width / 2 &&
twin_fixed_abs(y - py) < spline->line_width / 2)
return i;
}
return -1;
}

Expand Down Expand Up @@ -123,28 +187,36 @@ 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 n_points)
{
static const twin_widget_layout_t preferred = {0, 0, 1, 1};
static twin_widget_layout_t preferred = {0, 0, 1, 1};
preferred.height = parent->widget.window->screen->height * 2 / 3;
_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);
twin_matrix_identity(&spline->transition);
twin_matrix_rotate(&spline->transition, TWIN_ANGLE_11_25);
twin_matrix_identity(&spline->inverse_transition);
twin_matrix_rotate(&spline->inverse_transition, -TWIN_ANGLE_11_25);
spline->points = calloc(n_points, sizeof(twin_point_t));
spline->n_points = n_points;
_init_control_point(spline);
twin_button_t *button =
twin_button_create(parent, "SwitchCurve", 0xffae0000, D(10),
TwinStyleBold | TwinStyleOblique);
twin_widget_set(&button->label.widget, 0xc0808080);
button->signal = _apps_spline_button_signal;
button->closure = spline;
button->label.widget.shape = TwinShapeRectangle;
}

static apps_spline_t *apps_spline_create(twin_box_t *parent)
static apps_spline_t *apps_spline_create(twin_box_t *parent, int n_points)
{
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, n_points);
return spline;
}

Expand All @@ -157,7 +229,7 @@ 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);
apps_spline_t *spline = apps_spline_create(&toplevel->box, 4);
(void) spline;
twin_toplevel_show(toplevel);
}
33 changes: 32 additions & 1 deletion include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ void twin_clear_file(twin_file_t *file);
*/

#define twin_fixed_mul(a, b) ((twin_fixed_t) (((int64_t) (a) * (b)) >> 16))
#define twin_fixed_div(a, b) ((twin_fixed_t) ((((int64_t) (a)) << 16) / b))
#define twin_fixed_div(a, b) ((twin_fixed_t) ((((int64_t) (a)) << 16) / (b)))

twin_fixed_t twin_fixed_sqrt(twin_fixed_t a);

Expand Down Expand Up @@ -800,6 +800,7 @@ void twin_path_ellipse(twin_path_t *path,
twin_fixed_t y,
twin_fixed_t x_radius,
twin_fixed_t y_radius);

void twin_path_arc(twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
Expand All @@ -808,6 +809,26 @@ void twin_path_arc(twin_path_t *path,
twin_angle_t start,
twin_angle_t extent);

void twin_path_arc_ellipse(twin_path_t *path,
bool large_arc,
bool sweep,
twin_fixed_t radius_x,
twin_fixed_t radius_y,
twin_fixed_t cur_x,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use

twin_point_t cur_point;

Replace

twin_fixed_t cur_x;
twin_fixed_t cur_y;

The advantage of doing this is that it directly separates the function of radius_x from the reading the code here, clearly indicating that it is an object representing a point.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I have also considered replacing cur_x and cur_y with cur_point. However, it might create an inconsistent style compared to other path functions like twin_path_arc, twin_path_circle, twin_path_ellipse.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

I got it, close this review, please.

twin_fixed_t cur_y,
twin_fixed_t target_x,
twin_fixed_t target_y,
twin_angle_t rotation);

void twin_path_arc_circle(twin_path_t *path,
bool large_arc,
bool sweep,
twin_fixed_t radius,
twin_fixed_t cur_x,
twin_fixed_t cur_y,
twin_fixed_t target_x,
twin_fixed_t target_y);

void twin_path_rectangle(twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
Expand Down Expand Up @@ -1061,6 +1082,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 Expand Up @@ -1098,6 +1125,10 @@ twin_fixed_t twin_tan(twin_angle_t a);

void twin_sincos(twin_angle_t a, twin_fixed_t *sin, twin_fixed_t *cos);

twin_angle_t twin_atan2(twin_fixed_t y, twin_fixed_t x);

twin_angle_t twin_acos(twin_fixed_t x);

/*
* widget.c
*/
Expand Down
Loading