This repository has been archived by the owner on Mar 25, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.lua
349 lines (310 loc) · 7.81 KB
/
main.lua
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
love.filesystem.setRequirePath(love.filesystem.getRequirePath() .. ";libs/?.lua;libs/?/init.lua")
love.filesystem.setRequirePath(love.filesystem.getRequirePath() .. ";src/?.lua;src/?/init.lua")
local cpml = require "cpml"
local node = require "node"
local serial = require "serial_nood"
local editgrid = require "editgrid"
local node_list = {}
local selected = false
local grabbed = false
local new_connection = false
local offset, zoom, panning = cpml.vec2(0, 0), 1, false
local lf, menu
function love.load(args)
love.window.setMode(1280, 720, {
fullscreen = false,
msaa = 4,
srgb = true,
resizable = true,
vsync = true,
})
love.window.setTitle("Noodler")
-- Depends on the window being created first.
lf = require "libs.UnekFrames"
love.graphics.setFont(love.graphics.newFont("assets/NotoSans-Regular.ttf", 12))
love.graphics.setBackgroundColor { 40, 40, 40 }
local items = {}
for i, file in ipairs(love.filesystem.getDirectoryItems("src/nodes")) do
if file:gmatch("%.lua()")() == string.len(file)+1 then
xpcall(function()
local item = love.filesystem.load("src/nodes/" .. file)(string.sub(file, 1, -5))
items[item.category] = items[item.category] or {}
table.insert(items[item.category], item)
end, print)
end
end
local panel = assert(lf.Create("panel"))
function panel:draw()
for k, v in ipairs(self.children) do
v:draw()
end
end
panel:SetSize(love.graphics.getDimensions())
menu = lf.Create("menu", panel)
menu:SetVisible(false)
local menus = {}
for name, category in pairs(items) do
for _, item in ipairs(category) do
menus[name] = menus[name] or {}
table.insert(menus[name], item)
end
end
for name, category in pairs(menus) do
local sub = lf.Create("menu")
for _, item in ipairs(category) do
sub:AddOption(item.name, false, function()
local pos = cpml.vec2(love.mouse.getPosition())
grabbed = item.new((pos - offset):unpack())
selected = grabbed
love.mouse.setGrabbed(true)
grabbed.selected = true
grabbed:update()
table.insert(node_list, grabbed)
end)
end
menu:AddSubMenu(name, false, sub)
end
local w, h = love.graphics.getDimensions()
offset.x = w / 2
offset.y = h / 2
end
function love.keypressed(k, s, rep)
if k == "g" and not rep then
if grabbed then
love.mouse.setGrabbed(false)
grabbed = false
elseif selected then
love.mouse.setGrabbed(true)
grabbed = selected
end
return
end
if (k == "x" or k == "delete") and selected and not selected.invulnerable and not rep then
selected:disconnect(nil, true)
for i, v in ipairs(node_list) do
if v == selected then
table.remove(node_list, i)
break
end
end
selected = false
end
if k == "d" and selected and not rep then
local new = selected:clone()
new:update()
selected.selected = false
selected.hit = false
new.selected = true
new.hit = true
selected = new
grabbed = new
table.insert(node_list, new)
end
-- TEST SERIALIZATION
if k == "q" then
debug_serial = serial.encode(node_list)
node_list = {}
end
if k == "e" then
node_list = serial.decode(debug_serial)
debug_serial = nil
end
-- END TEST
if k == "home" then
local w, h = love.graphics.getDimensions()
offset.x = w / 2
offset.y = h / 2
zoom = 1
end
if k == "space" then
panning = true
end
lf.keypressed(k)
end
function love.keyreleased(k)
if k == "space" then
panning = false
end
lf.keyreleased(k)
end
--
function love.textinput(t)
lf.textinput(t)
end
function love.wheelmoved(x, y)
if y ~= 0 then
zoom = cpml.utils.clamp(zoom + y / 4, 0.25, 1.0)
end
end
function love.mousepressed(x, y, button)
if button == 1 and zoom == 1 then
if not grabbed and not new_connection then
selected = false
for i, v in ipairs(node_list) do
v:check_hit(x, y, offset)
if v.hit_connector and v.hit_connector.output then
new_connection = v.hit_connector
elseif v.hit and not v.hit_connector then
grabbed = v
love.mouse.setGrabbed(true)
end
v.selected = v.hit
if v.selected then
selected = v
end
end
else
love.mouse.setGrabbed(false)
grabbed = false
new_connection = false
end
end
if button == 2 then
menu:SetPos(x, y)
menu:SetVisible(true)
end
if button == 3 then
panning = true
end
if button <= 5 then
lf.mousepressed(x, y, button)
end
end
function love.mousereleased(x, y, button)
if button == 1 then
if grabbed then
grabbed = false
love.mouse.setGrabbed(false)
end
if new_connection and zoom == 1 then
for i, v in ipairs(node_list) do
v:check_hit(x, y, offset)
if v.hit_connector and v.hit_connector.input then
print("dropped on a connector!")
print(new_connection.socket, v.hit_connector.socket)
new_connection.node:connect(v.hit_connector.node, new_connection.socket, v.hit_connector.socket)
end
end
new_connection = false
end
end
if button == 2 and zoom == 1 then
for i, v in ipairs(node_list) do
v:check_hit(x, y, offset)
if v.hit_connector and v.hit_connector.input then
v:disconnect(v.hit_connector.socket)
break
end
end
end
if button == 3 then
panning = false
end
if button <= 5 then
lf.mousereleased(x, y, button)
end
end
function love.mousemoved(x, y, dx, dy)
if zoom == 1 then
for i, v in ipairs(node_list) do
v:check_hit(x, y, offset)
end
end
if grabbed then
grabbed.position = grabbed.position + cpml.vec2(dx, dy) * (1/zoom)
grabbed:update()
end
if panning then
offset.x = offset.x + dx
offset.y = offset.y + dy
end
end
function love.update(dt)
for i, v in ipairs(node_list) do
if v.needs_eval then
print("Nodes updated, evaluating...")
node.evaluate_list(node_list)
break
end
end
lf.update(dt)
end
function love.draw()
local w, h = love.graphics.getDimensions()
-- background grid
editgrid.draw({
x = (-offset.x + w / 2) * (1/zoom),
y = (-offset.y + h / 2) * (1/zoom),
zoom = zoom,
sw = w,
sh = h
}, {
size = 50,
subdivisions = 5,
color = { 20, 20, 20 },
xColor = { 255, 0, 0 },
yColor = { 0, 255, 0 },
fadeFactor = 1.5,
textFadeFactor = 0.75,
})
love.graphics.push()
love.graphics.translate(offset:unpack())
love.graphics.scale(zoom, zoom)
node.draw_nodes(node_list)
if new_connection then
node.draw_noodle(new_connection.position, cpml.vec2(love.mouse.getPosition()) - offset, new_connection.color)
end
love.graphics.pop()
lf.draw()
end
-- Exactly the same as normal, but only renders when an event was handled.
function love.run()
if love.math then
love.math.setRandomSeed(os.time())
for i=1,3 do love.math.random() end
end
if love.event then
love.event.pump()
end
if love.load then love.load(arg) end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
local dt = 0
-- Main loop time.
local needs_update = 0
while true do
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return
end
end
love.handlers[name](a,b,c,d,e,f)
needs_update = 3
end
end
-- Update dt, as we'll be passing it to update
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.window and love.graphics and love.window.isCreated() and needs_update > 0 then
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
if love.draw then love.draw() end
love.graphics.present()
needs_update = needs_update - 1
end
-- Run a fast GC cycle so that it happens at predictable times.
collectgarbage("step")
if love.timer then love.timer.sleep(0.01) end
end
end