6

The edges are matching. I would like them to be parallel horizontally. I tried changing the value of the yshit parameter, but it didn't work.

   \documentclass{article}
   \usepackage{geometry}
   \usepackage{tikz}
   \usetikzlibrary{automata, arrows.meta, positioning, shapes}


   \begin{document}

   \resizebox{0.9\textwidth}{!}{
   \begin{tikzpicture}[
   >={Stealth[round]},
   line width=0.8pt, 
   state/.style={draw,rounded rectangle,line width=0.4pt},
   ]

   \node [state] (q1){$\lbrace q_2,q_3,q_4,q_5,q_7,q_8,q_9 \rbrace$};
   \node [state, right=2cm of q1] (q2){$\lbrace q_2,q_3,q_5,q_6,q_7 \rbrace$};

   \path [->]
   (q1) [transform canvas={yshift=4mm}, shorten <=-0.25pt, shorten >=-0.5pt] edge node    [above, pos = 0.5] {$1$} (q2)
    (q2) [transform canvas={yshift=-4mm}, shorten <=-0.25pt, shorten >=-0.5pt] edge node [below, pos = 0.5] {$0$} (q1);
\end{tikzpicture}
}

\end{document}

enter image description here

3
  • I am not sure what your goal is. Do you want the arrows to start at the border of q1 and go horizontal to the border of q2 but above(or below) center line? For this you need eg. the intersection library or tikz-cd. What is your shorten for? Aug 19 at 17:48
  • Not the same problem but he same shape and cause for the headache: Q619274 Aug 19 at 18:55
  • Do you just want to shift it or should it still reach the node's border? Aug 19 at 18:59

3 Answers 3

5

Probably better to do in tikz-cd, but here is an intersection solution. I use (q1.4) where .4 is the angle on the border. To use yshift then the intersection with the first node border is also needed.

\documentclass[tikz, border=1cm]{standalone}
\usetikzlibrary{arrows.meta, positioning, shapes, intersections}
\begin{document}
\begin{tikzpicture}[
>={Stealth[round]},
thick, 
state/.style={draw, rounded rectangle, thin},
]
\node [state] (q1) {$\lbrace q_2,q_3,q_4,q_5,q_7,q_8,q_9 \rbrace$};
\node [state, right=2cm of q1, name path=q2] (q2) {$\lbrace q_2,q_3,q_5,q_6,q_7 \rbrace$};
\path[name path=aboveh] (q1.4) -| (q2);
\draw[->, name intersections={of=aboveh and q2}] (q1.4) --node[above] {$1$} (intersection-1);
\path[name path=belowh] (q1.-4) -| (q2);
\draw[<-, name intersections={of=belowh and q2}] (q1.-4) --node[below] {$2$} (intersection-1);
\end{tikzpicture}
\end{document}

Two nodes with arrows above and below centerline

Edit: I now find that shift left in tikz-cd just shifts the arrow - so it will no longer start/end on the border. transform canvas={yshift=...} will work the same. -an option you can give to \draw(not to the edge) like this:

\draw[->, transform canvas={yshift=1mm}] (q1) --node[above]{$1$} (q2); 

Arrow not ending on node border

Manual adjustment with shorten will not be perfect with all node sizes.

2
  • 1
    We could extract the the angles and the radii of the nodes and use those to calculate all sorts of things but that's a lot of work. If the nodes all have the same height/depth (which we could force) we could just use (q1.4) -- (q2.176) but that only looks good if the content in one row is similar in height. Aug 19 at 19:02
  • @Qrrbrbirlbel I tried this solution (same angles but reversed) but it only works if both nodes are exactly the same width. Otherwise, the edge is not horizontal.
    – SebGlav
    Aug 19 at 19:09
4

This solution automatically finds the point on the border of two rounded rectangles that connect these nodes by a line that is shifted to the left.

Use key shift between rounded corners=<length> with an optional value (default: 4pt).

For this to work best, the following must be considered

  • both nodes are of the shape rounded rectangle
  • the <length> must not be too big so that
    • it can at least one intersection point
    • it should not find one than more connection point

Every rounded corners stores away the \radius, the \halfarcangle as well as the \outer xsep. We can retrieve them by using the PGF-internal macros \pgf@sh@s@rounded rectangle and \pgf@sh@ma@<node name>.

