summaryrefslogtreecommitdiff
path: root/main.lua
blob: 83f3fbf836f73cc264083dc0c5f151476408248c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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.setPointSize(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