14

I would like to have callouts with multiple pointers. These callouts should have an opacity that is different from 1 and drop a shadow. Further, ideally they should come as overlays. There are two brilliant answers allowing one to created callouts with multiple pointers, callouts with multiple pointers and callout with multiple pointers, how to design it. Using the first of these posts, I can almost achieve what I want, but not quite.

Here is my MWE:

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{shapes.callouts,shadows.blur,positioning,arrows}
\makeatletter
\tikzset{ % from https://tex.stackexchange.com/a/109506/121799
  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}%
}
% from https://tex.stackexchange.com/a/361532/121799
% The goal is to translate
%    \overlaynode<red,blue>{hallo};
% into
%    \node[red]{hallo};
%    \node[blue]{hallo};
\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
    }
}

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

\makeatother
\newcommand{\tikznode}[2]{\relax
\ifmmode%
  \tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$%\tikznodestyle 
  #2$};
\else
  \tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {#2};%
\fi}
% 
\begin{document}
\begin{frame}
\frametitle{Pythagoras}
\begin{overlayarea}{\textwidth}{\textheight}
Pythagoras theorem says that\\[3cm]
\[ \tikznode{a}{a}^2+\tikznode{b}{b}^2~=~\tikznode{c}{c}^2\]
\tikz[remember picture,overlay,opacity=0.8]{
\path[multiple absolute pointers={
    [text opacity=1,fill=blue!20,align=center, above=2cm of a,xshift=-2.3cm,
    anchor=north west,rounded corners,draw=none,blur shadow]
    {[fill=blue!20]a.north,[fill=blue!20]b.north}
    {shorter edges}
  }];
}
\tikz[remember picture, overlay,opacity=0.8]{
\node[blur shadow,text opacity=1,
align=center, fill=blue!20, 
rounded corners,draw=none,rectangle callout,
anchor=pointer,callout absolute pointer={(c)},above right=2cm of c] {longer edge};}



\tikz[remember picture, overlay,opacity=0.8]{
    \overlaynodedrawfill[draw=none,fill=blue!20,opacity=.8]<
            {callout absolute pointer=(a)},
            {callout absolute pointer=(b)}
        >
        [rectangle callout,blur shadow,rounded corners]at (a)
        {where am I}
    ;
}

\tikz[remember picture,opacity=0.8]{
    \overlaynodedrawfill[fill=blue!20]<
            {callout absolute pointer=(a)},
            {callout absolute pointer=(b)},
        >
        [draw=none,rectangle callout,blur shadow,rounded corners]
        at(a){I got lost}
    ;
}

\end{overlayarea}
\end{frame}

\end{document}

enter image description here

What I am after is the appearance of the "longer edges" callout, but with multiple pointers. The "shorter edges" almost achieves this, but the shadow is drawn after the pointers, so it does not look so good (IMO).

With the second strategy, I couldn't get that far, but this might be the cleaner strategy.

  • When I use overlay, the callout gets lost, and I don't quite understand why or how one can fix that.

  • I am unable to cast a shadow.

  • The placement of the callouts does not terminate , i.e. there is a warning LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

QUESTION: Is it possible to have the same appearance as the "longer edge" callout but multiple pointers? (Ideally, this would be implemented as a single macro that takes a list of nodes the pointers should stretch to as an argument.)

11
+500

Here is some "non low level" code that creates a multi-pointer text bubble by saving first the node path and then using it in a append after command.

\documentclass[tikz,border=7pt,convert={density=1400}]{standalone}
\usetikzlibrary{decorations.pathreplacing,calc,shadows.blur,shapes}
\tikzset{
  % styles to save the path in `\savedpath`
  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{}},
  % the style to create the path
  callouts/.style={
    store path,
    append after command={
       foreach \target in {#1}{
        ($(callout)!2pt!-90:\target$)--\target --($(callout)!2pt!90:\target$)
      } \savedpath
    },
    alias=callout
  },
  % the style to display the callout
  custom style/.style={fill=blue!20,text=,blur shadow},
  % some fun cloudy node
  cloudy/.style={cloud,cloud puffs=10,cloud puff arc=120, aspect=2}
}
\begin{document}
  \begin{tikzpicture}
    \path node(test){test};
    \path[custom style] (3,1) node[cloudy,callouts={(test.east),(1,2),(1,-3)}]{blah blah};
  \end{tikzpicture}
\end{document}

enter image description here

  • 1
    I'm really impressed! This is so much simpler than the other codes (IMHO). – user121799 May 18 '18 at 20:41
  • It is not perfect : the path is composed of multiple closed curves, so the shadow is not very nice in some directions :( – Kpym May 18 '18 at 20:44
  • The only thing one has to do to make it perfect is to use ordinary rather than blur shadows. I really like it! – user121799 May 18 '18 at 20:47
  • nice answer (+1), however, if i like to draw border of shape, where i should add option raw? or this only work without shape border line? Symbol's answer tex.stackexchange.com/questions/187165/… solve this. – Zarko Sep 7 '18 at 11:19
  • @Zarko Try to put preaction={thick,draw} in custom style. – Kpym Sep 9 '18 at 18:22

Your Answer

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