Skip to content
Open
209 changes: 178 additions & 31 deletions L5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,12 @@ function defaults()
SIZEALL = "sizeall"
NO = "no"
HAND = "hand"
-- beginShape kinds
POINTS = "points"
LINES = "lines"
TRIANGLES = "triangles"
TRIANGLE_FAN = "fan"
TRIANGLE_STRIP = "strip"

-- global user vars - can be read by user but shouldn't be altered by user
key = "" --default, overriden with key presses detected in love.update(dt)
Expand Down Expand Up @@ -893,6 +899,12 @@ function define_env_globals()
L5_env.pixelsLoaded = false
-- custom shape drawing
L5_env.vertices = {}
L5_env.kind = nil
L5_env.shapeKinds = {[POINTS] = true, [LINES] = true, [TRIANGLES]=true, [TRIANGLE_FAN]=true, [TRIANGLE_STRIP]=true}
L5_env.mesh = love.graphics.newMesh(
{{"VertexPosition", "float", 2}},
4096, "triangles", "dynamic"
) -- reusable mesh for non-texture shapes
-- custom texture mesh
L5_env.currentTexture = nil
L5_env.useTexture = false
Expand Down Expand Up @@ -2301,10 +2313,27 @@ function textureWrap(_mode)
end
end

function beginShape()
function beginShape(...)

local n = select('#' , ...)
if(n > 1) then
error("beginShape(kind) accepts at most one argument", 2)
end

local _kind = select(1, ...)

if n == 0 then
_kind = nil
elseif _kind == nil then
error("This kind is not defined (undefined variable passed)")
elseif not L5_env.shapeKinds[_kind] then -- if any other type is passed
error("Invalid kind: " .. tostring(_kind))
end

-- reset custom shape vertices table
L5_env.vertices = {}
L5_env.useTexture = false
L5_env.kind = _kind
end

function vertex(_x, _y, _u, _v)
Expand All @@ -2328,43 +2357,161 @@ function endShape(_close)
-- no vertices, early exit
if #L5_env.vertices == 0 then return end

