7

I have a tikz-cd diagram, and I would like to bend the arrows by a certain distance (so that their heights are a fixed distance from the straight line connecting the endpoints). With bend left, the height depends on the width, for example in the following diagram.

enter image description here

How can I instead specify a distance (so that I can make the arrows in the above example the same height, for instance)? The diagrams are intended to be output from a program, so it's okay if the solution involves manual calculation, but should avoid defining new macros for the purpose.

Section 52.3 of the TikZ & PGF manual seems relevant, but I couldn't work out how to use any of the options there to achieve this.

\documentclass{article}

\usepackage{tikz-cd}

\begin{document}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, bend left] \arrow[from=1-2, to=1-4, bend left] \arrow[from=1-4, to=1-7, bend left] \end{tikzcd}

\end{document}


I'd like to be able to control the height of arrows in any direction: e.g. the vertical and diagonal arrows below should be the same height.

enter image description here

\begin{tikzcd}
    \bullet & \bullet \\
    \bullet && \bullet \\
    \\
    \bullet &&&& \bullet
    \arrow[from=2-3, to=4-5, bend left]
    \arrow[from=1-1, to=2-1, bend left]
    \arrow[from=2-1, to=4-1, bend left]
    \arrow[from=1-2, to=2-3, bend left]
\end{tikzcd}
varkor
  • 539
  • 2
  • 12
  • I have understood not well your question. +1. – Sebastiano Jul 31 '20 at 19:25
  • 2
    I added a new style called my curve 2 to my answer. – AndréC Aug 01 '20 at 05:16
  • 1
    See New style my curve 5 where we can control the height of the arrows in any direction. The control points are placed by default at 0.25 and 0.75 from the length of the path and at a distance of 7 mm from the path. – AndréC Aug 04 '20 at 04:52

1 Answers1

8

Here are my proposals:

Update 6: New style my curve 7 same principle as style 5.

But with a pattern like my curve 7 = 9mm of .25

my curve 7

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}

