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