Prev Up Top Next Contents

7.A MetaPost

This appendix describes the most important MetaPost commands [13] .
MethaPost files are text files, like therion or survex files, with commands for MetaPost, just like therion files contain commands for therion and survex files contain commands for survex. To work with metapost is therefore enough to write text file (with extension ".mp" by default) and input them to mpost. The outcome are little PostScript files that can be viewed with any PostScript viewer (for example, with ghostscript).
Any command is terminated by ';'. The character '%' starts a comment. The comment continues till the end of the line.
The metapost commands specify how to draw the figure(s), therefore they are mostly related to line drawing. MetaPost has nine data types:
In MetaPost you can define arrays, for instance "path p[];" and then refer to its elements as "p0", "p1" etc. Index with decimal part are allowed: "p1.2". The elements of an array can be indexed with subscript too: "p.a". Multidimensional array are supported: "path p[]q[], rs[][];". An element of the first could be "p2q3", one of the second "rs1 5".

7.A.1 Boolean

A boolean variable may be used as parameter in a function to determine its behaviour. For example
   % warning is called with filled=true, p must be a cycle
   vardef draw_or_fill(expr p, filled) =
     if filled: fill p else: draw p fi
   enddef;

7.A.2 Pens

MetaPost has a few predefined pens: pencircle", "pensquare", and "penrazor". The variable "currentpen" refers to the pen in use. The variable "defaultpen" is the default pen.
The operator "makepen" takes as parameter a path and produces a pen, with shape the convex hull of the path. The inverse operator is "makepath" that, given a pen, produces the path contour of the pen. The composition of the two operators is the convex hull of the path.

7.A.3 Colors

A color is composed of three numbers (red, green, and blue) betweeen 0 and 1. There are also predefined colors that can be referred to by the name: "black", "white", "red", "green" and "blue". A grey color can be defined as a fraction of the white: "0.4 white".
To get the components of a color there are the operators "redpart", "greenpart" and "bluepart".

7.A.4 Trasformations

A transformation has six parameters
    X' = tx + txx X + txy Y
    Y' = ty + tyx X + tyy Y
The transformations are applied in the order they are read, from the left to the right. For instance p rotated R shifted S; says that the path "p" is first rotated by "R" then trasnlated by "S". The transformations can be composed to form new (complex) ones. A particular transformation is the "identity". A roto-translation can be defined as
    transform T; T = identity rotated 60 shifted (2.0, 3.0);
There are a few operators that define transformations:
If "T" is a transformation, "inverse T" is its inverse transformation. To get the individual parameters of a transformation there are the operators "xpart", "xxpart", "xypart", etc. Finally it is possible to define a transformation specifying how it acts on three points, ie, with a system of three linear equations.

7.A.5 Numbers and points

Points and lines must be expressed with a units. If it is not specified, MetaPost uses typographics points (1/72 inch). One can use cm, mm, ... or a unit "u" can be defined, as therion does, and everything can be expressed in terms of "u".
A point is a pair (x,y) of coordinates. MetaPost has a convention to refer to the points: pairs of coordinates have name with prefix "z"; their x and y components have names with prefixes "x" and "y", respecively. For example,
    z1:=(1.2u, 0.5u);
implies that "x1" is 1.2u and "y1" is 0.5u.
Numbers and points can be defined through a system of linear equations. This is convenient for geometric drawings to refer to points of intersection of lines. For example
  z0 = 1/3[z1,z2];
defines the point Z0 = (2 Z1 + Z2)/3. Another example,
  a + b = 3;
  a - b = 1;
defines "a" and "b" through a system of two linear equations ("a" is 2, "b" is 1).
The operators "xpart" and ypart" refer to the components of a point.
A 2D vector is represented as a pair. The operator "unitvector" applied to a pair, produces the unit length vector in the direction of the pair. The operator "lft", "rt", "top" and "bot" applied to a pair produce the left, right, upper and lower coordinates, respectively.

7.A.6 Lines

There are three main ways to define the piece of a path between two points, "z1" and "z2", Furthermore the last point of a path can be defined with the keyword "cycle" which states to close the path on the first point. For example, a square at 45 degrees,
   p := (-u,0) -- (0,u) -- (u,0) -- (0-u) -- cycle;
Several operators controls the curvature of a curved line. One can define the "tension" in the segment (even asymmetrically), the endpoints curvature, and the direction (the tangent) at the endpoints. The most important way is however through the control points. Each point of a line can have two control points, a forward one and a backward one. Therion uses this method. The curve between Z1 and Z2 depends on the two points Z1" (to the right of Z1) and Z2' (to the left of Z2) as follows
Z(t) = t3 Z1 + 3 t2 (1-t) Z1" + 3 t (1-t)2 Z2' + (1-t)3 Z2
When Z1"=Z1 and Z2'=Z2 this equation becomes the straight segment Z1-Z2.
Once you have defined a line, you can draw it using one of these operators.
Every draw/fill operation covers what has already been drawn. For example "undraw" is an effective erase, as it draws with the background color. Therefore it is important to write the drawing operators in the correct order, to avoid having a piece of the drawing hidden behind another.
The function "drawoptions" changes the global drawing settings:
   drawoptions(withcolor  O.5[black, white]);  % set the color grey
   drawoptions(dashed evenly);
   drawoptions(withpen pensquare scaled 0.4);  % set the pen
