assume starty can be nil in update
[view.love.git] / source_file.lua
blobdbb6f95525984493119c30315975188fc79e0937
1 -- primitives for saving to file and loading from file
3 function file_exists(filename)
4 local infile = App.open_for_reading(App.save_dir..filename)
5 if not infile then
6 infile = App.open_for_reading(App.source_dir..filename)
7 end
8 if infile then
9 infile:close()
10 return true
11 else
12 return false
13 end
14 end
16 -- the source editor supports only files in the save dir backed by the source dir
17 function load_from_disk(State)
18 local infile = App.open_for_reading(App.save_dir..State.filename)
19 if not infile then
20 infile = App.open_for_reading(App.source_dir..State.filename)
21 end
22 State.lines = load_from_file(infile)
23 if infile then infile:close() end
24 end
26 function load_from_file(infile)
27 local result = {}
28 if infile then
29 local infile_next_line = infile:lines() -- works with both Lua files and LÖVE Files (https://www.love2d.org/wiki/File)
30 while true do
31 local line = infile_next_line()
32 if line == nil then break end
33 if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
34 table.insert(result, load_drawing(infile_next_line))
35 else
36 table.insert(result, {mode='text', data=line})
37 end
38 end
39 end
40 if #result == 0 then
41 table.insert(result, {mode='text', data=''})
42 end
43 return result
44 end
46 function save_to_disk(State)
47 local outfile = App.open_for_writing(App.save_dir..State.filename)
48 if not outfile then
49 error('failed to write to "'..State.filename..'"')
50 end
51 for _,line in ipairs(State.lines) do
52 if line.mode == 'drawing' then
53 store_drawing(outfile, line)
54 else
55 outfile:write(line.data)
56 outfile:write('\n')
57 end
58 end
59 outfile:close()
60 end
62 function load_drawing(infile_next_line)
63 local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
64 while true do
65 local line = infile_next_line()
66 assert(line)
67 if line == '```' then break end
68 local shape = json.decode(line)
69 if shape.mode == 'freehand' then
70 -- no changes needed
71 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
72 local name = shape.p1.name
73 shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
74 drawing.points[shape.p1].name = name
75 name = shape.p2.name
76 shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
77 drawing.points[shape.p2].name = name
78 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
79 for i,p in ipairs(shape.vertices) do
80 local name = p.name
81 shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
82 drawing.points[shape.vertices[i]].name = name
83 end
84 elseif shape.mode == 'circle' or shape.mode == 'arc' then
85 local name = shape.center.name
86 shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
87 drawing.points[shape.center].name = name
88 elseif shape.mode == 'deleted' then
89 -- ignore
90 else
91 print(shape.mode)
92 assert(false)
93 end
94 table.insert(drawing.shapes, shape)
95 end
96 return drawing
97 end
99 function store_drawing(outfile, drawing)
100 outfile:write('```lines\n')
101 for _,shape in ipairs(drawing.shapes) do
102 if shape.mode == 'freehand' then
103 outfile:write(json.encode(shape))
104 outfile:write('\n')
105 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
106 local line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]})
107 outfile:write(line)
108 outfile:write('\n')
109 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
110 local obj = {mode=shape.mode, vertices={}}
111 for _,p in ipairs(shape.vertices) do
112 table.insert(obj.vertices, drawing.points[p])
114 local line = json.encode(obj)
115 outfile:write(line)
116 outfile:write('\n')
117 elseif shape.mode == 'circle' then
118 outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}))
119 outfile:write('\n')
120 elseif shape.mode == 'arc' then
121 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}))
122 outfile:write('\n')
123 elseif shape.mode == 'deleted' then
124 -- ignore
125 else
126 print(shape.mode)
127 assert(false)
130 outfile:write('```\n')
133 -- for tests
134 function load_array(a)
135 local result = {}
136 local next_line = ipairs(a)
137 local i,line,drawing = 0, ''
138 while true do
139 i,line = next_line(a, i)
140 if i == nil then break end
141 --? print(line)
142 if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
143 --? print('inserting drawing')
144 i, drawing = load_drawing_from_array(next_line, a, i)
145 --? print('i now', i)
146 table.insert(result, drawing)
147 else
148 --? print('inserting text')
149 local line_info = {mode='text'}
150 line_info.data = line
151 table.insert(result, line_info)
154 if #result == 0 then
155 table.insert(result, {mode='text', data=''})
157 return result
160 function load_drawing_from_array(iter, a, i)
161 local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
162 local line
163 while true do
164 i, line = iter(a, i)
165 assert(i)
166 --? print(i)
167 if line == '```' then break end
168 local shape = json.decode(line)
169 if shape.mode == 'freehand' then
170 -- no changes needed
171 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
172 local name = shape.p1.name
173 shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
174 drawing.points[shape.p1].name = name
175 name = shape.p2.name
176 shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
177 drawing.points[shape.p2].name = name
178 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
179 for i,p in ipairs(shape.vertices) do
180 local name = p.name
181 shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
182 drawing.points[shape.vertices[i]].name = name
184 elseif shape.mode == 'circle' or shape.mode == 'arc' then
185 local name = shape.center.name
186 shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
187 drawing.points[shape.center].name = name
188 elseif shape.mode == 'deleted' then
189 -- ignore
190 else
191 print(shape.mode)
192 assert(false)
194 table.insert(drawing.shapes, shape)
196 return i, drawing
199 function is_absolute_path(path)
200 local os_path_separator = package.config:sub(1,1)
201 if os_path_separator == '/' then
202 -- POSIX systems permit backslashes in filenames
203 return path:sub(1,1) == '/'
204 elseif os_path_separator == '\\' then
205 if path:sub(2,2) == ':' then return true end -- DOS drive letter followed by volume separator
206 local f = path:sub(1,1)
207 return f == '/' or f == '\\'
208 else
209 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
213 function is_relative_path(path)
214 return not is_absolute_path(path)
217 function dirname(path)
218 local os_path_separator = package.config:sub(1,1)
219 if os_path_separator == '/' then
220 -- POSIX systems permit backslashes in filenames
221 return path:match('.*/') or './'
222 elseif os_path_separator == '\\' then
223 return path:match('.*[/\\]') or './'
224 else
225 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
229 function test_dirname()
230 check_eq(dirname('a/b'), 'a/', 'F - test_dirname')
231 check_eq(dirname('x'), './', 'F - test_dirname/current')
234 function basename(path)
235 local os_path_separator = package.config:sub(1,1)
236 if os_path_separator == '/' then
237 -- POSIX systems permit backslashes in filenames
238 return string.gsub(path, ".*/(.*)", "%1")
239 elseif os_path_separator == '\\' then
240 return string.gsub(path, ".*[/\\](.*)", "%1")
241 else
242 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
246 function empty(h)
247 for _,_ in pairs(h) do
248 return false
250 return true