Inline main.lua:
Code: Select all
--[[
Fast 2D point-in-polygon test. This uses the "crossings count" (AKA raycast, even-odd) rule.
Lua/LÖVE port by Rafael Navega (2020).
License:
Public Domain.
]]
io.stdout:setvbuf('no')
local testPoly
local renderPolys
local inside = false
function createPickablePolygon(points)
-- Takes in a table with a sequence of ints for the (x, y) of each point of the polygon.
-- Example: {x1, y1, x2, y2, x3, y3, ...}
-- Note: no need to repeat the first point at the end of the table, the testing function
-- already handles that.
local poly = { }
local lastX = points[#points-1]
local lastY = points[#points]
for index = 1, #points-1, 2 do
local px = points[index]
local py = points[index+1]
-- Only store non-horizontal edges.
if py ~= lastY then
local index = #poly
poly[index+1] = px
poly[index+2] = py
poly[index+3] = (lastX - px) / (lastY - py)
end
lastX = px
lastY = py
end
return poly
end
function isPointInPolygon(x, y, poly)
-- Takes in the x and y of the point in question, and a 'poly' table created by
-- createPickablePolygon(). Returns true if the point is within the polygon, otherwise false.
-- Note: the coordinates of the test point and the polygon points are all assumed to be in
-- the same space.
-- Original algorithm by W. Randolph Franklin (WRF):
-- https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
local lastPX = poly[#poly-2]
local lastPY = poly[#poly-1]
local inside = false
for index = 1, #poly, 3 do
local px = poly[index]
local py = poly[index+1]
local deltaX_div_deltaY = poly[index+2]
-- 'deltaX_div_deltaY' is a precomputed optimization. The original line is:
-- if ((py > y) ~= (lastPY > y)) and (x < (y - py) * (lastX - px) / (lastY - py) + px) then
if ((py > y) ~= (lastPY > y)) and (x < (y - py) * deltaX_div_deltaY + px) then
inside = not inside
end
lastPX = px
lastPY = py
end
return inside
end
function love.load()
love.window.setMode(620, 439)
-- List of points taken from a flat mesh made in Blender.
local points = {
10, 10,
31, 128,
124, 170,
10, 350,
149, 174,
149, 398,
150, 398,
150, 174,
237, 249,
237, 399,
307, 399,
307, 360,
378, 360,
378, 399,
520, 399,
520, 219,
399, 219,
610, 50,
399, 199,
399, 13,
313, 81,
295, 10
}
-- The script to print out that list of points is this, to be run in the Console view (just
-- make sure the mesh is filled with an Ngon face first):
--[[
import bmesh
bm = bmesh.from_edit_mesh(C.object.data)
v = next(v for v in bm.verts if v.select)
finalLoop = v.link_loops[0].link_loop_prev
l = v.link_loops[0]
while l != finalLoop:
print(('%i, %i' % l.vert.co[:2]).replace('-', ''))
l = l.link_loop_next
]]
testPoly = createPickablePolygon(points)
renderPolys = love.math.triangulate(unpack(points))
end
local DRAW_TRANSLATE_Y = 20
function love.mousemoved(x, y)
inside = isPointInPolygon(x, y - DRAW_TRANSLATE_Y, testPoly)
end
function love.draw()
love.graphics.print('(Hover the polygon with the mouse cursor)', 10, 10)
love.graphics.translate(0, DRAW_TRANSLATE_Y)
if inside then
love.graphics.setColor(0.4, 0.65, 1.0)
else
love.graphics.setColor(0.2, 0.2, 0.6)
end
for index = 1, #renderPolys do
love.graphics.polygon('fill', renderPolys[index])
end
love.graphics.setColor(1.0, 1.0, 1.0)
end
function love.keypressed(key)
if key == 'escape' then love.event.quit() end
end