diff --git a/doc/graphics.txt b/doc/graphics.txt index 37e5066..56d0dbe 100644 --- a/doc/graphics.txt +++ b/doc/graphics.txt @@ -28,47 +28,53 @@ end API overview -------------- --- Callback functions you can define -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 +-- Callback functions you can define +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 -pd:Class:paint(g) -- Paint callback, returns a graphics_context object (commonly called g) that you can call these drawing functions on: -g:set_size(w, h) -- Sets the size of the object. -width, height = g:get_size(w, h) -- Gets the size of the object. +pd:Class:repaint() -- Request a repaint, after this the "paint" callback will occur +pd:Class:paint(g) -- Paint callback, returns a graphics_context object (commonly called g) that you can call these drawing functions on: + +g:set_size(w, h) -- Sets the size of the object +width, height = g:get_size(w, h) -- Gets the size of the object + +g:set_color(r, g, b, a=1.0) -- Sets the color for the next drawing operation + +g:fill_ellipse(x, y, w, h) -- Draws a filled ellipse at the specified position and size +g:stroke_ellipse(x, y, w, h, line_width) -- Draws the outline of an ellipse at the specified position and size -g:set_color(r, g, b, a=1.0) -- Sets the color for the next drawing operation. +g:fill_rect(x, y, w, h) -- Draws a filled rectangle at the specified position and size +g:stroke_rect(x, y, w, h, line_width) -- Draws the outline of a rectangle at the specified position and size -g:fill_ellipse(x, y, w, h) -- Draws a filled ellipse at the specified position and size. -g:stroke_ellipse(x, y, w, h, line_width) -- Draws the outline of an ellipse at the specified position and size. +g:fill_rounded_rect(x, y, w, h, corner_radius) -- Draws a filled rounded rectangle at the specified position and size +g:stroke_rounded_rect(x, y, w, h, corner_radius, line_width) -- Draws the outline of a rounded rectangle at the specified position and size -g:fill_rect(x, y, w, h) -- Draws a filled rectangle at the specified position and size. -g:stroke_rect(x, y, w, h, line_width) -- Draws the outline of a rectangle at the specified position and size. +g:draw_line(x1, y1, x2, y2) -- Draws a line between two points +g:draw_text(text, x, y, w, fontsize) -- Draws text at the specified position and size -g:fill_rounded_rect(x, y, w, h, corner_radius) -- Draws a filled rounded rectangle at the specified position and size. -g:stroke_rounded_rect(x, y, w, h, corner_radius, line_width) -- Draws the outline of a rounded rectangle at the specified position and size. +g: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) -g:draw_line(x1, y1, x2, y2) -- Draws a line between two points. -g:draw_text(text, x, y, w, fontsize) -- Draws text at the specified position and size. +g:translate(tx, ty) -- Translates the coordinate system by the specified amounts +g:scale(sx, sy) -- Scales the coordinate system by the specified factors. This will always happen after the translation +g:reset_transform() -- Resets current scale and translation -g: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) +p = Path(x, y) -- Initiates a new path at the specified point +p:line_to(x, y) -- Adds a line segment to the path +p:quad_to(x1, y1, x2, y2) -- Adds a quadratic Bezier curve to the path +p:cubic_to(x1, y1, x2, y2, x3, y) -- Adds a cubic Bezier curve to the path +p:close_path() -- Closes the path -g:translate(tx, ty) -- Translates the coordinate system by the specified amounts. -g:scale(sx, sy) -- Scales the coordinate system by the specified factors. This will always happen after the translation -g:reset_transform() -- Resets current scale and translation +g:stroke_path(p, line_width) -- Draws the outline of the path with the specified line width +g:fill_path(p) -- Fills the current path -p = Path(x, y) -- Initiates a new path at the specified point -p:line_to(x, y) -- Adds a line segment to the path. -p:quad_to(x1, y1, x2, y2) -- Adds a quadratic Bezier curve to the path. -p:cubic_to(x1, y1, x2, y2, x3, y) -- Adds a cubic Bezier curve to the path. -p:close_path() -- Closes the path. +-- Additional functions +expandedsymbol = pd:Class:canvas_realizedollar(s) -- Expand dollar symbols in patch canvas context +pd:Class:set_args(args) -- Set the object arguments to be saved in the patch file -g:stroke_path(p, line_width) -- Draws the outline of the path with the specified line width. -g:fill_path(p) -- Fills the current path. Basic example --------------------- diff --git a/tutorial/pd-lua-intro.md b/tutorial/pd-lua-intro.md index 0d2ef78..e9c405d 100644 --- a/tutorial/pd-lua-intro.md +++ b/tutorial/pd-lua-intro.md @@ -718,7 +718,7 @@ Sending messages to a receiver is straightforward: This works pretty much like the `outlet` method, but outputs messages to the given receiver instead. For instance, let's say you have a toggle with receiver symbol `onoff` in your patch, then you can turn on that toggle with a call like `pd.send("onoff", "float", {1})`. (Recall that the `atoms` argument always needs to be a table, even if it is a singleton, lest you'll get that "invalid atoms table" error that we discussed earlier). -One complication are receiver symbols using a `$0-` patch id prefix, which are commonly used to differentiate receiver symbols in different toplevel patches or abstractions, in order to prevent name clashes. A Pd-Lua object doesn't have any idea of what toplevel patch it is located in, and what the numeric id of that patch is, so you'll have to expand the `$0-` prefix on the Pd side and pass it, e.g., as a creation argument. For instance, suppose that the toggle receiver is in fact named `$0-onoff`, then something like the following Pd-Lua object will do the trick, if you invoke it as `luasend $0-onoff`: +One complication are receiver symbols using a `$0-` patch id prefix, which are commonly used to differentiate receiver symbols in different toplevel patches or abstractions, in order to prevent name clashes. For instance, suppose that the toggle receiver is in fact named `$0-onoff`, then something like the following Pd-Lua object will do the trick, if you invoke it as `luasend $0-onoff` (you'll also find an explanation further below on how to manage this expansion for symbols from incoming messages): ~~~lua local luasend = pd.Class:new():register("luasend") @@ -1231,6 +1231,40 @@ I'm sure you can imagine many more creative uses for this simple but surprisingl The extended example adds messages for resizing the object and setting colors, and also shows how to save and restore object state in the creation arguments using the `set_args()` method mentioned at the beginning of this section. The accompanying patch covers all the examples we discussed here, and adds a third example showing how to utilize our dial object as a dB meter. +### Expanding dollar symbols + +As mentioned above, the patch id `$0` is widely used in Pd for send and receive names to avoid conflicts with other receivers and senders of similar names. Pd expands `$0` to its local id, which differs for every open patch (similarly, it expands `$1`, `$2` etc. if the corresponding creation arguments are set in the context of an abstraction or clone instance). + +If you set these sender or receiver names for your Pd-Lua object as creation arguments, they will automatically get expanded as demonstrated before. If you want to set them through messages in Lua however (which is common for Pd's GUI objects like sliders, radio buttons, etc.), it becomes slightly more complicated since you will need to expand them yourself. + +Luckily, Pd's `canvas_realizedollar()` method does exactly this and is also available on the Lua side. Combined with the `set_args()` method, you can create objects that allow managing sender and receiver names, applying the expanded version immediately and also storing the original names in the arguments. Here's a simple example illustrating this: + +~~~lua +local localsend = pd.Class:new():register("localsend") + +function localsend:initialize(sel, atoms) + self.inlets = 1 + -- pass the symbol from the creation argument, + -- which gets automatically expanded here + self.sender = tostring(atoms[1]) + return true +end + +function localsend:in_1_sender(x) + local sendername = tostring(x[1]) + + -- store the original name as argument (like "\$0-foo") + self:set_args(sendername) + + -- apply the expanded name with the local id + self.sender = self:canvas_realizedollar(sendername) +end + +function localsend:in_1_bang() + pd.send(self.sender, "float", {1}) +end +~~~ + ## Live coding I've been telling you all along that in order to make Pd-Lua pick up changes you made to your .pd_lua files, you have to relaunch Pd and reload your patches. Well, as you probably guessed by now, this isn't actually true. So in this section we are going to cover Pd-Lua's *live coding* features, which let you modify your sources and have Pd-Lua reload them on the fly, without ever exiting the Pd environment. This rapid incremental style of development is one of the hallmark features of dynamic interactive programming environments like Pd and Lua. Musicians also like to employ it to modify their programs live on stage, which is where the term "live coding" comes from.