Metapost Equations

250 views Asked by At

I was given a homework assignment in one of my courses that asked us to google the Metapost language and find the use for the equation solving feature in the language. After going through the first dozen or so pages of the Metapost user manual I found only one reason as to why it's useful and it's that "allows many programs to be written in a largely declarative style." Besides stating that it makes the programming more "declarative" (which from what I understood means that we tell the language what to do as opposed to how to do it) I couldn't think of any other reason why the equations solving is useful. Can anyone help me out?

1

There are 1 answers

0
Gassa On

Here is an illustration of how solving equations in MetaPost — and declarative programming, for that matter — might be useful.

Suppose we want to draw a die:

pic 0

To do that, let us first define a macro which will draw a single face of the die: a square with number s on it.

def face (expr s) = image (begingroup
    pickup pencircle scaled 1pt;
    draw (0.5, 0.5) -- (0.5, 9.5) -- (9.5, 9.5) -- (9.5, 0.5) -- cycle;
    label (s, (5, 5));
endgroup) scaled 10 enddef;

Now we can draw it and get the picture:

draw face ("1");

pic 1

Next, we need an upper face and a right face. To draw them, we will have to compose an affine transformation to skew them. This can be tricky since the only readily available primitive transformation for skewing is slanted a which transforms a point (x, y) into (x + ay, y). Here is our picture slanted 1:

draw face ("2") slanted 1;

pic 2

We will then (or rather, before that) have to scale by one of the coordinates:

draw face ("2") yscaled 0.35 slanted 1;

pic 3

The same approach does not work for the third face right away:

draw face ("3") xscaled 0.35 slanted 1;

pic 4

After a bit of experimentation, we find the right code:

draw face ("3") rotated 90 yscaled 0.35 slanted -1 rotated -90;

pic 5

But why all the tedium? We know exactly what transformation we need. One natural way to express it is by using primitives. But if that proves unintuitive, as our last line was, it may be more comfortable to just specify which points of the plane transform to which.

transform t;
(0, 0) transformed t = (0, 0);
(0, 1) transformed t = (0.35, 0.35);
(1, 0) transformed t = (1, 0);
draw face ("3") transformed t;

This basically tells MetaPost: there is a transform t, under which the three points we specified move to the other three points we specified. Turns out this uniquely determines a plane transformation, and we get the same picture:

pic 6

Putting all that together (the code is beginfig (7) at end of post) allows us to finally see our die:

pic 7

In this simple case, the “coordinates and equations” approach is comparable in difficulty to the “primitive transformations” approach. Now, imagine we wanted a slight tilt for our cube. With the same declarative approach, it would be still possible without invoking three-dimensional geometry (the code is beginfig (8) at end of post):

pic 8


The complete program is below.

prologues := 3;

def face (expr s) = image (begingroup
    pickup pencircle scaled 1pt;
    draw (0.5, 0.5) -- (0.5, 9.5) -- (9.5, 9.5) -- (9.5, 0.5) -- cycle;
    label (s, (5, 5));
endgroup) scaled 10 enddef;

beginfig (1)
    draw face ("1");
endfig;

beginfig (2)
    draw face ("2") slanted 1;
endfig;

beginfig (3)
    draw face ("2") yscaled 0.35 slanted 1;
endfig;

beginfig (4)
    draw face ("3") xscaled 0.35 slanted 1;
endfig;

beginfig (5)
    draw face ("3") rotated 90 yscaled 0.35 slanted -1 rotated -90;
endfig;

beginfig (6)
    transform t;
    (0, 0) transformed t = (0, 0);
    (0, 1) transformed t = (0, 1);
    (1, 0) transformed t = (0.35, 0.35);
    draw face ("3") transformed t;
endfig;

beginfig (7)
    transform t [];

    draw face ("1");

    (0, 0) transformed t[1] = (0, 0);
    (0, 1) transformed t[1] = (0.35, 0.35);
    (1, 0) transformed t[1] = (1, 0);
    draw face ("2") transformed t[1] shifted (0, 100);

    (0, 0) transformed t[2] = (0, 0);
    (0, 1) transformed t[2] = (0, 1);
    (1, 0) transformed t[2] = (0.35, 0.35);
    draw face ("3") transformed t[2] shifted (100, 0);
endfig;

beginfig (8)
    transform t [];
    pair Ox, Oy, Oz;
    Ox = (0.86, -0.21);
    Oy = (0.21, 0.86);
    Oz = (0.29, 0.44);

    (0, 0) transformed t[1] = (0, 0);
    (1, 0) transformed t[1] = Ox;
    (0, 1) transformed t[1] = Oy;
    draw face ("4") transformed t[1];

    (0, 0) transformed t[2] = (0, 0);
    (1, 0) transformed t[2] = Ox;
    (0, 1) transformed t[2] = Oz;
    draw face ("5") transformed t[2] shifted (Oy scaled 100);

    (0, 0) transformed t[3] = (0, 0);
    (1, 0) transformed t[3] = Oz;
    (0, 1) transformed t[3] = Oy;
    draw face ("6") transformed t[3] shifted (Ox scaled 100);
endfig;

end