-- bezier curve drawing functions for playdate lua -- these are based on de Casteljau's algorithm -- this site has a nice interactive demo to compare both types of curve: https://pomax.github.io/bezierinfo/#flattening -- Original code source: https://gist.github.com/jaames/5de95dd62d4522b86b8409f095b3925f -- draws a curve starting at x1,y1, ending at x3,y3, with x2,y2 being a control point that "pulls" the curve towards it -- steps is the number of line segments to use, lower is better for performance, higher makes your curve look smoother -- the playdate is kinda slow, so it's recommended to find a relatively low step number that looks passable enough! function drawQuadraticBezier(x1, y1, x2, y2, x3, y3, steps) steps = steps or 8 local d = 1 / steps local prevX = x1 local prevY = y1 local x, y for t = d, 1, d do x = (1 - t) ^ 2 * x1 + 2 * (1 - t) * t * x2 + t ^ 2 * x3 y = (1 - t) ^ 2 * y1 + 2 * (1 - t) * t * y2 + t ^ 2 * y3 playdate.graphics.drawLine(prevX, prevY, x, y) prevX = x prevY = y end end -- Create an array of lineSegments and return it, instead of drawing it function buildQuadraticBezier(x1, y1, x2, y2, x3, y3, steps) steps = steps or 8 local lineSegments = {} local d = 1 / steps local prevX = x1 local prevY = y1 local x, y for t = d, 1, d do x = (1 - t) ^ 2 * x1 + 2 * (1 - t) * t * x2 + t ^ 2 * x3 y = (1 - t) ^ 2 * y1 + 2 * (1 - t) * t * y2 + t ^ 2 * y3 local lineSegment = playdate.geometry.lineSegment.new(prevX, prevY, x, y) table.insert(lineSegments, lineSegment) prevX = x prevY = y end return lineSegments end -- draws a curve starting at x1,y1, ending at x4,y4, with x2,y2 and x3,y3 being a control point that "pulls" the curve towards them -- (nb: this is the kind of curve you make using the pen tool in vector drawing apps like Adobe Illustator!) -- steps is the number of line segments to use, lower is better for performance, higher makes your curve look smoother -- the playdate is kinda slow, so it's recommended to find a relatively low step number that looks passable enough! function drawCubicBezier(x1, y1, x2, y2, x3, y3, x4, y4, steps) steps = steps or 12 local d = 1 / steps local prevX = x1 local prevY = y1 local x, y for t = d, 1, d do x = (1 - t) ^ 3 * x1 + 3 * (1 - t) ^ 2 * t * x2 + 3 * (1 - t) * t ^ 2 * x3 + t ^ 3 * x4 y = (1 - t) ^ 3 * y1 + 3 * (1 - t) ^ 2 * t * y2 + 3 * (1 - t) * t ^ 2 * y3 + t ^ 3 * y4 playdate.graphics.drawLine(prevX, prevY, x, y) prevX = x prevY = y end end -- example usage: function playdate.update() -- curves are just drawn as a bunch of lines, so you can tweak line settings like width, cap, color, etc gfx.setLineWidth(2) drawQuadraticBezier( 10, 80, -- curve start x,y coordinate 200, 50, -- control x,y coordinate - your curve will be "pulled" towards this point 390, 80, -- curve end x,y coordinate 8 -- number of line segments used to draw the curve, 8 is probably plenty to get a smooth curve ) drawCubicBezier( 10, 160, -- curve start x,y coordinate 100, 110, -- first control x,y coordinate 300, 210, -- seccond control x,y coordinate 390, 160, -- curve end x,y coordinate 12 -- number of line segments used to draw the curve ) end