I explored multiple solution to represent a graph cycle in TikZ with arbitrary nodes distributed around a circle and joined by arrows like the example below.
What worked for me was to place nodes on a chain using polar coordinates and then call a macro I wrote (\arcarrow
) to draw an joining nodes. \arcarrow
takes 4 arguments : an optional style, two nodes, and the center of the arc.
It uses the intersections
library to compute pseudo-anchor coordinates for the arrows.
Here is the MWE :
\documentclass[
]{article}
\usepackage{tikz}
\usetikzlibrary{intersections, chains, scopes, through, calc, shapes.geometric}
% \arcarrow
% draw an arc bewteen two nodes with a given center
% TODO : find a way to intersect with nodes without prior setting of a "name path global"
\newcommand\arcarrow[4][solid]{
% #1 style; optional, default = solid
% #2 start node
% #3 end node
% #4 center
% don't really need a node here but "circle through" does
\node [draw = none, name path = hcirc] at (#4) [circle through=(#2)] {};
% find intersection of nodes with help circle
\foreach \n in {#2,#3}
\path [name intersections={of = hcirc and \n,sort by = hcirc}]
(intersection-1) coordinate (\n-1)
%[fill=red, opacity=0.5] circle (2pt) [above left] node {\n-1} % display point
(intersection-2) coordinate (\n-2)
%[fill=red, opacity=0.5] circle (2pt) [above left] node {\n-2} % display point
;
%calculate polar angles
\pgfmathanglebetweenpoints{\pgfpointanchor{#4}{center}}{%
\pgfpointanchor{#2-1}{center}}
\let\aan\pgfmathresult
\pgfmathanglebetweenpoints{\pgfpointanchor{#4}{center}}{%
\pgfpointanchor{#2-2}{center}}
\let\ban\pgfmathresult
\pgfmathanglebetweenpoints{\pgfpointanchor{#4}{center}}{%
\pgfpointanchor{#3-1}{center}}
\let\can\pgfmathresult
\pgfmathanglebetweenpoints{\pgfpointanchor{#4}{center}}{%
\pgfpointanchor{#3-2}{center}}
\let\dan\pgfmathresult
% Find the starting point and the angles
% This is tricky when the node center is around O°
\pgfmathparse{(\ban-\aan)<180 ? 1 : 2}
\edef\startpoint{#2-\pgfmathresult}
\pgfmathparse{(\ban-\aan)<180 ? \aan : \ban}
\let\startan\pgfmathresult
\pgfmathparse{(\dan-\can)<180 ? \dan : \can}
\let\endan\pgfmathresult
% Draw the arrow
% we have to compute radius with \p1 and \n1 (see https://tikz.dev/tutorial-Euclid#autosec-263)
\draw [#1] (\startpoint) let
\p1 = ($ (#2) - (#4) $),
\n1 = {veclen(\x1,\y1)}
in
arc (\startan:\endan:\n1);
}
\begin{document}
\begin{figure}\centering\begin{tikzpicture}[node distance=2cm]
\tikzstyle{mynode} = [align=center, draw=black]
\tikzstyle{arrow} = [thick,->,>=latex]
\def \orig {90} % so we can change the origin without other things
\def \radius {4cm}
\coordinate (I) at (0,0); %TODO:use this center for polar coordinate so we can translate the whole diagram
{ [start chain=loop placed {at=(\orig-\tikzchaincount*90+90:\radius)}]
% draw nodes on chain
\node foreach \s in {circle, isosceles triangle, rectangle, trapezium}
[mynode, \s, on chain, name path global = loop-\tikzchaincount] {\s};
% draw arrows between nodes
% TODO : find a more TikZ and less LaTeX way to do this (i.e. redefine a chain join) if possible
\arcarrow [arrow] {loop-1} {loop-2} {I}
\arcarrow [arrow] {loop-2} {loop-3} {I}
\arcarrow [arrow] {loop-3} {loop-4} {I}
\arcarrow [arrow, dashed] {loop-4} {loop-1} {I}
}
\end{tikzpicture}\caption{The most robust and pretty cycle created by Ti\textit{k}Z i.e. \LaTeX, at the expense of using intersections package and doing angles calculation.}\end{figure}
\end{document}
I am quite happy with it except for two things that bugs me.
- Since the
intersections
library works with paths, not nodes, I must name the nodes paths in a way I can retrieve them from within the macro definition. - Even worst it has to be "global" (
name path global
).
Though I am looking for another way to access the nodes' paths from within the \arcarrow
macro.
The best solution would be to be able to set name path
on the existing nodes passed to the macro. Is it possible ?
The second solution would be to assign an arbitrary name path
, preferably not global, and preferably automaticaly (maybe with some \pgfkeys
and some .style
/.code
stuff). Plus there would be a way to access this name path
from within the macro.
I don't know the underlying of TikZ so it may not makes sense and I may be stuck with my name path global
.
fill=white
. Your solution also ignores the line width. The angular border will lie on the outside of the line. The intersection will be calculated with the middle of the line. Though, this will probably be lost to TikZ imprecisions anyway as long as you use sensible line widths.