From 0e2c0fff92ea0db5b781385ab85867b5d36979d0 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 03:25:52 +0100 Subject: [PATCH 01/62] Initial commit for pdlua graphics --- pd.lua | 35 ++++ pdlua.c | 88 +++++++- pdlua_gfx.h | 593 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 711 insertions(+), 5 deletions(-) create mode 100644 pdlua_gfx.h diff --git a/pd.lua b/pd.lua index 61ce0b0..3888eb6 100644 --- a/pd.lua +++ b/pd.lua @@ -77,6 +77,33 @@ pd._dispatcher = function (object, inlet, sel, atoms) end end +-- repaint method dispatcher +pd._repaint = function (object) + local obj = pd._objects[object] + if nil ~= obj then + obj:repaint(); + end +end + +-- mouse event dispatcher +pd._mouseevent = function (object, x, y, event_type) + if nil ~= pd._objects[object] then + local obj = pd._objects[object] + if event_type == 0 and type(obj.mouse_down) == "function" then + obj:mouse_down(x, y) + end + if event_type == 1 and type(obj.mouse_up) == "function" then + obj:mouse_up(x, y) + end + if event_type == 2 and type(obj.mouse_move) == "function" then + obj:mouse_move(x, y) + end + if event_type == 3 and type(obj.mouse_drag) == "function" then + obj:mouse_drag(x, y) + end + end +end + -- clock method dispatcher pd._clockdispatch = function (c) if nil ~= pd._clocks[c] then @@ -336,6 +363,14 @@ end function pd.Class:initialize(sel, atoms) end +function pd.Class:repaint() + if type(self.paint) == "function" then + gfx._start_paint(); + self:paint(); + gfx._end_paint(); + end +end + function pd.Class:postinitialize() end function pd.Class:finalize() end diff --git a/pdlua.c b/pdlua.c index 802286d..886d3f3 100644 --- a/pdlua.c +++ b/pdlua.c @@ -52,6 +52,8 @@ #include "m_imp.h" // for struct _class /* BAD: support for Pd < 0.41 */ +#include "pdlua_gfx.h" + #if PD_MAJOR_VERSION == 0 # if PD_MINOR_VERSION >= 41 # define PDLUA_PD41 @@ -126,6 +128,7 @@ typedef struct pdlua int outlets; /**< Number of outlets. */ t_outlet **out; /**< The outlets themselves. */ t_canvas *canvas; /**< The canvas that the object was created on. */ + t_pdlua_gfx gfx; /**< Holds state for graphics. */ } t_pdlua; /** Proxy inlet object data. */ @@ -249,7 +252,7 @@ static int pdlua_loader_legacy (t_canvas *canvas, char *name); __declspec(dllexport) #endif #ifdef PLUGDATA -void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length); +void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void* callback_target, void(*register_gui_callback)(void*, t_object*)); #else void pdlua_setup (void); #endif @@ -467,6 +470,7 @@ static t_pdlua *pdlua_new return NULL; } } + PDLUA_DEBUG("pdlua_new: start with stack top %d", lua_gettop(__L)); lua_getglobal(__L, "pd"); lua_getfield(__L, -1, "_checkbase"); @@ -541,6 +545,7 @@ static t_pdlua *pdlua_new } else pd_error(NULL, "lua: error loading `%s': canvas_open() failed", buf); } + PDLUA_DEBUG("pdlua_new: after load script. stack top %d", lua_gettop(__L)); lua_getfield(__L, -1, "_constructor"); lua_pushstring(__L, s->s_name); @@ -589,6 +594,23 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */) return; } +static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_pdlua *x = (t_pdlua *)z; + float x1 = text_xpix((t_text *)x, glist), y1 = text_ypix((t_text *)x, glist); + + if(x->gfx.has_gui) { + *xp1 = x1; + *yp1 = y1; + *xp2 = x1 + x->gfx.width; + *yp2 = y1 + x->gfx.height; + } + else { + text_widgetbehavior.w_getrectfn(x, glist, xp1, yp1, xp2, yp2); + } +} + + #if 0 static void pdlua_stack_dump (lua_State *L) { @@ -690,6 +712,8 @@ static void pdlua_menu_open(t_pdlua *o) PDLUA_DEBUG("pdlua_menu_open end. stack top is %d", lua_gettop(__L)); } +t_widgetbehavior pdlua_widgetbehavior; + /** Lua class registration. This is equivalent to the "setup" method for an ordinary Pd class */ static int pdlua_class_new(lua_State *L) /**< Lua interpreter state. @@ -707,6 +731,15 @@ static int pdlua_class_new(lua_State *L) c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET, A_GIMME, 0); + pdlua_widgetbehavior.w_getrectfn = pdlua_getrect; + pdlua_widgetbehavior.w_displacefn = text_widgetbehavior.w_displacefn; + pdlua_widgetbehavior.w_selectfn = text_widgetbehavior.w_selectfn;; + pdlua_widgetbehavior.w_deletefn = text_widgetbehavior.w_deletefn; + pdlua_widgetbehavior.w_clickfn = text_widgetbehavior.w_clickfn; + pdlua_widgetbehavior.w_visfn = text_widgetbehavior.w_visfn; + pdlua_widgetbehavior.w_activatefn = NULL; + class_setwidget(c, &pdlua_widgetbehavior); + /* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ if (c) class_addmethod(c, (t_method)pdlua_menu_open, gensym("menu-open"), A_NULL);/* (mrpeach 20111025) */ @@ -730,17 +763,31 @@ static int pdlua_object_new(lua_State *L) if (lua_islightuserdata(L, 1)) { t_class *c = lua_touserdata(L, 1); - if (c) + if(c) { PDLUA_DEBUG("pdlua_object_new: path is %s", c->c_externdir->s_name); t_pdlua *o = (t_pdlua *) pd_new(c); if (o) { + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L, o); + lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L, 1); + o->inlets = 0; o->in = NULL; o->outlets = 0; o->out = NULL; o->canvas = canvas_getcurrent(); + + o->gfx.width = 80; + o->gfx.height = 80; + o->gfx.has_gui = 0; +#if !PLUGDATA + o->gfx.num_tags = 0; +#endif + lua_pushlightuserdata(L, o); PDLUA_DEBUG("pdlua_object_new: success end. stack top is %d", lua_gettop(L)); return 1; @@ -1030,12 +1077,22 @@ static void pdlua_dispatch lua_pushnumber(__L, inlet + 1); /* C has 0.., Lua has 1.. */ lua_pushstring(__L, s->s_name); pdlua_pushatomtable(argc, argv); + + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L, o); // Use a unique key for your userdata + lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); // Store the userdata in the registry + lua_pop(__L, 1); // Pop the registry table from the stack + if (lua_pcall(__L, 4, 0, 0)) { pd_error(o, "lua: error in dispatcher:\n%s", lua_tostring(__L, -1)); lua_pop(__L, 1); /* pop the error string */ } lua_pop(__L, 1); /* pop the global "pd" */ + + + PDLUA_DEBUG("pdlua_dispatch: end. stack top %d", lua_gettop(__L)); return; } @@ -1454,8 +1511,6 @@ static int pdlua_error(lua_State *L) * */ { t_pdlua *o; - - const char *s; PDLUA_DEBUG("pdlua_error: stack top is %d", lua_gettop(L)); @@ -1862,6 +1917,22 @@ static int pdlua_loader_pathwise } + +lua_State* get_lua_state() +{ + return __L; +} + +t_canvas* get_pdlua_canvas(t_object* x) +{ + return ((t_pdlua*)x)->canvas; +} + +t_pdlua_gfx* get_pdlua_gfx_state(t_object* x) +{ + return &((t_pdlua*)x)->gfx; +} + #ifdef WIN32 #include #else @@ -1882,7 +1953,7 @@ static int pdlua_loader_pathwise __declspec(dllexport) #endif #ifdef PLUGDATA -void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length) +void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void* callback_target, void(*register_gui_callback)(void*, t_object*)) #else void pdlua_setup(void) #endif @@ -2022,6 +2093,13 @@ void pdlua_setup(void) pd_error(NULL, "lua: error loading `pd.lua': canvas_open() failed"); pd_error(NULL, "lua: loader will not be registered!"); } + + initialize_graphics(__L); + +#if PLUGDATA + set_gui_callback(callback_target, register_gui_callback); +#endif + PDLUA_DEBUG("pdlua_setup: end. stack top %d", lua_gettop(__L)); #ifndef PLUGDATA /* nw.js support. */ diff --git a/pdlua_gfx.h b/pdlua_gfx.h new file mode 100644 index 0000000..c42566d --- /dev/null +++ b/pdlua_gfx.h @@ -0,0 +1,593 @@ +#include + +typedef struct _pdlua_gfx +{ +#if !PLUGDATA + char active_tags[128][128]; + int num_tags; + int current_red, current_green, current_blue; +#endif + + int has_gui; + int width, height; +} t_pdlua_gfx; + +static int gfx_initialize(lua_State* L); +static int start_paint(lua_State* L); +static int end_paint(lua_State* L); + +static int set_color(lua_State* L); + +static int fill_ellipse(lua_State* L); +static int stroke_ellipse(lua_State* L); +static int fill_all(lua_State* L); +static int fill_rect(lua_State* L); +static int stroke_rect(lua_State* L); +static int fill_rounded_rect(lua_State* L); +static int stroke_rounded_rect(lua_State* L); + +static int draw_text(lua_State* L); + +static int start_path(lua_State* L); +static int line_to(lua_State* L); +static int quad_to(lua_State* L); +static int stroke_path(lua_State* L); +static int fill_path(lua_State* L); + +static int translate(lua_State* L); +static int scale(lua_State* L); +static int save(lua_State* L); +static int restore(lua_State* L); + +lua_State* get_lua_state(); +t_canvas* get_pdlua_canvas(t_object* object); +t_pdlua_gfx* get_pdlua_gfx_state(t_object* object); + +void pdlua_gfx_repaint(t_object* o) { + lua_State* __L = get_lua_state(); + 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" */ +} + +void pdlua_gfx_mouse_event(t_object* o, int x, int y, int type) { + lua_State* __L = get_lua_state(); + 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" */ +} + +void pdlua_gfx_mouse_down(t_object* o, int x, int y) { + pdlua_gfx_mouse_event(o, x, y, 0); +} +void pdlua_gfx_mouse_up(t_object* o, int x, int y) { + pdlua_gfx_mouse_event(o, x, y, 1); +} + +void pdlua_gfx_mouse_move(t_object* o, int x, int y) { + pdlua_gfx_mouse_event(o, x, y, 2); +} + +void pdlua_gfx_mouse_drag(t_object* o, int x, int y) { + pdlua_gfx_mouse_event(o, x, y, 3); +} + +#define PDLUA_OBJECT_REGISTRTY_ID 512 + +static t_object* get_current_object(lua_State* L) +{ + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_geti(L, -1, PDLUA_OBJECT_REGISTRTY_ID); + + // Check if the value is a light userdata + if (lua_islightuserdata(L, -1)) { + // Retrieve the userdata pointer + return lua_touserdata(L, -1); + } + + return NULL; +} + +// Register C++ functions with Lua +static const luaL_Reg gfx[] = { + {"set_color", set_color}, + {"fill_ellipse", fill_ellipse}, + {"stroke_ellipse", stroke_ellipse}, + {"fill_rect", fill_rect}, + {"stroke_rect", stroke_rect}, + {"fill_rounded_rect", fill_rounded_rect}, + {"stroke_rounded_rect", stroke_rounded_rect}, + {"text", draw_text}, + {"start_path", start_path}, + {"line_to", line_to}, + {"quad_to", quad_to}, + {"stroke_path", stroke_path}, + {"fill_path", fill_path}, + {"fill_all", fill_all}, + {"translate", translate}, + {"scale", scale}, + {"save", save}, + {"restore", restore}, + {"_start_paint", start_paint}, + {"_end_paint", end_paint}, + {"initialize", gfx_initialize }, + {NULL, NULL} // Sentinel to end the list +}; + + +void* gui_target; +void(*register_gui)(void*, t_object*); + +int set_gui_callback(void* callback_target, void(*callback)(void*, t_object*)) { + gui_target = callback_target; + register_gui = callback; + return 0; +} + +int initialize_graphics(lua_State* L) { + // Register functions with Lua + luaL_newlib(L, gfx); + lua_setglobal(L, "gfx"); + return 1; // Number of values pushed onto the stack +} + +#if PLUGDATA +void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); + +static int gfx_initialize(lua_State* L) +{ + t_object* obj = get_current_object(L); + register_gui(gui_target, obj); + + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + gfx->has_gui = 1; + + return 0; +} + +static int start_paint(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_start_paint"), 0, NULL); + return 0; +} + +static int end_paint(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_end_paint"), 0, NULL); + return 0; +} + +static int set_color(lua_State* L) { + t_object* obj = get_current_object(L); + 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 + SETFLOAT(args + 3, luaL_optnumber(L, 4, 1.0)); // a (optional, default to 1.0) + plugdata_forward_message(obj, gensym("lua_set_color"), 4, args); + return 0; +} + +static int fill_ellipse(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[4]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + plugdata_forward_message(obj, gensym("lua_fill_ellipse"), 4, args); + return 0; +} + +static int stroke_ellipse(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[4]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // width + plugdata_forward_message(obj, gensym("lua_stroke_ellipse"), 4, args); + return 0; +} + +static int fill_all(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_fill_all"), 0, NULL); + return 0; +} + +static int fill_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[4]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + plugdata_forward_message(obj, gensym("lua_fill_rect"), 4, args); + return 0; +} + +static int stroke_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[5]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // corner_radius + plugdata_forward_message(obj, gensym("lua_stroke_rect"), 5, args); + return 0; +} + +static int fill_rounded_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[4]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + plugdata_forward_message(obj, gensym("lua_fill_rounded_rect"), 4, args); + return 0; +} + +static int stroke_rounded_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[6]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // corner_radius + SETFLOAT(args + 5, luaL_checknumber(L, 6)); // width + plugdata_forward_message(obj, gensym("lua_stroke_rounded_rect"), 6, args); + return 0; +} + +static int draw_text(lua_State* L) { + t_object* obj = get_current_object(L); + const char* text = luaL_checkstring(L, 1); // Assuming text is a string + t_atom args[5]; + SETSYMBOL(args, gensym(text)); + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // x + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // w + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // h + plugdata_forward_message(obj, gensym("lua_text"), 5, args); + return 0; +} + +static int start_path(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_start_path"), 0, NULL); + return 0; +} + +static int line_to(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[2]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + plugdata_forward_message(obj, gensym("lua_line_to"), 2, args); + return 0; +} + +static int quad_to(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[4]; + SETFLOAT(args, luaL_checknumber(L, 1)); // bx + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // by + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // cx + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // cy + plugdata_forward_message(obj, gensym("lua_quad_to"), 4, args); + return 0; +} + +static int stroke_path(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_stroke_path"), 0, NULL); + return 0; +} + +static int fill_path(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_fill_path"), 0, NULL); + return 0; +} + +static int translate(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[2]; + SETFLOAT(args, luaL_checknumber(L, 1)); // tx + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // ty + plugdata_forward_message(obj, gensym("lua_translate"), 2, args); + return 0; +} + +static int scale(lua_State* L) { + t_object* obj = get_current_object(L); + t_atom args[2]; + SETFLOAT(args, luaL_checknumber(L, 1)); // sx + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // sy + plugdata_forward_message(obj, gensym("lua_scale"), 2, args); + return 0; +} + +static int save(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_save"), 0, NULL); + return 0; +} + +static int restore(lua_State* L) { + t_object* obj = get_current_object(L); + plugdata_forward_message(obj, gensym("lua_restore"), 0, NULL); + return 0; +} +#else + +static const char* register_drawing(t_object* object) +{ + t_pdlua_gfx* gfx = get_pdlua_gfx_state(object); + snprintf(gfx->active_tags[gfx->num_tags], 128, ".x%lx%lx", object, rand()); + gfx->active_tags[gfx->num_tags][127] = '\0'; + + return gfx->active_tags[gfx->num_tags++]; +} + +static int gfx_initialize(lua_State* L) +{ + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + gfx->has_gui = 1; + return 0; +} + +static int start_paint(lua_State* L) { + t_object* obj = get_current_object(L); + t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + + // Delete drawings made previously + for(int i = 0; i < gfx->num_tags; i++) + { + pdgui_vmess(0, "crs", cnv, "delete", gfx->active_tags[i]); + } + gfx->num_tags = 0; + return 0; +} + +static int end_paint(lua_State* L) { + return 0; +} + +static int set_color(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + + gfx->current_red = luaL_checknumber(L, 1); + gfx->current_green = luaL_checknumber(L, 2); + gfx->current_blue = luaL_checknumber(L, 3); + return 0; +} + + +static int fill_ellipse(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); + int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); + int x2 = x1 + luaL_checknumber(L, 3); + int y2 = y1 + luaL_checknumber(L, 4); + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); + return 0; +} + +static int stroke_ellipse(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); + int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); + int x2 = x1 + luaL_checknumber(L, 3); + int y2 = y1 + luaL_checknumber(L, 4); + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-outline", hex_color, "-tags", 1, tags); + return 0; +} + +static int fill_all(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv); + int y1 = text_ypix(obj, cnv); + int x2 = x1 + gfx->width; + int y2 = y1 + gfx->height; + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; // Assuming #RRGGBB format + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); + return 0; +} + +static int fill_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); + int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); + int x2 = x1 + luaL_checknumber(L, 3); + int y2 = y1 + luaL_checknumber(L, 4); + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; // Assuming #RRGGBB format + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); + return 0; +} + +static int stroke_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); + int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); + int x2 = x1 + luaL_checknumber(L, 3); + int y2 = y1 + luaL_checknumber(L, 4); + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; // Assuming #RRGGBB format + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", hex_color, "-tags", 1, tags); + return 0; +} + +static int fill_rounded_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); + int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); + int x2 = x1 + luaL_checknumber(L, 3); + int y2 = y1 + luaL_checknumber(L, 4); + int radius = luaL_checknumber(L, 5); // Radius for rounded corners + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; // Assuming #RRGGBB format + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + // Draw the main body of the rounded rectangle + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); + + // Draw rounded corners using arcs + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y1, x1+2*radius, y1+2*radius, "-start", 90, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y1, x2, y1+2*radius, "-start", 0, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y2-2*radius, x1+2*radius, y2, "-start", 180, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y2-2*radius, x2, y2, "-start", 270, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); + return 0; +} + +static int stroke_rounded_rect(lua_State* L) { + t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); + int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); + int x2 = x1 + luaL_checknumber(L, 3); + int y2 = y1 + luaL_checknumber(L, 4); + int radius = luaL_checknumber(L, 5); // Radius for rounded corners + + const char* tags[] = { register_drawing(obj) }; + + char hex_color[8]; // Assuming #RRGGBB format + snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + + // Draw the main body of the rounded rectangle with an outline + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", hex_color, "-width", 1, "-tags", 1, tags); + + // Draw rounded corners using arcs + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y1, x1+2*radius, y1+2*radius, "-start", 90, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y1, x2, y1+2*radius, "-start", 0, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y2-2*radius, x1+2*radius, y2, "-start", 180, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y2-2*radius, x2, y2, "-start", 270, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); + return 0; +} + +static int draw_text(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int start_path(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int line_to(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int quad_to(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int stroke_path(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int fill_path(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int translate(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int scale(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int save(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +static int restore(lua_State* L) { + t_object* obj = get_current_object(L); + return 0; +} + +#endif From 1a5d958bf423499d215042a1ca396bcb9b5d74d8 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 03:51:16 +0100 Subject: [PATCH 02/62] Small cleanup --- pdlua_gfx.h | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index c42566d..381990c 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -98,16 +98,14 @@ static t_object* get_current_object(lua_State* L) lua_pushvalue(L, LUA_REGISTRYINDEX); lua_geti(L, -1, PDLUA_OBJECT_REGISTRTY_ID); - // Check if the value is a light userdata if (lua_islightuserdata(L, -1)) { - // Retrieve the userdata pointer return lua_touserdata(L, -1); } return NULL; } -// Register C++ functions with Lua +// Register functions with Lua static const luaL_Reg gfx[] = { {"set_color", set_color}, {"fill_ellipse", fill_ellipse}, @@ -390,7 +388,6 @@ static int set_color(lua_State* L) { return 0; } - static int fill_ellipse(lua_State* L) { t_object* obj = get_current_object(L); t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); @@ -441,7 +438,7 @@ static int fill_all(lua_State* L) { const char* tags[] = { register_drawing(obj) }; - char hex_color[8]; // Assuming #RRGGBB format + char hex_color[8]; snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); @@ -460,7 +457,7 @@ static int fill_rect(lua_State* L) { const char* tags[] = { register_drawing(obj) }; - char hex_color[8]; // Assuming #RRGGBB format + char hex_color[8]; snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); @@ -479,7 +476,7 @@ static int stroke_rect(lua_State* L) { const char* tags[] = { register_drawing(obj) }; - char hex_color[8]; // Assuming #RRGGBB format + char hex_color[8]; snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", hex_color, "-tags", 1, tags); @@ -499,7 +496,7 @@ static int fill_rounded_rect(lua_State* L) { const char* tags[] = { register_drawing(obj) }; - char hex_color[8]; // Assuming #RRGGBB format + char hex_color[8]; snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); // Draw the main body of the rounded rectangle @@ -526,7 +523,7 @@ static int stroke_rounded_rect(lua_State* L) { const char* tags[] = { register_drawing(obj) }; - char hex_color[8]; // Assuming #RRGGBB format + char hex_color[8]; snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); // Draw the main body of the rounded rectangle with an outline From 9967a965a75457a9b51fb3df8137e3646fa1c3c2 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 04:51:48 +0100 Subject: [PATCH 03/62] Implement mouse interaction for pd-vanilla, also clip bounds before drawing --- pd.lua | 35 -------------------------- pdlua.c | 41 +++++++++++++++++++++++++++++- pdlua_gfx.h | 72 +++++++++++++++++++++++++++++++++++------------------ 3 files changed, 88 insertions(+), 60 deletions(-) diff --git a/pd.lua b/pd.lua index 3888eb6..61ce0b0 100644 --- a/pd.lua +++ b/pd.lua @@ -77,33 +77,6 @@ pd._dispatcher = function (object, inlet, sel, atoms) end end --- repaint method dispatcher -pd._repaint = function (object) - local obj = pd._objects[object] - if nil ~= obj then - obj:repaint(); - end -end - --- mouse event dispatcher -pd._mouseevent = function (object, x, y, event_type) - if nil ~= pd._objects[object] then - local obj = pd._objects[object] - if event_type == 0 and type(obj.mouse_down) == "function" then - obj:mouse_down(x, y) - end - if event_type == 1 and type(obj.mouse_up) == "function" then - obj:mouse_up(x, y) - end - if event_type == 2 and type(obj.mouse_move) == "function" then - obj:mouse_move(x, y) - end - if event_type == 3 and type(obj.mouse_drag) == "function" then - obj:mouse_drag(x, y) - end - end -end - -- clock method dispatcher pd._clockdispatch = function (c) if nil ~= pd._clocks[c] then @@ -363,14 +336,6 @@ end function pd.Class:initialize(sel, atoms) end -function pd.Class:repaint() - if type(self.paint) == "function" then - gfx._start_paint(); - self:paint(); - gfx._end_paint(); - end -end - function pd.Class:postinitialize() end function pd.Class:finalize() end diff --git a/pdlua.c b/pdlua.c index 886d3f3..9cea283 100644 --- a/pdlua.c +++ b/pdlua.c @@ -594,6 +594,45 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */) return; } + +static void pdlua_key(void *z, t_symbol *keysym, t_floatarg fkey){ + +} + +static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, + t_floatarg up) +{ + t_pdlua *x = (t_pdlua *)z; + x->gfx.mouse_x = x->gfx.mouse_x + dx; + x->gfx.mouse_y = x->gfx.mouse_y + dy; + + if (up) + { + if(!x->gfx.mouse_up) + { + pdlua_gfx_mouse_up((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); + } + pdlua_gfx_mouse_move((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); + } + else { + if(x->gfx.mouse_up) + { + pdlua_gfx_mouse_down((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); + } + pdlua_gfx_mouse_drag((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); + } + + x->gfx.mouse_up = up; +} + +static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){ + alt = dbl = 0; // remove warning + if(doit){ + glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, (t_glistkeyfn)pdlua_key, xpos, ypos); + } + return(1); +} + static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_pdlua *x = (t_pdlua *)z; @@ -735,7 +774,7 @@ static int pdlua_class_new(lua_State *L) pdlua_widgetbehavior.w_displacefn = text_widgetbehavior.w_displacefn; pdlua_widgetbehavior.w_selectfn = text_widgetbehavior.w_selectfn;; pdlua_widgetbehavior.w_deletefn = text_widgetbehavior.w_deletefn; - pdlua_widgetbehavior.w_clickfn = text_widgetbehavior.w_clickfn; + pdlua_widgetbehavior.w_clickfn = pdlua_click; pdlua_widgetbehavior.w_visfn = text_widgetbehavior.w_visfn; pdlua_widgetbehavior.w_activatefn = NULL; class_setwidget(c, &pdlua_widgetbehavior); diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 381990c..254c999 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -6,6 +6,7 @@ typedef struct _pdlua_gfx char active_tags[128][128]; int num_tags; int current_red, current_green, current_blue; + int mouse_x, mouse_y, mouse_up; #endif int has_gui; @@ -343,6 +344,38 @@ static int restore(lua_State* L) { } #else +static int max(int a, int b) { + return (a > b) ? a : b; +} + +static int min(int a, int b) { + return (a < b) ? a : b; +} + +static void get_bounds_args(lua_State* L, t_object* obj, t_pdlua_gfx* gfx, int* x1, int* y1, int* x2, int* y2) { + t_canvas* cnv = get_pdlua_canvas(obj); + + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + + // Calculate the clipped rectangle + x = min(max(x, 0), gfx->width - w); + y = min(max(y, 0), gfx->height - h); + w = min(x + w, gfx->width) - x; + h = min(y + h, gfx->height) - y; + + x += text_xpix(obj, cnv); + y += text_ypix(obj, cnv); + + *x1 = x; + *y1 = y; + *x2 = x + w; + *y2 = y + h; +} + + static const char* register_drawing(t_object* object) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(object); @@ -352,6 +385,7 @@ static const char* register_drawing(t_object* object) return gfx->active_tags[gfx->num_tags++]; } + static int gfx_initialize(lua_State* L) { t_object* obj = get_current_object(L); @@ -393,10 +427,8 @@ static int fill_ellipse(lua_State* L) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); t_canvas* cnv = get_pdlua_canvas(obj); - int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); - int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); - int x2 = x1 + luaL_checknumber(L, 3); - int y2 = y1 + luaL_checknumber(L, 4); + int x1, y1, x2, y2; + get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); const char* tags[] = { register_drawing(obj) }; @@ -412,10 +444,8 @@ static int stroke_ellipse(lua_State* L) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); t_canvas* cnv = get_pdlua_canvas(obj); - int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); - int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); - int x2 = x1 + luaL_checknumber(L, 3); - int y2 = y1 + luaL_checknumber(L, 4); + int x1, y1, x2, y2; + get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); const char* tags[] = { register_drawing(obj) }; @@ -450,10 +480,8 @@ static int fill_rect(lua_State* L) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); t_canvas* cnv = get_pdlua_canvas(obj); - int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); - int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); - int x2 = x1 + luaL_checknumber(L, 3); - int y2 = y1 + luaL_checknumber(L, 4); + int x1, y1, x2, y2; + get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); const char* tags[] = { register_drawing(obj) }; @@ -469,10 +497,8 @@ static int stroke_rect(lua_State* L) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); t_canvas* cnv = get_pdlua_canvas(obj); - int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); - int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); - int x2 = x1 + luaL_checknumber(L, 3); - int y2 = y1 + luaL_checknumber(L, 4); + int x1, y1, x2, y2; + get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); const char* tags[] = { register_drawing(obj) }; @@ -488,10 +514,9 @@ static int fill_rounded_rect(lua_State* L) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); t_canvas* cnv = get_pdlua_canvas(obj); - int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); - int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); - int x2 = x1 + luaL_checknumber(L, 3); - int y2 = y1 + luaL_checknumber(L, 4); + int x1, y1, x2, y2; + get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); + int radius = luaL_checknumber(L, 5); // Radius for rounded corners const char* tags[] = { register_drawing(obj) }; @@ -515,10 +540,9 @@ static int stroke_rounded_rect(lua_State* L) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); t_canvas* cnv = get_pdlua_canvas(obj); - int x1 = text_xpix(obj, cnv) + luaL_checknumber(L, 1); - int y1 = text_ypix(obj, cnv) + luaL_checknumber(L, 2); - int x2 = x1 + luaL_checknumber(L, 3); - int y2 = y1 + luaL_checknumber(L, 4); + int x1, y1, x2, y2; + get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); + int radius = luaL_checknumber(L, 5); // Radius for rounded corners const char* tags[] = { register_drawing(obj) }; From bc62c06cf5de2e059c276c6089a727be41e199d4 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 04:52:54 +0100 Subject: [PATCH 04/62] Revert mistake --- pd.lua | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pd.lua b/pd.lua index 61ce0b0..3888eb6 100644 --- a/pd.lua +++ b/pd.lua @@ -77,6 +77,33 @@ pd._dispatcher = function (object, inlet, sel, atoms) end end +-- repaint method dispatcher +pd._repaint = function (object) + local obj = pd._objects[object] + if nil ~= obj then + obj:repaint(); + end +end + +-- mouse event dispatcher +pd._mouseevent = function (object, x, y, event_type) + if nil ~= pd._objects[object] then + local obj = pd._objects[object] + if event_type == 0 and type(obj.mouse_down) == "function" then + obj:mouse_down(x, y) + end + if event_type == 1 and type(obj.mouse_up) == "function" then + obj:mouse_up(x, y) + end + if event_type == 2 and type(obj.mouse_move) == "function" then + obj:mouse_move(x, y) + end + if event_type == 3 and type(obj.mouse_drag) == "function" then + obj:mouse_drag(x, y) + end + end +end + -- clock method dispatcher pd._clockdispatch = function (c) if nil ~= pd._clocks[c] then @@ -336,6 +363,14 @@ end function pd.Class:initialize(sel, atoms) end +function pd.Class:repaint() + if type(self.paint) == "function" then + gfx._start_paint(); + self:paint(); + gfx._end_paint(); + end +end + function pd.Class:postinitialize() end function pd.Class:finalize() end From d69208fd4a480efbe20de7b20a67a96443508219 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 19:08:17 +0100 Subject: [PATCH 05/62] Better way to enable GUI mode, improved pd-vanilla support, improved drawing API --- pd.lua | 2 + pdlua.c | 58 ++++++++- pdlua_gfx.h | 360 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 313 insertions(+), 107 deletions(-) diff --git a/pd.lua b/pd.lua index 3888eb6..3f794e1 100644 --- a/pd.lua +++ b/pd.lua @@ -311,6 +311,7 @@ function pd.Class:construct(sel, atoms) if self:initialize(sel, atoms) then pd._createinlets(self._object, self.inlets) pd._createoutlets(self._object, self.outlets) + pd._creategui(self._object, self.gui) self:postinitialize() return self else @@ -419,6 +420,7 @@ local lua = pd.Class:new():register("pdlua") -- global controls (the [pdlua] ob function lua:initialize(sel, atoms) self.inlets = 1 + self.gui = 0 self.outlets = 0 -- FIXME: might be nice to have errors go here? return true end diff --git a/pdlua.c b/pdlua.c index 9cea283..e65b624 100644 --- a/pdlua.c +++ b/pdlua.c @@ -128,6 +128,7 @@ typedef struct pdlua int outlets; /**< Number of outlets. */ t_outlet **out; /**< The outlets themselves. */ t_canvas *canvas; /**< The canvas that the object was created on. */ + int has_gui; /**< True if graphics are enabled. */ t_pdlua_gfx gfx; /**< Holds state for graphics. */ } t_pdlua; @@ -599,20 +600,23 @@ static void pdlua_key(void *z, t_symbol *keysym, t_floatarg fkey){ } + static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { +#if !PLUGDATA t_pdlua *x = (t_pdlua *)z; x->gfx.mouse_x = x->gfx.mouse_x + dx; x->gfx.mouse_y = x->gfx.mouse_y + dy; if (up) { + pdlua_gfx_mouse_move((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); + if(!x->gfx.mouse_up) { pdlua_gfx_mouse_up((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); } - pdlua_gfx_mouse_move((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); } else { if(x->gfx.mouse_up) @@ -623,6 +627,7 @@ static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, } x->gfx.mouse_up = up; +#endif } static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){ @@ -633,12 +638,20 @@ static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, i return(1); } +static void pdlua_displace(t_pdlua *z, t_glist *glist, int dx, int dy){ + t_pdlua *x = (t_pdlua *)z; + x->pd.te_xpix += dx, x->pd.te_ypix += dy; + dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); + pdlua_gfx_repaint(z); + canvas_fixlinesfor(glist, (t_text*)x); +} + + static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_pdlua *x = (t_pdlua *)z; - float x1 = text_xpix((t_text *)x, glist), y1 = text_ypix((t_text *)x, glist); - - if(x->gfx.has_gui) { + if(x->has_gui) { + float x1 = text_xpix((t_text *)x, glist), y1 = text_ypix((t_text *)x, glist); *xp1 = x1; *yp1 = y1; *xp2 = x1 + x->gfx.width; @@ -649,6 +662,16 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp } } +void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ + if(vis) + { + pdlua_gfx_repaint((t_object*)z); + } + else { + pdlua_gfx_clear((t_object*)z); + } +} + #if 0 static void pdlua_stack_dump (lua_State *L) @@ -751,6 +774,7 @@ static void pdlua_menu_open(t_pdlua *o) PDLUA_DEBUG("pdlua_menu_open end. stack top is %d", lua_gettop(__L)); } + t_widgetbehavior pdlua_widgetbehavior; /** Lua class registration. This is equivalent to the "setup" method for an ordinary Pd class */ @@ -771,11 +795,11 @@ static int pdlua_class_new(lua_State *L) (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET, A_GIMME, 0); pdlua_widgetbehavior.w_getrectfn = pdlua_getrect; - pdlua_widgetbehavior.w_displacefn = text_widgetbehavior.w_displacefn; + pdlua_widgetbehavior.w_displacefn = pdlua_displace; pdlua_widgetbehavior.w_selectfn = text_widgetbehavior.w_selectfn;; pdlua_widgetbehavior.w_deletefn = text_widgetbehavior.w_deletefn; pdlua_widgetbehavior.w_clickfn = pdlua_click; - pdlua_widgetbehavior.w_visfn = text_widgetbehavior.w_visfn; + pdlua_widgetbehavior.w_visfn = pdlua_vis; pdlua_widgetbehavior.w_activatefn = NULL; class_setwidget(c, &pdlua_widgetbehavior); @@ -822,9 +846,16 @@ static int pdlua_object_new(lua_State *L) o->gfx.width = 80; o->gfx.height = 80; - o->gfx.has_gui = 0; + #if !PLUGDATA o->gfx.num_tags = 0; + o->gfx.num_path_segments = 0; + o->gfx.scale_x = 1.0f; + o->gfx.scale_y = 1.0f; + o->gfx.translate_x = 0; + o->gfx.translate_y = 0; + o->gfx.mouse_x = 0; + o->gfx.mouse_y = 0; #endif lua_pushlightuserdata(L, o); @@ -837,6 +868,16 @@ static int pdlua_object_new(lua_State *L) return 0; } +static int pdlua_object_creategui(lua_State *L) +{ + t_pdlua *o = lua_touserdata(L, 1); + o->has_gui = luaL_checknumber(L, 2); + if(o->has_gui) + { + gfx_initialize(o); + } +} + /** Lua object inlet creation. */ static int pdlua_object_createinlets(lua_State *L) /**< Lua interpreter state. @@ -1775,6 +1816,9 @@ static void pdlua_init(lua_State *L) lua_pushstring(L, "_createoutlets"); lua_pushcfunction(L, pdlua_object_createoutlets); lua_settable(L, -3); + lua_pushstring(L, "_creategui"); + lua_pushcfunction(L, pdlua_object_creategui); + lua_settable(L, -3); lua_pushstring(L, "_canvaspath"); lua_pushcfunction(L, pdlua_object_canvaspath); lua_settable(L, -3); diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 254c999..13f4707 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -3,17 +3,24 @@ typedef struct _pdlua_gfx { #if !PLUGDATA + char object_tag[128]; char active_tags[128][128]; int num_tags; - int current_red, current_green, current_blue; + + int path_segments[4096][2]; + int num_path_segments; + int path_start_x, path_start_y; + int path_smooth; + int mouse_x, mouse_y, mouse_up; + int translate_x, translate_y; + float scale_x, scale_y; + char current_color[8]; #endif - - int has_gui; int width, height; } t_pdlua_gfx; -static int gfx_initialize(lua_State* L); +static int gfx_initialize(t_object* obj); static int start_paint(lua_State* L); static int end_paint(lua_State* L); @@ -31,14 +38,15 @@ static int draw_text(lua_State* L); static int start_path(lua_State* L); static int line_to(lua_State* L); -static int quad_to(lua_State* L); +static int close_path(lua_State* L); static int stroke_path(lua_State* L); static int fill_path(lua_State* L); static int translate(lua_State* L); static int scale(lua_State* L); -static int save(lua_State* L); -static int restore(lua_State* L); +static int reset_transform(lua_State* L); + +void pdlua_gfx_clear(t_object* obj); lua_State* get_lua_state(); t_canvas* get_pdlua_canvas(t_object* object); @@ -107,7 +115,7 @@ static t_object* get_current_object(lua_State* L) } // Register functions with Lua -static const luaL_Reg gfx[] = { +static const luaL_Reg gfx_lib[] = { {"set_color", set_color}, {"fill_ellipse", fill_ellipse}, {"stroke_ellipse", stroke_ellipse}, @@ -118,17 +126,15 @@ static const luaL_Reg gfx[] = { {"text", draw_text}, {"start_path", start_path}, {"line_to", line_to}, - {"quad_to", quad_to}, + {"close_path", close_path}, {"stroke_path", stroke_path}, {"fill_path", fill_path}, {"fill_all", fill_all}, {"translate", translate}, {"scale", scale}, - {"save", save}, - {"restore", restore}, + {"reset_transform", reset_transform}, {"_start_paint", start_paint}, {"_end_paint", end_paint}, - {"initialize", gfx_initialize }, {NULL, NULL} // Sentinel to end the list }; @@ -144,7 +150,7 @@ int set_gui_callback(void* callback_target, void(*callback)(void*, t_object*)) { int initialize_graphics(lua_State* L) { // Register functions with Lua - luaL_newlib(L, gfx); + luaL_newlib(L, gfx_lib); lua_setglobal(L, "gfx"); return 1; // Number of values pushed onto the stack } @@ -152,14 +158,13 @@ int initialize_graphics(lua_State* L) { #if PLUGDATA void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); -static int gfx_initialize(lua_State* L) +void pdlua_gfx_clear(t_object* obj) { +} + +static int gfx_initialize(t_object* obj) { - t_object* obj = get_current_object(L); register_gui(gui_target, obj); - - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - gfx->has_gui = 1; - + pdlua_gfx_repaint(obj); return 0; } @@ -264,20 +269,22 @@ static int stroke_rounded_rect(lua_State* L) { static int draw_text(lua_State* L) { t_object* obj = get_current_object(L); - const char* text = luaL_checkstring(L, 1); // Assuming text is a string + const char* text = luaL_checkstring(L, 1); t_atom args[5]; SETSYMBOL(args, gensym(text)); SETFLOAT(args + 1, luaL_checknumber(L, 2)); // x SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y SETFLOAT(args + 3, luaL_checknumber(L, 4)); // w - SETFLOAT(args + 4, luaL_checknumber(L, 5)); // h plugdata_forward_message(obj, gensym("lua_text"), 5, args); return 0; } static int start_path(lua_State* L) { t_object* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_start_path"), 0, NULL); + t_atom args[2]; + SETFLOAT(args , luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + plugdata_forward_message(obj, gensym("lua_start_path"), 2, args); return 0; } @@ -290,17 +297,13 @@ static int line_to(lua_State* L) { return 0; } -static int quad_to(lua_State* L) { +static int close_path(lua_State* L) { t_object* obj = get_current_object(L); - t_atom args[4]; - SETFLOAT(args, luaL_checknumber(L, 1)); // bx - SETFLOAT(args + 1, luaL_checknumber(L, 2)); // by - SETFLOAT(args + 2, luaL_checknumber(L, 3)); // cx - SETFLOAT(args + 3, luaL_checknumber(L, 4)); // cy - plugdata_forward_message(obj, gensym("lua_quad_to"), 4, args); + plugdata_forward_message(obj, gensym("lua_close_path"), 0, NULL); return 0; } + static int stroke_path(lua_State* L) { t_object* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_stroke_path"), 0, NULL); @@ -331,15 +334,9 @@ static int scale(lua_State* L) { return 0; } -static int save(lua_State* L) { +static int reset_transform(lua_State* L) { t_object* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_save"), 0, NULL); - return 0; -} - -static int restore(lua_State* L) { - t_object* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_restore"), 0, NULL); + plugdata_forward_message(obj, gensym("lua_reset_transform"), 0, NULL); return 0; } #else @@ -352,6 +349,17 @@ static int min(int a, int b) { return (a < b) ? a : b; } +void pdlua_gfx_clear(t_object* obj) { + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + for(int i = 0; i < gfx->num_tags; i++) + { + pdgui_vmess(0, "crs", cnv, "delete", gfx->active_tags[i]); + } + gfx->num_tags = 0; +} + static void get_bounds_args(lua_State* L, t_object* obj, t_pdlua_gfx* gfx, int* x1, int* y1, int* x2, int* y2) { t_canvas* cnv = get_pdlua_canvas(obj); @@ -366,6 +374,14 @@ static void get_bounds_args(lua_State* L, t_object* obj, t_pdlua_gfx* gfx, int* w = min(x + w, gfx->width) - x; h = min(y + h, gfx->height) - y; + x *= gfx->scale_x * glist_getzoom(cnv); + y *= gfx->scale_y * glist_getzoom(cnv); + w *= gfx->scale_x * glist_getzoom(cnv); + h *= gfx->scale_y * glist_getzoom(cnv); + + x += gfx->translate_x; + y += gfx->translate_y; + x += text_xpix(obj, cnv); y += text_ypix(obj, cnv); @@ -379,18 +395,18 @@ static void get_bounds_args(lua_State* L, t_object* obj, t_pdlua_gfx* gfx, int* static const char* register_drawing(t_object* object) { t_pdlua_gfx* gfx = get_pdlua_gfx_state(object); - snprintf(gfx->active_tags[gfx->num_tags], 128, ".x%lx%lx", object, rand()); + snprintf(gfx->active_tags[gfx->num_tags], 128, ".x%lx", rand()); gfx->active_tags[gfx->num_tags][127] = '\0'; return gfx->active_tags[gfx->num_tags++]; } - -static int gfx_initialize(lua_State* L) +static int gfx_initialize(t_object* obj) { - t_object* obj = get_current_object(L); t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - gfx->has_gui = 1; + snprintf(gfx->object_tag, 128, ".x%lx", obj); + gfx->object_tag[127] = '\0'; + pdlua_gfx_repaint(obj); return 0; } @@ -416,9 +432,13 @@ static int set_color(lua_State* L) { t_object* obj = get_current_object(L); t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - gfx->current_red = luaL_checknumber(L, 1); - gfx->current_green = luaL_checknumber(L, 2); - gfx->current_blue = luaL_checknumber(L, 3); + int r = luaL_checknumber(L, 1); + int g = luaL_checknumber(L, 2); + int b = luaL_checknumber(L, 3); + + snprintf(gfx->current_color, 8, "#%02X%02X%02X", r, g, b); + gfx->current_color[7] = '\0'; + return 0; } @@ -430,12 +450,10 @@ static int fill_ellipse(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); - const char* tags[] = { register_drawing(obj) }; + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); - - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); + 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; } @@ -447,12 +465,10 @@ static int stroke_ellipse(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); - const char* tags[] = { register_drawing(obj) }; + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); - - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-outline", hex_color, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-outline", gfx->current_color, "-tags", 2, tags); + return 0; } @@ -466,12 +482,10 @@ static int fill_all(lua_State* L) { int x2 = x1 + gfx->width; int y2 = y1 + gfx->height; - const char* tags[] = { register_drawing(obj) }; + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-tags", 2, tags); - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); - - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); return 0; } @@ -483,12 +497,10 @@ static int fill_rect(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); - const char* tags[] = { register_drawing(obj) }; + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + pdgui_vmess(0, "crr iiii rs ri rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-width", 0, "-tags", 2, tags); - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); - - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); return 0; } @@ -500,12 +512,10 @@ static int stroke_rect(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); - const char* tags[] = { register_drawing(obj) }; - - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", hex_color, "-tags", 1, tags); + pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", gfx->current_color, "-tags", 2, tags); + return 0; } @@ -518,20 +528,22 @@ static int fill_rounded_rect(lua_State* L) { get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); int radius = luaL_checknumber(L, 5); // Radius for rounded corners + int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); + int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); - const char* tags[] = { register_drawing(obj) }; + int width = x2 - x1; + int height = y2 - y1; - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + // Tcl/tk can't fill rounded rectangles, so we draw 2 smaller rectangles with 4 ovals over the corners + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x1, y1, x1 + radius_x * 2, y1 + radius_y * 2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x2 - radius_x * 2 , y1, x2, y1 + radius_y * 2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x1, y2 - radius_y * 2, x1 + radius_x * 2, y2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x2 - radius_x * 2, y2 - radius_y * 2, x2, y2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "rectangle", x1 + radius_x, y1, x2 - radius_x, y2, "-width", 0, "-fill", gfx->current_color, "-tag", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "rectangle", x1, y1 + radius_y, x2, y2 - radius_y, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); - // Draw the main body of the rounded rectangle - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", hex_color, "-tags", 1, tags); - - // Draw rounded corners using arcs - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y1, x1+2*radius, y1+2*radius, "-start", 90, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y1, x2, y1+2*radius, "-start", 0, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y2-2*radius, x1+2*radius, y2, "-start", 180, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y2-2*radius, x2, y2, "-start", 270, "-extent", 90, "-fill", hex_color, "-width", 1, "-tags", 1, tags); return 0; } @@ -542,72 +554,220 @@ static int stroke_rounded_rect(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); - - int radius = luaL_checknumber(L, 5); // Radius for rounded corners - const char* tags[] = { register_drawing(obj) }; + int radius = luaL_checknumber(L, 5); // Radius for rounded corners + int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); + int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); - char hex_color[8]; - snprintf(hex_color, sizeof(hex_color), "#%02X%02X%02X", gfx->current_red, gfx->current_green, gfx->current_blue); - - // Draw the main body of the rounded rectangle with an outline - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", hex_color, "-width", 1, "-tags", 1, tags); + int width = x2 - x1; + int height = y2 - y1; + + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + // 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", 1, "-start", 90, "-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, y1, x2, y1 + radius_y*2, + "-start", 270, "-extent", 90, "-width", 1, "-start", 0, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri ri ri ri rs rs rS", cnv, "create", "arc", x1, y2 - radius_y*2, x1 + radius_x*2, y2, + "-start", 180, "-extent", 90, "-width", 1, "-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", 1, "-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", 1, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 + radius_y, y2, x1 + width - radius_y, y2, + "-width", 1, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 , y1 + radius_y, x1, y2 - radius_y, + "-width", 1, "-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", 1, "-fill", gfx->current_color, "-tags", 2, tags); - // Draw rounded corners using arcs - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y1, x1+2*radius, y1+2*radius, "-start", 90, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y1, x2, y1+2*radius, "-start", 0, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x1, y2-2*radius, x1+2*radius, y2, "-start", 180, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "arc", x2-2*radius, y2-2*radius, x2, y2, "-start", 270, "-extent", 90, "-outline", hex_color, "-width", 1, "-tags", 1, tags); return 0; } static int draw_text(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + 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); + + // Calculate the clipped rectangle + x = min(max(x, 0), gfx->width - w); + y = min(max(y, 0), gfx->height); + w = min(x + w, gfx->width) - x; + + // TODO: for text, this doesn't really scale it + x *= gfx->scale_x * glist_getzoom(cnv); + y *= gfx->scale_y * glist_getzoom(cnv); + w *= gfx->scale_x * glist_getzoom(cnv); + + x += gfx->translate_x; + y += gfx->translate_y; + + x += text_xpix(obj, cnv); + y += text_ypix(obj, cnv); + + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + pdgui_vmess(0, "crr ii rs rs rs rS", cnv, "create", "text", + x, y, "-anchor", "w", "-width", w, "-text", text, "-tags", 2, tags); + return 0; } static int start_path(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + + gfx->num_path_segments = 0; + gfx->path_start_x = luaL_checknumber(L, 1); + gfx->path_start_y = luaL_checknumber(L, 2); + gfx->path_smooth = luaL_checknumber(L, 3); + + gfx->path_segments[gfx->num_path_segments][0] = gfx->path_start_x; + gfx->path_segments[gfx->num_path_segments][1] = gfx->path_start_y; + gfx->num_path_segments++; return 0; } +// Function to add a line to the current path static int line_to(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + + if(gfx->num_path_segments >= 4096) return 0; + + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + + + gfx->path_segments[gfx->num_path_segments][0] = x; + gfx->path_segments[gfx->num_path_segments][1] = y; + gfx->num_path_segments++; + return 0; } -static int quad_to(lua_State* L) { +// Function to close the current path +static int close_path(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + + if(gfx->num_path_segments >= 4096) return 0; + + gfx->path_segments[gfx->num_path_segments][0] = gfx->path_start_x; + gfx->path_segments[gfx->num_path_segments][1] = gfx->path_start_y; + gfx->num_path_segments++; + return 0; } static int stroke_path(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + char coordinates[16384]; + int offset = 0; + int obj_x = text_xpix(obj, cnv); + int obj_y = text_ypix(obj, cnv); + for (int i = 0; i < gfx->num_path_segments; i++) { + int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + + x *= gfx->scale_x * glist_getzoom(cnv); + y *= gfx->scale_y * glist_getzoom(cnv); + + x += gfx->translate_x + obj_x; + y += gfx->translate_y + obj_y; + + int charsWritten = snprintf(coordinates + offset, 16384 - offset, "%i %i ", x, y); + if (charsWritten >= 0) { + offset += charsWritten; + } else { + break; + } + } + // Remove the trailing space + if (offset > 0) { + coordinates[offset - 1] = '\0'; + } + + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + pdgui_vmess(0, "crr r ri ri rs rs rS", cnv, "create", "polygon", coordinates, "-smooth", gfx->path_smooth, "-smooth", gfx->path_smooth, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); + return 0; } static int fill_path(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_canvas* cnv = get_pdlua_canvas(obj); + + char coordinates[16384]; + int offset = 0; + int obj_x = text_xpix(obj, cnv); + int obj_y = text_ypix(obj, cnv); + for (int i = 0; i < gfx->num_path_segments; i++) { + int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + + x *= gfx->scale_x * glist_getzoom(cnv); + y *= gfx->scale_y * glist_getzoom(cnv); + + x += gfx->translate_x + obj_x; + y += gfx->translate_y + obj_y; + + int charsWritten = snprintf(coordinates + offset, 16384 - offset, "%d %d ", x, y ); + if (charsWritten >= 0) { + offset += charsWritten; + } else { + break; + } + } + // Remove the trailing space + if (offset > 0) { + coordinates[offset - 1] = '\0'; + } + + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + pdgui_vmess(0, "crr r ri ri rs rS", cnv, "create", "polygon", coordinates, "-width", 0, "-smooth", gfx->path_smooth, "-fill", gfx->current_color, "-tags", 2, tags); + return 0; } + static int translate(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + gfx->translate_x = luaL_checknumber(L, 1); + gfx->translate_y = luaL_checknumber(L, 2); return 0; } static int scale(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + gfx->scale_x = luaL_checknumber(L, 1); + gfx->scale_y = luaL_checknumber(L, 2); return 0; } -static int save(lua_State* L) { - t_object* obj = get_current_object(L); - return 0; -} -static int restore(lua_State* L) { +static int reset_transform(lua_State* L) { t_object* obj = get_current_object(L); + t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + gfx->translate_x = 0; + gfx->translate_y = 0; + gfx->scale_x = 1.0f; + gfx->scale_y = 1.0f; return 0; } From 9ade045527abde8a74fa2b4b8286e2e4b3de9fbf Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 19:29:22 +0100 Subject: [PATCH 06/62] Fixed text drawing for non-gui objects --- pdlua.c | 27 +++++++++++++++++++++++++++ pdlua_gfx.h | 3 --- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/pdlua.c b/pdlua.c index e65b624..16620a2 100644 --- a/pdlua.c +++ b/pdlua.c @@ -50,6 +50,7 @@ #include "m_pd.h" #include "s_stuff.h" // for sys_register_loader() #include "m_imp.h" // for struct _class +#include "g_canvas.h" /* BAD: support for Pd < 0.41 */ #include "pdlua_gfx.h" @@ -663,6 +664,30 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp } void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ + if(!((t_pdlua *)z)->has_gui) + { + t_text *x = (t_text *)z; + if (vis) + { + if (gobj_shouldvis(&x->te_g, glist)) + { + t_rtext *y = glist_findrtext(glist, x); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 1); + rtext_draw(y); + } + } + else + { + t_rtext *y = glist_findrtext(glist, x); + if (gobj_shouldvis(&x->te_g, glist)) + { + text_eraseborder(x, glist, rtext_gettag(y)); + rtext_erase(y); + } + } + return; + } if(vis) { pdlua_gfx_repaint((t_object*)z); @@ -876,6 +901,8 @@ static int pdlua_object_creategui(lua_State *L) { gfx_initialize(o); } + + return 0; } /** Lua object inlet creation. */ diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 13f4707..2c7c909 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -1,5 +1,3 @@ -#include - typedef struct _pdlua_gfx { #if !PLUGDATA @@ -647,7 +645,6 @@ static int line_to(lua_State* L) { int x = luaL_checknumber(L, 1); int y = luaL_checknumber(L, 2); - gfx->path_segments[gfx->num_path_segments][0] = x; gfx->path_segments[gfx->num_path_segments][1] = y; gfx->num_path_segments++; From 38d843a77c665d2d21ec5c318cc4d5f45112895f Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 22:03:59 +0100 Subject: [PATCH 07/62] Fixed text drawing for pd, implement size management, small API and implementation improvements --- pdlua.c | 39 +------ pdlua_gfx.h | 294 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 182 insertions(+), 151 deletions(-) diff --git a/pdlua.c b/pdlua.c index 16620a2..bf1b810 100644 --- a/pdlua.c +++ b/pdlua.c @@ -46,8 +46,9 @@ #include #include -/* we use Pd */ -#include "m_pd.h" + +#include "pdlua.h" + #include "s_stuff.h" // for sys_register_loader() #include "m_imp.h" // for struct _class #include "g_canvas.h" @@ -120,19 +121,6 @@ typedef struct pdlua_readerdata char buffer[MAXPDSTRING]; /**< Buffer to read into. */ } t_pdlua_readerdata; -/** Pd object data. */ -typedef struct pdlua -{ - t_object pd; /**< We are a Pd object. */ - int inlets; /**< Number of inlets. */ - struct pdlua_proxyinlet *in; /**< The inlets themselves. */ - int outlets; /**< Number of outlets. */ - t_outlet **out; /**< The outlets themselves. */ - t_canvas *canvas; /**< The canvas that the object was created on. */ - int has_gui; /**< True if graphics are enabled. */ - t_pdlua_gfx gfx; /**< Holds state for graphics. */ -} t_pdlua; - /** Proxy inlet object data. */ typedef struct pdlua_proxyinlet { @@ -612,8 +600,6 @@ static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, if (up) { - pdlua_gfx_mouse_move((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); - if(!x->gfx.mouse_up) { pdlua_gfx_mouse_up((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); @@ -2026,23 +2012,6 @@ static int pdlua_loader_pathwise return pdlua_loader_wrappath(fd, objectname, dirbuf); } - - -lua_State* get_lua_state() -{ - return __L; -} - -t_canvas* get_pdlua_canvas(t_object* x) -{ - return ((t_pdlua*)x)->canvas; -} - -t_pdlua_gfx* get_pdlua_gfx_state(t_object* x) -{ - return &((t_pdlua*)x)->gfx; -} - #ifdef WIN32 #include #else @@ -2204,7 +2173,7 @@ void pdlua_setup(void) pd_error(NULL, "lua: loader will not be registered!"); } - initialize_graphics(__L); + pdlua_gfx_setup(__L); #if PLUGDATA set_gui_callback(callback_target, register_gui_callback); diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 2c7c909..df039c7 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -1,25 +1,8 @@ -typedef struct _pdlua_gfx -{ -#if !PLUGDATA - char object_tag[128]; - char active_tags[128][128]; - int num_tags; - - int path_segments[4096][2]; - int num_path_segments; - int path_start_x, path_start_y; - int path_smooth; - - int mouse_x, mouse_y, mouse_up; - int translate_x, translate_y; - float scale_x, scale_y; - char current_color[8]; -#endif - int width, height; -} t_pdlua_gfx; -static int gfx_initialize(t_object* obj); -static int start_paint(lua_State* L); +static int gfx_initialize(t_pdlua *obj); + +static int set_size(lua_State *L); +static int start_paint(lua_State *L); static int end_paint(lua_State* L); static int set_color(lua_State* L); @@ -44,14 +27,16 @@ static int translate(lua_State* L); static int scale(lua_State* L); static int reset_transform(lua_State* L); -void pdlua_gfx_clear(t_object* obj); +void pdlua_gfx_clear(t_pdlua *obj); -lua_State* get_lua_state(); -t_canvas* get_pdlua_canvas(t_object* object); -t_pdlua_gfx* get_pdlua_gfx_state(t_object* object); +typedef struct _pdlua_mouse_proxy{ + t_object p_obj; + t_symbol *p_sym; + t_clock *p_clock; + t_pdlua *p_parent; +}t_pdlua_mouse_proxy; -void pdlua_gfx_repaint(t_object* o) { - lua_State* __L = get_lua_state(); +void pdlua_gfx_repaint(t_pdlua *o) { lua_getglobal(__L, "pd"); lua_getfield (__L, -1, "_repaint"); lua_pushlightuserdata(__L, o); @@ -65,8 +50,7 @@ void pdlua_gfx_repaint(t_object* o) { lua_pop(__L, 1); /* pop the global "pd" */ } -void pdlua_gfx_mouse_event(t_object* o, int x, int y, int type) { - lua_State* __L = get_lua_state(); +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); @@ -83,24 +67,24 @@ void pdlua_gfx_mouse_event(t_object* o, int x, int y, int type) { lua_pop(__L, 1); /* pop the global "pd" */ } -void pdlua_gfx_mouse_down(t_object* o, int x, int y) { +void pdlua_gfx_mouse_down(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 0); } -void pdlua_gfx_mouse_up(t_object* o, int x, int y) { +void pdlua_gfx_mouse_up(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 1); } -void pdlua_gfx_mouse_move(t_object* o, int x, int y) { +void pdlua_gfx_mouse_move(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 2); } -void pdlua_gfx_mouse_drag(t_object* o, int x, int y) { +void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 3); } #define PDLUA_OBJECT_REGISTRTY_ID 512 -static t_object* get_current_object(lua_State* L) +static t_pdlua* get_current_object(lua_State* L) { lua_pushvalue(L, LUA_REGISTRYINDEX); lua_geti(L, -1, PDLUA_OBJECT_REGISTRTY_ID); @@ -114,6 +98,7 @@ static t_object* get_current_object(lua_State* L) // Register functions with Lua static const luaL_Reg gfx_lib[] = { + {"set_size", set_size}, {"set_color", set_color}, {"fill_ellipse", fill_ellipse}, {"stroke_ellipse", stroke_ellipse}, @@ -140,46 +125,101 @@ static const luaL_Reg gfx_lib[] = { void* gui_target; void(*register_gui)(void*, t_object*); + int set_gui_callback(void* callback_target, void(*callback)(void*, t_object*)) { gui_target = callback_target; register_gui = callback; return 0; } -int initialize_graphics(lua_State* L) { +#if !PLUGDATA +t_class* pdlua_mouse_proxy_class; + +static void pdlua_mouse_proxy_any(t_pdlua_mouse_proxy *p, t_symbol*s, int ac, t_atom *av){ + + if(s == gensym("motion") && ! p->p_parent->gfx.mouse_up) + { + t_canvas *cnv = p->p_parent->canvas; + int zoom = glist_getzoom(cnv); + int x = (int)(av->a_w.w_float / zoom); + int y = (int)((av+1)->a_w.w_float / zoom); + x -= text_xpix(p->p_parent, cnv); + y -= text_ypix(p->p_parent, cnv); + + if(x > 0 && y > 0 && x < p->p_parent->gfx.width && y < p->p_parent->gfx.height) { + pdlua_gfx_mouse_move(p->p_parent, x, y); + } + } +} + +static void pdlua_mouse_proxy_free(t_pdlua_mouse_proxy *p){ + pd_unbind(&p->p_obj.ob_pd, p->p_sym); + //clock_free(p->p_clock); + pd_free(&p->p_obj.ob_pd); +} + +static t_pdlua_mouse_proxy *pdlua_mouse_proxy_new(t_pdlua *x, t_symbol *s){ + t_pdlua_mouse_proxy *p = (t_pdlua_mouse_proxy*)pd_new(pdlua_mouse_proxy_class); + p->p_parent = x; + pd_bind(&p->p_obj.ob_pd, p->p_sym = s); + //p->p_clock = clock_new(p, (t_method)pdlua_mouse_proxy_free); + return(p); +} +#endif + + +int pdlua_gfx_setup(lua_State* L) { // Register functions with Lua luaL_newlib(L, gfx_lib); lua_setglobal(L, "gfx"); + +#if !PLUGDATA + pdlua_mouse_proxy_class = class_new(0, 0, 0, sizeof(t_pdlua_mouse_proxy), CLASS_NOINLET | CLASS_PD, 0); + class_addanything(pdlua_mouse_proxy_class, pdlua_mouse_proxy_any); +#endif + return 1; // Number of values pushed onto the stack } #if PLUGDATA void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); -void pdlua_gfx_clear(t_object* obj) { +void pdlua_gfx_clear(t_pdlua* obj) { } -static int gfx_initialize(t_object* obj) +static int gfx_initialize(t_pdlua* obj) { register_gui(gui_target, obj); pdlua_gfx_repaint(obj); return 0; } +static int set_size(lua_State* L) +{ + t_pdlua* obj = get_current_object(L); + obj->gfx.width = luaL_checknumber(L, 1); + obj->gfx.height = luaL_checknumber(L, 2); + t_atom args[2]; + SETFLOAT(args, luaL_checknumber(L, 1)); // w + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // h + plugdata_forward_message(obj, gensym("lua_resized"), 2, args); + return 0; +} + static int start_paint(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_start_paint"), 0, NULL); return 0; } static int end_paint(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_end_paint"), 0, NULL); return 0; } static int set_color(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[4]; SETFLOAT(args, luaL_checknumber(L, 1)); // r SETFLOAT(args + 1, luaL_checknumber(L, 2)); // g @@ -190,7 +230,7 @@ static int set_color(lua_State* L) { } static int fill_ellipse(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[4]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -201,7 +241,7 @@ static int fill_ellipse(lua_State* L) { } static int stroke_ellipse(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[4]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -213,13 +253,13 @@ static int stroke_ellipse(lua_State* L) { } static int fill_all(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_fill_all"), 0, NULL); return 0; } static int fill_rect(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[4]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -230,7 +270,7 @@ static int fill_rect(lua_State* L) { } static int stroke_rect(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[5]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -242,7 +282,7 @@ static int stroke_rect(lua_State* L) { } static int fill_rounded_rect(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[4]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -253,7 +293,7 @@ static int fill_rounded_rect(lua_State* L) { } static int stroke_rounded_rect(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[6]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -266,7 +306,7 @@ static int stroke_rounded_rect(lua_State* L) { } static int draw_text(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); const char* text = luaL_checkstring(L, 1); t_atom args[5]; SETSYMBOL(args, gensym(text)); @@ -278,7 +318,7 @@ static int draw_text(lua_State* L) { } static int start_path(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[2]; SETFLOAT(args , luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -287,7 +327,7 @@ static int start_path(lua_State* L) { } static int line_to(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -296,26 +336,28 @@ static int line_to(lua_State* L) { } static int close_path(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_close_path"), 0, NULL); return 0; } static int stroke_path(lua_State* L) { - t_object* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_stroke_path"), 0, NULL); + t_pdlua* obj = get_current_object(L); + t_atom arg; + SETFLOAT(&arg, luaL_checknumber(L, 1)); // line thickness + plugdata_forward_message(obj, gensym("lua_stroke_path"), 1, &arg); return 0; } static int fill_path(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_fill_path"), 0, NULL); return 0; } static int translate(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // tx SETFLOAT(args + 1, luaL_checknumber(L, 2)); // ty @@ -324,7 +366,7 @@ static int translate(lua_State* L) { } static int scale(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // sx SETFLOAT(args + 1, luaL_checknumber(L, 2)); // sy @@ -333,7 +375,7 @@ static int scale(lua_State* L) { } static int reset_transform(lua_State* L) { - t_object* obj = get_current_object(L); + t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_reset_transform"), 0, NULL); return 0; } @@ -347,9 +389,9 @@ static int min(int a, int b) { return (a < b) ? a : b; } -void pdlua_gfx_clear(t_object* obj) { - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); +void pdlua_gfx_clear(t_pdlua *obj) { + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; for(int i = 0; i < gfx->num_tags; i++) { @@ -358,8 +400,8 @@ void pdlua_gfx_clear(t_object* obj) { gfx->num_tags = 0; } -static void get_bounds_args(lua_State* L, t_object* obj, t_pdlua_gfx* gfx, int* x1, int* y1, int* x2, int* y2) { - t_canvas* cnv = get_pdlua_canvas(obj); +static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x1, int* y1, int* x2, int* y2) { + t_canvas *cnv = obj->canvas; int x = luaL_checknumber(L, 1); int y = luaL_checknumber(L, 2); @@ -390,28 +432,47 @@ static void get_bounds_args(lua_State* L, t_object* obj, t_pdlua_gfx* gfx, int* } -static const char* register_drawing(t_object* object) +static const char* register_drawing(t_pdlua *object) { - t_pdlua_gfx* gfx = get_pdlua_gfx_state(object); + t_pdlua_gfx *gfx = &object->gfx; snprintf(gfx->active_tags[gfx->num_tags], 128, ".x%lx", rand()); gfx->active_tags[gfx->num_tags][127] = '\0'; return gfx->active_tags[gfx->num_tags++]; } -static int gfx_initialize(t_object* obj) + +static int gfx_initialize(t_pdlua *obj) { - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; + snprintf(gfx->object_tag, 128, ".x%lx", obj); gfx->object_tag[127] = '\0'; pdlua_gfx_repaint(obj); + + char buf[MAXPDSTRING]; + snprintf(buf, MAXPDSTRING-1, ".x%lx", (unsigned long)cnv); + buf[MAXPDSTRING-1] = 0; + + gfx->mouse_proxy = pdlua_mouse_proxy_new(obj, gensym(buf)); + + return 0; +} + +static int set_size(lua_State* L) +{ + t_pdlua* obj = get_current_object(L); + obj->gfx.width = luaL_checknumber(L, 1); + obj->gfx.height = luaL_checknumber(L, 2); + pdlua_gfx_repaint(obj); return 0; } static int start_paint(lua_State* L) { - t_object* obj = get_current_object(L); - t_canvas* cnv = get_pdlua_canvas(obj); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_canvas *cnv = obj->canvas; + t_pdlua_gfx *gfx = &obj->gfx; // Delete drawings made previously for(int i = 0; i < gfx->num_tags; i++) @@ -427,8 +488,8 @@ static int end_paint(lua_State* L) { } static int set_color(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; int r = luaL_checknumber(L, 1); int g = luaL_checknumber(L, 2); @@ -441,9 +502,9 @@ static int set_color(lua_State* L) { } static int fill_ellipse(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -456,9 +517,9 @@ static int fill_ellipse(lua_State* L) { } static int stroke_ellipse(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -471,9 +532,9 @@ static int stroke_ellipse(lua_State* L) { } static int fill_all(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1 = text_xpix(obj, cnv); int y1 = text_ypix(obj, cnv); @@ -488,9 +549,9 @@ static int fill_all(lua_State* L) { } static int fill_rect(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -503,9 +564,9 @@ static int fill_rect(lua_State* L) { } static int stroke_rect(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -518,9 +579,9 @@ static int stroke_rect(lua_State* L) { } static int fill_rounded_rect(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -546,9 +607,9 @@ static int fill_rounded_rect(lua_State* L) { } static int stroke_rounded_rect(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -586,9 +647,9 @@ static int stroke_rounded_rect(lua_State* L) { } static int draw_text(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; const char* text = luaL_checkstring(L, 1); // Assuming text is a string @@ -614,15 +675,15 @@ static int draw_text(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr ii rs rs rs rS", cnv, "create", "text", + pdgui_vmess(0, "crr ii rs ri rs rS", cnv, "create", "text", x, y, "-anchor", "w", "-width", w, "-text", text, "-tags", 2, tags); return 0; } static int start_path(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->num_path_segments = 0; gfx->path_start_x = luaL_checknumber(L, 1); @@ -637,8 +698,8 @@ static int start_path(lua_State* L) { // Function to add a line to the current path static int line_to(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; if(gfx->num_path_segments >= 4096) return 0; @@ -654,8 +715,8 @@ static int line_to(lua_State* L) { // Function to close the current path static int close_path(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; if(gfx->num_path_segments >= 4096) return 0; @@ -667,9 +728,11 @@ static int close_path(lua_State* L) { } static int stroke_path(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; + + int stroke_width = luaL_checknumber(L, 1); char coordinates[16384]; int offset = 0; @@ -698,15 +761,15 @@ static int stroke_path(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr r ri ri rs rs rS", cnv, "create", "polygon", coordinates, "-smooth", gfx->path_smooth, "-smooth", gfx->path_smooth, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); + pdgui_vmess(0, "crr r ri ri ri rs rs rS", cnv, "create", "polygon", coordinates, "-width", stroke_width, "-smooth", gfx->path_smooth, "-smooth", gfx->path_smooth, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); return 0; } static int fill_path(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); - t_canvas* cnv = get_pdlua_canvas(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = obj->canvas; char coordinates[16384]; int offset = 0; @@ -742,16 +805,16 @@ static int fill_path(lua_State* L) { static int translate(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->translate_x = luaL_checknumber(L, 1); gfx->translate_y = luaL_checknumber(L, 2); return 0; } static int scale(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->scale_x = luaL_checknumber(L, 1); gfx->scale_y = luaL_checknumber(L, 2); return 0; @@ -759,13 +822,12 @@ static int scale(lua_State* L) { static int reset_transform(lua_State* L) { - t_object* obj = get_current_object(L); - t_pdlua_gfx* gfx = get_pdlua_gfx_state(obj); + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->translate_x = 0; gfx->translate_y = 0; gfx->scale_x = 1.0f; gfx->scale_y = 1.0f; return 0; } - #endif From 3e274ebb34762fae78ccbddca931f1aa36441bcd Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 20 Dec 2023 22:06:39 +0100 Subject: [PATCH 08/62] Added new file --- pdlua.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 pdlua.h diff --git a/pdlua.h b/pdlua.h new file mode 100644 index 0000000..09173e6 --- /dev/null +++ b/pdlua.h @@ -0,0 +1,42 @@ +#include "m_pd.h" + +/** Global Lua interpreter state, needed in the constructor. */ +static lua_State *__L; + +typedef struct _pdlua_gfx +{ +#if !PLUGDATA + char object_tag[128]; + char active_tags[128][128]; + int num_tags; + + int path_segments[4096][2]; + int num_path_segments; + int path_start_x, path_start_y; + int path_smooth; + + int mouse_x, mouse_y, mouse_up; + int translate_x, translate_y; + float scale_x, scale_y; + char current_color[8]; + + t_symbol* mouse_proxy_sym; + void* mouse_proxy; +#endif + int width, height; +} t_pdlua_gfx; + +/** Pd object data. */ +typedef struct pdlua +{ + t_object pd; /**< We are a Pd object. */ + int inlets; /**< Number of inlets. */ + struct pdlua_proxyinlet *in; /**< The inlets themselves. */ + int outlets; /**< Number of outlets. */ + t_outlet **out; /**< The outlets themselves. */ + t_canvas *canvas; /**< The canvas that the object was created on. */ + int has_gui; /**< True if graphics are enabled. */ + t_pdlua_gfx gfx; /**< Holds state for graphics. */ +} t_pdlua; + + From 918f235e16ff4f5cebdbbe7326fe1400bec0ca11 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 16:35:04 +0100 Subject: [PATCH 09/62] Better interface for bezier curves --- pdlua.c | 4 +- pdlua.h | 1 - pdlua_gfx.h | 175 ++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 150 insertions(+), 30 deletions(-) diff --git a/pdlua.c b/pdlua.c index bf1b810..60f1ae0 100644 --- a/pdlua.c +++ b/pdlua.c @@ -641,8 +641,8 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp float x1 = text_xpix((t_text *)x, glist), y1 = text_ypix((t_text *)x, glist); *xp1 = x1; *yp1 = y1; - *xp2 = x1 + x->gfx.width; - *yp2 = y1 + x->gfx.height; + *xp2 = x1 + x->gfx.width * glist->gl_zoom; + *yp2 = y1 + x->gfx.height * glist->gl_zoom; } else { text_widgetbehavior.w_getrectfn(x, glist, xp1, yp1, xp2, yp2); diff --git a/pdlua.h b/pdlua.h index 09173e6..31594ff 100644 --- a/pdlua.h +++ b/pdlua.h @@ -13,7 +13,6 @@ typedef struct _pdlua_gfx int path_segments[4096][2]; int num_path_segments; int path_start_x, path_start_y; - int path_smooth; int mouse_x, mouse_y, mouse_up; int translate_x, translate_y; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index df039c7..072ac7e 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -1,4 +1,5 @@ +// Functions that need to be implemented separately for each Pd flavour static int gfx_initialize(t_pdlua *obj); static int set_size(lua_State *L); @@ -19,6 +20,8 @@ static int draw_text(lua_State* L); static int start_path(lua_State* L); static int line_to(lua_State* L); +static int quad_to(lua_State* L); +static int cubic_to(lua_State* L); static int close_path(lua_State* L); static int stroke_path(lua_State* L); static int fill_path(lua_State* L); @@ -109,6 +112,8 @@ static const luaL_Reg gfx_lib[] = { {"text", draw_text}, {"start_path", start_path}, {"line_to", line_to}, + {"quad_to", quad_to}, + {"cubic_to", cubic_to}, {"close_path", close_path}, {"stroke_path", stroke_path}, {"fill_path", fill_path}, @@ -139,7 +144,7 @@ static void pdlua_mouse_proxy_any(t_pdlua_mouse_proxy *p, t_symbol*s, int ac, t_ if(s == gensym("motion") && ! p->p_parent->gfx.mouse_up) { - t_canvas *cnv = p->p_parent->canvas; + t_canvas *cnv = glist_getcanvas(p->p_parent->canvas); int zoom = glist_getzoom(cnv); int x = (int)(av->a_w.w_float / zoom); int y = (int)((av+1)->a_w.w_float / zoom); @@ -313,6 +318,7 @@ static int draw_text(lua_State* L) { SETFLOAT(args + 1, luaL_checknumber(L, 2)); // x SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y SETFLOAT(args + 3, luaL_checknumber(L, 4)); // w + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // h plugdata_forward_message(obj, gensym("lua_text"), 5, args); return 0; } @@ -335,6 +341,34 @@ static int line_to(lua_State* L) { return 0; } +static int quad_to(lua_State* L) { + t_pdlua* obj = get_current_object(L); + t_atom args[4]; // Assuming quad_to takes 3 arguments + SETFLOAT(args, luaL_checknumber(L, 1)); // x1 + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y1 + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // x2 + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // y2 + + // Forward the message to the appropriate function + plugdata_forward_message(obj, gensym("lua_quad_to"), 4, args); + return 0; +} + +static int cubic_to(lua_State* L) { + t_pdlua* obj = get_current_object(L); + t_atom args[6]; // Assuming cubic_to takes 4 arguments + SETFLOAT(args, luaL_checknumber(L, 1)); // x1 + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y1 + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // x2 + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // y2 + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // x3 + SETFLOAT(args + 5, luaL_checknumber(L, 6)); // y3 + + // Forward the message to the appropriate function + plugdata_forward_message(obj, gensym("lua_cubic_to"), 6, args); + return 0; +} + static int close_path(lua_State* L) { t_pdlua* obj = get_current_object(L); plugdata_forward_message(obj, gensym("lua_close_path"), 0, NULL); @@ -391,7 +425,7 @@ static int min(int a, int b) { void pdlua_gfx_clear(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); for(int i = 0; i < gfx->num_tags; i++) { @@ -401,7 +435,7 @@ void pdlua_gfx_clear(t_pdlua *obj) { } static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x1, int* y1, int* x2, int* y2) { - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x = luaL_checknumber(L, 1); int y = luaL_checknumber(L, 2); @@ -445,7 +479,7 @@ static const char* register_drawing(t_pdlua *object) static int gfx_initialize(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); snprintf(gfx->object_tag, 128, ".x%lx", obj); gfx->object_tag[127] = '\0'; @@ -471,7 +505,7 @@ static int set_size(lua_State* L) static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); t_pdlua_gfx *gfx = &obj->gfx; // Delete drawings made previously @@ -504,7 +538,7 @@ static int set_color(lua_State* L) { static int fill_ellipse(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -519,7 +553,7 @@ static int fill_ellipse(lua_State* L) { static int stroke_ellipse(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -534,12 +568,12 @@ static int stroke_ellipse(lua_State* L) { static int fill_all(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1 = text_xpix(obj, cnv); int y1 = text_ypix(obj, cnv); - int x2 = x1 + gfx->width; - int y2 = y1 + gfx->height; + int x2 = x1 + gfx->width * glist_getzoom(cnv); + int y2 = y1 + gfx->height * glist_getzoom(cnv); const char* tags[] = { gfx->object_tag, register_drawing(obj) }; @@ -551,7 +585,7 @@ static int fill_all(lua_State* L) { static int fill_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -566,7 +600,7 @@ static int fill_rect(lua_State* L) { static int stroke_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -581,7 +615,7 @@ static int stroke_rect(lua_State* L) { static int fill_rounded_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -609,7 +643,7 @@ static int fill_rounded_rect(lua_State* L) { static int stroke_rounded_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); @@ -649,23 +683,23 @@ static int stroke_rounded_rect(lua_State* L) { static int draw_text(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + 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 fontHeight = luaL_checknumber(L, 5); // Calculate the clipped rectangle x = min(max(x, 0), gfx->width - w); y = min(max(y, 0), gfx->height); w = min(x + w, gfx->width) - x; - // TODO: for text, this doesn't really scale it - x *= gfx->scale_x * glist_getzoom(cnv); - y *= gfx->scale_y * glist_getzoom(cnv); - w *= gfx->scale_x * glist_getzoom(cnv); + int zoom = glist_getzoom(cnv); + x *= gfx->scale_x * zoom; + y *= gfx->scale_y * zoom; + w *= gfx->scale_x * zoom; x += gfx->translate_x; y += gfx->translate_y; @@ -673,11 +707,26 @@ static int draw_text(lua_State* L) { x += text_xpix(obj, cnv); y += text_ypix(obj, cnv); + // Font size and position are offset to make sure they match drawing in plugdata + fontHeight *= 0.8f; + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; pdgui_vmess(0, "crr ii rs ri rs rS", cnv, "create", "text", - x, y, "-anchor", "w", "-width", w, "-text", text, "-tags", 2, tags); + 0, 0, "-anchor", "nw", "-width", w, "-text", text, "-tags", 2, tags); + + t_atom fontatoms[3]; + SETSYMBOL(fontatoms+0, gensym(sys_font)); + SETFLOAT (fontatoms+1, fontHeight * zoom * gfx->scale_y); + SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); + pdgui_vmess(0, "crs rA rs rs", cnv, "itemconfigure", tags[1], + "-font", 3, fontatoms, + "-fill", gfx->current_color, + "-justify", "left"); + + pdgui_vmess(0, "crs ii", cnv, "coords", tags[1], x + 18, y); + return 0; } @@ -688,7 +737,6 @@ static int start_path(lua_State* L) { gfx->num_path_segments = 0; gfx->path_start_x = luaL_checknumber(L, 1); gfx->path_start_y = luaL_checknumber(L, 2); - gfx->path_smooth = luaL_checknumber(L, 3); gfx->path_segments[gfx->num_path_segments][0] = gfx->path_start_x; gfx->path_segments[gfx->num_path_segments][1] = gfx->path_start_y; @@ -701,7 +749,10 @@ static int line_to(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - if(gfx->num_path_segments >= 4096) return 0; + if(gfx->num_path_segments >= 4096) { + luaL_error(L, "Too many path segments"); + return 0; + } int x = luaL_checknumber(L, 1); int y = luaL_checknumber(L, 2); @@ -713,6 +764,76 @@ static int line_to(lua_State* L) { return 0; } +static int quad_to(lua_State* L) { + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + + int x2 = luaL_checknumber(L, 1); + int y2 = luaL_checknumber(L, 2); + int x3 = luaL_checknumber(L, 3); + int y3 = luaL_checknumber(L, 4); + + int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][0] : x2; + int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][1] : y2; + + // Get the last point + float t = 0.0; + const float resolution = 1000; + while (t <= 1.0) { + t += 1.0 / resolution; + if (gfx->num_path_segments < 4096) { + // 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; + + gfx->path_segments[gfx->num_path_segments][0] = x; + gfx->path_segments[gfx->num_path_segments][1] = y; + gfx->num_path_segments++; + } else { + luaL_error(L, "Too many path segments"); + return 0; + } + } + + return 0; +} +static int cubic_to(lua_State* L) { + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + + int x2 = luaL_checknumber(L, 1); + int y2 = luaL_checknumber(L, 2); + int x3 = luaL_checknumber(L, 3); + int y3 = luaL_checknumber(L, 4); + int x4 = luaL_checknumber(L, 5); + int y4 = luaL_checknumber(L, 6); + + int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][0] : x2; + int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][1] : y2; + + // Get the last point + float t = 0.0; + const float resolution = 1000; + while (t <= 1.0) { + t += 1.0 / resolution; + if (gfx->num_path_segments < 4096) { + + 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; + + // Calculate bezier curve as points (source: https://en.wikipedia.org/wiki/B%C3%A9zier_curve) + gfx->path_segments[gfx->num_path_segments][0] = x; + gfx->path_segments[gfx->num_path_segments][1] = y; + gfx->num_path_segments++; + } else { + luaL_error(L, "Too many path segments"); + return 0; + } + } + + return 0; +} + // Function to close the current path static int close_path(lua_State* L) { t_pdlua* obj = get_current_object(L); @@ -730,7 +851,7 @@ static int close_path(lua_State* L) { static int stroke_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); int stroke_width = luaL_checknumber(L, 1); @@ -761,7 +882,7 @@ static int stroke_path(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr r ri ri ri rs rs rS", cnv, "create", "polygon", coordinates, "-width", stroke_width, "-smooth", gfx->path_smooth, "-smooth", gfx->path_smooth, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); + pdgui_vmess(0, "crr r ri rs rs rS", cnv, "create", "polygon", coordinates, "-width", stroke_width, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); return 0; } @@ -769,7 +890,7 @@ static int stroke_path(lua_State* L) { static int fill_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = obj->canvas; + t_canvas *cnv = glist_getcanvas(obj->canvas); char coordinates[16384]; int offset = 0; @@ -798,7 +919,7 @@ static int fill_path(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr r ri ri rs rS", cnv, "create", "polygon", coordinates, "-width", 0, "-smooth", gfx->path_smooth, "-fill", gfx->current_color, "-tags", 2, tags); + pdgui_vmess(0, "crr r ri rs rS", cnv, "create", "polygon", coordinates, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); return 0; } From ef21ce9a01ed1957419a7673ac4bfaf0b68cb50e Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 16:55:52 +0100 Subject: [PATCH 10/62] Fixed potential heap overflow when sending path data to pd --- pdlua_gfx.h | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 072ac7e..a79009a 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -853,10 +853,9 @@ static int stroke_path(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - int stroke_width = luaL_checknumber(L, 1); - - char coordinates[16384]; - int offset = 0; + int stroke_width = luaL_checknumber(L, 1) * glist_getzoom(cnv); + + // Apply transformations to all coordinates int obj_x = text_xpix(obj, cnv); int obj_y = text_ypix(obj, cnv); for (int i = 0; i < gfx->num_path_segments; i++) { @@ -867,15 +866,31 @@ static int stroke_path(lua_State* L) { x += gfx->translate_x + obj_x; y += gfx->translate_y + obj_y; + + gfx->path_segments[i][0] = x; + gfx->path_segments[i][1] = y; + } + + int totalSize = 0; + // Determine the total size needed + for (int i = 0; i < gfx->num_path_segments; i++) { + int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + // Calculate size for x and y + totalSize += snprintf(NULL, 0, "%i %i ", x, y); + } + char *coordinates = (char*)getbytes(totalSize + 1); // +1 for null terminator - int charsWritten = snprintf(coordinates + offset, 16384 - offset, "%i %i ", x, y); + int offset = 0; + for (int i = 0; i < gfx->num_path_segments; i++) { + int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int charsWritten = snprintf(coordinates + offset, totalSize - offset, "%i %i ", x, y); if (charsWritten >= 0) { offset += charsWritten; } else { break; } } - // Remove the trailing space + // Replace the trailing space with string terminator if (offset > 0) { coordinates[offset - 1] = '\0'; } @@ -884,6 +899,8 @@ static int stroke_path(lua_State* L) { pdgui_vmess(0, "crr r ri rs rs rS", cnv, "create", "polygon", coordinates, "-width", stroke_width, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); + freebytes(coordinates, totalSize+1); + return 0; } @@ -892,8 +909,7 @@ static int fill_path(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - char coordinates[16384]; - int offset = 0; + // Apply transformations to all coordinates int obj_x = text_xpix(obj, cnv); int obj_y = text_ypix(obj, cnv); for (int i = 0; i < gfx->num_path_segments; i++) { @@ -905,13 +921,30 @@ static int fill_path(lua_State* L) { x += gfx->translate_x + obj_x; y += gfx->translate_y + obj_y; - int charsWritten = snprintf(coordinates + offset, 16384 - offset, "%d %d ", x, y ); + gfx->path_segments[i][0] = x; + gfx->path_segments[i][1] = y; + } + + int totalSize = 0; + // Determine the total size needed + for (int i = 0; i < gfx->num_path_segments; i++) { + int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + // Calculate size for x and y + totalSize += snprintf(NULL, 0, "%i %i ", x, y); + } + char *coordinates = (char*)getbytes(totalSize + 1); // +1 for null terminator + + int offset = 0; + for (int i = 0; i < gfx->num_path_segments; i++) { + int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int charsWritten = snprintf(coordinates + offset, totalSize - offset, "%i %i ", x, y); if (charsWritten >= 0) { offset += charsWritten; } else { break; } } + // Remove the trailing space if (offset > 0) { coordinates[offset - 1] = '\0'; @@ -921,6 +954,8 @@ static int fill_path(lua_State* L) { pdgui_vmess(0, "crr r ri rs rS", cnv, "create", "polygon", coordinates, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); + freebytes(coordinates, totalSize+1); + return 0; } From 6a8b66dc3fcb8ea1c4fde9ac9ad7ea39e5c627e8 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 18:01:30 +0100 Subject: [PATCH 11/62] Improved drawing mechanism for plugdata --- pdlua.c | 4 +++- pdlua.h | 3 +++ pdlua_gfx.h | 60 +++++++++++++++++++++++++++++------------------------ 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/pdlua.c b/pdlua.c index 60f1ae0..026c037 100644 --- a/pdlua.c +++ b/pdlua.c @@ -46,7 +46,6 @@ #include #include - #include "pdlua.h" #include "s_stuff.h" // for sys_register_loader() @@ -867,6 +866,9 @@ static int pdlua_object_new(lua_State *L) o->gfx.translate_y = 0; o->gfx.mouse_x = 0; o->gfx.mouse_y = 0; +#else + o->gfx.plugdata_draw_callback = NULL; + o->gfx.plugdata_callback_target = NULL; #endif lua_pushlightuserdata(L, o); diff --git a/pdlua.h b/pdlua.h index 31594ff..70482f2 100644 --- a/pdlua.h +++ b/pdlua.h @@ -21,6 +21,9 @@ typedef struct _pdlua_gfx t_symbol* mouse_proxy_sym; void* mouse_proxy; +#else + void(*plugdata_draw_callback)(void*, t_symbol*, int, t_atom*); + void* plugdata_callback_target; #endif int width, height; } t_pdlua_gfx; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index a79009a..c1cf45c 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -187,7 +187,13 @@ int pdlua_gfx_setup(lua_State* L) { } #if PLUGDATA -void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); + +static inline void plugdata_draw(t_pdlua* obj, t_symbol* sym, int argc, t_atom* argv) +{ + if(obj->gfx.plugdata_callback_target) { + obj->gfx.plugdata_draw_callback(obj->gfx.plugdata_callback_target, sym, argc, argv); + } +} void pdlua_gfx_clear(t_pdlua* obj) { } @@ -207,19 +213,19 @@ static int set_size(lua_State* L) t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // w SETFLOAT(args + 1, luaL_checknumber(L, 2)); // h - plugdata_forward_message(obj, gensym("lua_resized"), 2, args); + plugdata_draw(obj, gensym("lua_resized"), 2, args); return 0; } static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_start_paint"), 0, NULL); + plugdata_draw(obj, gensym("lua_start_paint"), 0, NULL); return 0; } static int end_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_end_paint"), 0, NULL); + plugdata_draw(obj, gensym("lua_end_paint"), 0, NULL); return 0; } @@ -230,7 +236,7 @@ static int set_color(lua_State* L) { SETFLOAT(args + 1, luaL_checknumber(L, 2)); // g SETFLOAT(args + 2, luaL_checknumber(L, 3)); // b SETFLOAT(args + 3, luaL_optnumber(L, 4, 1.0)); // a (optional, default to 1.0) - plugdata_forward_message(obj, gensym("lua_set_color"), 4, args); + plugdata_draw(obj, gensym("lua_set_color"), 4, args); return 0; } @@ -241,7 +247,7 @@ static int fill_ellipse(lua_State* L) { SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h - plugdata_forward_message(obj, gensym("lua_fill_ellipse"), 4, args); + plugdata_draw(obj, gensym("lua_fill_ellipse"), 4, args); return 0; } @@ -253,13 +259,13 @@ static int stroke_ellipse(lua_State* L) { SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h SETFLOAT(args + 4, luaL_checknumber(L, 5)); // width - plugdata_forward_message(obj, gensym("lua_stroke_ellipse"), 4, args); + plugdata_draw(obj, gensym("lua_stroke_ellipse"), 4, args); return 0; } static int fill_all(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_fill_all"), 0, NULL); + plugdata_draw(obj, gensym("lua_fill_all"), 0, NULL); return 0; } @@ -270,7 +276,7 @@ static int fill_rect(lua_State* L) { SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h - plugdata_forward_message(obj, gensym("lua_fill_rect"), 4, args); + plugdata_draw(obj, gensym("lua_fill_rect"), 4, args); return 0; } @@ -282,7 +288,7 @@ static int stroke_rect(lua_State* L) { SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h SETFLOAT(args + 4, luaL_checknumber(L, 5)); // corner_radius - plugdata_forward_message(obj, gensym("lua_stroke_rect"), 5, args); + plugdata_draw(obj, gensym("lua_stroke_rect"), 5, args); return 0; } @@ -293,7 +299,7 @@ static int fill_rounded_rect(lua_State* L) { SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h - plugdata_forward_message(obj, gensym("lua_fill_rounded_rect"), 4, args); + plugdata_draw(obj, gensym("lua_fill_rounded_rect"), 4, args); return 0; } @@ -306,7 +312,7 @@ static int stroke_rounded_rect(lua_State* L) { SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h SETFLOAT(args + 4, luaL_checknumber(L, 5)); // corner_radius SETFLOAT(args + 5, luaL_checknumber(L, 6)); // width - plugdata_forward_message(obj, gensym("lua_stroke_rounded_rect"), 6, args); + plugdata_draw(obj, gensym("lua_stroke_rounded_rect"), 6, args); return 0; } @@ -319,7 +325,7 @@ static int draw_text(lua_State* L) { SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y SETFLOAT(args + 3, luaL_checknumber(L, 4)); // w SETFLOAT(args + 4, luaL_checknumber(L, 5)); // h - plugdata_forward_message(obj, gensym("lua_text"), 5, args); + plugdata_draw(obj, gensym("lua_text"), 5, args); return 0; } @@ -328,7 +334,7 @@ static int start_path(lua_State* L) { t_atom args[2]; SETFLOAT(args , luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y - plugdata_forward_message(obj, gensym("lua_start_path"), 2, args); + plugdata_draw(obj, gensym("lua_start_path"), 2, args); return 0; } @@ -337,7 +343,7 @@ static int line_to(lua_State* L) { t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y - plugdata_forward_message(obj, gensym("lua_line_to"), 2, args); + plugdata_draw(obj, gensym("lua_line_to"), 2, args); return 0; } @@ -348,9 +354,9 @@ static int quad_to(lua_State* L) { SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y1 SETFLOAT(args + 2, luaL_checknumber(L, 3)); // x2 SETFLOAT(args + 3, luaL_checknumber(L, 4)); // y2 - + // Forward the message to the appropriate function - plugdata_forward_message(obj, gensym("lua_quad_to"), 4, args); + plugdata_draw(obj, gensym("lua_quad_to"), 4, args); return 0; } @@ -363,15 +369,15 @@ static int cubic_to(lua_State* L) { SETFLOAT(args + 3, luaL_checknumber(L, 4)); // y2 SETFLOAT(args + 4, luaL_checknumber(L, 5)); // x3 SETFLOAT(args + 5, luaL_checknumber(L, 6)); // y3 - + // Forward the message to the appropriate function - plugdata_forward_message(obj, gensym("lua_cubic_to"), 6, args); + plugdata_draw(obj, gensym("lua_cubic_to"), 6, args); return 0; } static int close_path(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_close_path"), 0, NULL); + plugdata_draw(obj, gensym("lua_close_path"), 0, NULL); return 0; } @@ -380,13 +386,13 @@ static int stroke_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_atom arg; SETFLOAT(&arg, luaL_checknumber(L, 1)); // line thickness - plugdata_forward_message(obj, gensym("lua_stroke_path"), 1, &arg); + plugdata_draw(obj, gensym("lua_stroke_path"), 1, &arg); return 0; } static int fill_path(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_fill_path"), 0, NULL); + plugdata_draw(obj, gensym("lua_fill_path"), 0, NULL); return 0; } @@ -395,7 +401,7 @@ static int translate(lua_State* L) { t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // tx SETFLOAT(args + 1, luaL_checknumber(L, 2)); // ty - plugdata_forward_message(obj, gensym("lua_translate"), 2, args); + plugdata_draw(obj, gensym("lua_translate"), 2, args); return 0; } @@ -404,13 +410,13 @@ static int scale(lua_State* L) { t_atom args[2]; SETFLOAT(args, luaL_checknumber(L, 1)); // sx SETFLOAT(args + 1, luaL_checknumber(L, 2)); // sy - plugdata_forward_message(obj, gensym("lua_scale"), 2, args); + plugdata_draw(obj, gensym("lua_scale"), 2, args); return 0; } static int reset_transform(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_forward_message(obj, gensym("lua_reset_transform"), 0, NULL); + plugdata_draw(obj, gensym("lua_reset_transform"), 0, NULL); return 0; } #else @@ -778,7 +784,7 @@ static int quad_to(lua_State* L) { // Get the last point float t = 0.0; - const float resolution = 1000; + const float resolution = 100; while (t <= 1.0) { t += 1.0 / resolution; if (gfx->num_path_segments < 4096) { @@ -813,7 +819,7 @@ static int cubic_to(lua_State* L) { // Get the last point float t = 0.0; - const float resolution = 1000; + const float resolution = 100; while (t <= 1.0) { t += 1.0 / resolution; if (gfx->num_path_segments < 4096) { From 336d3011785745bf4ac6dfa8489e17947cf4d101 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 20:24:45 +0100 Subject: [PATCH 12/62] Remove limit on number of path segments, fixed mouse_move implementation for pd, fixed memory leaks --- pdlua.c | 3 + pdlua.h | 3 +- pdlua_gfx.h | 175 +++++++++++++++++++++++----------------------------- 3 files changed, 83 insertions(+), 98 deletions(-) diff --git a/pdlua.c b/pdlua.c index 026c037..e181649 100644 --- a/pdlua.c +++ b/pdlua.c @@ -866,6 +866,7 @@ static int pdlua_object_new(lua_State *L) o->gfx.translate_y = 0; o->gfx.mouse_x = 0; o->gfx.mouse_y = 0; + o->gfx.mouse_up = 1; #else o->gfx.plugdata_draw_callback = NULL; o->gfx.plugdata_callback_target = NULL; @@ -1140,6 +1141,8 @@ static int pdlua_object_free(lua_State *L) if (lua_islightuserdata(L, 1)) { t_pdlua *o = lua_touserdata(L, 1); + gfx_free(&o->gfx); + if (o) { if (o->in) free(o->in); diff --git a/pdlua.h b/pdlua.h index 70482f2..a727b77 100644 --- a/pdlua.h +++ b/pdlua.h @@ -10,8 +10,9 @@ typedef struct _pdlua_gfx char active_tags[128][128]; int num_tags; - int path_segments[4096][2]; + int* path_segments; int num_path_segments; + int num_path_segments_allocated; int path_start_x, path_start_y; int mouse_x, mouse_y, mouse_up; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index c1cf45c..0ebfd99 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -1,4 +1,7 @@ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define PDLUA_OBJECT_REGISTRTY_ID 512 + // Functions that need to be implemented separately for each Pd flavour static int gfx_initialize(t_pdlua *obj); @@ -33,10 +36,10 @@ static int reset_transform(lua_State* L); void pdlua_gfx_clear(t_pdlua *obj); typedef struct _pdlua_mouse_proxy{ - t_object p_obj; - t_symbol *p_sym; - t_clock *p_clock; - t_pdlua *p_parent; + t_object object; + t_symbol *bind_sym; + t_clock *free_clock; + t_pdlua *parent; }t_pdlua_mouse_proxy; void pdlua_gfx_repaint(t_pdlua *o) { @@ -54,6 +57,7 @@ void pdlua_gfx_repaint(t_pdlua *o) { } 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); @@ -61,6 +65,12 @@ void pdlua_gfx_mouse_event(t_pdlua *o, int x, int y, int type) { lua_pushinteger(__L, y); lua_pushinteger(__L, type); + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L, o); + lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L, 1); + if (lua_pcall(__L, 4, 0, 0)) { pd_error(o, "lua: error in mouseevent:\n%s", lua_tostring(__L, -1)); @@ -85,8 +95,6 @@ void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 3); } -#define PDLUA_OBJECT_REGISTRTY_ID 512 - static t_pdlua* get_current_object(lua_State* L) { lua_pushvalue(L, LUA_REGISTRYINDEX); @@ -142,32 +150,32 @@ t_class* pdlua_mouse_proxy_class; static void pdlua_mouse_proxy_any(t_pdlua_mouse_proxy *p, t_symbol*s, int ac, t_atom *av){ - if(s == gensym("motion") && ! p->p_parent->gfx.mouse_up) + if(s == gensym("motion") && p->parent->gfx.mouse_up) { - t_canvas *cnv = glist_getcanvas(p->p_parent->canvas); + t_canvas *cnv = glist_getcanvas(p->parent->canvas); int zoom = glist_getzoom(cnv); int x = (int)(av->a_w.w_float / zoom); int y = (int)((av+1)->a_w.w_float / zoom); - x -= text_xpix(p->p_parent, cnv); - y -= text_ypix(p->p_parent, cnv); + x -= text_xpix(p->parent, cnv); + y -= text_ypix(p->parent, cnv); - if(x > 0 && y > 0 && x < p->p_parent->gfx.width && y < p->p_parent->gfx.height) { - pdlua_gfx_mouse_move(p->p_parent, x, y); + if(x > 0 && y > 0 && x < p->parent->gfx.width && y < p->parent->gfx.height) { + pdlua_gfx_mouse_move(p->parent, x, y); } } } static void pdlua_mouse_proxy_free(t_pdlua_mouse_proxy *p){ - pd_unbind(&p->p_obj.ob_pd, p->p_sym); - //clock_free(p->p_clock); - pd_free(&p->p_obj.ob_pd); + pd_unbind(&p->object.ob_pd, p->bind_sym); + clock_free(p->free_clock); + pd_free(&p->object.ob_pd); } static t_pdlua_mouse_proxy *pdlua_mouse_proxy_new(t_pdlua *x, t_symbol *s){ t_pdlua_mouse_proxy *p = (t_pdlua_mouse_proxy*)pd_new(pdlua_mouse_proxy_class); - p->p_parent = x; - pd_bind(&p->p_obj.ob_pd, p->p_sym = s); - //p->p_clock = clock_new(p, (t_method)pdlua_mouse_proxy_free); + p->parent = x; + pd_bind(&p->object.ob_pd, p->bind_sym = s); + p->free_clock = clock_new(p, (t_method)pdlua_mouse_proxy_free); return(p); } #endif @@ -205,6 +213,10 @@ static int gfx_initialize(t_pdlua* obj) return 0; } +static void gfx_free(t_pdlua_gfx* gfx) +{ +} + static int set_size(lua_State* L) { t_pdlua* obj = get_current_object(L); @@ -421,12 +433,10 @@ static int reset_transform(lua_State* L) { } #else -static int max(int a, int b) { - return (a > b) ? a : b; -} - -static int min(int a, int b) { - return (a < b) ? a : b; +static void gfx_free(t_pdlua_gfx* gfx) +{ + freebytes(gfx->path_segments, gfx->num_path_segments_allocated * sizeof(int)); + clock_delay(((t_pdlua_mouse_proxy*)gfx->mouse_proxy)->free_clock, 0); } void pdlua_gfx_clear(t_pdlua *obj) { @@ -447,13 +457,7 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x int y = luaL_checknumber(L, 2); int w = luaL_checknumber(L, 3); int h = luaL_checknumber(L, 4); - - // Calculate the clipped rectangle - x = min(max(x, 0), gfx->width - w); - y = min(max(y, 0), gfx->height - h); - w = min(x + w, gfx->width) - x; - h = min(y + h, gfx->height) - y; - + x *= gfx->scale_x * glist_getzoom(cnv); y *= gfx->scale_y * glist_getzoom(cnv); w *= gfx->scale_x * glist_getzoom(cnv); @@ -697,11 +701,6 @@ static int draw_text(lua_State* L) { int w = luaL_checknumber(L, 4); int fontHeight = luaL_checknumber(L, 5); - // Calculate the clipped rectangle - x = min(max(x, 0), gfx->width - w); - y = min(max(y, 0), gfx->height); - w = min(x + w, gfx->width) - x; - int zoom = glist_getzoom(cnv); x *= gfx->scale_x * zoom; y *= gfx->scale_y * zoom; @@ -736,6 +735,17 @@ static int draw_text(lua_State* L) { return 0; } +static void add_path_segment(t_pdlua_gfx* gfx, int x, int y) +{ + int path_segment_space = (gfx->num_path_segments + 1) * 2; + gfx->path_segments = (int*)resizebytes(gfx->path_segments, gfx->num_path_segments_allocated * sizeof(int), MAX((path_segment_space + 1), gfx->num_path_segments_allocated) * sizeof(int)); + gfx->num_path_segments_allocated = path_segment_space; + + gfx->path_segments[gfx->num_path_segments * 2] = x; + gfx->path_segments[gfx->num_path_segments * 2 + 1] = y; + gfx->num_path_segments++; +} + static int start_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; @@ -744,29 +754,20 @@ static int start_path(lua_State* L) { gfx->path_start_x = luaL_checknumber(L, 1); gfx->path_start_y = luaL_checknumber(L, 2); - gfx->path_segments[gfx->num_path_segments][0] = gfx->path_start_x; - gfx->path_segments[gfx->num_path_segments][1] = gfx->path_start_y; - gfx->num_path_segments++; + add_path_segment(gfx, gfx->path_start_x, gfx->path_start_y); return 0; } + + // Function to add a line to the current path static int line_to(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - - if(gfx->num_path_segments >= 4096) { - luaL_error(L, "Too many path segments"); - return 0; - } - + int x = luaL_checknumber(L, 1); int y = luaL_checknumber(L, 2); - - gfx->path_segments[gfx->num_path_segments][0] = x; - gfx->path_segments[gfx->num_path_segments][1] = y; - gfx->num_path_segments++; - + add_path_segment(gfx, x, y); return 0; } @@ -779,26 +780,19 @@ static int quad_to(lua_State* L) { int x3 = luaL_checknumber(L, 3); int y3 = luaL_checknumber(L, 4); - int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][0] : x2; - int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][1] : y2; + int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2] : x2; + int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2 + 1] : y2; // Get the last point float t = 0.0; const float resolution = 100; while (t <= 1.0) { t += 1.0 / resolution; - if (gfx->num_path_segments < 4096) { - // 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; - - gfx->path_segments[gfx->num_path_segments][0] = x; - gfx->path_segments[gfx->num_path_segments][1] = y; - gfx->num_path_segments++; - } else { - luaL_error(L, "Too many path segments"); - return 0; - } + + // 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; + add_path_segment(gfx, x, y); } return 0; @@ -814,27 +808,20 @@ static int cubic_to(lua_State* L) { int x4 = luaL_checknumber(L, 5); int y4 = luaL_checknumber(L, 6); - int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][0] : x2; - int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[gfx->num_path_segments - 1][1] : y2; + int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2] : x2; + int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2 + 1] : y2; // Get the last point float t = 0.0; const float resolution = 100; while (t <= 1.0) { t += 1.0 / resolution; - if (gfx->num_path_segments < 4096) { - - 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; - - // Calculate bezier curve as points (source: https://en.wikipedia.org/wiki/B%C3%A9zier_curve) - gfx->path_segments[gfx->num_path_segments][0] = x; - gfx->path_segments[gfx->num_path_segments][1] = y; - gfx->num_path_segments++; - } else { - luaL_error(L, "Too many path segments"); - return 0; - } + + // 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; + + add_path_segment(gfx, x, y); } return 0; @@ -844,13 +831,7 @@ static int cubic_to(lua_State* L) { static int close_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - - if(gfx->num_path_segments >= 4096) return 0; - - gfx->path_segments[gfx->num_path_segments][0] = gfx->path_start_x; - gfx->path_segments[gfx->num_path_segments][1] = gfx->path_start_y; - gfx->num_path_segments++; - + add_path_segment(gfx, gfx->path_start_x, gfx->path_start_y); return 0; } @@ -865,22 +846,22 @@ static int stroke_path(lua_State* L) { int obj_x = text_xpix(obj, cnv); int obj_y = text_ypix(obj, cnv); for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; - + int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; + x *= gfx->scale_x * glist_getzoom(cnv); y *= gfx->scale_y * glist_getzoom(cnv); x += gfx->translate_x + obj_x; y += gfx->translate_y + obj_y; - gfx->path_segments[i][0] = x; - gfx->path_segments[i][1] = y; + gfx->path_segments[i * 2] = x; + gfx->path_segments[i * 2 + 1] = y; } int totalSize = 0; // Determine the total size needed for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; // Calculate size for x and y totalSize += snprintf(NULL, 0, "%i %i ", x, y); } @@ -888,7 +869,7 @@ static int stroke_path(lua_State* L) { int offset = 0; for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; int charsWritten = snprintf(coordinates + offset, totalSize - offset, "%i %i ", x, y); if (charsWritten >= 0) { offset += charsWritten; @@ -919,7 +900,7 @@ static int fill_path(lua_State* L) { int obj_x = text_xpix(obj, cnv); int obj_y = text_ypix(obj, cnv); for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; x *= gfx->scale_x * glist_getzoom(cnv); y *= gfx->scale_y * glist_getzoom(cnv); @@ -927,14 +908,14 @@ static int fill_path(lua_State* L) { x += gfx->translate_x + obj_x; y += gfx->translate_y + obj_y; - gfx->path_segments[i][0] = x; - gfx->path_segments[i][1] = y; + gfx->path_segments[i * 2] = x; + gfx->path_segments[i * 2 + 1] = y; } int totalSize = 0; // Determine the total size needed for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; // Calculate size for x and y totalSize += snprintf(NULL, 0, "%i %i ", x, y); } @@ -942,7 +923,7 @@ static int fill_path(lua_State* L) { int offset = 0; for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i][0], y = gfx->path_segments[i][1]; + int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; int charsWritten = snprintf(coordinates + offset, totalSize - offset, "%i %i ", x, y); if (charsWritten >= 0) { offset += charsWritten; From 3e0cd722416bc1e8790ac865deca9fc3635a73e9 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 21:02:13 +0100 Subject: [PATCH 13/62] Simplified mouse event implementation for pd-vanilla --- pdlua.c | 58 ++++++++++++++++++++------------ pdlua.h | 4 +-- pdlua_gfx.h | 97 +++++++++++++++++++++-------------------------------- 3 files changed, 78 insertions(+), 81 deletions(-) diff --git a/pdlua.c b/pdlua.c index e181649..9a78837 100644 --- a/pdlua.c +++ b/pdlua.c @@ -594,33 +594,49 @@ static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, { #if !PLUGDATA t_pdlua *x = (t_pdlua *)z; - x->gfx.mouse_x = x->gfx.mouse_x + dx; - x->gfx.mouse_y = x->gfx.mouse_y + dy; + x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx; + x->gfx.mouse_drag_y = x->gfx.mouse_drag_y + dy; - if (up) + if (!up) { - if(!x->gfx.mouse_up) - { - pdlua_gfx_mouse_up((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); - } - } - else { - if(x->gfx.mouse_up) - { - pdlua_gfx_mouse_down((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); - } - pdlua_gfx_mouse_drag((t_object*)x, x->gfx.mouse_x, x->gfx.mouse_y); + pdlua_gfx_mouse_drag((t_object*)x, x->gfx.mouse_drag_x - text_xpix(&x->pd, x->canvas), x->gfx.mouse_drag_y - text_ypix(&x->pd, x->canvas)); } - - x->gfx.mouse_up = up; #endif } static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){ alt = dbl = 0; // remove warning - if(doit){ - glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, (t_glistkeyfn)pdlua_key, xpos, ypos); + if(x->has_gui) + { + int xpix = xpos - text_xpix(&x->pd, gl); + int ypix = ypos - text_ypix(&x->pd, gl); + + if(doit){ + if(x->gfx.mouse_down) + { + pdlua_gfx_mouse_down((t_object*)x, xpix, ypix); + } + + x->gfx.mouse_drag_x = xpos; + x->gfx.mouse_drag_y = ypos; + + glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, (t_glistkeyfn)pdlua_key, xpos, ypos); + } + else { + pdlua_gfx_mouse_move(x, xpix, ypix); + + if(!x->gfx.mouse_down) + { + pdlua_gfx_mouse_up((t_object*)x, xpix, ypix); + } + } + + x->gfx.mouse_down = doit; } + else { + text_widgetbehavior.w_clickfn(x, gl, xpos, ypos, shift, alt, dbl, doit); + } + return(1); } @@ -864,9 +880,9 @@ static int pdlua_object_new(lua_State *L) o->gfx.scale_y = 1.0f; o->gfx.translate_x = 0; o->gfx.translate_y = 0; - o->gfx.mouse_x = 0; - o->gfx.mouse_y = 0; - o->gfx.mouse_up = 1; + o->gfx.mouse_drag_x = 0; + o->gfx.mouse_drag_y = 0; + o->gfx.mouse_down = 1; #else o->gfx.plugdata_draw_callback = NULL; o->gfx.plugdata_callback_target = NULL; diff --git a/pdlua.h b/pdlua.h index a727b77..fd8dca9 100644 --- a/pdlua.h +++ b/pdlua.h @@ -7,7 +7,7 @@ typedef struct _pdlua_gfx { #if !PLUGDATA char object_tag[128]; - char active_tags[128][128]; + char active_tags[8192][128]; int num_tags; int* path_segments; @@ -15,7 +15,7 @@ typedef struct _pdlua_gfx int num_path_segments_allocated; int path_start_x, path_start_y; - int mouse_x, mouse_y, mouse_up; + int mouse_drag_x, mouse_drag_y, mouse_down; int translate_x, translate_y; float scale_x, scale_y; char current_color[8]; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 0ebfd99..07f2f64 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -35,13 +35,6 @@ static int reset_transform(lua_State* L); void pdlua_gfx_clear(t_pdlua *obj); -typedef struct _pdlua_mouse_proxy{ - t_object object; - t_symbol *bind_sym; - t_clock *free_clock; - t_pdlua *parent; -}t_pdlua_mouse_proxy; - void pdlua_gfx_repaint(t_pdlua *o) { lua_getglobal(__L, "pd"); lua_getfield (__L, -1, "_repaint"); @@ -145,52 +138,10 @@ int set_gui_callback(void* callback_target, void(*callback)(void*, t_object*)) { return 0; } -#if !PLUGDATA -t_class* pdlua_mouse_proxy_class; - -static void pdlua_mouse_proxy_any(t_pdlua_mouse_proxy *p, t_symbol*s, int ac, t_atom *av){ - - if(s == gensym("motion") && p->parent->gfx.mouse_up) - { - t_canvas *cnv = glist_getcanvas(p->parent->canvas); - int zoom = glist_getzoom(cnv); - int x = (int)(av->a_w.w_float / zoom); - int y = (int)((av+1)->a_w.w_float / zoom); - x -= text_xpix(p->parent, cnv); - y -= text_ypix(p->parent, cnv); - - if(x > 0 && y > 0 && x < p->parent->gfx.width && y < p->parent->gfx.height) { - pdlua_gfx_mouse_move(p->parent, x, y); - } - } -} - -static void pdlua_mouse_proxy_free(t_pdlua_mouse_proxy *p){ - pd_unbind(&p->object.ob_pd, p->bind_sym); - clock_free(p->free_clock); - pd_free(&p->object.ob_pd); -} - -static t_pdlua_mouse_proxy *pdlua_mouse_proxy_new(t_pdlua *x, t_symbol *s){ - t_pdlua_mouse_proxy *p = (t_pdlua_mouse_proxy*)pd_new(pdlua_mouse_proxy_class); - p->parent = x; - pd_bind(&p->object.ob_pd, p->bind_sym = s); - p->free_clock = clock_new(p, (t_method)pdlua_mouse_proxy_free); - return(p); -} -#endif - - int pdlua_gfx_setup(lua_State* L) { // Register functions with Lua luaL_newlib(L, gfx_lib); lua_setglobal(L, "gfx"); - -#if !PLUGDATA - pdlua_mouse_proxy_class = class_new(0, 0, 0, sizeof(t_pdlua_mouse_proxy), CLASS_NOINLET | CLASS_PD, 0); - class_addanything(pdlua_mouse_proxy_class, pdlua_mouse_proxy_any); -#endif - return 1; // Number of values pushed onto the stack } @@ -328,6 +279,16 @@ static int stroke_rounded_rect(lua_State* L) { return 0; } +static int draw_line(lua_State* L) { + t_atom args[5]; + SETFLOAT(args, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // line width + plugdata_draw(obj, gensym("lua_draw_line"), 5, args); +} + static int draw_text(lua_State* L) { t_pdlua* obj = get_current_object(L); const char* text = luaL_checkstring(L, 1); @@ -436,7 +397,6 @@ static int reset_transform(lua_State* L) { static void gfx_free(t_pdlua_gfx* gfx) { freebytes(gfx->path_segments, gfx->num_path_segments_allocated * sizeof(int)); - clock_delay(((t_pdlua_mouse_proxy*)gfx->mouse_proxy)->free_clock, 0); } void pdlua_gfx_clear(t_pdlua *obj) { @@ -445,8 +405,10 @@ void pdlua_gfx_clear(t_pdlua *obj) { for(int i = 0; i < gfx->num_tags; i++) { - pdgui_vmess(0, "crs", cnv, "delete", gfx->active_tags[i]); + //pdgui_vmess(0, "crs", cnv, "delete", gfx->active_tags[i]); } + + pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); gfx->num_tags = 0; } @@ -494,13 +456,6 @@ static int gfx_initialize(t_pdlua *obj) snprintf(gfx->object_tag, 128, ".x%lx", obj); gfx->object_tag[127] = '\0'; pdlua_gfx_repaint(obj); - - char buf[MAXPDSTRING]; - snprintf(buf, MAXPDSTRING-1, ".x%lx", (unsigned long)cnv); - buf[MAXPDSTRING-1] = 0; - - gfx->mouse_proxy = pdlua_mouse_proxy_new(obj, gensym(buf)); - return 0; } @@ -690,6 +645,32 @@ static int stroke_rounded_rect(lua_State* L) { return 0; } +static int draw_line(lua_State* L) { + t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + t_canvas *cnv = glist_getcanvas(obj->canvas); + + const char* text = luaL_checkstring(L, 1); // Assuming text is a string + int x1 = luaL_checknumber(L, 1); + int y1 = luaL_checknumber(L, 2); + int x2 = luaL_checknumber(L, 3); + int y2 = luaL_checknumber(L, 3); + int lineWidth = luaL_checknumber(L, 4); + + int zoom = glist_getzoom(cnv); + x1 *= gfx->scale_x * zoom; + y1 *= gfx->scale_y * zoom; + x2 *= gfx->scale_x * zoom; + y2 *= gfx->scale_y * zoom; + lineWidth *= zoom; + + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; + + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1, y1, x2, y2, + "-width", lineWidth, "-fill", gfx->current_color, "-tags", 2, tags); +} + + static int draw_text(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; From e08ed6d96b1a43deb5bc3443df258309e7c2c3d8 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 21:07:47 +0100 Subject: [PATCH 14/62] Add draw_line function --- pdlua_gfx.h | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 07f2f64..a9444a0 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -19,6 +19,7 @@ static int stroke_rect(lua_State* L); static int fill_rounded_rect(lua_State* L); static int stroke_rounded_rect(lua_State* L); +static int draw_line(lua_State* L); static int draw_text(lua_State* L); static int start_path(lua_State* L); @@ -110,7 +111,8 @@ static const luaL_Reg gfx_lib[] = { {"stroke_rect", stroke_rect}, {"fill_rounded_rect", fill_rounded_rect}, {"stroke_rounded_rect", stroke_rounded_rect}, - {"text", draw_text}, + {"draw_line", draw_line}, + {"draw_text", draw_text}, {"start_path", start_path}, {"line_to", line_to}, {"quad_to", quad_to}, @@ -280,6 +282,7 @@ static int stroke_rounded_rect(lua_State* L) { } static int draw_line(lua_State* L) { + t_pdlua* obj = get_current_object(L); t_atom args[5]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y @@ -425,11 +428,8 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x w *= gfx->scale_x * glist_getzoom(cnv); h *= gfx->scale_y * glist_getzoom(cnv); - x += gfx->translate_x; - y += gfx->translate_y; - - x += text_xpix(obj, cnv); - y += text_ypix(obj, cnv); + x += gfx->translate_x + text_xpix(obj, cnv); + y += gfx->translate_y + text_ypix(obj, cnv); *x1 = x; *y1 = y; @@ -650,18 +650,23 @@ static int draw_line(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - const char* text = luaL_checkstring(L, 1); // Assuming text is a string int x1 = luaL_checknumber(L, 1); int y1 = luaL_checknumber(L, 2); int x2 = luaL_checknumber(L, 3); - int y2 = luaL_checknumber(L, 3); - int lineWidth = luaL_checknumber(L, 4); + int y2 = luaL_checknumber(L, 4); + int lineWidth = luaL_checknumber(L, 5); int zoom = glist_getzoom(cnv); x1 *= gfx->scale_x * zoom; y1 *= gfx->scale_y * zoom; x2 *= gfx->scale_x * zoom; y2 *= gfx->scale_y * zoom; + + x1 += gfx->translate_x + text_xpix(obj, cnv); + y1 += gfx->translate_y + text_ypix(obj, cnv); + x2 += gfx->translate_x + text_xpix(obj, cnv); + y2 += gfx->translate_y + text_ypix(obj, cnv); + lineWidth *= zoom; const char* tags[] = { gfx->object_tag, register_drawing(obj) }; @@ -687,11 +692,8 @@ static int draw_text(lua_State* L) { y *= gfx->scale_y * zoom; w *= gfx->scale_x * zoom; - x += gfx->translate_x; - y += gfx->translate_y; - - x += text_xpix(obj, cnv); - y += text_ypix(obj, cnv); + x += gfx->translate_x + text_xpix(obj, cnv); + y += gfx->translate_y + text_ypix(obj, cnv); // Font size and position are offset to make sure they match drawing in plugdata fontHeight *= 0.8f; From 32d0344fab7de523936b059b0ef4b47c9f28fc23 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 21:44:03 +0100 Subject: [PATCH 15/62] No limit on number of drawings, fixed missing return statement, fixed non-gui object behavior --- pdlua.c | 98 ++++++++++++++++++++++++++++------------------------- pdlua.h | 3 +- pdlua_gfx.h | 26 ++++---------- 3 files changed, 59 insertions(+), 68 deletions(-) diff --git a/pdlua.c b/pdlua.c index 9a78837..4474cbb 100644 --- a/pdlua.c +++ b/pdlua.c @@ -583,13 +583,44 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */) return; } - -static void pdlua_key(void *z, t_symbol *keysym, t_floatarg fkey){ +void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ + // If there's no gui, use default text vis behavior + if(!((t_pdlua *)z)->has_gui) + { + t_text *x = (t_text *)z; + if (vis) + { + if (gobj_shouldvis(&x->te_g, glist)) + { + t_rtext *y = glist_findrtext(glist, x); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 1); + rtext_draw(y); + } + } + else + { + t_rtext *y = glist_findrtext(glist, x); + if (gobj_shouldvis(&x->te_g, glist)) + { + text_eraseborder(x, glist, rtext_gettag(y)); + rtext_erase(y); + } + } + return; + } + // Otherwise, repaint or clear the custom graphics + if(vis) + { + pdlua_gfx_repaint((t_object*)z); + } + else { + pdlua_gfx_clear((t_object*)z); + } } - -static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, +static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { #if !PLUGDATA @@ -604,8 +635,9 @@ static void pdlua_motion(void *z, t_floatarg dx, t_floatarg dy, #endif } -static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){ - alt = dbl = 0; // remove warning +static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit){ + t_pdlua *x = (t_pdlua *)z; +#if !PLUGDATA if(x->has_gui) { int xpix = xpos - text_xpix(&x->pd, gl); @@ -620,7 +652,7 @@ static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, i x->gfx.mouse_drag_x = xpos; x->gfx.mouse_drag_y = ypos; - glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, (t_glistkeyfn)pdlua_key, xpos, ypos); + glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, NULL, xpos, ypos); } else { pdlua_gfx_mouse_move(x, xpix, ypix); @@ -637,17 +669,27 @@ static int pdlua_click(t_pdlua *x, t_glist *gl, int xpos, int ypos, int shift, i text_widgetbehavior.w_clickfn(x, gl, xpos, ypos, shift, alt, dbl, doit); } +#endif return(1); } -static void pdlua_displace(t_pdlua *z, t_glist *glist, int dx, int dy){ +static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ t_pdlua *x = (t_pdlua *)z; x->pd.te_xpix += dx, x->pd.te_ypix += dy; dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); - pdlua_gfx_repaint(z); + + pdlua_vis(z, glist, 0); + pdlua_vis(z, glist, 1); canvas_fixlinesfor(glist, (t_text*)x); } +static void pdlua_activate(t_pdlua *z, t_glist *glist, int state) +{ + if(!((t_pdlua *)z)->has_gui) + { + text_widgetbehavior.w_activatefn(z, glist, state); + } +} static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { @@ -664,41 +706,6 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp } } -void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ - if(!((t_pdlua *)z)->has_gui) - { - t_text *x = (t_text *)z; - if (vis) - { - if (gobj_shouldvis(&x->te_g, glist)) - { - t_rtext *y = glist_findrtext(glist, x); - text_drawborder(x, glist, rtext_gettag(y), - rtext_width(y), rtext_height(y), 1); - rtext_draw(y); - } - } - else - { - t_rtext *y = glist_findrtext(glist, x); - if (gobj_shouldvis(&x->te_g, glist)) - { - text_eraseborder(x, glist, rtext_gettag(y)); - rtext_erase(y); - } - } - return; - } - if(vis) - { - pdlua_gfx_repaint((t_object*)z); - } - else { - pdlua_gfx_clear((t_object*)z); - } -} - - #if 0 static void pdlua_stack_dump (lua_State *L) { @@ -826,7 +833,7 @@ static int pdlua_class_new(lua_State *L) pdlua_widgetbehavior.w_deletefn = text_widgetbehavior.w_deletefn; pdlua_widgetbehavior.w_clickfn = pdlua_click; pdlua_widgetbehavior.w_visfn = pdlua_vis; - pdlua_widgetbehavior.w_activatefn = NULL; + pdlua_widgetbehavior.w_activatefn = pdlua_activate; class_setwidget(c, &pdlua_widgetbehavior); /* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ @@ -874,7 +881,6 @@ static int pdlua_object_new(lua_State *L) o->gfx.height = 80; #if !PLUGDATA - o->gfx.num_tags = 0; o->gfx.num_path_segments = 0; o->gfx.scale_x = 1.0f; o->gfx.scale_y = 1.0f; diff --git a/pdlua.h b/pdlua.h index fd8dca9..f6634d4 100644 --- a/pdlua.h +++ b/pdlua.h @@ -7,8 +7,7 @@ typedef struct _pdlua_gfx { #if !PLUGDATA char object_tag[128]; - char active_tags[8192][128]; - int num_tags; + char current_paint_tag[128]; int* path_segments; int num_path_segments; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index a9444a0..b1c1126 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -290,6 +290,8 @@ 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(obj, gensym("lua_draw_line"), 5, args); + + return 0; } static int draw_text(lua_State* L) { @@ -405,14 +407,7 @@ static void gfx_free(t_pdlua_gfx* gfx) void pdlua_gfx_clear(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - - for(int i = 0; i < gfx->num_tags; i++) - { - //pdgui_vmess(0, "crs", cnv, "delete", gfx->active_tags[i]); - } - pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); - gfx->num_tags = 0; } static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x1, int* y1, int* x2, int* y2) { @@ -441,10 +436,9 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x static const char* register_drawing(t_pdlua *object) { t_pdlua_gfx *gfx = &object->gfx; - snprintf(gfx->active_tags[gfx->num_tags], 128, ".x%lx", rand()); - gfx->active_tags[gfx->num_tags][127] = '\0'; - - return gfx->active_tags[gfx->num_tags++]; + snprintf(gfx->current_paint_tag, 128, ".x%lx", rand()); + gfx->current_paint_tag[127] = '\0'; + return gfx->current_paint_tag; } @@ -470,15 +464,7 @@ static int set_size(lua_State* L) static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_canvas *cnv = glist_getcanvas(obj->canvas); - t_pdlua_gfx *gfx = &obj->gfx; - - // Delete drawings made previously - for(int i = 0; i < gfx->num_tags; i++) - { - pdgui_vmess(0, "crs", cnv, "delete", gfx->active_tags[i]); - } - gfx->num_tags = 0; + pdlua_gfx_clear(obj); return 0; } From c6a05f70fc5538ee0aa94dc7a008dbba1e018f81 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 21 Dec 2023 22:12:02 +0100 Subject: [PATCH 16/62] Initialization fixes --- pd.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pd.lua b/pd.lua index 3f794e1..7c97f93 100644 --- a/pd.lua +++ b/pd.lua @@ -307,6 +307,7 @@ function pd.Class:construct(sel, atoms) self._object = pd._create(self._class) self.inlets = 0 self.outlets = 0 + self.gui = 0 self._canvaspath = pd._canvaspath(self._object) .. "/" if self:initialize(sel, atoms) then pd._createinlets(self._object, self.inlets) @@ -437,6 +438,7 @@ function luax:initialize(sel, atoms) -- motivation: pd-list 2007-09-23 -- create a dummy object, which can still be clicked for help self.inlets = 0 self.outlets = 0 + self.gui = 0 self._scriptname = "" return true end From dd6c14125ace0da4efcfeb8b7fc7346f77f2c6e5 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 02:25:59 +0100 Subject: [PATCH 17/62] Simplification for plugdata gui registration, fixed repaint bug --- pdlua.c | 18 +++++++++++++++--- pdlua_gfx.h | 24 +++++++++++------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/pdlua.c b/pdlua.c index 4474cbb..a85f4b8 100644 --- a/pdlua.c +++ b/pdlua.c @@ -241,7 +241,7 @@ static int pdlua_loader_legacy (t_canvas *canvas, char *name); __declspec(dllexport) #endif #ifdef PLUGDATA -void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void* callback_target, void(*register_gui_callback)(void*, t_object*)); +void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_gui_callback)(t_object*)); #else void pdlua_setup (void); #endif @@ -743,6 +743,12 @@ static void pdlua_stack_dump (lua_State *L) Wilkes' Pd-L2Ork variant and access to the GUI uses JavaScript. */ static void (*nw_gui_vmess)(const char *sel, char *fmt, ...) = NULL; +/* plugdata support. Similarly, if we're running inside plugdata, we can send GUI messages with plugdata_forward_message + This allows opening an in-gui text editor instead of opening another app + */ +#if PLUGDATA +void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); +#endif /** a handler for the open item in the right-click menu (mrpeach 20111025) */ /** Here we find the lua code for the object and open it in an editor */ static void pdlua_menu_open(t_pdlua *o) @@ -799,10 +805,16 @@ static void pdlua_menu_open(t_pdlua *o) #else logpost(NULL, 3, "Opening %s for editing", pathname); #endif +#if PLUGDATA + t_atom arg; + SETSYMBOL(&arg, gensym(pathname)); + plugdata_forward_message(o, gensym("open_textfile"), 1, &arg); +#else if (nw_gui_vmess) nw_gui_vmess("open_textfile", "s", pathname); else sys_vgui("::pd_menucommands::menu_openfile {%s}\n", pathname); +#endif } PDLUA_DEBUG("pdlua_menu_open end. stack top is %d", lua_gettop(__L)); } @@ -2059,7 +2071,7 @@ static int pdlua_loader_pathwise __declspec(dllexport) #endif #ifdef PLUGDATA -void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void* callback_target, void(*register_gui_callback)(void*, t_object*)) +void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_gui_callback)(t_object*)) #else void pdlua_setup(void) #endif @@ -2203,7 +2215,7 @@ void pdlua_setup(void) pdlua_gfx_setup(__L); #if PLUGDATA - set_gui_callback(callback_target, register_gui_callback); + pdlua_gfx_register_gui = register_gui_callback; #endif PDLUA_DEBUG("pdlua_setup: end. stack top %d", lua_gettop(__L)); diff --git a/pdlua_gfx.h b/pdlua_gfx.h index b1c1126..ea57d6b 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -41,6 +41,13 @@ void pdlua_gfx_repaint(t_pdlua *o) { lua_getfield (__L, -1, "_repaint"); lua_pushlightuserdata(__L, o); + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L, o); + lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L, 1); + + if (lua_pcall(__L, 1, 0, 0)) { pd_error(o, "lua: error in repaint:\n%s", lua_tostring(__L, -1)); @@ -129,16 +136,8 @@ static const luaL_Reg gfx_lib[] = { {NULL, NULL} // Sentinel to end the list }; - -void* gui_target; -void(*register_gui)(void*, t_object*); - - -int set_gui_callback(void* callback_target, void(*callback)(void*, t_object*)) { - gui_target = callback_target; - register_gui = callback; - return 0; -} +// Hook to inform plugdata which class names are lua GUIs +void(*pdlua_gfx_register_gui)(t_object*); int pdlua_gfx_setup(lua_State* L) { // Register functions with Lua @@ -161,7 +160,7 @@ void pdlua_gfx_clear(t_pdlua* obj) { static int gfx_initialize(t_pdlua* obj) { - register_gui(gui_target, obj); + pdlua_gfx_register_gui(obj); pdlua_gfx_repaint(obj); return 0; } @@ -303,7 +302,7 @@ static int draw_text(lua_State* L) { SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y SETFLOAT(args + 3, luaL_checknumber(L, 4)); // w SETFLOAT(args + 4, luaL_checknumber(L, 5)); // h - plugdata_draw(obj, gensym("lua_text"), 5, args); + plugdata_draw(obj, gensym("lua_draw_text"), 5, args); return 0; } @@ -359,7 +358,6 @@ static int close_path(lua_State* L) { return 0; } - static int stroke_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_atom arg; From 5673a53917d534f076c3f9c62cdcece8d2c688be Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 13:36:00 +0100 Subject: [PATCH 18/62] Small bugfix --- pdlua_gfx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index ea57d6b..c2c1948 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -217,13 +217,13 @@ static int fill_ellipse(lua_State* L) { static int stroke_ellipse(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom args[4]; + t_atom args[5]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h SETFLOAT(args + 4, luaL_checknumber(L, 5)); // width - plugdata_draw(obj, gensym("lua_stroke_ellipse"), 4, args); + plugdata_draw(obj, gensym("lua_stroke_ellipse"), 5, args); return 0; } From 88bc5dc8cf7a5a215f8bf6a1262591bf6a88c8a3 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 13:37:26 +0100 Subject: [PATCH 19/62] Another small bugfix --- pdlua_gfx.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index c2c1948..ef96d4e 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -258,12 +258,13 @@ static int stroke_rect(lua_State* L) { static int fill_rounded_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom args[4]; + t_atom args[5]; SETFLOAT(args, luaL_checknumber(L, 1)); // x SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y SETFLOAT(args + 2, luaL_checknumber(L, 3)); // w SETFLOAT(args + 3, luaL_checknumber(L, 4)); // h - plugdata_draw(obj, gensym("lua_fill_rounded_rect"), 4, args); + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // corner radius + plugdata_draw(obj, gensym("lua_fill_rounded_rect"), 5, args); return 0; } From 03e462c76676a2e55da579ee38a4a9dc84c1c7d9 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 16:05:09 +0100 Subject: [PATCH 20/62] Mouse event fixes for pd --- pdlua.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/pdlua.c b/pdlua.c index a85f4b8..a143478 100644 --- a/pdlua.c +++ b/pdlua.c @@ -644,33 +644,31 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in int ypix = ypos - text_ypix(&x->pd, gl); if(doit){ - if(x->gfx.mouse_down) + if(!x->gfx.mouse_down) { pdlua_gfx_mouse_down((t_object*)x, xpix, ypix); + x->gfx.mouse_drag_x = xpos; + x->gfx.mouse_drag_y = ypos; } - x->gfx.mouse_drag_x = xpos; - x->gfx.mouse_drag_y = ypos; - glist_grab(x->canvas, &x->pd.te_g, (t_glistmotionfn)pdlua_motion, NULL, xpos, ypos); } else { pdlua_gfx_mouse_move(x, xpix, ypix); - if(!x->gfx.mouse_down) + if(x->gfx.mouse_down) { pdlua_gfx_mouse_up((t_object*)x, xpix, ypix); } } x->gfx.mouse_down = doit; + return 1; } - else { - text_widgetbehavior.w_clickfn(x, gl, xpos, ypos, shift, alt, dbl, doit); - } + + return text_widgetbehavior.w_clickfn(x, gl, xpos, ypos, shift, alt, dbl, doit); #endif - return(1); } static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ @@ -900,7 +898,7 @@ static int pdlua_object_new(lua_State *L) o->gfx.translate_y = 0; o->gfx.mouse_drag_x = 0; o->gfx.mouse_drag_y = 0; - o->gfx.mouse_down = 1; + o->gfx.mouse_down = 0; #else o->gfx.plugdata_draw_callback = NULL; o->gfx.plugdata_callback_target = NULL; @@ -1244,6 +1242,13 @@ static void pdlua_receivedispatch lua_pushlightuserdata(__L, r); lua_pushstring(__L, s->s_name); pdlua_pushatomtable(argc, argv); + + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L, r->owner); + lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L, 1); + if (lua_pcall(__L, 3, 0, 0)) { pd_error(r->owner, "lua: error in receive dispatcher:\n%s", lua_tostring(__L, -1)); @@ -1262,6 +1267,13 @@ static void pdlua_clockdispatch( t_pdlua_proxyclock *clock) lua_getglobal(__L, "pd"); lua_getfield (__L, -1, "_clockdispatch"); lua_pushlightuserdata(__L, clock); + + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L, clock->owner); + lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L, 1); + if (lua_pcall(__L, 1, 0, 0)) { pd_error(clock->owner, "lua: error in clock dispatcher:\n%s", lua_tostring(__L, -1)); From 65b92b17f491f00adcee00ad1a2ce3c791198a25 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 16:26:45 +0100 Subject: [PATCH 21/62] pd-vanilla drawing fixes --- pdlua_gfx.h | 98 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index ef96d4e..e24d780 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -417,18 +417,18 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x int w = luaL_checknumber(L, 3); int h = luaL_checknumber(L, 4); - x *= gfx->scale_x * glist_getzoom(cnv); - y *= gfx->scale_y * glist_getzoom(cnv); - w *= gfx->scale_x * glist_getzoom(cnv); - h *= gfx->scale_y * glist_getzoom(cnv); + x *= gfx->scale_x; + y *= gfx->scale_y; + w *= gfx->scale_x; + h *= gfx->scale_y; x += gfx->translate_x + text_xpix(obj, cnv); y += gfx->translate_y + text_ypix(obj, cnv); - *x1 = x; - *y1 = y; - *x2 = x + w; - *y2 = y + h; + *x1 = x * glist_getzoom(cnv); + *y1 = y * glist_getzoom(cnv); + *x2 = (x + w) * glist_getzoom(cnv); + *y2 = (y + h) * glist_getzoom(cnv); } @@ -508,9 +508,11 @@ static int stroke_ellipse(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); + int line_width = luaL_checknumber(L, 5) * glist_getzoom(cnv); + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-outline", gfx->current_color, "-tags", 2, tags); + 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; } @@ -520,8 +522,8 @@ static int fill_all(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - int x1 = text_xpix(obj, cnv); - int y1 = text_ypix(obj, cnv); + int x1 = text_xpix(obj, cnv) * glist_getzoom(cnv); + int y1 = text_ypix(obj, cnv) * glist_getzoom(cnv); int x2 = x1 + gfx->width * glist_getzoom(cnv); int y2 = y1 + gfx->height * glist_getzoom(cnv); @@ -555,9 +557,11 @@ static int stroke_rect(lua_State* L) { int x1, y1, x2, y2; get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); + int line_width = luaL_checknumber(L, 5) * glist_getzoom(cnv); + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-outline", gfx->current_color, "-tags", 2, tags); + 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; } @@ -602,6 +606,8 @@ static int stroke_rounded_rect(lua_State* L) { int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); + int line_width = luaL_checknumber(L, 6) * glist_getzoom(cnv); + int width = x2 - x1; int height = y2 - y1; @@ -609,23 +615,23 @@ static int stroke_rounded_rect(lua_State* L) { // 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", 1, "-start", 90, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); + "-start", 0, "-extent", 90, "-width", line_width, "-start", 90, "-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, y1, x2, y1 + radius_y*2, - "-start", 270, "-extent", 90, "-width", 1, "-start", 0, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); + "-start", 270, "-extent", 90, "-width", line_width, "-start", 0, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri ri ri ri rs rs rS", cnv, "create", "arc", x1, y2 - radius_y*2, x1 + radius_x*2, y2, - "-start", 180, "-extent", 90, "-width", 1, "-start", 180, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); + "-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", 1, "-start", 270, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); + "-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", 1, "-fill", gfx->current_color, "-tags", 2, tags); + "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 + radius_y, y2, x1 + width - radius_y, y2, - "-width", 1, "-fill", gfx->current_color, "-tags", 2, tags); + "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 , y1 + radius_y, x1, y2 - radius_y, - "-width", 1, "-fill", gfx->current_color, "-tags", 2, tags); + "-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", 1, "-fill", gfx->current_color, "-tags", 2, tags); + "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); return 0; } @@ -641,18 +647,23 @@ static int draw_line(lua_State* L) { int y2 = luaL_checknumber(L, 4); int lineWidth = luaL_checknumber(L, 5); - int zoom = glist_getzoom(cnv); - x1 *= gfx->scale_x * zoom; - y1 *= gfx->scale_y * zoom; - x2 *= gfx->scale_x * zoom; - y2 *= gfx->scale_y * zoom; + + x1 *= gfx->scale_x; + y1 *= gfx->scale_y; + x2 *= gfx->scale_x; + y2 *= gfx->scale_y; x1 += gfx->translate_x + text_xpix(obj, cnv); y1 += gfx->translate_y + text_ypix(obj, cnv); x2 += gfx->translate_x + text_xpix(obj, cnv); y2 += gfx->translate_y + text_ypix(obj, cnv); - lineWidth *= zoom; + int canvas_zoom = glist_getzoom(cnv); + x1 *= canvas_zoom; + y1 *= canvas_zoom; + x2 *= canvas_zoom; + y2 *= canvas_zoom; + lineWidth *= canvas_zoom; const char* tags[] = { gfx->object_tag, register_drawing(obj) }; @@ -672,15 +683,19 @@ static int draw_text(lua_State* L) { int w = luaL_checknumber(L, 4); int fontHeight = luaL_checknumber(L, 5); - int zoom = glist_getzoom(cnv); - x *= gfx->scale_x * zoom; - y *= gfx->scale_y * zoom; - w *= gfx->scale_x * zoom; + x *= gfx->scale_x; + y *= gfx->scale_y; + w *= gfx->scale_x; x += gfx->translate_x + text_xpix(obj, cnv); y += gfx->translate_y + text_ypix(obj, cnv); - // Font size and position are offset to make sure they match drawing in plugdata + int canvas_zoom = glist_getzoom(cnv); + x *= canvas_zoom; + y *= canvas_zoom; + w *= canvas_zoom; + + // Font size is offset to make sure it matches the size in plugdata fontHeight *= 0.8f; const char* tags[] = { gfx->object_tag, register_drawing(obj) }; @@ -690,7 +705,7 @@ static int draw_text(lua_State* L) { t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(sys_font)); - SETFLOAT (fontatoms+1, fontHeight * zoom * gfx->scale_y); + SETFLOAT (fontatoms+1, fontHeight * canvas_zoom * gfx->scale_y); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); pdgui_vmess(0, "crs rA rs rs", cnv, "itemconfigure", tags[1], @@ -698,7 +713,7 @@ static int draw_text(lua_State* L) { "-fill", gfx->current_color, "-justify", "left"); - pdgui_vmess(0, "crs ii", cnv, "coords", tags[1], x + 18, y); + pdgui_vmess(0, "crs ii", cnv, "coords", tags[1], x, y); return 0; } @@ -816,14 +831,15 @@ static int stroke_path(lua_State* L) { for (int i = 0; i < gfx->num_path_segments; i++) { int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; - x *= gfx->scale_x * glist_getzoom(cnv); - y *= gfx->scale_y * glist_getzoom(cnv); + x *= gfx->scale_x; + y *= gfx->scale_y; x += gfx->translate_x + obj_x; y += gfx->translate_y + obj_y; - gfx->path_segments[i * 2] = x; - gfx->path_segments[i * 2 + 1] = y; + int canvas_zoom = glist_getzoom(cnv); + gfx->path_segments[i * 2] = x * canvas_zoom; + gfx->path_segments[i * 2 + 1] = y * canvas_zoom; } int totalSize = 0; @@ -870,14 +886,14 @@ static int fill_path(lua_State* L) { for (int i = 0; i < gfx->num_path_segments; i++) { int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; - x *= gfx->scale_x * glist_getzoom(cnv); - y *= gfx->scale_y * glist_getzoom(cnv); + x *= gfx->scale_x; + y *= gfx->scale_y; x += gfx->translate_x + obj_x; y += gfx->translate_y + obj_y; - gfx->path_segments[i * 2] = x; - gfx->path_segments[i * 2 + 1] = y; + gfx->path_segments[i * 2] = x * glist_getzoom(cnv); + gfx->path_segments[i * 2 + 1] = y * glist_getzoom(cnv); } int totalSize = 0; From a9c3270d74dddd43b3b37d00510169351d830780 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 16:40:06 +0100 Subject: [PATCH 22/62] Fixed displaying in graphs for pd-vanilla --- pdlua_gfx.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index e24d780..147ab24 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -422,8 +422,8 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x w *= gfx->scale_x; h *= gfx->scale_y; - x += gfx->translate_x + text_xpix(obj, cnv); - y += gfx->translate_y + text_ypix(obj, cnv); + x += gfx->translate_x + text_xpix(obj, obj->canvas); + y += gfx->translate_y + text_ypix(obj, obj->canvas); *x1 = x * glist_getzoom(cnv); *y1 = y * glist_getzoom(cnv); @@ -522,8 +522,8 @@ static int fill_all(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - int x1 = text_xpix(obj, cnv) * glist_getzoom(cnv); - int y1 = text_ypix(obj, cnv) * glist_getzoom(cnv); + int x1 = text_xpix(obj, obj->canvas) * glist_getzoom(cnv); + int y1 = text_ypix(obj, obj->canvas) * glist_getzoom(cnv); int x2 = x1 + gfx->width * glist_getzoom(cnv); int y2 = y1 + gfx->height * glist_getzoom(cnv); @@ -653,10 +653,10 @@ static int draw_line(lua_State* L) { x2 *= gfx->scale_x; y2 *= gfx->scale_y; - x1 += gfx->translate_x + text_xpix(obj, cnv); - y1 += gfx->translate_y + text_ypix(obj, cnv); - x2 += gfx->translate_x + text_xpix(obj, cnv); - y2 += gfx->translate_y + text_ypix(obj, cnv); + x1 += gfx->translate_x + text_xpix(obj, obj->canvas); + y1 += gfx->translate_y + text_ypix(obj, obj->canvas); + x2 += gfx->translate_x + text_xpix(obj, obj->canvas); + y2 += gfx->translate_y + text_ypix(obj, obj->canvas); int canvas_zoom = glist_getzoom(cnv); x1 *= canvas_zoom; @@ -687,8 +687,8 @@ static int draw_text(lua_State* L) { y *= gfx->scale_y; w *= gfx->scale_x; - x += gfx->translate_x + text_xpix(obj, cnv); - y += gfx->translate_y + text_ypix(obj, cnv); + x += gfx->translate_x + text_xpix(obj, obj->canvas); + y += gfx->translate_y + text_ypix(obj, obj->canvas); int canvas_zoom = glist_getzoom(cnv); x *= canvas_zoom; @@ -826,8 +826,8 @@ static int stroke_path(lua_State* L) { int stroke_width = luaL_checknumber(L, 1) * glist_getzoom(cnv); // Apply transformations to all coordinates - int obj_x = text_xpix(obj, cnv); - int obj_y = text_ypix(obj, cnv); + int obj_x = text_xpix(obj, obj->canvas); + int obj_y = text_ypix(obj, obj->canvas); for (int i = 0; i < gfx->num_path_segments; i++) { int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; @@ -881,8 +881,8 @@ static int fill_path(lua_State* L) { t_canvas *cnv = glist_getcanvas(obj->canvas); // Apply transformations to all coordinates - int obj_x = text_xpix(obj, cnv); - int obj_y = text_ypix(obj, cnv); + int obj_x = text_xpix(obj, obj->canvas); + int obj_y = text_ypix(obj, obj->canvas); for (int i = 0; i < gfx->num_path_segments; i++) { int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; From 1463cf842204ad7a7dc0e8d4352c737aff8fb8b0 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 16:57:40 +0100 Subject: [PATCH 23/62] pd: Don't clear canvas if nothing was painted --- pdlua_gfx.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 147ab24..7a04d6b 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -407,6 +407,7 @@ void pdlua_gfx_clear(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); + gfx->current_paint_tag[0] = '\0'; } static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x1, int* y1, int* x2, int* y2) { @@ -448,6 +449,8 @@ static int gfx_initialize(t_pdlua *obj) snprintf(gfx->object_tag, 128, ".x%lx", obj); gfx->object_tag[127] = '\0'; + gfx->current_paint_tag[0] = '\0'; + pdlua_gfx_repaint(obj); return 0; } @@ -463,7 +466,10 @@ static int set_size(lua_State* L) static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); - pdlua_gfx_clear(obj); + + // check if anything was painted before + if(strlen(obj->gfx.current_paint_tag)) + pdlua_gfx_clear(obj); return 0; } From d2b7ddd58431e543ec23568b5ad7de451a143dd3 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 18:01:14 +0100 Subject: [PATCH 24/62] Pd zoom and graph behavior fixes --- pdlua.c | 54 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/pdlua.c b/pdlua.c index a143478..d7a7c4d 100644 --- a/pdlua.c +++ b/pdlua.c @@ -587,26 +587,7 @@ void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ // If there's no gui, use default text vis behavior if(!((t_pdlua *)z)->has_gui) { - t_text *x = (t_text *)z; - if (vis) - { - if (gobj_shouldvis(&x->te_g, glist)) - { - t_rtext *y = glist_findrtext(glist, x); - text_drawborder(x, glist, rtext_gettag(y), - rtext_width(y), rtext_height(y), 1); - rtext_draw(y); - } - } - else - { - t_rtext *y = glist_findrtext(glist, x); - if (gobj_shouldvis(&x->te_g, glist)) - { - text_eraseborder(x, glist, rtext_gettag(y)); - rtext_erase(y); - } - } + text_widgetbehavior.w_visfn(z, glist, vis); return; } @@ -620,17 +601,30 @@ void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ } } +static void pdlua_delete(t_gobj *z, t_glist *glist){ + if(!((t_pdlua *)z)->has_gui) + { + text_widgetbehavior.w_deletefn(z, glist); + return; + } + pdlua_vis(z, glist, 0); + canvas_deletelinesfor(glist, (t_text *)z); +} + static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { #if !PLUGDATA - t_pdlua *x = (t_pdlua *)z; - x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx; - x->gfx.mouse_drag_y = x->gfx.mouse_drag_y + dy; - if (!up) { - pdlua_gfx_mouse_drag((t_object*)x, x->gfx.mouse_drag_x - text_xpix(&x->pd, x->canvas), x->gfx.mouse_drag_y - text_ypix(&x->pd, x->canvas)); + t_pdlua *x = (t_pdlua *)z; + x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx; + x->gfx.mouse_drag_y = x->gfx.mouse_drag_y + dy; + int zoom = glist_getzoom(glist_getcanvas(x->canvas)); + int xpos = (x->gfx.mouse_drag_x / zoom) - text_xpix(&x->pd, x->canvas); + int ypos = (x->gfx.mouse_drag_y / zoom) - text_ypix(&x->pd, x->canvas); + + pdlua_gfx_mouse_drag((t_object*)x, xpos, ypos); } #endif } @@ -640,8 +634,9 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in #if !PLUGDATA if(x->has_gui) { - int xpix = xpos - text_xpix(&x->pd, gl); - int ypix = ypos - text_ypix(&x->pd, gl); + int zoom = glist_getzoom(gl); + int xpix = (xpos / zoom) - text_xpix(&x->pd, gl); + int ypix = (ypos / zoom) - text_ypix(&x->pd, gl); if(doit){ if(!x->gfx.mouse_down) @@ -671,6 +666,7 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in #endif } + static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ t_pdlua *x = (t_pdlua *)z; x->pd.te_xpix += dx, x->pd.te_ypix += dy; @@ -839,8 +835,8 @@ static int pdlua_class_new(lua_State *L) pdlua_widgetbehavior.w_getrectfn = pdlua_getrect; pdlua_widgetbehavior.w_displacefn = pdlua_displace; - pdlua_widgetbehavior.w_selectfn = text_widgetbehavior.w_selectfn;; - pdlua_widgetbehavior.w_deletefn = text_widgetbehavior.w_deletefn; + pdlua_widgetbehavior.w_selectfn = text_widgetbehavior.w_selectfn; + pdlua_widgetbehavior.w_deletefn = pdlua_delete; pdlua_widgetbehavior.w_clickfn = pdlua_click; pdlua_widgetbehavior.w_visfn = pdlua_vis; pdlua_widgetbehavior.w_activatefn = pdlua_activate; From 1bea6f1cfb5af94a2f413f8b6b55a2c02256a1e8 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 18:26:16 +0100 Subject: [PATCH 25/62] Added a more reliable way to get script path, added _reload method to reload any object dynamically --- pd.lua | 24 ++++++++++++++++++++++++ pdlua.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/pd.lua b/pd.lua index 7c97f93..c92cd1c 100644 --- a/pd.lua +++ b/pd.lua @@ -25,6 +25,7 @@ pd._objects = { } pd._clocks = { } pd._receives = { } pd._loadpath = "" +pd._fullpath = "" -- add a path to Lua's "require" search paths pd._setrequirepath = function(path) @@ -119,6 +120,14 @@ pd._whoami = function (object) end end +--whoami method dispatcher +pd._whereami = function (object) + if nil ~= pd._objects[object] then + return pd._objects[object]._fullpath + end +end + + --class method dispatcher pd._get_class = function (object) if nil ~= pd._objects[object] then @@ -295,6 +304,13 @@ function pd.Class:register(name) self._class = pd._register(name) -- register new class self._name = name self._loadpath = fullpath + + if pd._fullpath == nil or pd._fullpath == '' then + self._fullpath = fullname .. ".pd_lua" + else + self._fullpath = self._currentpath + end + if name == "pdlua" then self._scriptname = "pd.lua" else @@ -385,7 +401,9 @@ function pd.Class:dofilex(file) local pathsave = pd._loadpath pd._loadname = nil pd._loadpath = self._loadpath + pd._fullpath = file local f, path = pd._dofilex(self._class, file) + pd._fullpath = = nil pd._loadname = namesave pd._loadpath = pathsave return f, path @@ -399,7 +417,9 @@ function pd.Class:dofile(file) local pathsave = pd._loadpath pd._loadname = nil pd._loadpath = self._loadpath + pd._fullpath = file local f, path = pd._dofile(self._object, file) + pd._fullpath = = nil pd._loadname = namesave pd._loadpath = pathsave return f, path @@ -413,6 +433,10 @@ function pd.Class:whoami() return self._scriptname or self._name end +function pd.Class:in_1_reload() + return self:dofile(self._fullpath) +end + function pd.Class:get_class() -- accessor for t_class* return self._class or nil end diff --git a/pdlua.c b/pdlua.c index d7a7c4d..395d880 100644 --- a/pdlua.c +++ b/pdlua.c @@ -667,6 +667,11 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in } +static void pdlua_reload(t_gobj* z) +{ + pdlua_dispatch((t_pdlua *)z, 0, gensym("_reload"), 0, NULL); +} + static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ t_pdlua *x = (t_pdlua *)z; x->pd.te_xpix += dx, x->pd.te_ypix += dy; @@ -747,6 +752,28 @@ void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); /** Here we find the lua code for the object and open it in an editor */ static void pdlua_menu_open(t_pdlua *o) { + #if PLUGDATA + lua_getglobal(__L, "pd"); + lua_getfield(__L, -1, "_whereami"); + lua_pushstring(__L, o->pd.te_pd->c_name->s_name); + + if (lua_pcall(__L, 1, 1, 0)) + { + pd_error(NULL, "lua: error in whereami:\n%s", lua_tostring(__L, -1)); + lua_pop(__L, 2); /* pop the error string and the global "pd" */ + return; + } + if(lua_isstring(__L, -1)) { + const char* fullpath = luaL_checkstring(__L, -1); + if(fullpath) { + t_atom arg; + SETSYMBOL(&arg, gensym(fullpath)); + plugdata_forward_message(o, gensym("open_textfile"), 1, &arg); + } + return; + } +#endif + const char *name; const char *path; char pathname[FILENAME_MAX]; @@ -842,9 +869,12 @@ static int pdlua_class_new(lua_State *L) pdlua_widgetbehavior.w_activatefn = pdlua_activate; class_setwidget(c, &pdlua_widgetbehavior); -/* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ - if (c) + + if (c) { + /* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ class_addmethod(c, (t_method)pdlua_menu_open, gensym("menu-open"), A_NULL);/* (mrpeach 20111025) */ + class_addmethod(c, (t_method)pdlua_reload, gensym("_reload"), A_NULL);/* (mrpeach 20111025) */ + } /**/ lua_pushlightuserdata(L, c); From 69aaa8513fcf7db168637c10362b97fa462eba34 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 18:36:01 +0100 Subject: [PATCH 26/62] Fixed all warnings --- pdlua.c | 16 ++++++++-------- pdlua_gfx.h | 54 ++++++++++++++++++++--------------------------------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/pdlua.c b/pdlua.c index 395d880..1fed765 100644 --- a/pdlua.c +++ b/pdlua.c @@ -594,10 +594,10 @@ void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ // Otherwise, repaint or clear the custom graphics if(vis) { - pdlua_gfx_repaint((t_object*)z); + pdlua_gfx_repaint((t_pdlua *)z); } else { - pdlua_gfx_clear((t_object*)z); + pdlua_gfx_clear((t_pdlua *)z); } } @@ -624,7 +624,7 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, int xpos = (x->gfx.mouse_drag_x / zoom) - text_xpix(&x->pd, x->canvas); int ypos = (x->gfx.mouse_drag_y / zoom) - text_ypix(&x->pd, x->canvas); - pdlua_gfx_mouse_drag((t_object*)x, xpos, ypos); + pdlua_gfx_mouse_drag(x, xpos, ypos); } #endif } @@ -641,7 +641,7 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in if(doit){ if(!x->gfx.mouse_down) { - pdlua_gfx_mouse_down((t_object*)x, xpix, ypix); + pdlua_gfx_mouse_down(x, xpix, ypix); x->gfx.mouse_drag_x = xpos; x->gfx.mouse_drag_y = ypos; } @@ -653,7 +653,7 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in if(x->gfx.mouse_down) { - pdlua_gfx_mouse_up((t_object*)x, xpix, ypix); + pdlua_gfx_mouse_up(x, xpix, ypix); } } @@ -661,7 +661,7 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in return 1; } - return text_widgetbehavior.w_clickfn(x, gl, xpos, ypos, shift, alt, dbl, doit); + return text_widgetbehavior.w_clickfn(z, gl, xpos, ypos, shift, alt, dbl, doit); #endif } @@ -682,7 +682,7 @@ static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ canvas_fixlinesfor(glist, (t_text*)x); } -static void pdlua_activate(t_pdlua *z, t_glist *glist, int state) +static void pdlua_activate(t_gobj *z, t_glist *glist, int state) { if(!((t_pdlua *)z)->has_gui) { @@ -701,7 +701,7 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp *yp2 = y1 + x->gfx.height * glist->gl_zoom; } else { - text_widgetbehavior.w_getrectfn(x, glist, xp1, yp1, xp2, yp2); + text_widgetbehavior.w_getrectfn(z, glist, xp1, yp1, xp2, yp2); } } diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 7a04d6b..eb4d6cc 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -423,8 +423,8 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x w *= gfx->scale_x; h *= gfx->scale_y; - x += gfx->translate_x + text_xpix(obj, obj->canvas); - y += gfx->translate_y + text_ypix(obj, obj->canvas); + x += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); + y += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); *x1 = x * glist_getzoom(cnv); *y1 = y * glist_getzoom(cnv); @@ -432,22 +432,19 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x *y2 = (y + h) * glist_getzoom(cnv); } - static const char* register_drawing(t_pdlua *object) { t_pdlua_gfx *gfx = &object->gfx; - snprintf(gfx->current_paint_tag, 128, ".x%lx", rand()); + snprintf(gfx->current_paint_tag, 128, ".x%d", rand()); gfx->current_paint_tag[127] = '\0'; return gfx->current_paint_tag; } - static int gfx_initialize(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; - t_canvas *cnv = glist_getcanvas(obj->canvas); - snprintf(gfx->object_tag, 128, ".x%lx", obj); + snprintf(gfx->object_tag, 128, ".x%lx", (long)obj); gfx->object_tag[127] = '\0'; gfx->current_paint_tag[0] = '\0'; @@ -528,8 +525,8 @@ static int fill_all(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - int x1 = text_xpix(obj, obj->canvas) * glist_getzoom(cnv); - int y1 = text_ypix(obj, obj->canvas) * glist_getzoom(cnv); + int x1 = text_xpix((t_object*)obj, obj->canvas) * glist_getzoom(cnv); + int y1 = text_ypix((t_object*)obj, obj->canvas) * glist_getzoom(cnv); int x2 = x1 + gfx->width * glist_getzoom(cnv); int y2 = y1 + gfx->height * glist_getzoom(cnv); @@ -583,10 +580,7 @@ static int fill_rounded_rect(lua_State* L) { int radius = luaL_checknumber(L, 5); // Radius for rounded corners int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); - - int width = x2 - x1; - int height = y2 - y1; - + const char* tags[] = { gfx->object_tag, register_drawing(obj) }; // Tcl/tk can't fill rounded rectangles, so we draw 2 smaller rectangles with 4 ovals over the corners @@ -611,12 +605,8 @@ static int stroke_rounded_rect(lua_State* L) { int radius = luaL_checknumber(L, 5); // Radius for rounded corners int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); - int line_width = luaL_checknumber(L, 6) * glist_getzoom(cnv); - int width = x2 - x1; - int height = y2 - y1; - const char* tags[] = { gfx->object_tag, register_drawing(obj) }; // Tcl/tk can't stroke rounded rectangles either, so we draw 2 lines connecting with 4 arcs at the corners @@ -632,7 +622,7 @@ static int stroke_rounded_rect(lua_State* L) { // 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); - pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 + radius_y, y2, x1 + width - radius_y, y2, + pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 + radius_y, y2, x2 - radius_y, y2, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1 , y1 + radius_y, x1, y2 - radius_y, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); @@ -653,16 +643,15 @@ static int draw_line(lua_State* L) { int y2 = luaL_checknumber(L, 4); int lineWidth = luaL_checknumber(L, 5); - x1 *= gfx->scale_x; y1 *= gfx->scale_y; x2 *= gfx->scale_x; y2 *= gfx->scale_y; - x1 += gfx->translate_x + text_xpix(obj, obj->canvas); - y1 += gfx->translate_y + text_ypix(obj, obj->canvas); - x2 += gfx->translate_x + text_xpix(obj, obj->canvas); - y2 += gfx->translate_y + text_ypix(obj, obj->canvas); + x1 += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); + y1 += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); + x2 += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); + y2 += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); int canvas_zoom = glist_getzoom(cnv); x1 *= canvas_zoom; @@ -675,8 +664,9 @@ static int draw_line(lua_State* L) { pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1, y1, x2, y2, "-width", lineWidth, "-fill", gfx->current_color, "-tags", 2, tags); -} + return 0; +} static int draw_text(lua_State* L) { t_pdlua* obj = get_current_object(L); @@ -693,8 +683,8 @@ static int draw_text(lua_State* L) { y *= gfx->scale_y; w *= gfx->scale_x; - x += gfx->translate_x + text_xpix(obj, obj->canvas); - y += gfx->translate_y + text_ypix(obj, obj->canvas); + x += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); + y += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); int canvas_zoom = glist_getzoom(cnv); x *= canvas_zoom; @@ -747,8 +737,6 @@ static int start_path(lua_State* L) { return 0; } - - // Function to add a line to the current path static int line_to(lua_State* L) { t_pdlua* obj = get_current_object(L); @@ -832,8 +820,8 @@ static int stroke_path(lua_State* L) { int stroke_width = luaL_checknumber(L, 1) * glist_getzoom(cnv); // Apply transformations to all coordinates - int obj_x = text_xpix(obj, obj->canvas); - int obj_y = text_ypix(obj, obj->canvas); + int obj_x = text_xpix((t_object*)obj, obj->canvas); + int obj_y = text_ypix((t_object*)obj, obj->canvas); for (int i = 0; i < gfx->num_path_segments; i++) { int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; @@ -887,8 +875,8 @@ static int fill_path(lua_State* L) { t_canvas *cnv = glist_getcanvas(obj->canvas); // Apply transformations to all coordinates - int obj_x = text_xpix(obj, obj->canvas); - int obj_y = text_ypix(obj, obj->canvas); + int obj_x = text_xpix((t_object*)obj, obj->canvas); + int obj_y = text_ypix((t_object*)obj, obj->canvas); for (int i = 0; i < gfx->num_path_segments; i++) { int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 1]; @@ -936,7 +924,6 @@ static int fill_path(lua_State* L) { return 0; } - static int translate(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; @@ -953,7 +940,6 @@ static int scale(lua_State* L) { return 0; } - static int reset_transform(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; From 8f6ebc427477733a97c8df741f820979d32302c0 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 19:38:29 +0100 Subject: [PATCH 27/62] Make alpha argument really optional, fixed pd zooming issues --- pdlua.c | 9 ++++----- pdlua_gfx.h | 54 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/pdlua.c b/pdlua.c index 1fed765..02213e6 100644 --- a/pdlua.c +++ b/pdlua.c @@ -621,9 +621,8 @@ static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx; x->gfx.mouse_drag_y = x->gfx.mouse_drag_y + dy; int zoom = glist_getzoom(glist_getcanvas(x->canvas)); - int xpos = (x->gfx.mouse_drag_x / zoom) - text_xpix(&x->pd, x->canvas); - int ypos = (x->gfx.mouse_drag_y / zoom) - text_ypix(&x->pd, x->canvas); - + int xpos = (x->gfx.mouse_drag_x - text_xpix(&x->pd, x->canvas)) / zoom; + int ypos = (x->gfx.mouse_drag_y - text_ypix(&x->pd, x->canvas)) / zoom; pdlua_gfx_mouse_drag(x, xpos, ypos); } #endif @@ -635,8 +634,8 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in if(x->has_gui) { int zoom = glist_getzoom(gl); - int xpix = (xpos / zoom) - text_xpix(&x->pd, gl); - int ypix = (ypos / zoom) - text_ypix(&x->pd, gl); + int xpix = (xpos - text_xpix(&x->pd, gl)) / zoom; + int ypix = (ypos - text_ypix(&x->pd, gl)) / zoom; if(doit){ if(!x->gfx.mouse_down) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index eb4d6cc..488b551 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -199,7 +199,12 @@ static int set_color(lua_State* L) { SETFLOAT(args, luaL_checknumber(L, 1)); // r SETFLOAT(args + 1, luaL_checknumber(L, 2)); // g SETFLOAT(args + 2, luaL_checknumber(L, 3)); // b - SETFLOAT(args + 3, luaL_optnumber(L, 4, 1.0)); // a (optional, default to 1.0) + if (lua_gettop(L) > 1) { // a (optional, default to 1.0) + SETFLOAT(args + 3, luaL_checknumber(L, 4)); + } + else { + SETFLOAT(args + 3, 1.0f); + } plugdata_draw(obj, gensym("lua_set_color"), 4, args); return 0; } @@ -423,8 +428,8 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x w *= gfx->scale_x; h *= gfx->scale_y; - x += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); - y += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); + x += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / glist_getzoom(cnv)); + y += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / glist_getzoom(cnv)); *x1 = x * glist_getzoom(cnv); *y1 = y * glist_getzoom(cnv); @@ -481,7 +486,8 @@ static int set_color(lua_State* L) { int r = luaL_checknumber(L, 1); int g = luaL_checknumber(L, 2); int b = luaL_checknumber(L, 3); - + // AFAIK, alpha is not supported in tcl/tk + snprintf(gfx->current_color, 8, "#%02X%02X%02X", r, g, b); gfx->current_color[7] = '\0'; @@ -525,11 +531,11 @@ static int fill_all(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - int x1 = text_xpix((t_object*)obj, obj->canvas) * glist_getzoom(cnv); - int y1 = text_ypix((t_object*)obj, obj->canvas) * glist_getzoom(cnv); + 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(obj) }; pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-tags", 2, tags); @@ -648,12 +654,13 @@ static int draw_line(lua_State* L) { x2 *= gfx->scale_x; y2 *= gfx->scale_y; - x1 += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); - y1 += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); - x2 += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); - y2 += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); - int canvas_zoom = glist_getzoom(cnv); + + x1 += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / canvas_zoom); + y1 += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / canvas_zoom); + x2 += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / canvas_zoom); + y2 += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / canvas_zoom); + x1 *= canvas_zoom; y1 *= canvas_zoom; x2 *= canvas_zoom; @@ -683,10 +690,10 @@ static int draw_text(lua_State* L) { y *= gfx->scale_y; w *= gfx->scale_x; - x += gfx->translate_x + text_xpix((t_object*)obj, obj->canvas); - y += gfx->translate_y + text_ypix((t_object*)obj, obj->canvas); - int canvas_zoom = glist_getzoom(cnv); + x += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / canvas_zoom); + y += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / canvas_zoom); + x *= canvas_zoom; y *= canvas_zoom; w *= canvas_zoom; @@ -819,6 +826,7 @@ static int stroke_path(lua_State* L) { int stroke_width = luaL_checknumber(L, 1) * glist_getzoom(cnv); + // Apply transformations to all coordinates // 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); @@ -828,12 +836,12 @@ static int stroke_path(lua_State* L) { x *= gfx->scale_x; y *= gfx->scale_y; - x += gfx->translate_x + obj_x; - y += gfx->translate_y + obj_y; + x += gfx->translate_x; + y += gfx->translate_y; int canvas_zoom = glist_getzoom(cnv); - gfx->path_segments[i * 2] = x * canvas_zoom; - gfx->path_segments[i * 2 + 1] = y * canvas_zoom; + gfx->path_segments[i * 2] = (x * canvas_zoom) + obj_x; + gfx->path_segments[i * 2 + 1] = (y * canvas_zoom) + obj_y; } int totalSize = 0; @@ -883,11 +891,11 @@ static int fill_path(lua_State* L) { x *= gfx->scale_x; y *= gfx->scale_y; - x += gfx->translate_x + obj_x; - y += gfx->translate_y + obj_y; + x += gfx->translate_x; + y += gfx->translate_y; - gfx->path_segments[i * 2] = x * glist_getzoom(cnv); - gfx->path_segments[i * 2 + 1] = y * glist_getzoom(cnv); + gfx->path_segments[i * 2] = x * glist_getzoom(cnv) + obj_x; + gfx->path_segments[i * 2 + 1] = y * glist_getzoom(cnv) + obj_y; } int totalSize = 0; From 0aa203c576c2bf71d6b71a84d71e915865e5de08 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 20:02:38 +0100 Subject: [PATCH 28/62] Fix for optional argument --- pdlua_gfx.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 488b551..e63991a 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -196,10 +196,13 @@ static int end_paint(lua_State* L) { static int set_color(lua_State* L) { t_pdlua* obj = get_current_object(L); 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) > 1) { // a (optional, default to 1.0) + + if (lua_gettop(L) > 5) { // object and table are already on stack, hence 5 + // alpha (optional, default to 1.0) SETFLOAT(args + 3, luaL_checknumber(L, 4)); } else { From e72befbf41259b17bfc42c33bd9508d82f76363a Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 20:10:19 +0100 Subject: [PATCH 29/62] Added GPL license to new files --- pdlua.h | 17 +++++++++++++++++ pdlua_gfx.h | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pdlua.h b/pdlua.h index f6634d4..93d7f5e 100644 --- a/pdlua.h +++ b/pdlua.h @@ -1,3 +1,20 @@ + /** + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + **/ + #include "m_pd.h" /** Global Lua interpreter state, needed in the constructor. */ diff --git a/pdlua_gfx.h b/pdlua_gfx.h index e63991a..72743c6 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -1,3 +1,25 @@ +/** @file pdlua_gfx.h + * @brief pdlua_gfx -- an extension to pdlua that allows GUI rendering and interaction in pure-data and plugdata + * @author Timothy Schoen + * @date 2023 + * + * Copyright (C) 2023 Timothy Schoen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define PDLUA_OBJECT_REGISTRTY_ID 512 From 543d5f47fb997a2d6cf93189f30afb85f59cce3d Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 20:18:12 +0100 Subject: [PATCH 30/62] Added some comments to explain stuff --- pdlua.h | 18 +++++++++++------- pdlua_gfx.h | 14 +++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/pdlua.h b/pdlua.h index 93d7f5e..0690de7 100644 --- a/pdlua.h +++ b/pdlua.h @@ -23,25 +23,29 @@ static lua_State *__L; typedef struct _pdlua_gfx { #if !PLUGDATA - char object_tag[128]; - char current_paint_tag[128]; + char object_tag[128]; // Tcl/tk tag that is attached to all drawings + char current_paint_tag[128]; // Tcl/tk tag that is only attached to the current drawing in progress + // Variables for managing vector paths int* path_segments; int num_path_segments; int num_path_segments_allocated; int path_start_x, path_start_y; + // Variables to keep track of mouse button state and drag position int mouse_drag_x, mouse_drag_y, mouse_down; + + // Variables for managing transforms int translate_x, translate_y; float scale_x, scale_y; - char current_color[8]; - t_symbol* mouse_proxy_sym; - void* mouse_proxy; + char current_color[8]; // Keep track of current color #else - void(*plugdata_draw_callback)(void*, t_symbol*, int, t_atom*); - void* plugdata_callback_target; + void(*plugdata_draw_callback)(void*, t_symbol*, int, t_atom*); // Callback to perform drawing in plugdata + void* plugdata_callback_target; // Pointer to plugdata object, the target for our draw callback #endif + + // Size variables int width, height; } t_pdlua_gfx; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 72743c6..5939a35 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -56,8 +56,11 @@ static int translate(lua_State* L); static int scale(lua_State* L); static int reset_transform(lua_State* L); -void pdlua_gfx_clear(t_pdlua *obj); +// pdlua_gfx_clear, pdlua_gfx_repaint and pdlua_gfx_mouse_* correspond to the various callbacks the user can assign +void pdlua_gfx_clear(t_pdlua *obj); // only for pd-vanilla, to delete all tcl/tk items + +// Trigger repaint callback in lua script void pdlua_gfx_repaint(t_pdlua *o) { lua_getglobal(__L, "pd"); lua_getfield (__L, -1, "_repaint"); @@ -79,6 +82,7 @@ void pdlua_gfx_repaint(t_pdlua *o) { lua_pop(__L, 1); /* pop the global "pd" */ } +// Pass mouse events to lua script void pdlua_gfx_mouse_event(t_pdlua *o, int x, int y, int type) { lua_getglobal(__L, "pd"); @@ -103,6 +107,7 @@ void pdlua_gfx_mouse_event(t_pdlua *o, int x, int y, int type) { lua_pop(__L, 1); /* pop the global "pd" */ } +// Pass mouse events to lua script (but easier to understand) void pdlua_gfx_mouse_down(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 0); } @@ -118,6 +123,8 @@ void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 3); } +// We need to have access to the current object always, even in the constructor +// This function can guarantee that static t_pdlua* get_current_object(lua_State* L) { lua_pushvalue(L, LUA_REGISTRYINDEX); @@ -170,6 +177,7 @@ int pdlua_gfx_setup(lua_State* L) { #if PLUGDATA +// Wrapper around draw callback to plugdata static inline void plugdata_draw(t_pdlua* obj, t_symbol* sym, int argc, t_atom* argv) { if(obj->gfx.plugdata_callback_target) { @@ -182,8 +190,8 @@ void pdlua_gfx_clear(t_pdlua* obj) { static int gfx_initialize(t_pdlua* obj) { - pdlua_gfx_register_gui(obj); - pdlua_gfx_repaint(obj); + pdlua_gfx_register_gui(obj); // let plugdata know that our classname is a GUI object + pdlua_gfx_repaint(obj); // Initial repaint return 0; } From b37efc51aca7ea8ff7d0f406baf91032fc23c21a Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 20:44:58 +0100 Subject: [PATCH 31/62] Make sure iolets are also drawn --- pdlua.c | 23 ++++++++++++++++++----- pdlua_gfx.h | 9 +++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pdlua.c b/pdlua.c index 02213e6..c2d0855 100644 --- a/pdlua.c +++ b/pdlua.c @@ -584,20 +584,22 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */) } void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ + + t_pdlua* x = (t_pdlua *)z; // If there's no gui, use default text vis behavior - if(!((t_pdlua *)z)->has_gui) + if(!x->has_gui) { text_widgetbehavior.w_visfn(z, glist, vis); return; } - + // Otherwise, repaint or clear the custom graphics if(vis) { - pdlua_gfx_repaint((t_pdlua *)z); + pdlua_gfx_repaint(x); } else { - pdlua_gfx_clear((t_pdlua *)z); + pdlua_gfx_clear(x); } } @@ -665,7 +667,9 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in #endif } - +// The _reload method will tell the pdlua object to reload the original script that spawned it +// This is used in plugdata for dynamic reloading, but can be useful in other environments too +// Prefixed with _ to prevent namespace pollution static void pdlua_reload(t_gobj* z) { pdlua_dispatch((t_pdlua *)z, 0, gensym("_reload"), 0, NULL); @@ -676,8 +680,10 @@ static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ x->pd.te_xpix += dx, x->pd.te_ypix += dy; dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); + // Will re-draw GUI pdlua_vis(z, glist, 0); pdlua_vis(z, glist, 1); + canvas_fixlinesfor(glist, (t_text*)x); } @@ -685,6 +691,7 @@ static void pdlua_activate(t_gobj *z, t_glist *glist, int state) { if(!((t_pdlua *)z)->has_gui) { + // Bypass to text widgetbehaviour if we're not a GUI text_widgetbehavior.w_activatefn(z, glist, state); } } @@ -700,6 +707,7 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp *yp2 = y1 + x->gfx.height * glist->gl_zoom; } else { + // Bypass to text widgetbehaviour if we're not a GUI text_widgetbehavior.w_getrectfn(z, glist, xp1, yp1, xp2, yp2); } } @@ -752,6 +760,8 @@ void plugdata_forward_message(void* x, t_symbol *s, int argc, t_atom *argv); static void pdlua_menu_open(t_pdlua *o) { #if PLUGDATA + // This is a more reliable method of finding out what file an object came from + // TODO: we might also want to use something like this for pd-vanilla? lua_getglobal(__L, "pd"); lua_getfield(__L, -1, "_whereami"); lua_pushstring(__L, o->pd.te_pd->c_name->s_name); @@ -859,6 +869,7 @@ static int pdlua_class_new(lua_State *L) c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET, A_GIMME, 0); + // Set custom widgetbehaviour for GUIs pdlua_widgetbehavior.w_getrectfn = pdlua_getrect; pdlua_widgetbehavior.w_displacefn = pdlua_displace; pdlua_widgetbehavior.w_selectfn = text_widgetbehavior.w_selectfn; @@ -916,6 +927,7 @@ static int pdlua_object_new(lua_State *L) o->gfx.height = 80; #if !PLUGDATA + // Init graphics state for pd o->gfx.num_path_segments = 0; o->gfx.scale_x = 1.0f; o->gfx.scale_y = 1.0f; @@ -925,6 +937,7 @@ static int pdlua_object_new(lua_State *L) o->gfx.mouse_drag_y = 0; o->gfx.mouse_down = 0; #else + // NULL until plugdata overrides them with something useful o->gfx.plugdata_draw_callback = NULL; o->gfx.plugdata_callback_target = NULL; #endif diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 5939a35..56c21d2 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -446,6 +446,8 @@ void pdlua_gfx_clear(t_pdlua *obj) { t_canvas *cnv = glist_getcanvas(obj->canvas); pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); gfx->current_paint_tag[0] = '\0'; + + glist_eraseiofor(glist_getcanvas(cnv), obj, gfx->object_tag); } static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x1, int* y1, int* x2, int* y2) { @@ -509,6 +511,13 @@ static int start_paint(lua_State* L) { } static int end_paint(lua_State* L) { + t_pdlua* obj = get_current_object(L); + + // 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, obj->gfx.object_tag, xpos, ypos, xpos + obj->gfx.width, ypos + obj->gfx.height); + return 0; } From 29a65dee6f81f37737966fff9cf49a04f7bbcdee Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 20:53:37 +0100 Subject: [PATCH 32/62] Cleaned up system for letting plugdata detect which classnames are pdlua objects --- pdlua.c | 16 ++++++++++++---- pdlua_gfx.h | 4 ---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pdlua.c b/pdlua.c index c2d0855..f4e6b06 100644 --- a/pdlua.c +++ b/pdlua.c @@ -108,6 +108,9 @@ // So we pass a data directory to the setup function instead and store it here #if PLUGDATA char plugdata_datadir[MAXPDSTRING]; + + // Hook to inform plugdata which class names are lua objects + void(*plugdata_register_class)(const char*); #endif /** Global Lua interpreter state, needed in the constructor. */ @@ -241,7 +244,7 @@ static int pdlua_loader_legacy (t_canvas *canvas, char *name); __declspec(dllexport) #endif #ifdef PLUGDATA -void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_gui_callback)(t_object*)); +void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_class_callback)(const char*)); #else void pdlua_setup (void); #endif @@ -868,7 +871,12 @@ static int pdlua_class_new(lua_State *L) PDLUA_DEBUG3("pdlua_class_new: L is %p, name is %s stack top is %d", L, name, lua_gettop(L)); c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET, A_GIMME, 0); - + + // Let plugdata know this class is a lua object +#if PLUGDATA + plugdata_register_class(name); +#endif + // Set custom widgetbehaviour for GUIs pdlua_widgetbehavior.w_getrectfn = pdlua_getrect; pdlua_widgetbehavior.w_displacefn = pdlua_displace; @@ -2121,7 +2129,7 @@ static int pdlua_loader_pathwise __declspec(dllexport) #endif #ifdef PLUGDATA -void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_gui_callback)(t_object*)) +void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_class_callback)(const char*)) #else void pdlua_setup(void) #endif @@ -2265,7 +2273,7 @@ void pdlua_setup(void) pdlua_gfx_setup(__L); #if PLUGDATA - pdlua_gfx_register_gui = register_gui_callback; + plugdata_register_class = register_class_callback; #endif PDLUA_DEBUG("pdlua_setup: end. stack top %d", lua_gettop(__L)); diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 56c21d2..bcf175e 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -165,9 +165,6 @@ static const luaL_Reg gfx_lib[] = { {NULL, NULL} // Sentinel to end the list }; -// Hook to inform plugdata which class names are lua GUIs -void(*pdlua_gfx_register_gui)(t_object*); - int pdlua_gfx_setup(lua_State* L) { // Register functions with Lua luaL_newlib(L, gfx_lib); @@ -190,7 +187,6 @@ void pdlua_gfx_clear(t_pdlua* obj) { static int gfx_initialize(t_pdlua* obj) { - pdlua_gfx_register_gui(obj); // let plugdata know that our classname is a GUI object pdlua_gfx_repaint(obj); // Initial repaint return 0; } From 3fe4dd7e5775c494b20ff1c5ed4473017255f089 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 20:59:29 +0100 Subject: [PATCH 33/62] Fixed plugdata crash --- pdlua.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pdlua.c b/pdlua.c index f4e6b06..87eb215 100644 --- a/pdlua.c +++ b/pdlua.c @@ -2170,6 +2170,8 @@ void pdlua_setup(void) snprintf(luaversionStr, MAXPDSTRING-1, "Using lua version %d.%d", lvm, lvl); #if PLUGDATA + plugdata_register_class = register_class_callback; + snprintf(versbuf, versbuf_length-1, #ifdef ELSE "pdlua %s ELSE (lua %d.%d)", @@ -2272,10 +2274,6 @@ void pdlua_setup(void) pdlua_gfx_setup(__L); -#if PLUGDATA - plugdata_register_class = register_class_callback; -#endif - PDLUA_DEBUG("pdlua_setup: end. stack top %d", lua_gettop(__L)); #ifndef PLUGDATA /* nw.js support. */ From eff49f2bfd8677e44de773eff96d3382505bbb53 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 21:46:43 +0100 Subject: [PATCH 34/62] Fixed for stroking lines in pd --- pdlua_gfx.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index bcf175e..6a565bf 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -908,8 +908,10 @@ static int stroke_path(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(obj) }; - pdgui_vmess(0, "crr r ri rs rs rS", cnv, "create", "polygon", coordinates, "-width", stroke_width, "-outline", gfx->current_color, "-fill", "", "-tags", 2, tags); + 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); + pdgui_vmess(0, "crs r", cnv, "coords", tags[1], coordinates); + freebytes(coordinates, totalSize+1); return 0; From 19428ddb5263f42cd4117eea5dfc399892843e5c Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 22:40:42 +0100 Subject: [PATCH 35/62] Added documentation --- doc/graphics.txt | 111 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 doc/graphics.txt diff --git a/doc/graphics.txt b/doc/graphics.txt new file mode 100644 index 0000000..2372136 --- /dev/null +++ b/doc/graphics.txt @@ -0,0 +1,111 @@ +pdlua graphics +=== + +pdlua's graphics allow you to draw basic vector graphics and receive mouse events on pure-data and plugdata. Drawing functions are prefixed with 'gfx.', and should only be called from within the "paint" function + +You can enable GUI mode by setting the self.gui variable in the constructor, like this: + +function yourclass:initialize(sel, atoms) + self.inlets = 1 + self.gui = 1 + return true +end + +Painting: +-------------- +You can paint by defining the "paint" function, for example: + +function yourclass:paint() + gfx.set_color(250, 200, 240) + gfx.fill_all() +end + + +Mouse Events: +-------------- +You can receive mouse events by defining the "mouse_down", "mouse_up", "mouse_move" and "mouse_drag" functions. Both pass the x,y coordinates as arguments. For example: + +function yourclass:mouse_down(x, y) + pd.post(tostring(x)) + pd.post(tostring(y)) +end + + +API overview +-------------- +-- Callback functions you can define +paint() -- Paint callback. This is the only place where you're allowed to call any of the paint functions +pd:Class:mouse_down(x, y) -- Mouse down callback, called when the mouse is clicked +pd:Class:mouse_up(x, y) -- Mouse up callback, called when the mouse button is released +pd:Class:mouse_move(x, y) -- Mouse move callback, called when the mouse is moved while not being down +pd:Class:mouse_drag(x, y) -- Mouse drag callback, called when the mouse is moved while also being down + +-- Functions you can call +pd:Class:repaint() -- Request a repaint, after this the "paint" callback will occur + +gfx.set_size(w, h) -- Sets the size of the drawing object. + +gfx.set_color(r, g, b, a=1.0) -- Sets the color for the next drawing operation. + +gfx.fill_ellipse(x, y, w, h) -- Draws a filled ellipse at the specified position and size. +gfx.stroke_ellipse(x, y, w, h, line_width) -- Draws the outline of an ellipse at the specified position and size. + +gfx.fill_rect(x, y, w, h) -- Draws a filled rectangle at the specified position and size. +gfx.stroke_rect(x, y, w, h, line_width) -- Draws the outline of a rectangle at the specified position and size. + +gfx.fill_rounded_rect(x, y, w, h, corner_radius) -- Draws a filled rounded rectangle at the specified position and size. +gfx.troke_rounded_rect(x, y, w, h, corner_radius, line_width) -- Draws the outline of a rounded rectangle at the specified position and size. + +gfx.draw_line(x1, y1, x2, y2) -- Draws a line between two points. +gfx.draw_text(text, x, y, w, fontsize) -- Draws text at the specified position and size. + +gfx.start_path(x, y) -- Initiates a new path at the specified point. +gfx.line_to(x, y) -- Adds a line segment to the current path. +gfx.quad_to(x1, y1, x2, y2) -- Adds a quadratic Bezier curve to the current path. +gfx.cubic_to(x1, y1, x2, y2, x3, y) -- Adds a cubic Bezier curve to the current path. +gfx.close_path() -- Closes the current path. +gfx.stroke_path(line_width) -- Draws the outline of the current path with the specified line width. +gfx.fill_path() -- Fills the current path. + +gfx.fill_all() -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) + +gfx.translate(tx, ty) -- Translates the coordinate system by the specified amounts. +gfx.scale(sx, sy) -- Scales the coordinate system by the specified factors. This will always happen after the translation +gfx.reset_transform() -- Resets current scale and translation + + +Basic example +--------------------- + +-- Create an object named "example" +local example = pd.Class:new():register("example") + +function example:initialize(sel, atoms) + self.inlets = 1 + self.circle_x = 10 + self.circle_y = 10 + + -- Enabled GUI mode + self.gui = 1 + return true +end + +-- Receive mouse drag events +function example:mouse_drag(x, y) + -- Set circle position + self.circle_x = x - 15 + self.circle_y = y - 15 + + -- Request a repaint + self:repaint() +end + +function example:paint() + -- Fill background with color + gfx.set_color(255, 0, 0, 1) + gfx.fill_all() + + -- Draw an ellipse + gfx.set_color(0, 255, 0, 1) + gfx.fill_ellipse(self.circle_x, self.circle_y, 30, 30) +end \ No newline at end of file From cf13e1ec125a6e593ffef9f8ec1d409946c17a9e Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 23:15:16 +0100 Subject: [PATCH 36/62] Updated helpfile --- doc/graphics.txt | 12 ++++----- pdlua-help.pd | 65 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/doc/graphics.txt b/doc/graphics.txt index 2372136..1f688cd 100644 --- a/doc/graphics.txt +++ b/doc/graphics.txt @@ -34,11 +34,11 @@ end API overview -------------- -- Callback functions you can define -paint() -- Paint callback. This is the only place where you're allowed to call any of the paint functions -pd:Class:mouse_down(x, y) -- Mouse down callback, called when the mouse is clicked -pd:Class:mouse_up(x, y) -- Mouse up callback, called when the mouse button is released -pd:Class:mouse_move(x, y) -- Mouse move callback, called when the mouse is moved while not being down -pd:Class:mouse_drag(x, y) -- Mouse drag callback, called when the mouse is moved while also being down +paint() -- Paint callback. This is the only place where you're allowed to call any of the paint functions +pd:Class:mouse_down(x, y) -- Mouse down callback, called when the mouse is clicked +pd:Class:mouse_up(x, y) -- Mouse up callback, called when the mouse button is released +pd:Class:mouse_move(x, y) -- Mouse move callback, called when the mouse is moved while not being down +pd:Class:mouse_drag(x, y) -- Mouse drag callback, called when the mouse is moved while also being down -- Functions you can call pd:Class:repaint() -- Request a repaint, after this the "paint" callback will occur @@ -54,7 +54,7 @@ gfx.fill_rect(x, y, w, h) -- Draws a f gfx.stroke_rect(x, y, w, h, line_width) -- Draws the outline of a rectangle at the specified position and size. gfx.fill_rounded_rect(x, y, w, h, corner_radius) -- Draws a filled rounded rectangle at the specified position and size. -gfx.troke_rounded_rect(x, y, w, h, corner_radius, line_width) -- Draws the outline of a rounded rectangle at the specified position and size. +gfx.troke_rounded_rect(x, y, w, h, corner_radius, line_width) -- Draws the outline of a rounded rectangle at the specified position and size. gfx.draw_line(x1, y1, x2, y2) -- Draws a line between two points. gfx.draw_text(text, x, y, w, fontsize) -- Draws text at the specified position and size. diff --git a/pdlua-help.pd b/pdlua-help.pd index ab0d4fa..34c3780 100644 --- a/pdlua-help.pd +++ b/pdlua-help.pd @@ -1,35 +1,33 @@ #N canvas 467 36 561 528 10; #X declare -lib pdlua -path pdlua; #X declare -path pdlua/examples; -#X msg 55 227 load hello.lua; #X text 16 358 See also:; -#X obj 351 234 hello; #X obj 55 257 pdlua; #X obj 81 359 pdluax hello; -#X obj 4 397 cnv 3 550 3 empty empty inlets 8 12 0 13 #dcdcdc #000000 0; -#X obj 4 432 cnv 3 550 3 empty empty outlets 8 12 0 13 #dcdcdc #000000 0; -#X obj 4 467 cnv 3 550 3 empty empty arguments 8 12 0 13 #dcdcdc #000000 0; -#X obj 143 406 cnv 17 3 17 empty empty 0 5 9 0 16 #dcdcdc #9c9c9c 0; -#X obj 4 500 cnv 15 552 21 empty empty empty 20 12 0 14 #e0e0e0 #202020 0; -#X text 243 442 NONE; -#X text 243 477 NONE; -#X text 177 407 load ; +#X obj 6 446 cnv 3 550 3 empty empty inlets 8 12 0 13 #dcdcdc #000000 0; +#X obj 6 481 cnv 3 550 3 empty empty outlets 8 12 0 13 #dcdcdc #000000 0; +#X obj 6 516 cnv 3 550 3 empty empty arguments 8 12 0 13 #dcdcdc #000000 0; +#X obj 145 455 cnv 17 3 17 empty empty 0 5 9 0 16 #dcdcdc #9c9c9c 0; +#X obj 6 549 cnv 15 552 21 empty empty empty 20 12 0 14 #e0e0e0 #202020 0; +#X text 245 491 NONE; +#X text 245 526 NONE; +#X text 179 456 load ; #X text 151 226 <-- load and run a Lua file; #X text 91 257 <-- global interface to pdlua; #X obj 306 4 cnv 15 250 40 empty empty empty 12 13 0 18 #7c7c7c #e0e4dc 0; #N canvas 382 141 749 319 (subpatch) 0; -#X coords 0 -1 1 1 252 42 2 100 100; +#X coords 0 -1 1 1 249 39 2 100 100; #X restore 305 3 pd; #X obj 315 18 cnv 10 10 10 empty empty Load\ externals\ written\ in\ Lua 0 6 2 20 #7c7c7c #e0e4dc 0; #X obj 3 3 cnv 15 301 42 empty empty pdlua 20 20 2 37 #e0e0e0 #000000 0; #N canvas 0 22 450 278 (subpatch) 0; -#X coords 0 1 100 -1 302 42 1 0 0; +#X coords 0 1 100 -1 299 39 1 0 0; #X restore 3 3 graph; #X text 390 233 <- right click for help or open, f 16; #X text 22 60 [pdlua] registers a loader that allows Pd externals written in Lua (with the "*.pd_lua" extension) to be loaded. To guarantee Pd will load these externals \, you should load [pdlua] as a library \, either at startup or with [delcare]:, f 80; #X text 75 114 loading [pdlua] from [declare] -->; -#X text 261 407 - load and run a '*.lua' file; -#X text 334 346 Details on writting lua externals ----->, f 23; +#X text 263 456 - load and run a '*.lua' file; +#X text 324 344 Details on writting lua externals ----->, f 23; #X text 23 291 The [hello] object above is loaded from a 'hello.pd_lua' file. You can also provide help files for it and right click on it to ask for them. Right click also allows you to open the .pd_lua file if your system has a known application for it., f 80; #X text 164 353 (this version loads files as arguments), f 20; #X obj 290 115 declare -lib pdlua -path pdlua; @@ -152,16 +150,16 @@ #X obj 46 2226 cnv 17 450 20 empty empty empty 20 12 0 14 #cccccc #404040 0; #X text 71 2229 (modified from doc/examples/pdlua/lua.txt by mrpeach 2011/10/06), f 64; #N canvas 0 22 450 278 (subpatch) 0; -#X coords 0 1 100 -1 167 101 1; +#X coords 0 1 100 -1 167 101 1 0 0; #X restore 497 1661 graph; #N canvas 0 22 450 278 (subpatch) 0; -#X coords 0 1 100 -1 167 120 1; +#X coords 0 1 100 -1 167 120 1 0 0; #X restore 497 1762 graph; #N canvas 0 22 450 278 (subpatch) 0; -#X coords 0 1 100 -1 167 121 1; +#X coords 0 1 100 -1 167 121 1 0 0; #X restore 497 1882 graph; #N canvas 0 22 450 278 (subpatch) 0; -#X coords 0 1 100 -1 451 2163 1; +#X coords 0 1 100 -1 451 2163 1 0 0; #X restore 46 82 graph; #X text 69 21 Find basic instructions/examples below on how to write externals in Lua. For a detailed tutorial \, check the 'pdlua/tutorial' folder for a PDF documente. You can also find this tutorial online at: https://agraef.github.io/pd-lua/tutorial/pd-lua-intro.html, f 66; #X obj 515 1624 declare -path pdlua/examples; @@ -175,5 +173,32 @@ #X connect 110 0 65 0; #X connect 111 0 65 0; #X connect 112 0 67 0; -#X restore 438 364 pd quickstart; -#X connect 0 0 3 0; +#X restore 446 359 pd quickstart; +#N canvas 0 22 450 300 graphics 1; +#X obj 8 77 hello-gui; +#X text 8 323 You can enable GUI mode by setting the self.gui variable in the constructor \, like this:; +#X text 8 366 function yourclass:initialize(sel \, atoms); +#X text 25 383 self.gui = 1; +#X text 25 400 return true; +#X text 8 418 end; +#X text 8 457 -------------- Painting: --------------; +#X text 8 480 You can paint by defining the "paint" function \, for example:; +#X text 8 561 end; +#X text 8 509 function yourclass:paint(); +#X text 25 526 gfx.set_color(250 \, 200 \, 240); +#X text 25 543 gfx.fill_all(); +#X text 8 604 -------------- Mouse Events: --------------; +#X text 8 633 You can receive mouse events by defining the "mouse_down" \, "mouse_up" \, "mouse_move" and "mouse_drag" functions. Both pass the x \, y coordinates as arguments. For example:; +#X text 8 743 end; +#X text 8 691 function yourclass:mouse_down(x \, y); +#X text 25 708 pd.post(tostring(x)); +#X text 25 725 pd.post(tostring(y)); +#X text 8 11 pdlua's graphics allow you to draw basic vector graphics and receive mouse events on pure-data and plugdata. Drawing functions are prefixed with 'gfx.' \, and should only be called from within the "paint" function, f 72; +#X text 8 788 -------------- API: --------------; +#X text 356 816 -- Paint callback. This is the only place where you're allowed to call any of the paint functions \; \; -- Mouse down callback \, called when the mouse is clicked \; -- Mouse up callback \, called when the mouse button is released \; -- Mouse move callback \, called when the mouse is moved while not being down \; -- Mouse drag callback \, called when the mouse is moved while also being down \; \; \; \; -- Request a repaint \, after this the "paint" callback will occur \; \; -- Sets the size of the drawing object. \; -- Sets the color for the next drawing operation. \; \; -- Draws a filled ellipse at the specified position and size. \; -- Draws the outline of an ellipse at the specified position and size. \; -- Draws a filled rectangle at the specified position and size. \; -- Draws the outline of a rectangle at the specified position and size. \; -- Draws a filled rounded rectangle at the specified position and size. \; -- Draws the outline of a rounded rectangle at the specified position and size. \; \; -- Draws a line between two points. \; -- Draws text at the specified position and size. \; \; -- Initiates a new path at the specified point. \; -- Adds a line segment to the current path. \; -- Adds a quadratic Bezier curve to the current path. \; -- Adds a cubic Bezier curve to the current path. \; -- Closes the current path. \; -- Draws the outline of the current path with the specified line width. \; -- Fills the current path. \; \; -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) \; \; -- Translates the coordinate system by the specified amounts. \; -- Scales the coordinate system by the specified factors. This will always happen after the translation \; -- Resets current scale and translation \;, f 134; +#X text 8 816 paint() \; \; pd:Class:mouse_down(x \, y) \; pd:Class:mouse_up(x \, y) \; pd:Class:mouse_move(x \, y) \; pd:Class:mouse_drag(x \, y) \; \; -- Functions you can call \; \; pd:Class:repaint() \; \; gfx.set_size(w \, h) \; gfx.set_color(r \, g \, b \, a=1.0) \; \; gfx.fill_ellipse(x \, y \, w \, h) \; gfx.stroke_ellipse(x \, y \, w \, h \, line_width) \; gfx.fill_rect(x \, y \, w \, h) \; gfx.stroke_rect(x \, y \, w \, h \, line_width) \; gfx.fill_rounded_rect(x \, y \, w \, h \, corner_radius) \; gfx.stroke_rounded_rect(x \, y \, w \, h \, corner_radius \, line_width) \; \; gfx.draw_line(x1 \, y1 \, x2 \, y2) \; gfx.draw_text(text \, x \, y \, w \, fontsize) \; \; gfx.start_path(x \, y) \; gfx.line_to(x \, y) \; gfx.quad_to(x1 \, y1 \, x2 \, y2) \; gfx.cubic_to(x1 \, y1 \, x2 \, y2 \, x3 \, y) \; gfx.close_path() \; gfx.stroke_path(line_width) \; gfx.fill_path() \; \; gfx.fill_all() \; \; gfx.translate(tx \, ty) \; gfx.scale(sx \, sy) \; gfx.reset_transform() \;, f 58; +#X restore 446 409 pd graphics; +#X text 324 384 Details on how to create GUI objects ------->, f 18; +#X obj 342 227 hello; +#X msg 55 227 load hello-gui.pd_lua; +#X connect 31 0 1 0; From a98b11481c34e6e8cf69fb4d0d344e2039dfcb33 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 22 Dec 2023 23:32:13 +0100 Subject: [PATCH 37/62] Updated pd.lua --- pd.lua | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/pd.lua b/pd.lua index c92cd1c..c56b994 100644 --- a/pd.lua +++ b/pd.lua @@ -20,12 +20,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -- storage for Pd C<->Lua interaction pd._classes = { } -- take absolute paths and turn them into classes +pd._fullpaths = { } pd._pathnames = { } -- look up absolute path by creation name pd._objects = { } pd._clocks = { } pd._receives = { } pd._loadpath = "" -pd._fullpath = "" +pd._currentpath = "" -- add a path to Lua's "require" search paths pd._setrequirepath = function(path) @@ -120,14 +121,15 @@ pd._whoami = function (object) end end ---whoami method dispatcher -pd._whereami = function (object) - if nil ~= pd._objects[object] then - return pd._objects[object]._fullpath - end +--whereami method dispatcher +pd._whereami = function(name) + if nil ~= pd._fullpaths[name] then + return pd._fullpaths[name] + end + + return nil end - --class method dispatcher pd._get_class = function (object) if nil ~= pd._objects[object] then @@ -299,18 +301,20 @@ function pd.Class:register(name) else regname = name end + + --pd._fullpaths[regname] = pd._currentpath or (fullname .. ".pd_lua") + if pd._currentpath == nil or pd._currentpath == '' then + pd._fullpaths[regname] = fullname .. ".pd_lua" + else + pd._fullpaths[regname] = pd._currentpath + end + pd._pathnames[regname] = fullname pd._classes[fullname] = self -- record registration self._class = pd._register(name) -- register new class self._name = name + self._path = pd._fullpaths[regname] self._loadpath = fullpath - - if pd._fullpath == nil or pd._fullpath == '' then - self._fullpath = fullname .. ".pd_lua" - else - self._fullpath = self._currentpath - end - if name == "pdlua" then self._scriptname = "pd.lua" else @@ -401,9 +405,8 @@ function pd.Class:dofilex(file) local pathsave = pd._loadpath pd._loadname = nil pd._loadpath = self._loadpath - pd._fullpath = file + pd._currentpath = file local f, path = pd._dofilex(self._class, file) - pd._fullpath = = nil pd._loadname = namesave pd._loadpath = pathsave return f, path @@ -417,9 +420,8 @@ function pd.Class:dofile(file) local pathsave = pd._loadpath pd._loadname = nil pd._loadpath = self._loadpath - pd._fullpath = file + pd._currentpath = file local f, path = pd._dofile(self._object, file) - pd._fullpath = = nil pd._loadname = namesave pd._loadpath = pathsave return f, path @@ -433,8 +435,8 @@ function pd.Class:whoami() return self._scriptname or self._name end -function pd.Class:in_1_reload() - return self:dofile(self._fullpath) +function pd.Class:in_1__reload() + self:dofile(self._path) end function pd.Class:get_class() -- accessor for t_class* @@ -454,7 +456,6 @@ function lua:in_1_load(atoms) -- execute a script self:dofile(atoms[1]) end - local luax = pd.Class:new():register("pdluax") -- classless lua externals (like [pdluax foo]) function luax:initialize(sel, atoms) -- motivation: pd-list 2007-09-23 From 2f9f6d45561e3a5be8a45cf9d1e42e8fff56fffc Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Sat, 23 Dec 2023 05:18:36 +0100 Subject: [PATCH 38/62] Add example file --- pdlua/hello-gui.pd_lua | 1 + 1 file changed, 1 insertion(+) create mode 100644 pdlua/hello-gui.pd_lua diff --git a/pdlua/hello-gui.pd_lua b/pdlua/hello-gui.pd_lua new file mode 100644 index 0000000..4ae47fb --- /dev/null +++ b/pdlua/hello-gui.pd_lua @@ -0,0 +1 @@ +local hello = pd.Class:new():register("hello") function hello:initialize(sel, atoms) self.inlets = 1 self.circle_x = 480 self.circle_y = 15 self.circle_radius = 15 self.animation_speed = 2 self.draggable_rect_x = 550 self.draggable_rect_y = 130 self.draggable_rect_size = 50 self.dragging_rect = false self.mouse_down_pos = {0, 0} self.rect_down_pos = {0, 0} self.gui = 1 gfx.set_size(630, 230) return true end function math.clamp(val, lower, upper) if lower > upper then lower, upper = upper, lower end -- swap if boundaries supplied the wrong way return math.max(lower, math.min(upper, val)) end function hello:postinitialize() self.clock = pd.Clock:new():register(self, "tick") self.clock:delay(16) end function hello:finalize() self.clock:destruct() end function hello:mouse_down(x, y) if x > self.draggable_rect_x and y > self.draggable_rect_y and x < self.draggable_rect_x + self.draggable_rect_size and y < self.draggable_rect_y + self.draggable_rect_size then dragging_rect = true self.mouse_down_pos[0] = x self.mouse_down_pos[1] = y self.rect_down_pos[0] = self.draggable_rect_x self.rect_down_pos[1] = self.draggable_rect_y else dragging_rect = false end end function hello:mouse_drag(x, y) if dragging_rect == true then self.draggable_rect_x = self.rect_down_pos[0] + (x - self.mouse_down_pos[0]) self.draggable_rect_y = self.rect_down_pos[1] + (y - self.mouse_down_pos[1]) self.draggable_rect_x = math.clamp(self.draggable_rect_x, 0, 620 - self.draggable_rect_size) self.draggable_rect_y = math.clamp(self.draggable_rect_y, 0, 230 - self.draggable_rect_size) self:repaint() end end function hello:paint() gfx.set_color(250, 200, 240) gfx.fill_all() -- Filled examples gfx.set_color(66, 207, 201, 0.3) gfx.fill_ellipse(30, 50, 30, 30) gfx.set_color(0, 159, 147, 1) gfx.fill_rect(120, 50, 30, 30) gfx.set_color(250, 84, 108, 1) gfx.fill_rounded_rect(210, 50, 30, 30, 5) gfx.set_color(252, 118, 81, 1) -- Star using line_to paths local starX1, starY1 = 310, 45 local starSize = 15 gfx.start_path(starX1, starY1) -- Star using line_to paths gfx.line_to(starX1 + 5, starY1 + 14) gfx.line_to(starX1 + 20, starY1 + 14) gfx.line_to(starX1 + 8, starY1 + 22) gfx.line_to(starX1 + 14, starY1 + 36) gfx.line_to(starX1, starY1 + 27) gfx.line_to(starX1 - 14, starY1 + 36) gfx.line_to(starX1 - 6, starY1 + 22) gfx.line_to(starX1 - 18, starY1 + 14) gfx.line_to(starX1 - 3, starY1 + 14) gfx.close_path() gfx.fill_path() gfx.set_color(255, 219, 96, 1) -- Bezier curve example gfx.translate(140, 20) gfx.scale(0.5, 1.0) gfx.start_path(450, 50) gfx.cubic_to(500, 30, 550, 70, 600, 50) gfx.close_path() gfx.stroke_path(2) gfx.reset_transform() -- Stroked examples gfx.set_color(66, 207, 201, 1) gfx.stroke_ellipse(30, 150, 30, 30, 2) gfx.set_color(0, 159, 147, 1) gfx.stroke_rect(120, 150, 30, 30, 2) gfx.set_color(250, 84, 108, 1) gfx.stroke_rounded_rect(210, 150, 30, 30, 5, 2) gfx.set_color(252, 118, 81, 1) local starX2, starY2 = 310, 145 local starSize = 15 -- Star using line_to paths gfx.start_path(starX2, starY2) gfx.line_to(starX2 + 5, starY2 + 14) gfx.line_to(starX2 + 20, starY2 + 14) gfx.line_to(starX2 + 8, starY2 + 22) gfx.line_to(starX2 + 14, starY2 + 36) gfx.line_to(starX2, starY2 + 27) gfx.line_to(starX2 - 14, starY2 + 36) gfx.line_to(starX2 - 6, starY2 + 22) gfx.line_to(starX2 - 18, starY2 + 14) gfx.line_to(starX2 - 3, starY2 + 14) gfx.close_path() gfx.stroke_path(2) gfx.set_color(255, 219, 96, 1) -- Bezier curve example gfx.translate(140, 20) gfx.scale(0.5, 1.0) gfx.start_path(450, 150) gfx.cubic_to(500, 130, 550, 170, 600, 150) gfx.fill_path() gfx.reset_transform() -- Draggable rectangle gfx.set_color(66, 207, 201, 1) gfx.fill_rounded_rect(self.draggable_rect_x, self.draggable_rect_y, self.draggable_rect_size, self.draggable_rect_size, 5) gfx.set_color(0, 0, 0, 1) gfx.draw_text("Drag me!", self.draggable_rect_x + 8, self.draggable_rect_y + 20, self.draggable_rect_size, 10) -- Titles gfx.set_color(252, 118, 81, 1) gfx.draw_text("Ellipse", 32, 190, 120, 10) gfx.draw_text("Rectangle", 116, 190, 120, 10) gfx.draw_text("Rounded Rectangle", 188, 190, 120, 10) gfx.draw_text("Paths", 300, 190, 120, 10) gfx.draw_text("Bezier Paths", 380, 190, 120, 10) gfx.draw_text("Animation", 470, 190, 120, 10) gfx.draw_text("Mouse Interaction", 540, 190, 120, 10) gfx.set_color(250, 84, 108, 1) gfx.fill_ellipse(self.circle_x, self.circle_y, self.circle_radius, self.circle_radius) end function hello:tick() self.circle_y = self.circle_y + self.animation_speed if self.circle_y > 160 + self.circle_radius then self.circle_y = -self.circle_radius end self:repaint() self.clock:delay(16) end function hello:in_1_bang() self:repaint() end \ No newline at end of file From 8410b57a2d038af81662f38b4221581ef69b3b74 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Sat, 23 Dec 2023 05:28:37 +0100 Subject: [PATCH 39/62] Example fix --- pdlua/hello-gui.pd_lua | 171 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) diff --git a/pdlua/hello-gui.pd_lua b/pdlua/hello-gui.pd_lua index 4ae47fb..962fbd1 100644 --- a/pdlua/hello-gui.pd_lua +++ b/pdlua/hello-gui.pd_lua @@ -1 +1,170 @@ -local hello = pd.Class:new():register("hello") function hello:initialize(sel, atoms) self.inlets = 1 self.circle_x = 480 self.circle_y = 15 self.circle_radius = 15 self.animation_speed = 2 self.draggable_rect_x = 550 self.draggable_rect_y = 130 self.draggable_rect_size = 50 self.dragging_rect = false self.mouse_down_pos = {0, 0} self.rect_down_pos = {0, 0} self.gui = 1 gfx.set_size(630, 230) return true end function math.clamp(val, lower, upper) if lower > upper then lower, upper = upper, lower end -- swap if boundaries supplied the wrong way return math.max(lower, math.min(upper, val)) end function hello:postinitialize() self.clock = pd.Clock:new():register(self, "tick") self.clock:delay(16) end function hello:finalize() self.clock:destruct() end function hello:mouse_down(x, y) if x > self.draggable_rect_x and y > self.draggable_rect_y and x < self.draggable_rect_x + self.draggable_rect_size and y < self.draggable_rect_y + self.draggable_rect_size then dragging_rect = true self.mouse_down_pos[0] = x self.mouse_down_pos[1] = y self.rect_down_pos[0] = self.draggable_rect_x self.rect_down_pos[1] = self.draggable_rect_y else dragging_rect = false end end function hello:mouse_drag(x, y) if dragging_rect == true then self.draggable_rect_x = self.rect_down_pos[0] + (x - self.mouse_down_pos[0]) self.draggable_rect_y = self.rect_down_pos[1] + (y - self.mouse_down_pos[1]) self.draggable_rect_x = math.clamp(self.draggable_rect_x, 0, 620 - self.draggable_rect_size) self.draggable_rect_y = math.clamp(self.draggable_rect_y, 0, 230 - self.draggable_rect_size) self:repaint() end end function hello:paint() gfx.set_color(250, 200, 240) gfx.fill_all() -- Filled examples gfx.set_color(66, 207, 201, 0.3) gfx.fill_ellipse(30, 50, 30, 30) gfx.set_color(0, 159, 147, 1) gfx.fill_rect(120, 50, 30, 30) gfx.set_color(250, 84, 108, 1) gfx.fill_rounded_rect(210, 50, 30, 30, 5) gfx.set_color(252, 118, 81, 1) -- Star using line_to paths local starX1, starY1 = 310, 45 local starSize = 15 gfx.start_path(starX1, starY1) -- Star using line_to paths gfx.line_to(starX1 + 5, starY1 + 14) gfx.line_to(starX1 + 20, starY1 + 14) gfx.line_to(starX1 + 8, starY1 + 22) gfx.line_to(starX1 + 14, starY1 + 36) gfx.line_to(starX1, starY1 + 27) gfx.line_to(starX1 - 14, starY1 + 36) gfx.line_to(starX1 - 6, starY1 + 22) gfx.line_to(starX1 - 18, starY1 + 14) gfx.line_to(starX1 - 3, starY1 + 14) gfx.close_path() gfx.fill_path() gfx.set_color(255, 219, 96, 1) -- Bezier curve example gfx.translate(140, 20) gfx.scale(0.5, 1.0) gfx.start_path(450, 50) gfx.cubic_to(500, 30, 550, 70, 600, 50) gfx.close_path() gfx.stroke_path(2) gfx.reset_transform() -- Stroked examples gfx.set_color(66, 207, 201, 1) gfx.stroke_ellipse(30, 150, 30, 30, 2) gfx.set_color(0, 159, 147, 1) gfx.stroke_rect(120, 150, 30, 30, 2) gfx.set_color(250, 84, 108, 1) gfx.stroke_rounded_rect(210, 150, 30, 30, 5, 2) gfx.set_color(252, 118, 81, 1) local starX2, starY2 = 310, 145 local starSize = 15 -- Star using line_to paths gfx.start_path(starX2, starY2) gfx.line_to(starX2 + 5, starY2 + 14) gfx.line_to(starX2 + 20, starY2 + 14) gfx.line_to(starX2 + 8, starY2 + 22) gfx.line_to(starX2 + 14, starY2 + 36) gfx.line_to(starX2, starY2 + 27) gfx.line_to(starX2 - 14, starY2 + 36) gfx.line_to(starX2 - 6, starY2 + 22) gfx.line_to(starX2 - 18, starY2 + 14) gfx.line_to(starX2 - 3, starY2 + 14) gfx.close_path() gfx.stroke_path(2) gfx.set_color(255, 219, 96, 1) -- Bezier curve example gfx.translate(140, 20) gfx.scale(0.5, 1.0) gfx.start_path(450, 150) gfx.cubic_to(500, 130, 550, 170, 600, 150) gfx.fill_path() gfx.reset_transform() -- Draggable rectangle gfx.set_color(66, 207, 201, 1) gfx.fill_rounded_rect(self.draggable_rect_x, self.draggable_rect_y, self.draggable_rect_size, self.draggable_rect_size, 5) gfx.set_color(0, 0, 0, 1) gfx.draw_text("Drag me!", self.draggable_rect_x + 8, self.draggable_rect_y + 20, self.draggable_rect_size, 10) -- Titles gfx.set_color(252, 118, 81, 1) gfx.draw_text("Ellipse", 32, 190, 120, 10) gfx.draw_text("Rectangle", 116, 190, 120, 10) gfx.draw_text("Rounded Rectangle", 188, 190, 120, 10) gfx.draw_text("Paths", 300, 190, 120, 10) gfx.draw_text("Bezier Paths", 380, 190, 120, 10) gfx.draw_text("Animation", 470, 190, 120, 10) gfx.draw_text("Mouse Interaction", 540, 190, 120, 10) gfx.set_color(250, 84, 108, 1) gfx.fill_ellipse(self.circle_x, self.circle_y, self.circle_radius, self.circle_radius) end function hello:tick() self.circle_y = self.circle_y + self.animation_speed if self.circle_y > 160 + self.circle_radius then self.circle_y = -self.circle_radius end self:repaint() self.clock:delay(16) end function hello:in_1_bang() self:repaint() end \ No newline at end of file +local hello = pd.Class:new():register("hello-gui") + +function hello:initialize(sel, atoms) + self.inlets = 1 + + self.circle_x = 480 + self.circle_y = 15 + self.circle_radius = 15 + self.animation_speed = 2 + + self.draggable_rect_x = 550 + self.draggable_rect_y = 130 + self.draggable_rect_size = 50 + self.dragging_rect = false + self.mouse_down_pos = {0, 0} + self.rect_down_pos = {0, 0} + + self.gui = 1 + gfx.set_size(630, 230) + return true +end + +function math.clamp(val, lower, upper) + if lower > upper then lower, upper = upper, lower end -- swap if boundaries supplied the wrong way + return math.max(lower, math.min(upper, val)) +end + +function hello:postinitialize() + self.clock = pd.Clock:new():register(self, "tick") + self.clock:delay(16) +end + +function hello:finalize() + self.clock:destruct() +end + +function hello:mouse_down(x, y) + if x > self.draggable_rect_x and y > self.draggable_rect_y and x < self.draggable_rect_x + self.draggable_rect_size and y < self.draggable_rect_y + self.draggable_rect_size then + dragging_rect = true + self.mouse_down_pos[0] = x + self.mouse_down_pos[1] = y + self.rect_down_pos[0] = self.draggable_rect_x + self.rect_down_pos[1] = self.draggable_rect_y + else + dragging_rect = false + end +end + +function hello:mouse_drag(x, y) + if dragging_rect == true then + self.draggable_rect_x = self.rect_down_pos[0] + (x - self.mouse_down_pos[0]) + self.draggable_rect_y = self.rect_down_pos[1] + (y - self.mouse_down_pos[1]) + self.draggable_rect_x = math.clamp(self.draggable_rect_x, 0, 620 - self.draggable_rect_size) + self.draggable_rect_y = math.clamp(self.draggable_rect_y, 0, 230 - self.draggable_rect_size) + self:repaint() + end +end + +function hello:paint() + gfx.set_color(250, 200, 240) + gfx.fill_all() + + -- Filled examples + gfx.set_color(66, 207, 201, 0.3) + gfx.fill_ellipse(30, 50, 30, 30) + gfx.set_color(0, 159, 147, 1) + gfx.fill_rect(120, 50, 30, 30) + gfx.set_color(250, 84, 108, 1) + gfx.fill_rounded_rect(210, 50, 30, 30, 5) + + gfx.set_color(252, 118, 81, 1) + + -- Star using line_to paths + local starX1, starY1 = 310, 45 + local starSize = 15 + + gfx.start_path(starX1, starY1) + + -- Star using line_to paths + gfx.line_to(starX1 + 5, starY1 + 14) + gfx.line_to(starX1 + 20, starY1 + 14) + gfx.line_to(starX1 + 8, starY1 + 22) + gfx.line_to(starX1 + 14, starY1 + 36) + gfx.line_to(starX1, starY1 + 27) + gfx.line_to(starX1 - 14, starY1 + 36) + gfx.line_to(starX1 - 6, starY1 + 22) + gfx.line_to(starX1 - 18, starY1 + 14) + gfx.line_to(starX1 - 3, starY1 + 14) + gfx.close_path() + gfx.fill_path() + + gfx.set_color(255, 219, 96, 1) + -- Bezier curve example + gfx.translate(140, 20) + gfx.scale(0.5, 1.0) + gfx.start_path(450, 50) + gfx.cubic_to(500, 30, 550, 70, 600, 50) + gfx.close_path() + gfx.stroke_path(2) + gfx.reset_transform() + + -- Stroked examples + gfx.set_color(66, 207, 201, 1) + gfx.stroke_ellipse(30, 150, 30, 30, 2) + gfx.set_color(0, 159, 147, 1) + gfx.stroke_rect(120, 150, 30, 30, 2) + gfx.set_color(250, 84, 108, 1) + gfx.stroke_rounded_rect(210, 150, 30, 30, 5, 2) + + gfx.set_color(252, 118, 81, 1) + + local starX2, starY2 = 310, 145 + local starSize = 15 + + -- Star using line_to paths + gfx.start_path(starX2, starY2) + gfx.line_to(starX2 + 5, starY2 + 14) + gfx.line_to(starX2 + 20, starY2 + 14) + gfx.line_to(starX2 + 8, starY2 + 22) + gfx.line_to(starX2 + 14, starY2 + 36) + gfx.line_to(starX2, starY2 + 27) + gfx.line_to(starX2 - 14, starY2 + 36) + gfx.line_to(starX2 - 6, starY2 + 22) + gfx.line_to(starX2 - 18, starY2 + 14) + gfx.line_to(starX2 - 3, starY2 + 14) + gfx.close_path() + gfx.stroke_path(2) + + gfx.set_color(255, 219, 96, 1) + -- Bezier curve example + gfx.translate(140, 20) + gfx.scale(0.5, 1.0) + gfx.start_path(450, 150) + gfx.cubic_to(500, 130, 550, 170, 600, 150) + gfx.fill_path() + gfx.reset_transform() + + -- Draggable rectangle + gfx.set_color(66, 207, 201, 1) + gfx.fill_rounded_rect(self.draggable_rect_x, self.draggable_rect_y, self.draggable_rect_size, self.draggable_rect_size, 5) + gfx.set_color(0, 0, 0, 1) + gfx.draw_text("Drag me!", self.draggable_rect_x + 8, self.draggable_rect_y + 20, self.draggable_rect_size, 10) + + -- Titles + gfx.set_color(252, 118, 81, 1) + gfx.draw_text("Ellipse", 32, 190, 120, 10) + gfx.draw_text("Rectangle", 116, 190, 120, 10) + gfx.draw_text("Rounded Rectangle", 188, 190, 120, 10) + gfx.draw_text("Paths", 300, 190, 120, 10) + gfx.draw_text("Bezier Paths", 380, 190, 120, 10) + gfx.draw_text("Animation", 470, 190, 120, 10) + gfx.draw_text("Mouse Interaction", 540, 190, 120, 10) + + gfx.set_color(250, 84, 108, 1) + gfx.fill_ellipse(self.circle_x, self.circle_y, self.circle_radius, self.circle_radius) +end + +function hello:tick() + self.circle_y = self.circle_y + self.animation_speed + if self.circle_y > 160 + self.circle_radius then + self.circle_y = -self.circle_radius + end + self:repaint() + self.clock:delay(16) +end + + +function hello:in_1_bang() + self:repaint() +end \ No newline at end of file From 5446dc0743194fbf1183dda0e6932f6dc3b7924e Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Mon, 15 Jan 2024 17:43:08 +0100 Subject: [PATCH 40/62] Added pd multi-instance support --- pdlua.c | 363 +++++++++++++++++++-------------------- pdlua.h | 5 +- pdlua_gfx.h | 50 +++--- pdlua_multi_instance.cpp | 42 +++++ 4 files changed, 248 insertions(+), 212 deletions(-) create mode 100644 pdlua_multi_instance.cpp diff --git a/pdlua.c b/pdlua.c index 87eb215..e7ef14f 100644 --- a/pdlua.c +++ b/pdlua.c @@ -113,9 +113,6 @@ void(*plugdata_register_class)(const char*); #endif -/** Global Lua interpreter state, needed in the constructor. */ -static lua_State *__L; - /** State for the Lua file reader. */ typedef struct pdlua_readerdata { @@ -397,30 +394,30 @@ static void pdlua_pushatomtable { int i; - PDLUA_DEBUG("pdlua_pushatomtable: stack top %d", lua_gettop(__L)); - lua_newtable(__L); + PDLUA_DEBUG("pdlua_pushatomtable: stack top %d", lua_gettop(__L())); + lua_newtable(__L()); for (i = 0; i < argc; ++i) { - lua_pushnumber(__L, i+1); + lua_pushnumber(__L(), i+1); switch (argv[i].a_type) { case A_FLOAT: - lua_pushnumber(__L, argv[i].a_w.w_float); + lua_pushnumber(__L(), argv[i].a_w.w_float); break; case A_SYMBOL: - lua_pushstring(__L, argv[i].a_w.w_symbol->s_name); + lua_pushstring(__L(), argv[i].a_w.w_symbol->s_name); break; case A_POINTER: /* FIXME: check experimentality */ - lua_pushlightuserdata(__L, argv[i].a_w.w_gpointer); + lua_pushlightuserdata(__L(), argv[i].a_w.w_gpointer); break; default: pd_error(NULL, "lua: zomg weasels!"); - lua_pushnil(__L); + lua_pushnil(__L()); break; } - lua_settable(__L, -3); + lua_settable(__L(), -3); } - PDLUA_DEBUG("pdlua_pushatomtable: end. stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_pushatomtable: end. stack top %d", lua_gettop(__L())); } static const char *basename(const char *name) @@ -463,13 +460,13 @@ static t_pdlua *pdlua_new } } - PDLUA_DEBUG("pdlua_new: start with stack top %d", lua_gettop(__L)); - lua_getglobal(__L, "pd"); - lua_getfield(__L, -1, "_checkbase"); - lua_pushstring(__L, s->s_name); - lua_pcall(__L, 1, 1, 0); - int needs_base = lua_toboolean(__L, -1); - lua_pop(__L, 1); /* pop boolean */ + PDLUA_DEBUG("pdlua_new: start with stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield(__L(), -1, "_checkbase"); + lua_pushstring(__L(), s->s_name); + lua_pcall(__L(), 1, 1, 0); + int needs_base = lua_toboolean(__L(), -1); + lua_pop(__L(), 1); /* pop boolean */ /* have to load the .pd_lua file for basename if another class is owning it */ if(needs_base) { char buf[MAXPDSTRING]; @@ -479,90 +476,90 @@ static t_pdlua *pdlua_new int fd = canvas_open(current, s->s_name, ".pd_lua", buf, &ptr, MAXPDSTRING, 1); if (fd >= 0) { - PDLUA_DEBUG("basename open: stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("basename open: stack top %d", lua_gettop(__L())); /* save old loadname, restore later in case of * nested loading */ int n, load_name_save, load_path_save; - lua_getfield(__L, -1, "_loadname"); - load_name_save = luaL_ref(__L, LUA_REGISTRYINDEX); - lua_pushnil(__L); - lua_setfield(__L, -2, "_loadname"); - lua_getfield(__L, -1, "_loadpath"); - load_path_save = luaL_ref(__L, LUA_REGISTRYINDEX); - lua_pushstring(__L, buf); - lua_setfield(__L, -2, "_loadpath"); + lua_getfield(__L(), -1, "_loadname"); + load_name_save = luaL_ref(__L(), LUA_REGISTRYINDEX); + lua_pushnil(__L()); + lua_setfield(__L(), -2, "_loadname"); + lua_getfield(__L(), -1, "_loadpath"); + load_path_save = luaL_ref(__L(), LUA_REGISTRYINDEX); + lua_pushstring(__L(), buf); + lua_setfield(__L(), -2, "_loadpath"); PDLUA_DEBUG("pdlua_new (basename load) path is %s", buf); //pdlua_setpathname(o, buf);/* change the scriptname to include its path - pdlua_setrequirepath(__L, buf); + pdlua_setrequirepath(__L(), buf); class_set_extern_dir(gensym(buf)); strncpy(buf, s->s_name, MAXPDSTRING - 8); strcat(buf, ".pd_lua"); reader.fd = fd; - n = lua_gettop(__L); + n = lua_gettop(__L()); #if LUA_VERSION_NUM < 502 - if (lua_load(__L, pdlua_reader, &reader, buf)) + if (lua_load(__L(), pdlua_reader, &reader, buf)) #else // 5.2 style - if (lua_load(__L, pdlua_reader, &reader, buf, NULL)) + if (lua_load(__L(), pdlua_reader, &reader, buf, NULL)) #endif // LUA_VERSION_NUM < 502 { close(fd); - pdlua_clearrequirepath(__L); - lua_error(__L); + pdlua_clearrequirepath(__L()); + lua_error(__L()); } else { - if (lua_pcall(__L, 0, LUA_MULTRET, 0)) + if (lua_pcall(__L(), 0, LUA_MULTRET, 0)) { - pd_error(NULL, "lua: error running `%s':\n%s", buf, lua_tostring(__L, -1)); - lua_pop(__L, 1); + pd_error(NULL, "lua: error running `%s':\n%s", buf, lua_tostring(__L(), -1)); + lua_pop(__L(), 1); close(fd); - pdlua_clearrequirepath(__L); + pdlua_clearrequirepath(__L()); } else { /* succeeded */ close(fd); - pdlua_clearrequirepath(__L); + pdlua_clearrequirepath(__L()); } } class_set_extern_dir(&s_); - lua_settop(__L, n); /* discard results of load */ - lua_rawgeti(__L, LUA_REGISTRYINDEX, load_path_save); - lua_setfield(__L, -2, "_loadpath"); - luaL_unref(__L, LUA_REGISTRYINDEX, load_path_save); - lua_rawgeti(__L, LUA_REGISTRYINDEX, load_name_save); - lua_setfield(__L, -2, "_loadname"); - luaL_unref(__L, LUA_REGISTRYINDEX, load_name_save); + lua_settop(__L(), n); /* discard results of load */ + lua_rawgeti(__L(), LUA_REGISTRYINDEX, load_path_save); + lua_setfield(__L(), -2, "_loadpath"); + luaL_unref(__L(), LUA_REGISTRYINDEX, load_path_save); + lua_rawgeti(__L(), LUA_REGISTRYINDEX, load_name_save); + lua_setfield(__L(), -2, "_loadname"); + luaL_unref(__L(), LUA_REGISTRYINDEX, load_name_save); } else pd_error(NULL, "lua: error loading `%s': canvas_open() failed", buf); } - PDLUA_DEBUG("pdlua_new: after load script. stack top %d", lua_gettop(__L)); - lua_getfield(__L, -1, "_constructor"); - lua_pushstring(__L, s->s_name); + PDLUA_DEBUG("pdlua_new: after load script. stack top %d", lua_gettop(__L())); + lua_getfield(__L(), -1, "_constructor"); + lua_pushstring(__L(), s->s_name); pdlua_pushatomtable(argc, argv); - PDLUA_DEBUG("pdlua_new: before lua_pcall(L, 2, 1, 0) stack top %d", lua_gettop(__L)); - if (lua_pcall(__L, 2, 1, 0)) + PDLUA_DEBUG("pdlua_new: before lua_pcall(L, 2, 1, 0) stack top %d", lua_gettop(__L())); + if (lua_pcall(__L(), 2, 1, 0)) { - pd_error(NULL, "pdlua_new: error in constructor for `%s':\n%s", s->s_name, lua_tostring(__L, -1)); - lua_pop(__L, 2); /* pop the error string and the global "pd" */ + pd_error(NULL, "pdlua_new: error in constructor for `%s':\n%s", s->s_name, lua_tostring(__L(), -1)); + lua_pop(__L(), 2); /* pop the error string and the global "pd" */ return NULL; } else { t_pdlua *object = NULL; - PDLUA_DEBUG("pdlua_new: done lua_pcall(L, 2, 1, 0) stack top %d", lua_gettop(__L)); - if (lua_islightuserdata(__L, -1)) + PDLUA_DEBUG("pdlua_new: done lua_pcall(L, 2, 1, 0) stack top %d", lua_gettop(__L())); + if (lua_islightuserdata(__L(), -1)) { - object = lua_touserdata(__L, -1); - lua_pop(__L, 2);/* pop the userdata and the global "pd" */ - PDLUA_DEBUG2("pdlua_new: before returning object %p stack top %d", object, lua_gettop(__L)); + object = lua_touserdata(__L(), -1); + lua_pop(__L(), 2);/* pop the userdata and the global "pd" */ + PDLUA_DEBUG2("pdlua_new: before returning object %p stack top %d", object, lua_gettop(__L())); return object; } else { - lua_pop(__L, 2);/* pop the userdata and the global "pd" */ + lua_pop(__L(), 2);/* pop the userdata and the global "pd" */ PDLUA_DEBUG("pdlua_new: done FALSE lua_islightuserdata(L, -1)", 0); return NULL; } @@ -572,17 +569,17 @@ static t_pdlua *pdlua_new /** Pd object destructor. */ static void pdlua_free( t_pdlua *o /**< The object to destruct. */) { - PDLUA_DEBUG("pdlua_free: stack top %d", lua_gettop(__L)); - lua_getglobal(__L, "pd"); - lua_getfield (__L, -1, "_destructor"); - lua_pushlightuserdata(__L, o); - if (lua_pcall(__L, 1, 0, 0)) + PDLUA_DEBUG("pdlua_free: stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_destructor"); + lua_pushlightuserdata(__L(), o); + if (lua_pcall(__L(), 1, 0, 0)) { - pd_error(NULL, "lua: error in destructor:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 1); /* pop the error string */ + pd_error(NULL, "lua: error in destructor:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 1); /* pop the error string */ } - lua_pop(__L, 1); /* pop the global "pd" */ - PDLUA_DEBUG("pdlua_free: end. stack top %d", lua_gettop(__L)); + lua_pop(__L(), 1); /* pop the global "pd" */ + PDLUA_DEBUG("pdlua_free: end. stack top %d", lua_gettop(__L())); return; } @@ -765,18 +762,18 @@ static void pdlua_menu_open(t_pdlua *o) #if PLUGDATA // This is a more reliable method of finding out what file an object came from // TODO: we might also want to use something like this for pd-vanilla? - lua_getglobal(__L, "pd"); - lua_getfield(__L, -1, "_whereami"); - lua_pushstring(__L, o->pd.te_pd->c_name->s_name); + lua_getglobal(__L(), "pd"); + lua_getfield(__L(), -1, "_whereami"); + lua_pushstring(__L(), o->pd.te_pd->c_name->s_name); - if (lua_pcall(__L, 1, 1, 0)) + if (lua_pcall(__L(), 1, 1, 0)) { - pd_error(NULL, "lua: error in whereami:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 2); /* pop the error string and the global "pd" */ + pd_error(NULL, "lua: error in whereami:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 2); /* pop the error string and the global "pd" */ return; } - if(lua_isstring(__L, -1)) { - const char* fullpath = luaL_checkstring(__L, -1); + if(lua_isstring(__L(), -1)) { + const char* fullpath = luaL_checkstring(__L(), -1); if(fullpath) { t_atom arg; SETSYMBOL(&arg, gensym(fullpath)); @@ -791,39 +788,39 @@ static void pdlua_menu_open(t_pdlua *o) char pathname[FILENAME_MAX]; t_class *class; - PDLUA_DEBUG("pdlua_menu_open stack top is %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_menu_open stack top is %d", lua_gettop(__L())); /** Get the scriptname of the object */ - lua_getglobal(__L, "pd"); - lua_getfield(__L, -1, "_whoami"); - lua_pushlightuserdata(__L, o); - if (lua_pcall(__L, 1, 1, 0)) + lua_getglobal(__L(), "pd"); + lua_getfield(__L(), -1, "_whoami"); + lua_pushlightuserdata(__L(), o); + if (lua_pcall(__L(), 1, 1, 0)) { - pd_error(NULL, "lua: error in whoami:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 2); /* pop the error string and the global "pd" */ + pd_error(NULL, "lua: error in whoami:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 2); /* pop the error string and the global "pd" */ return; } - name = luaL_checkstring(__L, -1); - PDLUA_DEBUG3("pdlua_menu_open: L is %p, name is %s stack top is %d", __L, name, lua_gettop(__L)); + name = luaL_checkstring(__L(), -1); + PDLUA_DEBUG3("pdlua_menu_open: L is %p, name is %s stack top is %d", __L(), name, lua_gettop(__L())); if (name) { if (name[strlen(name)-1] == 'x') { /* pdluax is a class, the particular file should loadable by name alone, we hope */ sprintf(pathname, "%s", name); - lua_pop(__L, 2); /* pop name and the global "pd" */ + lua_pop(__L(), 2); /* pop name and the global "pd" */ } else { - lua_getglobal(__L, "pd"); - lua_getfield(__L, -1, "_get_class"); - lua_pushlightuserdata(__L, o); - if (lua_pcall(__L, 1, 1, 0)) + lua_getglobal(__L(), "pd"); + lua_getfield(__L(), -1, "_get_class"); + lua_pushlightuserdata(__L(), o); + if (lua_pcall(__L(), 1, 1, 0)) { - pd_error(NULL, "lua: error in get_class:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 4); /* pop the error string, global "pd", name, global "pd"*/ + pd_error(NULL, "lua: error in get_class:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 4); /* pop the error string, global "pd", name, global "pd"*/ return; } - class = (t_class *)lua_touserdata(__L, -1); + class = (t_class *)lua_touserdata(__L(), -1); #if PLUGDATA if (!*class->c_externdir->s_name) path = plugdata_datadir; @@ -831,7 +828,7 @@ static void pdlua_menu_open(t_pdlua *o) #endif path = class->c_externdir->s_name; sprintf(pathname, "%s/%s", path, name); - lua_pop(__L, 4); /* pop class, global "pd", name, global "pd"*/ + lua_pop(__L(), 4); /* pop class, global "pd", name, global "pd"*/ } #if PD_MAJOR_VERSION==0 && PD_MINOR_VERSION<43 post("Opening %s for editing", pathname); @@ -849,7 +846,7 @@ static void pdlua_menu_open(t_pdlua *o) sys_vgui("::pd_menucommands::menu_openfile {%s}\n", pathname); #endif } - PDLUA_DEBUG("pdlua_menu_open end. stack top is %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_menu_open end. stack top is %d", lua_gettop(__L())); } @@ -920,10 +917,10 @@ static int pdlua_object_new(lua_State *L) if (o) { // Write object ptr to registry to make it reliably accessible - lua_pushvalue(__L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(__L, o); - lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); - lua_pop(__L, 1); + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), o); + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L(), 1); o->inlets = 0; o->in = NULL; @@ -1246,30 +1243,30 @@ static void pdlua_dispatch t_atom *argv /**< The atoms in the message. */ ) { - PDLUA_DEBUG("pdlua_dispatch: stack top %d", lua_gettop(__L)); - lua_getglobal(__L, "pd"); - lua_getfield (__L, -1, "_dispatcher"); - lua_pushlightuserdata(__L, o); - lua_pushnumber(__L, inlet + 1); /* C has 0.., Lua has 1.. */ - lua_pushstring(__L, s->s_name); + PDLUA_DEBUG("pdlua_dispatch: stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_dispatcher"); + lua_pushlightuserdata(__L(), o); + lua_pushnumber(__L(), inlet + 1); /* C has 0.., Lua has 1.. */ + lua_pushstring(__L(), s->s_name); pdlua_pushatomtable(argc, argv); // Write object ptr to registry to make it reliably accessible - lua_pushvalue(__L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(__L, o); // Use a unique key for your userdata - lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); // Store the userdata in the registry - lua_pop(__L, 1); // Pop the registry table from the stack + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), o); // Use a unique key for your userdata + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); // Store the userdata in the registry + lua_pop(__L(), 1); // Pop the registry table from the stack - if (lua_pcall(__L, 4, 0, 0)) + if (lua_pcall(__L(), 4, 0, 0)) { - pd_error(o, "lua: error in dispatcher:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 1); /* pop the error string */ + pd_error(o, "lua: error in dispatcher:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 1); /* pop the error string */ } - lua_pop(__L, 1); /* pop the global "pd" */ + lua_pop(__L(), 1); /* pop the global "pd" */ - PDLUA_DEBUG("pdlua_dispatch: end. stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_dispatch: end. stack top %d", lua_gettop(__L())); return; } @@ -1282,26 +1279,26 @@ static void pdlua_receivedispatch t_atom *argv /**< The atoms in the message. */ ) { - PDLUA_DEBUG("pdlua_receivedispatch: stack top %d", lua_gettop(__L)); - lua_getglobal(__L, "pd"); - lua_getfield (__L, -1, "_receivedispatch"); - lua_pushlightuserdata(__L, r); - lua_pushstring(__L, s->s_name); + PDLUA_DEBUG("pdlua_receivedispatch: stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_receivedispatch"); + lua_pushlightuserdata(__L(), r); + lua_pushstring(__L(), s->s_name); pdlua_pushatomtable(argc, argv); // Write object ptr to registry to make it reliably accessible - lua_pushvalue(__L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(__L, r->owner); - lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); - lua_pop(__L, 1); + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), r->owner); + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L(), 1); - if (lua_pcall(__L, 3, 0, 0)) + if (lua_pcall(__L(), 3, 0, 0)) { - pd_error(r->owner, "lua: error in receive dispatcher:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 1); /* pop the error string */ + pd_error(r->owner, "lua: error in receive dispatcher:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 1); /* pop the error string */ } - lua_pop(__L, 1); /* pop the global "pd" */ - PDLUA_DEBUG("pdlua_receivedispatch: end. stack top %d", lua_gettop(__L)); + lua_pop(__L(), 1); /* pop the global "pd" */ + PDLUA_DEBUG("pdlua_receivedispatch: end. stack top %d", lua_gettop(__L())); return; } @@ -1309,24 +1306,24 @@ static void pdlua_receivedispatch static void pdlua_clockdispatch( t_pdlua_proxyclock *clock) /**< The proxy clock that received the message. */ { - PDLUA_DEBUG("pdlua_clockdispatch: stack top %d", lua_gettop(__L)); - lua_getglobal(__L, "pd"); - lua_getfield (__L, -1, "_clockdispatch"); - lua_pushlightuserdata(__L, clock); + PDLUA_DEBUG("pdlua_clockdispatch: stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_clockdispatch"); + lua_pushlightuserdata(__L(), clock); // Write object ptr to registry to make it reliably accessible - lua_pushvalue(__L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(__L, clock->owner); - lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); - lua_pop(__L, 1); + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), clock->owner); + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L(), 1); - if (lua_pcall(__L, 1, 0, 0)) + if (lua_pcall(__L(), 1, 0, 0)) { - pd_error(clock->owner, "lua: error in clock dispatcher:\n%s", lua_tostring(__L, -1)); - lua_pop(__L, 1); /* pop the error string */ + pd_error(clock->owner, "lua: error in clock dispatcher:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 1); /* pop the error string */ } - lua_pop(__L, 1); /* pop the global "pd" */ - PDLUA_DEBUG("pdlua_clockdispatch: end. stack top %d", lua_gettop(__L)); + lua_pop(__L(), 1); /* pop the global "pd" */ + PDLUA_DEBUG("pdlua_clockdispatch: end. stack top %d", lua_gettop(__L())); return; } @@ -2006,26 +2003,26 @@ static int pdlua_loader_fromfd { t_pdlua_readerdata reader; - PDLUA_DEBUG("pdlua_loader: stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_loader: stack top %d", lua_gettop(__L())); class_set_extern_dir(gensym(dirbuf)); - pdlua_setrequirepath(__L, dirbuf); + pdlua_setrequirepath(__L(), dirbuf); reader.fd = fd; #if LUA_VERSION_NUM < 502 - if (lua_load(__L, pdlua_reader, &reader, name) || lua_pcall(__L, 0, 0, 0)) + if (lua_load(__L(), pdlua_reader, &reader, name) || lua_pcall(__L(), 0, 0, 0)) #else // 5.2 style - if (lua_load(__L, pdlua_reader, &reader, name, NULL) || lua_pcall(__L, 0, 0, 0)) + if (lua_load(__L(), pdlua_reader, &reader, name, NULL) || lua_pcall(__L(), 0, 0, 0)) #endif // LUA_VERSION_NUM < 502 { - pd_error(NULL, "lua: error loading `%s':\n%s", name, lua_tostring(__L, -1)); - lua_pop(__L, 1); - pdlua_clearrequirepath(__L); + pd_error(NULL, "lua: error loading `%s':\n%s", name, lua_tostring(__L(), -1)); + lua_pop(__L(), 1); + pdlua_clearrequirepath(__L()); class_set_extern_dir(&s_); - PDLUA_DEBUG("pdlua_loader: script error end. stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_loader: script error end. stack top %d", lua_gettop(__L())); return 0; } - pdlua_clearrequirepath(__L); + pdlua_clearrequirepath(__L()); class_set_extern_dir(&s_); - PDLUA_DEBUG("pdlua_loader: end. stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_loader: end. stack top %d", lua_gettop(__L())); return 1; } @@ -2042,30 +2039,30 @@ static int pdlua_loader_wrappath const char* basenamep = basename(name); int load_name_save = 0, load_path_save; const int is_loadname = basenamep > name; - lua_getglobal(__L, "pd"); + lua_getglobal(__L(), "pd"); if (is_loadname) { /* save old loadname, restore later in case of * nested loading */ - lua_getfield(__L, -1, "_loadname"); - load_name_save = luaL_ref(__L, LUA_REGISTRYINDEX); - lua_pushstring(__L, name); - lua_setfield(__L, -2, "_loadname"); + lua_getfield(__L(), -1, "_loadname"); + load_name_save = luaL_ref(__L(), LUA_REGISTRYINDEX); + lua_pushstring(__L(), name); + lua_setfield(__L(), -2, "_loadname"); } - lua_getfield(__L, -1, "_loadpath"); - load_path_save = luaL_ref(__L, LUA_REGISTRYINDEX); - lua_pushstring(__L, dirbuf); - lua_setfield(__L, -2, "_loadpath"); + lua_getfield(__L(), -1, "_loadpath"); + load_path_save = luaL_ref(__L(), LUA_REGISTRYINDEX); + lua_pushstring(__L(), dirbuf); + lua_setfield(__L(), -2, "_loadpath"); result=pdlua_loader_fromfd(fd, basenamep, dirbuf); - lua_rawgeti(__L, LUA_REGISTRYINDEX, load_path_save); - lua_setfield(__L, -2, "_loadpath"); - luaL_unref(__L, LUA_REGISTRYINDEX, load_path_save); + lua_rawgeti(__L(), LUA_REGISTRYINDEX, load_path_save); + lua_setfield(__L(), -2, "_loadpath"); + luaL_unref(__L(), LUA_REGISTRYINDEX, load_path_save); if (is_loadname) { - lua_rawgeti(__L, LUA_REGISTRYINDEX, load_name_save); - lua_setfield(__L, -2, "_loadname"); - luaL_unref(__L, LUA_REGISTRYINDEX, load_name_save); + lua_rawgeti(__L(), LUA_REGISTRYINDEX, load_name_save); + lua_setfield(__L(), -2, "_loadname"); + luaL_unref(__L(), LUA_REGISTRYINDEX, load_name_save); } - lua_pop(__L, 1); + lua_pop(__L(), 1); sys_close(fd); } return result; @@ -2202,15 +2199,13 @@ void pdlua_setup(void) pd_error(NULL, "lua: (is Pd using a different float size?)"); return; } -#if LUA_VERSION_NUM < 502 - __L = lua_open(); -#else // 5.2 style - __L = luaL_newstate(); -#endif // LUA_VERSION_NUM < 502 - PDLUA_DEBUG("pdlua lua_open done L = %p", __L); - luaL_openlibs(__L); + + initialise_lua_state(); + + PDLUA_DEBUG("pdlua lua_open done L = %p", __L()); + luaL_openlibs(__L()); PDLUA_DEBUG("pdlua luaL_openlibs done", 0); - pdlua_init(__L); + pdlua_init(__L()); PDLUA_DEBUG("pdlua pdlua_init done", 0); /* "pd.lua" is the Lua part of pdlua, want to keep the C part minimal */ /* canvas_open can't find pd.lua unless we give the path to pd beforehand like pd -path /usr/lib/extra/pdlua */ @@ -2230,28 +2225,28 @@ void pdlua_setup(void) /* fd = canvas_open(canvas_getcurrent(), "pd", ".lua", buf, &ptr, MAXPDSTRING, 1); looks all over and rarely succeeds */ PDLUA_DEBUG ("pd.lua loaded from %s", pd_lua_path); PDLUA_DEBUG("pdlua canvas_open done fd = %d", fd); - PDLUA_DEBUG("pdlua_setup: stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_setup: stack top %d", lua_gettop(__L())); if (fd >= 0) { /* pd.lua was opened */ reader.fd = fd; #if LUA_VERSION_NUM < 502 - result = lua_load(__L, pdlua_reader, &reader, "pd.lua"); + result = lua_load(__L(), pdlua_reader, &reader, "pd.lua"); #else // 5.2 style - result = lua_load(__L, pdlua_reader, &reader, "pd.lua", NULL); // mode bt for binary or text + result = lua_load(__L(), pdlua_reader, &reader, "pd.lua", NULL); // mode bt for binary or text #endif // LUA_VERSION_NUM < 502 PDLUA_DEBUG ("pdlua lua_load returned %d", result); if (0 == result) { - result = lua_pcall(__L, 0, 0, 0); + result = lua_pcall(__L(), 0, 0, 0); PDLUA_DEBUG ("pdlua lua_pcall returned %d", result); } if (0 != result) - //if (lua_load(__L, pdlua_reader, &reader, "pd.lua") || lua_pcall(__L, 0, 0, 0)) + //if (lua_load(__L(), pdlua_reader, &reader, "pd.lua") || lua_pcall(__L(), 0, 0, 0)) { - pd_error(NULL, "lua: error loading `pd.lua':\n%s", lua_tostring(__L, -1)); + pd_error(NULL, "lua: error loading `pd.lua':\n%s", lua_tostring(__L(), -1)); pd_error(NULL, "lua: loader will not be registered!"); pd_error(NULL, "lua: (is `pd.lua' in Pd's path list?)"); - lua_pop(__L, 1); + lua_pop(__L(), 1); } else { @@ -2272,9 +2267,9 @@ void pdlua_setup(void) pd_error(NULL, "lua: loader will not be registered!"); } - pdlua_gfx_setup(__L); + pdlua_gfx_setup(__L()); - PDLUA_DEBUG("pdlua_setup: end. stack top %d", lua_gettop(__L)); + PDLUA_DEBUG("pdlua_setup: end. stack top %d", lua_gettop(__L())); #ifndef PLUGDATA /* nw.js support. */ #ifdef WIN32 diff --git a/pdlua.h b/pdlua.h index 0690de7..5d81248 100644 --- a/pdlua.h +++ b/pdlua.h @@ -17,9 +17,6 @@ #include "m_pd.h" -/** Global Lua interpreter state, needed in the constructor. */ -static lua_State *__L; - typedef struct _pdlua_gfx { #if !PLUGDATA @@ -63,3 +60,5 @@ typedef struct pdlua } t_pdlua; +lua_State* __L(); +void initialise_lua_state(); diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 6a565bf..e8d3ad1 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -62,49 +62,49 @@ void pdlua_gfx_clear(t_pdlua *obj); // only for pd-vanilla, to delete all tcl/tk // Trigger repaint callback in lua script void pdlua_gfx_repaint(t_pdlua *o) { - lua_getglobal(__L, "pd"); - lua_getfield (__L, -1, "_repaint"); - lua_pushlightuserdata(__L, o); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_repaint"); + lua_pushlightuserdata(__L(), o); // Write object ptr to registry to make it reliably accessible - lua_pushvalue(__L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(__L, o); - lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); - lua_pop(__L, 1); + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), o); + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L(), 1); - if (lua_pcall(__L, 1, 0, 0)) + 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 */ + 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" */ + lua_pop(__L(), 1); /* pop the global "pd" */ } // 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); + 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); // Write object ptr to registry to make it reliably accessible - lua_pushvalue(__L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(__L, o); - lua_seti(__L, -2, PDLUA_OBJECT_REGISTRTY_ID); - lua_pop(__L, 1); + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), o); + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); + lua_pop(__L(), 1); - if (lua_pcall(__L, 4, 0, 0)) + 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 */ + 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" */ + lua_pop(__L(), 1); /* pop the global "pd" */ } // Pass mouse events to lua script (but easier to understand) diff --git a/pdlua_multi_instance.cpp b/pdlua_multi_instance.cpp new file mode 100644 index 0000000..edd2adc --- /dev/null +++ b/pdlua_multi_instance.cpp @@ -0,0 +1,42 @@ +#include + +extern "C" +{ + +#include +#include +#include +#include + + +#ifdef PDINSTANCE +std::map lua_state; + +lua_State* __L() +{ + return lua_state[pd_this]; +} + +void initialise_lua_state() +{ + lua_state[pd_this] = luaL_newstate(); +} +#else + +static lua_State *__lua_state; + +lua_State* __L() +{ + return __lua_state; +} + +void initialise_lua_state() +{ + __lua_state = luaL_newstate(); +} + +#endif + +} + + From d2b5462cea0bc899911efc3568cd700abdd20c28 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 26 Jan 2024 01:23:50 +0100 Subject: [PATCH 41/62] pd-vanilla bugfixes --- pdlua.c | 6 ++++-- pdlua_gfx.h | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/pdlua.c b/pdlua.c index 87eb215..4b556d6 100644 --- a/pdlua.c +++ b/pdlua.c @@ -612,8 +612,10 @@ static void pdlua_delete(t_gobj *z, t_glist *glist){ text_widgetbehavior.w_deletefn(z, glist); return; } - pdlua_vis(z, glist, 0); - canvas_deletelinesfor(glist, (t_text *)z); + if(glist_isvisible(glist) && gobj_shouldvis(z, glist)) { + pdlua_vis(z, glist, 0); + canvas_deletelinesfor(glist, (t_text *)z); + } } static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 6a565bf..f23e22a 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -210,7 +210,8 @@ static int set_size(lua_State* L) static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); plugdata_draw(obj, gensym("lua_start_paint"), 0, NULL); - return 0; + lua_pushboolean(L, 1); // Return a value, which decides whether we're allowed to paint or not + return 1; } static int end_paint(lua_State* L) { @@ -432,6 +433,11 @@ static int reset_transform(lua_State* L) { } #else +static int can_draw(t_pdlua* obj) +{ + return glist_isvisible(obj->canvas) && gobj_shouldvis(obj, obj->canvas); +} + static void gfx_free(t_pdlua_gfx* gfx) { freebytes(gfx->path_segments, gfx->num_path_segments_allocated * sizeof(int)); @@ -500,10 +506,14 @@ static int set_size(lua_State* L) static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); + int draw = can_draw(obj); + lua_pushboolean(L, draw); // Return a value, which decides whether we're allowed to paint or not + // check if anything was painted before - if(strlen(obj->gfx.current_paint_tag)) + if(draw && strlen(obj->gfx.current_paint_tag)) pdlua_gfx_clear(obj); - return 0; + + return 1; } static int end_paint(lua_State* L) { @@ -519,6 +529,7 @@ static int end_paint(lua_State* L) { static int set_color(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; int r = luaL_checknumber(L, 1); @@ -534,6 +545,7 @@ static int set_color(lua_State* L) { static int fill_ellipse(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -549,6 +561,7 @@ static int fill_ellipse(lua_State* L) { static int stroke_ellipse(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -566,6 +579,7 @@ static int stroke_ellipse(lua_State* L) { static int fill_all(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -583,6 +597,7 @@ static int fill_all(lua_State* L) { static int fill_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -598,6 +613,7 @@ static int fill_rect(lua_State* L) { static int stroke_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -615,6 +631,7 @@ static int stroke_rect(lua_State* L) { static int fill_rounded_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -640,6 +657,7 @@ static int fill_rounded_rect(lua_State* L) { static int stroke_rounded_rect(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -678,6 +696,7 @@ static int stroke_rounded_rect(lua_State* L) { static int draw_line(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -715,6 +734,7 @@ static int draw_line(lua_State* L) { static int draw_text(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -772,6 +792,7 @@ static void add_path_segment(t_pdlua_gfx* gfx, int x, int y) static int start_path(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->num_path_segments = 0; @@ -785,6 +806,7 @@ static int start_path(lua_State* L) { // Function to add a line to the current path static int line_to(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; int x = luaL_checknumber(L, 1); @@ -795,6 +817,7 @@ static int line_to(lua_State* L) { static int quad_to(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; int x2 = luaL_checknumber(L, 1); @@ -821,6 +844,7 @@ static int quad_to(lua_State* L) { } static int cubic_to(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; int x2 = luaL_checknumber(L, 1); @@ -852,6 +876,7 @@ static int cubic_to(lua_State* L) { // Function to close the current path static int close_path(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; add_path_segment(gfx, gfx->path_start_x, gfx->path_start_y); return 0; @@ -859,6 +884,7 @@ static int close_path(lua_State* L) { static int stroke_path(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -919,6 +945,7 @@ static int stroke_path(lua_State* L) { static int fill_path(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -974,6 +1001,7 @@ static int fill_path(lua_State* L) { static int translate(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->translate_x = luaL_checknumber(L, 1); gfx->translate_y = luaL_checknumber(L, 2); @@ -982,6 +1010,7 @@ static int translate(lua_State* L) { static int scale(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->scale_x = luaL_checknumber(L, 1); gfx->scale_y = luaL_checknumber(L, 2); @@ -990,6 +1019,7 @@ static int scale(lua_State* L) { static int reset_transform(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; gfx->translate_x = 0; gfx->translate_y = 0; From 87a08b1051754edc7538a9eec16e315dffcb420e Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 6 Feb 2024 17:52:48 +0100 Subject: [PATCH 42/62] Use lua_threads for multi-instance support --- pdlua.c | 9 +++++++++ pdlua_multi_instance.cpp | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/pdlua.c b/pdlua.c index e7ef14f..4f760ae 100644 --- a/pdlua.c +++ b/pdlua.c @@ -2125,6 +2125,15 @@ static int pdlua_loader_pathwise #ifdef _WIN32 __declspec(dllexport) #endif + + +void pdlua_instance_setup() +{ +#if PDINSTANCE + initialise_lua_state(); +#endif +} + #ifdef PLUGDATA void pdlua_setup(const char *datadir, char *versbuf, int versbuf_length, void(*register_class_callback)(const char*)) #else diff --git a/pdlua_multi_instance.cpp b/pdlua_multi_instance.cpp index edd2adc..97eef54 100644 --- a/pdlua_multi_instance.cpp +++ b/pdlua_multi_instance.cpp @@ -9,22 +9,29 @@ extern "C" #include +static inline lua_State *__lua_state = nullptr; + #ifdef PDINSTANCE -std::map lua_state; + +std::map lua_threads; lua_State* __L() { - return lua_state[pd_this]; + return lua_threads[pd_this]; } void initialise_lua_state() { - lua_state[pd_this] = luaL_newstate(); + if(!__lua_state) { + __lua_state = luaL_newstate(); + lua_threads[pd_this] = __lua_state; + } + else { + lua_threads[pd_this] = lua_newthread(__lua_state); + } } #else -static lua_State *__lua_state; - lua_State* __L() { return __lua_state; @@ -32,7 +39,7 @@ lua_State* __L() void initialise_lua_state() { - __lua_state = luaL_newstate(); + if(!__lua_state) __lua_state = luaL_newstate(); } #endif From f60060cc3948e1d4e89329bfc42ec43202f5bff6 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 23 Feb 2024 16:42:53 +0100 Subject: [PATCH 43/62] Improved API, simplified multi-instance support --- pd.lua | 25 ++- pdlua.c | 54 ++++++- pdlua.h | 8 +- pdlua/hello-gui.pd_lua | 151 +++++++++--------- pdlua_gfx.h | 323 ++++++++++++++++++++++++++------------- pdlua_multi_instance.cpp | 49 ------ 6 files changed, 361 insertions(+), 249 deletions(-) delete mode 100644 pdlua_multi_instance.cpp diff --git a/pd.lua b/pd.lua index c56b994..fe4987d 100644 --- a/pd.lua +++ b/pd.lua @@ -126,7 +126,7 @@ pd._whereami = function(name) if nil ~= pd._fullpaths[name] then return pd._fullpaths[name] end - + return nil end @@ -301,7 +301,7 @@ function pd.Class:register(name) else regname = name end - + --pd._fullpaths[regname] = pd._currentpath or (fullname .. ".pd_lua") if pd._currentpath == nil or pd._currentpath == '' then pd._fullpaths[regname] = fullname .. ".pd_lua" @@ -385,14 +385,23 @@ end function pd.Class:initialize(sel, atoms) end -function pd.Class:repaint() - if type(self.paint) == "function" then - gfx._start_paint(); - self:paint(); - gfx._end_paint(); +function pd.Class:repaint() + if type(self.paint) == "function" then + local g = _gfx_internal.gfx_new() + _gfx_internal.start_paint(); + self:paint(g); + _gfx_internal.end_paint(); end end +function pd.Class:get_size() + return _gfx_internal.get_size() +end + +function pd.Class:set_size(width, height) + return _gfx_internal.set_size(width, height) +end + function pd.Class:postinitialize() end function pd.Class:finalize() end @@ -474,7 +483,7 @@ function luax:initialize(sel, atoms) -- motivation: pd-list 2007-09-23 end self._scriptname = pathname .. '/' .. basename(atoms[1]) .. ".pd_luax" -- mrpeach 20120201 local atomstail = { } -- munge for better lua<->luax compatibility - for i,_ in ipairs(atoms) do + for i,_ in ipairs(atoms) do if i > 1 then atomstail[i-1] = atoms[i] end diff --git a/pdlua.c b/pdlua.c index 4f760ae..e5df958 100644 --- a/pdlua.c +++ b/pdlua.c @@ -55,6 +55,56 @@ #include "pdlua_gfx.h" +#ifdef PDINSTANCE +#define MAX_THREADS 2048 + +lua_State* lua_threads[MAX_THREADS] = {0}; + +size_t hash_pointer(void *ptr) { + // Simple hashing function for pointers + size_t val = (size_t)ptr; + val = ((val >> 16) ^ val) * 0x45d9f3b; + val = ((val >> 16) ^ val) * 0x45d9f3b; + val = (val >> 16) ^ val; + return val % MAX_THREADS; +} + +lua_State* __L() +{ + size_t idx = hash_pointer(pd_this); + return lua_threads[idx]; +} + +void initialise_lua_state() +{ + size_t idx = hash_pointer(pd_this); + if (!lua_threads[idx]) { + lua_threads[idx] = luaL_newstate(); + } + else { + lua_threads[idx] = lua_newthread(lua_threads[idx]); + } +} + +#else + +static lua_State* __lua_state = NULL; + +lua_State* __L() +{ + return __lua_state; +} + +void initialise_lua_state() +{ + if (!__lua_state) { + __lua_state = luaL_newstate(); + } +} + +#endif + + #if PD_MAJOR_VERSION == 0 # if PD_MINOR_VERSION >= 41 # define PDLUA_PD41 @@ -933,7 +983,6 @@ static int pdlua_object_new(lua_State *L) #if !PLUGDATA // Init graphics state for pd - o->gfx.num_path_segments = 0; o->gfx.scale_x = 1.0f; o->gfx.scale_y = 1.0f; o->gfx.translate_x = 0; @@ -1216,8 +1265,7 @@ static int pdlua_object_free(lua_State *L) if (lua_islightuserdata(L, 1)) { t_pdlua *o = lua_touserdata(L, 1); - gfx_free(&o->gfx); - + if (o) { if (o->in) free(o->in); diff --git a/pdlua.h b/pdlua.h index 5d81248..f8348f3 100644 --- a/pdlua.h +++ b/pdlua.h @@ -22,13 +22,7 @@ typedef struct _pdlua_gfx #if !PLUGDATA char object_tag[128]; // Tcl/tk tag that is attached to all drawings char current_paint_tag[128]; // Tcl/tk tag that is only attached to the current drawing in progress - - // Variables for managing vector paths - int* path_segments; - int num_path_segments; - int num_path_segments_allocated; - int path_start_x, path_start_y; - + // Variables to keep track of mouse button state and drag position int mouse_drag_x, mouse_drag_y, mouse_down; diff --git a/pdlua/hello-gui.pd_lua b/pdlua/hello-gui.pd_lua index 962fbd1..b13adce 100644 --- a/pdlua/hello-gui.pd_lua +++ b/pdlua/hello-gui.pd_lua @@ -16,7 +16,7 @@ function hello:initialize(sel, atoms) self.rect_down_pos = {0, 0} self.gui = 1 - gfx.set_size(630, 230) + self:set_size(630, 230) return true end @@ -56,103 +56,104 @@ function hello:mouse_drag(x, y) end end -function hello:paint() - gfx.set_color(250, 200, 240) - gfx.fill_all() +function hello:paint(g) + g.set_color(250, 200, 240) + g.fill_all() -- Filled examples - gfx.set_color(66, 207, 201, 0.3) - gfx.fill_ellipse(30, 50, 30, 30) - gfx.set_color(0, 159, 147, 1) - gfx.fill_rect(120, 50, 30, 30) - gfx.set_color(250, 84, 108, 1) - gfx.fill_rounded_rect(210, 50, 30, 30, 5) + g.set_color(66, 207, 201, 0.3) + g.fill_ellipse(30, 50, 30, 30) + g.set_color(0, 159, 147, 1) + g.fill_rect(120, 50, 30, 30) + g.set_color(250, 84, 108, 1) + g.fill_rounded_rect(210, 50, 30, 30, 5) - gfx.set_color(252, 118, 81, 1) + g.set_color(252, 118, 81, 1) -- Star using line_to paths local starX1, starY1 = 310, 45 local starSize = 15 - gfx.start_path(starX1, starY1) + local star = path.start(starX1, starY1) -- Star using line_to paths - gfx.line_to(starX1 + 5, starY1 + 14) - gfx.line_to(starX1 + 20, starY1 + 14) - gfx.line_to(starX1 + 8, starY1 + 22) - gfx.line_to(starX1 + 14, starY1 + 36) - gfx.line_to(starX1, starY1 + 27) - gfx.line_to(starX1 - 14, starY1 + 36) - gfx.line_to(starX1 - 6, starY1 + 22) - gfx.line_to(starX1 - 18, starY1 + 14) - gfx.line_to(starX1 - 3, starY1 + 14) - gfx.close_path() - gfx.fill_path() - - gfx.set_color(255, 219, 96, 1) + star:line_to(starX1 + 5, starY1 + 14) + star:line_to(starX1 + 20, starY1 + 14) + star:line_to(starX1 + 8, starY1 + 22) + star:line_to(starX1 + 14, starY1 + 36) + star:line_to(starX1, starY1 + 27) + star:line_to(starX1 - 14, starY1 + 36) + star:line_to(starX1 - 6, starY1 + 22) + star:line_to(starX1 - 18, starY1 + 14) + star:line_to(starX1 - 3, starY1 + 14) + star:close() + + g.fill_path(star) + + g.set_color(255, 219, 96, 1) -- Bezier curve example - gfx.translate(140, 20) - gfx.scale(0.5, 1.0) - gfx.start_path(450, 50) - gfx.cubic_to(500, 30, 550, 70, 600, 50) - gfx.close_path() - gfx.stroke_path(2) - gfx.reset_transform() + g.translate(140, 20) + g.scale(0.5, 1.0) + local curve = path.start(450, 50) + curve:cubic_to(500, 30, 550, 70, 600, 50) + curve:close() + g.stroke_path(curve, 2) + g.reset_transform() -- Stroked examples - gfx.set_color(66, 207, 201, 1) - gfx.stroke_ellipse(30, 150, 30, 30, 2) - gfx.set_color(0, 159, 147, 1) - gfx.stroke_rect(120, 150, 30, 30, 2) - gfx.set_color(250, 84, 108, 1) - gfx.stroke_rounded_rect(210, 150, 30, 30, 5, 2) + g.set_color(66, 207, 201, 1) + g.stroke_ellipse(30, 150, 30, 30, 2) + g.set_color(0, 159, 147, 1) + g.stroke_rect(120, 150, 30, 30, 2) + g.set_color(250, 84, 108, 1) + g.stroke_rounded_rect(210, 150, 30, 30, 5, 2) - gfx.set_color(252, 118, 81, 1) + g.set_color(252, 118, 81, 1) local starX2, starY2 = 310, 145 local starSize = 15 -- Star using line_to paths - gfx.start_path(starX2, starY2) - gfx.line_to(starX2 + 5, starY2 + 14) - gfx.line_to(starX2 + 20, starY2 + 14) - gfx.line_to(starX2 + 8, starY2 + 22) - gfx.line_to(starX2 + 14, starY2 + 36) - gfx.line_to(starX2, starY2 + 27) - gfx.line_to(starX2 - 14, starY2 + 36) - gfx.line_to(starX2 - 6, starY2 + 22) - gfx.line_to(starX2 - 18, starY2 + 14) - gfx.line_to(starX2 - 3, starY2 + 14) - gfx.close_path() - gfx.stroke_path(2) - - gfx.set_color(255, 219, 96, 1) + local star2 = path.start(starX2, starY2) + star2:line_to(starX2 + 5, starY2 + 14) + star2:line_to(starX2 + 20, starY2 + 14) + star2:line_to(starX2 + 8, starY2 + 22) + star2:line_to(starX2 + 14, starY2 + 36) + star2:line_to(starX2, starY2 + 27) + star2:line_to(starX2 - 14, starY2 + 36) + star2:line_to(starX2 - 6, starY2 + 22) + star2:line_to(starX2 - 18, starY2 + 14) + star2:line_to(starX2 - 3, starY2 + 14) + star2:close() + g.stroke_path(star2, 2) + + g.set_color(255, 219, 96, 1) -- Bezier curve example - gfx.translate(140, 20) - gfx.scale(0.5, 1.0) - gfx.start_path(450, 150) - gfx.cubic_to(500, 130, 550, 170, 600, 150) - gfx.fill_path() - gfx.reset_transform() + g.translate(140, 20) + g.scale(0.5, 1.0) + local curve2 = path.start(450, 150) + curve2:cubic_to(500, 130, 550, 170, 600, 150) + g.fill_path(curve2) + g.reset_transform() -- Draggable rectangle - gfx.set_color(66, 207, 201, 1) - gfx.fill_rounded_rect(self.draggable_rect_x, self.draggable_rect_y, self.draggable_rect_size, self.draggable_rect_size, 5) - gfx.set_color(0, 0, 0, 1) - gfx.draw_text("Drag me!", self.draggable_rect_x + 8, self.draggable_rect_y + 20, self.draggable_rect_size, 10) + g.set_color(66, 207, 201, 1) + g.fill_rounded_rect(self.draggable_rect_x, self.draggable_rect_y, self.draggable_rect_size, self.draggable_rect_size, 5) + g.set_color(0, 0, 0, 1) + g.draw_text("Drag me!", self.draggable_rect_x + 8, self.draggable_rect_y + 20, self.draggable_rect_size, 10) -- Titles - gfx.set_color(252, 118, 81, 1) - gfx.draw_text("Ellipse", 32, 190, 120, 10) - gfx.draw_text("Rectangle", 116, 190, 120, 10) - gfx.draw_text("Rounded Rectangle", 188, 190, 120, 10) - gfx.draw_text("Paths", 300, 190, 120, 10) - gfx.draw_text("Bezier Paths", 380, 190, 120, 10) - gfx.draw_text("Animation", 470, 190, 120, 10) - gfx.draw_text("Mouse Interaction", 540, 190, 120, 10) - - gfx.set_color(250, 84, 108, 1) - gfx.fill_ellipse(self.circle_x, self.circle_y, self.circle_radius, self.circle_radius) + g.set_color(252, 118, 81, 1) + g.draw_text("Ellipse", 32, 190, 120, 10) + g.draw_text("Rectangle", 116, 190, 120, 10) + g.draw_text("Rounded Rectangle", 188, 190, 120, 10) + g.draw_text("Paths", 300, 190, 120, 10) + g.draw_text("Bezier Paths", 380, 190, 120, 10) + g.draw_text("Animation", 470, 190, 120, 10) + g.draw_text("Mouse Interaction", 540, 190, 120, 10) + + g.set_color(250, 84, 108, 1) + g.fill_ellipse(self.circle_x, self.circle_y, self.circle_radius, self.circle_radius) end function hello:tick() @@ -167,4 +168,4 @@ end function hello:in_1_bang() self:repaint() -end \ No newline at end of file +end diff --git a/pdlua_gfx.h b/pdlua_gfx.h index e8d3ad1..e708666 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -28,6 +28,7 @@ static int gfx_initialize(t_pdlua *obj); static int set_size(lua_State *L); +static int get_size(lua_State *L); static int start_paint(lua_State *L); static int end_paint(lua_State* L); @@ -56,6 +57,8 @@ static int translate(lua_State* L); static int scale(lua_State* L); static int reset_transform(lua_State* L); +static int free_path(lua_State* L); + // pdlua_gfx_clear, pdlua_gfx_repaint and pdlua_gfx_mouse_* correspond to the various callbacks the user can assign void pdlua_gfx_clear(t_pdlua *obj); // only for pd-vanilla, to delete all tcl/tk items @@ -123,6 +126,19 @@ void pdlua_gfx_mouse_drag(t_pdlua *o, int x, int y) { pdlua_gfx_mouse_event(o, x, y, 3); } +typedef struct _path_state +{ +#if PLUGDATA + t_symbol* path_id; +#else + // Variables for managing vector paths + int* path_segments; + int num_path_segments; + int num_path_segments_allocated; + int path_start_x, path_start_y; +#endif +} path_state; + // We need to have access to the current object always, even in the constructor // This function can guarantee that static t_pdlua* get_current_object(lua_State* L) @@ -137,9 +153,36 @@ static t_pdlua* get_current_object(lua_State* L) return NULL; } + +static int gfx_new(lua_State *L) { + luaL_setmetatable(L, "graphics_context"); + return 1; +} + // Register functions with Lua static const luaL_Reg gfx_lib[] = { {"set_size", set_size}, + {"get_size", get_size}, + {"start_paint", start_paint}, + {"end_paint", end_paint}, + {"gfx_new", gfx_new}, + {NULL, NULL} // Sentinel to end the list +}; + +static const luaL_Reg path_methods[] = { + {"line_to", line_to}, + {"quad_to", quad_to}, + {"cubic_to", cubic_to}, + {"close", close_path}, + {"__gc", free_path}, +}; + +static const luaL_Reg path_constructor[] = { + {"start", start_path}, +}; + +// Register functions with Lua +static const luaL_Reg gfx_methods[] = { {"set_color", set_color}, {"fill_ellipse", fill_ellipse}, {"stroke_ellipse", stroke_ellipse}, @@ -149,29 +192,45 @@ static const luaL_Reg gfx_lib[] = { {"stroke_rounded_rect", stroke_rounded_rect}, {"draw_line", draw_line}, {"draw_text", draw_text}, - {"start_path", start_path}, - {"line_to", line_to}, - {"quad_to", quad_to}, - {"cubic_to", cubic_to}, - {"close_path", close_path}, {"stroke_path", stroke_path}, {"fill_path", fill_path}, {"fill_all", fill_all}, {"translate", translate}, {"scale", scale}, {"reset_transform", reset_transform}, - {"_start_paint", start_paint}, - {"_end_paint", end_paint}, {NULL, NULL} // Sentinel to end the list }; int pdlua_gfx_setup(lua_State* L) { // Register functions with Lua luaL_newlib(L, gfx_lib); - lua_setglobal(L, "gfx"); + lua_setglobal(L, "_gfx_internal"); + + luaL_newlib(L, path_constructor); + lua_setglobal(L, "path"); + + luaL_newmetatable(L, "path_state"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, path_methods, 0); + + luaL_newmetatable(L, "graphics_context"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, gfx_methods, 0); + return 1; // Number of values pushed onto the stack } + +static int get_size(lua_State* L) +{ + t_pdlua* obj = get_current_object(L); + lua_pushnumber(L, (lua_Number)obj->gfx.width); + lua_pushnumber(L, (lua_Number)obj->gfx.height); + return 2; +} + #if PLUGDATA // Wrapper around draw callback to plugdata @@ -191,10 +250,6 @@ static int gfx_initialize(t_pdlua* obj) return 0; } -static void gfx_free(t_pdlua_gfx* gfx) -{ -} - static int set_size(lua_State* L) { t_pdlua* obj = get_current_object(L); @@ -341,69 +396,124 @@ static int draw_text(lua_State* L) { return 0; } +t_symbol* generate_path_id() { + int length = 32; + static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + char *random_string = malloc((length + 1) * sizeof(char)); + + for (int i = 0; i < length; i++) { + int index = rand() % (sizeof(charset) - 1); + random_string[i] = charset[index]; + } + + random_string[length] = '\0'; + t_symbol* sym = gensym(random_string); + free(random_string); + return sym; +} + static int start_path(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom args[2]; - SETFLOAT(args , luaL_checknumber(L, 1)); // x - SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y - plugdata_draw(obj, gensym("lua_start_path"), 2, args); - return 0; + + path_state *path = (path_state *)lua_newuserdata(L, sizeof(path_state)); + luaL_setmetatable(L, "path_state"); + path->path_id = generate_path_id(); + + t_atom args[3]; + SETSYMBOL(args, path->path_id); // path id + SETFLOAT(args + 1, luaL_checknumber(L, 1)); // x + SETFLOAT(args + 2, luaL_checknumber(L, 2)); // y + plugdata_draw(obj, gensym("lua_start_path"), 3, args); + return 1; } static int line_to(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom args[2]; - SETFLOAT(args, luaL_checknumber(L, 1)); // x - SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y - plugdata_draw(obj, gensym("lua_line_to"), 2, args); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args[3]; + SETSYMBOL(args, path->path_id); // path id + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // x + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y + plugdata_draw(obj, gensym("lua_line_to"), 3, args); return 0; } static int quad_to(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom args[4]; // Assuming quad_to takes 3 arguments - SETFLOAT(args, luaL_checknumber(L, 1)); // x1 - SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y1 - SETFLOAT(args + 2, luaL_checknumber(L, 3)); // x2 - SETFLOAT(args + 3, luaL_checknumber(L, 4)); // y2 + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args[5]; // Assuming quad_to takes 3 arguments + SETSYMBOL(args, path->path_id); // path id + SETFLOAT(args + 1, luaL_checknumber(L, 1)); // x1 + SETFLOAT(args + 2, luaL_checknumber(L, 2)); // y1 + SETFLOAT(args + 3, luaL_checknumber(L, 3)); // x2 + SETFLOAT(args + 4, luaL_checknumber(L, 4)); // y2 // Forward the message to the appropriate function - plugdata_draw(obj, gensym("lua_quad_to"), 4, args); + plugdata_draw(obj, gensym("lua_quad_to"), 5, args); return 0; } static int cubic_to(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom args[6]; // Assuming cubic_to takes 4 arguments - SETFLOAT(args, luaL_checknumber(L, 1)); // x1 - SETFLOAT(args + 1, luaL_checknumber(L, 2)); // y1 - SETFLOAT(args + 2, luaL_checknumber(L, 3)); // x2 - SETFLOAT(args + 3, luaL_checknumber(L, 4)); // y2 - SETFLOAT(args + 4, luaL_checknumber(L, 5)); // x3 - SETFLOAT(args + 5, luaL_checknumber(L, 6)); // y3 + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args[7]; // Assuming cubic_to takes 4 arguments + + SETSYMBOL(args, path->path_id); // path id + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // x1 + SETFLOAT(args + 2, luaL_checknumber(L, 3)); // y1 + SETFLOAT(args + 3, luaL_checknumber(L, 4)); // x2 + SETFLOAT(args + 4, luaL_checknumber(L, 5)); // y2 + SETFLOAT(args + 5, luaL_checknumber(L, 6)); // x3 + SETFLOAT(args + 6, luaL_checknumber(L, 7)); // y3 // Forward the message to the appropriate function - plugdata_draw(obj, gensym("lua_cubic_to"), 6, args); + plugdata_draw(obj, gensym("lua_cubic_to"), 7, args); return 0; } static int close_path(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_draw(obj, gensym("lua_close_path"), 0, NULL); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args; + SETSYMBOL(&args, path->path_id); // path id + + plugdata_draw(obj, gensym("lua_close_path"), 1, &args); + return 0; +} + +static int free_path(lua_State* L) { + t_pdlua* obj = get_current_object(L); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args; + SETSYMBOL(&args, path->path_id); // path id + plugdata_draw(obj, gensym("lua_free_path"), 1, &args); return 0; } static int stroke_path(lua_State* L) { t_pdlua* obj = get_current_object(L); - t_atom arg; - SETFLOAT(&arg, luaL_checknumber(L, 1)); // line thickness - plugdata_draw(obj, gensym("lua_stroke_path"), 1, &arg); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args[2]; + SETSYMBOL(args, path->path_id); // path id + SETFLOAT(args + 1, luaL_checknumber(L, 2)); // line thickness + plugdata_draw(obj, gensym("lua_stroke_path"), 2, args); return 0; } static int fill_path(lua_State* L) { t_pdlua* obj = get_current_object(L); - plugdata_draw(obj, gensym("lua_fill_path"), 0, NULL); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + + t_atom args; + SETSYMBOL(&args, path->path_id); // path id + + plugdata_draw(obj, gensym("lua_fill_path"), 1, &args); return 0; } @@ -432,9 +542,11 @@ static int reset_transform(lua_State* L) { } #else -static void gfx_free(t_pdlua_gfx* gfx) +static int free_path(lua_State* L) { - freebytes(gfx->path_segments, gfx->num_path_segments_allocated * sizeof(int)); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + freebytes(path->path_segments, path->num_path_segments_allocated * sizeof(int)); + return 0; } void pdlua_gfx_clear(t_pdlua *obj) { @@ -443,7 +555,7 @@ void pdlua_gfx_clear(t_pdlua *obj) { pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); gfx->current_paint_tag[0] = '\0'; - glist_eraseiofor(glist_getcanvas(cnv), obj, gfx->object_tag); + glist_eraseiofor(glist_getcanvas(cnv), &obj->pd, gfx->object_tag); } static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x1, int* y1, int* x2, int* y2) { @@ -759,51 +871,48 @@ static int draw_text(lua_State* L) { return 0; } -static void add_path_segment(t_pdlua_gfx* gfx, int x, int y) +static void add_path_segment(path_state* path, int x, int y) { - int path_segment_space = (gfx->num_path_segments + 1) * 2; - gfx->path_segments = (int*)resizebytes(gfx->path_segments, gfx->num_path_segments_allocated * sizeof(int), MAX((path_segment_space + 1), gfx->num_path_segments_allocated) * sizeof(int)); - gfx->num_path_segments_allocated = path_segment_space; + int path_segment_space = (path->num_path_segments + 1) * 2; + path->path_segments = (int*)resizebytes(path->path_segments, path->num_path_segments_allocated * sizeof(int), MAX((path_segment_space + 1), path->num_path_segments_allocated) * sizeof(int)); + path->num_path_segments_allocated = path_segment_space; - gfx->path_segments[gfx->num_path_segments * 2] = x; - gfx->path_segments[gfx->num_path_segments * 2 + 1] = y; - gfx->num_path_segments++; + path->path_segments[path->num_path_segments * 2] = x; + path->path_segments[path->num_path_segments * 2 + 1] = y; + path->num_path_segments++; } static int start_path(lua_State* L) { - t_pdlua* obj = get_current_object(L); - t_pdlua_gfx *gfx = &obj->gfx; - - gfx->num_path_segments = 0; - gfx->path_start_x = luaL_checknumber(L, 1); - gfx->path_start_y = luaL_checknumber(L, 2); + path_state *path = (path_state *)lua_newuserdata(L, sizeof(path_state)); + luaL_setmetatable(L, "path_state"); - add_path_segment(gfx, gfx->path_start_x, gfx->path_start_y); - return 0; + path->num_path_segments = 0; + 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; } // Function to add a line to the current path static int line_to(lua_State* L) { - t_pdlua* obj = get_current_object(L); - t_pdlua_gfx *gfx = &obj->gfx; - - int x = luaL_checknumber(L, 1); - int y = luaL_checknumber(L, 2); - add_path_segment(gfx, x, y); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + int x = luaL_checknumber(L, 2); + int y = luaL_checknumber(L, 3); + add_path_segment(path, x, y); return 0; } static int quad_to(lua_State* L) { - t_pdlua* obj = get_current_object(L); - t_pdlua_gfx *gfx = &obj->gfx; - - int x2 = luaL_checknumber(L, 1); - int y2 = luaL_checknumber(L, 2); - int x3 = luaL_checknumber(L, 3); - int y3 = luaL_checknumber(L, 4); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + 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 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2] : x2; - int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2 + 1] : y2; + 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; // Get the last point float t = 0.0; @@ -814,24 +923,22 @@ static int quad_to(lua_State* L) { // 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; - add_path_segment(gfx, x, y); + add_path_segment(path, x, y); } return 0; } static int cubic_to(lua_State* L) { - t_pdlua* obj = get_current_object(L); - t_pdlua_gfx *gfx = &obj->gfx; - - int x2 = luaL_checknumber(L, 1); - int y2 = luaL_checknumber(L, 2); - int x3 = luaL_checknumber(L, 3); - int y3 = luaL_checknumber(L, 4); - int x4 = luaL_checknumber(L, 5); - int y4 = luaL_checknumber(L, 6); - - int x1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2] : x2; - int y1 = gfx->num_path_segments > 0 ? gfx->path_segments[(gfx->num_path_segments - 1) * 2 + 1] : y2; + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + 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; // Get the last point float t = 0.0; @@ -843,7 +950,7 @@ static int cubic_to(lua_State* L) { 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; - add_path_segment(gfx, x, y); + add_path_segment(path, x, y); } return 0; @@ -851,9 +958,8 @@ static int cubic_to(lua_State* L) { // Function to close the current path static int close_path(lua_State* L) { - t_pdlua* obj = get_current_object(L); - t_pdlua_gfx *gfx = &obj->gfx; - add_path_segment(gfx, gfx->path_start_x, gfx->path_start_y); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + add_path_segment(path, path->path_start_x, path->path_start_y); return 0; } @@ -862,14 +968,15 @@ static int stroke_path(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); - int stroke_width = luaL_checknumber(L, 1) * glist_getzoom(cnv); + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); + int stroke_width = luaL_checknumber(L, 2) * glist_getzoom(cnv); // Apply transformations to all coordinates // 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); - for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 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]; x *= gfx->scale_x; y *= gfx->scale_y; @@ -878,22 +985,22 @@ static int stroke_path(lua_State* L) { y += gfx->translate_y; int canvas_zoom = glist_getzoom(cnv); - gfx->path_segments[i * 2] = (x * canvas_zoom) + obj_x; - gfx->path_segments[i * 2 + 1] = (y * canvas_zoom) + obj_y; + path->path_segments[i * 2] = (x * canvas_zoom) + obj_x; + path->path_segments[i * 2 + 1] = (y * canvas_zoom) + obj_y; } int totalSize = 0; // Determine the total size needed - for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 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]; // Calculate size for x and y totalSize += snprintf(NULL, 0, "%i %i ", x, y); } char *coordinates = (char*)getbytes(totalSize + 1); // +1 for null terminator int offset = 0; - for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 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]; int charsWritten = snprintf(coordinates + offset, totalSize - offset, "%i %i ", x, y); if (charsWritten >= 0) { offset += charsWritten; @@ -921,12 +1028,14 @@ static int fill_path(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); + + path_state* path = (path_state*)luaL_checkudata(L, 1, "path_state"); // 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); - for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 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]; x *= gfx->scale_x; y *= gfx->scale_y; @@ -934,22 +1043,22 @@ static int fill_path(lua_State* L) { x += gfx->translate_x; y += gfx->translate_y; - gfx->path_segments[i * 2] = x * glist_getzoom(cnv) + obj_x; - gfx->path_segments[i * 2 + 1] = y * glist_getzoom(cnv) + obj_y; + path->path_segments[i * 2] = x * glist_getzoom(cnv) + obj_x; + path->path_segments[i * 2 + 1] = y * glist_getzoom(cnv) + obj_y; } int totalSize = 0; // Determine the total size needed - for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 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]; // Calculate size for x and y totalSize += snprintf(NULL, 0, "%i %i ", x, y); } char *coordinates = (char*)getbytes(totalSize + 1); // +1 for null terminator int offset = 0; - for (int i = 0; i < gfx->num_path_segments; i++) { - int x = gfx->path_segments[i * 2], y = gfx->path_segments[i * 2 + 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]; int charsWritten = snprintf(coordinates + offset, totalSize - offset, "%i %i ", x, y); if (charsWritten >= 0) { offset += charsWritten; diff --git a/pdlua_multi_instance.cpp b/pdlua_multi_instance.cpp deleted file mode 100644 index 97eef54..0000000 --- a/pdlua_multi_instance.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include - -extern "C" -{ - -#include -#include -#include -#include - - -static inline lua_State *__lua_state = nullptr; - -#ifdef PDINSTANCE - -std::map lua_threads; - -lua_State* __L() -{ - return lua_threads[pd_this]; -} - -void initialise_lua_state() -{ - if(!__lua_state) { - __lua_state = luaL_newstate(); - lua_threads[pd_this] = __lua_state; - } - else { - lua_threads[pd_this] = lua_newthread(__lua_state); - } -} -#else - -lua_State* __L() -{ - return __lua_state; -} - -void initialise_lua_state() -{ - if(!__lua_state) __lua_state = luaL_newstate(); -} - -#endif - -} - - From 19b3955878b993a8d043176cd2f47cb6847ef42a Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 23 Feb 2024 16:50:25 +0100 Subject: [PATCH 44/62] Updated helpfile --- pdlua-help.pd | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pdlua-help.pd b/pdlua-help.pd index 34c3780..7d25e83 100644 --- a/pdlua-help.pd +++ b/pdlua-help.pd @@ -1,4 +1,4 @@ -#N canvas 467 36 561 528 10; +#N canvas 466 36 565 577 10; #X declare -lib pdlua -path pdlua; #X declare -path pdlua/examples; #X text 16 358 See also:; @@ -174,19 +174,16 @@ #X connect 111 0 65 0; #X connect 112 0 67 0; #X restore 446 359 pd quickstart; -#N canvas 0 22 450 300 graphics 1; +#N canvas 121 146 669 774 graphics 1; #X obj 8 77 hello-gui; #X text 8 323 You can enable GUI mode by setting the self.gui variable in the constructor \, like this:; #X text 8 366 function yourclass:initialize(sel \, atoms); #X text 25 383 self.gui = 1; -#X text 25 400 return true; -#X text 8 418 end; +#X text 24 415 return true; +#X text 7 433 end; #X text 8 457 -------------- Painting: --------------; #X text 8 480 You can paint by defining the "paint" function \, for example:; #X text 8 561 end; -#X text 8 509 function yourclass:paint(); -#X text 25 526 gfx.set_color(250 \, 200 \, 240); -#X text 25 543 gfx.fill_all(); #X text 8 604 -------------- Mouse Events: --------------; #X text 8 633 You can receive mouse events by defining the "mouse_down" \, "mouse_up" \, "mouse_move" and "mouse_drag" functions. Both pass the x \, y coordinates as arguments. For example:; #X text 8 743 end; @@ -197,6 +194,10 @@ #X text 8 788 -------------- API: --------------; #X text 356 816 -- Paint callback. This is the only place where you're allowed to call any of the paint functions \; \; -- Mouse down callback \, called when the mouse is clicked \; -- Mouse up callback \, called when the mouse button is released \; -- Mouse move callback \, called when the mouse is moved while not being down \; -- Mouse drag callback \, called when the mouse is moved while also being down \; \; \; \; -- Request a repaint \, after this the "paint" callback will occur \; \; -- Sets the size of the drawing object. \; -- Sets the color for the next drawing operation. \; \; -- Draws a filled ellipse at the specified position and size. \; -- Draws the outline of an ellipse at the specified position and size. \; -- Draws a filled rectangle at the specified position and size. \; -- Draws the outline of a rectangle at the specified position and size. \; -- Draws a filled rounded rectangle at the specified position and size. \; -- Draws the outline of a rounded rectangle at the specified position and size. \; \; -- Draws a line between two points. \; -- Draws text at the specified position and size. \; \; -- Initiates a new path at the specified point. \; -- Adds a line segment to the current path. \; -- Adds a quadratic Bezier curve to the current path. \; -- Adds a cubic Bezier curve to the current path. \; -- Closes the current path. \; -- Draws the outline of the current path with the specified line width. \; -- Fills the current path. \; \; -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) \; \; -- Translates the coordinate system by the specified amounts. \; -- Scales the coordinate system by the specified factors. This will always happen after the translation \; -- Resets current scale and translation \;, f 134; #X text 8 816 paint() \; \; pd:Class:mouse_down(x \, y) \; pd:Class:mouse_up(x \, y) \; pd:Class:mouse_move(x \, y) \; pd:Class:mouse_drag(x \, y) \; \; -- Functions you can call \; \; pd:Class:repaint() \; \; gfx.set_size(w \, h) \; gfx.set_color(r \, g \, b \, a=1.0) \; \; gfx.fill_ellipse(x \, y \, w \, h) \; gfx.stroke_ellipse(x \, y \, w \, h \, line_width) \; gfx.fill_rect(x \, y \, w \, h) \; gfx.stroke_rect(x \, y \, w \, h \, line_width) \; gfx.fill_rounded_rect(x \, y \, w \, h \, corner_radius) \; gfx.stroke_rounded_rect(x \, y \, w \, h \, corner_radius \, line_width) \; \; gfx.draw_line(x1 \, y1 \, x2 \, y2) \; gfx.draw_text(text \, x \, y \, w \, fontsize) \; \; gfx.start_path(x \, y) \; gfx.line_to(x \, y) \; gfx.quad_to(x1 \, y1 \, x2 \, y2) \; gfx.cubic_to(x1 \, y1 \, x2 \, y2 \, x3 \, y) \; gfx.close_path() \; gfx.stroke_path(line_width) \; gfx.fill_path() \; \; gfx.fill_all() \; \; gfx.translate(tx \, ty) \; gfx.scale(sx \, sy) \; gfx.reset_transform() \;, f 58; +#X text 8 509 function yourclass:paint(g); +#X text 25 526 g.set_color(250 \, 200 \, 240); +#X text 25 543 g.fill_all(); +#X text 23 400 self.set_size(100 \, 100); #X restore 446 409 pd graphics; #X text 324 384 Details on how to create GUI objects ------->, f 18; #X obj 342 227 hello; From 0e80202251188c6a3acf1b56c7829ed0ab4da5af Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Fri, 23 Feb 2024 17:26:50 +0100 Subject: [PATCH 45/62] Fixed problem when drawing inside graphs in pd-vanilla --- pdlua.c | 1 - pdlua_gfx.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pdlua.c b/pdlua.c index 515607b..e592ed6 100644 --- a/pdlua.c +++ b/pdlua.c @@ -935,7 +935,6 @@ static int pdlua_class_new(lua_State *L) pdlua_widgetbehavior.w_visfn = pdlua_vis; pdlua_widgetbehavior.w_activatefn = pdlua_activate; class_setwidget(c, &pdlua_widgetbehavior); - if (c) { /* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 94250e3..05ffa87 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -545,7 +545,7 @@ static int reset_transform(lua_State* L) { static int can_draw(t_pdlua* obj) { - return glist_isvisible(obj->canvas) && gobj_shouldvis(obj, obj->canvas); + return gobj_shouldvis(obj, obj->canvas); } static int free_path(lua_State* L) From 90c340bc45e1b809a3c1c9663187309b4e80841a Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Sat, 24 Feb 2024 16:57:52 +0100 Subject: [PATCH 46/62] Updated helpfile --- pdlua-help.pd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pdlua-help.pd b/pdlua-help.pd index 7d25e83..e914996 100644 --- a/pdlua-help.pd +++ b/pdlua-help.pd @@ -1,4 +1,4 @@ -#N canvas 466 36 565 577 10; +#N canvas 466 36 564 576 10; #X declare -lib pdlua -path pdlua; #X declare -path pdlua/examples; #X text 16 358 See also:; @@ -174,7 +174,7 @@ #X connect 111 0 65 0; #X connect 112 0 67 0; #X restore 446 359 pd quickstart; -#N canvas 121 146 669 774 graphics 1; +#N canvas 21 133 772 759 graphics 0; #X obj 8 77 hello-gui; #X text 8 323 You can enable GUI mode by setting the self.gui variable in the constructor \, like this:; #X text 8 366 function yourclass:initialize(sel \, atoms); @@ -190,14 +190,14 @@ #X text 8 691 function yourclass:mouse_down(x \, y); #X text 25 708 pd.post(tostring(x)); #X text 25 725 pd.post(tostring(y)); -#X text 8 11 pdlua's graphics allow you to draw basic vector graphics and receive mouse events on pure-data and plugdata. Drawing functions are prefixed with 'gfx.' \, and should only be called from within the "paint" function, f 72; #X text 8 788 -------------- API: --------------; -#X text 356 816 -- Paint callback. This is the only place where you're allowed to call any of the paint functions \; \; -- Mouse down callback \, called when the mouse is clicked \; -- Mouse up callback \, called when the mouse button is released \; -- Mouse move callback \, called when the mouse is moved while not being down \; -- Mouse drag callback \, called when the mouse is moved while also being down \; \; \; \; -- Request a repaint \, after this the "paint" callback will occur \; \; -- Sets the size of the drawing object. \; -- Sets the color for the next drawing operation. \; \; -- Draws a filled ellipse at the specified position and size. \; -- Draws the outline of an ellipse at the specified position and size. \; -- Draws a filled rectangle at the specified position and size. \; -- Draws the outline of a rectangle at the specified position and size. \; -- Draws a filled rounded rectangle at the specified position and size. \; -- Draws the outline of a rounded rectangle at the specified position and size. \; \; -- Draws a line between two points. \; -- Draws text at the specified position and size. \; \; -- Initiates a new path at the specified point. \; -- Adds a line segment to the current path. \; -- Adds a quadratic Bezier curve to the current path. \; -- Adds a cubic Bezier curve to the current path. \; -- Closes the current path. \; -- Draws the outline of the current path with the specified line width. \; -- Fills the current path. \; \; -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) \; \; -- Translates the coordinate system by the specified amounts. \; -- Scales the coordinate system by the specified factors. This will always happen after the translation \; -- Resets current scale and translation \;, f 134; -#X text 8 816 paint() \; \; pd:Class:mouse_down(x \, y) \; pd:Class:mouse_up(x \, y) \; pd:Class:mouse_move(x \, y) \; pd:Class:mouse_drag(x \, y) \; \; -- Functions you can call \; \; pd:Class:repaint() \; \; gfx.set_size(w \, h) \; gfx.set_color(r \, g \, b \, a=1.0) \; \; gfx.fill_ellipse(x \, y \, w \, h) \; gfx.stroke_ellipse(x \, y \, w \, h \, line_width) \; gfx.fill_rect(x \, y \, w \, h) \; gfx.stroke_rect(x \, y \, w \, h \, line_width) \; gfx.fill_rounded_rect(x \, y \, w \, h \, corner_radius) \; gfx.stroke_rounded_rect(x \, y \, w \, h \, corner_radius \, line_width) \; \; gfx.draw_line(x1 \, y1 \, x2 \, y2) \; gfx.draw_text(text \, x \, y \, w \, fontsize) \; \; gfx.start_path(x \, y) \; gfx.line_to(x \, y) \; gfx.quad_to(x1 \, y1 \, x2 \, y2) \; gfx.cubic_to(x1 \, y1 \, x2 \, y2 \, x3 \, y) \; gfx.close_path() \; gfx.stroke_path(line_width) \; gfx.fill_path() \; \; gfx.fill_all() \; \; gfx.translate(tx \, ty) \; gfx.scale(sx \, sy) \; gfx.reset_transform() \;, f 58; #X text 8 509 function yourclass:paint(g); #X text 25 526 g.set_color(250 \, 200 \, 240); #X text 25 543 g.fill_all(); #X text 23 400 self.set_size(100 \, 100); +#X text 8 11 pdlua's graphics allow you to draw basic vector graphics and receive mouse events on pure-data and plugdata. Drawing functions need to be called on the graphics context class \, which is passed into the paint() function as the first argument., f 72; +#X text 8 816 pd:Class:repaint() \; \; pd:Class:mouse_down(x \, y) \; pd:Class:mouse_up(x \, y) \; pd:Class:mouse_move(x \, y) \; pd:Class:mouse_drag(x \, y) \; \; \; pd:Class:paint(g) \; \; g.set_size(w \, h) \; g.set_color(r \, g \, b \, a=1.0) \; \; g.fill_ellipse(x \, y \, w \, h) \; g.stroke_ellipse(x \, y \, w \, h \, line_width) \; g.fill_rect(x \, y \, w \, h) \; g.stroke_rect(x \, y \, w \, h \, line_width) \; g.fill_rounded_rect(x \, y \, w \, h \, corner_radius) \; g.stroke_rounded_rect(x \, y \, w \, h \, corner_radius \, line_width) \; \; g.draw_line(x1 \, y1 \, x2 \, y2) \; g.draw_text(text \, x \, y \, w \, fontsize) \; \; g.start_path(x \, y) \; g.line_to(x \, y) \; g.quad_to(x1 \, y1 \, x2 \, y2) \; g.cubic_to(x1 \, y1 \, x2 \, y2 \, x3 \, y) \; g.close_path() \; g.stroke_path(line_width) \; g.fill_path() \; \; g.fill_all() \; \; g.translate(tx \, ty) \; g.scale(sx \, sy) \; g.reset_transform() \;, f 58; +#X text 353 816 -- Request a repaint \, after this the "paint" callback will occur \; \; -- Mouse down callback \, called when the mouse is clicked \; -- Mouse up callback \, called when the mouse button is released \; -- Mouse move callback \, called when the mouse is moved while not being down \; -- Mouse drag callback \, called when the mouse is moved while also being down \; \; \; \; -- Paint callback The argument "g" is the graphics context that you can call these drawing functions on: -- Sets the size of the drawing object. \; -- Sets the color for the next drawing operation. \; \; -- Draws a filled ellipse at the specified position and size. \; -- Draws the outline of an ellipse at the specified position and size. \; -- Draws a filled rectangle at the specified position and size. \; -- Draws the outline of a rectangle at the specified position and size. \; -- Draws a filled rounded rectangle at the specified position and size. \; -- Draws the outline of a rounded rectangle at the specified position and size. \; \; -- Draws a line between two points. \; -- Draws text at the specified position and size. \; \; -- Initiates a new path at the specified point. \; -- Adds a line segment to the current path. \; -- Adds a quadratic Bezier curve to the current path. \; -- Adds a cubic Bezier curve to the current path. \; -- Closes the current path. \; -- Draws the outline of the current path with the specified line width. \; -- Fills the current path. \; \; -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) \; \; -- Translates the coordinate system by the specified amounts. \; -- Scales the coordinate system by the specified factors. This will always happen after the translation \; -- Resets current scale and translation \;, f 134; #X restore 446 409 pd graphics; #X text 324 384 Details on how to create GUI objects ------->, f 18; #X obj 342 227 hello; From e7d530b866a24a1d5d30303c8e0337ee7b2dbf7d Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Sat, 24 Feb 2024 16:58:38 +0100 Subject: [PATCH 47/62] Fixed pd-vanilla drawing bugs, fixed correct ordering in pd-vanilla, added enum with default colours --- pd.lua | 1 + pdlua.c | 10 ++--- pdlua.h | 5 ++- pdlua_gfx.h | 107 +++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 100 insertions(+), 23 deletions(-) diff --git a/pd.lua b/pd.lua index a06da3e..69bab41 100644 --- a/pd.lua +++ b/pd.lua @@ -496,4 +496,5 @@ function luax:initialize(sel, atoms) -- motivation: pd-list 2007-09-23 end end +Colors = {background = "0", foreground = "1", outline = "2"} -- fin pd.lua diff --git a/pdlua.c b/pdlua.c index e592ed6..10a9aae 100644 --- a/pdlua.c +++ b/pdlua.c @@ -646,10 +646,10 @@ void pdlua_vis(t_gobj *z, t_glist *glist, int vis){ // Otherwise, repaint or clear the custom graphics if(vis) { - pdlua_gfx_repaint(x); + pdlua_gfx_repaint(x, 1); } else { - pdlua_gfx_clear(x); + pdlua_gfx_clear(x, 1); } } @@ -732,9 +732,9 @@ static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ x->pd.te_xpix += dx, x->pd.te_ypix += dy; dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); - // Will re-draw GUI - pdlua_vis(z, glist, 0); - pdlua_vis(z, glist, 1); +#if !PLUGDATA + gfx_displace(z, glist, dx, dy); +#endif canvas_fixlinesfor(glist, (t_text*)x); } diff --git a/pdlua.h b/pdlua.h index f8348f3..29bb8c4 100644 --- a/pdlua.h +++ b/pdlua.h @@ -21,8 +21,9 @@ typedef struct _pdlua_gfx { #if !PLUGDATA char object_tag[128]; // Tcl/tk tag that is attached to all drawings + char order_tag[128]; // Tag for invisible line, used to preserve correct object ordering char current_paint_tag[128]; // Tcl/tk tag that is only attached to the current drawing in progress - + // Variables to keep track of mouse button state and drag position int mouse_drag_x, mouse_drag_y, mouse_down; @@ -30,6 +31,8 @@ typedef struct _pdlua_gfx int translate_x, translate_y; float scale_x, scale_y; + int first_draw; + char current_color[8]; // Keep track of current color #else void(*plugdata_draw_callback)(void*, t_symbol*, int, t_atom*); // Callback to perform drawing in plugdata diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 05ffa87..7945cff 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -61,10 +61,13 @@ static int free_path(lua_State* L); // pdlua_gfx_clear, pdlua_gfx_repaint and pdlua_gfx_mouse_* correspond to the various callbacks the user can assign -void pdlua_gfx_clear(t_pdlua *obj); // only for pd-vanilla, to delete all tcl/tk items +void pdlua_gfx_clear(t_pdlua *obj, int removed); // only for pd-vanilla, to delete all tcl/tk items // Trigger repaint callback in lua script -void pdlua_gfx_repaint(t_pdlua *o) { +void pdlua_gfx_repaint(t_pdlua *o, int firsttime) { +#if !PLUGDATA + o->gfx.first_draw = firsttime; +#endif lua_getglobal(__L(), "pd"); lua_getfield (__L(), -1, "_repaint"); lua_pushlightuserdata(__L(), o); @@ -75,7 +78,6 @@ void pdlua_gfx_repaint(t_pdlua *o) { lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); lua_pop(__L(), 1); - if (lua_pcall(__L(), 1, 0, 0)) { pd_error(o, "lua: error in repaint:\n%s", lua_tostring(__L(), -1)); @@ -83,6 +85,9 @@ void pdlua_gfx_repaint(t_pdlua *o) { } lua_pop(__L(), 1); /* pop the global "pd" */ +#if !PLUGDATA + o->gfx.first_draw = 0; +#endif } // Pass mouse events to lua script @@ -241,12 +246,12 @@ static inline void plugdata_draw(t_pdlua* obj, t_symbol* sym, int argc, t_atom* } } -void pdlua_gfx_clear(t_pdlua* obj) { +void pdlua_gfx_clear(t_pdlua* obj, int removed) { } static int gfx_initialize(t_pdlua* obj) { - pdlua_gfx_repaint(obj); // Initial repaint + pdlua_gfx_repaint(obj, 0); // Initial repaint return 0; } @@ -277,8 +282,15 @@ static int end_paint(lua_State* L) { static int set_color(lua_State* L) { t_pdlua* obj = get_current_object(L); + + if (lua_gettop(L) == 3) { // Single argument: parse as color ID instead of RGB + t_atom arg; + SETFLOAT(&arg, luaL_checknumber(L, 1)); // color ID + 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 @@ -545,7 +557,7 @@ static int reset_transform(lua_State* L) { static int can_draw(t_pdlua* obj) { - return gobj_shouldvis(obj, obj->canvas); + return (glist_isvisible(obj->canvas) && gobj_shouldvis(obj, obj->canvas)) || obj->gfx.first_draw; } static int free_path(lua_State* L) @@ -555,12 +567,18 @@ static int free_path(lua_State* L) return 0; } -void pdlua_gfx_clear(t_pdlua *obj) { +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); gfx->current_paint_tag[0] = '\0'; + if(removed && gfx->order_tag[0] != '\0') + { + pdgui_vmess(0, "crs", cnv, "delete", gfx->order_tag); + gfx->order_tag[0] = '\0'; + } + glist_eraseiofor(glist_getcanvas(cnv), &obj->pd, gfx->object_tag); } @@ -586,6 +604,16 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x *y2 = (y + h) * glist_getzoom(cnv); } +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 xpos = text_xpix((t_object*)x, x->canvas); + int ypos = text_ypix((t_object*)x, x->canvas); + glist_drawiofor(glist_getcanvas(x->canvas), (t_object*)x, 0, x->gfx.object_tag, xpos, ypos, xpos + x->gfx.width, ypos + x->gfx.height); +} + static const char* register_drawing(t_pdlua *object) { t_pdlua_gfx *gfx = &object->gfx; @@ -602,7 +630,7 @@ static int gfx_initialize(t_pdlua *obj) gfx->object_tag[127] = '\0'; gfx->current_paint_tag[0] = '\0'; - pdlua_gfx_repaint(obj); + pdlua_gfx_repaint(obj, 0); return 0; } @@ -611,30 +639,57 @@ static int set_size(lua_State* L) t_pdlua* obj = get_current_object(L); obj->gfx.width = luaL_checknumber(L, 1); obj->gfx.height = luaL_checknumber(L, 2); - pdlua_gfx_repaint(obj); + pdlua_gfx_repaint(obj, 0); return 0; } static int start_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_pdlua_gfx *gfx = &obj->gfx; + if(gfx->object_tag[0] == '\0') + { + return 0; + } + int draw = can_draw(obj); lua_pushboolean(L, draw); // Return a value, which decides whether we're allowed to paint or not - // check if anything was painted before - if(draw && strlen(obj->gfx.current_paint_tag)) - pdlua_gfx_clear(obj); + if(gfx->first_draw) + { + // Whenever the objects gets painted for the first time with a "vis" message, + // we add a small invisible line that won't get touched or repainted later. + // 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); + snprintf(gfx->order_tag, 128, ".x%d", rand()); + gfx->order_tag[127] = '\0'; + + const char* tags[] = { gfx->order_tag }; + pdgui_vmess(0, "crr iiii ri rS", cnv, "create", "line", 0, 0, 1, 1, + "-width", 1, "-tags", 1, tags); + } + // check if anything was painted before + if(draw && strlen(gfx->current_paint_tag)) + pdlua_gfx_clear(obj, 0); + return 1; } static int end_paint(lua_State* L) { t_pdlua* obj = get_current_object(L); + t_canvas *cnv = glist_getcanvas(obj->canvas); + t_pdlua_gfx *gfx = &obj->gfx; // 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, obj->gfx.object_tag, xpos, ypos, xpos + obj->gfx.width, ypos + obj->gfx.height); + glist_drawiofor(glist_getcanvas(obj->canvas), (t_object*)obj, 1, gfx->object_tag, xpos, ypos, xpos + gfx->width, ypos + gfx->height); + + 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; } @@ -644,9 +699,27 @@ static int set_color(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; - int r = luaL_checknumber(L, 1); - int g = luaL_checknumber(L, 2); - int b = luaL_checknumber(L, 3); + int r, g, b; + if (lua_gettop(L) == 3) { // Single argument: parse as color ID instead of RGB + int color_id = luaL_checknumber(L, 1); + if(color_id != 1) + { + r = 255; + g = 255; + b = 255; + } + else { + r = 0; + g = 0; + b = 0; + } + } + else { + r = luaL_checknumber(L, 1); + g = luaL_checknumber(L, 2); + b = luaL_checknumber(L, 3); + } + // AFAIK, alpha is not supported in tcl/tk snprintf(gfx->current_color, 8, "#%02X%02X%02X", r, g, b); From 9c4a605786c477a7dcf20b526873905d5d73971a Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Mon, 26 Feb 2024 15:48:10 +0100 Subject: [PATCH 48/62] Fixed broken dragging for non-gui objects --- pdlua.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/pdlua.c b/pdlua.c index 10a9aae..13c8791 100644 --- a/pdlua.c +++ b/pdlua.c @@ -729,11 +729,17 @@ static void pdlua_reload(t_gobj* z) static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ t_pdlua *x = (t_pdlua *)z; - x->pd.te_xpix += dx, x->pd.te_ypix += dy; - dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); #if !PLUGDATA - gfx_displace(z, glist, dx, dy); + if(x->has_gui) + { + x->pd.te_xpix += dx, x->pd.te_ypix += dy; + dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); + gfx_displace(z, glist, dx, dy); + } + else { + text_widgetbehavior.w_displacefn(z, glist, dx, dy); + } #endif canvas_fixlinesfor(glist, (t_text*)x); @@ -855,33 +861,25 @@ static void pdlua_menu_open(t_pdlua *o) PDLUA_DEBUG3("pdlua_menu_open: L is %p, name is %s stack top is %d", __L(), name, lua_gettop(__L())); if (name) { - if (name[strlen(name)-1] == 'x') + lua_getglobal(__L(), "pd"); + lua_getfield(__L(), -1, "_get_class"); + lua_pushlightuserdata(__L(), o); + if (lua_pcall(__L(), 1, 1, 0)) { - /* pdluax is a class, the particular file should loadable by name alone, we hope */ - sprintf(pathname, "%s", name); - lua_pop(__L(), 2); /* pop name and the global "pd" */ + pd_error(NULL, "lua: error in get_class:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 4); /* pop the error string, global "pd", name, global "pd"*/ + return; } - else - { - lua_getglobal(__L(), "pd"); - lua_getfield(__L(), -1, "_get_class"); - lua_pushlightuserdata(__L(), o); - if (lua_pcall(__L(), 1, 1, 0)) - { - pd_error(NULL, "lua: error in get_class:\n%s", lua_tostring(__L(), -1)); - lua_pop(__L(), 4); /* pop the error string, global "pd", name, global "pd"*/ - return; - } - class = (t_class *)lua_touserdata(__L(), -1); + class = (t_class *)lua_touserdata(__L(), -1); #if PLUGDATA - if (!*class->c_externdir->s_name) - path = plugdata_datadir; - else + if (!*class->c_externdir->s_name) + path = plugdata_datadir; + else #endif - path = class->c_externdir->s_name; - sprintf(pathname, "%s/%s", path, name); - lua_pop(__L(), 4); /* pop class, global "pd", name, global "pd"*/ - } + path = class->c_externdir->s_name; + sprintf(pathname, "%s/%s", path, name); + lua_pop(__L(), 4); /* pop class, global "pd", name, global "pd"*/ + #if PD_MAJOR_VERSION==0 && PD_MINOR_VERSION<43 post("Opening %s for editing", pathname); #else From 206b27d9469b0161f2f1e9bc8f1f9fbcf2b5347f Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Mon, 26 Feb 2024 17:09:07 +0100 Subject: [PATCH 49/62] Implement better affine transform system for pd-vanilla --- pdlua.c | 6 +-- pdlua.h | 13 ++++-- pdlua_gfx.h | 114 +++++++++++++++++++++++++++++++++------------------- 3 files changed, 84 insertions(+), 49 deletions(-) diff --git a/pdlua.c b/pdlua.c index 13c8791..fc0db16 100644 --- a/pdlua.c +++ b/pdlua.c @@ -982,10 +982,8 @@ static int pdlua_object_new(lua_State *L) #if !PLUGDATA // Init graphics state for pd - o->gfx.scale_x = 1.0f; - o->gfx.scale_y = 1.0f; - o->gfx.translate_x = 0; - o->gfx.translate_y = 0; + o->gfx.num_transforms = 0; + o->gfx.transforms = NULL; o->gfx.mouse_drag_x = 0; o->gfx.mouse_drag_y = 0; o->gfx.mouse_down = 0; diff --git a/pdlua.h b/pdlua.h index 29bb8c4..ad6fd2f 100644 --- a/pdlua.h +++ b/pdlua.h @@ -17,6 +17,14 @@ #include "m_pd.h" +typedef enum {SCALE, TRANSLATE} transform_type; + +typedef struct _gfx_transform +{ + transform_type type; + float x, y; +} gfx_transform; + typedef struct _pdlua_gfx { #if !PLUGDATA @@ -27,9 +35,8 @@ typedef struct _pdlua_gfx // Variables to keep track of mouse button state and drag position int mouse_drag_x, mouse_drag_y, mouse_down; - // Variables for managing transforms - int translate_x, translate_y; - float scale_x, scale_y; + gfx_transform* transforms; + int num_transforms; int first_draw; diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 7945cff..9e11b69 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -59,6 +59,7 @@ static int reset_transform(lua_State* L); static int free_path(lua_State* L); + // pdlua_gfx_clear, pdlua_gfx_repaint and pdlua_gfx_mouse_* correspond to the various callbacks the user can assign void pdlua_gfx_clear(t_pdlua *obj, int removed); // only for pd-vanilla, to delete all tcl/tk items @@ -567,6 +568,33 @@ static int free_path(lua_State* L) return 0; } +static int transform_size(t_pdlua_gfx* gfx, int* w, int* h) { + for(int i = gfx->num_transforms - 1; i >= 0; i--) + { + if(gfx->transforms[i].type == SCALE) + { + *w *= gfx->transforms[i].x; + *h *= gfx->transforms[i].y; + } + } +} + +static int transform_point(t_pdlua_gfx* gfx, int* x, int* 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; + } + } +} + void pdlua_gfx_clear(t_pdlua *obj, int removed) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); @@ -590,13 +618,11 @@ static void get_bounds_args(lua_State* L, t_pdlua* obj, t_pdlua_gfx *gfx, int* x int w = luaL_checknumber(L, 3); int h = luaL_checknumber(L, 4); - x *= gfx->scale_x; - y *= gfx->scale_y; - w *= gfx->scale_x; - h *= gfx->scale_y; + transform_point(gfx, &x, &y); + transform_size(gfx, &w, &h); - x += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / glist_getzoom(cnv)); - y += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / glist_getzoom(cnv)); + 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); @@ -824,8 +850,10 @@ static int fill_rounded_rect(lua_State* L) { get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); int radius = luaL_checknumber(L, 5); // Radius for rounded corners - int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); - int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); + int radius_x = radius * glist_getzoom(cnv); + int radius_y = radius * glist_getzoom(cnv); + + transform_size(gfx, &radius_x, &radius_y); const char* tags[] = { gfx->object_tag, register_drawing(obj) }; @@ -850,8 +878,9 @@ static int stroke_rounded_rect(lua_State* L) { get_bounds_args(L, obj, gfx, &x1, &y1, &x2, &y2); int radius = luaL_checknumber(L, 5); // Radius for rounded corners - int radius_x = radius * gfx->scale_x * glist_getzoom(cnv); - int radius_y = radius * gfx->scale_y * glist_getzoom(cnv); + 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(obj) }; @@ -891,17 +920,15 @@ static int draw_line(lua_State* L) { int y2 = luaL_checknumber(L, 4); int lineWidth = luaL_checknumber(L, 5); - x1 *= gfx->scale_x; - y1 *= gfx->scale_y; - x2 *= gfx->scale_x; - y2 *= gfx->scale_y; + transform_point(gfx, &x1, &y1); + transform_point(gfx, &x2, &y2); int canvas_zoom = glist_getzoom(cnv); - x1 += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / canvas_zoom); - y1 += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / canvas_zoom); - x2 += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / canvas_zoom); - y2 += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / canvas_zoom); + x1 += text_xpix((t_object*)obj, obj->canvas) / canvas_zoom; + y1 += text_ypix((t_object*)obj, obj->canvas) / canvas_zoom; + x2 += text_xpix((t_object*)obj, obj->canvas) / canvas_zoom; + y2 += text_ypix((t_object*)obj, obj->canvas) / canvas_zoom; x1 *= canvas_zoom; y1 *= canvas_zoom; @@ -929,13 +956,12 @@ static int draw_text(lua_State* L) { int w = luaL_checknumber(L, 4); int fontHeight = luaL_checknumber(L, 5); - x *= gfx->scale_x; - y *= gfx->scale_y; - w *= gfx->scale_x; + transform_point(gfx, &x, &y); + transform_size(gfx, &w, &fontHeight); int canvas_zoom = glist_getzoom(cnv); - x += gfx->translate_x + (text_xpix((t_object*)obj, obj->canvas) / canvas_zoom); - y += gfx->translate_y + (text_ypix((t_object*)obj, obj->canvas) / canvas_zoom); + 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; @@ -951,7 +977,7 @@ static int draw_text(lua_State* L) { t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(sys_font)); - SETFLOAT (fontatoms+1, fontHeight * canvas_zoom * gfx->scale_y); + SETFLOAT (fontatoms+1, fontHeight * canvas_zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); pdgui_vmess(0, "crs rA rs rs", cnv, "itemconfigure", tags[1], @@ -1072,11 +1098,7 @@ static int stroke_path(lua_State* L) { for (int i = 0; i < path->num_path_segments; i++) { int x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; - x *= gfx->scale_x; - y *= gfx->scale_y; - - x += gfx->translate_x; - y += gfx->translate_y; + transform_point(gfx, &x, &y); int canvas_zoom = glist_getzoom(cnv); path->path_segments[i * 2] = (x * canvas_zoom) + obj_x; @@ -1132,11 +1154,7 @@ static int fill_path(lua_State* L) { for (int i = 0; i < path->num_path_segments; i++) { int x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; - x *= gfx->scale_x; - y *= gfx->scale_y; - - x += gfx->translate_x; - y += gfx->translate_y; + transform_point(gfx, &x, &y); path->path_segments[i * 2] = x * glist_getzoom(cnv) + obj_x; path->path_segments[i * 2 + 1] = y * glist_getzoom(cnv) + obj_y; @@ -1176,12 +1194,19 @@ static int fill_path(lua_State* L) { return 0; } + static int translate(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - gfx->translate_x = luaL_checknumber(L, 1); - gfx->translate_y = luaL_checknumber(L, 2); + + 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; } @@ -1189,8 +1214,14 @@ static int scale(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - gfx->scale_x = luaL_checknumber(L, 1); - gfx->scale_y = luaL_checknumber(L, 2); + + 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; } @@ -1198,10 +1229,9 @@ static int reset_transform(lua_State* L) { t_pdlua* obj = get_current_object(L); t_pdlua_gfx *gfx = &obj->gfx; - gfx->translate_x = 0; - gfx->translate_y = 0; - gfx->scale_x = 1.0f; - gfx->scale_y = 1.0f; + + gfx->transforms = resizebytes(gfx->transforms, gfx->num_transforms * sizeof(gfx_transform), 0); + gfx->num_transforms = 0; return 0; } #endif From 03d00ee346c61e4b48414cd876821f29181a6b56 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Mon, 26 Feb 2024 17:16:46 +0100 Subject: [PATCH 50/62] Free transform memory after use --- pdlua.c | 11 +++++++++++ pdlua.h | 2 ++ pdlua_gfx.h | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pdlua.c b/pdlua.c index fc0db16..f96f34a 100644 --- a/pdlua.c +++ b/pdlua.c @@ -630,6 +630,17 @@ static void pdlua_free( t_pdlua *o /**< The object to destruct. */) } lua_pop(__L(), 1); /* pop the global "pd" */ PDLUA_DEBUG("pdlua_free: end. stack top %d", lua_gettop(__L())); + + if(o->has_gui) + { +#if !PLUGDATA + if(o->gfx.num_transforms != 0) + { + freebytes(o->gfx.transforms, o->gfx.num_transforms * sizeof(gfx_transform)); + } +#endif + } + return; } diff --git a/pdlua.h b/pdlua.h index ad6fd2f..154a1e6 100644 --- a/pdlua.h +++ b/pdlua.h @@ -17,6 +17,7 @@ #include "m_pd.h" +#if !PLUGDATA typedef enum {SCALE, TRANSLATE} transform_type; typedef struct _gfx_transform @@ -24,6 +25,7 @@ typedef struct _gfx_transform transform_type type; float x, y; } gfx_transform; +#endif typedef struct _pdlua_gfx { diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 9e11b69..2f2136f 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -568,7 +568,7 @@ static int free_path(lua_State* L) return 0; } -static int transform_size(t_pdlua_gfx* gfx, int* w, int* h) { +static void transform_size(t_pdlua_gfx* gfx, int* w, int* h) { for(int i = gfx->num_transforms - 1; i >= 0; i--) { if(gfx->transforms[i].type == SCALE) @@ -579,7 +579,7 @@ static int transform_size(t_pdlua_gfx* gfx, int* w, int* h) { } } -static int transform_point(t_pdlua_gfx* gfx, int* x, int* y) { +static void transform_point(t_pdlua_gfx* gfx, int* x, int* y) { for(int i = gfx->num_transforms - 1; i >= 0; i--) { if(gfx->transforms[i].type == SCALE) From 2a62d4eb1e4a486f91d9959849e5d140a16d53c4 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 27 Feb 2024 01:25:44 +0100 Subject: [PATCH 51/62] Fixed some memory issues --- pdlua_gfx.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 2f2136f..fa75897 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -181,10 +181,12 @@ static const luaL_Reg path_methods[] = { {"cubic_to", cubic_to}, {"close", close_path}, {"__gc", free_path}, + {NULL, NULL} // Sentinel to end the list }; static const luaL_Reg path_constructor[] = { {"start", start_path}, + {NULL, NULL} // Sentinel to end the list }; // Register functions with Lua @@ -993,7 +995,13 @@ static int draw_text(lua_State* L) { static void add_path_segment(path_state* path, int x, int y) { int path_segment_space = (path->num_path_segments + 1) * 2; - path->path_segments = (int*)resizebytes(path->path_segments, path->num_path_segments_allocated * sizeof(int), MAX((path_segment_space + 1), path->num_path_segments_allocated) * sizeof(int)); + if(!path->num_path_segments_allocated) { + path->path_segments = (int*)getbytes((path_segment_space + 1) * sizeof(int)); + } + else { + path->path_segments = (int*)resizebytes(path->path_segments, path->num_path_segments_allocated * sizeof(int), MAX((path_segment_space + 1), path->num_path_segments_allocated) * sizeof(int)); + } + path->num_path_segments_allocated = path_segment_space; path->path_segments[path->num_path_segments * 2] = x; @@ -1200,7 +1208,16 @@ static int translate(lua_State* L) { t_pdlua_gfx *gfx = &obj->gfx; - gfx->transforms = resizebytes(gfx->transforms, gfx->num_transforms * sizeof(gfx_transform), (gfx->num_transforms + 1) * sizeof(gfx_transform)); + 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); From 7243dbfc69f204aaa51581b173fcb19927c1c36c Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 27 Feb 2024 01:33:04 +0100 Subject: [PATCH 52/62] Fixed object moving issues --- pdlua.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pdlua.c b/pdlua.c index f96f34a..476b764 100644 --- a/pdlua.c +++ b/pdlua.c @@ -741,17 +741,19 @@ static void pdlua_reload(t_gobj* z) static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ t_pdlua *x = (t_pdlua *)z; -#if !PLUGDATA + if(x->has_gui) { - x->pd.te_xpix += dx, x->pd.te_ypix += dy; - dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); + x->pd.te_xpix += dx, x->pd.te_ypix += dy; + dx *= glist_getzoom(glist), dy *= glist_getzoom(glist); +#if !PLUGDATA gfx_displace(z, glist, dx, dy); +#endif } else { text_widgetbehavior.w_displacefn(z, glist, dx, dy); } -#endif + canvas_fixlinesfor(glist, (t_text*)x); } From d6643741fee948947db946e00833cb423638ee89 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 27 Feb 2024 03:43:31 +0100 Subject: [PATCH 53/62] Added signal processing support --- pd.lua | 18 +++++- pdlua.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++------ pdlua.h | 7 ++- 3 files changed, 195 insertions(+), 21 deletions(-) diff --git a/pd.lua b/pd.lua index 69bab41..a5a8b6b 100644 --- a/pd.lua +++ b/pd.lua @@ -79,10 +79,24 @@ pd._dispatcher = function (object, inlet, sel, atoms) end end +pd._dsp = function (object, samplerate, blocksize) + local obj = pd._objects[object] + if nil ~= obj and type(obj.dsp) == "function" then + pd._objects[object]:dsp(samplerate, blocksize) + end +end + +pd._perform_dsp = function (object, ...) + local obj = pd._objects[object] + if nil ~= obj and type(obj.perform) == "function" then + return pd._objects[object]:perform(...) + end +end + -- repaint method dispatcher pd._repaint = function (object) local obj = pd._objects[object] - if nil ~= obj then + if nil ~= obj and type(obj.repaint) == "function" then obj:repaint(); end end @@ -496,5 +510,7 @@ function luax:initialize(sel, atoms) -- motivation: pd-list 2007-09-23 end end +DATA = 0 +SIGNAL = 1 Colors = {background = "0", foreground = "1", outline = "2"} -- fin pd.lua diff --git a/pdlua.c b/pdlua.c index 476b764..e0e1bad 100644 --- a/pdlua.c +++ b/pdlua.c @@ -912,6 +912,114 @@ static void pdlua_menu_open(t_pdlua *o) PDLUA_DEBUG("pdlua_menu_open end. stack top is %d", lua_gettop(__L())); } +static t_int *pdlua_perform(t_int *w){ + t_pdlua *o = (t_pdlua *)(w[1]); + int nblock = (int)(w[2]); + + + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), o); // Use a unique key for your userdata + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); // Store the userdata in the registry + lua_pop(__L(), 1); // Pop the registry table from the stack + + //t_float *in1 = (t_float *)(w[3]); + //t_float *in2 = (t_float *)(w[4]); + PDLUA_DEBUG("pdlua_perform: stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_perform_dsp"); + lua_pushlightuserdata(__L(), o); + + for (int i = 0; i < o->siginlets; i++) + { + lua_newtable(__L()); + t_float *in = (t_float*)w[i + 3]; + for (int j = 0; j < nblock; j++) + { + lua_pushinteger(__L(), j + 1); + lua_pushnumber(__L(), in[j]); + lua_settable(__L(), -3); + } + } + + if (lua_pcall(__L(), 1 + o->siginlets, o->sigoutlets, 0)) + { + pd_error(o, "pdlua: error in perform:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 1); /* pop the error string */ + } + + if (!lua_istable(__L(), -1)) + { + const char *s = "pdlua: 'perform' function should return"; + if (o->sigoutlets == 1) + pd_error(o, "%s %s", s, "a table"); + else if (o->sigoutlets > 1) + pd_error(o, "%s %d %s", s, o->sigoutlets, "tables"); + lua_pop(__L(), 1 + o->sigoutlets); + return w + o->siginlets + o->sigoutlets + 3; + } + + for (int i = o->sigoutlets - 1; i >= 0; i--) + { + t_float *out = (t_float*)w[i + 3 + o->siginlets]; + for (int j = 0; j < nblock; j++) + { + lua_pushinteger(__L(), (lua_Integer)(j + 1)); + lua_gettable(__L(), -2); + if (lua_isnumber(__L(), -1)) + out[j] = (t_float)lua_tonumber(__L(), -1); + else if (lua_isboolean(__L(), -1)) + out[j] = (t_float)lua_toboolean(__L(), -1); + else + out[j] = 0.0f; + lua_pop(__L(), 1); + } + lua_pop(__L(), 1); + } + + lua_pop(__L(), 1); /* pop the global "pd" */ + + PDLUA_DEBUG("pdlua_perform: end. stack top %d", lua_gettop(__L())); + + return w + o->siginlets + o->sigoutlets + 3; +} + +static void pdlua_dsp(t_pdlua *x, t_signal **sp){ + int sum = x->siginlets + x->sigoutlets; + if(sum == 0) return; + + PDLUA_DEBUG("pdlua_dsp: stack top %d", lua_gettop(__L())); + lua_getglobal(__L(), "pd"); + lua_getfield (__L(), -1, "_dsp"); + lua_pushlightuserdata(__L(), x); + lua_pushnumber(__L(), sys_getsr()); + lua_pushnumber(__L(), sys_getblksize()); + + // Write object ptr to registry to make it reliably accessible + lua_pushvalue(__L(), LUA_REGISTRYINDEX); + lua_pushlightuserdata(__L(), x); // Use a unique key for your userdata + lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); // Store the userdata in the registry + lua_pop(__L(), 1); // Pop the registry table from the stack + + if (lua_pcall(__L(), 3, 0, 0)) + { + pd_error(x, "pdlua: error in dsp:\n%s", lua_tostring(__L(), -1)); + lua_pop(__L(), 1); /* pop the error string */ + } + lua_pop(__L(), 1); /* pop the global "pd" */ + + PDLUA_DEBUG("pdlua_dsp: end. stack top %d", lua_gettop(__L())); + + x->w = getbytes((sum + 2) * sizeof(t_int*)); + t_int **w = x->w; + w[0] = (t_int*)x; + w[1] = (t_int*)sp[0]->s_n; + for (int i = 0; i < sum; i++) + w[i + 2] = (t_int *)sp[i]->s_vec; + + dsp_addv(pdlua_perform, sum + 2, (t_int *)(w)); +} + t_widgetbehavior pdlua_widgetbehavior; @@ -951,6 +1059,7 @@ static int pdlua_class_new(lua_State *L) /* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ class_addmethod(c, (t_method)pdlua_menu_open, gensym("menu-open"), A_NULL);/* (mrpeach 20111025) */ class_addmethod(c, (t_method)pdlua_reload, gensym("_reload"), A_NULL);/* (mrpeach 20111025) */ + class_addmethod(c, (t_method)pdlua_dsp, gensym("dsp"), A_CANT, 0); /* timschoen 20240226 */ } /**/ @@ -988,6 +1097,8 @@ static int pdlua_object_new(lua_State *L) o->in = NULL; o->outlets = 0; o->out = NULL; + o->siginlets = 0; + o->sigoutlets = 0; o->canvas = canvas_getcurrent(); o->gfx.width = 80; @@ -1036,22 +1147,42 @@ static int pdlua_object_createinlets(lua_State *L) * \li \c 2 Number of inlets. * */ { - int i; - PDLUA_DEBUG("pdlua_object_createinlets: stack top is %d", lua_gettop(L)); if (lua_islightuserdata(L, 1)) { t_pdlua *o = lua_touserdata(L, 1); - if (o) - { - o->inlets = luaL_checknumber(L, 2); - o->in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); - for (i = 0; i < o->inlets; ++i) - { - pdlua_proxyinlet_init(&o->in[i], o, i); - inlet_new(&o->pd, &o->in[i].pd, 0, 0); + if(o) { + if (lua_isnumber(L, 2)) { + // If it's a number, it means the number of data inlets + o->inlets = luaL_checknumber(L, 2); + o->in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); + for (int i = 0; i < o->inlets; ++i) + { + pdlua_proxyinlet_init(&o->in[i], o, i); + inlet_new(&o->pd, &o->in[i].pd, 0, 0); + } + } else if (lua_istable(L, 2)) { + // If it's a table, it means a list of inlet types (data or signal) + o->inlets = lua_rawlen(L, 2); + o->in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); + for (int i = 0; i < o->inlets; ++i) + { + lua_rawgeti(L, 2, i + 1); // Get element at index i+1 + if (lua_isnumber(L, -1)) { + int is_signal = lua_tonumber(L, -1); + o->siginlets += is_signal; + + pdlua_proxyinlet_init(&o->in[i], o, i); + inlet_new(&o->pd, &o->in[i].pd, is_signal ? &s_signal : 0, is_signal ? &s_signal : 0); + } + lua_pop(L, 1); // Pop the value from the stack + } + } else { + // Invalid argument type + return luaL_error(L, "inlets must be a number or a table"); } } + } PDLUA_DEBUG("pdlua_object_createinlets: end. stack top is %d", lua_gettop(L)); return 0; @@ -1065,21 +1196,45 @@ static int pdlua_object_createoutlets(lua_State *L) * \li \c 2 Number of outlets. * */ { - int i; - PDLUA_DEBUG("pdlua_object_createoutlets: stack top is %d", lua_gettop(L)); if (lua_islightuserdata(L, 1)) { t_pdlua *o = lua_touserdata(L, 1); if (o) { - o->outlets = luaL_checknumber(L, 2); - if (o->outlets > 0) - { - o->out = malloc(o->outlets * sizeof(t_outlet *)); - for (i = 0; i < o->outlets; ++i) o->out[i] = outlet_new(&o->pd, 0); + if (lua_isnumber(L, 2)) { + // If it's a number, it means the number of data outlets + o->outlets = luaL_checknumber(L, 2); + if (o->outlets > 0) + { + o->out = malloc(o->outlets * sizeof(t_outlet *)); + for (int i = 0; i < o->outlets; ++i) o->out[i] = outlet_new(&o->pd, 0); + } + else o->out = NULL; + } else if (lua_istable(L, 2)) { + // If it's a table, it means a list of outlet types (data or signal) + o->outlets = lua_rawlen(L, 2); + if (o->outlets > 0) + { + o->out = malloc(o->outlets * sizeof(t_outlet *)); + for (int i = 0; i < o->outlets; ++i) + { + lua_rawgeti(L, 2, i + 1); // Get element at index i+1 + if (lua_isnumber(L, -1)) { + int is_signal = lua_tonumber(L, -1); + o->sigoutlets += is_signal; + // Do something with the value + o->out[i] = outlet_new(&o->pd, is_signal ? &s_signal : 0); + } + lua_pop(L, 1); // Pop the value from the stack + } + } + else o->out = NULL; + } else { + // Invalid argument type + return luaL_error(L, "outlets must be a number or a table"); } - else o->out = NULL; + } } PDLUA_DEBUG("pdlua_object_createoutlets: end stack top is %d", lua_gettop(L)); diff --git a/pdlua.h b/pdlua.h index 154a1e6..658e99b 100644 --- a/pdlua.h +++ b/pdlua.h @@ -60,9 +60,12 @@ typedef struct pdlua struct pdlua_proxyinlet *in; /**< The inlets themselves. */ int outlets; /**< Number of outlets. */ t_outlet **out; /**< The outlets themselves. */ + int siginlets; /**< Number of signal inlets. */ + int sigoutlets; /**< Number of signal outlets. */ t_canvas *canvas; /**< The canvas that the object was created on. */ - int has_gui; /**< True if graphics are enabled. */ - t_pdlua_gfx gfx; /**< Holds state for graphics. */ + int has_gui; /**< True if graphics are enabled. */ + t_pdlua_gfx gfx; /**< Holds state for graphics. */ + t_int** w; /**< Holds state for signals. */ } t_pdlua; From be8983ae8c438a8613b6cf7606597e69682f816b Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 27 Feb 2024 03:49:38 +0100 Subject: [PATCH 54/62] Cleaned up --- pdlua.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pdlua.c b/pdlua.c index e0e1bad..27d72e2 100644 --- a/pdlua.c +++ b/pdlua.c @@ -922,9 +922,7 @@ static t_int *pdlua_perform(t_int *w){ lua_pushlightuserdata(__L(), o); // Use a unique key for your userdata lua_seti(__L(), -2, PDLUA_OBJECT_REGISTRTY_ID); // Store the userdata in the registry lua_pop(__L(), 1); // Pop the registry table from the stack - - //t_float *in1 = (t_float *)(w[3]); - //t_float *in2 = (t_float *)(w[4]); + PDLUA_DEBUG("pdlua_perform: stack top %d", lua_gettop(__L())); lua_getglobal(__L(), "pd"); lua_getfield (__L(), -1, "_perform_dsp"); From 34395e0d5cf165d0a1344c31d568fbf5e8468b37 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 27 Feb 2024 03:54:18 +0100 Subject: [PATCH 55/62] Removed useless comment --- pdlua.c | 1 - 1 file changed, 1 deletion(-) diff --git a/pdlua.c b/pdlua.c index 27d72e2..630d30b 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1221,7 +1221,6 @@ static int pdlua_object_createoutlets(lua_State *L) if (lua_isnumber(L, -1)) { int is_signal = lua_tonumber(L, -1); o->sigoutlets += is_signal; - // Do something with the value o->out[i] = outlet_new(&o->pd, is_signal ? &s_signal : 0); } lua_pop(L, 1); // Pop the value from the stack From 54150ea43f7f5f5172fd2db697234dd94d38fc17 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Tue, 27 Feb 2024 13:58:44 +0100 Subject: [PATCH 56/62] Fixed potential crash on shutdown, clean up inlets --- pdlua.c | 27 +++++++++++++++++++-------- pdlua.h | 3 ++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/pdlua.c b/pdlua.c index 630d30b..989651e 100644 --- a/pdlua.c +++ b/pdlua.c @@ -672,8 +672,8 @@ static void pdlua_delete(t_gobj *z, t_glist *glist){ } if(glist_isvisible(glist) && gobj_shouldvis(z, glist)) { pdlua_vis(z, glist, 0); - canvas_deletelinesfor(glist, (t_text *)z); } + canvas_deletelinesfor(glist, (t_text *)z); } static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, @@ -1093,6 +1093,7 @@ static int pdlua_object_new(lua_State *L) o->inlets = 0; o->in = NULL; + o->proxy_in = NULL; o->outlets = 0; o->out = NULL; o->siginlets = 0; @@ -1153,16 +1154,18 @@ static int pdlua_object_createinlets(lua_State *L) if (lua_isnumber(L, 2)) { // If it's a number, it means the number of data inlets o->inlets = luaL_checknumber(L, 2); - o->in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); + o->proxy_in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); + o->in = malloc(o->inlets * sizeof(t_inlet*)); for (int i = 0; i < o->inlets; ++i) { - pdlua_proxyinlet_init(&o->in[i], o, i); - inlet_new(&o->pd, &o->in[i].pd, 0, 0); + pdlua_proxyinlet_init(&o->proxy_in[i], o, i); + o->in[i] = inlet_new(&o->pd, &o->proxy_in[i].pd, 0, 0); } } else if (lua_istable(L, 2)) { // If it's a table, it means a list of inlet types (data or signal) o->inlets = lua_rawlen(L, 2); - o->in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); + o->proxy_in = malloc(o->inlets * sizeof(t_pdlua_proxyinlet)); + o->in = malloc(o->inlets * sizeof(t_inlet*)); for (int i = 0; i < o->inlets; ++i) { lua_rawgeti(L, 2, i + 1); // Get element at index i+1 @@ -1170,8 +1173,8 @@ static int pdlua_object_createinlets(lua_State *L) int is_signal = lua_tonumber(L, -1); o->siginlets += is_signal; - pdlua_proxyinlet_init(&o->in[i], o, i); - inlet_new(&o->pd, &o->in[i].pd, is_signal ? &s_signal : 0, is_signal ? &s_signal : 0); + pdlua_proxyinlet_init(&o->proxy_in[i], o, i); + o->in[i] = inlet_new(&o->pd, &o->proxy_in[i].pd, is_signal ? &s_signal : 0, is_signal ? &s_signal : 0); } lua_pop(L, 1); // Pop the value from the stack } @@ -1430,7 +1433,15 @@ static int pdlua_object_free(lua_State *L) if (o) { - if (o->in) free(o->in); + if(o->in) + { + for (i = 0; i < o->inlets; ++i) inlet_free(o->in[i]); + free(o->in); + o->in = NULL; + } + + if (o->proxy_in) freebytes(o->proxy_in, sizeof(struct pdlua_proxyinlet) * o->inlets); + if(o->out) { for (i = 0; i < o->outlets; ++i) outlet_free(o->out[i]); diff --git a/pdlua.h b/pdlua.h index 658e99b..92c2342 100644 --- a/pdlua.h +++ b/pdlua.h @@ -57,7 +57,8 @@ typedef struct pdlua { t_object pd; /**< We are a Pd object. */ int inlets; /**< Number of inlets. */ - struct pdlua_proxyinlet *in; /**< The inlets themselves. */ + struct pdlua_proxyinlet *proxy_in; /**< The inlets themselves. */ + t_inlet **in; int outlets; /**< Number of outlets. */ t_outlet **out; /**< The outlets themselves. */ int siginlets; /**< Number of signal inlets. */ From a95236075c67b54bb763a2d2eb1207af28a8e5fe Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 28 Feb 2024 13:07:50 +0100 Subject: [PATCH 57/62] plugdata compilation fix --- pdlua.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pdlua.c b/pdlua.c index 989651e..4a6aadb 100644 --- a/pdlua.c +++ b/pdlua.c @@ -724,10 +724,8 @@ static int pdlua_click(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, in x->gfx.mouse_down = doit; return 1; } - - return text_widgetbehavior.w_clickfn(z, gl, xpos, ypos, shift, alt, dbl, doit); - #endif + return text_widgetbehavior.w_clickfn(z, gl, xpos, ypos, shift, alt, dbl, doit); } // The _reload method will tell the pdlua object to reload the original script that spawned it From f1dcfde45444f22bf2727d314ce07b6e6a48eb15 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 28 Feb 2024 13:08:45 +0100 Subject: [PATCH 58/62] Call fixlinesfor when resizing Fixed typo Whoops, fixed wrong variable name --- pdlua_gfx.h | 1 + 1 file changed, 1 insertion(+) diff --git a/pdlua_gfx.h b/pdlua_gfx.h index fa75897..5506a6a 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -668,6 +668,7 @@ static int set_size(lua_State* L) obj->gfx.width = luaL_checknumber(L, 1); obj->gfx.height = luaL_checknumber(L, 2); pdlua_gfx_repaint(obj, 0); + canvas_fixlinesfor(obj->canvas, (t_text*)obj); return 0; } From 649863ba19f227a39673ced7087fef9fffbefd5c Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Wed, 28 Feb 2024 17:03:07 +0100 Subject: [PATCH 59/62] Simplifications for plugdata drawing mechanism, fixed iolet position offset when zoomed in pd-vanilla --- pdlua.c | 1 - pdlua.h | 1 - pdlua_gfx.h | 12 ++++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pdlua.c b/pdlua.c index 4a6aadb..bc6c8b7 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1111,7 +1111,6 @@ static int pdlua_object_new(lua_State *L) #else // NULL until plugdata overrides them with something useful o->gfx.plugdata_draw_callback = NULL; - o->gfx.plugdata_callback_target = NULL; #endif lua_pushlightuserdata(L, o); diff --git a/pdlua.h b/pdlua.h index 92c2342..8cd721d 100644 --- a/pdlua.h +++ b/pdlua.h @@ -45,7 +45,6 @@ typedef struct _pdlua_gfx char current_color[8]; // Keep track of current color #else void(*plugdata_draw_callback)(void*, t_symbol*, int, t_atom*); // Callback to perform drawing in plugdata - void* plugdata_callback_target; // Pointer to plugdata object, the target for our draw callback #endif // Size variables diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 5506a6a..c7f8aa5 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -244,8 +244,8 @@ static int get_size(lua_State* L) // Wrapper around draw callback to plugdata static inline void plugdata_draw(t_pdlua* obj, t_symbol* sym, int argc, t_atom* argv) { - if(obj->gfx.plugdata_callback_target) { - obj->gfx.plugdata_draw_callback(obj->gfx.plugdata_callback_target, sym, argc, argv); + if(obj->gfx.plugdata_draw_callback) { + obj->gfx.plugdata_draw_callback(obj, sym, argc, argv); } } @@ -637,9 +637,11 @@ 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(glist_getcanvas(x->canvas), (t_object*)x, 0, x->gfx.object_tag, xpos, ypos, xpos + x->gfx.width, ypos + x->gfx.height); + glist_drawiofor(x->canvas, (t_object*)x, 0, x->gfx.object_tag, xpos, ypos, xpos + (x->gfx.width * scale), ypos + (x->gfx.height * scale)); } static const char* register_drawing(t_pdlua *object) @@ -710,10 +712,12 @@ static int end_paint(lua_State* L) { t_canvas *cnv = glist_getcanvas(obj->canvas); t_pdlua_gfx *gfx = &obj->gfx; + 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, ypos + gfx->height); + glist_drawiofor(glist_getcanvas(obj->canvas), (t_object*)obj, 1, gfx->object_tag, xpos, ypos, xpos + (obj->gfx.width * scale), ypos + (obj->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 From 812dadf2e9d20e1d26c909e110dc3771bb8e39ed Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 29 Feb 2024 00:31:11 +0100 Subject: [PATCH 60/62] Instead of the gui flag, toggle gui/non-gui based on paint() function's existance --- pd.lua | 7 +++---- pdlua.c | 8 ++------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/pd.lua b/pd.lua index a5a8b6b..ede95a4 100644 --- a/pd.lua +++ b/pd.lua @@ -341,12 +341,13 @@ function pd.Class:construct(sel, atoms) self._object = pd._create(self._class) self.inlets = 0 self.outlets = 0 - self.gui = 0 self._canvaspath = pd._canvaspath(self._object) .. "/" if self:initialize(sel, atoms) then pd._createinlets(self._object, self.inlets) pd._createoutlets(self._object, self.outlets) - pd._creategui(self._object, self.gui) + if type(self.paint) == "function" then + pd._creategui(self._object) + end self:postinitialize() return self else @@ -472,7 +473,6 @@ local lua = pd.Class:new():register("pdlua") -- global controls (the [pdlua] ob function lua:initialize(sel, atoms) self.inlets = 1 - self.gui = 0 self.outlets = 0 -- FIXME: might be nice to have errors go here? return true end @@ -488,7 +488,6 @@ function luax:initialize(sel, atoms) -- motivation: pd-list 2007-09-23 -- create a dummy object, which can still be clicked for help self.inlets = 0 self.outlets = 0 - self.gui = 0 self._scriptname = "" return true end diff --git a/pdlua.c b/pdlua.c index bc6c8b7..f0d9cdf 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1126,12 +1126,8 @@ static int pdlua_object_new(lua_State *L) static int pdlua_object_creategui(lua_State *L) { t_pdlua *o = lua_touserdata(L, 1); - o->has_gui = luaL_checknumber(L, 2); - if(o->has_gui) - { - gfx_initialize(o); - } - + o->has_gui = 1; + gfx_initialize(o); return 0; } From ff50436014c9ad287a71b10e07ff675bad2dff73 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 29 Feb 2024 00:43:15 +0100 Subject: [PATCH 61/62] Use ints instead of strings for color presets --- pd.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pd.lua b/pd.lua index ede95a4..e82c4be 100644 --- a/pd.lua +++ b/pd.lua @@ -511,5 +511,5 @@ end DATA = 0 SIGNAL = 1 -Colors = {background = "0", foreground = "1", outline = "2"} +Colors = {background = 0, foreground = 1, outline = 2} -- fin pd.lua From dfd6f2e44541ec95c192b2110c968fc95aa4f3d8 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Thu, 29 Feb 2024 12:56:26 +0100 Subject: [PATCH 62/62] Improved lua multi-instance support --- pdlua.c | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/pdlua.c b/pdlua.c index f0d9cdf..0202919 100644 --- a/pdlua.c +++ b/pdlua.c @@ -56,34 +56,51 @@ #include "pdlua_gfx.h" #ifdef PDINSTANCE -#define MAX_THREADS 2048 -lua_State* lua_threads[MAX_THREADS] = {0}; +typedef struct _lua_Instance { + void* pd_instance; + lua_State* state; + struct _lua_Instance* next; +} lua_Instance; -size_t hash_pointer(void *ptr) { - // Simple hashing function for pointers - size_t val = (size_t)ptr; - val = ((val >> 16) ^ val) * 0x45d9f3b; - val = ((val >> 16) ^ val) * 0x45d9f3b; - val = (val >> 16) ^ val; - return val % MAX_THREADS; -} +lua_Instance* lua_threads = NULL; lua_State* __L() { - size_t idx = hash_pointer(pd_this); - return lua_threads[idx]; + lua_Instance* iter = lua_threads; + while(iter) + { + if(iter->pd_instance == pd_this) + { + return iter->state; + } + iter = iter->next; + } + + return NULL; // should never happen } void initialise_lua_state() { - size_t idx = hash_pointer(pd_this); - if (!lua_threads[idx]) { - lua_threads[idx] = luaL_newstate(); + if(!lua_threads) + { + lua_threads = t_getbytes(sizeof(lua_Instance)); + lua_threads->pd_instance = pd_this; + lua_threads->state = luaL_newstate(); + lua_threads->next = NULL; + return; } - else { - lua_threads[idx] = lua_newthread(lua_threads[idx]); + + lua_Instance* iter = lua_threads; + while(iter->next) + { + iter = iter->next; } + + iter->next = t_getbytes(sizeof(lua_Instance)); + iter->next->pd_instance = pd_this; + iter->next->state = lua_newthread(lua_threads->state); + iter->next->next = NULL; } #else