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
|