3

How can I automatically convert a number inside a metafun loop? I tried using MPvar but this doesn't work.

\starttext
\startMPpage
for i=1 upto 10:
   label (i,(10i,0));
   label ("\convertnumber{R}{\MPvar{i}}",(10i,30));
endfor;
\stopMPpage
\stoptext

I know I could create an array with Roman numerals and use this answer to do it, but knowing a way to do it automatically would be helpful.

EDIT I did not tag this question as metapost but I'll change this if it may be usefull to LaTeX+METAPOST users

sztruks
  • 3,074
  • 1
    I note that you cannot simply do label("\convertnumber{R}{" & decimal i & "}", (x,y)); as you could with lualatex because Context adds a lot of clever processing of the argument to label, so that the TeX is called on the argument apparently before the normal MP macro argument expansion takes place. This seems to me to be a bug in Context. – Thruston Jan 18 '21 at 14:40
  • 1
    @Thruston I think it's on purpose, because some TeX elements, e.g. conditionals, are used inside MP environments to hide/expose code before it's passed to MetaPost. I proposed a solution using Lua because ConTeXt fully exploits it, but asking on the mailing list whether or not it's a bug in this concrete case wouldn't harm. –  Jan 18 '21 at 17:16
  • 1
    If it really is on purpose then I think that is poor design. It completely changes the MP rule that arguments to macros should be completely expanded before they are used. I shall raise it elsewhere as @JairoA.delRio suggests. – Thruston Jan 18 '21 at 22:20

2 Answers2

6

If you don't want to reinvent the wheel, ConTeXt provides Lua bindings for Metafun, so you have at least two ways to make your conversion in Lua in order to avoid issues with TeX macros:

Directly calling Lua to make a conversion

Since you're on ConTeXt, use some Lua magic instead. In order to call a Lua function directly from MetaPost, you could use the lua spelling and invoke converters.Romannumerals for uppercase and converters.romannumerals for lowercase variants (the complete list of conversions is at core-con.lua):

%Modified original so numbers don't overlap
\starttext
\startMPpage
for i=1 step 7 until 50:
   label (i,(4i,0));
   label (lua("mp.string(converters.romannumerals(" & decimal i & "))"),(4i,30));
endfor;
\stopMPpage
\startMPpage
for i=1 step 9 until 100:
   label (i,(0,-1.5i));
   label (lua("mp.string(converters.Romannumerals(" & decimal i & "))"),(30,-1.5i));
endfor;
\stopMPpage
\stoptext

First page:

enter image description here

Second page:

enter image description here

Defining Metafun functions in Lua

However, if either calling Lua turns out to be verbose and confusing or you want to use a roman definition as @Marijn did (or both), the following is possible and clearer IMHO:

\startluacode
    --MP is a namespace reserved for Metafun functions
    --This will be lua.MP.roman in Metafun
    function MP.roman(n)
    --You could use many others, but you shouldn't forget to
    --send results to Metafun via mp.string or related functions.
        mp.string(converters.Romannumerals(n))
    end
\stopluacode
\startMPpage
vardef roman primary n =
    lua.MP.roman(n)
enddef;
for i=1 step 37 until 1000:
   label (i,(0,-i/3));
   label (roman i,(50,-i/3));
endfor;
\stopMPpage
\stoptext

enter image description here

  • Great! I didn't know about this lua calls (a lmtx feature?) may be very usefull for further works. Could you add one explainer regarding those decimal inside the lua calls? – sztruks Jan 17 '21 at 18:11
  • 1
    It's explained in the Metafun manual (chapter 15). It's available since ConTeXt MkIV (LuaTeX) and, indeed, it's very useful as it is roughly the Metafun equivalent of \ctxlua/\directlua. Glad it helped :) –  Jan 17 '21 at 18:15
  • 1
    I read this chapter some while ago, but did not figure this out. Thanks for the hint. – sztruks Jan 17 '21 at 18:17
  • 1
    Ah, sorry, I missed your comment on decimal. It's from MetaPost (I don't remember if a primitive or a macro, mentioned in MetaPost manual section 6.3) to convert numbers to strings, e.g., numeric age; age := 26; label("I'm " & decimal age & " years old", origin);. You're passing a string to lua, so decimal is mandatory in this case. –  Jan 17 '21 at 18:34
4

Starting from the conversion function from https://tex.stackexchange.com/a/237400/ I defined a recursive conversion function for roman numerals:

\starttext
\startMPpage
vardef roman primary h = 
   if     h=0: ""
   elseif h<4: "I" & roman (h-1)
   elseif h=4: "IV"
   elseif h<9: "V" & roman (h-5)
   elseif h=9: "IX"
   elseif h<40: "X" & roman (h-10)
   elseif h<50: "XL" & roman (h-40)
   elseif h<90: "L" & roman (h-50)
   elseif h<100: "XC" & roman (h-90)
   elseif h<400: "C" & roman (h-100)
   elseif h<500: "CD" & roman (h-400)
   elseif h<900: "D" & roman (h-500)
   elseif h<1000: "CM" & roman (h-900)
   else: "M" & roman (h-1000)
   fi
enddef;
for i=1 step 9 until 109:
   label (decimal i & "\ " & roman i,(0,10-10i/9));
endfor;
\stopMPpage
\stoptext

enter image description here

Marijn
  • 37,699
  • This would be indeed slightly better than a array filled by hand. However it should be if h<4 and elseif h=4: "IV" – sztruks Jan 17 '21 at 18:11
  • @sztruks I agree, the conversion function needs some improvement, I just copied it as-is from the other answer. However I do think that it would be easy to correct and extend the code to cover larger numbers (the original solution was for a clock, so 12 was the maximum required value), and then it would arguably be as general and easy to use as \convertnumber or converters.romannumerals. – Marijn Jan 17 '21 at 18:24
  • @sztruks I went ahead and extended the function. – Marijn Jan 17 '21 at 20:14
  • @sztruks my original roman macro from the linked answer was for a clock face, where 4 is traditionally given as IIII – Thruston Jan 18 '21 at 13:37
  • @Thruston Google gives a mixed result: when searching for https://www.google.com/search?q=clock+roman+numerals&tbm=isch I see both IIII and IV being used on clocks, but IV seems a bit more common. – Marijn Jan 18 '21 at 14:05
  • 1
    @marijn that's interesting. I was defining "traditionally" as "what I can see on the clock over my fireplace" :-). – Thruston Jan 18 '21 at 14:36