5

Is there a way that the pointers from the callouts use the shortest possible way to the nodes? So this is somehow an optimization problem ;-) This is my code. The callout part is copied from here.

    \documentclass[border=10mm]{standalone}
\usepackage{tikz}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usetikzlibrary{positioning,chains, shapes.geometric,decorations.pathreplacing,calc,shadows.blur,shapes}
\tikzset{
    add path/.style = {
        decoration={show path construction,
            moveto code={
                \xdef\savedpath{\savedpath (\tikzinputsegmentfirst)}
            },
            lineto code={
                \xdef\savedpath{\savedpath -- (\tikzinputsegmentlast)}
            },
            curveto code={
                \xdef\savedpath{\savedpath .. controls (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb) ..(\tikzinputsegmentlast)}
            },
            closepath code={
                \xdef\savedpath{\savedpath -- cycle}
            }
        },
        decorate
    },
    store path/.style = {add path},
    store path/.prefix code={\xdef\savedpath{}},
    callouts/.style={
        store path,
        append after command={
            foreach \target in {#1}{
                ($(callout)!2pt!-90:\target$)--\target --($(callout)!2pt!90:\target$)
            } \savedpath
        },
        alias=callout
    },
    custom style/.style={fill=blue!20,text=},
}
\begin{document}
\begin{tikzpicture}[
[baserect/.style={text width=0.5cm,align=left,draw,},
diam/.style={diamond,draw,align=left,text width=1cm,inner sep=1.5pt,fill=yellow,text=white,minimum size=2cm},
basecirc/.style={circle,draw,align=center,text width=1.5cm},
circ/.style={basecirc,fill=blue!60,text=black}]     
\node [diam] (1) {asdf};
\node [circ, right=5 of 1] (2) {asdf};
\node [diam, below=2 of 2] (3) {asdf};
\path[custom style] (3,1) node[callouts={(1.east),(2.west),(3.north west)}]{blah blah};
\end{tikzpicture} 
\end{document}

Edit: My question is how I can optimize the distance from the callout to where the pointers touch the nodes. In the image the green line is shorter than the red one. How do I find the coordinate of the point of the node which is closest to the callout? enter image description here

ESV
  • 51
  • 2
    pgfpointshapeborder might solve part of your question. For instance the shortest path from blah to the blue circle is from blah.south east to \pgfpointshapeborder{blue circle}{blah.south east}. For shortest paths between rectangles, they are usually from corner to corner. – Symbol 1 Sep 09 '18 at 20:11

2 Answers2

3

One possibility that is not a real optimization, but can be approached is to use the fit library. This library creates a node that contains all the coordinates. This node is the smallest bounding box that contains all the coordinates. Once this is done, we can for example place the callout in the center of this bounding box.

\documentclass[border=10mm]{standalone}
\usepackage{tikz}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usetikzlibrary{positioning,chains, shapes.geometric,decorations.pathreplacing,calc,shadows.blur,shapes}
\usetikzlibrary{fit}
\tikzset{
    add path/.style = {
        decoration={show path construction,
            moveto code={
                \xdef\savedpath{\savedpath (\tikzinputsegmentfirst)}
            },
            lineto code={
                \xdef\savedpath{\savedpath -- (\tikzinputsegmentlast)}
            },
            curveto code={
                \xdef\savedpath{\savedpath .. controls (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb) ..(\tikzinputsegmentlast)}
            },
            closepath code={
                \xdef\savedpath{\savedpath -- cycle}
            }
        },
        decorate
    },
    store path/.style = {add path},
    store path/.prefix code={\xdef\savedpath{}},
    callouts/.style={
        store path,
        append after command={
            foreach \target in {#1}{
                ($(callout)!2pt!-90:\target$)--\target --($(callout)!2pt!90:\target$)
            } \savedpath
        },
        alias=callout
    },
    custom style/.style={fill=blue!20,text=},
}
\begin{document}
\begin{tikzpicture}[
[baserect/.style={text width=0.5cm,align=left,draw,},
diam/.style={diamond,draw,align=left,text width=1cm,inner sep=1.5pt,fill=yellow,text=white,minimum size=2cm},
basecirc/.style={circle,draw,align=center,text width=1.5cm},
circ/.style={basecirc,fill=blue!60,text=black}]     
\node [diam] (1) {asdf};
\node [circ, right=5 of 1] (2) {asdf};
\node [diam, below=2 of 2] (3) {asdf};
\node[draw=red, fit=(1) (2) (3)](fit) {box};
\path[custom style] (fit) node[callouts={(1.east),(2.west),(3.north west)}]{blah blah};
\end{tikzpicture} 
\end{document}

Output:

node-callout

AndréC
  • 24,137
  • +1. barycentric cs: from the pgfmanual section 13.2.2 might also be a nice option. –  Sep 09 '18 at 20:24
  • @marmot Yes, that's exactly what I was thinking. I let you write this solution which will be more beautiful because I have to go to sleep. – AndréC Sep 09 '18 at 20:28
  • No, please add it to your nice answer. There is no point in having two "competing" answers here. ;-) I will then remove my comments. –  Sep 09 '18 at 20:31
  • Thanks for your answers. Apparently my question hasn't been clear enough (I'm sorry, I find it quite difficult to describe it). I hope it's clear now what I am after. – ESV Sep 09 '18 at 20:56