To set two or more settings write all of them in a row, separated by spaces in a single call to "drawoptions()". For example drawoptions(withpen pencircle scaled 0.4 dashed evenly);.
It is possible to specify which pen to use, "withpen q", and which color, "withcolor c".
To draw arrows one has the functions "drawarrow" and "drawdblarrow". The size of the arrow head depends on the variables "ahlength" (length) and "ahangle" (angle at the arrow head).
Dashed lines are drawn using the option "dashed" followed by the dash pattern,
   draw p dashed pattern;
A pattern is a picture without texts and "fill" operations. There are two predefined patterns, "evenly" and "withdots". Being pictures, the patterns can be transformed (for example "scaled"). Finally the function "dashpattern" allows to define a patters specifying a list of "on"/"off".
The variable "linecap" determines the ending of the lines. By default it is "rounded", but it can be "squared" or "butt". The variable "linejoin" determines how to draw the line corners. By default it is "rounded", but it can be "beveled" (a polygon) or "mitered" (pointed).
To get informations about the position of a line there are the operators "llcorner, "lrcorner", etc. (for the bounding box corners) and "bbox" for the bounding box (with a small extra margin, "bboxmargin"). These operators can be applied also to picture and pen.
A special operator is "buildcycle". Applied to a sequence of paths it finds a close line (a cycle) joining the pieces of the paths delimited by the intersections of the path with the previous and the next ones. The result depends on the number of intersections of the paths. Roughly MetaPost trys to find the intersections that maximize the length of these pieces; however the algorithm is more complex, and in case of multiple intersections the result could not be what expected.
The operators "precontrol" and "postcontrol" give the control points of a path. For example "precontrol 2 of p" gives the precontrol point of the third point on the path (indices start at 0).
The drawing functions are implemented through the function "addto" which adds a graphical element (picture, path, or contour) to a picture.

7.A.7 Text

The following functions create objects of string type, ie, text:
The variables "defaultfont" and "defaultscale" define the font for the texts. For example,
   defaultfont := "Times-Roman";
   defaultscale := 10 pt;
   label("My text", z0);
The "fontsize" operator returns the size of the font.
If the text has formatting commands and must be processed by TeX, it must be delimted by "btex" and "etex". This is useful to write math formulas in the drawing The "btex"/"etex" block is a picture; it can be rotated, etc. To include a piece of TeX at the beginning of the file, you use "verbatimtex" (teminated by "etex"). For example (cyrillic fonts)
   verbatimtex
     \font\cyr=wncyr10
   etex

7.A.8 Picture

The "currentpicture" variable refers to the current picture. Predefined pictures are "fillcircle", a filled circle, and "halfcircle", the semicircle with positive y.
The "clip" function clips a picture to the region inside a path. For example "clip picture to path;".

7.A.9 Operations

The assignment operator ":=" assigns the value of the expression on the right hand side to the variable on the left hand side. For example "bboxmargin:=5" sets the margin of the bounding-box to 5 pt. The equal sign. "=" defines an equation, and can be used to define the value of a variable.
MetaPost can do arithmetics (sum, product, etc. even exponentiation), logical operations ("and", "or"), math functions ("sqrt", "abs", etc.). In particular for points there is the scalar product "dotprod", and for numbers the pythagoric sum and difference, "a++b" = (a2 + b2)1/2, "a+-+b" = (a2 - b2)1/2. "angle p" is the inverse tangent (arctan) of the (x,y) pair of "p".
Strings have the concatenation operation, "s1 & s2", and the substring extraction, "substring (a,b) of s". The operator "decimal" applied to a number produces its string expression.
MetaPost uses the curvilinear coordinate to describe every curve mathematically. This varies between 0 and N, the number of points of the line, and it is cyclically repeated if the line is close. MetaPost has sevral path operations:
The rules of precedence and composition of the operators are important to understand some MetaPost errors. MetaPost has six categories of objects. These are, roughly,
  1. atom: numbers, variables, expressions in parenthesis, begingroup/endgroup blocks, btex/etex blocks
  2. primary: an atom, the result of a unary operator applied to a primary, the result of an interpolation (square brackets), the result of an operator with "of", the variables "str" and "z" with a suffix.
  3. secondary: a primary, or the result of a primary binary operator applied to a secondary and a primary;
  4. terziary: a secondary, or the result of a secondary binary operator applied to a terziary and a secondary;
  5. subexpression: a terziary, or the result of a path-join ("--", "..",etc.)
  6. expression: a subexpression, or the result of a terziary binary operator applied to an expression and a terziary.