We then use these values to do the path again (\pgf@sh@bg@rounded rectangle), however we don't use the exact original one but one on the outside of the border – just like all the anchors we would use with normal connections. However, since we do not want to find the point of the border between the center and the other node we need to find these intersections ourselves.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{automata, arrows.meta, positioning, shapes.misc, intersections}
\makeatletter
\tikzset{
  @insert rounded rectangle path/.code=% #1 = node name
    \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}%
    \csname pgf@sh@s@rounded rectangle\endcsname
    \csname pgf@sh@ma@#1\endcsname
    \expandafter\def\expandafter\roundedrectanglepoints\expandafter{\roundedrectanglepoints
      \pgfmathsetlengthmacro\radius{\radius+\outerxsep}%
      \pgfmathsetlengthmacro\arcwidth{\arcwidth+\outerxsep}%
      \pgfmathsetlengthmacro\halfheight{\halfheight+\outerysep}%
      \pgfmathsetlengthmacro\halfwidth{\halfwidth+\outerxsep}%
      \def\outerxsep{0pt}\def\outerysep{0pt}}%
    \pgf@sh@savedpoints
    \pgftransformshift{\pgfpointanchor{#1}{center}}%
    \csname pgf@sh@bg@rounded rectangle\endcsname}
\makeatother
\tikzset{
  shift between rounded corners/.default=4pt,
  shift between rounded corners/.style={%
    % this assumes both nodes to be rounded corners
    % this assumes that \tikztostart and \tikztotarget
    % are actually node names and no other coordinates
    %
    % #1 = shift to the left
    %      must be small enough so that the connection even hits the nodes
    %      with arc lengths > 180, more than one intersection
    %      exist which will lead to the wrong point to be found
    /utils/exec={%
      \pgfinterruptpath
        \path[name path=@pathofstart,@insert rounded rectangle path=\tikztostart];
        \path[name path=@pathoftarget,@insert rounded rectangle path=\tikztotarget];
        \path[name path=@pathofstraight]\pgfextra
            \pgfmathanglebetweenpoints{\pgfpointanchor{\tikztostart}{center}}{\pgfpointanchor{\tikztotarget}{center}}%
            \pgftransformshift{\pgfpointanchor{\tikztostart}{center}}%
            \pgftransformrotate{\pgfmathresult}%
            \pgfpathmoveto{\pgfpoint{0}{#1}}%
            \pgfpathlineto{\pgfpointadd{\pgfpointanchor{\tikztotarget}{center}}{\pgfpoint{0}{#1}}}%
          \endpgfextra;
        \tikzset{
          name intersections={of=@pathofstart and @pathofstraight, by={@start}},
          name intersections={of=@pathoftarget and @pathofstraight, by={@target}}}
      \endpgfinterruptpath
    },
    to path={(@start) -- (@target) \tikztonodes}}}
\begin{document}
\begin{tikzpicture}[
  >={Stealth[round]}, line width=0.8pt,
  state/.style={draw, rounded rectangle, line width=0.4pt},
]
\node [state] (q1)                  {$\lbrace q_2,q_3,q_4,q_5,q_7,q_8,q_9 \rbrace$};
\node [state, right=2cm of q1] (q2) {$\lbrace q_2,q_3,q_5,q_6,q_7 \rbrace$};
\path [->]
  (q1) edge[shift between rounded corners] node [above, pos = 0.5] {$1$} (q2)
  (q2) edge[shift between rounded corners] node [below, pos = 0.5] {$0$} (q1);
\end{tikzpicture}
\begin{tikzpicture}[
  >={Stealth[round]}, line width=0.8pt,
  state/.style={draw, rounded rectangle, line width=0.4pt, rounded rectangle east arc=concave},
]
\node [state, rotate=-55] (q1)                  {$\lbrace q_2,q_3,q_4,q_5,q_7,q_8,q_9 \rbrace$};
\node [state, rotate=-80, below right=2cm of q1] (q2) {$\lbrace q_2,q_3,q_5,q_6,q_7 \rbrace$};
\path [->]
  (q1) edge[shift between rounded corners] node [above, pos = 0.5] {$1$} (q2)
  (q2) edge[shift between rounded corners] node [below, pos = 0.5] {$0$} (q1);
\end{tikzpicture}
\end{document}

Output

enter image description here

second picture

1
  • I realize now that this only works best for an arc length of 180, otherwise the reconstructing of the outer path won't work precisely. Aug 21 at 1:57
1

Here's another intersection-based answer, similar in spirit to hpekristiansen's answer. The main difference at the user-level is that it allows you to specify the shift as a distance rather than by using an angle-anchor on the node. It also uses the spath3 library to handle the intersection stuff.

It works by starting out with a path drawn between the centres of the two nodes. It then translates that by the desired amount. Then it intersects the shifted path with the node boundaries and throws away the outer parts. Finally, it renders the remaining piece of the path, with any extras such as arrows and nodes.

This is all nicely wrapped up in a to path.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/654584/86}
\usepackage{geometry}
\usepackage{tikz}
\usetikzlibrary{
  automata,
  arrows.meta,
  positioning,
  shapes,
  spath3,
  intersections
}

\makeatletter
\tikzset{
  every node/.style={
    spath/save global=\tikz@fig@name
  },
  connect/.style={
    to path={
      \pgfextra{
        \path[overlay,spath/save=connection path] (\tikztostart.center) -- (\tikztotarget.center);
        \tikzset{
          spath/transform={connection path}{yshift=#1},
          spath/split at intersections with={connection path}{\tikztostart},
          spath/split at intersections with={connection path}{\tikztotarget},
          spath/remove components={connection path}{1,3},
        }
      } [spath/use=connection path] \tikztonodes
    }
  }
}
\makeatother

\begin{document}

\begin{tikzpicture}[
  >={Stealth[round]},
  line width=0.8pt, 
  state/.style={
    draw,
    rounded rectangle,
    line width=0.4pt,
  },
]

\node [state] (q1){\(\lbrace q_2,q_3,q_4,q_5,q_7,q_8,q_9 \rbrace\)};
\node [state, right=2cm of q1] (q2){\(\lbrace q_2,q_3,q_5,q_6,q_7 \rbrace\)};

\draw [->] (q1) to[connect=2mm] node [above, pos = 0.5] {\(1\)} (q2);
\draw [->] (q2) to[connect=-2mm] node [below, pos = 0.5] {\(0\)} (q1);

\end{tikzpicture}

\end{document}

Shifted arrows between nodes

(The apparent difference in the line thicknesses is due to my pdf viewer.)

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.