\tikzset{my curve 7/.style args={#1of#2}{ to path={.. controls ($(\tikztostart)!#2!(\tikztotarget)!#1!90:(\tikztotarget)$) and ($(\tikztostart)!1-#2!(\tikztotarget)!#1!90:(\tikztotarget)$) .. (\tikztotarget)\tikztonodes}}, my curve 7/.default={7mm of 0.25}}

\begin{document} %With the "\textbf{my curve 7}" style you can control the height of the arrows in any direction: eg. the vertical and diagonal arrows below are the same height. % % As for the previous styles, by default the control points are placed at 0.25 and 0.75 from the length of the path and at a distance of 7 mm from the path.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow["a",blue,from=1-1, to=1-2, my curve 7] \arrow["b",red,from=1-2, to=1-4, my curve 7=7mm of .25] \arrow["c",violet,from=1-4, to=1-7, my curve 7=7mm of .25] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow["d"blue,from=1-2, to=1-1, my curve 7] \arrow["e"red,from=1-4, to=1-2, my curve 7=11mm of .4] \arrow["f"violet,from=1-7, to=1-4, my curve 7] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet \ \bullet && \bullet \ \ \bullet &&&& \bullet \arrow["g",from=2-3, to=4-5, my curve 7] \arrow["h",from=1-1, to=2-1, my curve 7=11mm of .3] \arrow["i",from=2-1, to=4-1, my curve 7=11mm of .3] \arrow["j",from=1-2, to=2-3, my curve 7] \arrow["k",from=4-5, to=2-3, my curve 7=11mm of .4] \arrow["l",from=2-1, to=1-1, my curve 7] \arrow["m",from=4-1, to=2-1, my curve 7] \arrow["n",from=2-3, to=1-2, my curve 7=11mm of .4] \end{tikzcd} \end{document}

Update 5: New style my curve 6: Both arguments are now optional.

Code adapted from How to use “style n args” with three or four arguments

[enter image description here

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}
%\usepackage{amsmath}

% code adapted from https://tex.stackexchange.com/questions/532341/how-to-use-style-n-args-with-three-or-four-arguments \tikzset{my curve 6/.style={varkors settings={#1},to path={.. controls ($(\tikztostart)!\pv{pos}!(\tikztotarget)!\pv{height}!90:(\tikztotarget)$) and ($(\tikztostart)!1-\pv{pos}!(\tikztotarget)!\pv{height}!90:(\tikztotarget)$$) .. (\tikztotarget)\tikztonodes}}, varkors settings/.code={\tikzset{varkor/.cd,#1} \def\pv##1{\pgfkeysvalueof{/tikz/varkor/##1}}}, varkor/.cd,pos/.initial=0.25,height/.initial=7mm}

\begin{document} %With the "\textbf{my curve 6}" style you can control the height of the arrows in any direction: eg. the vertical and diagonal arrows below are the same height. Both arguments are now optional. % % As for the previous styles, by default the control points are placed at 0.25 and 0.75 from the length of the path and at a distance of 7 mm from the path.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow["a",from=1-1, to=1-2, my curve 6] \arrow["b",from=1-2, to=1-4, my curve 6={pos=.25}] \arrow["c",from=1-4, to=1-7, my curve 6={height=7mm}] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow["d",from=1-2, to=1-1, my curve 6] \arrow["e",from=1-4, to=1-2, my curve 6={pos=.4,height=11mm}] \arrow["f",from=1-7, to=1-4, my curve 6] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet \ \bullet && \bullet \ \ \bullet &&&& \bullet \arrow["g",from=2-3, to=4-5, my curve 6] \arrow["h",from=1-1, to=2-1, my curve 6={pos=.3,height=11mm}] \arrow["i",from=2-1, to=4-1, my curve 6={pos=.3,height=11mm}] \arrow["j",from=1-2, to=2-3, my curve 6] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet \ \bullet && \bullet \ \ \bullet &&&& \bullet \arrow["k",from=4-5, to=2-3, my curve 6={pos=.4,height=11mm}] \arrow["l",from=2-1, to=1-1, my curve 6] \arrow["m",from=4-1, to=2-1, my curve 6] \arrow["n",from=2-3, to=1-2, my curve 6={pos=.4}] \end{tikzcd} \end{document}

Update 4: Style my curve 5

With the my curve 5 style you can control the height of the arrows in any direction: eg. the vertical and diagonal arrows below are the same height.

As for the previous styles, by default the control points are placed at 0.25 and 0.75 from the length of the path and at a distance of 7 mm from the path.

screenshot

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}
\usepackage{amsmath}

\tikzset{my curve 5/.style 2 args={ to path={.. controls ($(\tikztostart)!#1!(\tikztotarget)!#2!90:(\tikztotarget)$) and ($(\tikztostart)!1-#1!(\tikztotarget)!#2!90:(\tikztotarget)$) .. (\tikztotarget)\tikztonodes}}, my curve 5/.default={.25}{7mm} }

\begin{document} %With the "\textbf{my curve 5}" style you can control the height of the arrows in any direction: eg. the vertical and diagonal arrows below are the same height. % % As for the previous styles, by default the control points are placed at 0.25 and 0.75 from the length of the path and at a distance of 7 mm from the path.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 5] \arrow[from=1-2, to=1-4, my curve 5] \arrow[from=1-4, to=1-7, my curve 5] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-2, to=1-1, my curve 5] \arrow[from=1-4, to=1-2, my curve 5] \arrow[from=1-7, to=1-4, my curve 5] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet \ \bullet && \bullet \ \ \bullet &&&& \bullet \arrow[from=2-3, to=4-5, my curve 5] \arrow[from=1-1, to=2-1, my curve 5={.3}{11mm}] \arrow[from=2-1, to=4-1, my curve 5={.3}{11mm}] \arrow[from=1-2, to=2-3, my curve 5] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet \ \bullet && \bullet \ \ \bullet &&&& \bullet \arrow[from=4-5, to=2-3, my curve 5={.3}{9mm}] \arrow[from=2-1, to=1-1, my curve 5] \arrow[from=4-1, to=2-1, my curve 5] \arrow[from=2-3, to=1-2, my curve 5={.3}{9mm}] \end{tikzcd}

\end{document}

Update 3: Style my curve 4 with two parameters that defaults to 0.25 and 7mm.

my curve 4

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}
\usepackage{amsmath}

\tikzset{my curve 4/.style 2 args={ to path={.. controls ($(\tikztostart)!#1!(\tikztotarget)+(0,#2)$$) and ($(\tikztostart)!1-#1!(\tikztotarget)+(0,#2)$$) .. (\tikztotarget)\tikztonodes}}, my curve 4/.default={.25}{7mm} }

\begin{document} \begin{minipage}{.65\textwidth}

\begin{enumerate}

\item By default "$\textcolor{blue}{\text{my curve 4}}$".

The control points are placed at 0.25 and 0.75 of the path length, the height is 7 mm.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 4] \arrow[from=1-2, to=1-4, my curve 4] \arrow[from=1-4, to=1-7, my curve 4] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-2, to=1-1, my curve 4] \arrow[from=1-4, to=1-2, my curve 4] \arrow[from=1-7, to=1-4, my curve 4] \end{tikzcd}

\item You can change the location of these control points.

Here \mbox{"\textcolor{blue}{$\text{my curve 4={.4}{5mm}}$"}}.

They are located at 0.4 and 0.6, the height is 5 mm.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 4={.4}{5mm}] \arrow[from=1-2, to=1-4, my curve 4={.4}{5mm}] \arrow[from=1-4, to=1-7, my curve 4={.4}{5mm}] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-2, to=1-1, my curve 4={.4}{5mm}] \arrow[from=1-4, to=1-2, my curve 4={.4}{5mm}] \arrow[from=1-7, to=1-4, my curve 4={.4}{5mm}] \end{tikzcd}

\item Here: "\textcolor{blue}{$\text{my curve 4={.3}{9mm}}$}".

They are located at 0.3 and 0.7, the height is 9 mm.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 4={.3}{9mm}] \arrow[from=1-2, to=1-4, my curve 4={.3}{9mm}] \arrow[from=1-4, to=1-7, my curve 4={.3}{9mm}] \end{tikzcd}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-2, to=1-1, my curve 4={.3}{9mm}] \arrow[from=1-4, to=1-2, my curve 4={.3}{9mm}] \arrow[from=1-7, to=1-4, my curve 4={.3}{9mm}] \end{tikzcd} \end{enumerate} \end{minipage} \end{document}

