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.
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It only takes a minute to sign up.
Sign up to join this communityI 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.
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).
\documentclass[tikz]{standalone}
\usetikzlibrary{hobby}
\usetikzlibrary{intersections}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{tikzpicture}
\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) ..
(6,-2.0)
}
] (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);
\end{tikzpicture}
\end{document}
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);
\end{tikzpicture}
Topics you might wish to look into in the PGF manual/stuff I've used here:
arrows.meta
library, p.202ff.-|
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.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.
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
.
Code (preamble is the same as above):
\begin{tikzpicture}
\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) ..
(6,-2.0)
}
] (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);
\end{tikzpicture}
(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!
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.
Dec 31, 2017 at 12:12
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.
Here's the code, with what I hope are useful comments:
\RequirePackage{luatex85}
\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
beginfig(1);
% 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
drawoptions();
endfig;
\end{mplibcode}
\end{document}
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.