3

For my mindmap, I would like to replace the "volcano" at the end of the connection with an arrow.

Since the connection is supposed to be curved, I use the approach from this answer. I have tried a lot but my knowledge in tikz/pgf is unfortunately not sufficient to get the arrow drawn correctly. I thought the arrow was best defined where the definition of the second volcano originally was. At this point I have left an associated comment in the code.

Here is what I have so far:

\documentclass[tikz,border=0pt]{standalone}
\usetikzlibrary{positioning, mindmap}

\makeatletter \newdimen\pgfutil@tempdimc

\pgfdeclaredecoration{circle connection boomerang}{initial} { \state{initial}[width=0pt,next state=boomerang] { { \pgftransformxshift{-\pgfkeysvalueof{/pgf/decoration/start radius}}% \pgftransformrotate{\pgfdecorationsegmentaspect}%%% \pgfpathmoveto{\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} \pgfpatharc{\pgfdecorationsegmentangle}{-\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}} \pgfutil@tempcnta=-\pgfdecorationsegmentangle\relax \advance\pgfutil@tempcnta by90\relax \pgfmathsetlength\pgfutil@tempdima{\pgfkeysvalueof{/pgf/decoration/start radius}} \pgfmathsetlength\pgfutil@tempdimb{\pgfdecorationsegmentamplitude} \pgfpathcurveto {\pgfpointadd {\pgfpointpolar{-\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} {\pgfpointpolar{\the\pgfutil@tempcnta}{.25\pgfutil@tempdima}}} {\pgfqpoint{1.25\pgfutil@tempdima}{-.5\pgfutil@tempdimb}} {\pgfqpoint{1.5\pgfutil@tempdima}{-.5\pgfutil@tempdimb}} \pgfpathlineto{\pgfpoint{1.5\pgfutil@tempdima}{.5\pgfutil@tempdimb}} \pgfutil@tempcnta=\pgfdecorationsegmentangle\relax \advance\pgfutil@tempcnta by-90\relax \pgfpathcurveto {\pgfpoint{1.25\pgfutil@tempdima}{.5\pgfutil@tempdimb}} {\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} {\pgfpointpolar{\the\pgfutil@tempcnta}{.25\pgfutil@tempdima}}} {\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} \pgfpathclose } } \state{boomerang}[width=0pt,next state=end] { \pgfmathsetlength\pgfutil@tempdima{\pgfkeysvalueof{/pgf/decoration/start radius}}% \pgfmathsetlength\pgfutil@tempdimb{\pgfkeysvalueof{/pgf/decoration/end radius}}% \pgfmathsetlength\pgfutil@tempdimc{\pgfdecorationsegmentamplitude}% \pgftransformxshift{-\pgfutil@tempdima} \pgfpathmoveto {\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentaspect}{1.5\pgfutil@tempdima}} {\pgfpointpolar{\pgfdecorationsegmentaspect-90}{.5\pgfutil@tempdimc}}} \pgftransformxshift{\pgfdecoratedremainingdistance+\pgfutil@tempdima+\pgfutil@tempdimb} \pgfpathquadraticcurveto {\pgfpoint{-.5\pgfdecoratedremainingdistance-\pgfutil@tempdimb}{(.5\pgfdecoratedremainingdistance+.5\pgfutil@tempdima+.5\pgfutil@tempdimb)tan(\pgfdecorationsegmentaspect)-.5\pgfutil@tempdimc}} {\pgfpointadd {\pgfpointpolar{-\pgfdecorationsegmentaspect}{-1.5\pgfutil@tempdimb}} {\pgfpointpolar{-\pgfdecorationsegmentaspect-90}{.5\pgfutil@tempdimc}}} \pgfpathlineto{\pgfpointadd {\pgfpointpolar{-\pgfdecorationsegmentaspect}{-1.5\pgfutil@tempdimb}} {\pgfpointpolar{-\pgfdecorationsegmentaspect+90}{.5\pgfutil@tempdimc}}} \pgftransformxshift{-\pgfdecoratedremainingdistance-\pgfutil@tempdima-\pgfutil@tempdimb} \pgfpathquadraticcurveto {\pgfpoint{.5\pgfdecoratedremainingdistance+\pgfutil@tempdima}{(.5\pgfdecoratedremainingdistance+.5\pgfutil@tempdima+.5\pgfutil@tempdimb)tan(\pgfdecorationsegmentaspect)+.5\pgfutil@tempdimc}} {\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentaspect}{1.5\pgfutil@tempdima}} {\pgfpointpolar{\pgfdecorationsegmentaspect+90}{.5\pgfutil@tempdimc}}} \pgfpathclose } \state{end}[width=0pt,next state=final] { { % the arrow should be defined here % % original code producing the "volcano" % \pgftransformxshift{\pgfdecoratedremainingdistance}% % \pgftransformxscale{-1}% % \pgftransformxshift{-\pgfkeysvalueof{/pgf/decoration/end radius}}% % \pgftransformrotate{\pgfdecorationsegmentaspect}%%% % \pgfpathmoveto{\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/end radius}}} % \pgfpatharc{\pgfdecorationsegmentangle}{-\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/end radius}} % \pgfutil@tempcnta=-\pgfdecorationsegmentangle\relax % \advance\pgfutil@tempcnta by90\relax % \pgfmathsetlength\pgfutil@tempdima{\pgfkeysvalueof{/pgf/decoration/end radius}} % \pgfmathsetlength\pgfutil@tempdimb{\pgfdecorationsegmentamplitude}% % \pgfpathcurveto % {\pgfpointadd % {\pgfpointpolar{-\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/end radius}}} % {\pgfpointpolar{\the\pgfutil@tempcnta}{.25\pgfutil@tempdima}}} % {\pgfqpoint{1.25\pgfutil@tempdima}{-.5\pgfutil@tempdimb}} % {\pgfqpoint{1.5\pgfutil@tempdima}{-.5\pgfutil@tempdimb}} % \pgfpathlineto{\pgfpoint{1.5\pgfutil@tempdima}{.5\pgfutil@tempdimb}} % \pgfutil@tempcnta=\pgfdecorationsegmentangle\relax % \advance\pgfutil@tempcnta by-90\relax % \pgfpathcurveto % {\pgfpoint{1.25\pgfutil@tempdima}{.5\pgfutil@tempdimb}} % {\pgfpointadd % {\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/end radius}}} % {\pgfpointpolar{\the\pgfutil@tempcnta}{.25\pgfutil@tempdima}}} % {\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/end radius}}} % \pgfpathclose } } \state{final} {} } \tikzoption{circle connection boomerang switch color}{\tikz@parse@switch@boomerang#1\pgf@unique} \def\tikz@parse@switch@boomerang from (#1) to (#2) by #3\pgf@unique{% \tikzset{to path={% \pgfextra{% \tikz@lib@mindmap@check{\tikztostart}% \tikz@compute@circle@radii\tikz@compute@segmentamplitude% \global\let\tikz@lib@save@start=\tikztostart% \global\let\tikz@lib@save@target=\tikztotarget% \pgfkeysgetvalue{/pgf/decoration/start radius}\pgf@temp% \global\let\tikz@lib@saved@start@radius=\pgf@temp% \pgfkeysgetvalue{/pgf/decoration/end radius}\pgf@temp% \global\let\tikz@lib@saved@end@radius=\pgf@temp% }% [every circle connection bar] decorate [decoration={circle connection boomerang,aspect=#3}] { -- (\tikztotarget) \tikztonodes} }, append after command={ [fill=none,draw=none,path picture=\tikz@lib@shade@pic] \pgfextra{ \pgfutil@colorlet{tikz@switch@from}{#1}% \pgfutil@colorlet{tikz@switch@to}{#2}% } } } }

\pgfdeclarehorizontalshading[tikz@switch@from,tikz@switch@to]{tikz@shade@bar}{10000bp}{% color(0pt)=(tikz@switch@from); color(100bp)=(tikz@switch@to)}

\tikzset{% concept color and bend/.code 2 args= { \let\tikz@old@concept@color=\tikz@concept@color% \def\tikz@edge@to@parent@path{ (\tikzparentnode) to[circle connection boomerang switch color=from (\tikz@old@concept@color) to (#1) by #2] (\tikzchildnode)} \def\tikz@concept@color{#1}% } }

\begin{document} \begin{tikzpicture} \node[draw, circle, red] (n) {$n$}; \node[draw, circle] (m) [below = of n] {$m$}; \draw (n) to[circle connection boomerang switch color=from (red) to (black) by -5] (m); \end{tikzpicture} \end{document}

Producing

enter image description here

And this is how the result should look like:

enter image description here

Nico
  • 1,694

1 Answers1

3

I probably overlooked something, but this seems to look quite right (tested for different angles):

\documentclass[tikz,border=0pt]{standalone}
\usetikzlibrary{positioning, mindmap}

\makeatletter \newdimen\pgfutil@tempdimc

\pgfdeclaredecoration{circle connection boomerang}{initial} { \state{initial}[width=0pt,next state=boomerang] { { \pgftransformxshift{-\pgfkeysvalueof{/pgf/decoration/start radius}}% \pgftransformrotate{\pgfdecorationsegmentaspect}%%% \pgfpathmoveto{\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} \pgfpatharc{\pgfdecorationsegmentangle}{-\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}} \pgfutil@tempcnta=-\pgfdecorationsegmentangle\relax \advance\pgfutil@tempcnta by90\relax \pgfmathsetlength\pgfutil@tempdima{\pgfkeysvalueof{/pgf/decoration/start radius}} \pgfmathsetlength\pgfutil@tempdimb{\pgfdecorationsegmentamplitude} \pgfpathcurveto {\pgfpointadd {\pgfpointpolar{-\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} {\pgfpointpolar{\the\pgfutil@tempcnta}{.25\pgfutil@tempdima}}} {\pgfqpoint{1.25\pgfutil@tempdima}{-.5\pgfutil@tempdimb}} {\pgfqpoint{1.5\pgfutil@tempdima}{-.5\pgfutil@tempdimb}} \pgfpathlineto{\pgfpoint{1.5\pgfutil@tempdima}{.5\pgfutil@tempdimb}} \pgfutil@tempcnta=\pgfdecorationsegmentangle\relax \advance\pgfutil@tempcnta by-90\relax \pgfpathcurveto {\pgfpoint{1.25\pgfutil@tempdima}{.5\pgfutil@tempdimb}} {\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} {\pgfpointpolar{\the\pgfutil@tempcnta}{.25\pgfutil@tempdima}}} {\pgfpointpolar{\pgfdecorationsegmentangle}{\pgfkeysvalueof{/pgf/decoration/start radius}}} \pgfpathclose } } \state{boomerang}[width=0pt,next state=end] { \pgfmathsetlength\pgfutil@tempdima{\pgfkeysvalueof{/pgf/decoration/start radius}}% \pgfmathsetlength\pgfutil@tempdimb{10pt}% % <-- changeable \pgfmathsetlength\pgfutil@tempdimc{\pgfdecorationsegmentamplitude}% \pgftransformxshift{-\pgfutil@tempdima} \pgfpathmoveto {\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentaspect}{1.5\pgfutil@tempdima}} {\pgfpointpolar{\pgfdecorationsegmentaspect-90}{.5\pgfutil@tempdimc}}} \pgftransformxshift{\pgfdecoratedremainingdistance+\pgfutil@tempdima+\pgfutil@tempdimb} \pgfpathquadraticcurveto {\pgfpoint{-.5\pgfdecoratedremainingdistance-\pgfutil@tempdimb}{(.5\pgfdecoratedremainingdistance+.5\pgfutil@tempdima+.5\pgfutil@tempdimb)tan(\pgfdecorationsegmentaspect)-.5\pgfutil@tempdimc}} {\pgfpointadd {\pgfpointpolar{-\pgfdecorationsegmentaspect}{-1.5\pgfutil@tempdimb}} {\pgfpointpolar{-\pgfdecorationsegmentaspect-90}{.5\pgfutil@tempdimc}}} \pgfpathlineto{\pgfpointadd {\pgfpointpolar{-\pgfdecorationsegmentaspect}{-1.5\pgfutil@tempdimb}} {\pgfpointpolar{-\pgfdecorationsegmentaspect+90}{.5\pgfutil@tempdimc}}} \pgftransformxshift{-\pgfdecoratedremainingdistance-\pgfutil@tempdima-\pgfutil@tempdimb} \pgfpathquadraticcurveto {\pgfpoint{.5\pgfdecoratedremainingdistance+\pgfutil@tempdima}{(.5\pgfdecoratedremainingdistance+.5\pgfutil@tempdima+.5\pgfutil@tempdimb)tan(\pgfdecorationsegmentaspect)+.5\pgfutil@tempdimc}} {\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentaspect}{1.5\pgfutil@tempdima}} {\pgfpointpolar{\pgfdecorationsegmentaspect+90}{.5\pgfutil@tempdimc}}} \pgfpathclose } \state{end}[width=0pt,next state=final] { { \pgftransformxshift{\pgfdecoratedremainingdistance}% \pgftransformxscale{-1} \pgfmathsetlength\pgfutil@tempdima{10pt}% \pgftransformxshift{-\pgfutil@tempdima}% \pgfpathmoveto{\pgfpointpolar{\pgfdecorationsegmentaspect}{\pgfutil@tempdima}} \pgfpathlineto{\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentaspect-90}{-2pt}} % <-- changeable {\pgfpointpolar{\pgfdecorationsegmentaspect}{1.5\pgfutil@tempdima}}} \pgfpathlineto{\pgfpointadd {\pgfpointpolar{\pgfdecorationsegmentaspect-90}{2pt}} % <-- changeable {\pgfpointpolar{\pgfdecorationsegmentaspect}{1.5\pgfutil@tempdima}}} \pgfpathclose } } \state{final} {} } \tikzoption{circle connection boomerang switch color}{\tikz@parse@switch@boomerang#1\pgf@unique} \def\tikz@parse@switch@boomerang from (#1) to (#2) by #3\pgf@unique{% \tikzset{to path={% \pgfextra{% \tikz@lib@mindmap@check{\tikztostart}% \tikz@compute@circle@radii\tikz@compute@segmentamplitude% \global\let\tikz@lib@save@start=\tikztostart% \global\let\tikz@lib@save@target=\tikztotarget% \pgfkeysgetvalue{/pgf/decoration/start radius}\pgf@temp% \global\let\tikz@lib@saved@start@radius=\pgf@temp% \pgfkeysgetvalue{/pgf/decoration/end radius}\pgf@temp% \global\let\tikz@lib@saved@end@radius=\pgf@temp% }% [every circle connection bar] decorate [decoration={circle connection boomerang,aspect=#3}] { -- (\tikztotarget) \tikztonodes} }, append after command={ [fill=none,draw=none,path picture=\tikz@lib@shade@pic] \pgfextra{ \pgfutil@colorlet{tikz@switch@from}{#1}% \pgfutil@colorlet{tikz@switch@to}{#2}% } } } }

\pgfdeclarehorizontalshading[tikz@switch@from,tikz@switch@to]{tikz@shade@bar}{10000bp}{% color(0pt)=(tikz@switch@from); color(100bp)=(tikz@switch@to)}

\tikzset{% concept color and bend/.code 2 args= { \let\tikz@old@concept@color=\tikz@concept@color% \def\tikz@edge@to@parent@path{ (\tikzparentnode) to[circle connection boomerang switch color=from (\tikz@old@concept@color) to (#1) by #2] (\tikzchildnode)} \def\tikz@concept@color{#1}% } }

\begin{document} \begin{tikzpicture} \node[draw, circle, red] (n) {$n$}; \node[draw, circle] (m) [below = of n] {$m$}; \draw (n) to[circle connection boomerang switch color=from (red) to (black) by 20] (m); \end{tikzpicture} \end{document}

enter image description here

Note that I needed to change one line in the boomerang state (the line where the first changeable annotation stands) to make the arrow fixed size, regardless of the size of the node it points to.

As for now, the size of the arrow is hard coded in the style, which is probably not optimal. You can adapt the size and width of the arrow by changing the values 10pt, -2pt and 2pt in the lines marked with the changeable annotations.

Note also that there seems to be a bug in the original code from the answer on which this code bases: If you set the angle to a higher value, such as 50, the gradient will not fill the whole arrow anymore and some parts will become transparent and therefore invisible. Sadly, I haven't found a solution to this problem.

  • First of all, thank you very much, this already looks very good. However, two things are still not quite ideal, especially for the transfer to my actual problem. You have already addressed the first point: Could you think of a way to adapt the size of the arrow or even take the arrowhead from the tikzlibraries arrows or arrows.meta? And second: If I set the angle to a higher value (e.g. 50) both the beginning and the end of the arrow are not displayed correctly anymore and are overlaid with a white rectangle. – Nico Nov 23 '21 at 15:49
  • You are right, something's clipping the arrow head off. But, guess what, this is also the case for the code that we use as basis. Try to put 50 as angle and you see the very same effect. I guess this is because the gradient is not large enough to cover the whole curved line or something like that. – Jasper Habicht Nov 23 '21 at 18:35
  • As for the arrows: I don't know how to place an arrowhead here, but you can adapt the size of the arrow by changing the last values in the three lines marked by changeable in the code. – Jasper Habicht Nov 23 '21 at 18:48
  • @JasperHabicht what a classy answer ! – JeT Nov 23 '21 at 21:39
  • @JasperHabicht Yes I noticed this in the original code as well. I'll accept your answer as it answers the question posed. Thanks again. – Nico Nov 24 '21 at 08:44