Update 2: Style my curve 3 with a parameter that defaults to 0.25.

my curve 3

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}
\usepackage{amsmath}

\tikzset{my curve 3/.style={ to path={([xshift=-3pt]\tikztostart.north east) .. controls ($(\tikztostart)!#1!(\tikztotarget)+(0,.7)$$) and ($(\tikztostart)!1-#1!(\tikztotarget)+(0,.7)$$) .. ([xshift=3pt]\tikztotarget.north west)\tikztonodes}}, my curve 3/.default=.25 }

\begin{document} \begin{enumerate}

\item By default "$\textcolor{blue}{\text{my curve 3}}$" the control points are placed at 0.25 and 0.75 of the path length.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 3] \arrow[from=1-2, to=1-4, my curve 3] \arrow[from=1-4, to=1-7, my curve 3] \end{tikzcd}

\item You can change the location of these control points, here \mbox{"\textcolor{blue}{$\text{my curve 3}=.4$"}} they are located at 0.4 and 0.6.

Indeed: $1-0.4=0.6$.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 3=.4] \arrow[from=1-2, to=1-4, my curve 3=.4] \arrow[from=1-4, to=1-7, my curve 3=.4] \end{tikzcd}

\item Here they are located at 0.3 and 0.7. "\textcolor{blue}{$\text{my curve 3}=.3$}"

Indeed: \mbox{$1-0.3=0.7$}.

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 3=.3] \arrow[from=1-2, to=1-4, my curve 3=.3] \arrow[from=1-4, to=1-7, my curve 3=.3] \end{tikzcd}

\end{enumerate} \end{document}

Update 1: New style my curve 2

It uses the calc library

my curve 2

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}

\tikzset{my curve 2/.style={to path={([xshift=-3pt]\tikztostart.north east) .. controls ($(\tikztostart)!.25!(\tikztotarget)+(0,.7)$) and ($(\tikztostart)!.75!(\tikztotarget)+(0,.7)$) .. ([xshift=3pt]\tikztotarget.north west)\tikztonodes}}}

\begin{document}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve 2] \arrow[from=1-2, to=1-4, my curve 2] \arrow[from=1-4, to=1-7, my curve 2] \end{tikzcd} \end{document}

