The following Lua 4.0 script fails due to some of the variables being in an 'outer' scope. What are the different ways I can fix this? I would like to avoid putting things in the global scope completely if possible. Thanks.
function flokalAdd(iNx, iNy, iXmin, iSym, fEcc, bInside, fScale)
TWO_PI = 2 * PI;
iSx = 4;
iSy = 4;
function pow(fA, fB)
return fA^fB
end
function mag(fX, fY)
return dist(fX, fY, 0, 0)
end
function dist(fX1, fY1, fX2, fY2)
return sqrt(pow(fX2 - fX1, 2) + pow(fY2 - fY1, 2))
end
function norm()
local fY1 = 0;
local fX1 = 0;
local fX2 = exp(fX1) * cos(deg(fY1));
local fY2 = exp(fX1) * sin(deg(fY1)) + fEcc;
local fD = 0;
if (bInside == 1) then
fD = pow(mag(fX2, fY2), 1/iSym);
else
fD = pow(mag(fX2, fY2), -1/iSym);
end
local fArg = rad(atan2(fX2, fY2)) * -1/iSym;
local fX3 = fD * cos(deg(fArg))/TWO_PI;
local fY3 = fD * sin(deg(fArg))/TWO_PI;
return mag(fX3, fY3)
end
function fn(fX, fY)
local aP = {};
local fX1 = exp(fX) * cos(deg(fY));
local fY1 = exp(fX) * sin(deg(fY)) + fEcc;
local fD = 0;
if (bInside == 1) then
fD = pow(mag(fX1, fY1), 1/iSym);
else
fD = pow(mag(fX1, fY1), -1/iSym);
end
local fArg = rad(atan2(fX1, fY1)) * -1/iSym;
for i = 0, iSym - 1 do
local fX2 = fD * cos(deg(fArg + i * TWO_PI/iSym))/TWO_PI;
local fY2 = fD * sin(deg(fArg + i * TWO_PI/iSym))/TWO_PI;
aP[i + 1] = {fX2, fY2};
end
return aP
end
function lines(aP1, aP2)
for i = 0, iSym - 1 do
local fX1 = aP1[i + 1][0 + 1];
local fY1 = aP1[i + 1][1 + 1];
local fX2 = aP2[i + 1][0 + 1];
local fY2 = aP2[i + 1][1 + 1];
--addPebble(<sPebbleType>, <tPosition>, ?, ?, ?)
--addPebble("Pebble_2", {fX1 * fScale/fNorm, 0 * fScale/fNorm, fY1 * fScale/fNorm,}, 0, 0, 0)
-----------------------
-- DO SOMETHING HERE --
-----------------------
end
end
-- calculate the distance from the origin to the center of each eye, then use it to normalize the shape to within 1 unit of the center
fNorm = norm();
for i = 0, iNy - 1 do
local fY = i * TWO_PI/iNy;
for j = iXmin * iSx, iNx * iSx - 1 do
local fX1 = j * TWO_PI/iNx/iSx;
local fX2 = (j + 1) * TWO_PI/iNx/iSx;
lines(fn(fX1, fY), fn(fX2, fY))
end
end
for i = iXmin, iNx do
local fX = i * TWO_PI/iNx;
for j = 0, iNy * iSy - 1 do
local fY1 = j * TWO_PI/iNy/iSy;
local fY2 = (j + 1) * TWO_PI/iNy/iSy;
lines(fn(fX, fY1), fn(fX, fY2))
end
end
end
flokalAdd(16, 16, 12, 5, 256, false, 20000)
This happens due to Lua 4.0 scoping rules:
Function scopes have a special limitation in that outer scopes (other than the global scope) may not be accessed. This applies to any function, but is most noticeable in the case of nested functions where you may want to access locals of the enclosing scope.
In your code fEcc is defined in the scope of function flokalAdd, so it can't be accessed from the inner norm function.
You can fix this using upvalues:
Upvalues were added to work around the function scope limitation. Prefixing an outer-scope variable reference with % produces a copy of that variable as of the function's instantiation. Only the immediate scope containing the function and the global scope may be accessed in this manner.
Looking at the code, it seems that copying by value is OK. Here is the fixed version.
Related
I am working on a simple Pandoc reader that can process some of the basic html-like syntax used in forums (such as [b]bold[/b] and [h1]Header[/h1]).
I managed to get a basic reader working with LPEG (as described in the pandoc documentation), but the solution I landed on feels clunky. Is there better way to define the grammar around start and end tags (using things like priorities or negative lookahead, or LPEG groupings)?
Here was what I was able to get working:
local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt =
lpeg.P, lpeg.S, lpeg.R, lpeg.Cf, lpeg.Cc, lpeg.Ct, lpeg.V,
lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.B, lpeg.C, lpeg.Cmt
local whitespacechar = S(" \t\r\n")
local wordchar = (1 - whitespacechar)
local spacechar = S(" \t")
local newline = P"\r"^-1 * P"\n"
local blanklines = newline * (spacechar^0 * newline)^1
local endline = newline - blanklines
local emph_start = P"[i]"
local emph_end = P"[/i]"
local strong_start = P"[b]"
local strong_end = P"[/b]"
local header_start = P"[h" * (R"17" / tonumber) * "]"
local header_end = P"[/h" * R"17" * "]"
local tag_start = emph_start + strong_start + header_start
local tag_end = emph_end + strong_end + header_end
-- Grammar
G = P{ "Pandoc",
Pandoc = Ct(V"Block"^0) / pandoc.Pandoc;
Block = blanklines^0 * (V"Header" + V"Para") ;
Para = Ct(V"Inline"^1) / pandoc.Para;
Inline = V"Emph" + V"Strong" + V"Str" + V"Space" + V"SoftBreak" ;
Str = (1 - (whitespacechar + tag_end + tag_start))^1 / pandoc.Str;
Space = spacechar^1 / pandoc.Space;
SoftBreak = endline / pandoc.SoftBreak;
Emph = emph_start * Ct(V"Inline"^1) * emph_end / pandoc.Emph;
Strong = strong_start * Ct(V"Inline"^1) * strong_end / pandoc.Strong;
Header = header_start * Ct(V"Inline"^1) * header_end / pandoc.Header;
}
function Reader(input)
return lpeg.match(G, input)
end
And here's the kind of text I'd like to transfrom:
[h1]A Test[/h1]
The [i]quick[/i] dog jumped over the lazy stream!
Tags should be able to be applied [b]mid[/b]word.
I am trying to make a condition where the percentage would be calculated based on the number of fan operated and the amount of airflow. This is what I come out with
function System01()
CFM_SHOP1 = addr_getword("#W_HDW1")
CFM_SHOP2 = addr_getword("#W_HDW2")
STATUS_SHOP1 = addr_getbit("#B_M1")
STATUS_SHOP2 = addr_getbit("#B_M2")
OUTPUT_SHOP1 = addr_getword("#W_HDW10")
OUTPUT_SHOP2 = addr_getword("#W_HDW11")
CFM_1 = CFM_SHOP1 + CFM_SHOP2
if STATUS_SHOP1 == 1 then
OUTPUT_SHOP1 = CFM_SHOP1 * 10000 / CFM_1
addr_setword("#W_HDW10", OUTPUT_SHOP1)
if STATUS_SHOP2 == 1 then
OUTPUT_SHOP2 = CFM_SHOP2 * 10000 / CFM_1
addr_setword("#W_HDW11", OUTPUT_SHOP2)
end
TOTAL_1 = OUTPUT_SHOP1 + OUTPUT_SHOP2
addr_setword("#W_HDW19", TOTAL_1)
end
if STATUS_SHOP2 == 1 then
OUTPUT_SHOP2 = CFM_SHOP2 * 10000 / CFM_1
addr_setword("#W_HDW11", OUTPUT_SHOP2)
if STATUS_SHOP1 == 1 then
OUTPUT_SHOP1 = CFM_SHOP1 * 10000 / CFM_1
addr_setword("#W_HDW10", OUTPUT_SHOP1)
end
TOTAL_1 = OUTPUT_SHOP1 + OUTPUT_SHOP2
addr_setword("#W_HDW19", TOTAL_1)
end
addr_setbit("#B_M1", STATUS_SHOP1)
addr_setbit("#B_M2", STATUS_SHOP2)
addr_setbit("#B_M3", STATUS_SHOP3)
end
Is there any way that I can simplified it? Please note that this is only two example I give. There is total of 9 fan so it will be really complicated if i just use 'if'. Thanks in advance
To simplify the code use for-loop
function System01()
local CFM_SHOP = {}
local CFM = 0
for j = 1, 9 do
CFM_SHOP[j] = addr_getword("#W_HDW"..tostring(j))
CFM = CFM + CFM_SHOP[j]
end
local STATUS_SHOP = {}
for j = 1, 9 do
STATUS_SHOP[j] = addr_getbit("#B_M"..tostring(j))
end
local OUTPUT_SHOP = {}
for j = 1, 9 do
OUTPUT_SHOP[j] = addr_getword("#W_HDW"..tostring(j+9))
end
local TOTAL = 0
for j = 1, 9 do
if STATUS_SHOP[j] == 1 then
OUTPUT_SHOP[j] = CFM_SHOP[j] * 10000 / CFM
addr_setword("#W_HDW"..tostring(j+9), OUTPUT_SHOP[j])
end
TOTAL = TOTAL + OUTPUT_SHOP[j]
end
addr_setword("#W_HDW19", TOTAL)
for j = 1, 9 do
addr_setbit("#B_M"..tostring(j), STATUS_SHOP[j])
end
end
I can get this data with the following code. But it runs too slow:
local handle = io.popen("exiftool image.webp")
local result = handle:read("*a")
handle:close()
Is there a more elegant way to get the metadata?
UPD:
I use this software:
docker (20.10.7)
openresty/openresty:xenial (1.15.8.3)
luarocks (3.2.1)
LuaJIT (2.1.0-beta3)
Here is an example of a picture with a UserComment field: link
Exiftool sees this property:
$ exiftool -EXIF:UserComment Johnrogershousemay2020.webp
User Comment : {"foo":"bar"}
This method is less elegant, but it does not require to run external application :-)
function get_webp_user_comment(file_name)
local file = io.open(file_name, "rb")
local exif_offset, exif_found, is_big_endian, user_comment = 12
local function read_string(offset, size)
file:seek("set", offset)
return file:read(size)
end
local function read_uint(offset, size)
local n, s = 0, read_string(offset, size)
for j = 1, size do
n = n * 256 + s:byte(is_big_endian and j or size + 1 - j)
end
return n
end
local function read_uint32(disp)
return read_uint(exif_offset + disp, 4)
end
local function read_uint16(disp)
return read_uint(exif_offset + disp, 2)
end
local function search_for_tag(ifd_disp, tag)
if ifd_disp ~= 0 then
local entry_disp = ifd_disp + 2
for j = 1, read_uint16(ifd_disp) do
if read_uint16(entry_disp) == tag then
return read_uint32(entry_disp + 8), read_uint32(entry_disp + 4)
end
entry_disp = entry_disp + 12
end
return search_for_tag(read_uint32(entry_disp), tag)
end
end
if read_string(0, 4) == "RIFF" and read_string(8, 4) == "WEBP" then
local max_offset = read_uint32(-8)
while exif_offset < max_offset do
local section_name = read_string(exif_offset, 4)
exif_offset = exif_offset + 8
if section_name == "EXIF" then
local endianness = read_string(exif_offset, 2)
is_big_endian = endianness == "MM"
exif_found = is_big_endian or endianness == "II"
if exif_found then
break
end
end
exif_offset = exif_offset + read_uint32(-4)
exif_offset = exif_offset + exif_offset % 2
end
end
if exif_found then
local exif_ifd = search_for_tag(read_uint32(4), 0x8769)
if exif_ifd then
local disp, count = search_for_tag(exif_ifd, 0x9286)
user_comment = read_string(exif_offset + disp + 8, count - 8)
end
end
file:close()
return user_comment
end
Usage example:
local file_name = "path/to/Johnrogershousemay2020.webp"
print(get_webp_user_comment(file_name))
If all you need is 'UserComment', then pass that as a parameter during your popen call:
local handle = io.popen("exiftool -EXIF:UserComment image.webp")
I have the following lua code snippet (that uses cairo)
cairo_set_source_rgba(cr, COLOR_FONT_R, COLOR_FONT_G, COLOR_FONT_B, 1)
cairo_set_font_size(cr, 0.011 * WINDOW_HEIGHT)
local ps_str = conky_parse("${exec ps -Ao comm,pcpu,%mem --sort=-pcpu | head -n 15}")
local processes = {}
for line in string.gmatch(ps_str, '([^\n]+)') do
table.insert(processes, line)
end
for line = 1,table.getn(processes) do
cairo_move_to(cr, 0.213 * WINDOW_WIDTH, 0.443 * WINDOW_HEIGHT + line * 0.014 * WINDOW_HEIGHT)
cairo_show_text(cr, processes[line])
end
cairo_stroke(cr)
However, when I run it through conky, I get the following error (this is in the line, 5 lines from the end).
I get the error: attempt to call a nil value (field 'getn')
I have tried a few things suggested here, but I am not sure how to fix this so was wondering if there is an easy fix.
The suggested solution in the comments works beautifully for the above but not for the following:
function conky_geo_dotspiral(cx_str, cy_str, ...)
local cx = conky_to_num(cx_str)
local cy = conky_to_num(cy_str)
local arms = math.ceil(24 / table.getn(arg)) * table.getn(arg)
local rows = 10
local radius0, radius1 = 50, 140
local dotradius = 4
for i,v_str in ipairs(arg) do
v = conky_to_num(conky_parse(v_str))
for j = i-1, arms - 1, table.getn(arg) do
local p = j / arms
for k = 0, v / rows do
local dx = cx + (radius0 + (radius1-radius0) * k/rows) * math.cos(p * 2*math.pi + k * math.pi/arms)
local dy = cy + (radius0 + (radius1-radius0) * k/rows) * math.sin(p * 2*math.pi + k * math.pi/arms)
cairo_arc (cr, dx, dy, dotradius, 0, 2 * math.pi)
cairo_fill(cr)
end
end
end
end
I get the error:
attempt to call a nil value (field 'getn')
I tried replacing table.getn(arg) with #arg but still get the error.
conky: llua_do_call: function conky_geo_dotspiral execution failed: conky_geometry.lua:155: attempt to get length of a nil value (global 'arg')
Here is the code snippet, fixed:
cairo_set_source_rgba(cr, COLOR_FONT_R, COLOR_FONT_G, COLOR_FONT_B, 1)
cairo_set_font_size(cr, 0.011 * WINDOW_HEIGHT)
local ps_str = conky_parse("${exec ps -Ao comm,pcpu,%mem --sort=-pcpu | head -n 15}")
local processes = {}
for line in string.gmatch(ps_str, '([^\n]+)') do
table.insert(processes, line)
end
for line = 1,#processes do
cairo_move_to(cr, 0.213 * WINDOW_WIDTH, 0.443 * WINDOW_HEIGHT + line * 0.014 * WINDOW_HEIGHT)
cairo_show_text(cr, processes[line])
end
cairo_stroke(cr)
And the code snippet, fixed for the second question is:
function conky_geo_dotspiral(cx_str, cy_str, ...)
local cx = conky_to_num(cx_str)
local cy = conky_to_num(cy_str)
local arms = math.ceil(24 / #arg) * #arg
local rows = 10
local radius0, radius1 = 50, 140
local dotradius = 4
for i,v_str in ipairs(arg) do
v = conky_to_num(conky_parse(v_str))
for j = i-1, arms - 1, #arg do
local p = j / arms
for k = 0, v / rows do
local dx = cx + (radius0 + (radius1-radius0) * k/rows) * math.cos(p * 2*math.pi + k * math.pi/arms)
local dy = cy + (radius0 + (radius1-radius0) * k/rows) * math.sin(p * 2*math.pi + k * math.pi/arms)
cairo_arc (cr, dx, dy, dotradius, 0, 2 * math.pi)
cairo_fill(cr)
end
end
end
return ''
end
Thanks for all the suggestions that went into fixing this code.
I am trying to perform arithmetic on table values and keep getting an error. Here is my total code. I am basically trying to generate simplex noise. I have created a multidimensional array (table) and am trying to perform operations on the values but i keep getting an error that says I cannot perform arithmetic on a table value. I don't know If I have to convert it to something or what. Please Help.
totalNoiseMap = {}
function simplex_noise(width, height)
simplexnoise = {}
for i = 1,512 do
simplexnoise[i] = {}
for j = 1, 512 do
simplexnoise[i][j] = 0
end
end
frequency = 5.0 / width
for x = 1, width do
for y = 1, height do
simplexnoise[x][y] = noise(x * frequency,y * frequency)
simplexnoise[x][y] = (simplexnoise[x][y] + 1) / 2
end
end
return simplexnoise
end
function noise(x, y, frequency)
return simplex_noise(x / frequency, y / frequency)
end
function generateOctavedSimplexNoise(width,height,octaves,roughness,scale)
totalnoise = {}
for i = 1,512 do
totalnoise[i] = {}
for j = 1, 512 do
totalnoise[i][j] = 0
end
end
layerFrequency = scale
layerWeight = 1
weightSum = 0
for octave = 1, octaves do
for x = 1, width do
for y = 1, height do
totalnoise[x][y] = (totalnoise[x][y] + noise(x * layerFrequency,y * layerFrequency, 2) * layerWeight)
end
end
--Increase variables with each incrementing octave
layerFrequency = layerFrequency * 2
weightSum = weightSum + layerWeight
layerWeight = layerWeight * roughness
end
return totalnoise
end
totalNoiseMap = generateOctavedSimplexNoise(512, 512, 3, 0.4, 0.005)
totalnoise[x][y] + noise(x * layerFrequency,y * layerFrequency, 2) * layerWeight
Here you get table noise(x * layerFrequency,y * layerFrequency, 2), mult it by scalar layerWeight and than add it to scalar totalnoise[x][y].
I can think of how to multiply table by scalar - it should be something like
for i = 1,512 do
for j = 1,512 do
a[i][j] = t[i][j] * scalar
end
end
But I'm unable to get what you're trying to do by adding. Suppose it should be addition of two tables
for i = 1,512 do
for j = 1,512 do
a[i][j] = b[i][j] + c[i][j]
end
end
But it works only with same-sized tables