% output=pdftex engine=pdftex language=uk
%
% copyright=pragma-ade readme=readme.pdf licence=cc-by-nc-sa
\startcomponent mfun-002
\environment mfun-000
\chapter {A few more details}
\startintro
In this chapter we will see how to define a \METAPOST\
graphic, and how to include it in a document. Since the
exact dimensions of graphics play an important role in the
placement of a graphic, we will explore the way a bounding
box is constructed.
We will also pay attention to the usage of units and the
side effects of scaling and shifting, since they can
contradict our expectations in unexpected ways. Furthermore
we will explore a few obscure areas.
\stopintro
\section {Making graphics}
We will use \METAPOST\ in a rather straightforward way, and
we will try to avoid complicated math as much as possible.
We will do a bit of drawing, clipping, and moving around.
Occasionally we will see some more complicated
manipulations.
When defined as stand||alone graphic, a \METAPOST\ file
looks like this:
\starttyping
% Let's draw a circle.
beginfig (7) ;
draw fullcircle scaled 3cm withpen pencircle scaled 1cm ;
endfig ;
end .
\stoptyping
The main structuring components in such a file are the \type
{beginfig} and \type {endfig} macros. Like in a big story,
the file has many sub||sentences, where each sub||sentence
ends with a semi||colon. Although the \type {end} command at
the end of the file concludes the story, putting a period
there is a finishing touch. Actually, after the \type {end}
command you can put whatever text you wish, your comments,
your grocery list, whatever. Comments in \METAPOST, prefixed
by a percent sign, as in \typ {% Let's draw a circle}, are
ignored by the interpreter, but useful reminders for the
programmer.
If the file is saved as \type {yourfile.mp}, then the file is
processed by \METAPOST\ by issuing the following command:
\starttyping
mpost yourfile
\stoptyping
after which you will have a graphic called \type
{yourfile.7}, which contains a series of \POSTSCRIPT\
commands. Because \METAPOST\ does all the work, this file is
efficient and compact. The number of distinct \POSTSCRIPT\
operators used is limited, which has the advantage that we
can postprocess this file rather easily.
We can view this file in a \POSTSCRIPT\ viewer like
\GHOSTVIEW\ or convert the graphic to \PDF\ and view the
result in a suitable \PDF\ viewer like \ACROBAT. Of course,
you can embed such a file in a \CONTEXT\ document, using a
command like:
\starttyping
\externalfigure[yourfile.7]
\stoptyping
We will go in more detail about embedding graphics in \in
{chapter} [sec:embedding].
If you have installed \CONTEXT, somewhere on your system
there resides a file \type {mp-tool.mp}. If you make a
stand||alone graphic, it's best to put the following line at
the top of your file:
\starttyping
input mp-tool ;
\stoptyping
By loading this file, the resulting graphic will provide a
high resolution bounding box, which enables more accurate
placement. The file also sets the \typ {prologues := 1} so
that viewers like \GHOSTVIEW\ can refresh the file when it is
changed.
Next we will introduce some more \METAPOST\ commands. From
now on, we will omit the encapsulating \type {beginfig} and
\type {endfig} macros. If you want to process these examples
yourself, you should add those commands.
\startbuffer
pickup pencircle scaled .5cm ;
draw unitsquare xscaled 8cm yscaled 1cm withcolor .625white ;
draw origin withcolor .625yellow ;
pickup pencircle scaled 1pt ;
draw bbox currentpicture withcolor .625red ;
\stopbuffer
\typebuffer
In this example we see a mixture of so called primitives as
well as macros. A primitive is something hard coded, a
built||in command, while a macro is a collection of such
primitives, packaged in a way that they can be recalled
easily. Where \type {scaled} is a primitive and \type
{draw} a macro, \type {unitsquare} is a path variable, an
abbreviation for:
\starttyping
unitsquare = (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle ;
\stoptyping
The double dash (\type {--}) is also a macro, used to
connect two points with a straight line segment. However,
\type {cycle} is a primitive, which connects the last point
of the unitsquare to the first on unitsquare's path. Path
variables must first be declared, as in:
\starttyping
path unitsquare ;
\stoptyping
A large collection of such macros is available when you
launch \METAPOST. Consult the \METAPOST\ manual for details.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
In the first line of our example, we set the drawing pen to
\type {.5cm}. You can also specify such a dimension in
other units, like points (\type {pt}). When no unit is
provided, \METAPOST\ will use a big point (\type {bp}) ,
the \POSTSCRIPT\ approximation of a point.
The second line does just as it says: it draws a rectangle
of certain dimensions in a certain color. In the third line
we draw a colored dot at the origin of the coordinate system
in which we are drawing. Finally, we set up a smaller pen
and draw the bounding box of the current picture, using the
variable \type {currentpicture}. Normally, all drawn shapes
end up in this picture variable.
\section {Bounding boxes}
If you take a close look at the last picture in the previous
section, you will notice that the bounding box is larger
than the picture. This is one of the nasty side effects of
\METAPOST's \type {bbox} macro. This macro draws a box, but
with a certain offset. The next example shows how we can
manipulate this offset. Remember that in order to process
the next examples, you should embed the code in \type
{beginfig} and \type {endfig} macros. Also, in stand||alone
graphics, don't forget to say \typ {\input mp-tool} first.
\startbuffer
pickup pencircle scaled .5cm ;
draw unitsquare xscaled 8cm yscaled 1cm withcolor .625white ;
path bb ; bboxmargin := 0pt ; bb := bbox currentpicture ;
draw bb withpen pencircle scaled 1pt withcolor .625red ;
\stopbuffer
\typebuffer
In the third line we define a path variable. We assign the
current bounding box to this variable, but first we set the
offset to zero. The last line demonstrates how to draw such
a path. Instead of setting the pen as we did in the first
line, we pass the dimensions directly.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
Where \type {draw} draws a path, the \type {fill} macro
fills one. In order to be filled, a path should be closed,
which is accomplished by the \type {cycle} primitive, as we
saw in constructing the \type {unitsquare} path.
\startbuffer
pickup pencircle scaled .5cm ;
fill unitsquare xscaled 8cm yscaled 1cm withcolor .625white ;
path bb ; bboxmargin := 0pt ; bb := bbox currentpicture ;
draw bb withpen pencircle scaled 1pt withcolor .625red ;
\stopbuffer
\typebuffer
This example demonstrates that when we fill the path, the
resulting graphic is smaller. Where \type {draw} follows the
center of a path, \type {fill} stays inside the path.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
A third alternative is the \type {filldraw} macro. From the
previous examples, we would expect a bounding box that
matches the one of the drawn path.
\startbuffer
pickup pencircle scaled .5cm ;
filldraw unitsquare xscaled 8cm yscaled 1cm withcolor .625white ;
path bb ; bboxmargin := 0pt ; bb := bbox currentpicture ;
draw bb withpen pencircle scaled 1pt withcolor .625red ;
\stopbuffer
\typebuffer
The resulting graphic has the bounding box of the fill. Note
how the path, because it is stroked with a .5cm pen, extends
beyond the border of the bounding box. The way this image
shows up depends on the viewer (settings) you use to render
the graphic. For example, in \GHOSTVIEW, if you disable
clipping to the bounding box, only the positive quadrant of
the graphic is shown. Further, if you enable clipping to the
bounding box, this image will look exactly like the previous
image created with the fill command. In many cases, it may
be best to avoid the \type {filldraw} command.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
From the previous examples, you can deduce that the
following alternative results in a proper bounding box:
\startbuffer
pickup pencircle scaled .5cm ;
path p ; p := unitsquare xscaled 8cm yscaled 1cm ;
fill p withcolor .625white ;
draw p withcolor .625white ;
path bb ; bboxmargin := 0pt ; bb := bbox currentpicture ;
draw bb withpen pencircle scaled 1pt withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
The \CONTEXT\ distribution comes with a set of \METAPOST\
modules, one of which contains the \type {drawfill} macro,
which provides the outer bounding box. We will demonstrate
its use in another, more complicated example.
\startbuffer
picture finalpicture ; finalpicture := nullpicture ;
numeric n ; n := 0 ; bboxmargin := 0pt ;
pickup pencircle scaled .5cm ;
def shape =
unitsquare scaled 2cm withcolor .625white ;
draw bbox currentpicture
withpen pencircle scaled .5mm withcolor .625red ;
addto finalpicture also currentpicture shifted(n*3cm,0) ;
currentpicture := nullpicture ; n := n+1 ;
enddef ;
fill shape ; draw shape ; filldraw shape ; drawfill shape ;
currentpicture := finalpicture ;
\stopbuffer
\typebuffer
Here we introduce a macro definition, \type {shape}. In
\METAPOST, the start of a macro definition is indicated with
the keyword \type {def}. Thereafter, you can insert other
variables and commands, even other macro definitions. The
keyword \type {enddef} signals the end of the macro
definition. The result is shown in \in {figure} [fig:draws
and fills]; watch the bounding boxes. Close reading of the
macro will reveal that the \type {fill}, \type {draw}, \type
{filldraw} and \type {drawfill} macros are applied to the
first \type {unitsquare} path in the macro.
\placefigure
[here]
[fig:draws and fills]
{A \type {fill}, \type {draw}, \type {filldraw} and \type
{drawfill} applied to the same square.}
{\processMPbuffer}
In this macro, \type {bbox} calls a macro that returns the
enlarged bounding box of a path. By setting \type
{bboxmargin} we can influence how much the bounding box is
enlarged. Since this is an existing variable, we don't have
to allocate it, like we do with~\type{numeric n}. Unless you
take special precautions, variables are global by nature
and persistent outside macros.
\starttyping
picture finalpicture ; finalpicture := nullpicture ;
\stoptyping
Just as \type {numeric} allocates an integer variable, the
\type {picture} primitive allocates a picture data structure.
We explicitly have to set this picture to nothing using the
built||in primitive \type {nullpicture}.
Later on, we will add the drawn paths as accumulated in \type
{currentpicture} to this \type {finalpicture} in the
following manner.
\starttyping
addto finalpicture also currentpicture shifted(n*3cm,0) ;
\stoptyping
Since we want to add a few more and don't want them to
overlap, we shift them. Therefore we have to erase the
current picture as well as increment the shift counter.
\starttyping
currentpicture := nullpicture ; n := n+1 ;
\stoptyping
The \type {drawfill} macro is one of the \METAFUN\ macros.
Another handy macro is \type {boundingbox}. When used
instead of \type {bbox}, you don't have to set the margin to
zero.
\startbuffer
drawoptions (withcolor .625white) ;
path p ; p := unitsquare scaled 2cm ;
fill p shifted (3cm,0) ;
pickup pencircle scaled .5cm ; fill p shifted (6cm,0) ;
fill p shifted (9cm,0) withpen pencircle scaled .5cm ;
\stopbuffer
\placefigure
[here]
[fig:more draws and fills]
{The influence of pens on \type {fill}.}
{\processMPbuffer}
There is a subtle point in filling a shape. In \in {figure}
[fig:more draws and fills] you see the influence of the pen
on a \type {fill} operation. An indirect specification has
no influence, and results in a filled rectangle with sharp
corners. The third rectangle is drawn with a direct pen
specification which results in a larger shape with rounds
corners. However, the bounding box is the same in all three
cases. The graphic is defined as follows. This time we
don't use a (complicated) macro.
\typebuffer
When a graphic is constructed, its components end up in an
internal data structure in a more or less layered way. This
means that as long as a graphic is not flushed, you may
consider it to be a stack of paths and texts with the paths
being drawn or filled shapes or acting as clipping paths or
bounding boxes.
When you ask for the dimensions of a graphic the lower left
and upper right corner are calculated using this stack.
Because you can explicitly set bounding boxes, you can lie
about the dimensions of a graphic. This is a very useful
feature. In the rare case that you want to know the truth
and nothing but the truth, you can tweak the \type
{truecorners} numeric variable. We will demonstrate this with
a few examples.
\startbuffer
fill fullcircle scaled 1cm withcolor .625yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\ruledhbox{\processMPbuffer}\stoplinecorrection
\startbuffer
fill fullcircle scaled 1cm withcolor .625yellow ;
setbounds currentpicture to boundingbox currentpicture enlarged 2mm ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\ruledhbox{\processMPbuffer}\stoplinecorrection
\startbuffer
fill fullcircle scaled 1cm withcolor .625yellow ;
setbounds currentpicture to boundingbox currentpicture enlarged 2mm ;
interim truecorners := 1 ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\ruledhbox{\processMPbuffer}\stoplinecorrection
\startbuffer
fill fullcircle scaled 1cm withcolor .625yellow ;
interim truecorners := 1 ;
setbounds currentpicture to boundingbox currentpicture enlarged 2mm ;
truecorners := 0 ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\ruledhbox{\processMPbuffer}\stoplinecorrection
As you can see here, as soon as we set \type {truecorners}
to~1, the bounding box settings are ignored.
\section {Units}
Like \TEX, \METAPOST\ supports multiple units of length. In
\TEX, these units are hard coded and handled by the parser,
where the internal unit of length is the scaled point (\type
{sp}), something on the nanometer range. Because \METAPOST\
is focused on \POSTSCRIPT\ output, its internal unit is the
big point (\type {bp}). All other units are derived from
this unit and available as numeric instead of hard coded.
\starttyping
mm = 2.83464 ; pt = 0.99626 ; dd = 1.06601 ; bp := 1 ;
cm = 28.34645 ; pc = 11.95517 ; cc = 12.79213 ; in := 72 ;
\stoptyping
Careful reading reveals that only the \type {bp} and \type
{in} are fixed, while the rest of the dimensions are scalar
multiples of \type {bp}.
Since we are dealing with graphics, the most commonly used
dimensions are \type {pt}, \type {bp}, \type {mm}, \type {cm}
and~\type {in}.
\startuseMPgraphic{pt}
fill fullsquare scaled 72.27pt withcolor .625yellow ;
fill fullcircle scaled 72.27pt withcolor white ;
label("72.27pt", center currentpicture) ;
\stopuseMPgraphic
\startuseMPgraphic{bp}
fill fullsquare scaled 72bp withcolor .625yellow ;
fill fullcircle scaled 72bp withcolor white ;
label("72bp", center currentpicture) ;
\stopuseMPgraphic
\startuseMPgraphic{mm}
fill fullsquare scaled 25.4mm withcolor .625yellow ;
fill fullcircle scaled 25.4mm withcolor white ;
label("25.4mm", center currentpicture) ;
\stopuseMPgraphic
\startuseMPgraphic{cm}
fill fullsquare scaled 2.54cm withcolor .625yellow ;
fill fullcircle scaled 2.54cm withcolor white ;
label("2.54cm", center currentpicture) ;
\stopuseMPgraphic
\startuseMPgraphic{in}
fill fullsquare scaled 1in withcolor .625yellow ;
fill fullcircle scaled 1in withcolor white ;
label("1in", center currentpicture) ;
\stopuseMPgraphic
\startlinecorrection[blank]
\hbox to \hsize
{\useMPgraphic{pt}\hss
\useMPgraphic{bp}\hss
\useMPgraphic{mm}\hss
\useMPgraphic{cm}\hss
\useMPgraphic{in}}
\stoplinecorrection
The text in the center of the leftmost graphic is typeset by
\METAPOST\ as a label.
\starttyping
fill fullsquare scaled 72.27pt withcolor .625yellow ;
fill fullcircle scaled 72.27pt withcolor white ;
label("72.27pt", center currentpicture) ;
\stoptyping
In \METAPOST\ the following lines are identical:
\starttyping
draw fullcircle scaled 100 ;
draw fullcircle scaled 100bp ;
\stoptyping
You might be tempted to omit the unit, but this can be
confusing, particularly if you also program in a language
like \METAFONT, where the \type {pt} is the base unit. This
means that a circle scaled to 100 in \METAPOST\ is not the
same as a circle scaled to 100 in \METAFONT. Consider the
next definition:
\startbuffer
pickup pencircle scaled 0 ;
fill unitsquare
xscaled 400pt yscaled -.5cm withcolor .625red ;
fill unitsquare
xscaled 400bp yscaled +.5cm withcolor .625yellow ;
drawoptions(withcolor white) ;
label.rt("400 pt", origin shifted (0, -.25cm)) ;
label.rt("400 bp", origin shifted (0, +.25cm)) ;
\stopbuffer
\typebuffer
When processed, the difference between a \type {pt} and
\type {bp} shows rather well. Watch how we use \type {.rt}
to move the label to the right; you can compare this with
\TEX's macro \type {\rlap}. You might want to experiment
with \type {.lft}, \type {.top}, \type {.bot}, \type
{.ulft}, \type {.urt}, \type {.llft} and \type {.lrt}.
The difference between both bars is exactly \scratchdimen =
400 bp \advance\scratchdimen by -400 pt \the \scratchdimen
\space (as calculated by \TEX).
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
Where \TEX\ is anchored in tradition, and therefore more or
less uses the \type {pt} as the default unit, \METAPOST,
much like \POSTSCRIPT, has its roots in the computer
sciences. There, to simplify calculations, an inch is
divided in 72 big points, and .72pt is sacrificed.
When you consider that \POSTSCRIPT\ is a high end graphic
programming language, you may wonder why this sacrifice was
made. Although the difference between \type {1bp} and \type
{1pt} is miniscule, this difference is the source of much
(unknown) confusion. When \TEX\ users talk about a \type
{10pt} font, a desktop publisher hears \type {10bp}. In a
similar vein, when we define a papersize having a width of
\type {600pt} and a height of \type {450pt}, which is
papersize \type {S6} in \CONTEXT, a \POSTSCRIPT\ or \PDF\
viewer will report slightly smaller values as page
dimensions. This is because those programs claim the \type
{pt} to be a \type {bp}. [This confusion can lead to
interesting discussions with desktop publishers when they
have to use \TEX. They often think that their demand of a
baseline distance of \type {13.4} is met when we set it to
\type {13.4pt}, while actually they were thinking of \type
{13.4bp}, which of course in other programs is specified
using a \type {pt} suffix.]
Therefore, when embedding graphics in \CONTEXT, we strongly
recommend that you use \type {pt} as the base unit instead.
The main reason why we spend so many words on this issue is
that, when neglected, large graphics may look inaccurate.
Actually, when taken care of, it is one of the (many)
reasons why \TEX\ documents always look so accurate. Given
that the eye is sensitive to distortions of far less than
\type {1pt}, you can be puzzled by the fact that many
drawing programs only provide a bounding box in rounded
units. Thereby, they round to the next position, to prevent
unwanted cropping. For some reason this low resolution has
made it into the high end \POSTSCRIPT\ standard.
In \CONTEXT\ we try to deal with these issues as well as
possible.
\section {Scaling and shifting}
When we draw a shape, \METAPOST\ will adapt the bounding box
accordingly. This means that a graphic has its natural
dimensions, unless of course we adapt the bounding box
manually. When you limit your graphic to a simple shape, say
a rectangle, shifting it to some place can get obscured by
this fact. Therefore, the following series of shapes appear to
be the same.
\startbuffer
draw
unitsquare xscaled 6cm yscaled 1.5cm
withpen pencircle scaled 2mm withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
\startbuffer
draw
unitsquare shifted (.5,.5) xscaled 6cm yscaled 1.5cm
withpen pencircle scaled 2mm withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
\startbuffer
draw
unitsquare shifted (-.5,-.5) xscaled 6cm yscaled 1.5cm
withpen pencircle scaled 2mm withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
\startbuffer
draw
unitsquare xscaled 6cm yscaled 1.5cm shifted (1cm,1cm)
withpen pencircle scaled 2mm withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
\startbuffer
draw
unitsquare xscaled 6cm yscaled 1.5cm shifted (1.5cm,1cm)
withpen pencircle scaled 2mm withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
However, when we combine such graphics into one, we will see
in what respect the scaling and shifting actually takes
place.
\startbuffer
draw
unitsquare xscaled 6cm yscaled 2cm
withpen pencircle scaled 3.0mm withcolor .625yellow ;
draw
unitsquare shifted (.5,.5) xscaled 6cm yscaled 2cm
withpen pencircle scaled 3.0mm withcolor .625red ;
draw
unitsquare xscaled 6cm yscaled 2cm shifted (1cm,1cm)
withpen pencircle scaled 3.0mm withcolor .625white ;
draw
unitsquare xscaled 6cm yscaled 2cm shifted (1.5cm,1cm)
withpen pencircle scaled 1.5mm withcolor white ;
draw
unitsquare shifted (-.5,-.5) xscaled 6cm yscaled 2cm
withpen pencircle scaled 1mm withcolor black ;
draw origin withpen pencircle scaled 1mm ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
As you can see, the transformations are applied in series.
Sometimes this is not what we want, in which case we can use
parentheses to force the desired behaviour. The lesson
learned is that {\em scaling and shifting} is not always the
same as {\em shifting and scaling}.
\startbuffer
draw
origin -- origin shifted ((4cm,0cm) shifted (4cm,0cm))
withpen pencircle scaled 1cm withcolor .625white ;
draw
origin -- origin shifted (4cm,0cm) shifted (4cm,0cm)
withpen pencircle scaled 8mm withcolor .625yellow ;
draw
(origin -- origin shifted (4cm,0cm)) shifted (4cm,0cm)
withpen pencircle scaled 6mm withcolor .625red ;
draw
origin -- (origin shifted (4cm,0cm) shifted (4cm,0cm))
withpen pencircle scaled 4mm withcolor white ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
Especially when a path results from a call to a macro, using
parentheses around a path may help, as in the following
example.
\startbuffer
def unitslant = origin -- origin shifted (1,1) enddef ;
draw
unitslant xscaled 5cm yscaled 1cm
withpen pencircle scaled 1cm withcolor .625red ;
draw
(unitslant) xscaled 5cm yscaled 1cm
withpen pencircle scaled 5mm withcolor .625yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
The next definition of \type {unitslant} is therefore better.
\startbuffer
def unitslant = (origin -- origin shifted (1,1)) enddef ;
draw
unitslant xscaled 5cm yscaled 1cm
withpen pencircle scaled 5mm withcolor .625red ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]\processMPbuffer\stoplinecorrection
An even better alternative is:
\startbuffer
path unitslant ; unitslant = origin -- origin shifted (1,1) ;
draw
unitslant xscaled 5cm yscaled 1cm
withpen pencircle scaled 5mm withcolor .625yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\section {Curve construction}
\doifmodeelse{screen}
{\def\Xcom{3}\def\Ycom{2}\def\Zcom{\the\textheight}}
{\def\Xcom{2}\def\Ycom{3}\def\Zcom{\the\textwidth }}
Chapter 3 of the \METAFONT\ book explains the mathematics
behind the construction of curves. Both \METAFONT\ and
\METAPOST\ implement B\'ezier curves. The fact that these
curves are named after Pierre B\'ezier obscures the fact
that the math behind them originates with Serge\u{\i}
Bernshte\u{\i}n.
The points on the curve are determined by the following
formula:
\placeformula[-]
\startformula
z(t) = (1-t)^3 z_1 + 3 (1-t)^2 t z_2 + 3 (1-t) t^2 z_3 + t^3 z_4
\stopformula
Here, the parameter $t$ runs from $[0,1]$. As you can see,
we are dealing with four points. In practice this means that
when we construct a curve from multiple points, we act on
two points and the two control points in between. So, the
segment that goes from $z_1$ to $z_4$ is calculated using
these two points and the points that \METAFONT|/|\METAPOST\
calls post control point and pre control point.
\startbuffer[a]
vardef dodrawmidpoints (expr a, b, c, d, n, col, m) =
save e, f, g, h, i, j ; pair e, f, g, h, i, j ;
e := .5[a,b] ; f := .5[b,c] ; g := .5[c,d] ;
h := .5[e,f] ; i := .5[f,g] ; j := .5[h,i] ;
if m= 0 : drawpoints j elseif
m= 1 : draw a--b--c--d elseif
m= 2 : draw e--f--g elseif
m= 3 : draw h--i elseif
m= 4 : draw a--e--h--j elseif
m= 5 : draw j--i--g--d elseif
m=11 : drawpoints a--b--c--d elseif
m=12 : drawpoints e--f--g elseif
m=13 : drawpoints h--i elseif
m=14 : drawpoints a--e--h--j elseif
m=15 : drawpoints j--i--g--d fi withcolor col ;
if n>1 :
dodrawmidpoints(a, e, h, j, n-1, col, m) ;
dodrawmidpoints(j, i, g, d, n-1, col, m) ;
fi ;
enddef ;
vardef drawmidpoints (expr p, n, col, m) =
save a, b, c, d ; pair a, b, c, d ;
for x=0 upto length(p)-1 :
a := point x of p ; b := postcontrol x of p ;
d := point x+1 of p ; c := precontrol x+1 of p ;
dodrawmidpoints(a,b,c,d,n,col,m) ;
endfor ;
enddef ;
\stopbuffer
\startbuffer[b]
path p ; p := (4cm,4cm)..(6cm,0cm)..(1cm,2cm) ;
\stopbuffer
\startbuffer[c]
drawpath p ;
drawcontrollines p withcolor .625yellow ;
drawcontrolpoints p withcolor .625red ;
drawpoints p withcolor .625red ;
freelabel(btex $z_1$ etex, point 0 of p, center p) ;
freelabel(btex $z_2$ etex, postcontrol 0 of p, center p) ;
freelabel(btex $z_3$ etex, precontrol 1 of p, center p) ;
freelabel(btex $z_4$ etex, point 1 of p, center p) ;
freelabel(btex $z_5$ etex, postcontrol 1 of p, center p) ;
freelabel(btex $z_6$ etex, precontrol 2 of p, center p) ;
freelabel(btex $z_7$ etex, point 2 of p, center p) ;
\stopbuffer
\startbuffer[x]
draw boundingbox p enlarged 1cm ;
setbounds currentpicture to boundingbox p enlarged 1cm ;
currentpicture := currentpicture xsized (.45*\Zcom) ;
\stopbuffer
\startlinecorrection[blank]
\processMPbuffer[a,b,c,x]
\stoplinecorrection
The previous curve is constructed from the three points
$z_1$, $z_4$ and $z_7$. The curve is drawn in \METAPOST\ by
\type {z1..z4..z7} and is made up out of two segments. The
first segment is determined by the following points:
\startitemize[packed,n]
\item point $z_1$ of the curve
\item the postcontrol point $z_2$ of $z_1$
\item the precontrol point $z_3$ of $z_4$
\item point $z_4$ of the curve
\stopitemize
On the next pages we will see how the whole curve is
constructed from these quadruples of points. The process
comes down to connecting the mid points of the straight
lines to the points mentioned. We do this three times, which
is why these curves are classified as third order
approximations.
The first series of graphics demonstrates the process of
determining the mid points. The third order midpoint is
positioned on the final curve. The second series focuses on
the results: new sets of four points that will be used in a
next stage. The last series only shows the third order
midpoints. As you can see, after some six iterations we have
already reached a rather good fit of the final curve. The
exact number of iterations depends on the resolution
needed. You will notice that the construction speed
(density) differs per segment.
\startpostponing
% cc .. hh in order to avoid conflicts with c-...
\startbuffer[cc]
drawpath p ; drawpoints p ; drawcontrolpoints p ;
\stopbuffer
\startbuffer[dd]
drawmidpoints(p,1,.625red, 11) ; drawmidpoints(p,1,.625yellow, 1) ;
\stopbuffer
\startbuffer[ee]
drawmidpoints(p,1,.625red, 12) ; drawmidpoints(p,1,.625yellow, 2) ;
\stopbuffer
\startbuffer[ff]
drawmidpoints(p,1,.625red, 13) ; drawmidpoints(p,1,.625yellow, 3) ;
\stopbuffer
\startbuffer[gg]
drawmidpoints(p,1,.625red, 14) ; drawmidpoints(p,1,.625yellow, 4) ;
\stopbuffer
\startbuffer[hh]
drawmidpoints(p,1,.625red, 15) ; drawmidpoints(p,1,.625yellow, 5) ;
\stopbuffer
\startbuffer
\startcombination[\Xcom*\Ycom]
{\processMPbuffer[a,b,cc,x]} {points}
{\processMPbuffer[a,b,cc,dd,x]} {first order curve}
{\processMPbuffer[a,b,cc,dd,ee,x]} {second order curve}
{\processMPbuffer[a,b,cc,dd,ee,ff,x]} {third order curve}
{\processMPbuffer[a,b,cc,dd,ee,gg,x]} {left side curves}
{\processMPbuffer[a,b,cc,dd,ee,hh,x]} {right side curves}
\stopcombination
\stopbuffer
\getbuffer \page
\startbuffer[dd]
drawmidpoints(p,1,.625red, 11) ;
drawmidpoints(p,1,.625yellow, 1) ;
\stopbuffer
\startbuffer[ee]
for i=11, 12 : drawmidpoints(p,1,.625red, i) ; endfor ;
drawmidpoints(p,1,.625yellow, 2) ;
\stopbuffer
\startbuffer[ff]
for i=11, 12, 13 : drawmidpoints(p,1,.625red, i) ; endfor ;
drawmidpoints(p,1,.625yellow, 3) ;
\stopbuffer
\startbuffer[gg]
for i=11,12,13,14 : drawmidpoints(p,1,.625red, i) ; endfor ;
drawmidpoints(p,1,.625yellow, 4) ;
\stopbuffer
\startbuffer[hh]
for i=11, 12, 13, 14, 15 : drawmidpoints(p,1,.625red, i) ; endfor ;
drawmidpoints(p,1,.625yellow, 5) ;
\stopbuffer
\startbuffer
\startcombination[\Xcom*\Ycom]
{\processMPbuffer[a,b,cc,x]} {points}
{\processMPbuffer[a,b,cc,dd,x]} {first order points}
{\processMPbuffer[a,b,cc,ee,x]} {second order points}
{\processMPbuffer[a,b,cc,ff,x]} {third order points}
{\processMPbuffer[a,b,cc,gg,x]} {left side points}
{\processMPbuffer[a,b,cc,hh,x]} {right side points}
\stopcombination
\stopbuffer
\getbuffer \page
\startbuffer[cc]
drawpath p ; drawmidpoints (p,1,.625yellow, 0) ;
\stopbuffer
\startbuffer[dd]
drawpath p ; drawmidpoints (p,2,.625yellow, 0) ;
\stopbuffer
\startbuffer[ee]
drawpath p ; drawmidpoints (p,3,.625yellow, 0) ;
\stopbuffer
\startbuffer[ff]
drawpath p ; drawmidpoints (p,4,.625yellow, 0) ;
\stopbuffer
\startbuffer[gg]
drawpath p ; drawmidpoints (p,5,.625yellow, 0) ;
\stopbuffer
\startbuffer[hh]
drawpath p ; drawmidpoints (p,6,.625yellow, 0) ;
\stopbuffer
\startbuffer
\startcombination[\Xcom*\Ycom]
{\processMPbuffer[a,b,cc,x]} {first iteration}
{\processMPbuffer[a,b,cc,dd,x]} {second iteration}
{\processMPbuffer[a,b,cc,dd,ee,x]} {third iteration}
{\processMPbuffer[a,b,cc,dd,ee,ff,x]} {fourth iteration}
{\processMPbuffer[a,b,cc,dd,ee,ff,gg,x]} {fifth iteration}
{\processMPbuffer[a,b,cc,dd,ee,ff,gg,hh,x]} {sixths iteration}
\stopcombination
\stopbuffer
\getbuffer \page
\stoppostponing
% here we pick up the thread, if we would not flush the
% pages before the next text, the reader could become
% confused
The path in these examples is defined as follows:
\typebuffer[b]
If you are playing with graphics like this, the \METAFUN\
macro \type {randomize} may come in handy:
\startbuffer[bb]
p := p randomized (1cm,.5cm) ;
\stopbuffer
If we apply this operation a couple of times we can see how
the control points vary. (Using the randomizer saves us the
troubles of finding nice example values.) The angle between
the tangent as well as the distance form the parent point
determine the curve.
\startbuffer[bb]
p := p randomized (1cm,.5cm) ;
\stopbuffer
\startbuffer[xx]
currentpicture := currentpicture scaled .5 ;
\stopbuffer
\startlinecorrection[blank]
\hbox to \hsize
{\processMPbuffer[a,b,bb,c,x,xx]\hss
\processMPbuffer[a,b,bb,c,x,xx]\hss
\processMPbuffer[a,b,bb,c,x,xx]\hss
\processMPbuffer[a,b,bb,c,x,xx]}
\stoplinecorrection
% new thread
Just in case you are interested in how such graphical
simulations can be organized, we show simplified versions of
the macros used here. (In the previous examples we mimimized
the complexity of the code by using buffers, but describing
this mechanism is out of the scope of this section.)
\startbuffer[demo]
vardef dodrawmidpoints (expr a, b, c, d, n) =
save e, f, g, h, i, j ; pair e, f, g, h, i, j ;
e := .5[a,b] ; f := .5[b,c] ; g := .5[c,d] ;
h := .5[e,f] ; i := .5[f,g] ; j := .5[h,i] ;
draw j ;
if n>1 :
dodrawmidpoints(a, e, h, j, n-1) ;
dodrawmidpoints(j, i, g, d, n-1) ;
fi ;
enddef ;
vardef drawmidpoints (expr p, n) =
save a, b, c, d ; pair a, b, c, d ;
for x=0 upto length(p)-1 :
a := point x of p ; b := postcontrol x of p ;
d := point x+1 of p ; c := precontrol x+1 of p ;
dodrawmidpoints(a, b, c, d, n) ;
endfor ;
enddef ;
\stopbuffer
We need to loop over all segments of a curve, where for
each segment the left and right side sub curves are handled
recursively, upto the requested depth (denoted as \type
{n}). For this we define the following macros.
\typebuffer[demo]
\startbuffer[zero]
drawoptions (withpen pencircle scaled 5pt withcolor .625red);
\stopbuffer
\startbuffer[extra]
drawoptions (withpen pencircle scaled 5pt withcolor .625yellow);
\stopbuffer
We apply this macro to a simple shape:
\startbuffer[one]
drawmidpoints (fullcircle xscaled 300pt yscaled 50pt, 1) ;
\stopbuffer
\typebuffer[one]
When drawn, this results in the points that makes up the
curve:
\startlinecorrection[blank]
\processMPbuffer[demo,zero,one]
\stoplinecorrection
We now add an extra iteration (resulting in the yellow points):
\startbuffer[two]
drawmidpoints (fullcircle xscaled 300pt yscaled 50pt, 2) ;
\stopbuffer
\typebuffer[two]
and get:
\startlinecorrection[blank]
\processMPbuffer[demo,zero,two,extra,one]
\stoplinecorrection
We don't even need that much iterations to get a good
result. The depth needed to get a good result depends on
the size of the pen and the resolution of the device on
which the curve is visualzied.
\startbuffer[zero]
drawoptions (withpen pencircle scaled 2pt withcolor .625red) ;
\stopbuffer
\startbuffer[three]
for i=1 upto 7 :
drawmidpoints (fullcircle
xscaled (300pt+i*10pt) yscaled (50pt+i*10pt), i) ;
endfor ;
\stopbuffer
\typebuffer[three]
Here we show 7 iterations in one graphic.
\startlinecorrection[blank]
\processMPbuffer[demo,zero,three]
\stoplinecorrection
In practice it is not that trivial to determine the depth
needed. The next example demonstrates how the resolution of
the result depends on the length and nature of the segment.
\startbuffer[four]
drawmidpoints (fullsquare
xscaled 300pt yscaled 50pt randomized (20pt,10pt), 5) ;
\stopbuffer
\typebuffer[four]
\startlinecorrection[blank]
\processMPbuffer[demo,zero,four]
\stoplinecorrection
\section{Inflection, tension and curl}
The \METAPOST\ manual describes the meaning of \type {...}
as \quotation {choose an inflection||free path between these
points unless the endpoint directions make this impossible}.
To use the words of David Arnold: a point of inflection is
where a path switches concavity, from concave up to concave
down, for example.
It is surprisingly difficult to find nice examples that
demonstrate the difference between \type {..} and \type
{...}, as it is often \quote {impossible} to honour the
request for less inflection. We will demonstrate this with a
few graphics.
In the four figures on the next pages, you will see that
\type {...} is not really suited for taming wild curves. If
you really want to make sure that a curve stays within
certain bounds, you have to specify it as such using control
or intermediate points. In the figures that follow, the gray
curves draw the random path using \type {..} on top of
yellow curves that use the \type {...} connection. As you
can see, in only a few occasions do the yellow \quote
{inflection} free curves show up.
For those who asked for the code that produces these
pictures, we now include it here. We use a macro \type
{sample} which we define as a usable graphic (nearly all
examples in this manual are coded in the document source).
\startbuffer
\startuseMPgraphic{sample}
def sample (expr rx, ry) =
path p, q ; numeric n, m, r, a, b ;
color c ; c := \MPcolor{lightgray} ;
a := 3mm ; b := 2mm ; r := 2cm ; n := 7 ; m := 5 ;
q := unitsquare scaled r xyscaled (n,m) shifted (.5r,.5r) ;
draw q withpen pencircle scaled (b/4) withcolor .625yellow;
for i=1 upto n : for j=1 upto m :
p := (fullcircle scaled r randomized (r/rx,r/ry))
shifted ((i,j) scaled r) ;
pickup pencircle scaled a ;
draw for k=0 upto length(p) :
point k of p .. endfor cycle withcolor c ;
draw for k=0 upto length(p) :
point k of p ... endfor cycle withcolor c ;
pickup pencircle scaled b ;
draw for k=0 upto length(p) :
point k of p .. endfor cycle withcolor .625yellow ;
draw for k=0 upto length(p) :
point k of p ... endfor cycle withcolor .625white ;
for k=0 upto length(p) :
draw point k of p withcolor .625red ;
endfor ;
endfor ; endfor ;
setbounds currentpicture to q ;
enddef ;
\stopuseMPgraphic
\stopbuffer
\typebuffer \getbuffer
As you see, not so much code is needed. The graphics
themselves were produced with a couple of commands like:
\startbuffer
\placefigure
{Circles with minimized inflection and 25\% randomized points.}
{\startMPcode
\includeMPgraphic{sample} ; sample(4,4) ;
\stopMPcode}
\stopbuffer
\typebuffer
\startpostponing
\placefigure
{Circles with minimized inflection and 25\% randomized points.}
{\startMPcode\includeMPgraphic{sample} ; sample(4,4) ; \stopMPcode}
\placefigure
{Circles with minimized inflection and 33\% randomized points.}
{\startMPcode\includeMPgraphic{sample} ; sample(3,3) ; \stopMPcode}
\page
\placefigure
{Circles with minimized inflection and 50\% randomized points.}
{\startMPcode\includeMPgraphic{sample} ; sample(2,2) ; \stopMPcode}
\placefigure
{Circles with minimized inflection and 100\% randomized points.}
{\startMPcode\includeMPgraphic{sample} ; sample(1,1) ; \stopMPcode}
\page
\stoppostponing
The tension specifier can be used to influence the curvature.
To quote the \METAPOST\ manual once more: \quotation {The
tension parameter can be less than one, but it must be at
least $3/4$}. The following paths are the same:
\starttyping
z1 .. z2
z1 .. tension 1 .. z2
z1 .. tension 1 and 1 .. z2
\stoptyping
The triple dot command \type {...} is actually a macro that
makes the following commands equivalent. Both commands will
draw identical paths.
\starttyping
z1 ... z2
z1 .. tension atleast 1 .. z2
\stoptyping
The \type {atleast} directive tells \METAPOST\ to do some
magic behind the screens. Both the $3/4$ and the \type
{atleast} lead directly to the question: \quotation {What,
exactly, is the influence of the tension directive?} We will
try to demystify \type {tension} specifier through a
sequence of graphics.
\startbuffer
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
def sample (expr p, c) =
draw p withpen pencircle scaled 2.5mm withcolor white ;
draw p withpen pencircle scaled 2.0mm withcolor c ;
enddef ;
for i=.75 step .05 until 1 :
sample (z1 .. tension i .. z2 .. z3, .625red) ;
endfor ;
for i=1 step .05 until 2 :
sample (z1 .. tension i .. z2 .. z3, .625yellow) ;
endfor ;
sample (z1 .. z2 .. z3, .625white) ;
sample (z1 ... z2 ... z3, .625white) ;
\stopbuffer
\typebuffer
Indeed values less than .75 give an error message, but large
values are okay. As you can see, the two gray curves are the
same. Here, \type {atleast 1} means~1, even if larger
values are useful.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\startbuffer
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
def sample (expr p, c) =
draw p withpen pencircle scaled 2.5mm withcolor white ;
draw p withpen pencircle scaled 2.0mm withcolor c ;
enddef ;
for i=.75 step .05 until 1 :
sample (z1 .. tension i and 2i .. z2 .. z3, .625red) ;
endfor ;
for i=1 step .05 until 2 :
sample (z1 .. tension i and 2i .. z2 .. z3, .625yellow) ;
endfor ;
sample (z1 .. z2 .. z3, .625white) ;
sample (z1 ... z2 ... z3, .625white) ;
\stopbuffer
Curves finally are made up out of points, and each point has
two control points. Since the \type {tension} specifier
finally becomes a control point, it is not surprising that
you may specify two tension values. If we replace the
tension in the previous example by
\starttyping
.. tension i and 2i ..
\stoptyping
we get the following graphic:
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\startbuffer
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
def sample (expr p, c) =
draw p withpen pencircle scaled 2.5mm withcolor white ;
draw p withpen pencircle scaled 2.0mm withcolor c ;
enddef ;
for i=.75 step .05 until 1 :
sample (z1 .. tension 2i and i .. z2 .. z3, .625red) ;
endfor ;
for i=1 step .05 until 2 :
sample (z1 .. tension 2i and i .. z2 .. z3, .625yellow) ;
endfor ;
sample (z1 .. z2 .. z3, .625white) ;
sample (z1 ... z2 ... z3, .625white) ;
\stopbuffer
If we swap both values (\type {.. tension 2i and i ..}) we
get:
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\startbuffer[a]
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
def sample (expr p, c) =
drawpath p withpen pencircle scaled 2.5mm withcolor c ;
drawcontrollines p withcolor c ;
drawpoints p ;
drawcontrolpoints p ;
enddef ;
\stopbuffer
We mentioned control points. We will now draw a few extreme
tensions and show the control points as \METAPOST\ calculates
them.
\startbuffer[b]
sample (z1 .. tension 0.75 .. z2 .. z3, .625red) ;
sample (z1 .. tension 2.00 .. z2 .. z3, .625yellow) ;
sample (z1 .. z2 .. z3, .625white) ;
\stopbuffer
\typebuffer[b]
First we will show the symmetrical tensions.
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
The asymetrical tensions are less prominent. We use the
following values:
\startbuffer[b]
sample (z1 .. tension .75 and 10 .. z2 .. z3, .625red) ;
sample (z1 .. tension 10 and .75 .. z2 .. z3, .625yellow) ;
sample (z1 .. z2 .. z3, .625white) ;
\stopbuffer
\typebuffer[b]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
What happens when you use the \METAPOST\ maximum value of
\type {infinity} instead of 10? Playing with this kind of
graphic can be fun, especially when we apply a few tricks.
\startbuffer
def sample (expr p, c) =
draw p withpen pencircle scaled 2.5mm withcolor white ;
draw p withpen pencircle scaled 2.0mm withcolor c ;
enddef;
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
for i=0 step .05 until 1 :
sample(z1 .. tension (.75+i) .. z2 .. z3, i[.625red,.625yellow]) ;
endfor;
\stopbuffer
\typebuffer
Here we change the color along with the tension. This
clearly demonstrates that we're dealing with a non linear
phenomena.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
We can (misuse) transparant colors to illustrate how the
effect becomes less with growing tension.
\startbuffer
def sample (expr p, c)=
draw p withpen pencircle scaled 2.0mm withcolor c ;
enddef;
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
for i=0 step .05 until 1 :
sample(z1 .. tension (.75+i) .. z2 .. z3, transparent(1,1-i,.625red)) ;
endfor;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
A third magic directive is \type {curl}. The curl is
attached to a point between \type {{ }}, like \type {{curl
2}}. Anything between curly braces is a direction specifier,
so instead of a \type {curl} you may specify a vector, like
\type {{(2,3)}}, a pair of numbers, as in \type {{2,3}}, or
a direction, like \type {{dir 30}}. Because vectors and
angles are straightforward, we will focus a bit on \type
{curl}.
\starttyping
z0 .. z1 .. z2
z0 {curl 1} .. z1 .. {curl 1} z2
\stoptyping
So, a \type {curl} of~1 is the default. When set to~1, the
begin and|/|or end points are approached. Given the following
definitions:
\startbuffer[a]
u := 1cm ; z1 = (0,0) ; z2 = (2u,4u) ; z3 = (4u,0) ;
def sample (expr p, c) =
draw p withpen pencircle scaled 2.5mm withcolor white ;
draw p withpen pencircle scaled 2.0mm withcolor c ;
enddef ;
\stopbuffer
\typebuffer[a]
We can draw three curved paths.
\startbuffer[b]
sample (z1 {curl 0} .. z2 .. {curl 0} z3, .625red) ;
sample (z1 {curl 2} .. z2 .. {curl 2} z3, .625yellow) ;
sample (z1 {curl 1} .. z2 .. {curl 1} z3, .625white) ;
\stopbuffer
\typebuffer[b]
The third (gray) curve is the default situation, so we
could have left the \type {curl} specifier out of the
expression.
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
\startbuffer[b]
sample (z1 {curl 0} .. z2 .. {curl 0} z3, .625red) ;
sample (z1 {curl infinity} .. z2 .. {curl infinity} z3, .625yellow) ;
sample (z1 {curl 1} .. z2 .. {curl 1} z3, .625white) ;
\stopbuffer
The curly specs have a lower bound of zero and no upper
bound. When we use \METAPOST\ maximum value of \type
{infinity} instead of~2, we get:
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
These curves were defined as:
\typebuffer[b]
It may sound strange, but internally \METAPOST\ can handle
larger values than \type {infinity}.
\startbuffer[b]
sample (z1 {curl infinity} .. z2 .. {curl infinity} z3, .625red) ;
sample (z1 {curl 4infinity} .. z2 .. {curl 4infinity} z3, .625yellow) ;
sample (z1 {curl 8infinity} .. z2 .. {curl 8infinity} z3, .625white) ;
\stopbuffer
\typebuffer[b]
Although this is quite certainly undefined behaviour,
interesting effects can be achieved. When you turn off
\METAPOST's first stage overflow catcher by setting \type
{warningcheck} to zero, you can go upto 8 times \type
{infinity}, which, being some $2^15$, is still far from what
today's infinity is supposed to be.
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
As the built||in \METAPOST\ command \type {..} accepts \type
{curl} and \type {tension} directive as described in this
section, you will now probably understand the following
plain \METAPOST\ definitions:
\starttyping
def -- = {curl 1} .. {curl 1} enddef ;
def --- = .. tension infinity .. enddef ;
def ... = .. tension atleast 1 .. enddef ;
\stoptyping
These definitions also point out why you cannot add
directives to the left or right side of \type {--}, \type
{---} and \type {...}: they are directives themselves!
\section {Transformations}
A \type {transform} is a vector that is used in what is
called an affine transformation. To quote the \METAPOST\
manual:
\startquotation
If $p=(p_x,p_y)$ is a pair and $T$ is a transform, then
\type {p transform T} is a pair of the form:
\startformula
(t_x + t_{xx} p_x + t_{xy} p_y, t_y + t_{yx} p_x + t_{yy} p_y)
\stopformula
where the six numeric quantities $(t_x, t_y, t_{xx}, t_{xy},
t_{yx}, t_{yy})$ determine T.
\stopquotation
In literature concerning \POSTSCRIPT\ and \PDF\ you will find
many references to such transformation matrices. A matrix of
$(s_x,0,0,s_y,0,0)$ is scaling by $s_x$ in the horizontal
direction and $s_y$ in the vertical direction, while
$(1,0,t_x,1,0,t_y)$ is a shift over $t_x,t_y$. Of course
combinations are also possible.
Although these descriptions seem in conflict with each other
in the nature and order of the transform components in the
vectors, the concepts are the same. You normally populate
transformation matrices using \type {scaled}, \type
{shifted}, \type {rotated}.
\starttyping
transform t ; t := identity shifted (a,b) rotated c scaled d ;
path p ; p := fullcircle transformed t ;
\stoptyping
The previous lines of code are equivalent to:
\starttyping
path p ; p := fullcircle shifted (a,b) rotated c scaled d ;
\stoptyping
You always need a starting point, in this case the identity
matrix \type {identity}: $(0,0,1,0,0,1)$. By the way, in
\POSTSCRIPT\ the zero vector is $(1,0,0,1,0,0)$. So, unless
you want to extract the components using \type {xpart},
\type {xypart}, \type {xxpart}, \type {ypart}, \type
{yxpart} and|/|or \ \type {yypart}, you may as well forget
about the internal representation.
You can invert a transformation using the \type {inverse}
macro, which is defined as follows, using an equation:
\starttyping
vardef inverse primary T =
transform T_ ; T_ transformed T = identity ; T_
enddef ;
\stoptyping
Using transform matrices makes sense when similar
transformations need to be applied on many paths, pictures,
pens, or other transforms. However, in most cases you will
use the predefined commands \type {scaled}, \type
{shifted}, \type {rotated} and alike. We will now
demonstrate the most common transformations in a text
example.
\startbuffer[a]
draw btex \bfd MetaFun etex ;
draw boundingbox currentpicture withcolor .625yellow ;
\stopbuffer
\typebuffer[a]
Before a \METAPOST\ run, the \typ {btex ... etex}'s are
filtered from the file and passed on to \TEX. After that,
the \DVI\ file is converted to a list of pictures, which is
consulted by \METAPOST. We can manipulate these pictures
like any graphic as well as draw it with \type {draw}.
\startlinecorrection[blank]
\processMPbuffer[a]
\stoplinecorrection
We show the transformations in relation to the origin and
make the origin stand out a bit more by painting it a bit
larger in white first.
\startbuffer[c]
draw origin withpen pencircle scaled 1.5mm withcolor white ;
draw origin withpen pencircle scaled 1mm withcolor .625red
\stopbuffer
\typebuffer[c]
The origin is in the lower left corner of the picture.
\startlinecorrection[blank]
\processMPbuffer[a,c]
\stoplinecorrection
Because the transformation keywords are proper english, we
let the pictures speak for themselves.
% shifted
\startbuffer[b]
currentpicture := currentpicture shifted (0,-1cm) ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% rotated
\startbuffer[b]
currentpicture := currentpicture rotated 180 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% rotatedaround
\startbuffer[b]
currentpicture := currentpicture rotatedaround(origin,30) ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% scaled
\startbuffer[b]
currentpicture := currentpicture scaled 1.75 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% scaled
\startbuffer[b]
currentpicture := currentpicture scaled -1 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% xscaled
\startbuffer[b]
currentpicture := currentpicture xscaled 3.50 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% xscaled
\startbuffer[b]
currentpicture := currentpicture xscaled -1 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% yscaled
\startbuffer[b]
currentpicture := currentpicture yscaled .5 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% yscaled
\startbuffer[b]
currentpicture := currentpicture yscaled -1 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% slanted
\startbuffer[b]
currentpicture := currentpicture slanted .5 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% slanted
\startbuffer[b]
currentpicture := currentpicture slanted -.5 ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% zscaled
\startbuffer[b]
currentpicture := currentpicture zscaled (.75,.25) ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% reflectedabout
\startbuffer[b]
currentpicture := currentpicture
reflectedabout(llcorner currentpicture,urcorner currentpicture) ;
\stopbuffer
\page[preference] \typebuffer[b] \page[no]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
% reverse counterclockwise turningnumber
A path has a certain direction. When the \type
{turningnumber} of a path is larger than zero, it runs in
clockwise direction. The \METAPOST\ primitive \type
{reverse} changes the direction, while the macro \type
{counterclockwise} can be used to get a path running in a
well defined direction.
\startbuffer
drawoptions(withpen pencircle scaled 2pt withcolor .625red) ;
path p ; p := fullcircle scaled 1cm ;
drawarrow p ;
drawarrow reverse p shifted (2cm,0) ;
drawarrow counterclockwise p shifted (4cm,0) ;
drawarrow counterclockwise reverse p shifted (6cm,0) ;
drawarrow reverse counterclockwise p shifted (8cm,0) ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\section {Only this far}
When you take a close look at the definitions of the
Computer Modern Roman fonts, defined in the \METAFONT\ book,
you will notice a high level of abstraction. Instead of hard
coded points you will find points defined in terms of \quote
{being the same as this point} or \quote {touching that
point}. In this section we will spend some time on this
touchy aspect.
\startbuffer[a]
pickup pencircle scaled 2mm ;
path p ; p := fullsquare scaled 2cm ;
draw p withcolor .625white ;
\stopbuffer
\startlinecorrection[blank]
\processMPbuffer[a]
\stoplinecorrection
This rectangle is a scaled instance of the predefined
\METAFUN\ path \type {fullsquare} which is centered around
the origin.
\typebuffer[a]
On this path, halfway between two of its corners, we define
a point \type {q}:
\startbuffer[b]
pair q ; q := .5[llcorner p, lrcorner p] ;
\stopbuffer
\typebuffer[b]
We draw this point in red, using:
\startbuffer[c]
draw q withcolor .625red ;
\stopbuffer
\typebuffer[c]
As you can see, this point is drawn on top of the path.
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
There are four of those midpoints, and when we connect them,
we get:
\startbuffer[c]
draw q -- q rotated 90 -- q rotated 180 --
q rotated 270 -- cycle withcolor .625red ;
\stopbuffer
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
Because path \type {p} is centered around the origin, we can
simply rotate point \type {q} a few times.
\typebuffer[c]
There are situations, where you don't want the red path to be
drawn inside another path, or more general: where you want
points to touch instead of being overlayed.
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
We can achieve this by defining point \type {q} to be located
on top of the midpoint.
\startbuffer[b]
pair q ; q := top .5[llcorner p, lrcorner p] ;
\stopbuffer
\typebuffer[b]
The predefined macro \type {top} moves the point over the
distance similar to the current pen width.
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
Because we are dealing with two drawing operations, and
since the path inside is drawn through the center of points,
we need to repeat this move in order to draw the red path
really inside the other one.
\startbuffer[b]
pair q ; q := top top .5[llcorner p, lrcorner p] ;
\stopbuffer
\typebuffer[b]
Operations like \type {top} and its relatives \type {bot},
\type {lft} and \type {rt} can be applied sequentally.
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
We already showed that \type {q} was defined as a series of
rotations.
\typebuffer[c]
As an intermezzo we will show an alternative definition of
\type {q}. Because each point is rotated 90 degrees more, we
can define a macro that expands into the point and rotates
afterwards. Because each consecutive point on the path is
rotated an additional 90 degrees, we use the \METAPOST\
macro \type {hide} to isolate the assignment. The \type
{hide} command executes the hidden command and afterwards
continues as if it were never there. You must confuse this
with grouping, since the hidden commands are visible to its
surroundings.
\startbuffer[c]
def qq = q hide(q := q rotated 90) enddef ;
draw qq -- qq -- qq -- qq -- cycle withcolor .625red ;
\stopbuffer
\typebuffer[c]
The macro \type {top} uses the characteristics of the
current pen to determine the displacement. However, for the
more complicated pen shapes we need a different trick to get
an inside path. Let's start by defining a elliptical path.
\startbuffer[a]
pickup pencircle xscaled 3mm yscaled 5mm rotated 30 ;
path p ; p := fullcircle xscaled 6cm yscaled 3cm ;
draw p withcolor .625white ;
\stopbuffer
\typebuffer[a]
We draw this path using a non standard pen. In the \METAFONT\
manual you will find methods to draw shapes with similar
pens, where the pen is also turning, as it does in real
calligraphy. Here we stick to a more simple one.
\startlinecorrection[blank]
\processMPbuffer[a]
\stoplinecorrection
We construct the inner path from the points that make up the
curve. Watch how we use a for loop to compose the new path.
When used this way, no semi colon may be used to end the
loop, since it would isolate the color directive.
\startbuffer[b]
draw point 0 of p
for i=1 upto length(p) : -- point (i) of p endfor
withcolor .625red ;
\stopbuffer
\typebuffer[b]
The points are still located on the original path.
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
We can move the points to the inside by shifting them over
the penwidth in the direction perpendicular to the point
which is a good approximation. Because we use this
transformation more than once, we wrap it into a macro. This
also keeps the code readable.
\startbuffer[b]
vardef inside expr pnt of p =
(point pnt of p shifted
-(penoffset direction pnt of p of currentpen))
enddef ;
draw inside 0 of p
for i=1 upto length(p) : -- inside i of p endfor
withcolor .625red ;
\stopbuffer
\typebuffer[b]
Whenever you define a pen, \METAPOST\ stores its
characteristics in some private variables which are used in
the \type {top} and alike directives. The \type {penoffset}
is a built in primitive and is defined as the \quotation
{point on the pen furthest to the right of the given
direction}. Deep down in \METAPOST\ pens are actually simple
paths and therefore \METAPOST\ has a notion of a point on
the penpath. In the \METAFONT\ book and \METAPOST\ manual
you can find in depth discussions on pens.
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
We're still not there. Like in a previous example, we need
to shift over twice the pen width. To get good results, we
should determine the width of the pen at that particular
point, which is not trivial. The more general solution,
which permits us to specify the amount of shifting, is as
follows.
\startbuffer[b]
vardef penpoint expr pnt of p =
save n, d ; numeric n, d ;
(n,d) = if pair pnt : pnt else : (pnt,1) fi ;
(point n of p shifted
((penoffset direction n of p of currentpen) scaled d))
enddef ;
\stopbuffer
\typebuffer[b]
When the point specification is extended with a distance,
in which case we have a pair expression, the point and
distance are derived from this specification. First we
demonstrate the simple case:
\startbuffer[c]
draw penpoint 0 of p
for i=1 upto length(p)-1 : .. penpoint i of p endfor .. cycle
withcolor .625red ;
\stopbuffer
\typebuffer[c]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
In the next graphic, we draw both an inner and and outer
path.
\startbuffer[c]
draw penpoint (0,-2) of p
for i=1 upto length(p)-1 : .. penpoint (i,-2) of p endfor .. cycle
withcolor .625red ;
draw penpoint (0,+2) of p
for i=1 upto length(p)-1 : .. penpoint (i,+2) of p endfor .. cycle
withcolor .625yellow ;
\stopbuffer
\typebuffer[c]
\startlinecorrection[blank]
\processMPbuffer[a,b,c]
\stoplinecorrection
\startbuffer[a]
path p, q, r ;
p := fullcircle scaled 3cm ;
q := p shifted (7cm,0cm) ;
r := center p -- center q ;
\stopbuffer
\startbuffer[b]
pair pr, qr ;
pr := p intersectionpoint r ;
qr := q intersectionpoint r ;
r := r cutbefore pr cutafter qr ;
\stopbuffer
\startbuffer[c]
r := r cutbefore (point 5pt on r) ;
r := r cutafter (point -5pt on r) ;
\stopbuffer
\startbuffer[cc]
r := r cutends 5pt ;
\stopbuffer
\startbuffer[d]
draw p withpen pencircle scaled 10pt withcolor .625red ;
draw q withpen pencircle scaled 10pt withcolor .625yellow ;
draw r withpen pencircle scaled 20pt withcolor .625white ;
\stopbuffer
\startbuffer[dd]
draw r withpen pencircle scaled 20pt withcolor .625white ;
draw p withpen pencircle scaled 10pt withcolor .625red ;
draw q withpen pencircle scaled 10pt withcolor .625yellow ;
\stopbuffer
Another case when \type {top} and friends cannot be applied
in a general way is the following. Consider the three paths:
\typebuffer[a]
We draw these paths with:
\typebuffer[d]
The line is drawn from center to center and since the line
has a non zero width and a round line cap, it extends beyond
this point.
\startlinecorrection[blank]
\processMPbuffer[a,d]
\stoplinecorrection
If we want to line to stop at the circular paths, we can cut
off the pieces that extend beyond those paths.
\typebuffer[b]
This time we get:
\startlinecorrection[blank]
\processMPbuffer[a,b,d]
\stoplinecorrection
Due to the thicker line width used when drawing the
straight line, part of that line is still visible inside
the circles. So, we need to clip off a bit more. \footnote
{This problem was posted at the \CONTEXT\ mailing list by
Marc van Dongen.}
\typebuffer[c]
The \type {point ... on} operation is a \METAFUN\ macro that
takes a dimension.
\startlinecorrection[blank]
\processMPbuffer[a,b,c,d]
\stoplinecorrection
In order to save you some typing, \METAFUN\ provides a macro
\type {cutends} that does the same job:
\typebuffer[cc]
This time we draw the path in a different order:
\typebuffer[dd]
That way we hide the still remaining overlapping part of the
line.
\startlinecorrection[blank]
\processMPbuffer[a,b,cc,dd]
\stoplinecorrection
\section {Directions}
Quite often you have to tell \METAPOST\ in what direction a
line should be drawn. A direction is specified as a vector.
There are four predefined vectors: \type {up}, \type {down},
\type {left}, \type {right}. These are defined as follows:
\starttyping
pair up, down, left, right ;
up = -down = (0,1) ; right = -left = (1,0) ;
\stoptyping
We can use these predefined pairs as specifications and in
calculations.
\startbuffer
dotlabel.top("up" , up * 1cm) ;
dotlabel.bot("down" , down * 1cm) ;
dotlabel.lft("left" , left * 1cm) ;
dotlabel.rt ("right", right * 1cm) ;
drawoptions (withpen pencircle scaled .25mm withcolor .625 red) ;
drawarrow origin -- up * 1cm ;
drawarrow origin -- down * 1cm ;
drawarrow origin -- left * 1cm ;
drawarrow origin -- right * 1cm ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
This graphic can also be defined in a more efficient (but
probably more cryptic) way. The next definition demonstrates
a few nice tricks. Instead of looping over the four
directions, we loop over their names. Inside the loop we
convert these names, or strings, into a pair by scanning the
string using \type {scantokens}. The \type {freedotlabel}
macro is part of \METAFUN\ and takes three arguments: a
label string (or alternatively a picture), a point
(location), and the \quote {center of gravity}. The label is
positioned in the direction opposite to this center of
gravity.
\startbuffer
pair destination ;
for whereto = "up", "down", "left", "right" :
destination := scantokens(whereto) * 1cm ;
freedotlabel(whereto, destination, origin) ;
drawarrow origin -- destination
withpen pencircle scaled .25mm withcolor .625 red ;
endfor ;
\stopbuffer
\typebuffer
So, in this code fragment, we use the string as string and
(by means of \type {scantokens}) as a point or vector.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
The previous definition is a stepping stone to the next one.
This time we don't use points, but the \type {dir} command.
This command converts an angle into an unitvector.
\startbuffer
pair destination ;
for whereto = 0 step 30 until 330 :
destination := dir(whereto) * 1.5cm ;
freedotlabel(decimal whereto, destination, origin) ;
drawarrow origin -- destination
withpen pencircle scaled .25mm withcolor .625 red ;
endfor ;
\stopbuffer
\typebuffer
In \METAPOST\ the angles go counter clockwise, which is
not that illogical if you look at it from the point of view
of vector algebra.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\section {Analyzing pictures}
{\em Unless you really want to know all details, you can
safely skip this section. The \METAPOST\ features discussed
here are mainly of importance when you write (advanced)
macros.}
Later we will discuss in detail how you can use either
\METAPOST\ or \TEX\ to typeset text (\in {section}
[sec:text] and \in {chapter} [sec:typesetting]), so here we
limit our exploration to a quick introduction. The most
direct way of processing text in \METAPOST\ is using the
\type {infont} operator.
\startbuffer[mp]
draw "this string will become a sequence of glyphs (MP)"
infont defaultfont scaled defaultscale ;
\stopbuffer
\typebuffer[mp]
The text between \type {"} is passed to \TEX, and the
resulting \DVI\ will be converted into a picture with
textual components. So, we get:
\startlinecorrection[blank]
\processMPbuffer[mp]
\stoplinecorrection
The same string typeset by \TEX\ shows up as:
\blank
this string will become a sequence of glyphs (\TeX)
\blank
The following \METAPOST\ features are not covered by the
\METAPOST\ manual, but most of them are discussed in the
appendix of the \type {graph} package written by John Hobby.
It is possible to disassemble a picture by means of a
special for loop using the \type {within} specifier. The
following code walks over a picture and draws the components
with their bounding boxes.
\startbuffer[show]
for i within currentpicture :
draw boundingbox i withcolor .625yellow ;
endfor ;
\stopbuffer
\typebuffer[show]
We can use the disassemble loop feature to look into the
previously shown example text.
\startlinecorrection[blank]
\processMPbuffer[mp,show]
\stoplinecorrection
The second line is typeset by \TEX. The resulting \DVI\ code
is converted into a series of pictures, which \METAPOST\
pastes into one picture. You may also notice that in the set
of pictures that originate in \TEX, the space is replaced by a
shift (this is because \TEX\ knows no space).
An interesting aspect of this \quote {loop over a picture}
feature, is that it can provide insight in how \TEX\ is
composing a paragraph.
\startbuffer
draw btex \framed[width=fit,align=middle]{\input tufte \relax} etex ;
for i within currentpicture :
draw boundingbox i withpen pencircle scaled .2pt withcolor .625yellow ;
endfor ;
\stopbuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
You may also notice, that rules produced by \TEX\ are
converted to straight line segments. Because the line
extends 50\% of its linewidth beyond a point, there is a
slight overshoot. This picture was defined in a few lines:
\typebuffer
If we use a Times Roman instead of a Palatino, we get quite
different results.
\startlinecorrection[blank]
\startMPenvironment
% \let\fontclass\empty
\usetypescript[times][texnansi]
\switchtobodyfont[times,10pt]
\stopMPenvironment
\processMPbuffer
\stoplinecorrection
In \CONTEXT, you can easily change the body font for
\METAPOST\ graphics with directives like:
\starttyping
\startMPenvironment
\usetypescript[times][texnansi]
\switchtobodyfont[times,10pt]
\stopMPenvironment
\stoptyping
This font has far less kerning. Even more interesting is the
Lucida Bright Handwriting font, which is defined in such a
way that no kerning is needed at all.
\startlinecorrection[blank]
\resetMPenvironment
\startMPenvironment
% \let\fontclass\empty
\usetypescript[lucida][texnansi]
\switchtobodyfont[lucida,hw,10pt]
\stopMPenvironment
\processMPbuffer
\stoplinecorrection
You can ask for the number of components with \type
{length}. A component can be a stroked or filled path, or a
text resulting from an \type {infont} operation. If the
(last) path is a clip path, or when the whole picture has a
forced boundingbox, the picture is treated as a whole. We
will demonstrate this later.
You may wonder if this \type {within} loop construct has any
real application, and as you can expect, it has. In \in
{section} [sec:color circles] a macro is defined that draws
a colored circle. If you want the inverted alternative, you
can pass the inverted color specification, but wouldn't it
be more convenient if there was an operator that did this
for you automatically? Unfortunately there isn't one so we
have to define one ourselves a macro.
\startbuffer
colorcircle(4cm,(.4,.6,.8),(.4,.8,.6),(.6,.4,.8)) ;
addto currentpicture also inverted currentpicture shifted (5cm,0) ;
\stopbuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
These circles were drawn using:
\typebuffer
When we \type {draw} a path, or stroke a path, as it is called
officially, we actually perform an addition:
\starttyping
addto currentpicture doublepath somepath
\stoptyping
The \type {fill} command is actually:
\starttyping
addto currentpicture contour somepath
\stoptyping
We will need both \type {doublepath} and \type {contour}
operations in the definition of \type {inverted}.
When \METAPOST\ has digested a path into a picture, it
keeps track of some characteristics. We can ask for them
using \type {part...} operators. The following operators can
be applied to a transform vector (one of \METAPOST's data
types), but also to a picture. Say that we have drawn a
circle:
\startbuffer[a]
draw fullcircle
xscaled 3cm yscaled 2cm
dashed dashpattern(on 3mm off 3mm)
withpen pencircle scaled 1mm
withcolor .625red ;
picture p ; p := currentpicture ;
\stopbuffer
\typebuffer[a]
This circle looks like:
\startlinecorrection[blank]
\processMPbuffer[a]
\stoplinecorrection
We can now ask for some of the characteristics of \type
{currentpicture}, like its color. We could write the values
to the log file, but it is more convenient to put them on
paper.
\startbuffer[b]
label.rt("redpart: " & decimal redpart p, (4cm,+.5cm)) ;
label.rt("greenpart: " & decimal greenpart p, (4cm, 0cm)) ;
label.rt("bluepart: " & decimal bluepart p, (4cm,-.5cm)) ;
\stopbuffer
\typebuffer[b]
Here the \type {&} glues strings together, while the decimal
operator converts a number into a string.
The result has no typographic beauty |<|keep in mind that
here we use \METAPOST\ to typeset the text|>|but the result
serves its purpose.
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
We can also ask for the path itself (\type {pathpart}), the
pen (\type {penpart}) and the dashpattern (\type
{dashpart}), but these can only be assigned to variables of
the corresponding type.
A path can be stroked or filled, in which case it is a
cyclic path. It can have a non natural bounding box, be a
clip path, consist of line segments or contain text. All
these characteristics can be tested.
\startbuffer[b]
label.rt("filled: " & condition filled p, (4cm,+1.25cm)) ;
label.rt("stroked: " & condition stroked p, (4cm,+0.75cm)) ;
label.rt("textual: " & condition textual p, (4cm,+0.25cm)) ;
label.rt("clipped: " & condition clipped p, (4cm,-0.25cm)) ;
label.rt("bounded: " & condition bounded p, (4cm,-0.75cm)) ;
label.rt("cycle: " & condition cycle pathpart p, (4cm,-1.25cm)) ;
\stopbuffer
\typebuffer[b]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
In this code snippet, \type {condition} is a macro that
takes care of translating a boolean value into a string (like
\type {decimal} does with a numeric value).
\starttyping
def condition primary b =
if b : "true" else : "false" fi
enddef ;
\stoptyping
Clip paths and bounding boxes are kind of special in the
sense that they can obscure components. The following
examples demonstrate this. In case of a clip path or
bounding box, the \type {pathpart} operator returns this
path. In any case that asking for a value does not make
sense |<|a clipping path for instance has no color|>| a zero
(null) value is returned.
\startbuffer[b]
n := 0 ;
for i within currentpicture : n := n + 1 ;
label("n: " & decimal n & " / " &
"length: " & decimal length i & " / " &
"stroked: " & condition stroked i & " / " &
"clipped: " & condition clipped i & " / " &
"bounded: " & condition bounded i , (0,-n*.5cm)) ;
endfor ;
\stopbuffer
\startbuffer[a]
draw fullcircle withpen pencircle scaled 3mm ;
clip currentpicture to fullcircle ;
setbounds currentpicture to fullcircle ;
\stopbuffer
\typebuffer[a]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
\startbuffer[a]
draw fullcircle withpen pencircle scaled 3mm ;
setbounds currentpicture to fullcircle ;
clip currentpicture to fullcircle ;
\stopbuffer
\typebuffer[a]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
\startbuffer[a]
clip currentpicture to fullcircle ;
draw fullcircle withpen pencircle scaled 3mm ;
setbounds currentpicture to fullcircle ;
\stopbuffer
\typebuffer[a]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
\startbuffer[a]
clip currentpicture to fullcircle ;
setbounds currentpicture to fullcircle ;
draw fullcircle withpen pencircle scaled 3mm ;
\stopbuffer
\typebuffer[a]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
\startbuffer[a]
setbounds currentpicture to fullcircle ;
clip currentpicture to fullcircle ;
draw fullcircle withpen pencircle scaled 3mm ;
\stopbuffer
\typebuffer[a]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
\startbuffer[a]
setbounds currentpicture to fullcircle ;
draw fullcircle withpen pencircle scaled 3mm ;
clip currentpicture to fullcircle ;
\stopbuffer
\typebuffer[a]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
The description lines were generated by the following loop:
\typebuffer[b]
If we have a textual picture, we can also ask for the text
and font. Take the following picture:
\startbuffer[a]
picture p ;
p := "MetaFun" infont defaultfont scaled 2 rotated 30 slanted .5 ;
p := p shifted (0,-ypart center p) ;
currentpicture := p ;
\stopbuffer
\typebuffer[a]
Here we can ask for:
\startbuffer[b]
label.rt("textpart: " & textpart p, (4cm,+0.25cm)) ;
label.rt("fontpart: " & fontpart p, (4cm,-0.25cm)) ;
\stopbuffer
\typebuffer[b]
and get:
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
If we're dealing with a path, the transformations have ended
up in the path specification. If we have a text picture, we
can explicitly ask for the transform components.
\startbuffer[b]
label.rt("xpart: " & decimal xpart p, (4cm,+1.25cm)) ;
label.rt("ypart: " & decimal ypart p, (4cm,+0.75cm)) ;
label.rt("xxpart: " & decimal xxpart p, (4cm,+0.25cm)) ;
label.rt("xypart: " & decimal xypart p, (4cm,-0.25cm)) ;
label.rt("yxpart: " & decimal yxpart p, (4cm,-0.75cm)) ;
label.rt("yypart: " & decimal yypart p, (4cm,-1.25cm)) ;
\stopbuffer
\typebuffer[b]
\startlinecorrection[blank]
\processMPbuffer[a,b]
\stoplinecorrection
We will now define the \type {inverted} macro using these
primitives. Because we have to return a picture, we cannot
use \type {draw} and \type {fill} but need to use the low
level operators. Because a picture can consist of more than
one path, we need a temporary picture \type {pp}.
\starttyping
vardef inverted expr p =
save pp ; picture pp ; pp := nullpicture ;
for i within p :
addto pp
if stroked i or filled i :
if filled i : contour else : doublepath fi pathpart i
dashed dashpart i withpen penpart i
else :
also i
fi
withcolor white-(redpart i, greenpart i, bluepart i) ;
endfor ;
pp
enddef ;
\stoptyping
We probably need to handle a few more border cases, but for
general purposes, this macro works as expected.
From the previous examples it may be clear that each picture
has some associated data stored with it. From the \type
{bounded} boolean test we can deduce that the bounding box
is part of this data Internally \METAPOST\ keeps track of
two bounding boxes: the natural one, and the forced one. The
forced one is actually a component of the picture which
applies to all previously added graphics. You can calculate
the bounding box from the \type {llcorner} and \type
{urcorner} or if you like \type {ulcorner} and \type
{lrcorner} and the \METAFUN\ command \type {boundingbox}
does so.
The four corners that make up the bounding box are either
the natural ones, or the ones forced by \type {setbounds}.
You can force \METAPOST\ to report the natural ones by
setting \type {truecorners} to~1. The next example
demonstrates this feature.
\startbuffer
pickup pencircle scaled 2mm ; path p, q ;
draw fullcircle
scaled 4cm slanted .5 withcolor .625white ;
setbounds currentpicture to
boundingbox currentpicture enlarged -5mm ;
interim truecorners := 0 ; p := boundingbox currentpicture ;
interim truecorners := 1 ; q := boundingbox currentpicture ;
pickup pencircle scaled 1mm ;
draw p withcolor .625red ;
draw q withcolor .625yellow ;
\stopbuffer
\typebuffer
We use \type {interim} because \type {truecorners} is an
internal \METAPOST\ variable.
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
Since \METAPOST\ can handle fonts (it can even generate font
metric files) it is no surprise that we can also ask for the
natural size of a font. For this we use \type {fontsize}.
However, you should beware of the fact that the size
reported is in base points. Since this is \METAPOST's native
unit, this is no problem in calculations, but it may look
confusing when you \type {show} this size on your terminal
and get less that 10 reported for a \type {cmr10} font.
\starttyping
show fontsize "cmr10" ;
\stoptyping
In order to demonstrate that \type {fontsize} is useful, we
extend the \type {infont} command. In the process we show a
few macro definition tricks. What we want is a \TEX\ like
specification of a font size:
\startbuffer[txt]
draw "MetaFun" infont defaultfont at 20pt ;
\stopbuffer
\typebuffer[txt]
We can store the current meaning of a primitive or macro in
a new macro. We do so with \type {infont}:
\startbuffer[a]
let normalinfont = infont ;
\stopbuffer
\typebuffer[a]
We can only know the size if we know the name of the font,
so we have to redefine \type {infont} to pick up this name.
\startbuffer[b]
numeric lastfontsize ; lastfontsize = fontsize defaultfont ;
\stopbuffer
\startbuffer[c]
def infont primary name =
hide(lastfontsize := fontsize name)
normalinfont name
enddef ;
\stopbuffer
\typebuffer[c]
Because we are replacing an operator, and since \METAPOST\
expects one, we have to use \type {def} instead of \type
{vardef} (which is actually a kind of variable). For the
same reason, we have to pick up a \type {primary}. If we
would use a \typ {expr name}, we would end up in an
unwanted look ahead. The \type {hide} macro hides the
assignment and makes this macro behave like a \type
{vardef} with respect to hiding expressions. We may not put
a semi colon after the \type {)} because it would stop
\METAPOST\ from reading on, and thereby invoke an error
message.
We can now define \type {at}. This macro picks up an
expression (which can be more than just a number) and
return a scale transform that normalizes the given size to
the design size.
\startbuffer[d]
def at expr size =
scaled (size/lastfontsize)
enddef ;
\stopbuffer
\typebuffer[d]
Because this macro is defined global, and therefore can be
used apart from \type {infont}, we predefine the size:
\typebuffer[b]
When defined this way \type {at} a comfortable 20 points, the
string \type {MetaFun} comes out as follows:
\startlinecorrection[blank]
\processMPbuffer[a,b,c,d,txt]
\stoplinecorrection
\section {Pitfalls}
When writing macros, you need to be careful in what
operations apply to what object. There is for instance a
difference between the following code:
\startbuffer
pickup pencircle scaled 2pt ;
draw (0,0)--(0,1)--(1,1) scaled 1cm withcolor .625 red ;
draw ((0,0)--(0,1)--(1,1)) scaled 2cm withcolor .625 yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
The \type {scaled} operates on the previous expression which
in the first case is the point \type {(1,1)} and in the
second case the whole path.
\startbuffer
pickup pencircle scaled 2pt ;
draw (0,0)--(0,1)--(1,1)--cycle scaled 1cm withcolor .625 red ;
draw ((0,0)--(0,1)--(1,1)--cycle) scaled 2cm withcolor .625 yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
Here the last element in the first case is not the cycle,
and the next alternative does not help us much in
discovering what is going on. (Well, at least something
{\em is} going on, because the result seems to have some
dimensions.)
\startbuffer
pickup pencircle scaled 2pt ;
draw (1,1)--cycle scaled 1cm withcolor .625 red ;
draw ((1,1)--cycle) scaled 1cm withcolor .625 yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
The next lines demonstrate that we're dealing with the dark
sides of \METAPOST, and from that we may conclude that in
case of doubt it's best to add parenthesis when such fuzzy
situations threaten to occur.
\startbuffer
pickup pencircle scaled 2pt ;
draw (0,1)--(1,1)--cycle scaled 1cm withcolor .625 red ;
draw ((0,1)--(1,1)--cycle) scaled 1cm withcolor .625 yellow ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
There are more cases where the result may surprise you. Take
the following code:
\startbuffer
drawarrow ((0,0)--(10,0))
withpen pencircle scaled 2pt
withcolor red randomized (.4,.9) ;
currentpicture := currentpicture scaled 8 ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
The arrow is made up out of two pieces and each piece gets a
different shade of red. This is because the attributes are
collected and applied to each of the components that make up
the arrow. Because for each component the attribute code is
expanded again, we get two random colors. One way around
this is to apply the color afterwards.
\startbuffer
draw
image (drawarrow ((0,0)--(10,0)) withpen pencircle scaled 2pt)
scaled 8 withcolor red randomized (.4,.9) ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
Here the \type {image} macro creates a picture and as you
can see, this provides a way to draw within a draw
operation.
Once you see the benefits of \type {image}, you will use it
frequently. Another handy (at first sight strange) macro is
\type {hide}. You can use this in situations where you
don't want code to interfere.
\starttyping
def mydraw text t =
boolean error ; error := false ;
def withpencil expr p = hide (error := true) enddef ;
draw t ;
if error : message "pencils are not supported here" fi ;
enddef ;
mydraw fullcircle scaled 10cm withpencil sharp ;
\stoptyping
Here, setting the boolean normally interferes with the draw
operation, but by hiding the assignment, this code becomes
valid. This code will bring the message to your terminal and
log file.
Once you start using expressions you have a good chance of
encountering messages with regards to redundant expressions.
The following code is for instance a recipe for problems:
\starttyping
z1 = (1,0) ; z1 = (2,0) ;
\stoptyping
Changing the \type {=} into \type {:=} helps, but this may
not be what you want.
Because the \type {z}||variables are used frequently, they
are reset each figure. You can also reset them yourself,
using the \type {clearxy} macro. The \METAFUN\ version
clears all \type {z}||variables, unless you explictly
specify what variables to reset. \footnote {This version
resulted from a discussion on the \METAFONT\ discussion
list and is due to Bogus\l{}aw Jackowski.} If you want to
play with this macro, see what happens when you run the
following code:
\starttyping
show x0 ; z0 = (10,10) ;
show x0 ; x0 := whatever ; y0 := whatever ;
show x0 ; z0 = (20,20) ;
show x0 ; clearxy 0 ;
show x0 ; z0 = (30,30) ;
\stoptyping
So, the following calls are all legal:
\starttyping
clearxy ; clearxy 1 ; clearxy 1, 8, 10 ;
\stoptyping
Keep in mind that for each figure a full clear is done
anyway. You should not confuse this command with \type
{clearit}, which clears \type {currentpicture}.
\section {\TeX\ versus \MetaPost}
If you are defining your own \TEX\ and \METAPOST\ macros,
you will notice that there are a couple of essential
differences between the two macro languages. In \TEX\ the
following code is invalid. \footnote {In \ETEX\ the
calculation can be done in less lines using a \type
{\numexpr}.}
\starttyping
\def\fancyplied#1%
{\ifnum#1=0
\message{zero argument}%
\fi
\count0=#1 \multiply \count0 by \count0
\count2=#1 \multiply \count2 by 2
\count4=#1 \divide \count4 by 2
\advance \count0 by \count2
\advance \count0 by \count4
\count4 }
\hskip \fancyplied{3} pt
\stoptyping
This is because \TEX\ is very strict in what tokens it
expects next. In \METAPOST\ however, you can use \type
{vardef}'d macros to hide nasty intermediate calculations.
\starttyping
vardef fancyplied expr x =
if x=0 : message "x is zero" ; (x*x+2x+x/2)
enddef ;
a := a shifted (fancyplied 3pt,0) ;
\stoptyping
Hiding intermediate calculations and manipulations is a very
strong point of \METAPOST.
Another important difference between both languages is the
way grouping is implemented. Because \TEX\ is dealing with a
flow of information, strong grouping is a must and therefore
part of the language. Occasionally you run into situations
where you wished that you could reach over a group (for
instance in order to pass a value).
In \METAPOST\ grouping behaves quite different. First of
all, it provides the mechanism that hides processing from
the current flow. The previously mentioned \type {vardef} is
implicitly grouped. Contrary to \TEX, in \METAPOST\ all
assignments are global by default, even in a group. If you
assign a variable inside a group it is persistent unless you
first save the variable (or macro) using the \type {save}
operator.
So, in the next code snippet, the value of \type {\value}
inside the box is {\em no} but after the box is typeset, it
will be {\em yes} again.
\starttyping
\def\value{yes} \hbox{\def\value{no}\value} \value
\stoptyping
To make a value local in \METAPOST, the following code is
needed.
\starttyping
string value ; value := "yes" ;
def intermezzo
begingroup ;
save value ; string value ; value := "no" ;
endgroup ;
enddef ;
\stoptyping
Once you start writing your own \METAPOST\ macros, you will
appreciate this \quote {always global} behaviour. As with
other differences between the two languages, they make
sense if you look at what the programs are supposed to do.
\section {Internals and Interims}
Related to grouping is the internal numeric datatype. When
numeric variables are defined as interim, you can quickly
overload them inside a group.
\starttyping
newinternal mynumber ; mynumber := 1 ;
begingroup ; ... interim mynumber := 0 ; ... ; endgroup ;
\stoptyping
You can only \type {interim} a variable if it is already
defined using \type {newinternal}.
Among the \METAPOST\ macros is one called \type {drawdot}.
This macro is kind of redundant because, at least at first
sight, you can use draw to achieve the same result. There
is however a very subtle difference: a dot is slightly
larger than a drawn point. We guess that it's about the
device pixel, so you may not even notice it. It may even be
due to differences in accuracy of the methods to render
them.
\startbuffer
pickup pencircle scaled 50pt ;
drawdot origin shifted (-120pt,0) ; draw origin shifted (-60pt,0) ;
drawdot origin ; draw origin withcolor white ;
setbounds currentpicture to boundingbox currentpicture enlarged 1pt ;
\stopbuffer
\typebuffer
\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection
\stopcomponent