Old answer : style my curve

screenshot

\documentclass{article}
\usepackage{tikz-cd}

\tikzset{my curve/.style={to path={([xshift=-3pt]\tikztostart.north east) .. controls ++(80:5ex) and ++(100:5ex) .. ([xshift=3pt]\tikztotarget.north west)\tikztonodes}}}

\begin{document}

\begin{tikzcd} \bullet & \bullet && \bullet &&& \bullet \arrow[from=1-1, to=1-2, my curve] \arrow[from=1-2, to=1-4, my curve] \arrow[from=1-4, to=1-7, my curve] \end{tikzcd}

\end{document}

AndréC
  • 24,137
  • Thanks, this is really nice. Is there a way to make the style (my curve 3) work regardless of the direction of the arrows? I can see how to manually change the style for fixed directions, but not how to make it work automatically for any direction. I'd like to parameterise the height too, but I should be able to work out how to do that. – varkor Aug 03 '20 at 18:09
  • 1
    See the new style my curve 4 – AndréC Aug 03 '20 at 19:11
  • I just realised my comment was ambiguous: I meant vertical edges (e.g. from 1-1 to 2-1) and diagonal edges (e.g. 1-1 to 2-3), so it works to specify distance for any arrow. But my curve 4 is close enough that I should be able to figure out what needs to be changed: I just need the length to be in the direction normal to the direction from the source to the target. If you know off the top of your head how to do that, that'd be amazing, but this is essentially everything I was looking for, so I'm marking as answered. Thank you for these solutions! – varkor Aug 03 '20 at 21:27
  • 1
    I want to improve the code, but I don't quite understand what you just said. Can you illustrate what you want to achieve with a freehand figure? – AndréC Aug 03 '20 at 21:31
  • I've edited my original post. Sorry, I meant that I could see that (0,#2) would need to change to "distance of #2 in the direction from source to target, plus 90°", but I'm not familiar with the syntax to know how to express that yet. – varkor Aug 03 '20 at 21:42
  • 1
    See the new style my curve 5 – AndréC Aug 04 '20 at 04:47
  • It looks great! I've just noticed labels don't seem to work any more with this style, e.g. \arrow["f", from=1-1, to=1-2, my curve 6]. Is there a way to make that work? – varkor Aug 04 '20 at 11:38
  • Do the labels work with the other styles 2; 3; 4 and 5? (I haven't tested this!) – AndréC Aug 04 '20 at 11:40
  • No, they appear not to. Sorry: I didn't check this before, because I didn't imagine using a custom style would affect label placement. – varkor Aug 04 '20 at 11:42
  • I hadn't imagined that either. I will investigate. – AndréC Aug 04 '20 at 11:44
  • 1
    Check out the update: all styles now support labels (unless I made a mistake). – AndréC Aug 04 '20 at 11:56
  • Amazing, this is perfect, thank you! – varkor Aug 04 '20 at 15:04
  • @varkor I just added a new style, with a more practical syntax which is a pattern. – AndréC Aug 06 '20 at 20:09
  • AndréC: this is working perfectly. I wondered if you might know whether it was possible to have an option to shorten the arrow from either end proportionally to the length of the curve? (shorten works badly with curves, and also isn't proportional.) If it's appropriate, I could open a new question for this problem: I've attempted it myself, but in honesty I don't know where to start. You seem the most appropriate person to ask, since I want it to interact with this curve code, but apologies if this is not a suitable place to ask. – varkor Oct 27 '20 at 23:36
  • @varkor Yes, ask a new question with the style you adopted by giving the code that didn't work. – AndréC Oct 28 '20 at 06:52
  • I have asked a new question here. – varkor Oct 28 '20 at 11:45
  • AndréC: I would like to include this style as part of a .sty file included in an open source project under the MIT licence, attributing it to you either by linking to your TeX.SE account, or however you would like. Are you happy for me to do so (or, if not, is there somewhere else we could discuss this)? Thank you! – varkor Nov 10 '20 at 15:12
  • 1
    @varkor Yes, please do, thank you. I haven't had time to think about the following question yet (to shorten the arrow).. As soon as I have time, I will get started. Have a good evening. – AndréC Nov 10 '20 at 19:30