-- helper function to convert flat {x,y,x,y...} to {{x,y},{x,y}...}
local function toVertTable(verts)
if type(verts[1]) == "number" then
local converted = {}
for i = 1, #verts, 2 do
converted[#converted+1] = {verts[i], verts[i+1]}
end
return converted
end
return verts
end

-- draw points
if L5_env.kind == POINTS then
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
for i = 1, #L5_env.vertices, 2 do
love.graphics.points(L5_env.vertices[i], L5_env.vertices[i+1])
end
love.graphics.setColor(r, g, b, a)

-- draw unconnected lines
elseif L5_env.kind == LINES then
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
for i = 1, #L5_env.vertices - 2, 4 do
love.graphics.line(
L5_env.vertices[i], L5_env.vertices[i+1],
L5_env.vertices[i+2], L5_env.vertices[i+3]
)
end
love.graphics.setColor(r, g, b, a)

-- draw separated triangles
elseif L5_env.kind == TRIANGLES then
local verts = toVertTable(L5_env.vertices)
if L5_env.useTexture and L5_env.currentTexture then
local mesh = love.graphics.newMesh(verts, TRIANGLES)
mesh:setTexture(L5_env.currentTexture)
L5_env.currentTexture:setWrap(L5_env.textureWrap, L5_env.textureWrap)
love.graphics.draw(mesh)
else
if L5_env.fill_mode == "fill" then
L5_env.mesh:setVertices(verts, 1, #verts)
L5_env.mesh:setDrawMode("triangles")
L5_env.mesh:setDrawRange(1, #verts)
love.graphics.draw(L5_env.mesh)
end
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
for i = 1, #verts, 3 do
local v1, v2, v3 = verts[i], verts[i+1], verts[i+2]
if v1 == nil or v2 == nil or v3 == nil then break end
love.graphics.line(v1[1],v1[2], v2[1],v2[2])
love.graphics.line(v2[1],v2[2], v3[1],v3[2])
love.graphics.line(v3[1],v3[2], v1[1],v1[2])
end
love.graphics.setColor(r, g, b, a)
end

-- draw triangle strip
elseif L5_env.kind == TRIANGLE_STRIP then
local verts = toVertTable(L5_env.vertices)
if L5_env.useTexture and L5_env.currentTexture then
local mesh = love.graphics.newMesh(verts, TRIANGLE_STRIP)
mesh:setTexture(L5_env.currentTexture)
L5_env.currentTexture:setWrap(L5_env.textureWrap, L5_env.textureWrap)
love.graphics.draw(mesh)
else
if L5_env.fill_mode == "fill" then
L5_env.mesh:setVertices(verts, 1, #verts)
L5_env.mesh:setDrawMode("strip")
L5_env.mesh:setDrawRange(1, #verts)
love.graphics.draw(L5_env.mesh)
end
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
for i = 1, #verts-2 do
local v1, v2, v3 = verts[i], verts[i+1], verts[i+2]
if v1 == nil or v2 == nil or v3 == nil then break end
love.graphics.line(v1[1],v1[2], v2[1],v2[2])
love.graphics.line(v2[1],v2[2], v3[1],v3[2])
love.graphics.line(v3[1],v3[2], v1[1],v1[2])
end
love.graphics.setColor(r, g, b, a)
end

-- draw triangles centered around the first vertex
elseif L5_env.kind == TRIANGLE_FAN then
local verts = toVertTable(L5_env.vertices)
if L5_env.useTexture and L5_env.currentTexture then
local mesh = love.graphics.newMesh(verts, TRIANGLE_FAN)
mesh:setTexture(L5_env.currentTexture)
L5_env.currentTexture:setWrap(L5_env.textureWrap, L5_env.textureWrap)
love.graphics.draw(mesh)
else
if L5_env.fill_mode == "fill" then
L5_env.mesh:setVertices(verts, 1, #verts)
L5_env.mesh:setDrawMode("fan")
L5_env.mesh:setDrawRange(1, #verts)
love.graphics.draw(L5_env.mesh)
end
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
for i = 2, #verts-1 do
local v1, v2, v3 = verts[1], verts[i], verts[i+1]
if v1 == nil or v2 == nil or v3 == nil then break end
love.graphics.line(v1[1],v1[2], v2[1],v2[2])
love.graphics.line(v2[1],v2[2], v3[1],v3[2])
love.graphics.line(v3[1],v3[2], v1[1],v1[2])
end
love.graphics.setColor(r, g, b, a)
end

-- polygon fallback (kind == nil)
-- if texture() triangulate fan mesh - convex assumed
if L5_env.useTexture and L5_env.currentTexture then
local mesh = love.graphics.newMesh(L5_env.vertices, "fan")
mesh:setTexture(L5_env.currentTexture)
L5_env.currentTexture:setWrap(L5_env.textureWrap, L5_env.textureWrap)
love.graphics.draw(mesh)
else
-- triangulate handles concave shapes but errors on self-intersecting polygons
if L5_env.fill_mode == "fill" then
local ok, triangles = pcall(love.math.triangulate, L5_env.vertices)
if ok then
local meshVerts = {}
for _, tri in ipairs(triangles) do
for i = 1, 6, 2 do
meshVerts[#meshVerts+1] = {tri[i], tri[i+1]}
if L5_env.useTexture and L5_env.currentTexture then
local mesh = love.graphics.newMesh(L5_env.vertices, "fan")
mesh:setTexture(L5_env.currentTexture)
L5_env.currentTexture:setWrap(L5_env.textureWrap, L5_env.textureWrap)
love.graphics.draw(mesh)
else
-- triangulate handles concave shapes but errors on self-intersecting polygons
if L5_env.fill_mode == "fill" then
local ok, triangles = pcall(love.math.triangulate, L5_env.vertices)
if ok then
local meshVerts = {}
for _, tri in ipairs(triangles) do
for i = 1, 6, 2 do
meshVerts[#meshVerts+1] = {tri[i], tri[i+1]}
end
end
L5_env.mesh:setVertices(meshVerts, 1, #meshVerts)
L5_env.mesh:setDrawMode("triangles")
L5_env.mesh:setDrawRange(1, #meshVerts)
love.graphics.draw(L5_env.mesh)
else
love.graphics.polygon("fill", L5_env.vertices)
end
local mesh = love.graphics.newMesh(meshVerts, "triangles")
love.graphics.draw(mesh)
end
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
if _close == CLOSE then
local verts = L5_env.vertices
-- draw all segments
love.graphics.line(verts[1], verts[2], unpack(verts, 3, #verts))
-- close by drawing back to start
love.graphics.line(verts[#verts-1], verts[#verts], verts[1], verts[2])
else
love.graphics.polygon("fill", L5_env.vertices)
-- continuous lines, doesn't close the last two segments aka OPEN
love.graphics.line(L5_env.vertices)
end
love.graphics.setColor(r, g, b, a)
end
local r, g, b, a = love.graphics.getColor()
love.graphics.setColor(unpack(L5_env.stroke_color))
if _close == CLOSE then
local verts = L5_env.vertices
-- draw all segments
love.graphics.line(verts[1], verts[2],
unpack(verts, 3, #verts))
-- close by drawing back to start
love.graphics.line(verts[#verts-1], verts[#verts], verts[1], verts[2])
else
-- continuous lines, doesn't close the last two segments aka OPEN
love.graphics.line(L5_env.vertices)
end
love.graphics.setColor(r, g, b, a)
end
end

Expand Down
2 changes: 1 addition & 1 deletion TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
* [X] ellipseMode doesn't seem to work?
* [X] imageMode(): add CORNERS mode
* [X] add custom shape creation via createShape() (processing) / beginShape() (p5.js), vertex() and endShape() (using love2d polygon - pass vertices as table?)
* [ ] improve beginShape to get feature parity with p5-processing with `kind`? at the very least, basic shape drawing should match basic examples on reference page.
* [ ] Add QUADS, QUAD_STRIP, TESS support for kind parameter in beginShape

## Filter

Expand Down
14 changes: 14 additions & 0 deletions examples/beginShapeLines.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require("L5")

function setup()
size(400, 400)
windowTitle("beginShape(LINES) example")

fill(0)
beginShape(LINES)
for i=0,10 do
vertex(random(width),random(height))
end
endShape()
describe("custom shape with beginShape(LINES) function, vertices and endShape()")
end
14 changes: 14 additions & 0 deletions examples/beginShapePoints.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require("L5")

function setup()
size(400, 400)
windowTitle("beginShape(POINTS) example")

fill(0)
beginShape(POINTS)
for i=0,10 do
vertex(random(width),random(height))
end
endShape()
describe("custom shape with beginShape(POINTS) function, vertices and endShape()")
end
24 changes: 24 additions & 0 deletions examples/beginShapeTriangleFan.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require("L5")

function setup()
size(400, 400)
windowTitle("beginShape(TRIANGLE_FAN) example")

fill(150, 200, 255)
stroke(51)
strokeWeight(5)

beginShape(TRIANGLE_FAN)
vertex(200, 200) -- center
vertex(200, 50) -- top
vertex(320, 110)
vertex(370, 250)
vertex(280, 370)
vertex(120, 370)
vertex(30, 250)
vertex(80, 110)
vertex(200, 50)
endShape()

describe("octagon drawn with beginShape(TRIANGLE_FAN) from a center point, with stroke")
end
23 changes: 23 additions & 0 deletions examples/beginShapeTriangleStrip.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require("L5")

function setup()
size(400, 400)
windowTitle("beginShape(TRIANGLE_STRIP) example")

fill(255, 180, 100)
stroke(51)
strokeWeight(2)

beginShape(TRIANGLE_STRIP)
vertex(50, 100)
vertex(50, 300)
vertex(150, 50)
vertex(150, 300)
vertex(250, 100)
vertex(250, 300)
vertex(350, 50)
vertex(350, 300)
endShape()

describe("ribbon of triangles drawn with beginShape(TRIANGLE_STRIP), with stroke")
end
23 changes: 23 additions & 0 deletions examples/beginShapeTriangles.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require("L5")

function setup()
size(400, 400)
windowTitle("beginShape(TRIANGLES) example")

fill(0)
beginShape(TRIANGLES);

-- Left triangle
vertex(30, 75);
vertex(40, 20);
vertex(50, 75);

-- Right triangle
vertex(60, 20);
vertex(70, 75);
vertex(80, 20);

endShape();

describe("custom shape with beginShape(TRIANGLES) function, vertices and endShape()")
end
23 changes: 23 additions & 0 deletions examples/beginShapeTrianglsWithTexture.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require("L5")

function setup()
size(400, 400)
windowTitle("texture() example")
textureMode(NORMAL)

cat = loadImage("examples/assets/cat.png")

beginShape(TRIANGLES)
texture(cat)
vertex(50, 100, 0, 0.2)
vertex(50, 300, 0, 1)
vertex(150, 50, 0.33, 0)
vertex(150, 300, 0.33, 1)
vertex(250, 100, 0.67, 0.2)
vertex(250, 300, 0.67, 1)
vertex(350, 50, 1, 0)
vertex(350, 300, 1, 1)
endShape()

describe("Wrapping a mesh texture around a custom shape with coordinates specified with u,v")
end