\usemodule [tugboat]
\setvariables
[tugboat]
[title=\CONTEXT{} nodes: commutative diagrams\\ and related graphics, % Subtitles don't show up!
% Note: it is poor style to capitalize following a colon.
keywords=\METAPOST,
author={A. Braslau, I. Hamid, and H. Hagen},
address={The \CONTEXT\ development team},
email={braslau.list (at) comcast.net,\\ishamid (at) colostate.edu,\\pragma (at) wxs.nl}]
\setupalign [tolerant,stretch] % verystrict, strict, tolerant, verytolerant, stretch
%\definesymbol [1] [$\cdot$] % dot rather than bullet, I hate bullets!
\setupitemize [symbol=2] % dash rather than bullet, I hate bullets!
%\setupnote [footnote] [next={ },split=verystrict]
\iffalse % this ends up being about 60% of the actual column width. mystery.
\setupframedtext
[style=small,
align={verytolerant,stretch},
\doifelse{\getvariable{tugboat}{columns}}{yes}
{width=\columnwidth}
{width=\textwidth}]
\fi
\setupframedtext [width=18.75pc,style=small,align={tolerant,stretch}]
\setupframed
[node]
[offset=1pt,
foregroundstyle=\tfa]
\defineframed
[nodeGray]
[node]
[foregroundcolor=darkgray,
foregroundstyle=italic]
\defineframed
[nodeSmall]
[node]
[foregroundstyle=\tfx]
\startbuffer [bib]
@ARTICLE{Krebs1946,
author = {Krebs, H. A.},
title = {Cyclic processes in living matter},
journal = {Enzymologia},
year = {1946},
volume = {12},
pages = {88--100}
}
@ARTICLE{Bethe1939a,
author = {Bethe, H. A.},
title = {Energy Production in Stars},
journal = {Physical Review},
year = {1939},
volume = {55},
pages = {103--103},
month = {Jan},
doi = {10.1103/PhysRev.55.103},
issue = {1},
publisher = {American Physical Society},
XXurl = {http://link.aps.org/doi/10.1103/PhysRev.55.103}
}
@ARTICLE{Bethe1939b,
author = {Bethe, H. A.},
title = {Energy Production in Stars},
journal = {Physical Review},
year = {1939},
volume = {55},
pages = {434--456},
month = {Mar},
doi = {10.1103/PhysRev.55.434},
issue = {5},
publisher = {American Physical Society},
XXurl = {http://link.aps.org/doi/10.1103/PhysRev.55.434}
}
@BOOK{Lawvere2009,
author = {Lawvere, F. William and Schanuel, Stephen H.},
title = {Conceptual Mathematics}
subtitle = {A first introduction to categories},
edition = {2\high{nd}},
publisher = {Cambridge University Press},
address = {Cambridge, UK},
year = {2009}
}
\stopbuffer
%\setupbtxrendering [default] [pagestate=start] % index cite pages in bibliography
\usebtxdataset [bib.buffer]
% Say, MP math arrows rather than font arrows:
\useMPlibrary[mat]
\definemathstackers
[mp]
[alternative=mp]
\definemathextensible [mp] [leftarrow] ["2190]
\definemathextensible [mp] [rightarrow] ["2192]
\definemathextensible [mp] [leftrightarrow] ["2194]
\definemathextensible [mp] [longleftrightarrow] ["27F7]
\definemathextensible [mp] [rightoverleftarrow] ["21C4]
\startMPinitializations
ahlength := .5EmWidth ; % 10pt ;
ahangle := 30 ;
ahvariant := 1 ; % dimpled curved
ahdimple := 4/5 ;
node_loopback_yscale := .7 ;
\stopMPinitializations
\StartAbstract
The graphical representation of node|-|based textual diagrams is a very
useful tool in the communication of ideas. These are composed of graphical
objects or blocks of text or a combination of both, i.e. a decorated label
or text block, each attached to some point (= the node). Additionally, such
diagrams may display other such objects (such as a line segment, an arrow, or
other curve) connecting node points. The set of nodes of a diagram will have
some spatial relation between nodes. In this article we discuss a new \METAPOST\
module for handling node|-|based graphics, as well as a derivative simplified
\CONTEXT\ module.
\StopAbstract
\StartArticle
\startsection [title=Introduction]
The graphical representation of textual diagrams is a very useful tool in the
communication of ideas. In category and topos theory, for example, many key
concepts, formulas, and theorems are expressed by means of \emph {commutative
diagrams}; these involve objects and arrows between them. Certain concepts
discovered by category theory, such as \emph {natural transformations}, are
becoming useful in areas outside of mathematics and natural science, e.g., in
philosophy. To make category and topos methods usable by both specialists and
non|-|specialists, commutative diagrams are an indispensable tool. (For many
examples of formal and informal commutative diagrams, see \cite [Lawvere2009].)
The use of nodal diagrams is not limited to category theory: they may
represent a flow diagram (of a process, for example), a chemical reaction
sequence or pathways, phases and phase transitions, a hierarchical
structure (of anything), a timeline or sequence of events or dependencies, a
family tree, etc.
The basic units of a node|-|based diagram include \emph {node objects}, each
attached to some point (=~the \emph {node}) in some spatial relationship. Note
that a set of objects might be associated with a single node. Given a node, it
also stands in a spatial relation to some other node. The spatial relationship
between the set of nodes of a diagram need not be in a regular network, although
it often is. Note that the spatial relationship between nodes is
graphical and may represent, e.g., a temporal or logical relationship, or a
transformation of one object into another or into others (one interesting
example might be that representing cell division or mitosis).
Given a spatial relation between any two nodes, a node|-|based diagram often
includes some \emph {path segment} or segments (such as arrows or other curves)
between two given nodes that \emph {relate(s)} them. Each path segment may be
augmented by some textual or graphical label.
A simple example of a node diagram is shown in \in{Figure} [fig:AB].
\startbuffer
\startMPcode
clearnodepath ;
nodepath = (left -- right) scaled .75cm ;
draw node(0,"A") ;
draw node(1,"B") ;
drawarrow fromto(0,0,1) ;
\stopMPcode
\stopbuffer
\startplacefigure [reference=fig:AB,
location=force]
\getbuffer
\stopplacefigure
More precisely, a \emph {node} is a point of intersection or branching of paths,
often a point on a regular lattice. (The nodes of the above diagram are the two
endpoints of a straight line segment.) Sometimes, however, a node might be a
single point as in an \emph {identity map} of category theory, referring to
itself:
\startplacefigure [reference=fig:ID,
location=force]
\startMPcode
clearnodepath ; nodepath = origin ;
draw node(0,"$O$") ;
drawarrow fromto.urt (.75cm,0,0) ;
%drawarrow fromto (.75cm,0,0) dashed evenly ;
%drawarrow fromto.llft (.75cm,0,0) ;
setbounds currentpicture to
boundingbox currentpicture enlarged (.5cm,0) ;
\stopMPcode
\stopplacefigure
\startframedtext %\raggedright \hyphenpenalty=10000
The standard arrowhead in \METAPOST\ is a simple triangle, whose length and
angle can be adjusted. \METAFUN\ provides further options, allowing this
arrowhead to be barbed or dimpled. In the present article, we use the settings
\type {ahlength:=10pt; ahangle:=30; ahvariant:=1; ahdimple:=4/5;}.
The loop|-|back arrow paths used here deviate from a circular segment,
becoming ellipsoidal, through the value \type {node_loopback_yscale:=.7;}.
These are all set within a \type {\startMPinitializations ...}
\type{\stopMPinitializations} pair.
\stopframedtext
In this article we discuss a new \METAPOST\ module designed for handling
node|-|based graphics as well as a derivative simple \CONTEXT\ interface.
To illustrate, the code producing \inlinebuffer\ {could} be,
in \METAPOST\ and the \CONTEXT\ interface respectively:
\startitemize [unpacked]
\startitem \METAPOST:
\starttyping
draw node(0,"A") ;
draw node(1,"B") ;
drawarrow fromto(0,1) ;
\stoptyping
\noindent
The \METAPOST\ code shown here has been simplified, as will be seen further on.
\stopitem
\startitem \CONTEXT: \hfuzz=7pt
\starttyping
\startnodes [dx=1.5cm]
\placenode [0,0] {A}
\placenode [1,0] {B}
\connectnodes [0,1] [alternative=arrow]
\stopnodes
\stoptyping
\noindent
The \CONTEXT\ interface has a limited set of features, and will remain simple.
\stopitem
\stopitemize
In each case, \TeX\ is told to draw an arrow from A to B (i.e., from node 0 to
node 1).
For beginners, casual users of \CONTEXT, or any others who might be
intimidated by \METAPOST\ syntax, the ability to construct simple diagrams by
means of standard \CONTEXT\ syntax is helpful. For those who have tried the
\CONTEXT\ interface and\slash or want to draw more advanced diagrams, the \METAPOST\
module is much more powerful and flexible.
\stopsection
\startsection [title=\METAPOST]
\METAPOST\ is a vector|-|graphics language which calls upon \TeX\ to typeset text
(such as labels); in \CONTEXT, furthermore, \METAPOST\ is integrated natively
through the library \MPLIB\ as well as the macro package \METAFUN. The tight integration
of \CONTEXT\ and \METAPOST\ provides advantages over the use of other, external
graphics engines. These advantages include ease of maintaining coherence of
style, as well as extensive flexibility without bloat. \METAPOST\ has further
advantages over most other graphics engines, including a very high degree of
precision as well as the possibility to solve certain types of algebraic
equations. This last feature is rarely used but should not be overlooked.
It is quite natural in \METAPOST\ to locate our node objects along a path or
on differing paths. This is a much more powerful concept than merely locating a
node at some pair of coordinates, e.g., on a square or a rectangular lattice,
for example (as in a table). Furthermore, these paths may be in three dimensions
(or more); of course the printed page will only involve some projection onto two
dimensions. Nor are the nodes restricted to a location on the points defining a
path: they may have, for an index, any \emph {time} along a given path \type {p}
ranging from the first defining point ($t = 0$) up to the last point of that
path ($t ≤ \mathtt{length(p)}$), the number of defining points of a path.
(Note that the time of a cyclic path is taken modulo the length of the path,
that is, $t$ outside of the range $[\mathtt0,\mathtt{length(p)}]$ will return the
first or the last point of an open path, but will \quotation {wrap} for a closed
path.)
Given a path \type {p}, nodes are defined (implicitly) as \type {picture}
elements: \type {picture p.pic[];}. This is a pseudo|-|array where the square
brackets indicate a set of numerical tokens, as in \type {p.pic[0]} or \type
{p.pic[i]} (for \type {i=0}), but also \type {p.pic0}. This number need not be
an integer, and \type {p.pic[.5]} or \type {p.pic.5} (not to be confused with
\type {p.pic5}) are also valid. These picture elements are taken to be located
relative to the path~\type {p}, with the index~\type {t} corresponding to a time
along the path, as in
\starttyping
draw p.pic[t] shifted point t of p;
\stoptyping
\noindent (although it
is not necessary to draw them in this way). This convention allows the nodes to
be oriented and offset with respect to the path in an arbitrary manner.
Note that a path can be defined, then nodes placed relative to this path. Or the
path may be declared but remain undefined, to be determined only after the
nodes are declared. Yet another possibility is that the path may be adjusted as
needed, as a function of whatever nodes are to be occupied. This will be
illustrated through examples further down.
\stopsection
\startsection [title=Some simple examples]
Let's begin by illustrating a typical commutative diagram from category
theory. Although it may appear trivial, this example helps to introduce
\METAPOST\ syntax. At the same time, a large part of the idea behind this module
is to facilitate use of this system without having to learn much \METAPOST.
\startbuffer
\startMPcode
path p ; p := fullsquare scaled 3cm ;
draw p ;
for i=0 upto length p:
draw point i of p
withpen pencircle scaled 5pt ;
endfor ;
\stopMPcode
\stopbuffer
\typebuffer
\noindent
A path is drawn as well as the points defining the path.
\startplacefigure [reference=fig:square]
\getbuffer
\stopplacefigure
Given the named path \type {nodepath}, we can now define and draw nodes as well
as connections between them (see \in{Figure} [fig:natural]):
\startbuffer
\startMPcode
clearnodepath ; nodepath = p ;
draw node(0,"\node{$G(X)$}") ;
draw node(1,"\node{$G(Y)$}") ;
draw node(2,"\node{$F(Y)$}") ;
draw node(3,"\node{$F(X)$}") ;
drawarrow fromto.bot(0,0,1,
"\nodeSmall{$G(f)$}") ;
drawarrow fromto.top(0,3,2,
"\nodeSmall{$F(f)$}") ;
drawarrow fromto.rt (0,2,1,
"\nodeSmall{$\eta_Y$}") ;
drawarrow fromto.lft(0,3,0,
"\nodeSmall{$\eta_X$}") ;
\stopMPcode
\stopbuffer
\typebuffer
\startplacefigure [reference=fig:natural,
title={Drawn using the \METAPOST\ interface.}]
\getbuffer
\stopplacefigure
In working with \METAPOST, it is good practice to reset or clear a variable
using the directive \type {save} for the \emph {suffix} (or variable name) \type
{nodepath}, as contained in the directive \type {clearnodepath} (defined as
\quotation {\type {save nodepath; path nodepath}}). The macros used here rely on
the creation of certain internal variables and may not function correctly if the
variable structure is not cleared. Indeed, any node may contain a combination of
picture elements, added successively, so it is crucial to \type {save}
the variable, making its use local rather than global. This point is
particularly true with \CONTEXT, where a single \MPLIB\ instance is used and
maintained over multiple runs.
\startframedtext \hfuzz=.3pt
The \CONTEXT\ directives \type {\startMPcode...\stopMPcode} include
grouping (\METAPOST\ \type {begingroup;...}\type {endgroup;}) and the use of
\type {save} (in \type {clearnodepath}) will make the suffix \type {nodepath}
local to this code block. In the code for \in{Figures} [fig:square] and \in
[fig:natural], the path \type {p} itself is not declared local (through the use
of a \type {save}); it therefore remains available for other \METAPOST\ code
blocks. We cannot do this with the default suffix name \type {nodepath} without
undesirable consequences.
\stopframedtext
Note that one should not confuse the above \METAPOST\ function \type {node()} with the
\CONTEXT\ command \type {\node{}}, defined as follows:
\starttyping
\defineframed
[node]
[frame=off,
offset=1pt]
\defineframed
[nodeSmall]
[node]
[foregroundstyle=small]
\stoptyping
\noindent
\type {\node{}} places the text within a \CONTEXT\ frame (with the frame border
turned off), whereas the \METAPOST\ function \type {node(i,"...")} sets and
returns a picture element associated with a point on path \type {nodepath}
indexed by its first argument. The second argument here is a string that gets
typeset by \TEX. (The use of \type {\node{}} adds an \type {offset}.)
By default, the \METAPOST\ function \type {fromto()} returns a path segment going between
two points of the path \type {nodepath}. The first argument (\type
{0} in the example above) can be used as a displacement to skew the path away
from a straight line (by an amount in units of the straight path length). The
last argument is a string to be typeset and placed at the midpoint of the segment. The
{suffix} appended to the function name gives an offset around this halfway
point. This follows standard \METAPOST\ conventions.
It is important to draw or declare the nodes \emph {before} drawing the
connections, using \type {fromto()}, in order to avoid overlapping
symbols, as one notices that the arrows drawn in the example above begin
and end on the border of the frame (or bounding box) surrounding the node text.
This would not be possible if the arrow were to be drawn before this
text was known.
As will be seen further on, one can specify the use of any defined path,
without restriction to the built|-|in name \type {nodepath} that is used by
default. Furthermore, a function \type {fromtopaths()} can be used to draw
segments connecting any two paths which may be distinct. This too will be
illustrated further on.
The \CONTEXT\ syntax for the current example looks like this:
\startbuffer
\startnodes [dx=3cm,dy=3cm]
\placenode [0,0] {\node{$G(X)$}}
\placenode [1,0] {\node{$G(Y)$}}
\placenode [1,1] {\node{$F(Y)$}}
\placenode [0,1] {\node{$F(X)$}}
\connectnodes [0,1] [alternative=arrow,
label={\nodeSmall{$G(f)$}},position=bottom]
\connectnodes [3,2] [alternative=arrow,
label={\nodeSmall{$F(f)$}},position=top]
\connectnodes [2,1] [alternative=arrow,
label={\nodeSmall{$\eta_Y$}},position=right]
\connectnodes [3,0] [alternative=arrow,
label={\nodeSmall{$\eta_X$}},position=left]
\stopnodes
\stopbuffer
{\hfuzz=.8pt \typebuffer}
\startplacefigure [location=force,
title={Drawn using the \CONTEXT\ interface.}]
\getbuffer
\stopplacefigure
\page[yes]
This follows the more classic (and limited) approach of placing nodes on the
coordinates of a regular lattice, here defined as a 3~cm square network.
[The lattice can be square ($\type{dx}=\type{dy}$), rectangular
($\type{dx}\ne\type{dy}$), or oblique (through $\type {rotation}\ne90$).]
The arguments are then $(x,y)$ coordinates of this lattice and the nodes are
indexed 0, 1, 2, \unknown\ in the order in which they are drawn. These are used as reference
indices in the commands \type {\connectnodes} (rather than requiring two \emph {pairs}
of coordinates); see \in{Figure} [fig:indices].
\startplacefigure [reference=fig:indices,
title={Coordinates and indices.
(For variety, a rectangular oblique lattice is
drawn.)\raggedright\hyphenpenalty=10000\par
}]
\startnodes [dx=3cm,dy=2cm,rotation=60]
\placenode [0,0] {\node{(0,0)}}
\placenode [1,0] {\node{(1,0)}}
\placenode [1,1] {\node{(1,1)}}
\placenode [0,1] {\node{(0,1)}}
\connectnodes [0,1] [alternative=arrow,
label={\nodeSmall{$0\rightarrow1$}},position=bottom]
\connectnodes [3,2] [alternative=arrow,
label={\nodeSmall{$3\rightarrow2$}},position=top]
\connectnodes [2,1] [alternative=arrow,
label={\nodeSmall{$~2\rightarrow1$}}, position=right]
\connectnodes [3,0] [alternative=arrow,
label={\nodeSmall{$3\rightarrow0$}}, position=upperleft]
\stopnodes
\stopplacefigure
Connecting numbered nodes (in the order in which they were declared) might seem
a bit confusing at first view, but it simplifies things in the end, really!
An identity map, as shown in \in{Figure} [fig:ID], earlier, and, below, in
\in{Figure} [fig:Me] is achieved by connecting a node to itself.
\startbuffer
\startnodes [dx=2cm,dy=.6cm]
\placenode [0,0] {\node{Me}}
\placenode [1,-1] {\node{You}}
\connectnodes [0,0] [alternative=arrow,
offset=.75cm,position=topright,
label=myself]
\connectnodes [1,1] [alternative=arrow,
offset=.75cm,position=bottomright,
label=yourself]
\stopnodes
\stopbuffer
\startplacefigure [reference=fig:Me,
title=Identity maps]
\getbuffer
\stopplacefigure
\typebuffer
The scale (diameter) of the circular loop|-|back is set by the keyword
\type {offset=} (normally used to curve or bow|-|away a path connecting nodes
from the straight|-|line segment between them), and the \type {position=}
keyword sets its orientation.
Let us now consider the following code which illustrates the \METAFUN\ operator
\type {crossingunder} (see \in{Figure}[fig:crossingunder]). The \type {nodepath}
indices are put into variables \type {A}, \type {B}, \type {C}, and \type {D},
thus simplifying the code.
\startbuffer
\startMPcode
clearnodepath ;
nodepath := fullsquare scaled 2cm ;
save A,B,C,D ;
A = 3 ; draw node(A,"\node{A}") ;
B = 2 ; draw node(B,"\node{B}") ;
C = 0 ; draw node(C,"\node{C}") ;
D = 1 ; draw node(D,"\node{D}") ;
drawarrow fromto(0,B,C) ;
drawarrow fromto(0,A,D)
crossingunder fromto(0,B,C) ;
\stopMPcode
\stopbuffer
\typebuffer
\startplacefigure [reference=fig:crossingunder,
title={A\raise.18ex\hbox{$\rightarrow\,$}D under
B\raise.18ex\hbox{$\,\rightarrow\,$}C.}]
\getbuffer
\stopplacefigure
Given a path segment to be crossed, \type {crossingunder} draws a path with
a segment surrounding the intersection with that path cut|-|out,
resulting in two (sub)path segments. This operator is of such general use that
it has been added to the \METAFUN\ base.
\startplacefigure [reference=fig:sincos,
title={\type{crossingunder}}]
\startMPcode
save u ; u := 2cm ;
save p ; path p[] ;
n := 64 ;
p2 := for i=0 upto n : if i>0 : .. fi (3u*(i/n), u*sind(720*(i/n))) endfor ;
p3 := for i=0 upto n : if i>0 : .. fi (3u*(i/n), u*cosd(720*(i/n))) endfor ;
p4 := point 0 of p2 -- point (length p2) of p2 shifted (left*.01u) ;
draw p2 withcolor red ;
begingroup ;
interim crossingscale := 20 ;
draw (p3 crossingunder p2) crossingunder p4 withcolor blue ;
endgroup ;
drawarrow (p4 crossingunder p2) ;
\stopMPcode
\stopplacefigure
Another illustration of the \type {crossingunder} operator in use is shown in
\in {figure} [fig:sincos]. Because the diagrams are all defined and drawn in
\METAPOST, one can easily use the power of \METAPOST\ to extend a simple node
drawing with any kind of graphical decoration.
This brings up an important point that has limited the development of a
full|-|featured \CONTEXT\ node module up to now. A pure \METAPOST\ interface affords
much more flexibility than can be conveniently reduced to a set of \TeX\ macros;
the \CONTEXT\ interface has been written to provide only basic functionality.
(One can use \type{\nodeMPcode{}} to inject arbitrary \METAPOST\ code within a
\type{\startnode...\stopnode} pair, although in this example one is probably
better off using the straight \METAPOST\ interface.)
\stopsection
\startsection [title=Cyclic diagrams]
For a somewhat more complicated example, let us consider the representation of a
catalytic process such as that given by \cite[author] [Krebs1946]~\cite
[Krebs1946]. The input is shown coming into the cycle from the center of a
circle; the products of the cycle are spun off from the outside of the circle.
We start by defining a circular path where each point corresponds to a step in
the cyclic process. Our example will use six steps. % "take" is incorrect - different meaning.
We also want to define a second circular path with the same number of points at
the interior of this first circle for the input, and a third circular path at
the exterior for the output (see \in{Figure} [fig:circles]).
\startbuffer
\startMPcode
save p ; path p[] ;
% define a fullcircle path
% with nodes at 60° (rather than 45°)
p1 := (for i=0 step 60 until 300:
dir(90-i) .. endfor cycle)
scaled 1.8cm ;
p0 := p1 scaled .5 ;
p2 := p1 scaled 1.5 ;
for i=0 upto 2:
draw p[i] ;
for j=1 upto length p[i]:
draw point j of p[i]
withpen currentpen scaled 10 ;
if i=1:
label.autoalign(angle
point j of p[i])
(decimal j, point j of p[i]) ;
fi
endfor
label.bot("\bf p" & decimal i,
point 0 of p[i]) ;
endfor
\stopMPcode
\stopbuffer
\startplacefigure [reference=fig:circles,
title={The paths that we will use for the anchoring of
nodes.}]
\getbuffer
\stopplacefigure
The code is as follows:
\typebuffer
\noindent
(\type {autoalign()} is a feature defined within \METAFUN.)
Nodes will then be drawn on each of these three circles and arrows will be used
to connect the various nodes, either on the same path or else between paths.
The \METAPOST\ function \type {fromto()} is used to give a path segment that
points from one node to another. It \emph {assumes} the path named \type {nodepath},
and in fact calls the function \type {fromtopaths} that explicitly takes path
names as arguments. That is, \type {fromto (d, i, j, ...)} is equivalent to
\type {fromtopaths (d, nodepath, i, nodepath, j, ...)}.
As stated above, this segment can be a straight line, or a path can be
bowed away from this straight line by a transverse displacement given by the
function's first argument (given in units of the straight segment length). When
both nodes are located on a single, defined path, this segment can be made to lie
on or follow this path, such as one of the circular paths defined above. This
behavior is obtained by using any non|-|numeric value (such as \type {true}) in
place of the first argument. Of course, this cannot work if the two nodes are not
located on the same path.
In \in
{figure} [fig:Bethe],
the circular arc segments labeled \emph {\darkgray a–f} are drawn using the following:
\starttyping
drawarrow fromtopaths.urt
(true,p1,0,p1,1,"\nodeGray{a}") ;
\stoptyping
\noindent
Here, \type {\nodeGray} is a frame that inherits from \type {\node}, changing
style and color:
\starttyping
\defineframed
[nodeGray]
[node]
[foregroundcolor=darkgray,
foregroundstyle=italic]
\stoptyping
\noindent
The bowed arrows feeding into the cyclic process and leading out to the
products\Dash between different paths, from the path \type {p0} to the path
\type {p1} and from the path \type {p1} to the path \type {p2},
respectively\Dash
are drawn using the deviations \type {+3/10} and \type {-1/10} (to and from
half|-|integer indices, thus mid|-|step, on path \type {p1}):
\starttyping
drawarrow fromtopaths( 3/10,p0,0,p1,0.5)
withcolor .6white ;
drawarrow fromtopaths(-1/10,p1,0.5,p2,1)
withcolor .6white ;
\stoptyping
\startplacefigure [reference=fig:Bethe,
title={The \cite[author] [Bethe1939a] cycle for the energy production in stars
\cite [Bethe1939a+Bethe1939b] in a \cite[author] [Krebs1946]
representation of a catalytic process~\cite [Krebs1946].}]
\startMPcode
% The Bethe cycle for energy production in stars (1939), following Krebs (1946)
save p ; path p[] ;
p1 := (for i=0 step 60 until 300: dir(90-i).. endfor cycle) scaled 1.8cm ;
p0 := p1 scaled .5 ;
p2 := p1 scaled 1.5 ;
draw node(p1,0,"\node{\chemical{^{12}C}}") ;
draw node(p1,1,"\node{\chemical{^{13}N}}") ;
draw node(p1,2,"\node{\chemical{^{13}C}}") ;
draw node(p1,3,"\node{\chemical{^{14}N}}") ;
draw node(p1,4,"\node{\chemical{^{15}O}}") ;
draw node(p1,5,"\node{\chemical{^{15}N}}") ;
drawarrow fromtopaths.urt (true,p1,0,p1,1,"\nodeGray{a}") ;
drawarrow fromtopaths.rt (true,p1,1,p1,2,"\nodeGray{b}") ;
drawarrow fromtopaths.lrt (true,p1,2,p1,3,"\nodeGray{c}") ;
drawarrow fromtopaths.llft(true,p1,3,p1,4,"\nodeGray{d}") ;
drawarrow fromtopaths.lft (true,p1,4,p1,5,"\nodeGray{e}") ;
drawarrow fromtopaths.ulft(true,p1,5,p1,6,"\nodeGray{f}") ;
draw node(p0,0,"\node{\chemical{^1H}}") ;
draw node(p0,2,"\node{\chemical{^1H}}") ;
draw node(p0,3,"\node{\chemical{^1H}}") ;
draw node(p0,5,"\node{\chemical{^1H}}") ;
drawarrow fromtopaths(3/10,p0,0,p1,0.5) withcolor .6white ;
drawarrow fromtopaths(3/10,p0,2,p1,2.5) withcolor .6white ;
drawarrow fromtopaths(3/10,p0,3,p1,3.5) withcolor .6white ;
drawarrow fromtopaths(3/10,p0,5,p1,5.5) withcolor .6white ;
draw node (p2,0,"\node{\chemical{^4He}}") ;
draw node (p2,1,"\node{$\gamma$}") ;
draw node.lrt (p2,2,"\node{$\mathrm{e}^+ + ν_\mathrm{e}$}") ;
draw node (p2,3,"\node{$\gamma$}") ;
draw node (p2,4,"\node{$\gamma$}") ;
draw node.ulft(p2,5,"\node{$\mathrm{e}^+ + ν_\mathrm{e}$}") ;
drawarrow fromtopaths(-1/10,p1,0.5,p2,1) withcolor .6white ;
drawarrow fromtopaths(-1/10,p1,1.5,p2,2) withcolor .6white ;
drawarrow fromtopaths(-1/10,p1,2.5,p2,3) withcolor .6white ;
drawarrow fromtopaths(-1/10,p1,3.5,p2,4) withcolor .6white ;
drawarrow fromtopaths(-1/10,p1,4.5,p2,5) withcolor .6white ;
drawarrow fromtopaths(-1/10,p1,5.5,p2,0) withcolor .6white ;
\stopMPcode
\stopplacefigure
\startsubsection [title={A lesson in \METAPOST}]
An \quote {array} of paths is declared through \type {path p[];} it is not a
formal array, but rather a syntactic definition of a collection of path
variables \type {p0}, \type {p1}, \unknown, each of whose names is prefixed with the tag
\quotation {p} followed by any number, not necessarily an integer (e.g., \type
{p3.14} is a valid path name). The syntax allows enclosing this \quotation
{index} within square brackets, as in \type {p[0]} or, more typically, \type
{p[i]}, where \type {i} would be a numeric variable or the index of a loop. Note
that the use of brackets is required when using a negative index, as in \type
{p[-1]} (since \type {p-1} is interpreted as three tokens, representing a
subtraction). Furthermore, the variable \type {p} itself would here be a
numeric (by default), so \type {p[p]} would be a valid syntactic construction!
One could, additionally, declare a set of variables \type {path p[][];} and so
forth, defining also \type {p[0][0]} (equivalently, \type {p0 0}) for example as
a valid path, coexisting with yet different from the path \type {p0}.
\METAPOST\ also admits variable names reminiscent of structured types in
programming; for example, the declaration \type {picture
p.pic[];} is used internally in the \type {node} macros, but this
becomes \type {picture p[]pic[];} when using a path \quote {array} syntax.
These variable names are associated with the suffix \type {p} and all become
undefined by \type {save p;}.
\stopsubsection
\stopsection
\startsection [title={Putting it together}]
What follows is an example of a natural transformation, discovered and
articulated in the course of a philosophical research project (by Idris Samawi
Hamid). \in{Figure} [fig:Croce] represents what is called the Croce Topos,
named after the Italian philosopher Benedetto Croce (1866–1952).
We define it using the \CONTEXT\ interface to the node package:
\startbuffer
\startMPcode
clearnodepath ;
nodepath = ((0,0) -- (1,0) -- (3.5,0) --
(3.5,1) -- (1,1) -- (0,1) --
cycle) scaled 3cm ;
draw node(0,"\node{Practical}") ;
draw node(1,"\node{Economic}") ;
draw node(2,"\node{Moral}") ;
draw node(3,"\node{Conceptual}") ;
draw node(4,"\node{Aesthetic}") ;
draw node(5,"\node{Theoretical}") ;
drawarrow fromto.rt (.1,5,0,"\node{$\gamma$}") ;
drawarrow fromto.lft(.1,0,5,"\node{$\gamma'$}") ;
drawarrow fromto.rt (.1,4,1,"\node{$F\gamma$}") ;
drawarrow fromto.lft(.1,1,4,"\node{$F\gamma'$}") ;
drawarrow fromto.rt (.1,3,2,"\node{$G\gamma$}") ;
drawarrow fromto.lft(.1,2,3,"\node{$G\gamma'$}") ;
drawarrow fromto.top( 0,4,3,
"\node{\it concretization$_1$}") ;
drawarrow fromto.bot(.1,3,4,
"\node{\it abstraction$_1$}")
dashed evenly ;
drawarrow fromto.top( 0,1,2,
"\node{\it concretization$_2$}") ;
drawarrow fromto.bot(.1,2,1,
"\node{\it abstraction$_2$}")
dashed evenly ;
\stopMPcode
\stopbuffer
% Let's forget that and rather use \startnodes...
\startbuffer
\startnodes [dx=3cm,dy=3cm,alternative=arrow]
\placenode [0, 0] {\node{Practical}}
\placenode [1, 0] {\node{Economic}}
\placenode [3.5,0] {\node{Moral}}
\placenode [3.5,1] {\node{Conceptual}}
\placenode [1, 1] {\node{Aesthetic}}
\placenode [0, 1] {\node{Theoretical}}
\connectnodes [5,0] [offset=.1,
position=right,label={\node{$\gamma$}}]
\connectnodes [0,5] [offset=.1,
position=left, label={\node{$\gamma'$}}]
\connectnodes [4,1] [offset=.1,
position=right,label={\node{$F\gamma$}}]
\connectnodes [1,4] [offset=.1,
position=left, label={\node{$F\gamma'$}}]
\connectnodes [3,2] [offset=.1,
position=right,label={\node{$G\gamma$}}]
\connectnodes [2,3] [offset=.1,
position=left, label={\node{$G\gamma'$}}]
\connectnodes [4,3] [position=top,
label={\node{\it concretization$_1$}}]
\connectnodes [3,4] [position=bottom,
offset=.1,option=dashed,
label={\node{\it abstraction$_1$}}]
\connectnodes [1,2] [position=top,
label={\node{\it concretization$_2$}}]
\connectnodes [2,1] [position=bottom,
offset=.1,option=dashed,
label={\node{\it abstraction$_2$}}]
\stopnodes
\stopbuffer
\startplacefigure [reference=fig:Croce,
title={A representation of the Croce Topos.}]
\getbuffer
\stopplacefigure
\typebuffer
\stopsection
\startplacefigure [reference=fig:DNA,
title={An example tree diagram.}]
\startMPcode
% third example: A tree diagram
save u ; u := 2cm ;
save n ; n := 2 ; % n children per generation
save p ; path p[] ;
p0 := origin ;
numeric d[] ; d1 := 3.8 ; d2 := 1.7 ; d3 := 1 ;
for g=1 upto 3:
p[g] :=
for i=0 upto length(p[g-1]):
for c=0 upto n-1:
if (i+c)>0: -- fi
((point i of p[g-1]) shifted (d[g]*(c/(n-1)-.5)*u,-u))
endfor
endfor ;
endfor
draw node(p0,0, "\node{DNA interactions with surfaces}") ;
draw node(p1,0, "\node{repulsive:}") ;
draw node(p1,1, "\node{attractive: adsorption}") ;
draw node(p2,0, "\node{confinement}") ;
draw node(p2,1, "\node[align=middle,location=high]{depletion,\\macromolecular\\crowding}") ;
draw node(p2,2, "\node{chemisorption}") ;
draw node(p2,3, "\node{physisorption}") ;
draw node(p3,5.5,"\node{immobilized}") ;
draw node(p3,7, "\node{mobile}") ;
drawarrow fromtopaths(0,p0,0,p1,0) ;
drawarrow fromtopaths(0,p0,0,p1,1) ;
drawarrow fromtopaths(0,p1,0,p2,0) ;
drawarrow fromtopaths(0,p1,0,p2,1) ;
drawarrow fromtopaths(0,p1,1,p2,2) ;
drawarrow fromtopaths(0,p1,1,p2,3) ;
drawarrow fromtopaths(0,p2,2,p3,5.5) ;
drawarrow fromtopaths(0,p2,3,p3,5.5) ;
drawarrow fromtopaths(0,p2,3,p3,7) ;
\stopMPcode
\stopplacefigure
\startsection [title=Tree diagrams]
The tree diagram shown in \in{Figure} [fig:DNA] is drawn using four paths, each
one defining a row or generation in the branching. The definition of the spacing
of nodes was crafted by hand and is somewhat arbitrary: 3.8, 1.7, and 1 for the
first, second and third generations. This might not be the best approach, but
it is how I (Alan) was thinking when I first created this figure.
\blank
Ultimately, one can do better by allowing \METAPOST\ to solve the relevant equations and
determine this spacing automatically. Because this is a somewhat advanced procedure, this
approach will be first illustrated through a simple example of a diagram
where the nodes will be placed on a declared but undefined path:
\starttyping
save p ; % path p ;
\stoptyping
\noindent
The \type {save p;} assures that the path is undefined. This path will later
be defined based on the contents of the nodes and a desired relative placement.
In fact, it is not even necessary to declare that the suffix will be a path, as
the path will be declared and automatically built once the positions of all the
nodes are determined. To emphasize this point, the \type {path} declaration above is
commented out.
\startdescription {Warning:}
Solving equations in \METAPOST\ can be non|-|trivial for those who are less
mathematically inclined. One needs to establish a coupled set of equations that
is solvable: that is, fully but not over|-|determined.
\stopdescription
A few helper functions have been defined: \type {makenode()} returns a suffix
(variable name) corresponding to the node's position. The first such node can be
placed at any finite point, for example the drawing's origin. The following
nodes can be placed in relation to this first node:
\starttyping
save nodepath ;
save first, second, third, fourth ;
pair first, second, third, fourth ;
first.i = 0 ; second.i = 1 ;
third.i = 2 ; fourth.i = 3 ;
first = makenode(first.i, "\node{first}");
second = makenode(second.i,"\node{second}");
third = makenode(third.i, "\node{third}");
fourth = makenode(fourth.i,"\node{fourth}");
first = origin ;
second = first
+ betweennodes.urt(nodepath,first.i,
nodepath,second.i,
whatever) ;
third = second
+ betweennodes.lft(nodepath,second.i,
nodepath,third.i,
whatever) ;
fourth = third
+ betweennodes.bot(nodepath,fourth.i,
nodepath,first.i,
3ahlength) ;
\stoptyping
The helper function \type {betweennodes()} returns a vector pointing in a
certain direction (here following the standard \METAPOST\ suffixes \type {urt},
\type {lft}, and \type {bot}), that takes into account the bounding boxes of the
contents of each node, plus an (optional) additional distance (here given in
units of the arrow|-|head length, \type {ahlength}). Using the keyword \type
{whatever} tells \METAPOST\ to adjust this distance as necessary. The above set
of equations is incomplete as written, so a fifth and final relation needs to be
added; the fourth node is also to be located directly to the left of the very
first node:
\starttyping
fourth = first
+ betweennodes.lft(nodepath,fourth.i,
nodepath,first.i,
3ahlength) ;
\stoptyping
\noindent
(Equivalently, we could declare that the first node located to the right of the fourth node:
\type {first = fourth + betweennodes.rt (nodepath, first.i, nodepath, fourth.i,
3ahlength);}.)
Note that the helper function \type {makenode()} can be used as many times as
needed; if given no content, it merely returns the node's position. Additional
nodes can be added to this diagram along with appropriate relational equations,
keeping in mind that the equations must, of course, be solvable. This last issue
is the one challenge that most users might face.
The function \type {node()}, used previously and returning a picture
element to be drawn, itself calls the function \type {makenode()}, used here. The
nodes have not yet been drawn:
\starttyping
for i = first.i,second.i,third.i,fourth.i :
draw node(i) ;
drawarrow fromto(0,i,i+1) ;
endfor
\stoptyping
\noindent
This results in \in{Figure} [fig:relative]. The path is now defined as one
running through the position of all of the defined nodes, and is cyclic.
\startplacefigure [reference=fig:relative]
\startMPcode
save nodepath ;
save first, second, third, fourth ;
pair first, second, third, fourth ;
first.i = 0 ; first = makenode(first.i, "\node{first}") ;
second.i = 1 ; second = makenode(second.i,"\node{second}") ;
third.i = 2 ; third = makenode(third.i, "\node{third}") ;
fourth.i = 3 ; fourth = makenode(fourth.i,"\node{fourth}") ;
first = origin ;
second = first + betweennodes.urt(nodepath,first.i, nodepath,second.i,whatever) ;
third = second + betweennodes.lft(nodepath,second.i,nodepath,third.i, whatever) ;
fourth = third + betweennodes.bot(nodepath,fourth.i,nodepath,first.i,3ahlength) ;
fourth = first + betweennodes.lft(nodepath,fourth.i,nodepath,first.i,3ahlength) ;
for i = first.i, second.i, third.i, fourth.i :
draw node(i) ;
drawarrow fromto(0,i,i+1) ;
endfor
\stopMPcode
\stopplacefigure
Using this approach, that of defining but not drawing the nodes until a complete
set of equations defining their relative positions has been constructed, imposes
several limitations. First, the nodes are expected to be numbered from $0$
to $n$, continuously and without any gaps for each defined path. This is just an
implicit, heuristic convention of the path construction. Second, when ultimately
defining all the nodes and their positions, the path needs to be constructed. A
function, \type {makenodepath(p)} accomplishes this; it gets implicitly called
(once) upon the drawing of any \type {node()} or connecting \type {fromto}. Of
course, \type {makenodepath()} can always be called explicitly once the set of
equations determining the node positions is completely defined.
\emph {We once again stress} that the writing of a solvable, yet not
over|-|determined, set of equations can be a common source of error for many
\METAPOST\ users.
Another example is the construction of a simple tree of descendance,
a.k.a.\ a
family tree. There are many ways to draw such a tree; in \in{Figure}
[fig:descendance], we show only three generations.
We leave it as an exercise to the reader to come up with the equations used to
determine this tree (or one can look at the source of this document).
\startplacefigure [reference=fig:descendance,
title={A tree of descendance.}]
\startMPcode
save p ; % path p[], p[][] ; get automagically defined
save spacing ; spacing = 10pt ;
save d ; d = 6ahlength ;
save mother, father ; pair mother, father ;
mother = makenode(p,0,"\node{mother}") ;
father = makenode(p,1,"\node{father}") ;
mother = origin ;
father = mother + betweennodes.rt(p,0,p,1,spacing) ;
% first generation
save child, spouse ; pair child[], spouse[] ;
child = 0 ; spouse = 1 ;
child1 = makenode(p0,child, "\node{child1}") ;
spouse1 = makenode(p0,spouse,"\node{spouse}") ;
child2 = makenode(p1,child, "\node{child2}") ;
spouse2 = makenode(p1,spouse,"\node{spouse}") ;
.5[child1,child2] = mother + d*down ;
spouse1 = child1 + betweennodes.rt(p0,child, p0,spouse,spacing) ;
child2 = spouse1 + betweennodes.rt(p0,spouse,p1,child, whatever) ;
spouse2 = child2 + betweennodes.rt(p1,child, p1,spouse,spacing) ;
% second generation
save grandchild, grandspouse ; pair grandchild[], grandspouse[] ;
grandchild1 = makenode(p0 0,child, "\node{grandchild1}") ;
grandspouse1 = makenode(p0 0,spouse,"\node{spouse}") ;
grandchild2 = makenode(p0 1,child, "\node{grandchild2}") ;
grandspouse2 = makenode(p0 1,spouse,"\node{spouse}") ;
grandchild3 = makenode(p1 0,child, "\node{grandchild3}") ;
grandspouse3 = makenode(p1 0,spouse,"\node{spouse}") ;
grandchild4 = makenode(p1 1,child, "\node{grandchild4}") ;
grandspouse4 = makenode(p1 1,spouse,"\node{spouse}") ;
.5[grandchild1,grandchild2] = child1 + d*down ;
.5[grandchild3,grandchild4] = child2 + d*down ;
grandchild2 = grandchild1 + betweennodes.rt(p0 0,child,p0 1,child,2spacing) ;
grandchild3 = grandchild2 + betweennodes.rt(p0 1,child,p1 0,child,2spacing) ;
grandchild4 = grandchild3 + betweennodes.rt(p1 0,child,p1 1,child,2spacing) ;
grandspouse1 = grandchild1 + nodeboundingpoint.bot(p0 0,child)
+ nodeboundingpoint.lrt(p0 0,spouse) ;
grandspouse2 = grandchild2 + nodeboundingpoint.bot(p0 1,child)
+ nodeboundingpoint.lrt(p0 1,spouse) ;
grandspouse3 = grandchild3 + nodeboundingpoint.bot(p1 0,child)
+ nodeboundingpoint.lrt(p1 0,spouse) ;
grandspouse4 = grandchild4 + nodeboundingpoint.bot(p1 1,child)
+ nodeboundingpoint.lrt(p1 1,spouse) ;
draw node(p,0) ;
draw node(p,1) withcolor blue ;
for i=0,1 :
draw node(p[i],child) ;
drawarrow fromtopaths(0,p,0,p[i],child) ;
draw node(p[i],spouse) withcolor blue ;
for j=0,1 :
draw node(p[i][j],child) ;
draw node(p[i][j],spouse) withcolor blue ;
endfor
drawarrow fromtopaths(0,p[i],child,p[i][0],child) ;
drawarrow fromtopaths(0,p[i],child,p[i][1],child) ;
endfor
\stopMPcode
\stopplacefigure
The requisite set of equations could be hidden from users wishing to
construct simple, pre|-|defined types of diagrams. However, such cases would
involve a loss of generality and flexibility. Nevertheless, the \ConTeXt-Nodes
module \emph {could} be extended in the future to provide a few simple models.
One might be a branching tree structure, although even the above example (as
drawn) does not easily fit into a simple, general model.
\blank
A user on the \CONTEXT\ mailing list asked if it is possible to make structure
trees for English sentences with categorical grammar, an example of which is
shown in \in {Figure} [fig:grammar].
\startbuffer
\startMPcode
save p ; path p[] ;
save n ; n = 0 ;
% rather than parsing a string,
% we can use "suffixes":
forsuffixes $=People,from,the,country,
can,become,quite,lonely :
p[n] = makenode(p[n],0,
"\node{\it" & (str $) & "}")
= (n,0) ;
% we work first with unit paths.
n := n + 1 ;
endfor
save u ; u := MakeupWidth/n ;
% build upward tree
vardef makeparentnode(text t) =
save i, xsum, xaverage, ymax ;
i = xsum = 0 ;
forsuffixes $ = t :
clearxy ; z = point infinity of $ ;
xsum := xsum + x ;
if unknown ymax : ymax = y ;
elseif y > ymax : ymax := y ; fi
i := i + 1 ;
endfor
xaverage = xsum / i ;
ymax := ymax + 1 ;
forsuffixes $ = t :
clearxy ;
z = point infinity of $ ;
$ := $ & z -- (x,ymax)
if i>1 : -- (xaverage,ymax) fi ;
endfor
enddef ;
makeparentnode(p2,p3) ;
makeparentnode(p4,p5) ;
makeparentnode(p6,p7) ;
makeparentnode(p1,p2) ;
makeparentnode(p0,p1) ;
makeparentnode(p4,p6) ;
makeparentnode(p0,p4) ;
makeparentnode(p0) ;
% the paths are all defined
% but need to be scaled.
for i=0 upto n-1 :
p[i] := p[i] xyscaled (u,.8u) ;
draw node(p[i],0) ;
endfor
save followpath ;
boolean followpath ; followpath = true ;
draw fromtopaths(followpath,p0,0,p0,1,
"\node{H:N}") ;
draw fromtopaths(followpath,p1,0,p1,1,
"\node{Rel:Prep}") ;
draw fromtopaths(followpath,p2,0,p2,1,
"\node{Dr:Dv}") ;
draw fromtopaths(followpath,p3,0,p3,1,
"\node{H:N}") ;
draw fromtopaths(followpath,p4,0,p4,1,
"\node{M:Aux}") ;
draw fromtopaths(followpath,p5,0,p5,1,
"\node{H:Mv}") ;
draw fromtopaths(followpath,p6,0,p6,1,
"\node{M:Adv}") ;
draw fromtopaths(followpath,p7,0,p7,1,
"\node{H:Adj}") ;
draw fromtopaths(followpath,p1,1,p1,2) ;
draw fromtopaths(followpath,p2,3,p2,4) ;
draw fromtopaths(followpath,p1,2,p1,3,
"\node{M:PP}") ;
draw fromtopaths(followpath,p2,1,p2,2) ;
draw fromtopaths(followpath,p3,1,p3,2) ;
draw fromtopaths(followpath,p2,2,p2,3,
"\node{Ax:NP}") ;
draw fromtopaths(followpath,p4,1,p4,2) ;
draw fromtopaths(followpath,p5,1,p5,2) ;
draw fromtopaths(followpath,p4,2,p4,3,
"\node{P:VP}") ;
draw fromtopaths(followpath,p6,1,p6,2) ;
draw fromtopaths(followpath,p7,1,p7,2) ;
draw fromtopaths(followpath,p6,2,p6,3,
"\node{PCs:AdjP}") ;
draw fromtopaths(followpath,p0,1,p0,2) ;
draw fromtopaths(followpath,p1,3,p1,4) ;
draw fromtopaths(followpath,p0,2,p0,3,
"\node{S:NP}") ;
draw fromtopaths(followpath,p4,3,p4,4) ;
draw fromtopaths(followpath,p6,3,p6,4) ;
draw fromtopaths(followpath,p4,4,p4,5,
"\node{Pred:PredP}") ;
draw node(p0,4.5,"\node{Cl}") ;
draw fromtopaths(followpath,p0,3,p0,4.5);
draw fromtopaths(followpath,p4,5,p4,6) ;
\stopMPcode
\stopbuffer
\startplacefigure [reference=fig:grammar,
title={A categorical grammar structure tree.}]
\getbuffer
\stopplacefigure
Here, I chose to define a series of parallel paths, one per word, with
one path terminating whenever it joins another path (or paths) at a common
parent. Naturally, labeling each branch of the tree structure requires a
knowledge of the tree structure. The code is not short, but hopefully it is
mostly clear. Note that diagrams such as those constructed here will each be
significantly different, making the writing of a general mechanism rather
complex. For example, one might need to construct a tree branching up rather
than down, or to the right (or left), or even following an arbitrary path, such
as a random walk. These can all be achieved individually in \METAPOST\ without
too much difficulty.
\typebuffer
\stopsection
\startsection [title=A 3D projection]
Although \METAPOST\ is a 2D drawing language, it can be easily extended to work
in 3D. Several attempts have been made in the past ranging from simple to
complicated. Here, we will take a simple approach.
The \METAPOST\ language includes a triplet variable type, used to handle
\type {rgb} colors (it also has a quadruplet type used for \type {cmyk}
colors). We will use this \type {triplet} type to hold 3D coordinates.
There is a separate \CONTEXT\ module, entitled \type {three}, which
creates a new \METAPOST\ instance (also named \type {three}), which
loads a set of macros that can be used to manipulate these triplet
coordinates.
\starttyping
\usemodule [three]
\startMPcode{three}
...
\stopMPcode
\stoptyping
\noindent
For our purposes here, only one function is really necessary: \type
{projection()}, which maps a 3D coordinate to a 2D projection on the page. This
will not be a perspective projection having a viewpoint and a focus point, but
rather a very simple oblique projection, useful for, e.g., pseudo|-|3D schematic
drawings. The \type {Z} coordinate is taken to be \type {up} and the \type {Y}
coordinate taken to be \type {right}, both in the plane of the paper. The third
coordinate \type {X} is an oblique projection in a right|-|hand coordinate
system.
Intended for schematic drawings, there is no automatic hidden|-|line removal nor
effects like shading, and line crossings need to be handled manually (using
\type {crossingunder} introduced previously). In \in{Figure} [fig:cube] we draw
a simple cubical commutative diagram, with a node at each corner.
\usemodule [three]
\startbuffer
\startMPcode{three}
save nodepath ;
path nodepath ;
nodepath = (projection Origin --
projection (1,0,0) --
projection (1,1,0) --
projection (0,1,0) --
projection (0,1,1) --
projection (1,1,1) --
projection (1,0,1) --
projection (0,0,1) --
cycle) scaled 5cm ;
draw node(0, "\node
{${\cal C}_{i\cal P}^{\mathrm{nt}}$}");
draw node(1, "\node
{${\cal C}_{i\cal G}^{\mathrm{nt}}$}");
draw node(2, "\node
{${\cal C}_{j\cal P}^{\mathrm{nt}}$}");
draw node(3, "\node
{${\cal C}_{j\cal G}^{\mathrm{nt}}$}");
draw node(4,
"\node{${\cal C}_{j\cal G}$}") ;
draw node(5,
"\node{${\cal C}_{j\cal P}$}") ;
draw node(6,
"\node{${\cal C}_{i\cal G}$}") ;
draw node(7,
"\node{${\cal C}_{i\cal P}$}") ;
interim crossingscale := 30 ;
drawdoublearrows fromto(0,0,1) ;
drawdoublearrows fromto(0,1,2) ;
drawdoublearrows fromto(0,2,3) ;
drawdoublearrows fromto(0,3,0)
crossingunder fromto(0,2,5) ;
drawdoublearrows fromto(0,7,6) ;
drawdoublearrows fromto(0,6,5) ;
drawdoublearrows fromto.ulft(0,5,4,
"\node{$\tau_j$~}") ;
drawdoublearrows fromto.top (0,7,4,
"\node{$\sigma$}") ;
drawdoublearrows fromto.lrt(0,0,7,
"\node{$Ψ^{\mathrm{nt}}$}")
crossingunder fromto(0,6,5) ;
drawdoublearrows fromto(0,1,6) ;
drawdoublearrows fromto(0,2,5) ;
drawdoublearrows fromto(0,3,4) ;
\stopMPcode
\stopbuffer
\typebuffer
\startplacefigure [reference=fig:cube]
\getbuffer
\stopplacefigure
\noindent
Note the use of \type {drawdoublearrows}, a new \METAFUN\ command that is
introduced here.
\stopsection
\startsection [title=Two final examples]
We end this paper with two examples of more advanced commutative diagrams. The
following example, shown in \in{Figure} [fig:tikz-cd], illustrates what in
category theory is called a \emph {pullback}. It is inspired from an example
given in the Ti{\em k}Z\ \acro{CD} (commutative diagrams) package.
\startbuffer[mp:tikz-cd]
\defineframed
[smallnode]
[node]
[foregroundstyle=\tfxx,
background=color,
backgroundcolor=white]
\startMPcode
save nodepath ; save l ; l = 5ahlength ;
save X, Y, Z, XxY, T ;
pair X, Y, Z, XxY, T ;
XxY.i = 0 ; XxY = makenode(XxY.i,"\node{$X\times_Z Y$}") ;
X.i = 1 ; X = makenode(X.i, "\node{$X$}") ;
Z.i = 2 ; Z = makenode(Z.i, "\node{$Z$}") ;
Y.i = 3 ; Y = makenode(Y.i, "\node{$Y$}") ;
T.i = 4 ; T = makenode(T.i, "\node{$T$}") ;
XxY = origin ;
X = XxY + betweennodes.rt (nodepath,XxY.i,nodepath,X.i)
+ (l,0) ;
Z = X + betweennodes.bot(nodepath,X.i, nodepath,Z.i)
+ (0,-.8l);
Y = XxY + betweennodes.bot(nodepath,XxY.i,nodepath,Y.i)
+ (0,-.8l) ;
T = XxY + nodeboundingpoint.ulft(XxY.i)
+ nodeboundingpoint.lft (T.i) + l*dir(135) ;
for i = XxY.i, X.i, Z.i, Y.i, T.i:
draw node(i) ;
endfor
drawarrow fromto.top(0, XxY.i,X.i,"\smallnode{$p$}") ;
drawarrow fromto.rt (0, X.i,Z.i,"\smallnode{$f$}") ;
drawarrow fromto.top(0, Y.i,Z.i,"\smallnode{$g$}") ;
drawarrow fromto.rt (0, XxY.i,Y.i,"\smallnode{$q$}") ;
drawarrow fromto.top( .13,T.i,X.i,"\smallnode{$x$}") ;
drawarrow fromto.urt(-.13,T.i,Y.i,"\smallnode{$y$}") ;
drawarrow fromto (0, T.i,XxY.i,"\smallnode{$(x,y)$}")
dashed withdots scaled .5
withpen currentpen scaled 2 ;
\stopMPcode
\stopbuffer
% Rather, let's do it similarly to TikZ.
\startbuffer[mp:tikz-cd]
\startnodes [dx=2.5cm,dy=2cm,
alternative=arrow]
\placenode [0, 0] {\node{$X\times_Z Y$}}
\placenode [1, 0] {\node{$X$}}
\placenode [1,-1] {\node{$Z$}}
\placenode [0,-1] {\node{$Y$}}
\placenode [-1,1] {\node{$T$}}
\connectnodes [0,1] [position=top,
label={\nodeSmall{$p$}}]
\connectnodes [1,2] [position=right,
label={\nodeSmall{$f$}}]
\connectnodes [0,3] [position=right,
label={\nodeSmall{$q$}}]
\connectnodes [3,2] [position=top,
label={\nodeSmall{$g$}}]
\connectnodes [4,0] [option=dotted,
rulethickness=1pt,
label={\nodeSmall{$(x,y)$}}]
\connectnodes [4,1] [offset=+.13,
position=top,
label={\nodeSmall{$x$}}]
\connectnodes [4,3] [offset=-.13,
position=topright,
label={\nodeSmall{$y$}}]
\stopnodes
\stopbuffer
\startplacefigure [reference=fig:tikz-cd]
\getbuffer[mp:tikz-cd]
\stopplacefigure
The arrow labeled \quotation {$(x,y)$} is drawn \type {dashed withdots} and
illustrates how the line gets broken, implicitly \type {crossingunder}
its centered label.
\typebuffer[mp:tikz-cd]
\noindent
The previous diagram was drawn using the \CONTEXT\ interface. Our final example,
shown in \in {Figure} [fig:tikz-cd2], gives another \quotation{real|-|life}
example of a categorical pullback, also inspired by Ti{\em k}Z-\acro{CD}, but this time drawn
through the \METAPOST\ interface and solving for positions.
\startbuffer[mp:tikz-cd2]
\startMPcode
clearnodepath;
save l ; l = 5ahlength ;
save A, B, C, D, E ;
pair A, B, C, D, E ;
A.i = 0 ; B.i = 1 ; C.i = 2 ;
D.i = 3 ; E.i = 4 ;
A = makenode(A.i,"\node
{$\pi_1(U_1\cap U_2)$}") ;
B = makenode(B.i,"\node
{$\pi_1(U_1)\ast_{\pi_1(U_1\cap U_2)}
\pi_1(U_2)$}") ;
C = makenode(C.i,
"\node{$\pi_1(X)$}") ;
D = makenode(D.i,
"\node{$\pi_1(U_2)$}") ;
E = makenode(E.i,
"\node{$\pi_1(U_1)$}") ;
A = origin ;
B = A + betweennodes.rt(nodepath,A.i,
nodepath,B.i)
+ ( l,0) ;
C = B + betweennodes.rt(nodepath,B.i,
nodepath,C.i)
+ (.7l,0) ;
D = .5[A,B] + (0,-.9l) ;
E = .5[A,B] + (0, .9l) ;
for i = A.i, B.i, C.i, D.i, E.i :
draw node(i) ;
endfor
drawarrow fromto.llft( 0,A.i,D.i,
"\smallnode{$i_2$}") ;
drawarrow fromto.ulft( 0,A.i,E.i,
"\smallnode{$i_1$}") ;
drawarrow fromto ( 0,D.i,B.i) ;
drawarrow fromto ( 0,E.i,B.i) ;
drawarrow fromto.urt( .1,E.i,C.i,
"\smallnode{$j_1$}") ;
drawarrow fromto.lrt(-.1,D.i,C.i,
"\smallnode{$j_2$}") ;
drawarrow fromto.top( 0,B.i,C.i)
dashed evenly ;
draw textext.top("{\strut$\simeq$}")
shifted point .4 of fromto(0,B.i,C.i) ;
\stopMPcode
\stopbuffer
\startplacefigure [reference=fig:tikz-cd2,
title={A categorical pullback diagram, with MetaPost finding the
positions.\hfil\break}]
\getbuffer[mp:tikz-cd2]
\stopplacefigure
\typebuffer[mp:tikz-cd2]
\stopsection
\startsection [title=Conclusions]
There was initial consensus at the 2017 \CONTEXT\ Meeting in Maibach, Germany,
where a version of this package was presented, that there was little use of
developing a purely \CONTEXT\ interface. Rather, the \METAPOST\ package should
be sufficiently accessible. Since then, however, we decided that the development
of a derivative \CONTEXT\ interface implementing some basic functionality could
indeed be useful for many users, although it will necessarily remain somewhat
limited. Users are recommended to turn to the pure \METAPOST\ interface when
more sophisticated functionality is needed.
\stopsection
\column
\startsection [title=Acknowledgements]
This module was inspired by a request made by Idris Samawi Hamid to draw a
natural transformation diagram in \METAPOST\ (see \in{Figure} [fig:natural]).
The \METAPOST\ macros that were then developed have benefited from improvements
suggested by Hans Hagen as well as inspiration provided by Taco Hoekwater.
\stopsection
\startsubject [title=References]
\placelistofpublications
\stopsubject
\StopArticle