1 -- primitives for saving to file and loading from file
2 function file_exists(filename
)
3 local infile
= App
.open_for_reading(filename
)
12 function load_from_disk(State
)
13 local infile
= App
.open_for_reading(State
.filename
)
14 State
.lines
= load_from_file(infile
)
15 if infile
then infile
:close() end
18 function load_from_file(infile
)
21 local infile_next_line
= infile
:lines() -- works with both Lua files and LÖVE Files (https://www.love2d.org/wiki/File)
23 local line
= infile_next_line()
24 if line
== nil then break end
25 if line
== '```lines' then -- inflexible with whitespace since these files are always autogenerated
26 table.insert(result
, load_drawing(infile_next_line
))
28 table.insert(result
, {mode
='text', data
=line
})
33 table.insert(result
, {mode
='text', data
=''})
38 function save_to_disk(State
)
39 local outfile
= App
.open_for_writing(State
.filename
)
40 if outfile
== nil then
41 error('failed to write to "'..State
.filename
..'"')
43 for _
,line
in ipairs(State
.lines
) do
44 if line
.mode
== 'drawing' then
45 store_drawing(outfile
, line
)
47 outfile
:write(line
.data
)
54 function load_drawing(infile_next_line
)
55 local drawing
= {mode
='drawing', h
=256/2, points
={}, shapes
={}, pending
={}}
57 local line
= infile_next_line()
59 if line
== '```' then break end
60 local shape
= json
.decode(line
)
61 if shape
.mode
== 'freehand' then
63 elseif shape
.mode
== 'line' or shape
.mode
== 'manhattan' then
64 local name
= shape
.p1
.name
65 shape
.p1
= Drawing
.find_or_insert_point(drawing
.points
, shape
.p1
.x
, shape
.p1
.y
, --[[large width to minimize overlap]] 1600)
66 drawing
.points
[shape
.p1
].name
= name
68 shape
.p2
= Drawing
.find_or_insert_point(drawing
.points
, shape
.p2
.x
, shape
.p2
.y
, --[[large width to minimize overlap]] 1600)
69 drawing
.points
[shape
.p2
].name
= name
70 elseif shape
.mode
== 'polygon' or shape
.mode
== 'rectangle' or shape
.mode
== 'square' then
71 for i
,p
in ipairs(shape
.vertices
) do
73 shape
.vertices
[i
] = Drawing
.find_or_insert_point(drawing
.points
, p
.x
,p
.y
, --[[large width to minimize overlap]] 1600)
74 drawing
.points
[shape
.vertices
[i]]
.name
= name
76 elseif shape
.mode
== 'circle' or shape
.mode
== 'arc' then
77 local name
= shape
.center
.name
78 shape
.center
= Drawing
.find_or_insert_point(drawing
.points
, shape
.center
.x
,shape
.center
.y
, --[[large width to minimize overlap]] 1600)
79 drawing
.points
[shape
.center
].name
= name
80 elseif shape
.mode
== 'deleted' then
86 table.insert(drawing
.shapes
, shape
)
91 function store_drawing(outfile
, drawing
)
92 outfile
:write('```lines\n')
93 for _
,shape
in ipairs(drawing
.shapes
) do
94 if shape
.mode
== 'freehand' then
95 outfile
:write(json
.encode(shape
), '\n')
96 elseif shape
.mode
== 'line' or shape
.mode
== 'manhattan' then
97 local line
= json
.encode({mode
=shape
.mode
, p1
=drawing
.points
[shape
.p1
], p2
=drawing
.points
[shape
.p2
]})
98 outfile
:write(line
, '\n')
99 elseif shape
.mode
== 'polygon' or shape
.mode
== 'rectangle' or shape
.mode
== 'square' then
100 local obj
= {mode
=shape
.mode
, vertices
={}}
101 for _
,p
in ipairs(shape
.vertices
) do
102 table.insert(obj
.vertices
, drawing
.points
[p
])
104 local line
= json
.encode(obj
)
105 outfile
:write(line
, '\n')
106 elseif shape
.mode
== 'circle' then
107 outfile
:write(json
.encode({mode
=shape
.mode
, center
=drawing
.points
[shape
.center
], radius
=shape
.radius
}), '\n')
108 elseif shape
.mode
== 'arc' then
109 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
}), '\n')
110 elseif shape
.mode
== 'deleted' then
117 outfile
:write('```\n')
121 function load_array(a
)
123 local next_line
= ipairs(a
)
124 local i
,line
,drawing
= 0, ''
126 i
,line
= next_line(a
, i
)
127 if i
== nil then break end
129 if line
== '```lines' then -- inflexible with whitespace since these files are always autogenerated
130 --? print('inserting drawing')
131 i
, drawing
= load_drawing_from_array(next_line
, a
, i
)
132 --? print('i now', i)
133 table.insert(result
, drawing
)
135 --? print('inserting text')
136 table.insert(result
, {mode
='text', data
=line
})
140 table.insert(result
, {mode
='text', data
=''})
145 function load_drawing_from_array(iter
, a
, i
)
146 local drawing
= {mode
='drawing', h
=256/2, points
={}, shapes
={}, pending
={}}
152 if line
== '```' then break end
153 local shape
= json
.decode(line
)
154 if shape
.mode
== 'freehand' then
156 elseif shape
.mode
== 'line' or shape
.mode
== 'manhattan' then
157 local name
= shape
.p1
.name
158 shape
.p1
= Drawing
.find_or_insert_point(drawing
.points
, shape
.p1
.x
, shape
.p1
.y
, --[[large width to minimize overlap]] 1600)
159 drawing
.points
[shape
.p1
].name
= name
161 shape
.p2
= Drawing
.find_or_insert_point(drawing
.points
, shape
.p2
.x
, shape
.p2
.y
, --[[large width to minimize overlap]] 1600)
162 drawing
.points
[shape
.p2
].name
= name
163 elseif shape
.mode
== 'polygon' or shape
.mode
== 'rectangle' or shape
.mode
== 'square' then
164 for i
,p
in ipairs(shape
.vertices
) do
166 shape
.vertices
[i
] = Drawing
.find_or_insert_point(drawing
.points
, p
.x
,p
.y
, --[[large width to minimize overlap]] 1600)
167 drawing
.points
[shape
.vertices
[i]]
.name
= name
169 elseif shape
.mode
== 'circle' or shape
.mode
== 'arc' then
170 local name
= shape
.center
.name
171 shape
.center
= Drawing
.find_or_insert_point(drawing
.points
, shape
.center
.x
,shape
.center
.y
, --[[large width to minimize overlap]] 1600)
172 drawing
.points
[shape
.center
].name
= name
173 elseif shape
.mode
== 'deleted' then
179 table.insert(drawing
.shapes
, shape
)
184 function is_absolute_path(path
)
185 local os_path_separator
= package
.config
:sub(1,1)
186 if os_path_separator
== '/' then
187 -- POSIX systems permit backslashes in filenames
188 return path
:sub(1,1) == '/'
189 elseif os_path_separator
== '\\' then
190 if path
:sub(2,2) == ':' then return true end -- DOS drive letter followed by volume separator
191 local f
= path
:sub(1,1)
192 return f
== '/' or f
== '\\'
194 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator
..'"')
198 function is_relative_path(path
)
199 return not is_absolute_path(path
)