sbrl / Minetest-WorldEditAdditions

Extra tools and commands to extend WorldEdit for Minetest
https://worldeditadditions.mooncarrot.space/
Mozilla Public License 2.0
16 stars 3 forks source link

//count: align text in chat window #14

Closed sbrl closed 3 years ago

sbrl commented 4 years ago

I've just implemented a new //count command, as I'm aiming to bring WorldEdit + WorldEditAdditions up to parity with this worldedit reference such that anything listed there that WorldEdit for Minecraft does you can with Minetest - even if it requires multiple or a more complex command to do so.

Unfortunately, despite the code I've written correctly aligning everything so that it looks nice in a table, this isn't the case in the chat window:

Minetest 5 2 0  OpenGL 3 0 _046

....this is because the chat window is not a monospace font. The solution here would normally be to measure the text and measure the width of a space and adjust for it, but I haven't yet located a method in the Minetest API that lets me do this.

Another solution would be to display it in the chat window with a monospace font, but again I don't think that this is possible.

I've tried to mitigate the worst of the effects by putting the numbers in the first 2 columns (as pictured), but it isn't ideal.

If we can solve this, we could also then print a histogram in dots at the end of each line of output :chart_with_upwards_trend: :smiley:

With that, we could also apply it to counting a specific item by layer too and returning an ascii art histogram showing the value at different levels.

Input would be greatly appreciated on this issue, as I'm stumped as to what to do about it.

Bastrabun commented 4 years ago

No need for ascii art if you make a formspec out of it. This had the added benefit of being copy&paste-able.

sbrl commented 4 years ago

Ah, interesting idea. If you've got modding experience, do you know of a guide or tutorial about formspecs? That would be very helpful, since I've never actually used a Minetest formspec before.

Bastrabun commented 4 years ago

I made my formspecs with a formspec editor:

https://ca.xeroxirc.net/formspec-editor/

sbrl commented 4 years ago

Hey, thanks! However, I don't see any elements there that would allow me to align a table of text.

Looking into it, the modding API has a table that you can use, but I don't see it present in that tool.

It sounds like there's going to be lots of work involved in this - I'll have to do some experimenting.

VorTechnix commented 3 years ago

2021-01-17 07_40_22-Minetest 5 3 0  OpenGL 4 6 0 Whatever you did seems to work over here. One suggestion would be to use engineering notation (exponents that are multiples of 3) for for numbers greater than 99'999. So for example:

100'000 => 100E3
4'321'000 => 4.32E6
48'321'000 => 48.3E6
8'743'593'000'000'000'000'000 => 8.7E21

As long as you make sure that the decimal precision shortens as the E value increases in length this should keep things nicely tidy. Also you could consider separating the columns with | like this: 8.74E21 |87.62% |air

sbrl commented 3 years ago

Ah yeah, using a monospace font for the chat window will solve the issue. Nice!

Oh, that's an interesting idea. The 2E3 syntax takes more effort for me to parse though, so I would suggest using K, M, G, etc would be a better choice here. Still, we should definitely look into that.

VorTechnix commented 3 years ago

Sorry for the delay... I had to teach myself some intermediate lua.

function luatools.Eshort (_n)
  if _n:len() > 102 then  -- This is to prevent 3 digit E values
    return "Number Too Big"
  elseif _n:len() > 5 then
    _e = math.floor((_n:len()-1)/3)*3;
    int = _n:sub(0,-_e-1); suf = "E".._e;
    frac = _n:sub(int:len()+1, 4-int:len()+int:len())
    return int .. "." .. frac .. suf
  else return _n
  end
end

Here is an engineering notation conversion function if you want it. As far as parsing goes K, M, G etc would seem to me to be harder to implement than E## notation as you would need an array of suffixes.

sbrl commented 3 years ago

Thanks, @VorTechnix! It doesn't seem to work for me though. Using this test program:

function Eshort (_n)
  if _n:len() > 102 then  -- This is to prevent 3 digit E values
    return "Number Too Big"
  elseif _n:len() > 5 then
    _e = math.floor((_n:len()-1)/3)*3;
    int = _n:sub(0,-_e-1); suf = "E".._e;
    frac = _n:sub(int:len()+1, 4-int:len()+int:len())
    return int .. "." .. frac .. suf
  else return _n
  end
end

print(Eshort(3000000))

...I get this error:

lua: /tmp/test.lua:2: attempt to index a number value (local '_n')
stack traceback:
    /tmp/test.lua:2: in function 'Eshort'
    /tmp/test.lua:14: in main chunk
    [C]: in ?

Yeah, you're right about needing an extra table there. I've seen something done before which I lifted in PHP and then later ported to Javascript here: https://github.com/sbrl/Pepperminty-Wiki/blob/0a81c940c5803856db250b29f54658476bc81e21/core/05-functions.php#L67

Porting this to Lua shouldn't be too hard at a quick glance - only we'll want to use SI units here (base 1000) instead of base 1024 - and we'll want to use K, G, T instead KiB, GiB, TiB.

sbrl commented 3 years ago

This should do the trick:

function human_size(n, decimals)
    sizes = { "", "K", "M", "G", "T", "P", "E", "Y", "Z" }
    local factor = math.floor((#tostring(n) - 1) / 3)
    local multiplier = 10^(decimals or 0)
    local result = math.floor(0.5 + (n / math.pow(1000, factor)) * multiplier) / multiplier
    return result .. sizes[factor+1]
end

Now I just need to put it to use.

VorTechnix commented 3 years ago

Thanks, @VorTechnix! It doesn't seem to work for me though. Using this test program:

function Eshort (_n)
  if _n:len() > 102 then  -- This is to prevent 3 digit E values
    return "Number Too Big"
  elseif _n:len() > 5 then
    _e = math.floor((_n:len()-1)/3)*3;
    int = _n:sub(0,-_e-1); suf = "E".._e;
    frac = _n:sub(int:len()+1, 4-int:len()+int:len())
    return int .. "." .. frac .. suf
  else return _n
  end
end

print(Eshort(3000000))

...I get this error:

lua: /tmp/test.lua:2: attempt to index a number value (local '_n')
stack traceback:
  /tmp/test.lua:2: in function 'Eshort'
  /tmp/test.lua:14: in main chunk
  [C]: in ?

The reason your test program didn't work is that you said print(Eshort(3000000)). Eshort takes string input so it should have been print(Eshort('3000000')). Minetest has a feature where it shortens numbers >= 100000000000000 to E values by default. The only way I could think of to get around that was to convert to string first.

sbrl commented 3 years ago

Ah, I see. But the numbers would be coming in from the rest of the program as numerical values instead of strings anyway, so it doesn't make much different :P

Thanks for the help!

sbrl commented 3 years ago

Implemented.