It is currently October 25th, 2021, 6:24 am

Some useful function in lua

Discuss the use of Lua in Script measures.
User avatar
death.crafter
Posts: 1065
Joined: April 24th, 2021, 8:13 pm

Re: Some useful function in lua

Post by death.crafter »

P.S. not related to toolkit but why don't SKIN object or dofile work if not inside a function block?
from the Realm of Death
My Current Desktop
User avatar
Yincognito
Rainmeter Sage
Posts: 4070
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Some useful function in lua

Post by Yincognito »

jsmorley wrote: August 22nd, 2021, 1:28 amNo need to duplicate any of the code from the toolkit into the active script.
Yeah, that was what I was thinking as well. If the function will be included in death.crafter's Functions.lua, then having the function again in the script will likely produce either some conflicts, or confusion at best.
death.crafter wrote: August 22nd, 2021, 1:29 amActually I went through smurfier's Lua Calender's lua and thought it might be a good idea to centralize some really useful functions that are used a lot.
[...]
I could include Rainmeter docs functions too I guess. Just one dofile and you are ready for some serious automation, without going over to the docs again and again
I was thinking the same thing. :D I was wondering if it would be feasible for the calendar itself to be added, but I reckon there will be too many specifics to take care of in that situation. I'm all for the inclusion of WriteINI snippet in your Functions.lua though. I couldn't fully understand it at the time, but I ended up doing a very similar thing in my savegame editor Lua script. With some more generalized form, I guess it could work as a rudimentary JSON parser as well (which is yet another idea, already implemented by someone somewhere, if I'm not mistaken).
User avatar
death.crafter
Posts: 1065
Joined: April 24th, 2021, 8:13 pm

Re: Some useful function in lua

Post by death.crafter »

Yincognito wrote: August 22nd, 2021, 12:27 am This or this seem to disagree... :???:
Oh... I thought of iterating through files and stuff, which us beyond the scope if vanilla lua, given io.popen is not available.

Well, that wouldn't be a commonly useful thing, but I guess I should include it so that its not lost to the flow of time :D

Also thanks for the currentsectionindex function.
from the Realm of Death
My Current Desktop
User avatar
jsmorley
Developer
Posts: 22374
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Some useful function in lua

Post by jsmorley »

death.crafter wrote: August 22nd, 2021, 1:31 am P.S. not related to toolkit but why don't SKIN object or dofile work if not inside a function block?
Not sure, it's just a "feature" of how we built the "glue" between Lua and Rainmeter.

Best to put the dofile in function Initialize() really. It doesn't have to be there, it can be in some other function, but you want to be sure you are loading it before anything in it is "used", and not loading it repeatedly. No need for that.
User avatar
death.crafter
Posts: 1065
Joined: April 24th, 2021, 8:13 pm

Re: Some useful function in lua

Post by death.crafter »

Yincognito wrote: August 22nd, 2021, 1:41 am I'm all for the inclusion of WriteINI snippet in your Functions.lua though. I couldn't fully understand it at the time, but I ended up doing a very similar thing in my savegame editor Lua script. With some more generalized form, I guess it could work as a rudimentary JSON parser as well (which is yet another idea, already implemented by someone somewhere, if I'm not mistaken).
The WriteINI function in the docs is tbh flawed. Why? Because tables in lua can't be iterated in order if not indexed using integers starting from 1. So you end up having a random list of unorderd sections(this matters, a lot) with unordered keys(doesn't matter but looks super weird).

I have my workaround but it needs some setup to be done. If you look at the ReadINI in this one, I have made a table of sections, section order and key order of each section, so you can iterate through them easily when needed. When using my version of WriteINI, you have to create a input table similar to the one ReadINI returns, so that WriteINI can put the things in order.

So, since it needs a little bit of more explanation, I will put it in a separate thread, along with ReadINI.
from the Realm of Death
My Current Desktop
User avatar
death.crafter
Posts: 1065
Joined: April 24th, 2021, 8:13 pm

Re: Some useful function in lua

Post by death.crafter »

jsmorley wrote: August 22nd, 2021, 1:46 am Not sure, it's just a "feature" of how we built the "glue" between Lua and Rainmeter.

Best to put the dofile in function Initialize() really. It doesn't have to be there, it can be in some other function, but you want to be sure you are loading it before anything in it is "used", and not loading it repeatedly. No need for that.
Yup. Well, doesn't really matter much since you can easily create a global variable in lua without a hassle.
from the Realm of Death
My Current Desktop
User avatar
Yincognito
Rainmeter Sage
Posts: 4070
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Some useful function in lua

Post by Yincognito »

death.crafter wrote: August 22nd, 2021, 1:52 am The WriteINI function in the docs is tbh flawed. Why? Because tables in lua can't be iterated in order if not indexed using integers starting from 1. So you end up having a random list of unorderd sections(this matters, a lot) with unordered keys(doesn't matter but looks super weird).

