I would like to learn how to draw diagrams like this one and this one. Unfortunately, I have no idea where to start.

I would appreciate all guidance. I prefer not to just dive blindly into a package like tikz which is why I'm hoping for guidance.

  • 1
    You will have to loose at least one eye if you want to learn to draw such things. – percusse Dec 29 '17 at 23:02
  • @percusse lose or loose? – Thruston Jan 1 '18 at 14:18

Three Branches

It's the weekend and I felt a bit like tinkering, so here's my attempt to more or less reproduce one of your two examples, with some color for contrast/clarification:

The position of the circles on the right side of the crosspoint is set by first extracting the intersection coordinate of two of the three paths, then marking the other path and the baseline at the same x coordinate as the intersection between the two upper paths (see the bring reference to lower curve comment).

ramification figure with hobby library

    \coordinate (crosspoint) at (4,-1);
    \draw[-latex,name path=first path]  (0,-2)   
        to[out angle=0,in angle=180,curve through={
            (2,-2.0) .. 
            (crosspoint) .. 
            (4.5,-0.25) .. 
            (6, 0.0)
    ] (8,-0.5);
    \draw[-latex,name path=second path] (0, 0)   
        to[out angle=0,in angle=180,curve through={
            (2, 0.0) .. 
            (crosspoint) .. 
    ] (8,-2);
    \draw[-latex,name path=third path]  (0,-1.5) 
        to[out angle=0,in angle=180,curve through={
            (2,-1.6) .. 
            (crosspoint) .. 
            (6, 0.2)
    ] (8, 0);

    \fill [red, name intersections={of=first path and third path, by={a,b}}] 
        (a) circle (2pt)
        (b) circle (2pt)

    % Baseline and labels
    \coordinate (reference) at (0,-2.5);
    \draw [blue] (reference) -- ++(8,0);
    \fill [blue] (a|-reference) circle (2pt);
    \fill [blue] (b|-reference) circle (2pt);
    % Bring reference to lower curve
    \draw[opacity=0,name path=vertical reference] (b|-reference) -- (b);
    \fill[red, name intersections={of=second path and vertical reference, by={c}}] 
        (c) circle (2pt)

    % Labels on left
    \node[shift={(-0.1,0)}] (y) at (reference) {\footnotesize$y$};
    \node[shift={(-0.1,1.75)}] (x) at (reference) {\footnotesize$x$};

    % Could also do this with intersections, of course,
    % to cut off at the appropriate height.
    \draw (7,-2.45) -- ++(0,0.30);

    \node at (6.7,-2.25) {\footnotesize$f$};

    % Arrowhead
    \draw[-{Latex[length=2.5mm]}] (6.95,-2.45) -- (7,-2.45);


The solution uses the Hobby package for drawing the lines as cubic Bézier curves to make them look a bit nicer. If that is not acceptable/desirable, you can always draw the image with straight curve segments and a corner radius:

\begin{tikzpicture}[rounded corners=5mm]
    \draw[-latex] (0,0) -- ++(2,0) -- ++(1,1) -- ++(2,0) -- ++(1,-0.5) -- ++(2,0);

straight segments

Topics you might wish to look into in the PGF manual/stuff I've used here:

  • Arrows: p.182ff., arrows.meta library, p.202ff.
  • The intersections library (p. 138ff., Section 13.3.2)
  • The -| and |- operators: -| takes the horizontal components of the first coordinate, and the vertical component of the second coordinate, and |- does the analogous the other way around.
  • relative coordinates: Section 13.4, p.140ff.
  • "Syntax for Path Specifications", Chapter 14, p.146ff. (includes rounded corners in Section 14.5 on p.150ff.)
  • "Actions on Paths", Chapter 15, p.164ff.
  • The tutorials in Part I of the PGF manual might be worth a look if you're just starting out. They introduce most of the important concepts, and if you want to delve deeper, the PGF manual is heavily cross-referenced, so it's usually as easy as just clicking a command and jumping to its thorough documentation elsewhere in the pdf.
  • If the curves have to fulfill some rigorous mathematical specifications instead of just being drawn through random points like my example, you can have a look at "Plots of Functions" (Chapter 22) in the PGF manual, or for more demanding tasks, the datavisualization command from Part IV of the PGF manual (p.756ff.), or the pgfplots package.

I'm sure there are several ways in which the code could be optimized, but one has to start somewhere.

Update: Two Branches

Since we can't use the intersection of two branches to mark the other branch as well as the baseline, we set a vertical helper line at refb 's x coordinate and mark the intersections of that helper line with the two branches, as well as its point on the blue baseline.

Set the helper line opacity=0 to make it invisible; I've left it slightly visible here to illustrate the concept (the upper example uses the same idea, but with a different reference for the x coordinate; namely the x coordinate of the intersection of the two upper branches).

Adjust the horizontal position of the helper line and the intersection with the branches as needed by changing the x coordinate of refb.

two branches

Code (preamble is the same as above):

    \coordinate (crosspoint) at (4,-1);
    \draw[-latex,name path=first path]  (0,-2)   
        to[out angle=0,in angle=180,curve through={
            (2,-2.0) .. 
            (crosspoint) .. 
            (6, 0.0)
    ] (8,-0.5);
    \draw[-latex,name path=second path] (0, 0)   
        to[out angle=0,in angle=180,curve through={
            (2, 0.0) .. 
            (crosspoint) .. 
    ] (8,-2);

    \fill [red] (crosspoint) circle (2pt);

    % Baseline and labels
    \coordinate (refa) at (0,-2.5);
    \draw [blue] (refa) -- ++(8,0);
    \fill [blue] (crosspoint|-refa) circle (2pt);

    % Mark point on reference line and bring up to the two branches
    \coordinate (refb) at (6,-2.5);
    % Helper line. The red dots on the two branches will shift along the
    % branches as you adjust the helper line's x coordinate.
    % Change opacity to 0 to make line invisible again.
    \draw[opacity=0.2,name path=vertical reference] (refb) -- ++(0,2.6);

    \fill[blue] (refb) circle (2pt);
    \fill[red, name intersections={of=first path and vertical reference, by={a}}]
        (a) circle (2pt);
    \fill[red, name intersections={of=second path and vertical reference, by={b}}]
        (b) circle (2pt);

    % Labels on left
    \node[shift={(-0.1,0)}] (y) at (refa) {\footnotesize$y$};
    \node[shift={(-0.1,1.75)}] (x) at (refa) {\footnotesize$x$};

    % Could also do this with intersections, of course,
    % to cut off at the appropriate height.
    \draw (7,-2.45) -- ++(0,0.30);

    \node at (6.7,-2.25) {\footnotesize$f$};

    % Arrowhead
    \draw[-{Latex[length=2.5mm]}] (6.95,-2.45) -- (7,-2.45);

  • Glad to be of service. Have fun! – alpenwasser Dec 30 '17 at 13:37
  • Dear alepnwasser, forgive me for bugging you. Using your marvelous answer I am trying to create a simpler ramification diagram featuring only two branches. I have deleted the 'third path' and also the line (4.5,-0.25) ... This "loosens" the 'first path' a little so that the upper red circle no longer lies on it. How can I move this upper red circle down to lie on the 'first path' again? (It seems your solution actually uses this red circle to determine the one below it, so I am not sure what to do.) Many thanks! – Arrow Dec 31 '17 at 11:02
  • @Arrow Yes, I can see how that would make things tricky; the x coordinate of the intersection between first path and third path is used as a reference to mark the other points in the first example. I've added a two-branch example and some more explanations on how the whole thing actually works. – alpenwasser Dec 31 '17 at 12:12
  • I really can't thank you enough. I'll start a bountry on this question when I can so that I can award your answer :) – Arrow Dec 31 '17 at 14:11

Just for comparison, here is an effort in Metapost, where "Hobby" curves originally came from.

I'm relying on the WP article that appears to be the source of the OP diagram for the semantics, so I hope my approach is appropriate. I've started by defining the common points, then drawing the x-fibres through them, rather than drawing lines and looking for the intersections. And I've tried to use the colors to emphasize the fact that this is supposed to be a map from x to y.

enter image description here

Here's the code, with what I hope are useful comments:


    % a variable for the width of the whole thing
    numeric wd;  wd = 240;

    % the paths we want
    path codomain, fiber[];

    % codomain is easy - just a base line
    codomain = (0,0) -- (wd,0);

    % to make the fibres, start by defining
    % common points that they pass through
    % note that we have nice implicit multiplication in MP, and fractions...
    z1 = (1/3 wd, 42);   
    z2 = (1/2 wd, 32);   
    z3 = (1/2 wd, 54);

    % % and some matching points on the co-domain line
    z4 = (x1,0);
    z5 = (x2,0);

    % start drawing from the back, with some dotted lines to link the matching
    % points
    draw z1 -- z4 dashed withdots scaled 1/4;
    draw z3 -- z5 dashed withdots scaled 1/4;

    % now define the fibers
    fiber1 = (0,32) {right} .. z1 {dir 40} .. z2 .. {right} (wd,48);
    fiber2 = (0,42) {right} .. z1 {dir-30} .. z2 .. {right} (wd,64);
    fiber3 = (0,64) {right} .. z1          .. z3 .. {right} (wd,32);

    % the "{right}" notation is the equivalent of TikZ in= and out= angles; here it
    % means "travelling right at this point", but it's quite general so you can put
    % any vector you want such as "{dir 40}" which gives a unit vector pointing at
    % 40° above the horizontal.  You can also leave them out.  As well as using the
    % directions at the start and finish, I've used them in the middle to tweak the
    % curves.   Now draw them...

    draw fiber1;
    draw fiber2;
    draw fiber3;

    % and mark some small circles at the key points
    fill fullcircle scaled 3 shifted z1;
    fill fullcircle scaled 3 shifted z2;
    fill fullcircle scaled 3 shifted z3;

    % and lets put a label at the left, 1/2 way between the start of fiber1 and
    % fiber3
    label.lft("$x$", 1/2[point 0 of fiber1, point 0 of fiber3]);

    % now change color to blue and draw the codomain and it's markers, and label
    drawoptions(withcolor 2/3 blue);
    draw codomain;
    fill fullcircle scaled 3 shifted z4;
    fill fullcircle scaled 3 shifted z5;
    label.lft("$y$", point 0 of codomain);

    % finally define another path for the mapping arrow, and draw & label that
    drawoptions(withcolor 3/4 red);
    path map; map = 1/2[z1,z2] shifted 8 down -- ((x1+x2)/2,5); 
    drawarrow map;
    label.rt("$f$", point 1/2 of map);

    % tidy up neatly


This is wrapped up in luamplib so compile this with lualatex, or work out how to adapt it for pdflatex with GMP, or for plain Metapost.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.