summaryrefslogtreecommitdiff
path: root/main.lua
diff options
context:
space:
mode:
authorLizzy Fleckenstein <lizzy@vlhl.dev>2026-06-22 22:55:22 +0200
committerLizzy Fleckenstein <lizzy@vlhl.dev>2026-06-22 22:55:22 +0200
commit0d739394fb36a10257751cd48874a42eb620f7a3 (patch)
tree7d10e5a291e47c924867da0fec34aff48af72522 /main.lua
downloadlove-quadtree-0d739394fb36a10257751cd48874a42eb620f7a3.tar.xz
init
Diffstat (limited to 'main.lua')
-rw-r--r--main.lua228
1 files changed, 228 insertions, 0 deletions
diff --git a/main.lua b/main.lua
new file mode 100644
index 0000000..267943b
--- /dev/null
+++ b/main.lua
@@ -0,0 +1,228 @@
+local quadtree = require("quadtree")
+local ok, bit = pcall(require, "bit")
+if not ok then
+ ok, bit = pcall(require, "bit32")
+ if not ok then error("no bit library") end
+end
+
+local state
+
+function love.load()
+ love.graphics.setBackgroundColor(1, 1, 1)
+ love.window.setTitle("Quadtree")
+ local w, h = love.window.getMode()
+ love.window.setMode(w, h, { resizable = true })
+
+ state = {}
+ state.map = quadtree.new(1, 1, 0.01)
+ state.pos = { x = state.map.width/2, y = state.map.height/2 }
+ state.zoom = 25
+ state.poly = {}
+ state.draft = {}
+ state.mode = "move"
+ local font = love.graphics.newFont(20)
+ state.text = {
+ move = love.graphics.newText(font, "Mode: Move\n\nControls:\nDrag: Move map"),
+ polygon = love.graphics.newText(font, "Mode: Polygon\n\nControls:\nClick: Add vertex\nP: Finish polygon\n"),
+ debug = love.graphics.newText(font, "Mode: Debug\n\nControls:\nClick: Print debug info about node"),
+ general = love.graphics.newText(font,
+[[
+M: Switch to Move Mode
+P: Switch to Polygon Mode
+D: Switch to Debug Mode
+Q: Quit
+Mouse Wheel: Zoom map
+]]
+ )
+ }
+ return ed
+end
+
+local function proj(x, y)
+ local w, h = love.graphics:getDimensions()
+ return w/2+(x-state.pos.x)*state.zoom, h/2-(y-state.pos.y)*state.zoom
+end
+
+local function unproj(x, y)
+ local w, h = love.graphics:getDimensions()
+ return state.pos.x+(x-w/2)/state.zoom, state.pos.y+(-y+h/2)/state.zoom
+end
+
+local function add_to_map(poly)
+ quadtree.insert(state.map, poly, 0.8)
+end
+
+local function poly_bounds(poly)
+ local x1, y1, x2, y2
+ for _, p in ipairs(poly) do
+ x1 = math.min(x1 or p.x, p.x)
+ x2 = math.max(x2 or p.x, p.x)
+ y1 = math.min(y1 or p.y, p.y)
+ y2 = math.max(y2 or p.y, p.y)
+ end
+ return x1, y1, x2, y2
+end
+
+local function shift_poly(poly, x, y)
+ local new = {}
+ for _, p in ipairs(poly) do
+ table.insert(new, { x = p.x + x, y = p.y + y})
+ end
+ return new
+end
+
+local function grow_map(poly)
+ local x1, y1, x2, y2 = poly_bounds(poly)
+ local x1, y1, x2, y2 = x1-2, y1-2, x2+2, y2+2
+ local w, h = state.map.width, state.map.height
+ if x1 < 0 or y1 < 0 then
+ local ox, oy = -math.min(0, x1), -math.min(0, y1)
+ x2, y2 = x2 + ox, y2 + oy
+ w, h = w + ox, h + oy
+ state.pos.x, state.pos.y = state.pos.x+ox, state.pos.y+oy
+ poly = shift_poly(poly, ox, oy)
+ for i, p in ipairs(state.poly) do
+ state.poly[i] = shift_poly(p, ox, oy)
+ end
+ end
+ w, h = math.max(w, x2), math.max(h, y2)
+ if w ~= state.map.width or h ~= state.map.height then
+ state.map = quadtree.new(w, h, 0.02)
+ for _, p in ipairs(state.poly) do
+ add_to_map(p)
+ end
+ end
+ return poly
+end
+
+local function add_poly(poly)
+ if not quadtree.poly_simple(poly) then
+ return
+ end
+
+ poly = grow_map(poly)
+
+ for _, tri in ipairs(quadtree.triangulate(poly)) do
+ add_to_map(tri)
+ table.insert(state.poly, tri)
+ end
+end
+
+local function bin(x)
+ local s = ""
+ for i = 1, 16 do
+ s = bit.band(x, 1) .. s
+ x = bit.rshift(x, 1)
+ end
+ return s
+end
+
+function love.mousepressed(x, y, button)
+ local x, y = unproj(x, y)
+ if button ~= 1 then
+ return
+ end
+ if state.mode == "polygon" then
+ table.insert(state.draft, { x = x, y = y })
+ elseif state.mode == "debug" then
+ print("---")
+ for _, d in ipairs(quadtree.debug) do
+ if x >= d.p1[1] and x < d.p2[1] and y >= d.p1[2] and y < d.p2[2] then
+ print(d.msg, "x="..bin(d.bits[1]), "y="..bin(d.bits[2]), "g="..bin(d.g))
+ end
+ end
+ end
+end
+
+function love.mousemoved(x, y, dx, dy, istouch)
+ if state.mode == "move" then
+ if love.mouse.isDown(1) then
+ state.pos.x = state.pos.x - dx/state.zoom
+ state.pos.y = state.pos.y + dy/state.zoom
+ end
+ end
+end
+
+function love.wheelmoved(x, y)
+ local cx, cy = love.mouse.getPosition()
+ local ox, oy = unproj(cx, cy)
+
+ local factor = y < 0 and 0.9 or 1.1
+ state.zoom = state.zoom * factor
+
+ local nx, ny = unproj(cx, cy)
+ state.pos.x = state.pos.x+ox-nx
+ state.pos.y = state.pos.y+oy-ny
+end
+
+function love.keypressed(key)
+ if key == "p" then
+ if state.mode == "polygon" then
+ if #state.draft >= 3 then
+ add_poly(state.draft)
+ end
+ state.draft = {}
+ state.mode = "move"
+ else
+ state.mode = "polygon"
+ end
+ elseif key == "d" then
+ state.mode = "debug"
+ elseif key == "m" then
+ state.mode = "move"
+ elseif key == "q" then
+ love.event.quit()
+ end
+end
+
+local function draw_node(node, w, h, x, y)
+ if node == true then
+ love.graphics.setColor(0.7, 0.7, 0.7)
+ love.graphics.rectangle("fill", x, y, w, h)
+ elseif node then
+ local w, h = w/2, h/2
+ draw_node(node[1], w, h, x, y)
+ draw_node(node[2], w, h, x, y+h)
+ draw_node(node[3], w, h, x+w, y)
+ draw_node(node[4], w, h, x+w, y+h)
+ return
+ end
+
+ love.graphics.setColor(0, 0, 0)
+ love.graphics.rectangle("line", x, y, w, h)
+end
+
+local function make_verts(poly)
+ local verts = {}
+ for _, p in ipairs(poly) do
+ local x, y = proj(p.x, p.y)
+ table.insert(verts, x)
+ table.insert(verts, y)
+ end
+ return verts
+end
+
+function love.draw()
+ love.graphics.setLineWidth(1)
+ draw_node(state.map.root, state.map.width*state.zoom, -state.map.height*state.zoom, proj(0, 0))
+
+ love.graphics.setColor(0, 0, 1)
+ love.graphics.setLineWidth(3)
+ for _, pl in ipairs(state.poly) do
+ love.graphics.polygon("line", make_verts(pl))
+ end
+
+ love.graphics.setColor(0, 1, 1)
+ if #state.draft == 1 then
+ -- love.graphics.setPointWidth(3)
+ love.graphics.points(proj(state.draft[1].x, state.draft[1].y))
+ elseif #state.draft >= 2 then
+ love.graphics.line(make_verts(state.draft))
+ end
+
+ love.graphics.setColor(0, 0, 0)
+ love.graphics.draw(state.text[state.mode])
+ love.graphics.draw(state.text.general, 0, state.text[state.mode]:getHeight())
+end
+
+return editor_new