123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- -- primitives for saving to file and loading from file
- function file_exists(filename)
- local infile = App.open_for_reading(App.save_dir..filename)
- if not infile then
- infile = App.open_for_reading(App.source_dir..filename)
- end
- if infile then
- infile:close()
- return true
- else
- return false
- end
- end
- -- the source editor supports only files in the save dir backed by the source dir
- function load_from_disk(State)
- local infile = App.open_for_reading(App.save_dir..State.filename)
- if not infile then
- infile = App.open_for_reading(App.source_dir..State.filename)
- end
- 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(App.save_dir..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')
- local line_info = {mode='text'}
- line_info.data = line
- table.insert(result, line_info)
- 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
- function dirname(path)
- local os_path_separator = package.config:sub(1,1)
- if os_path_separator == '/' then
- -- POSIX systems permit backslashes in filenames
- return path:match('.*/') or './'
- elseif os_path_separator == '\\' then
- return path:match('.*[/\\]') or './'
- else
- error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
- end
- end
- function test_dirname()
- check_eq(dirname('a/b'), 'a/', 'F - test_dirname')
- check_eq(dirname('x'), './', 'F - test_dirname/current')
- end
- function basename(path)
- local os_path_separator = package.config:sub(1,1)
- if os_path_separator == '/' then
- -- POSIX systems permit backslashes in filenames
- return string.gsub(path, ".*/(.*)", "%1")
- elseif os_path_separator == '\\' then
- return string.gsub(path, ".*[/\\](.*)", "%1")
- else
- error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
- end
- end
- function empty(h)
- for _,_ in pairs(h) do
- return false
- end
- return true
- end
|