-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.lua
351 lines (311 loc) · 8.51 KB
/
lib.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
--library functions for luaCAM
--written by Jesse Meade-Clift
newline = "\n"
versionno = "0.7"
--TODO: gui per macro!! ***
--todo: errors
--todo: if everything is a function, everything is composable
--trig functions that take degrees. rounding occasionally produces odd results (eg. extremely small numbers instead of zero)
--math.abs check is to remove badly behaved small decimals that should be whole numbers
function sin(x)
local ret = math.sin(math.rad(x))
if math.abs(ret) < 0.0001 then ret = 0 end
return ret
end
function cos(x)
local ret = math.cos(math.rad(x))
if math.abs(ret) < 0.0001 then ret = 0 end
return ret
end
--rotates coordinates around origin by alpha
function angletransform(x, y, alpha)
return x*cos(alpha)-y*sin(alpha), y*cos(alpha)+x*sin(alpha)
end
function round(num, digits)
return string.format("%."..digits.."f", num)
end
--returns coordinates of a given bolt hole on a given bolt hole circle
--hole number 1 is first positive angle from positive x-axis
--offset is angle from positive x-axis to first hole
--currently unused by luacam
function bhc_coord(hole_no, bcd, no_holes, offset)
--hole_no - 1 to start at first hole
local angle = offset + ((hole_no - 1)/no_holes)*360
local rad = bcd/2
return cos(angle)*rad, sin(angle)*rad
end
--generates a macro code for fetching the current tool diameter in machine
--currently unused
function tool_dia_mazak()
return "#[61000+#51999] (tool dia. current tool)"
end
--format decimal places in g-code addresses
--determines which addresses have their values rounded, ad also var interpolation
function line(line)
return string.gsub(line, "([aAxXyYzZiIjJpPfF])([+-]?%d+[%.]*[%d]*)", function (m,n)
return m..string.format("%.4f", n) --refactor
end)
end
--removes whitespace at beginning and end of string
function trim(s)
return string.gsub(s, "^%s*(.-)%s*$", "%1")
end
--creates a g-code header with program number, and an optional descriptive comment
--also adds comments with the date generated and the source file name/location
function headers(program_number, comment, src)
local headers = ""
comment = comment or ""
headers = headers .. "O"
.. string.format("%08d", program_number)
.. "(" .. comment .. ")"
.. newline
.. friendly_date()
.. version()
.. source(src)
return headers
end
--helper format function
function source(src)
return "(Source: " .. src .. ")\n"
end
--[%w_]+ matches one or more digits, letters or underscores after the $ sign, stopping at first whitespace. replaced with %1 first occurence found
--%(...%) escapes brackets. Finds minimum number of any characters between matching $( and ), replaced with first %1 occurence found
--eg: turns "$(x + y) $blah" into "\"..x+y.." "..blah..\""
function expand(s)
local st = string.gsub(s, "$([%w_]+)", "\".. %1 ..\"")
return string.gsub(st, "$%[(.-)%]", "\".. %1 ..\"")
end
--iterator, goes through all values of a table ignoring keys
function values (t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
--parallel iteration through two tables
--currently unused
function parallel(a, b)
local i = 0
return function()
i = i + 1
return a[i], b[i]
end
end
--pulls a number froma string, returns nil if none found
function extract_number(text)
return string.match(text, "(%d+)")
end
--unused
function str_sanitize(str)
return "o.write(o, string.upper(\""..str.."\\n\"))"
end
--todo, automated macro translation mode?
--self-contained loadstring env? could clobber stuff
--preprocessing function, does translation from hybrid templates to raw g-code
--TODO: doesn't handle F$feed on its own line
--determines which addresses can start a line
function preproc(str)
local addresses = {"G", "g", "M", "m", "S", "s", "T", "t", "X", "x", "Y", "y", "Z", "z"}
local out = "local output_ = \"\"\n"
for j in str_lines(str) do
i = trim(j) --trim whitespace at beginning (and end) of line
for v in values(addresses) do
--looks for an address plus one or more digits, i.e. "G0"
--or checks for EIA comments ()
if (string.find(i, "^"..v.."%d+") or string.find(i, "^%(")) then
i = "output_ = output_ .. string.upper(line(\""..expand(i).."\\n\"))"
end
end
out = out .. i .. "\n"
end
out = out .. "return output_"
return assert(loadstring(out))()
end
--returns number of lines with actual code, ignores comments and program number line
function numberoflines(str)
local exceptions = {"O", "("}
local lineno = 0
for j in str_lines(str) do
local flag = true
for v in values(exceptions) do
if(string.find(j, "^["..v.."]")) then
flag = false
end
end
if(flag) then
lineno = lineno + 1
end
end
return lineno
end
--returns the number of digits of num (an integer)
function digits(num)
return math.floor(math.log10(num)) + 1
end
--adds g-code linenumbers, only to lines with g-code
function linenumbers(str)
local exceptions = {"O", "("}
local out = ""
local digits = digits(numberoflines(str))
local lineno = 1--why not local?
for j in str_lines(str) do
i = trim(j)
local flag = true
for v in values(exceptions) do
if(string.find(i, "^["..v.."]")) then
flag = false
end
end
if(flag) then
i = "N" .. string.format("%0" .. digits .. "d", lineno) .. " " .. i
lineno = lineno + 1
end
out = out .. i .. "\n"
end
return out
end
--surrounds file in %..%, g-code standard. currently unused, machine doesn't like it
function percent(str)
local out = ""
for j in str_lines(str) do
i = trim(j)
out = out .. i .. "\n"
end
return "%\n" .. out .. "%\n"
end
function version()
return "(by luacam version " .. versionno .. ")\n"
end
function friendly_date()
return os.date("(Generated on %A %B %d %Y at %X)\n")
end
--iterates through each line of a string
--no good equivalent in std lib
function str_lines(str)
return string.gmatch(str, "[^" .. newline .. "]+")
end
--helper composition function
function comment_vars(str)
return comment(pull_vars(str))
end
--g-code comments a block of text
function comment(str)
local out = ""
for j in str_lines(str) do
i = trim(j)
out = out .. "(" .. j .. ")" .. newline
end
return out
end
--extracts special variables between --##s, for commenting and modifying
function pull_vars(str)
local out = ""
local flag = false
for j in str_lines(str) do
i = trim(j)
if(i == "--##") then
flag = not flag
elseif(flag) then
out = out .. j .. newline
end
end
return out
end
--same as pull_vars but makes a table instead of a string.
function pull_vars_table(str)
local t = {}
local k = 0
local flag = false
for j in str_lines(str) do
i = trim(j)
if(i == "--##") then
flag = not flag
elseif(flag) then
k = k + 1
t[k] = j
end
end
return t
end
function table_to_lines(t)
local out = ""
for k in pairs(t) do
out = out .. k .. newline
end
end
--write new variables between --##s
function write_vars(str, vars)
local out = ""
local flagcount = 0
for j in str_lines(str) do
i = trim(j)
if(i == "--##" and flagcount == 0) then
flagcount = flagcount + 1
out = out .. j .. newline
elseif(flagcount == 0 or flagcount == 2) then
out = out .. j .. newline
elseif(i == "--##" and flagcount == 1) then
flagcount = flagcount + 1
--assuming there is a newline on end of vars
out = out .. vars
out = out .. j .. newline
end
end
return out
end
--extract values from a variable assignment in --## section
function parse_assignment(line)
local work = trim(line)
local var, value = string.match(work, "(.+)%s*=%s*(.+)")
return var, value
end
--extract var name from var assignment as above
function parse_first(line)
local work = trim(line)
return string.match(work, "(.+)%s*=s*.+")
end
--take a function fn, call it from i to j inclusive over step, call j even if not a multiple of j-i, optionally decrement the step by dec
function cycle(fn, i, j, step, dec)
dec = dec or 0
local last = 0
while (i <= j) do
fn(i)
last = i
i = i + step
step = step - dec
if step <=0 then step = 0.001 end
end
if last ~= j then
fn(j)
end
end
--geometric cycle
function cycle_g(fn, i, j, factor)
local last = 0
local st = i
while (i <= j) do
fn(i)
last = i
st = st*factor
if st <= 0.001 then st = 0.001 end
i = i + st
end
if last ~= j then
fn(j)
end
end
--returns an array of directory listing
function scandir(directory)
local i, t = 0, {}
local file = io.popen("ls " .. directory) --popen doesn't handle errors with msg
for filename in file.lines(file) do
i = i + 1
t[i] = filename
end
return t
end
--returns number of cuts required for a given depth in cut depths of "cut"
function num_cuts(depth, cut)
return math.ceil(depth/cut)
end