I'm glad you said:
but would also be interested to see other ways it can be done.
because I don't use xy-pic
anymore (fantastic though it was when in the pre-TikZ days). Here's my best shot with TikZ.
Here's the result:
The code follows. A couple of comments on my choices. None of the libraries loaded is strictly necessary, but I felt that the matrix
and calc
libraries made for cleaner code and I don't like the standard arrows so the arrows
library makes it look better. I labelled the matrix entries explicitly, which isn't strictly necessary, again because I felt it made the code easier to read. I shifted the horizontal of the snake arrow upwards to avoid the labels on the downward arrows. The key effect, of rounded corners, is the (wait for it) rounded corners
option to the final path. The amsmath
package is purely to get the \DeclareMathOperator
command for \coker
(\ker
is already defined in latex). Again, this could easily be done another way.
(Added in edit) As pointed out in the comments, in the original code the arrow from the lower 0
to the A'
was slightly slanted. This was because the prime adds a little height to the node containing the A'
meaning that the natural targets for the arrow (east of 0
and west of A'
) aren't in a straight line. There are numerous ways to fix this, the one I chose was to align the nodes in the matrix according to their centres instead of their baselines.
Since this answer has proved so popular, I got a little more perfectionistic about it! I decided that the grey horizontal arrows (kernels and cokernels) were a little high for my taste. So I chose the 'mid' targets. Putting these in with the 'edge' command resulted in bizarre extra arrow heads (try it) so I had to put each one in as a separate \draw
command. I also changed the spacing of the grid so that the spaces were measured from the centres of the nodes rather than their edges.
Idle thoughts on this version: I ought to be able to put the mid east/west
targets in the edge
commands. I also wondered if there was some neat way to say "make sure all the arrows are strictly horizontal" rather than using the explicit targets. I wondered about using the "coordinates at intersections" syntax but would need to play with it a little and I'm not sure it would be shorter.
Edit: 2012-09-10 I've just learnt about the asymmetrical rectangle
of the tikz-cd
package and it answers my idle thoughts above so I thought - as this is in many ways my "show case" answer - I'd update this to take advantage of it.
\documentclass{article}
\thispagestyle{empty}
\usepackage{amsmath}
\usepackage{tikz}
\usepackage{tikz-cd}
\usetikzlibrary{%
matrix,%
calc,%
arrows%
}
\DeclareMathOperator{\coker}{coker}
\begin{document}
\begin{tikzpicture}[>=triangle 60]
\matrix[matrix of math nodes,column sep={60pt,between origins},row
sep={60pt,between origins},nodes={asymmetrical rectangle}] (s)
{
&|[name=ka]| \ker f &|[name=kb]| \ker g &|[name=kc]| \ker h \\
%
&|[name=A]| A' &|[name=B]| B' &|[name=C]| C' &|[name=01]| 0 \\
%
|[name=02]| 0 &|[name=A']| A &|[name=B']| B &|[name=C']| C \\
%
&|[name=ca]| \coker f &|[name=cb]| \coker g &|[name=cc]| \coker h \\
};
\draw[->] (ka) edge (A)
(kb) edge (B)
(kc) edge (C)
(A) edge (B)
(B) edge node[auto] {\(p\)} (C)
(C) edge (01)
(A) edge node[auto] {\(f\)} (A')
(B) edge node[auto] {\(g\)} (B')
(C) edge node[auto] {\(h\)} (C')
(02) edge (A')
(A') edge node[auto] {\(i\)} (B')
(B') edge (C')
(A') edge (ca)
(B') edge (cb)
(C') edge (cc)
;
\draw[->,gray] (ka) edge (kb)
(kb) edge (kc)
(ca) edge (cb)
(cb) edge (cc)
;
\draw[->,gray,rounded corners] (kc) -| node[auto,text=black,pos=.7]
{\(\partial\)} ($(01.east)+(.5,0)$) |- ($(B)!.35!(B')$) -|
($(02.west)+(-.5,0)$) |- (ca);
\end{tikzpicture}
\end{document}
Updated version (without crossing):
A possibility with random colors, widths, directions, lengths; the image was produced using
\RandArrow
\def\Columns{10}
\RandArrow[80]
The code:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\pgfdeclarelayer{background}
\pgfsetlayers{background,main}
\def\maxArrow{30}
\def\Columns{6}
\newcommand\randomarrow{
\pgfmathsetseed{\pdfuniformdeviate 10000000}
\edef\R{\pdfuniformdeviate 255}
\edef\G{\pdfuniformdeviate 255}
\edef\B{\pdfuniformdeviate 255}
\xdefinecolor{mycolor}{RGB}{\R,\G,\B}
\tikz\draw[->,line width=2pt*rnd+1pt,color=mycolor]
(rnd,rnd) -- ++(rnd*360:rnd+0.2);
}
\newcommand\RandArrow[1][30]{%
\def\maxArrow{#1}
\begin{tikzpicture}
\foreach [count=\i] \val in {1,...,\maxArrow}
{
\path
let \n{row}={int(mod(\i -1, \Columns))},
\n{col}={ int( ( \i - 1 ) / (-\Columns) ) }
in
(\n{row}, \n{col}) rectangle +(1,1)
+(0.5, 0.5) node{\randomarrow};
}
\begin{pgfonlayer}{background}
\draw[orange!70!black,line width=1pt,fill=yellow!15]
(current bounding box.north west)
rectangle
(current bounding box.south east);
\end{pgfonlayer}
\end{tikzpicture}%
}
\begin{document}
\RandArrow
\def\Columns{10}
\RandArrow[80]
\end{document}
The idea to avoid crossing arrows is to have a grid and place each arrow in one of the squares of the grid.
\maxArrows
allows to specify the number of arrows (initially set to 30
).
\Columns
controls the number of rows of the grid (initially set to 6
).
\randomarrow
draws an arrow; the width, length, color and direction are chosen randomly using rnd
; the length will only be (in the worst case) 0.2cm
larger than the width of the square; this is to prevent arrows from having zero length.
The main command is \RandArrow
with an optional argument allowing to decide the number of arrows to be drawn; the default value is 30
.
As suggested by Paul Gaborit in his answer
, \pgfmathsetseed{\pdfuniformdeviate 10000000}
was used in the definition of \randomarrow
to change the seed used by the pseudo-random generator at each compilation.
Introducing some randomness in the grid gives better results:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\pgfdeclarelayer{background}
\pgfsetlayers{background,main}
\def\maxArrow{30}
\def\Columns{6}
\newcommand\randomarrow{
\pgfmathsetseed{\pdfuniformdeviate 10000000}
\edef\R{\pdfuniformdeviate 255}
\edef\G{\pdfuniformdeviate 255}
\edef\B{\pdfuniformdeviate 255}
\xdefinecolor{mycolor}{RGB}{\R,\G,\B}
\tikz\draw[->,line width=2pt*rnd+1pt,color=mycolor]
(rnd,rnd) -- ++(rnd*360:rnd+0.1);
}
\newcommand\RandArrow[1][30]{%
\pgfmathsetseed{\pdfuniformdeviate 10000000}
\def\maxArrow{#1}
\begin{tikzpicture}
\foreach [count=\i] \val in {1,...,\maxArrow}
{
\path
let \n{row}={ int(mod(\i -1, \Columns))},
\n{col}={ int( ( \i - 1 ) / (-\Columns) ) }
in
(\n{row}, \n{col}) rectangle +({random(2,3)},rand)
node[near start] {\randomarrow};
}
\begin{pgfonlayer}{background}
\draw[orange!70!black,line width=1pt,fill=yellow!15]
(current bounding box.north west)
rectangle
(current bounding box.south east);
\end{pgfonlayer}
\end{tikzpicture}%
}
\begin{document}
\RandArrow
\def\Columns{10}
\RandArrow[80]
\end{document}
Best Answer
Pure TikZ solution: