007b965b11
Each one should provide a message that will show up within LÖVE. Stop relying on nearby prints to the terminal. I also found some unnecessary ones. There is some potential here for performance regressions: the format() calls will trigger whether or not the assertion fails, and cause allocations. So far Lua's GC seems good enough to manage the load even with Moby Dick, even in some situations that caused issues in the past like undo.
203 lines
7.3 KiB
Lua
203 lines
7.3 KiB
Lua
-- primitives for saving to file and loading from file
|
|
function file_exists(filename)
|
|
local infile = App.open_for_reading(filename)
|
|
if infile then
|
|
infile:close()
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function load_from_disk(State)
|
|
local infile = App.open_for_reading(State.filename)
|
|
State.lines = load_from_file(infile)
|
|
if infile then infile:close() end
|
|
end
|
|
|
|
function load_from_file(infile)
|
|
local result = {}
|
|
if infile then
|
|
local infile_next_line = infile:lines() -- works with both Lua files and LÖVE Files (https://www.love2d.org/wiki/File)
|
|
while true do
|
|
local line = infile_next_line()
|
|
if line == nil then break end
|
|
if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
|
|
table.insert(result, load_drawing(infile_next_line))
|
|
else
|
|
table.insert(result, {mode='text', data=line})
|
|
end
|
|
end
|
|
end
|
|
if #result == 0 then
|
|
table.insert(result, {mode='text', data=''})
|
|
end
|
|
return result
|
|
end
|
|
|
|
function save_to_disk(State)
|
|
local outfile = App.open_for_writing(State.filename)
|
|
if not outfile then
|
|
error('failed to write to "'..State.filename..'"')
|
|
end
|
|
for _,line in ipairs(State.lines) do
|
|
if line.mode == 'drawing' then
|
|
store_drawing(outfile, line)
|
|
else
|
|
outfile:write(line.data)
|
|
outfile:write('\n')
|
|
end
|
|
end
|
|
outfile:close()
|
|
end
|
|
|
|
function load_drawing(infile_next_line)
|
|
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
|
|
while true do
|
|
local line = infile_next_line()
|
|
assert(line, 'drawing in file is incomplete')
|
|
if line == '```' then break end
|
|
local shape = json.decode(line)
|
|
if shape.mode == 'freehand' then
|
|
-- no changes needed
|
|
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
|
|
local name = shape.p1.name
|
|
shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.p1].name = name
|
|
name = shape.p2.name
|
|
shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.p2].name = name
|
|
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
|
|
for i,p in ipairs(shape.vertices) do
|
|
local name = p.name
|
|
shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.vertices[i]].name = name
|
|
end
|
|
elseif shape.mode == 'circle' or shape.mode == 'arc' then
|
|
local name = shape.center.name
|
|
shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.center].name = name
|
|
elseif shape.mode == 'deleted' then
|
|
-- ignore
|
|
else
|
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
|
end
|
|
table.insert(drawing.shapes, shape)
|
|
end
|
|
return drawing
|
|
end
|
|
|
|
function store_drawing(outfile, drawing)
|
|
outfile:write('```lines\n')
|
|
for _,shape in ipairs(drawing.shapes) do
|
|
if shape.mode == 'freehand' then
|
|
outfile:write(json.encode(shape))
|
|
outfile:write('\n')
|
|
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
|
|
local line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]})
|
|
outfile:write(line)
|
|
outfile:write('\n')
|
|
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
|
|
local obj = {mode=shape.mode, vertices={}}
|
|
for _,p in ipairs(shape.vertices) do
|
|
table.insert(obj.vertices, drawing.points[p])
|
|
end
|
|
local line = json.encode(obj)
|
|
outfile:write(line)
|
|
outfile:write('\n')
|
|
elseif shape.mode == 'circle' then
|
|
outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}))
|
|
outfile:write('\n')
|
|
elseif shape.mode == 'arc' then
|
|
outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius, start_angle=shape.start_angle, end_angle=shape.end_angle}))
|
|
outfile:write('\n')
|
|
elseif shape.mode == 'deleted' then
|
|
-- ignore
|
|
else
|
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
|
end
|
|
end
|
|
outfile:write('```\n')
|
|
end
|
|
|
|
-- for tests
|
|
function load_array(a)
|
|
local result = {}
|
|
local next_line = ipairs(a)
|
|
local i,line,drawing = 0, ''
|
|
while true do
|
|
i,line = next_line(a, i)
|
|
if i == nil then break end
|
|
--? print(line)
|
|
if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
|
|
--? print('inserting drawing')
|
|
i, drawing = load_drawing_from_array(next_line, a, i)
|
|
--? print('i now', i)
|
|
table.insert(result, drawing)
|
|
else
|
|
--? print('inserting text')
|
|
table.insert(result, {mode='text', data=line})
|
|
end
|
|
end
|
|
if #result == 0 then
|
|
table.insert(result, {mode='text', data=''})
|
|
end
|
|
return result
|
|
end
|
|
|
|
function load_drawing_from_array(iter, a, i)
|
|
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
|
|
local line
|
|
while true do
|
|
i, line = iter(a, i)
|
|
assert(i, 'drawing in array is incomplete')
|
|
--? print(i)
|
|
if line == '```' then break end
|
|
local shape = json.decode(line)
|
|
if shape.mode == 'freehand' then
|
|
-- no changes needed
|
|
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
|
|
local name = shape.p1.name
|
|
shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.p1].name = name
|
|
name = shape.p2.name
|
|
shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.p2].name = name
|
|
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
|
|
for i,p in ipairs(shape.vertices) do
|
|
local name = p.name
|
|
shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.vertices[i]].name = name
|
|
end
|
|
elseif shape.mode == 'circle' or shape.mode == 'arc' then
|
|
local name = shape.center.name
|
|
shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
|
|
drawing.points[shape.center].name = name
|
|
elseif shape.mode == 'deleted' then
|
|
-- ignore
|
|
else
|
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
|
end
|
|
table.insert(drawing.shapes, shape)
|
|
end
|
|
return i, drawing
|
|
end
|
|
|
|
function is_absolute_path(path)
|
|
local os_path_separator = package.config:sub(1,1)
|
|
if os_path_separator == '/' then
|
|
-- POSIX systems permit backslashes in filenames
|
|
return path:sub(1,1) == '/'
|
|
elseif os_path_separator == '\\' then
|
|
if path:sub(2,2) == ':' then return true end -- DOS drive letter followed by volume separator
|
|
local f = path:sub(1,1)
|
|
return f == '/' or f == '\\'
|
|
else
|
|
error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
|
|
end
|
|
end
|
|
|
|
function is_relative_path(path)
|
|
return not is_absolute_path(path)
|
|
end
|