2022-05-18 04:18:17 +00:00
-- primitives for saving to file and loading from file
2022-07-06 04:56:03 +00:00
function file_exists ( filename )
local infile = App.open_for_reading ( filename )
if infile then
infile : close ( )
return true
else
return false
end
end
2022-07-26 02:56:39 +00:00
function load_from_disk ( State )
local infile = App.open_for_reading ( State.filename )
State.lines = load_from_file ( infile )
2022-05-18 04:18:17 +00:00
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
2022-07-26 02:56:39 +00:00
function save_to_disk ( State )
local outfile = App.open_for_writing ( State.filename )
2023-08-31 06:52:25 +00:00
if not outfile then
2022-07-26 02:56:39 +00:00
error ( ' failed to write to " ' .. State.filename .. ' " ' )
2022-07-05 21:02:52 +00:00
end
2022-07-26 02:56:39 +00:00
for _ , line in ipairs ( State.lines ) do
2022-05-18 04:18:17 +00:00
if line.mode == ' drawing ' then
store_drawing ( outfile , line )
else
2022-09-05 18:28:03 +00:00
outfile : write ( line.data )
outfile : write ( ' \n ' )
2022-05-18 04:18:17 +00:00
end
end
outfile : close ( )
2022-07-26 06:58:53 +00:00
if not State.recent_updated then
2022-07-26 16:55:42 +00:00
if State.file_path then -- might not be set in some tests from lines.love that I don't want to modify in capture.love; we don't want to touch the file system then anyway
local f = io.open ( State.directory .. ' recent ' , ' a ' )
if f then
f : write ( State.file_path , ' \n ' )
f : close ( )
end
2022-07-26 06:58:53 +00:00
end
State.recent_updated = true
end
2022-05-18 04:18:17 +00:00
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 ( )
2023-11-18 19:30:57 +00:00
assert ( line , ' drawing in file is incomplete ' )
2022-05-18 04:18:17 +00:00
if line == ' ``` ' then break end
local shape = json.decode ( line )
2022-05-21 21:03:06 +00:00
if shape.mode == ' freehand ' then
-- no changes needed
elseif shape.mode == ' line ' or shape.mode == ' manhattan ' then
local name = shape.p1 . name
2022-09-04 02:22:17 +00:00
shape.p1 = Drawing.find_or_insert_point ( drawing.points , shape.p1 . x , shape.p1 . y , --[[large width to minimize overlap]] 1600 )
2022-05-21 21:03:06 +00:00
drawing.points [ shape.p1 ] . name = name
name = shape.p2 . name
2022-09-04 02:22:17 +00:00
shape.p2 = Drawing.find_or_insert_point ( drawing.points , shape.p2 . x , shape.p2 . y , --[[large width to minimize overlap]] 1600 )
2022-05-21 21:03:06 +00:00
drawing.points [ shape.p2 ] . name = name
2022-05-21 15:28:34 +00:00
elseif shape.mode == ' polygon ' or shape.mode == ' rectangle ' or shape.mode == ' square ' then
2022-05-18 04:18:17 +00:00
for i , p in ipairs ( shape.vertices ) do
2022-05-21 21:03:06 +00:00
local name = p.name
2022-09-04 02:22:17 +00:00
shape.vertices [ i ] = Drawing.find_or_insert_point ( drawing.points , p.x , p.y , --[[large width to minimize overlap]] 1600 )
2022-05-21 21:03:06 +00:00
drawing.points [ shape.vertices [ i ] ] . name = name
2022-05-18 04:18:17 +00:00
end
elseif shape.mode == ' circle ' or shape.mode == ' arc ' then
2022-05-21 21:03:06 +00:00
local name = shape.center . name
2022-09-04 02:22:17 +00:00
shape.center = Drawing.find_or_insert_point ( drawing.points , shape.center . x , shape.center . y , --[[large width to minimize overlap]] 1600 )
2022-05-26 00:07:06 +00:00
drawing.points [ shape.center ] . name = name
2022-05-30 22:26:31 +00:00
elseif shape.mode == ' deleted ' then
-- ignore
2022-05-21 15:28:34 +00:00
else
2023-11-18 19:30:57 +00:00
assert ( false , ( ' unknown drawing mode %s ' ) : format ( shape.mode ) )
2022-05-18 04:18:17 +00:00
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
2023-05-30 07:00:43 +00:00
outfile : write ( json.encode ( shape ) )
outfile : write ( ' \n ' )
2022-05-18 04:18:17 +00:00
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 ] } )
2023-05-30 07:00:43 +00:00
outfile : write ( line )
outfile : write ( ' \n ' )
2022-05-21 15:28:34 +00:00
elseif shape.mode == ' polygon ' or shape.mode == ' rectangle ' or shape.mode == ' square ' then
2022-05-18 04:18:17 +00:00
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 )
2023-05-30 07:00:43 +00:00
outfile : write ( line )
outfile : write ( ' \n ' )
2022-05-18 04:18:17 +00:00
elseif shape.mode == ' circle ' then
2023-05-30 07:00:43 +00:00
outfile : write ( json.encode ( { mode = shape.mode , center = drawing.points [ shape.center ] , radius = shape.radius } ) )
outfile : write ( ' \n ' )
2022-05-18 04:18:17 +00:00
elseif shape.mode == ' arc ' then
2023-05-30 07:00:43 +00:00
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 ' )
2022-05-30 22:26:31 +00:00
elseif shape.mode == ' deleted ' then
-- ignore
2022-05-21 15:28:34 +00:00
else
2023-11-18 19:30:57 +00:00
assert ( false , ( ' unknown drawing mode %s ' ) : format ( shape.mode ) )
2022-05-18 04:18:17 +00:00
end
end
outfile : write ( ' ``` \n ' )
end
2022-05-23 06:17:06 +00:00
-- for tests
function load_array ( a )
local result = { }
local next_line = ipairs ( a )
2022-05-23 15:13:58 +00:00
local i , line , drawing = 0 , ' '
2022-05-23 06:17:06 +00:00
while true do
i , line = next_line ( a , i )
if i == nil then break end
2022-05-23 15:13:58 +00:00
--? print(line)
2022-05-23 06:17:06 +00:00
if line == ' ```lines ' then -- inflexible with whitespace since these files are always autogenerated
2022-05-23 15:13:58 +00:00
--? print('inserting drawing')
i , drawing = load_drawing_from_array ( next_line , a , i )
--? print('i now', i)
table.insert ( result , drawing )
2022-05-23 06:17:06 +00:00
else
2022-05-23 15:13:58 +00:00
--? print('inserting text')
2022-05-23 06:17:06 +00:00
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 )
2023-11-18 19:30:57 +00:00
assert ( i , ' drawing in array is incomplete ' )
2022-05-23 15:13:58 +00:00
--? print(i)
2022-05-23 06:17:06 +00:00
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
2022-09-04 02:22:17 +00:00
shape.p1 = Drawing.find_or_insert_point ( drawing.points , shape.p1 . x , shape.p1 . y , --[[large width to minimize overlap]] 1600 )
2022-05-23 06:17:06 +00:00
drawing.points [ shape.p1 ] . name = name
name = shape.p2 . name
2022-09-04 02:22:17 +00:00
shape.p2 = Drawing.find_or_insert_point ( drawing.points , shape.p2 . x , shape.p2 . y , --[[large width to minimize overlap]] 1600 )
2022-05-23 06:17:06 +00:00
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
2022-09-04 02:22:17 +00:00
shape.vertices [ i ] = Drawing.find_or_insert_point ( drawing.points , p.x , p.y , --[[large width to minimize overlap]] 1600 )
2022-05-23 06:17:06 +00:00
drawing.points [ shape.vertices [ i ] ] . name = name
end
elseif shape.mode == ' circle ' or shape.mode == ' arc ' then
local name = shape.center . name
2022-09-04 02:22:17 +00:00
shape.center = Drawing.find_or_insert_point ( drawing.points , shape.center . x , shape.center . y , --[[large width to minimize overlap]] 1600 )
2022-05-26 00:07:06 +00:00
drawing.points [ shape.center ] . name = name
2022-05-30 22:26:31 +00:00
elseif shape.mode == ' deleted ' then
-- ignore
2022-05-23 06:17:06 +00:00
else
2023-11-18 19:30:57 +00:00
assert ( false , ( ' unknown drawing mode %s ' ) : format ( shape.mode ) )
2022-05-23 06:17:06 +00:00
end
table.insert ( drawing.shapes , shape )
end
2022-05-23 15:13:58 +00:00
return i , drawing
2022-05-23 06:17:06 +00:00
end
2022-07-06 04:56:03 +00:00
2022-09-11 15:31:05 +00:00
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
2022-09-26 22:17:57 +00:00
if path : sub ( 2 , 2 ) == ' : ' then return true end -- DOS drive letter followed by volume separator
2022-09-11 15:31:05 +00:00
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
2022-09-11 15:47:06 +00:00
2022-07-06 04:56:03 +00:00
function dirname ( path )
2022-09-11 15:47:06 +00:00
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
2022-07-06 17:48:58 +00:00
end
function test_dirname ( )
check_eq ( dirname ( ' a/b ' ) , ' a/ ' , ' F - test_dirname ' )
check_eq ( dirname ( ' x ' ) , ' ./ ' , ' F - test_dirname/current ' )
2022-07-06 04:56:03 +00:00
end
function basename ( path )
2022-09-11 15:47:06 +00:00
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
2022-07-06 04:56:03 +00:00
end