I have my workaround but it needs some setup to be done. If you look at the ReadINI in this one, I have made a table of sections, section order and key order of each section, so you can iterate through them easily when needed. When using my version of WriteINI, you have to create a input table similar to the one ReadINI returns, so that WriteINI can put the things in order.

So, since it needs a little bit of more explanation, I will put it in a separate thread, along with ReadINI.
Ah, yes, now I remember, I believe I had the same problem when loking at that code. Personally, I used specific table array addressing (i.e. no pairs()), like (posted just for reference, disregard the unknowns in my savegame code):

Code: Select all

function SaveAutosave(outputfile)
  if not decryptedautosave then return true end
  local st = os.clock()
  local lines = {}
  for gi = 1, #items do
    lines[#lines + 1] = ('%s\n{'):format(items[gi][1])
    for ci = 1, #items[gi][2] do
      lines[#lines + 1] = ('%s {'):format(items[gi][2][ci][1])
      for fi = 1, #items[gi][2][ci][2] do
        for vi = 1, #items[gi][2][ci][2][fi][2][1] do
          lines[#lines + 1] = (' %s: %s'):format(items[gi][2][ci][2][fi][1], items[gi][2][ci][2][fi][2][vi][1])
        end
      end
      lines[#lines+1] = ('%s'):format('}\n')
    end
    if gi == #items then lines[#lines+1] = ('%s'):format('}') else lines[#lines+1] = ('%s'):format('}\n') end
  end
  local file = assert(io.open(outputfile, 'w'), 'ETS2 Autosave Editor: unable to save ' .. outputfile)
  file:write(table.concat(lines, '\n'))
  file:close()
  local et = os.clock()
  print('ETS2 Autosave Editor: saved autosave ' .. outputfile .. ' (' .. #lines .. ' lines)' .. string.format('; time = %.5f', et-st) .. ' s')
  return true
end
That, if I understood what you said correctly, of course. Only numerical indexing is retrieved in order in Lua, as far as I recall, while for pairs() key order is unspecified.
User avatar
jsmorley
Developer
Posts: 22374
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Some useful function in lua

Post by jsmorley »

This is a little toolkit dofile I put together a long time ago...

Code: Select all

function AddSuffix(num) -- Returns the ordinal suffix for an input number.
	local suffix = ''
	local n = num % 10
	if (num - n) == 10 then
		suffix = 'th'
	else
		suffix = (n == 1 and 'st' or n == 2 and 'nd' or n == 3 and 'rd' or 'th')
	end
	return suffix
end

function FormatCommas(numVar) -- Formats numbers with commas as the thousands separator.
	local prefix, number, postfix = string.match(numVar, '^([^%d]*%d)(%d*)(.-)$')
	return prefix..(number:reverse():gsub('(%d%d%d)', '%1,'):reverse())..postfix
end

function ConvertToHex(color) -- Converts RGB colors to HEX
	local hex = {}
	for rgb in color:gmatch('%d+') do
		table.insert(hex, ('%02X'):format(tonumber(rgb)))
	end
	return table.concat(hex)
end

function ConvertToRGB(color) -- Converts HEX colors to RGB
	local rgb = {}
	for hex in color:gmatch('..') do
		table.insert(rgb, tonumber(hex, 16))
	end
	return table.concat(rgb, ',')
end

function ConvertTemp(num, toScale) -- Converts between Celsius and Fahrenheit.
	local outTemp = 0
	if toScale == 'c' then
		outTemp = Round((5 / 9) * (num - 32))
	else
		outTemp = Round((9 / 5) * num + 32)
	end
	return outTemp
end

function AutoScale(num, idp) -- Autoscales a number and adds the scale prefix.
	local scales = {'B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'}
	local places = idp or 0
	local scale = ""
	local scaled = 0
	for i, v in ipairs(scales) do
		if (num < (1024 ^ i)) or (i == #scales) then
			scale = v
			scaled = Round(num / 1024 ^ (i - 1), places)
			break
		end
	end
	return scaled..' '..scale
end

function TitleCase(input) -- Capitalizes the first character of a string.
	return (input:gsub('(%S)(%S*)', function(first, rest) return first:upper() .. rest:lower() end))
end

function SentenceCase(input) -- Capitalizes the first character of each word in a string.
	return (input:gsub('[^.!?]+', function(sentence)
			local space, first, rest = sentence:match('(%s*)(.)(.*)')
			return space .. first:upper() .. rest:lower():gsub("%si([%s'])", ' I%1')
		end))
end

function ParseTwelveHourTime(Hour, Meridiem) -- Takes a number (1-12) and a string ('AM' or 'PM') and returns the equivalent 24-hour figure.
	if (string.upper(Meridiem) == 'AM') and (Hour == 12) then
		Hour = 0
	elseif (string.upper(Meridiem) == 'PM') and (Hour < 12) then
		Hour = Hour + 12
	end
	return Hour
end

function RightPad(str, len, char) --- Pads string str to length len with character char to the right.
	if char == nil then char = ' ' end
		return str .. string.rep(char, len - #str)
	end

function LeftPad(str, len, char) --- Pads string str to length len with character char to the left
	if char == nil then char = ' ' end
		return string.rep(char, len - #str) .. str
	end
	
function SplitPath(pathArg, pathBit) -- Splits a full path\filename.ext into component parts.
	path, file, ext = string.match(pathArg, '(.-)([^\\/]-%.?([^%.\\/]*))$')
	if string.upper(pathBit) == 'PATH' then
		return path
	elseif string.upper(pathBit) == 'FILE' then
		return file
	elseif string.upper(pathBit) == 'EXT' then
		return ext
	else
		return 'Invalid component'		
	end
end

function SplitLine(line, delimiter) -- Separate a string into a table by single character delimiter.
	local tbl = {}
	for word in line:gmatch('[^%' .. delimiter .. ']+') do
		table.insert(tbl, word)
	end
	return tbl
end

function SortRandom(tableName) -- Sorts a given table into a random order.
	math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6)))
	local temp, newTable = {}, {}
	for _, v in pairs(tableName) do
		table.insert(newTable, v)
	end
	while #newTable > 0 do
		table.insert(temp, table.remove(newTable, math.random(1, #newTable)))
	end
	return temp
end


-- Utility functions

function Round(num, idp) -- Rounds a number to an integer (default) or specified places.
	local mult = 10 ^ (idp or 0)
	if num >= 0 then
		return math.floor(num * mult + 0.5) / mult
	else
		return math.ceil(num * mult - 0.5) / mult
	end
end
User avatar
death.crafter
Posts: 1065
Joined: April 24th, 2021, 8:13 pm

Re: Some useful function in lua

Post by death.crafter »

jsmorley wrote: August 22nd, 2021, 10:45 am This is a little toolkit dofile I put together a long time ago...

Thanks!
You saved me the trouble of copy pasting docs snippets :D
from the Realm of Death
My Current Desktop
User avatar
smurfier
Moderator
Posts: 1930
Joined: January 29th, 2010, 1:43 am
Location: Willmar, MN

Re: Some useful function in lua

Post by smurfier »

Just for posterity's sake, here's my scary math version of the AutoScale function. I'm retyping this by converting an old Rainmeter post in Lua, so bear with me if there are typos.

Code: Select all

function AutoScale(num, idp)
	local scales = {'B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'}
	local scale = (num == 0) ? 1 : math.ceil(math.log(num) / (10 * math.log(2)))
	local scaled = Round((num / 1024 ^ (scale - 1)), idp or 0)
	return scaled .. ' ' .. scales[scale]
end
GitHub | DeviantArt | Tumblr
This is the song that never ends. It just goes on and on my friends. Some people started singing it not knowing what it was, and they'll continue singing it forever just because . . .