0

This is only a temporary post till AndréC wakes up. You can do that rather easily using the fact that you already load the calc library, but this code redefines the syntax, i.e. you can no longer put brackets around the pointers.

\documentclass[border=10mm]{standalone}
\usepackage{tikz}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usetikzlibrary{positioning,chains, shapes.geometric,decorations.pathreplacing,calc,shadows.blur,shapes}
\tikzset{
    add path/.style = {
        decoration={show path construction,
            moveto code={
                \xdef\savedpath{\savedpath (\tikzinputsegmentfirst)}
            },
            lineto code={
                \xdef\savedpath{\savedpath -- (\tikzinputsegmentlast)}
            },
            curveto code={
                \xdef\savedpath{\savedpath .. controls (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb) ..(\tikzinputsegmentlast)}
            },
            closepath code={
                \xdef\savedpath{\savedpath -- cycle}
            }
        },
        decorate
    },
    store path/.style = {add path},
    store path/.prefix code={\xdef\savedpath{}},
    callouts/.style={
        store path,
        append after command={
            foreach \target in {#1}{
            let \p1=($(\target.center)-(callout)$),\n1={atan2(\y1,\x1)+180}
            in
                ($(callout)!2pt!-90:(\target.center)$)--(\target.\n1) --
                ($(callout)!2pt!90:(\target.center)$)
            } \savedpath
        },
        alias=callout
    },
    custom style/.style={fill=blue!20,text=},
}
\begin{document}
\begin{tikzpicture}[
[baserect/.style={text width=0.5cm,align=left,draw,},
diam/.style={diamond,draw,align=left,text width=1cm,inner sep=1.5pt,fill=yellow,text=white,minimum size=2cm},
basecirc/.style={circle,draw,align=center,text width=1.5cm},
circ/.style={basecirc,fill=blue!60,text=black}]     
\node [diam] (1) {asdf};
\node [circ, right=5 of 1] (2) {asdf};
\node [diam, below=2 of 2] (3) {asdf};
\path[custom style] (3,1) node[callouts={1,2,3}]{blah blah};
\end{tikzpicture} 
\end{document}

enter image description here

  • Thanks marmot! I would like to use for one callout normal coordinates. If I just replace the name of the nodes by coordinates it doesn't work. What do I have to do so I can use normal coordinates aswell? – ESV Sep 10 '18 at 18:20
  • @ESV In this version, you could just give the coordinates names with \coordinate (X) at .... I am actually hoping that Symbol 1 spells out an answer using pgfpointshapeborder. This is the correct way to go. That's why this is only a temporary answer, and I do not want to "steal" the answer from Symbol 1 and/or AndréC. –  Sep 10 '18 at 18:29
  • @marmot Feel free to answer this question, I don't know enough about the system layer to answer this problem. – AndréC Sep 10 '18 at 18:45
  • @AndréC & marmot Thank you both for your answers. The trick with the coordinates works fine for the moment. When I have more time I'll read the respective chapter in the manual and see if I understand it. Otherwise I'll just ask again ;-) – ESV Sep 10 '18 at 18:47