From 34cd81550e796ee58a25dd82c11ea8841938352a Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Sat, 20 Apr 2024 23:26:03 +0200 Subject: [PATCH] Fixed rounding problems for path drawing --- pdlua_gfx.h | 383 +++++++++++++++++++++++++++------------------------- 1 file changed, 202 insertions(+), 181 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 69cdf6a..ba8034c 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -110,14 +110,14 @@ void pdlua_gfx_repaint(t_pdlua *o, int firsttime) { lua_getglobal(__L(), "pd"); lua_getfield (__L(), -1, "_repaint"); lua_pushlightuserdata(__L(), o); - - + + if (lua_pcall(__L(), 1, 0, 0)) { pd_error(o, "lua: error in repaint:\n%s", lua_tostring(__L(), -1)); lua_pop(__L(), 1); /* pop the error string */ } - + lua_pop(__L(), 1); /* pop the global "pd" */ #if !PLUGDATA o->gfx.first_draw = 0; @@ -126,20 +126,20 @@ void pdlua_gfx_repaint(t_pdlua *o, int firsttime) { // Pass mouse events to lua script void pdlua_gfx_mouse_event(t_pdlua *o, int x, int y, int type) { - + lua_getglobal(__L(), "pd"); lua_getfield (__L(), -1, "_mouseevent"); lua_pushlightuserdata(__L(), o); lua_pushinteger(__L(), x); lua_pushinteger(__L(), y); lua_pushinteger(__L(), type); - + if (lua_pcall(__L(), 4, 0, 0)) { pd_error(o, "lua: error in mouseevent:\n%s", lua_tostring(__L(), -1)); lua_pop(__L(), 1); /* pop the error string */ } - + lua_pop(__L(), 1); /* pop the global "pd" */ } @@ -165,10 +165,10 @@ void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) { typedef struct _path_state { // Variables for managing vector paths - int* path_segments; + float* path_segments; int num_path_segments; int num_path_segments_allocated; - int path_start_x, path_start_y; + float path_start_x, path_start_y; } t_path_state; @@ -222,7 +222,7 @@ int pdlua_gfx_setup(lua_State* L) { // for Path(x, y) constructor lua_pushcfunction(L, start_path); lua_setglobal(L, "Path"); - + luaL_newmetatable(L, "Path"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); @@ -232,11 +232,11 @@ int pdlua_gfx_setup(lua_State* L) { lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_setfuncs(L, gfx_methods, 0); - + // Register functions with Lua luaL_newlib(L, gfx_lib); lua_setglobal(L, "_gfx_internal"); - + return 1; // Number of values pushed onto the stack } @@ -245,7 +245,7 @@ static int get_size(lua_State* L) if (!lua_islightuserdata(L, 1)) { return 0; } - + t_pdlua *obj = (t_pdlua*)lua_touserdata(L, 1); lua_pushnumber(L, (lua_Number)obj->gfx.width); lua_pushnumber(L, (lua_Number)obj->gfx.height); @@ -288,7 +288,7 @@ static int set_size(lua_State* L) if (!lua_islightuserdata(L, 1)) { return 0; } - + t_pdlua *obj = (t_pdlua*)lua_touserdata(L, 1); obj->gfx.width = luaL_checknumber(L, 2); obj->gfx.height = luaL_checknumber(L, 3); @@ -305,10 +305,10 @@ static int start_paint(lua_State* L) { return 1; } t_pdlua *obj = (t_pdlua*)lua_touserdata(L, 1); - + lua_pushlightuserdata(L, &obj->gfx); luaL_setmetatable(L, "GraphicsContext"); - + plugdata_draw_callback = obj->gfx.plugdata_draw_callback; plugdata_draw(obj, gensym("lua_start_paint"), 0, NULL); return 1; @@ -330,12 +330,12 @@ static int set_color(lua_State* L) { plugdata_draw(obj, gensym("lua_set_color"), 1, &arg); return 0; } - + t_atom args[4]; SETFLOAT(args, luaL_checknumber(L, 1)); // r SETFLOAT(args + 1, luaL_checknumber(L, 2)); // g SETFLOAT(args + 2, luaL_checknumber(L, 3)); // b - + if (lua_gettop(L) > 4) { // object and table are already on stack, hence 5 // alpha (optional, default to 1.0) SETFLOAT(args + 3, luaL_checknumber(L, 4)); @@ -434,7 +434,7 @@ static int draw_line(lua_State* L) { SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h SETFLOAT(args + 4, luaL_checknumber(L, 5)); // line width plugdata_draw(gfx->object, gensym("lua_draw_line"), 5, args); - + return 0; } @@ -454,67 +454,67 @@ static int draw_text(lua_State* L) { static int stroke_path(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); int stroke_width = luaL_checknumber(L, 2) * glist_getzoom(cnv); - - int last_x = 0; - int last_y = 0; - + + float last_x = 0; + float last_y = 0; + t_atom* coordinates = malloc(2 * path->num_path_segments * sizeof(t_atom) + 1); SETFLOAT(coordinates, stroke_width); - + int num_real_segments = 0; for (int i = 0; i < path->num_path_segments; i++) { - int x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; + float x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; if(i != 0 && x == last_x && y == last_y) continue; // In case integer rounding causes the same point twice SETFLOAT(coordinates + (num_real_segments * 2) + 1, x); SETFLOAT(coordinates + (num_real_segments * 2) + 2, y); num_real_segments++; - + last_x = x; last_y = y; } - + plugdata_draw(gfx->object, gensym("lua_stroke_path"), num_real_segments * 2 + 1, coordinates); free(coordinates); - + return 0; } static int fill_path(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); - int last_x = 0; - int last_y = 0; - + float last_x = 0; + float last_y = 0; + t_atom* coordinates = malloc(2 * path->num_path_segments * sizeof(t_atom)); int num_real_segments = 0; for (int i = 0; i < path->num_path_segments; i++) { - int x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; + float x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; if(i != 0 && x == last_x && y == last_y) continue; // In case integer rounding causes the same point twice SETFLOAT(coordinates + (num_real_segments * 2), x); SETFLOAT(coordinates + (num_real_segments * 2) + 1, y); num_real_segments++; - + last_x = x; last_y = y; } - + plugdata_draw(gfx->object, gensym("lua_fill_path"), num_real_segments * 2, coordinates); free(coordinates); - + return 0; } @@ -555,7 +555,7 @@ static unsigned long long custom_rand() { const unsigned long long m = 4294967296; // 2^32 seed = (a * seed + c) % m; if(seed == 0) seed = 1; // We cannot return 0 since we use modulo on this. Having the rhs operator of % be zero leads to div-by-zero error on Windows - + return seed; } @@ -563,15 +563,15 @@ static unsigned long long custom_rand() { static void generate_random_id(char *str, size_t len) { const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; size_t charset_len = strlen(charset); - + str[0] = '.'; str[1] = 'x'; - + for (size_t i = 2; i < len - 1; ++i) { int key = custom_rand() % charset_len; str[i] = charset[key]; } - + str[len - 1] = '\0'; } @@ -602,12 +602,27 @@ static void transform_point(t_pdlua_gfx *gfx, int* x, int* y) { } } +static void transform_point_float(t_pdlua_gfx *gfx, float* x, float* y) { + for(int i = gfx->num_transforms - 1; i >= 0; i--) + { + if(gfx->transforms[i].type == SCALE) + { + *x *= gfx->transforms[i].x; + *y *= gfx->transforms[i].y; + } + else // translate + { + *x += gfx->transforms[i].x; + *y += gfx->transforms[i].y; + } + } +} static void pdlua_gfx_clear(t_pdlua *obj, int removed) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); - + if(removed && gfx->order_tag[0] != '\0') { pdgui_vmess(0, "crs", cnv, "delete", gfx->order_tag); @@ -619,18 +634,18 @@ static void pdlua_gfx_clear(t_pdlua *obj, int removed) { static void get_bounds_args(lua_State* L, t_pdlua *obj, int* x1, int* y1, int* x2, int* y2) { t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x = luaL_checknumber(L, 1); int y = luaL_checknumber(L, 2); int w = luaL_checknumber(L, 3); int h = luaL_checknumber(L, 4); - + transform_point(&obj->gfx, &x, &y); transform_size(&obj->gfx, &w, &h); - + x += text_xpix((t_object*)obj, obj->canvas) / glist_getzoom(cnv); y += text_ypix((t_object*)obj, obj->canvas) / glist_getzoom(cnv); - + *x1 = x * glist_getzoom(cnv); *y1 = y * glist_getzoom(cnv); *x2 = (x + w) * glist_getzoom(cnv); @@ -641,9 +656,9 @@ static void gfx_displace(t_pdlua *x, t_glist *glist, int dx, int dy) { sys_vgui(".x%lx.c move .x%lx %d %d\n", glist_getcanvas(x->canvas), (long)x, dx, dy); canvas_fixlinesfor(glist, (t_text*)x); - + int scale = glist_getzoom(glist_getcanvas(x->canvas)); - + int xpos = text_xpix((t_object*)x, x->canvas); int ypos = text_ypix((t_object*)x, x->canvas); glist_drawiofor(x->canvas, (t_object*)x, 0, x->gfx.object_tag, xpos, ypos, xpos + (x->gfx.width * scale), ypos + (x->gfx.height * scale)); @@ -658,14 +673,14 @@ static const char* register_drawing(t_pdlua_gfx *gfx) static int gfx_initialize(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; - + snprintf(gfx->object_tag, 128, ".x%lx", (long)obj); gfx->object_tag[127] = '\0'; gfx->order_tag[0] = '\0'; gfx->object = obj; gfx->transforms = NULL; gfx->num_transforms = 0; - + pdlua_gfx_repaint(obj, 0); return 0; } @@ -675,7 +690,7 @@ static int set_size(lua_State* L) if (!lua_islightuserdata(L, 1)) { return 0; } - + t_pdlua *obj = (t_pdlua*)lua_touserdata(L, 1); obj->gfx.width = luaL_checknumber(L, 2); obj->gfx.height = luaL_checknumber(L, 3); @@ -689,7 +704,7 @@ static int start_paint(lua_State* L) { lua_pushnil(L); return 1; } - + t_pdlua* obj = (t_pdlua*)lua_touserdata(L, 1); t_pdlua_gfx *gfx = &obj->gfx; if(gfx->object_tag[0] == '\0') @@ -708,13 +723,13 @@ static int start_paint(lua_State* L) { if(gfx->transforms) freebytes(gfx->transforms, gfx->num_transforms * sizeof(gfx_transform)); gfx->num_transforms = 0; gfx->transforms = NULL; - + lua_pushlightuserdata(L, gfx); luaL_setmetatable(L, "GraphicsContext"); - + // clear anything that was painted before if(strlen(gfx->object_tag)) pdlua_gfx_clear(obj, 0); - + if(gfx->first_draw) { // Whenever the objects gets painted for the first time with a "vis" message, @@ -722,12 +737,12 @@ static int start_paint(lua_State* L) { // We can then use this line to set the correct z-index for the drawings, using the tcl/tk "lower" command t_canvas *cnv = glist_getcanvas(obj->canvas); generate_random_id(gfx->order_tag, 64); - + const char* tags[] = { gfx->order_tag }; pdgui_vmess(0, "crr iiii ri rS", cnv, "create", "line", 0, 0, 0, 0, "-width", 1, "-tags", 1, tags); } - + return 1; } @@ -739,26 +754,26 @@ static int end_paint(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = (t_pdlua*)gfx->object; t_canvas *cnv = glist_getcanvas(obj->canvas); - + int scale = glist_getzoom(glist_getcanvas(obj->canvas)); - + // Draw iolets on top int xpos = text_xpix((t_object*)obj, obj->canvas); int ypos = text_ypix((t_object*)obj, obj->canvas); - + glist_drawiofor(glist_getcanvas(obj->canvas), (t_object*)obj, 1, gfx->object_tag, xpos, ypos, xpos + (gfx->width * scale), ypos + (gfx->height * scale)); - + if(!gfx->first_draw && gfx->order_tag[0] != '\0') { // Move everything to below the order marker, to make sure redrawn stuff isn't always on top pdgui_vmess(0, "crss", cnv, "lower", gfx->object_tag, gfx->order_tag); } - + return 0; } static int set_color(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); - + int r, g, b; if (lua_gettop(L) == 1) { // Single argument: parse as color ID instead of RGB int color_id = luaL_checknumber(L, 1); @@ -783,102 +798,102 @@ static int set_color(lua_State* L) { // AFAIK, alpha is not supported in tcl/tk snprintf(gfx->current_color, 8, "#%02X%02X%02X", r, g, b); gfx->current_color[7] = '\0'; - + return 0; } static int fill_ellipse(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1, y1, x2, y2; get_bounds_args(L, obj, &x1, &y1, &x2, &y2); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + pdgui_vmess(0, "crr iiii rs ri rS", cnv, "create", "oval", x1, y1, x2, y2, "-fill", gfx->current_color, "-width", 0, "-tags", 2, tags); - + return 0; } static int stroke_ellipse(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, &x1, &y1, &x2, &y2); - + int line_width = luaL_checknumber(L, 5) * glist_getzoom(cnv); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-width", line_width, "-outline", gfx->current_color, "-tags", 2, tags); - + return 0; } static int fill_all(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1 = text_xpix((t_object*)obj, obj->canvas); int y1 = text_ypix((t_object*)obj, obj->canvas); int x2 = x1 + gfx->width * glist_getzoom(cnv); int y2 = y1 + gfx->height * glist_getzoom(cnv); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-tags", 2, tags); - + return 0; } static int fill_rect(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1, y1, x2, y2; get_bounds_args(L, obj, &x1, &y1, &x2, &y2); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + pdgui_vmess(0, "crr iiii rs ri rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-width", 0, "-tags", 2, tags); - + return 0; } static int stroke_rect(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1, y1, x2, y2; get_bounds_args(L, obj, &x1, &y1, &x2, &y2); int line_width = luaL_checknumber(L, 5) * glist_getzoom(cnv); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-width", line_width, "-outline", gfx->current_color, "-tags", 2, tags); - + return 0; } static int fill_rounded_rect(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1, y1, x2, y2; get_bounds_args(L, obj, &x1, &y1, &x2, &y2); @@ -887,7 +902,7 @@ static int fill_rounded_rect(lua_State* L) { int radius_y = radius * glist_getzoom(cnv); transform_size(gfx, &radius_x, &radius_y); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; // Tcl/tk can't fill rounded rectangles, so we draw 2 smaller rectangles with 4 ovals over the corners @@ -904,20 +919,20 @@ static int fill_rounded_rect(lua_State* L) { static int stroke_rounded_rect(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1, y1, x2, y2; get_bounds_args(L, obj, &x1, &y1, &x2, &y2); - + int radius = luaL_checknumber(L, 5); // Radius for rounded corners int radius_x = radius * glist_getzoom(cnv); int radius_y = radius * glist_getzoom(cnv); transform_size(gfx, &radius_x, &radius_y); int line_width = luaL_checknumber(L, 6) * glist_getzoom(cnv); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + // Tcl/tk can't stroke rounded rectangles either, so we draw 2 lines connecting with 4 arcs at the corners pdgui_vmess(0, "crr iiii ri ri ri ri rs rs rS", cnv, "create", "arc", x1, y1 + radius_y*2, x1 + radius_x*2, y1, "-start", 0, "-extent", 90, "-width", line_width, "-start", 90, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); @@ -927,7 +942,7 @@ static int stroke_rounded_rect(lua_State* L) { "-start", 180, "-extent", 90, "-width", line_width, "-start", 180, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri ri ri ri rs rs rS", cnv, "create", "arc", x2 - radius_x*2, y2, x2, y2 - radius_y*2, "-start", 90, "-extent", 90, "-width", line_width, "-start", 270, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); - + // Connect with lines pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 + radius_x, y1, x2 - radius_x, y1, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); @@ -937,25 +952,25 @@ static int stroke_rounded_rect(lua_State* L) { "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x2 , y1 + radius_y, x2, y2 - radius_y, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); - + return 0; } static int draw_line(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + int x1 = luaL_checknumber(L, 1); int y1 = luaL_checknumber(L, 2); int x2 = luaL_checknumber(L, 3); int y2 = luaL_checknumber(L, 4); int line_width = luaL_checknumber(L, 5); - + transform_point(gfx, &x1, &y1); transform_point(gfx, &x2, &y2); - + int canvas_zoom = glist_getzoom(cnv); x1 += text_xpix((t_object*)obj, obj->canvas) / canvas_zoom; @@ -968,9 +983,9 @@ static int draw_line(lua_State* L) { x2 *= canvas_zoom; y2 *= canvas_zoom; line_width *= canvas_zoom; - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1, y1, x2, y2, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); @@ -980,29 +995,29 @@ static int draw_line(lua_State* L) { static int draw_text(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + const char* text = luaL_checkstring(L, 1); // Assuming text is a string int x = luaL_checknumber(L, 2); int y = luaL_checknumber(L, 3); int w = luaL_checknumber(L, 4); int font_height = luaL_checknumber(L, 5); font_height = sys_hostfontsize(font_height, glist_getzoom(cnv)); - + transform_point(gfx, &x, &y); transform_size(gfx, &w, &font_height); - + int canvas_zoom = glist_getzoom(cnv); x += text_xpix((t_object*)obj, obj->canvas) / canvas_zoom; y += text_ypix((t_object*)obj, obj->canvas) / canvas_zoom; - + x *= canvas_zoom; y *= canvas_zoom; w *= canvas_zoom; - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; - + pdgui_vmess(0, "crr ii rs ri rs rS", cnv, "create", "text", 0, 0, "-anchor", "nw", "-width", w, "-text", text, "-tags", 2, tags); @@ -1015,77 +1030,83 @@ static int draw_text(lua_State* L) { "-font", 3, fontatoms, "-fill", gfx->current_color, "-justify", "left"); - + pdgui_vmess(0, "crs ii", cnv, "coords", tags[1], x, y); - + return 0; } static int stroke_path(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); + if(path->num_path_segments < 3) + { + return 0; + } + int stroke_width = luaL_checknumber(L, 2) * glist_getzoom(cnv); - int obj_x = text_xpix((t_object*)obj, obj->canvas); int obj_y = text_ypix((t_object*)obj, obj->canvas); int canvas_zoom = glist_getzoom(cnv); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", 0, 0, 0, 0, "-width", stroke_width, "-fill", gfx->current_color, "-tags", 2, tags); - - int last_x, last_y; - + + float last_x, last_y; + sys_vgui(".x%lx.c coords %s", cnv, tags[1]); for (int i = 0; i < path->num_path_segments; i++) { - int x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; - if(i != 0 && x == last_x && y == last_y) continue; // In case integer rounding causes the same point twice + float x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; last_x = x; last_y = y; - - transform_point(gfx, &x, &y); - sys_vgui(" %d %d", (x * canvas_zoom) + obj_x, (y * canvas_zoom) + obj_y); + + transform_point_float(gfx, &x, &y); + sys_vgui(" %f %f", (x * canvas_zoom) + obj_x, (y * canvas_zoom) + obj_y); } sys_vgui("\n"); - + return 0; } static int fill_path(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); t_pdlua *obj = gfx->object; - + t_canvas *cnv = glist_getcanvas(obj->canvas); - + t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); + if(path->num_path_segments < 3) + { + return 0; + } // Apply transformations to all coordinates int obj_x = text_xpix((t_object*)obj, obj->canvas); int obj_y = text_ypix((t_object*)obj, obj->canvas); int canvas_zoom = glist_getzoom(cnv); - + const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "polygon", 0, 0, 0, 0, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); - - int last_x, last_y; - + + float last_x, last_y; + sys_vgui(".x%lx.c coords %s", cnv, tags[1]); for (int i = 0; i < path->num_path_segments; i++) { - int x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; - if(i != 0 && x == last_x && y == last_y) continue; // In case integer rounding causes the same point twice + float x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; last_x = x; last_y = y; - - transform_point(gfx, &x, &y); - sys_vgui(" %d %d", (x * canvas_zoom) + obj_x, (y * canvas_zoom) + obj_y); + + transform_point_float(gfx, &x, &y); + sys_vgui(" %f %f", (x * canvas_zoom) + obj_x, (y * canvas_zoom) + obj_y); } sys_vgui("\n"); - + return 0; } @@ -1096,31 +1117,31 @@ static int translate(lua_State* L) { if(gfx->num_transforms == 0) { gfx->transforms = getbytes(sizeof(gfx_transform)); - + } else { gfx->transforms = resizebytes(gfx->transforms, gfx->num_transforms * sizeof(gfx_transform), (gfx->num_transforms + 1) * sizeof(gfx_transform)); - + } - + gfx->transforms[gfx->num_transforms].type = TRANSLATE; gfx->transforms[gfx->num_transforms].x = luaL_checknumber(L, 1); gfx->transforms[gfx->num_transforms].y = luaL_checknumber(L, 2); - + gfx->num_transforms++; return 0; } static int scale(lua_State* L) { t_pdlua_gfx *gfx = pop_graphics_context(L); - + gfx->transforms = resizebytes(gfx->transforms, gfx->num_transforms * sizeof(gfx_transform), (gfx->num_transforms + 1) * sizeof(gfx_transform)); - + gfx->transforms[gfx->num_transforms].type = SCALE; gfx->transforms[gfx->num_transforms].x = luaL_checknumber(L, 1); gfx->transforms[gfx->num_transforms].y = luaL_checknumber(L, 2); - + gfx->num_transforms++; return 0; } @@ -1133,20 +1154,20 @@ static int reset_transform(lua_State* L) { return 0; } #endif -static void add_path_segment(t_path_state* path, int x, int y) +static void add_path_segment(t_path_state* path, float x, float y) { int path_segment_space = (path->num_path_segments + 1) * 2; int old_size = path->num_path_segments_allocated; int new_size = MAX(path_segment_space, path->num_path_segments_allocated); if(!path->num_path_segments_allocated) { - path->path_segments = (int*)getbytes(new_size * sizeof(int)); + path->path_segments = (float*)getbytes(new_size * sizeof(float)); } else { - path->path_segments = (int*)resizebytes(path->path_segments, old_size * sizeof(int), new_size * sizeof(int)); + path->path_segments = (float*)resizebytes(path->path_segments, old_size * sizeof(float), new_size * sizeof(float)); } path->num_path_segments_allocated = new_size; - + path->path_segments[path->num_path_segments * 2] = x; path->path_segments[path->num_path_segments * 2 + 1] = y; path->num_path_segments++; @@ -1160,7 +1181,7 @@ static int start_path(lua_State* L) { path->num_path_segments_allocated = 0; path->path_start_x = luaL_checknumber(L, 1); path->path_start_y = luaL_checknumber(L, 2); - + add_path_segment(path, path->path_start_x, path->path_start_y); return 1; } @@ -1168,72 +1189,72 @@ static int start_path(lua_State* L) { // Function to add a line to the current path static int line_to(lua_State* L) { t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); - int x = luaL_checknumber(L, 2); - int y = luaL_checknumber(L, 3); + float x = luaL_checknumber(L, 2); + float y = luaL_checknumber(L, 3); add_path_segment(path, x, y); return 0; } static int quad_to(lua_State* L) { t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); - int x2 = luaL_checknumber(L, 2); - int y2 = luaL_checknumber(L, 3); - int x3 = luaL_checknumber(L, 4); - int y3 = luaL_checknumber(L, 5); - - int x1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2] : x2; - int y1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2 + 1] : y2; - + float x2 = luaL_checknumber(L, 2); + float y2 = luaL_checknumber(L, 3); + float x3 = luaL_checknumber(L, 4); + float y3 = luaL_checknumber(L, 5); + + float x1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2] : x2; + float y1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2 + 1] : y2; + // heuristic for deciding the number of lines in our bezier curve float dx = x3 - x1; float dy = y3 - y1; float distance = sqrtf(dx * dx + dy * dy); float resolution = MAX(10.0f, distance); - + // Get the last point float t = 0.0; while (t <= 1.0) { t += 1.0 / resolution; - + // Calculate quadratic bezier curve as points (source: https://en.wikipedia.org/wiki/B%C3%A9zier_curve) - int x = (1.0f - t) * (1.0f - t) * x1 + 2.0f * (1.0f - t) * t * x2 + t * t * x3; - int y = (1.0f - t) * (1.0f - t) * y1 + 2.0f * (1.0f - t) * t * y2 + t * t * y3; + float x = (1.0f - t) * (1.0f - t) * x1 + 2.0f * (1.0f - t) * t * x2 + t * t * x3; + float y = (1.0f - t) * (1.0f - t) * y1 + 2.0f * (1.0f - t) * t * y2 + t * t * y3; add_path_segment(path, x, y); } - + return 0; } static int cubic_to(lua_State* L) { t_path_state* path = (t_path_state*)luaL_checkudata(L, 1, "Path"); - int x2 = luaL_checknumber(L, 2); - int y2 = luaL_checknumber(L, 3); - int x3 = luaL_checknumber(L, 4); - int y3 = luaL_checknumber(L, 5); - int x4 = luaL_checknumber(L, 6); - int y4 = luaL_checknumber(L, 7); - - int x1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2] : x2; - int y1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2 + 1] : y2; - + float x2 = luaL_checknumber(L, 2); + float y2 = luaL_checknumber(L, 3); + float x3 = luaL_checknumber(L, 4); + float y3 = luaL_checknumber(L, 5); + float x4 = luaL_checknumber(L, 6); + float y4 = luaL_checknumber(L, 7); + + float x1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2] : x2; + float y1 = path->num_path_segments > 0 ? path->path_segments[(path->num_path_segments - 1) * 2 + 1] : y2; + // heuristic for deciding the number of lines in our bezier curve float dx = x3 - x1; float dy = y3 - y1; float distance = sqrtf(dx * dx + dy * dy); float resolution = MAX(10.0f, distance); - + // Get the last point float t = 0.0; while (t <= 1.0) { t += 1.0 / resolution; // Calculate cubic bezier curve as points (source: https://en.wikipedia.org/wiki/B%C3%A9zier_curve) - int x = (1 - t)*(1 - t)*(1 - t) * x1 + 3 * (1 - t)*(1 - t) * t * x2 + 3 * (1 - t) * t*t * x3 + t*t*t * x4; - int y = (1 - t)*(1 - t)*(1 - t) * y1 + 3 * (1 - t)*(1 - t) * t * y2 + 3 * (1 - t) * t*t * y3 + t*t*t * y4; - + float x = (1 - t)*(1 - t)*(1 - t) * x1 + 3 * (1 - t)*(1 - t) * t * x2 + 3 * (1 - t) * t*t * x3 + t*t*t * x4; + float y = (1 - t)*(1 - t)*(1 - t) * y1 + 3 * (1 - t)*(1 - t) * t * y2 + 3 * (1 - t) * t*t * y3 + t*t*t * y4; + add_path_segment(path, x, y); } - + return 0; }