The operators are subdivided in categories:
  1. nullary: for example, "true", "false", "whatever";
  2. unary: with one argument, like the mathematical functions;
  3. type operators, that define a variable as of a given type;
  4. binary: with two arguments:
    • primary: *, /, **, and, dotprod, div, mod, infont ;
    • secondary: +, -, ++, +-+ , and the intersection operators;
    • terziary: &, = , and the operators of cut and comparison;
  5. "of" operators that have an argument preceeded by "of".

7.A.10 Control structures

MetaPost has the main control structures of a programming language: "for" loops, and "if" conditionals. These can be mixed in the definition of a path.
To check if a symbol is defined use the keyword "known": for example
    if known ATTR_name:

The syntax of the "for" loop is
   for i=0 step S until N:
     ...
   endfor
There is also "for i=0 upto N: ..." where "upto" is equivalen to "step 1 until".
Other loops:
Examples
  % define a path using a for-cycle
  path q;
  q = for i=0 upto 5: z[i] -- endfor z[6];
  draw q withcolor red;

  % invoke fun2() on all the args of fun()
  def fun( text t ) = forsuffixes $=t: fun2($) endfor enddef;

  % define a function that returns a path
  vardef p(text t) =
    k := 0;
    forsuffixes $=t:
      if ($ > k): z[$] -- else: z[$] fi
      hide( k:=k+1 )
      exitif( $ < k );
    endfor
  enddef;
  draw p(8, 6, 4, 2, 0);
The syntax of the "if" statement is
   if condition: 
     ...
   else:
     ...
   fi
There is also "elseif:" that help to reduce the nesting of the "if"s.

7.A.11 Macroes

New functions (called "macro") can be defined. These are important in writing MetaPost code to extend therion. The syntax is
    def function (expr ...) =
      body_of_the_function
    enddef;
The parameters of a macro are usually "expr" (expressions). They can be also "suffix" (variables), or "text" (string of characters). To invoke a macro passing the name of a variable to it, it is better to declare the parameter as "suffix".
The return value of a macro is the result of the last statement before "endgroup".
The "begingroup" and "endgroup" commands define a local scope. Also "beginfig" and "endfig" define a local scope. The "save" command, followed by the name of one or more variables, tells MetaPost to store away the current value of those variables, and replace them at the "endgroup". The "interim" command assigns to a variable a temporary value, and replace it with the old value at the "endgroup".
Another way to define a macro is the "vardef" command. This is very much like a function: a "begingroup"/"endgroup" block is automatically inserted. Moreover the name of the "vardef" can have suffixes; for example, "vardef a[]b (expr p) = ...". Two special variables are predefined: "@" is the last piece of the name of the "vardef", "#@" is everything that come before it.

7.A.12 An example

You can easily find several metapost examples on the web. The following is the drawing of the "danger" sign (an exclamation mark inside a square rotatbe by 45 degrees).
  beginfig(1);
  
    s = 20.0; % scale
    z0 = (200, 200); % offset
  
    z1 = z0 + (0,-1)*s; % corners
    z2 = z0 + (1,0)*s;
    z3 = z0 + (0,1)*s;  
    z4 = z0 + (-1,0)*s;
  
    z5 = 0.2 [ z1, z3 ];
    z6 = 0.3 [ z1, z3 ];
    z7 = 0.7 [ z1, z3 ];
    z8 = 0.8 [ z1, z3 ];
    z9 = z7 + (z8-z7) rotated 90;
    z10 = z7 - (z8-z7) rotated 90;
  
    pickup pencircle scaled (s/10);
    draw z1 -- z2 -- z3 -- z4 -- cycle;
  
    pickup pencircle scaled (s/8);
    draw z5;  
    fill z6 -- z9 .. z8 .. z10 -- cycle;
  
  endfig;
  end
This code is pretty simple. Actually, you do not need all the sofisticated commands of MetaPost to customize therion for your cave drawings. The result of the above code is shwon in the figure below.


Danger sign
Fig. 94. Danger sign

7.A.13 PostScript file

The command "shipout" generates the PostScript file. It takes as argument a picture. The PostScript filename has extension ".N" where N is the number specified in the command "beginfig". The command "endfig" invokes "shipout currentpicture;".
At the end of a metapost file you must add the command "end;" which tells to the interpreter mpost to quit.
When your metapost file is ready, and execute
    mpost file.mp
you get a list of files, "file.1", "file.2", ..., one for each figure. These are almost PostScript files. If you want to visualize them (with ghostview) or convert to pdf ( with ps2pdf) you may get some errors since a few things are missing. In particular the fonts definition and that of the command "fshow". Open the files of the figures with a text editor and add the missing definitions at the beginning (in the prolog),
   /cmr10 /CMR10 def
   /fshow {exch findfont exch scalefont setfont show}bind def
This is not enough. The figure will be centered in the lower left corner of the page, and you will see only part of it. To move it inside the page add the PostScript command (use suitable x-y offset values). You may also rotate it. For example,
    590 22 translate
    90 rotate

therion users - Thu Mar 4 17:04:18 2010
Prev Up Top Next Contents

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.