Лучшая подгонка линии к узлу в TiKZ

Лучшая подгонка линии к узлу в TiKZ

Я использую библиотеку автоматов TiKZ и размещаю линии между узлами толщиной 18pt. Проблема в том, что граница между линиями и состоянием не очень хорошо совпадают:

введите описание изображения здесь

Я хотел бы иметь это

введите описание изображения здесь

Есть идеи?

Вот минимальный пример создания первого варианта:

\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}

решение1

Вот попытка с наконечниками стрел.
(У меня также есть решение с украшениями, но они сбивают меня с толку больше, чем наконечники стрел.)

К сожалению, Round Capнаконечник стрелки добавляет (или удаляет с reversedопцией) только полукруг с конца линии. Это будет работать отлично, если ширина линии в два раза больше радиуса круглого узла.

Для setup codeнаконечника стрелы Hug Capвычисляетсянемного математикии сохраняет три значения:

  • радиус (берется из клавиши со стрелкой length),
  • час, это вставка (высота), и
  • угол (в конце концов, это дуга, которая рисуется).

Существует возможность расширить определение наконечника стрелки, включив такие параметры, как reversed, open, leftи т. д.

Стиль deroundвычисляет радиус из круговых узлов. (Хотя этот ответ вдохновлен стилем circle connection barи оформлением из mindmapбиблиотеки, в нем используется другой подход для извлечения радиуса из уже существующих узлов.)

Очевидно, что траектории должны быть ортогональны границе окружности.

Это решение необходимо использовать с операторами to/ (оно использует и для автоматического вычисления радиусов. Не используйте ничего, кроме ( по умолчанию), это также можно проверить внутри определения наконечника стрелки.edge\tikztostart\tikztotargetline capsbutt

Это нарушается, если ширина линии больше диаметра, а также при очень больших радиусах (но тогда зачем вообще это использовать?).

Код

\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}

Выход

введите описание изображения здесь

решение2

Я нашел способ, но он, похоже, не канонический, а просто работающий: использование слоев и последующее увеличение длины линии:

\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}

Результат

РЕДАКТИРОВАТЬ Второе решение, основанное на комментарии ниже (используйте центр в качестве координаты вместо более длинных линий)

\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}

Связанный контент