13

I know, that this question was asked more than year ago (see here), but given answer is limited to callouts without border line. Is there a way to improve there given answer with callout shape with uniform fill or shadings, can be used transparency and line border? Something like-call outs as can bee seen in comics ...

If it possible to design a new kind of callout, for example with following features defined by options:

shape= <rectangle multipointer callout> or <ellipse multipointer callout> 
number of pointers= <number>
pointer one= <absolute coordinate> or <relative coordinate>
pointer two= <absolute coordinate> or <relative coordinate>
...
pointer origin offset= <x-offset,y-offset>  % not necessary but usefull
other existed options of callout shapes

I think it would be very useful not just for me. One of examples of my use of multi-pointer callouts:

\documentclass[12pt,tikz,border=3mm]{standalone}
    \usetikzlibrary{arrows.meta,calc,chains,positioning,shadows,shapes.callouts}
%---------------------------------------------------------------%
    \begin{document}
%%%% SA-multipointer-callout
\begin{tikzpicture}[
    node distance=5mm and 44mm,
    start chain = going right,
           C/.style = {callout absolute pointer={#1},
                       rectangle callout,
                       callout pointer width=1.8 mm, callout pointer shorten=-1mm,
                       rounded corners, draw, fill=yellow!30,
                       semitransparent, text opacity=1,
                       font=\footnotesize, text=teal!40!black, align=center},
           L/.style = {line width=1mm, gray,
                       arrows={-Stealth[inset=0pt,length=0pt 2,angle'=90]},
                       text=black},
    N/.style 2 args = {name=n#1,
                       text width=#2,
                       draw, fill=white, minimum height=9mm, align=center, on chain, drop shadow}
                    ]\sffamily
\node[N={1}{21mm}]                  {oddajnik};
\node[name=n2,circle,inner sep=2pt,
      fill=white,draw,on chain]     {$+$};
\node[N={3}{21mm}]                  {sprejemnik};
\draw[L]    (n1) edge node[above,pin=above left:signal]    {$y(t)$}
                      node[below,black,pin=below left:sporo\v{c}ilo] {$y[n]$}    (n2)
            (n2) edge node[above] {$r(t)=y(t)+ n(t)$}
                      node[below] {$r[n]=y[n]+ z[n]$}
            (n3);
\draw[->]   ($(n2.north)+(0,1.1)$)
            node[right,align=left,black] {motnje:  $n(t)$ ali $z[n]$}  -- (n2);
\node[C={([xshift=-7mm] n2.west)},below = of n2]
          {komunikacijski\\ kanal};
\node[C={([xshift=+7mm] n2.east)},below = of n2]
          {komunikacijski\\ kanal};
\end{tikzpicture}
    \end{document}

So far I stacks two callout shapes, but result is quit ugly:

enter image description here

Addendum: After two and half year Symbol 1 provide answer which partly fulfill my expectation. However, I still have a dream, that one day the TikZ Library shapes.callouts will enriched with multi-pointer-callout shape ... with help of answers on my question :) which can be used concerning my example used as:

\node[ellipse multipointer callout, 
      absolute pointer one=n2.wet, absolute pointer two=n2.east,
      callout pointer width=1.8 mm, callout pointer shorten=-1mm,
      % other, not specific but important options
      draw, fill=yellow!30, semitransparent, text opacity=1, % <-- key features
      font=\footnotesize, text=teal!40!black, align=center] {komunikacijski\\ kanal};
  • Something like draw,black,linewidth=.8pt, postaction={fill,white} will work. – Symbol 1 Apr 1 '17 at 2:20
16
+50

New answer

We start from constructing a easy way to typeset multiple nodes at the same position:

\documentclass[border=9,tikz]{standalone}
    \usetikzlibrary{shapes.callouts}
\begin{document}

% The goal is to translate
%    \overlaynode<red,blue>{hallo};
% into
%    \node[red]{hallo};
%    \node[blue]{hallo};
\makeatletter
\def\overlaynode<#1>#2;{
        \gdef\stacknodecommonpart{#2}
        \pgfkeys{/typeset node/.list={#1}}
        % we are lazy
        % pgfkeys will translate
        %    \pgfkeys{/typeset node/.list={red,blue}}
        % into
        %    \pgfkeys{/typeset node=red}
        %    \pgfkeys{/typeset node=blue}
}
\pgfkeys{
    /typeset node/.code={
        \edef\pgf@marshal{\noexpand\node[#1]\stacknodecommonpart;}
        \pgf@marshal
    }
}
\tikz{
    \overlaynode<red,{blue,xshift=1}>{{Hello}}; % notice the nested {{}}
%   \overlaynode<red,{blue,xshift=1}>[]{Hello}; % workaround
    \overlaynode<red,{blue,rotate=5}>at(1,0)[draw]{from};
    \overlaynode<red,{blue,scale=1.1}>[circle]at(2,0)[draw]{the};
    \overlaynode<red,{blue,opacity=.5}>[fill=yellow!50]at(3,0){other};
    \overlaynode<{red,rectangle callout,fill},{blue,ellipse callout,draw}>at(4,0){side};
}

  • Each item in < > will lead to an individual node. In the example above, red gives you a red node and {blue,rotate=5} gives you a blue, rotated node.
  • you can set global property in [ ]. In the example above, [draw] will draw the borders of all nodes. Hence you can see both the red rectangle and the blue, rotated rectangle.

So one can do multiple callout without pain: since each callout is itself a node, there will be no problem combining absolute and relative and shorten.

\tikz{
    \overlaynode<
            {fill=red,callout absolute pointer=(45:2)},
            {fill=yellow,callout absolute pointer=(135:2),callout pointer shorten=1cm},
            {fill=green,callout relative pointer=(-135:2),callout pointer width=.5cm},
            {fill=blue,callout relative pointer=(-45:2),callout pointer shorten=1cm,callout pointer width=.5cm}
        >
        [rectangle callout,text=white]
        at(0,0){can you}
    ;
}

Now we come to the exciting part.

To depict the contour of all nodes, we need to call \overlaynode twice: once with draw and once with fill.

\def\overlaynodedrawfill{\pgfutil@ifnextchar[{\overlaynodedrawfill@opt}{\overlaynodedrawfill@opt[]}}
\def\overlaynodedrawfill@opt[#1]<#2>#3;{
    \begin{scope}[transparency group,draw=black,fill=white,line cap=round,line join=round,#1]
        \pgfmathsetmacro\pgflinewidthdouble{2\pgflinewidth}
        \overlaynode<#2>[draw=pgfstrokecolor,line width=\pgflinewidthdouble]#3;
        \overlaynode<#2>[fill=pgffillcolor]#3;
    \end{scope}
}
\tikz{
    \overlaynodedrawfill[draw=cyan,fill=magenta,opacity=.5]<
            {callout absolute pointer=(45:2)},
            {callout absolute pointer=(135:2),callout pointer shorten=1cm},
            {callout relative pointer=(-135:2),callout pointer width=.5cm},
            {callout relative pointer=(-45:2),callout pointer shorten=1cm,callout pointer width=.5cm}
        >
        [rectangle callout]
        at(0,0){hear me}
    ;
}

Notice that there is opacity=.5, but it looks like just one node!

ALso notice that we put draw=cyan,fill=magenta,opacity=.5 at the beginning and put rectangle callout at the end.

If you put draw=cyan at the end, then all nodes will be drawn and the result will not be the contour. (However, you still can do so if you know what are you doing.)

Even more tricks

\tikz{
    \overlaynodedrawfill<
            {cloud callout,callout absolute pointer=(90:2),inner sep=-20},
            {rectangle callout,callout absolute pointer=(210:2),minimum height=30},
            {ellipse callout,callout absolute  pointer=(-30:2)}
        >
        at(0,0){nice to meet you}
    ;
}

\tikz{
    \overlaynodedrawfill<
            {starburst},
            {cloud,inner sep=-10}
        >
        at(0,0){where you been}
    ;
}

\usetikzlibrary{shapes.arrows}
\tikz{
    \overlaynodedrawfill[arrow box,text opacity=0,minimum height=50,,minimum width=50,inner xsep=-10]<
            {rotate=22.5},
            {rotate=45},
            {rotate=67.5},
            {text opacity=1}
        >
        at(0,0){I can}
    ;
}

Full code

\documentclass[border=9,tikz]{standalone}
    \usetikzlibrary{shapes.callouts}
\begin{document}

% The goal is to translate
%    \overlaynode<red,blue>{hallo};
% into
%    \node[red]{hallo};
%    \node[blue]{hallo};
\makeatletter
\def\overlaynode<#1>#2;{
        \gdef\stacknodecommonpart{#2}
        \pgfkeys{/typeset node/.list={#1}}
        % we are lazy
        % pgfkeys will translate
        %    \pgfkeys{/typeset node/.list={red,blue}}
        % into
        %    \pgfkeys{/typeset node=red}
        %    \pgfkeys{/typeset node=blue}
}
\pgfkeys{
    /typeset node/.code={
        \edef\pgf@marshal{\noexpand\node[#1]\stacknodecommonpart;}
        \pgf@marshal
    }
}
\tikz{
    \overlaynode<red,{blue,xshift=1}>{{Hello}}; % notice the nested {{}}
%   \overlaynode<red,{blue,xshift=1}>[]{Hello}; % workaround
    \overlaynode<red,{blue,rotate=5}>at(1,0)[draw]{from};
    \overlaynode<red,{blue,scale=1.1}>[circle]at(2,0)[draw]{the};
    \overlaynode<red,{blue,opacity=.5}>[fill=yellow!50]at(3,0){other};
    \overlaynode<{red,rectangle callout,fill},{blue,ellipse callout,draw}>at(4,0){side};
}
\tikz{
    \overlaynode<
            {fill=red,callout absolute pointer=(45:2)},
            {fill=yellow,callout absolute pointer=(135:2),callout pointer shorten=1cm},
            {fill=green,callout relative pointer=(-135:2),callout pointer width=.5cm},
            {fill=blue,callout relative pointer=(-45:2),callout pointer shorten=1cm,callout pointer width=.5cm}
        >
        [rectangle callout,text=white]
        at(0,0){can you}
    ;
}

\def\overlaynodedrawfill{\pgfutil@ifnextchar[{\overlaynodedrawfill@opt}{\overlaynodedrawfill@opt[]}}
\def\overlaynodedrawfill@opt[#1]<#2>#3;{
    \begin{scope}[transparency group,draw=black,fill=white,line cap=round,line join=round,#1]
        \pgfmathsetmacro\pgflinewidthdouble{2\pgflinewidth}
        \overlaynode<#2>[draw=pgfstrokecolor,line width=\pgflinewidthdouble]#3;
        \overlaynode<#2>[fill=pgffillcolor]#3;
    \end{scope}
}
\tikz{
    \overlaynodedrawfill[draw=magenta,fill=cyan,opacity=.5]<
            {callout absolute pointer=(45:2)},
            {callout absolute pointer=(135:2),callout pointer shorten=1cm},
            {callout relative pointer=(-135:2),callout pointer width=.5cm},
            {callout relative pointer=(-45:2),callout pointer shorten=1cm,callout pointer width=.5cm}
        >
        [rectangle callout]
        at(0,0){hear me}
    ;
}

\tikz{
    \overlaynodedrawfill<
            {cloud callout,callout absolute pointer=(90:2),inner sep=-20},
            {rectangle callout,callout absolute pointer=(210:2),minimum height=30},
            {ellipse callout,callout absolute  pointer=(-30:2)}
        >
        at(0,0){nice to meet you}
    ;
}

\tikz{
    \overlaynodedrawfill<
            {starburst},
            {cloud,inner sep=-10}
        >
        at(0,0){where you been}
    ;
}

\usetikzlibrary{shapes.arrows}
\tikz{
    \overlaynodedrawfill[arrow box,text opacity=0,minimum height=50,minimum width=50,inner xsep=-10]<
            {rotate=22.5},
            {rotate=45},
            {rotate=67.5},
            {text opacity=1}
        >
        at(0,0){I can}
    ;
}

\end{document}

Old answer

THe idea is [draw,black,line width=.8pt,postaction={fill,white}].

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{automata,shapes,arrows}
\makeatletter
\tikzset{
    expand me/.style={#1},
    multiple absolute pointers/.style args={#1[#2]#3#4}{
        insert path={
            \foreach \qrr@tikz@calloutabsolutepointer in {#3} {
                \pgfextra
                    \expandafter\pgfutil@ifnextchar\expandafter[%
                    \expandafter\qrr@tikz@parse@calloutabsolutepointer\expandafter{%
                    \expandafter\qrr@tikz@parse@calloutabsolutepointer\expandafter[\expandafter]\expandafter}\qrr@tikz@calloutabsolutepointer\@qrr@tikz@parse@calloutabsolutepointer
                \endpgfextra
                node[#2, shape/.expanded=\tikz@shape\space callout, expand me/.expanded=\qrr@tikz@calloutabsolutepointer@options, callout absolute pointer={(\qrr@tikz@calloutabsolutepointer@)}] {#4}
            }
            \pgfextra
                \def\pgf@tempa{#1}
                \pgfutil@in@*{#1}
                \ifpgfutil@in@\else
                    \pgfkeysalso{insert path={node[#2] {#4}}}
                \fi
            \endpgfextra}}}

\def\qrr@tikz@parse@calloutabsolutepointer[#1]#2\@qrr@tikz@parse@calloutabsolutepointer{%
    \gdef\qrr@tikz@calloutabsolutepointer@options{#1}%
    \gdef\qrr@tikz@calloutabsolutepointer@{#2}%
}
\makeatother
\begin{document}
\begin{tikzpicture}[node distance=2cm, auto]
    \node[state,initial]     (s0)                            {$s_0$};
    \node[state]                     (s1) [right of=s0] {$s_1$};
    \node[state,accepting] (s2) [right of=s1] {$s_2$};
    \path[->]
        (s0) edge node {a} (s1)
        (s1) edge node {c} (s2)
                 edge [loop above] node {b} ();
    \path[multiple absolute pointers={
        [fill=green!20, text width=2.1cm, below of=s0]
        {
            {[draw,line width=.8pt]s1.south},%
            {[draw,line width=.8pt]s2.south},%
            {[draw,line width=.8pt]s0.south},%
            [fill=magenta]s1.south,%
            [fill=yellow]s2.south,%
            [fill=cyan]s0.south%
        }
        {Each circle is a \emph{state} of the automaton}
    }];
\end{tikzpicture}
\end{document}

  • huh, almost three year old question ... thank you for your answer. it very complex and is (only) near to perfect. Unfortunately it work only for rectangle shapes. Also use of transparency shows structure of composed images, what is not desired. If I wait for an answer so long, I will still wait for while, if maybe someone design such multi-pointer call out shape. – Zarko Apr 1 '17 at 16:56
  • @Zarko You need a transparency group for the transparency issue. See the manual's examples with symbolic shape construction. – cfr Apr 1 '17 at 20:44
  • amazing! for edits in your answer I need time to come through ... today evening (GMT) now is time to sleep :) – Zarko Apr 5 '17 at 1:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.