1

I first create ten points p[1],..., p[10] in MetaPost. Afterward, I use the following loop:

for i = 1 upto 10:

   dotlabel.bot(btex i etex, p[i]);

endfor

then it creates in the pdf file 10 letters 'i' below 10 point p[i]. But I want ten numbers from 1 to 10 below ten points p[i].

I do not know how to do that. Please help me.

5
  • Welcome to TeX SE. Please edit your post so that your example will be a MWE that can be compiled as it is: add the \starttext\stoptext and the piece of code creating your points.
    – sztruks
    Apr 9, 2022 at 9:45
  • I will add a proper answer later, but have a look at sections 11.2.4 and 12.5 of my notes on Drawing with Metapost.
    – Thruston
    Apr 9, 2022 at 10:24
  • decimal i might be a simple solution to your problem.
    – mickep
    Apr 9, 2022 at 10:34
  • @Thruston Your documents are undoubtedly wonderful! Thank you! Apr 9, 2022 at 11:04
  • @mickep It works correctly and easily. Thank you! Apr 9, 2022 at 11:08

2 Answers 2

2

About dynamic labels in Metapost

The label macro is defined, in plain.mp, to take two arguments

  1. a string or picture expression (but nothing else)
  2. a pair expression

It will draw the string or picture neatly at the point defined by the pair expression. So you can do

label("Hello", origin);

to get "Hello" centered on the point (0, 0).

The way this works is (simplified):

vardef label(expr s,z) =
  save p; picture p;
  p = s if string s: infont defaultfont fi;
  ... positioning code ...
enddef;

So that, if the first argument s is a string, then the macro turns it into a picture p first.

So the question is, how do I generate a string or a picture dynamically in a loop?

Simple labels in the default font

The simplest way to turn a numeric variable into a string is to use decimal

for i = 1 upto 10:
    label(decimal i, (20i, 0));
endfor

or perhaps you want them in a particular font other than the default.

for i = 1 upto 10:
    label(decimal i infont "phvr8r", (20i, 0));
endfor

Now you are sending a picture to label each time.

Dynamic labels using TeX

You can't generate dynamic labels directly using btex ... etex. This is because btex and etex act like a special pair of quote marks that turn a list of tokens into a picture variable, but MP does not expand any of the tokens inside btex ... etex before processing them with TeX. So if you try this

for i = 1 upto 10:
    label(btex i etex, (20i, 0));
endfor

You get no errors but each label is an "i". To get round this, you can use the TEX macro.

input TEX
for i = 1 upto 10:
    label(TEX(decimal i), (20i, 0));
endfor

the TEX macro takes a regular string and passes it out to TeX and then returns the resulting picture. This means you can do things like

input TEX
for i = 1 upto 10:
    label(TEX("$p_{" & decimal i & "}$"), (20i, 0));
endfor

because MP will expand the tokens before it passes them to the TEX macro. In this case there are three strings to concatenate.

Faster dynamic labels with TeX

The only problem with the TEX macro is that it is very slow. Because it writes each label out to a temporary file. At the cost of some low level fiddling with write you can speed it up like this.

for i = 1 upto 10:
    write "label(btex $p_{" & decimal i & "}$ etex, (" & decimal 20i & ",0));" to ".mplabels";
endfor
write EOF to ".mplabels";
input ".mplabels";

What this does is write ten lines to a temporary local file:

label(btex $p_{1}$ etex, (20, 0));
label(btex $p_{2}$ etex, (40, 0));
label(btex $p_{3}$ etex, (60, 0));
... etc ...

and then read them all back in and process them in one go.

Modern, fast dynamic labels

But actually you don't need to do any of that any more. Instead you should be using lualatex and luamplib instead. Use a template like this, and process it with lualatex instead of mpost.

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}  % <--- this is important
\begin{mplibcode}
beginfig(1);

endfig;
\end{mplibcode}
\end{document}

Then you can do your dynamic labels at high speed like this

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
beginfig(1);
    for i = 1 upto 10:
        label("$p_{" & decimal i & "}$", (20i, 0));
    endfor
endfig;
\end{mplibcode}
\end{document}

which produces this (when compiled with lualatex)

enter image description here

2
  • Thank you so much! Apr 12, 2022 at 3:59
  • 1
    If one wants to stick to MetaPost without Lua(La)Tex, there is also the latexmpMetaPost package which has its own textext macro, and a textextlabel=enable option. The only caveat is that a double compilation is needed to produce and include the labels. Slower than LuaLaTeX, but much quicker than the TEX macro. Apr 22, 2022 at 15:56
0

Your error is connected to using btex etex. This answer will work:

\starttext
\startMPpage
path p[];
for i = 1 upto 10:
   p[i]:= (10i,0);
   dotlabel.bot(i, p[i]);
endfor
%variables

\stopMPpage
\stoptext

BTW, please give us a fully compilable MWE, it will increase your chances of getting any answers, since we won't have to do any additional work.

1
  • 1
    Note that this works in Context because the Metafun version of the label macro is a bit smarter than the macro in plain MP. In plain MP you will get a "Not implemented" error if you pass a numeric to label (or `dotlabel), but the Metafun version will automatically convert a numeric into a string and then into a picture to be drawn.
    – Thruston
    Apr 10, 2022 at 16:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .