-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.lua
279 lines (264 loc) · 10.7 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
require("lib/term")
require("lib/canvas")
require("lib/term-ui")
local defaultCanvasWidth = 20
local defaultCanvasHeight = 10
local fileName = ({ ... })[1] or "ascii-art"
local fileHandle = io.open(fileName, "r")
local canvas
if fileHandle then
local fileContents = fileHandle:read("*a")
fileHandle:close()
canvas = Canvas.fromText(fileContents)
else
canvas = Canvas.new(defaultCanvasWidth, defaultCanvasHeight)
end
local palette
local canvasX
local canvasY
local resizing = false
local toolbarWidth = 12
local paletteWidth = 12
local tools = {
{
name = "Pencil",
char = "O"
},
{
name = "Eraser"
},
{
name = "Rectangle",
char = "+"
},
{
name = "Fill",
char = "."
},
{
name = "Global Fill",
char = "."
}
}
local pencil = 1
local eraser = 2
local rectangle = 3
local fill = 4
local globalFill = 5
local selectedTool = pencil
local function calculateToolbarWidth()
local maxWidth = 0
for _, tool in ipairs(tools) do
if #tool.name > maxWidth then
maxWidth = #tool.name
end
end
return maxWidth + 4
end
toolbarWidth = calculateToolbarWidth()
local function update(inputs)
for _, input in ipairs(inputs) do
if input.type == "char" and input.char == "q" then -- Q
return false
elseif input.type == "char" and input.char == "p" then -- P
selectedTool = pencil
elseif input.type == "char" and input.char == "e" then -- E
selectedTool = eraser
elseif input.type == "char" and input.char == "r" then -- R
selectedTool = rectangle
elseif input.type == "raw" and input.hex == "13" then -- Ctrl + S
local file = io.open(fileName, "w")
if file then
file:write(Canvas.toText(canvas))
file:close()
else
error("Could not open file for writing.")
end
elseif input.type == "mouse_move" then
if resizing and input.button == 0 then
local newWidth = input.x - canvasX - 1
local newHeight = input.y - canvasY - 1
if newWidth >= 1 and newHeight >= 1 then
canvas = Canvas.resize(canvas, newWidth, newHeight)
end
elseif selectedTool == pencil and input.button == 0 then -- pencil
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.setPixel(canvas, x, y, tools[pencil].char)
end
elseif selectedTool == eraser and input.button == 0 then -- eraser
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.setPixel(canvas, x, y, " ")
end
elseif selectedTool == rectangle and input.button == 0 then -- rectangle
local x = input.x - canvasX
local y = input.y - canvasY
if tools[rectangle].start then
tools[rectangle].fin = { x, y }
end
end
elseif input.type == "mouse_press" then
if input.x == canvasX + canvas.width + 1 and input.y == canvasY + canvas.height + 1 then
resizing = true
elseif input.x >= Term.width - toolbarWidth + 1 and input.y >= 3 and input.y <= #tools + 2 then
selectedTool = input.y - 2
elseif palette[input.x] and palette[input.x][input.y] and input.button == 0 then
tools[selectedTool].char = palette[input.x][input.y]
elseif selectedTool == pencil and input.button == 0 then
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.setPixel(canvas, x, y, tools[pencil].char)
end
elseif selectedTool == eraser and input.button == 0 then
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.setPixel(canvas, x, y, " ")
end
elseif selectedTool == rectangle and input.button == 0 then
tools[rectangle].start = { input.x - canvasX, input.y - canvasY }
elseif selectedTool == fill and input.button == 0 then
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.fill(canvas, x, y, tools[fill].char, false)
end
elseif selectedTool == globalFill and input.button == 0 then
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.fill(canvas, x, y, tools[globalFill].char, true)
end
end
elseif input.type == "mouse_release" then
resizing = false
if selectedTool == pencil and input.button == 0 then -- pencil
local x = input.x - canvasX
local y = input.y - canvasY
if x >= 1 and x <= canvas.width and y >= 1 and y <= canvas.height then
Canvas.setPixel(canvas, x, y, tools[pencil].char)
end
elseif selectedTool == rectangle and input.button == 0 then -- rectangle
local x = input.x - canvasX
local y = input.y - canvasY
if tools[rectangle].start and tools[rectangle].fin then
local x1, y1 = tools[rectangle].start[1], tools[rectangle].start[2]
local x2, y2 = tools[rectangle].fin[1], tools[rectangle].fin[2]
local xMin, xMax = math.min(x1, x2), math.max(x1, x2)
local yMin, yMax = math.min(y1, y2), math.max(y1, y2)
for x = xMin, xMax do
Canvas.trySetPixel(canvas, x, yMin, tools[rectangle].char)
Canvas.trySetPixel(canvas, x, yMax, tools[rectangle].char)
end
for y = yMin, yMax do
Canvas.trySetPixel(canvas, xMin, y, tools[rectangle].char)
Canvas.trySetPixel(canvas, xMax, y, tools[rectangle].char)
end
tools[rectangle].start = nil
tools[rectangle].fin = nil
end
end
elseif input.type == "resize" then
canvasX = math.floor(Term.width / 2 - canvas.width / 2)
canvasY = math.floor(Term.height / 2 - canvas.height / 2)
palette = {}
for c = 32, 126 do
local column = 0
if c >= 96 then
column = 2
elseif c >= 64 then
column = 1
end
local x = Term.width - toolbarWidth - paletteWidth + 2 + column * 3
local y = c - 29 - column * 32
palette[x] = palette[x] or {}
palette[x][y] = string.char(c)
end
elseif input.type == "mouse_scroll" then
local amplify = input.mods.shift and 3 or 1
if input.mods.ctrl then
if input.dir == "up" then
canvasX = canvasX + 2 * amplify
elseif input.dir == "down" then
canvasX = canvasX - 2 * amplify
end
else
if input.dir == "up" then
canvasY = canvasY + 1 * amplify
elseif input.dir == "down" then
canvasY = canvasY - 1 * amplify
end
end
end
end
return true
end
local function drawOverlayPixel(x, y, char)
local posX = x + canvasX
local posY = y + canvasY
if posX >= 1 and posX <= Term.width - toolbarWidth - 2 and posY >= 1 and posY <= Term.height then
Term.setCursorPos(posX, posY)
Term.write(char)
end
end
local function render()
TermUI.clear(Term, "+")
--canvas area
TermUI.drawCanvas(Term, canvas, canvasX + 1, canvasY + 1)
--current tool overlay
if selectedTool == rectangle and tools[rectangle].start and tools[rectangle].fin then
local x1, y1 = tools[rectangle].start[1], tools[rectangle].start[2]
local x2, y2 = tools[rectangle].fin[1], tools[rectangle].fin[2]
local xMin, xMax = math.min(x1, x2), math.max(x1, x2)
local yMin, yMax = math.min(y1, y2), math.max(y1, y2)
for x = xMin, xMax do
drawOverlayPixel(x, yMin, tools[rectangle].char)
drawOverlayPixel(x, yMax, tools[rectangle].char)
end
for y = yMin, yMax do
drawOverlayPixel(xMin, y, tools[rectangle].char)
drawOverlayPixel(xMax, y, tools[rectangle].char)
end
end
Term.setCursorPos(canvasX + canvas.width + 1, canvasY + canvas.height + 1)
Term.write("%")
-- toolbar
TermUI.fillRect(Term, Term.width - toolbarWidth + 1, 1, toolbarWidth, Term.height, " ")
TermUI.fillRect(Term, Term.width - toolbarWidth, 1, 1, Term.height, "|")
Term.setCursorPos(Term.width - toolbarWidth + 1, 1)
Term.write(" TOOLS")
TermUI.fillRect(Term, Term.width - toolbarWidth + 1, 2, toolbarWidth, 1, "-")
for i, tool in ipairs(tools) do
Term.setCursorPos(Term.width - toolbarWidth + 1, i + 2)
if i == selectedTool then
Term.write("> ")
else
Term.write(" ")
end
Term.write(tool.name)
end
-- palette
TermUI.fillRect(Term, Term.width - toolbarWidth - paletteWidth, 1, paletteWidth, Term.height, " ")
TermUI.fillRect(Term, Term.width - toolbarWidth - paletteWidth - 1, 1, 1, Term.height, "|")
Term.setCursorPos(Term.width - toolbarWidth - paletteWidth, 1)
Term.write(" PALETTE")
TermUI.fillRect(Term, Term.width - toolbarWidth - paletteWidth, 2, paletteWidth, 1, "-")
for x, v in pairs(palette) do
for y, c in pairs(v) do
if x >= 1 and x <= Term.width and y >= 1 and y <= Term.height then
if tools[selectedTool].char == c then
Term.setCursorPos(x - 1, y)
Term.write("> <")
end
Term.setCursorPos(x, y)
Term.write(c)
end
end
end
Term.flush()
end
Term.runApp(update, render)