0

I am using the automata library of TiKZ, and putting lines between nodes 18pt thick. The problem is that the border between the lines and the state do not fit well:

enter image description here

I would like to have this

enter image description here

Any ideas?

Here is a minimal example producing the first one:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{automata}

\begin{document}

\begin{tikzpicture}[thick]
  \node[state] (1) {};
  \draw[-, blue!20, line width=18pt] (1) to (0,1);
\end{tikzpicture}

\end{document}
6
  • Is the curve fixed? I mean, if you want the node to be "malleable" according to the path below, then it gets harder to do. And considering your answer, is it a curve or a circular node below it?
    – Alenanno
    Apr 28, 2015 at 18:06
  • Sorry for not being clear: it is a line between nodes (states) with the automata library. Just like in my answer below. Apr 28, 2015 at 18:08
  • This looks like a job for a decoration. See the circle connection bar decoration (used in mindmaps). Or maybe the Round Cap arrow tip. However, that can only do semi-circles. Apr 28, 2015 at 22:48
  • What surprise me is that there is the need to add a decoration, or layers as in my answer below, or some other add-hoc solution... but this look as a pretty common situation! If the line is thick enough, it will look ugly because it does not follow the border of the object it is touching. Nobody thought this problem before? Apr 29, 2015 at 2:54
  • 1
    Usually, one uses rather thin lines in relation to the size of nodes so the "dis-connection" doesn't show even more so on paper. For same-colored lines you can use the rect but. For more complex borders this will get complex really fast (just think of all the connections from a corner of an rectangle with varying angles of the lines). Apr 29, 2015 at 19:20

2 Answers 2

2

Here is an attempt with arrow tips.
(I also have a solution with decorations but those confuse me more than arrow tips.)

Unfortunately the Round Cap arrow tip only adds (or removes with the reversed option) a semi-circle from the line end. This would work great if the line width is twice the radius of a circular node.

The setup code for the arrow tip Hug Cap calculates some math and saves three values:

  • radius (this is taken from the arrow key length),
  • h, this is the inset (the height), and
  • an angle (its an arc that gets drawn after all).

There is the possibility to extend the arrow tip's definition to include options like reversed, open, left, etc.

The deround style calculates the radius from circular nodes. (While this answer is inspired by the circle connection bar style and decoration from the mindmap library, it uses a different approach to extract the radius from already existing nodes.)

Obviously, the paths must be orthogonal to the circles border.

This solution must be used with to/edge operators (it uses \tikztostart and \tikztotarget to automatic the radii calculations. Do not use other line caps than butt (the default), this could also be checked inside the definition of the arrow tip.

This breaks if the line width is higher than the diameter and also for very large radii (but then why use this even?).

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{automata,arrows.meta}
\pgfdeclarearrow{
  name=Hug Cap,
  parameters=\the\pgfarrowlength,
  setup code={
    % h = r - .5 sqrt(4 r^2 - s^2)
    \pgfmathsetlengthmacro\pgfarrowh{\pgfarrowlength-.5*sqrt(4*\pgfarrowlength*\pgfarrowlength-\pgflinewidth*\pgflinewidth}
    % a = asin(s / (2 r))
    \pgfmathsetmacro\pgfarrowangle{asin(\the\pgflinewidth/(2*\the\pgfarrowlength))}
    \pgfarrowssavethe\pgfarrowlength % radius
    \pgfarrowssave\pgfarrowh         % h
    \pgfarrowssave\pgfarrowangle     % a
    \pgfarrowsupperhullpoint{0pt}{.5\pgflinewidth}
    \pgfarrowsupperhullpoint{\pgfarrowh}{.5\pgflinewidth}
    \pgfarrowssetlineend{.1pt}       % eeh :\
  },
  drawing code={
    \pgfpathmoveto{\pgfqpoint{\pgfarrowh}{-.5\pgflinewidth}}
    \pgfpatharc{180+\pgfarrowangle}{180-\pgfarrowangle}{\pgfarrowlength}
    \pgfpathlineto{\pgfqpoint{0pt}{.5\pgflinewidth}}
    \pgfpathlineto{\pgfqpoint{0pt}{-.5\pgflinewidth}}
    \pgfpathclose
    \pgfusepathqfill}}
\makeatletter
\def\qrr@tikz@circle{circle}
\newcommand*\qrr@getRadius[1]{%
  \def\qrr@radius{0pt}%
  \tikz@scan@one@point\pgfutil@firstofone(#1)\relax
  \iftikz@shapeborder
    \edef\qrr@shape{\csname pgf@sh@ns@\tikz@pp@name{\tikz@shapeborder@name}\endcsname}%
    \ifx\qrr@tikz@circle\qrr@shape
      % ah circle, get the radius!
      \begingroup
        \csname pgf@sh@np@\tikz@pp@name{\tikz@shapeborder@name}\endcsname
        \let\qrr@radius\radius
        \pgfmath@smuggleone\qrr@radius
      \endgroup
    \fi
  \fi}
\tikzset{
  deround/.style={
    /utils/exec={%
     \qrr@getRadius\tikztostart
     \ifdim\qrr@radius=0pt
       \def\qrr@arrowsettings{-}\else
       \edef\qrr@arrowsettings{{Hug Cap[length=+\qrr@radius]}-}\fi
     \qrr@getRadius\tikztotarget
     \ifdim\qrr@radius=0pt\else
       \edef\qrr@arrowsettings{\qrr@arrowsettings{Hug Cap[length=+\qrr@radius]}}\fi
   },
   arrows/.expanded=\qrr@arrowsettings}
}
\makeatother
\begin{document}
\begin{tikzpicture}[thick]
  \node[state]          (1) {};
  \node[state] at (2,1) (2) {abcdef};
  \path[line width=12pt, every edge/.append style=deround]
    (2) edge (1)
        edge[line width=10pt, red, out=150, in=90] (1);
  \path[line width=12pt] (1) edge[out=180-30, in=180+30, looseness=4, deround] (1);
\end{tikzpicture}
\end{document}

Output

enter image description here

1
  • It is insane, but I love it :-) Apr 30, 2015 at 9:54
2

I found a way, but it does not seems to be the canonical one, just a way that works: Using layers, and then increasing the length of the line:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{automata}

\begin{document}

\pgfdeclarelayer{bg}
\pgfsetlayers{bg,main}
\begin{tikzpicture}[thick]
  \node[state,fill=white] (1) {};
  \begin{pgfonlayer}{bg}
    \draw[-,shorten >=-4pt,shorten <=-4pt,blue!20, line width=18pt]
    (1) to (0,1);
  \end{pgfonlayer}
\end{tikzpicture}

\end{document}

Result

EDIT Second solution, based on the comment below (use center as coordinate instead of longer lines)

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{automata}

\begin{document}

\pgfdeclarelayer{bg}
\pgfsetlayers{bg,main}
\begin{tikzpicture}[thick]
  \node[state,fill=white] (1) {};
  \begin{pgfonlayer}{bg}
    \draw[-,blue!20, line width=18pt]
    (1.center) to (0,1);
  \end{pgfonlayer}
\end{tikzpicture}

\end{document}
1
  • 1
    Instead of the negative shorten, you could just use (1.center) as the start now. Apr 29, 2015 at 19:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .