5

I'm trying to clarify how I can create a style that basically add some nodes/path on top (or even behind if possible) of a node. I tried to play with append after command and I finally managed to add several nodes, but I can't find how to add arbitrary code, and especially I can't add a path. What is the good way to proceed?

Thank you!

MWE:

\documentclass[]{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning,fit,backgrounds,decorations.pathreplacing,calc,math,matrix}
\usepackage{forest}


% My style
\tikzstyle{myboxes}=[draw=#1,fill=#1!20,rounded corners,anchor=base]

\begin{document}

\begin{frame}{My test}
  \begin{figure}
    \centering
    \begin{tikzpicture}[
        n/.style={myboxes=orange},
        keep name/.style={prefix after command={\pgfextra{\let\fixname\tikzlastnode}}},
        c/.style={
          keep name,
          append after command={
            node [
              at=(\fixname.north),
              inner sep=3pt,
              draw=red,
              thick,
              inner sep=-\pgflinewidth,
              cross out
              ] {}
            node [
              at=(\fixname.south),
              inner sep=3pt,
              draw=green,
              thick,
              inner sep=-\pgflinewidth,
              cross out
              ] {}
            % Does not work
            % draw [red,very thick] (\fixname.north) -- (\fixname.south)
          }
        }
        ]
      \matrix[matrix of nodes, row sep=2mm](sel){
        |[n,c]|A\\
        |[n,c]|C \\
        |[n]|B\\
      };      
    \end{tikzpicture}
  \end{figure}
\end{frame}
\end{document}

-- EDIT -- I tried with path picture as proposed by marmot, but unfortunately I can't go outside of the bounding box:

enter image description here

\documentclass[]{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning,fit,backgrounds,decorations.pathreplacing,calc,math,matrix}
\usepackage{forest}


% My style
\tikzstyle{myboxes}=[draw=#1,fill=#1!20,rounded corners,anchor=base]

\begin{document}

\begin{frame}{My test}
  \begin{figure}
    \centering
    \begin{tikzpicture}[
        n/.style={myboxes=orange},
        c/.style={
          path picture = {
            \draw[-latex] ($(path picture bounding box.north west)$) -- ($(path picture bounding box.south east)$);
            \draw[-latex] ($(path picture bounding box.north west)$) -- ($(path picture bounding box.north east)$);
          }
        }
        ]
      \matrix[matrix of nodes, row sep=2mm](sel){
        |[n,c]|A\\
        |[n,c]|C \\
        |[n]|B\\
      };      
    \end{tikzpicture}
  \end{figure}
\end{frame}
\end{document}

-- EDIT 2 -- The best general solution seems to be, as noticed by Ignasi, the one proposed by Marmot here. However it's not working on matrices, where basically all the points are centered near the central point because of the layers...

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc,matrix,positioning}
\usetikzlibrary{backgrounds}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\tikzset{
  % keep name/.style={prefix after command={\pgfextra{\let\fixname\tikzlastnode}}},
  myt/.style={
    % keep name,
    fill=green!30,
    rounded corners,
    draw=green,
    append after command={\pgfextra%
      % \fixname does not solve the problem either
      \begin{pgfonlayer}{background} 
        \draw[-latex,blue] (\tikzlastnode.north west) -- (\tikzlastnode.south east);
        \node[orange] at (\tikzlastnode.north) {\tiny \scalebox{.5}{A}};
        \node[orange] at (\tikzlastnode.south) {\tiny \scalebox{.5}{B}};
      \end{pgfonlayer}
      \node[] at ($(\tikzlastnode.east)!.5!(\tikzlastnode.north east)$) {\tiny \scalebox{.5}{Samelevel}};
      \begin{pgfonlayer}{foreground} 
        \draw[-latex,red] (\tikzlastnode.south west) -- (\tikzlastnode.north east);
        \node[purple] at (\tikzlastnode.east) {\tiny \scalebox{.5}{D}};
        \node[purple] at (\tikzlastnode.west) {\tiny \scalebox{.5}{C}};
      \end{pgfonlayer}
      \endpgfextra}
  }
}
\begin{document}
\begin{tikzpicture}
  \node[myt](a){a};
  \node[right=of a, myt](b){b};
  \node[myt, right=of b](c){c};
  \matrix[matrix of nodes, right=of c](matrixname){
    |[myt]|U & |[myt]|V \\
    |[myt]|W & |[myt]|X \\
  };
\end{tikzpicture}
\end{document}

Output works on single nodes:

enter image description here

But not on matrices:

enter image description here

-- EDIT 3 & 4 --

After the proposition of using pics, I tried an indeed it seems really powerful. The only problem is that pics are not really styles, but they can simulate styles pretty well else, and it's even possible to "compose" them and they can also be included in matrices. See below the code and picture:

enter image description here

\documentclass[]{article}

\usepackage{tikz}
\usetikzlibrary{arrows,positioning,shapes,calc}
\begin{document}

\tikzset{
  % Args: style of the main node, content of the main node
  % and color of back arrow and node
  pics/addarrows/.style args={#1/#2/#3}{
    code = {
      \node[rounded corners, #1,opacity=0] (tmpnode) {#2};
      \draw[-latex,#3] ($(tmpnode.west)+(-0.2,0)$) -- ($(tmpnode.east)+(0.2,0)$);
      \node[rounded corners, #1] {#2};
      \draw[-latex] (tmpnode.south west) -- (tmpnode.north east);
      \draw[-latex] (tmpnode.north west) -- (tmpnode.south east);
      \node[fill=#3,circle,inner sep=2pt] at (tmpnode.south) {};
    }},
  % Creates a shortcut
  pics/defarrows/.style args={#1}{
    code = {
      \pic{addarrows={draw=red,fill=red!20}/#1/red};
    }
  },
  pics/composableHorizVert/.style={
    code={
      \begin{scope}[transparency group, opacity=0]
        #1
      \end{scope}
      \draw[-latex] ($(tmpnode.west)+(-0.2,0)$) -- ($(tmpnode.east)+(0.2,0)$);
      #1
      \draw[-latex] ($(tmpnode.north)+(0,0.2)$) -- ($(tmpnode.south)+(0,-0.2)$);
    }
  },
  pics/composableDiag/.style={
    code={
      \begin{scope}[transparency group, opacity=0]
        #1
      \end{scope}
      \draw[-latex] ($(tmpnode.north west)+(-0.2,0.2)$) -- ($(tmpnode.south east)+(0.2,-0.2)$);
      #1
      \draw[-latex] ($(tmpnode.north east)+(0.2,0.2)$) -- ($(tmpnode.south west)+(-0.2,-0.2)$);
    }
  }
}

Some defaults pics on a matrix:\\
\begin{tikzpicture}[auto]  
  \matrix[](matrixname){
    \pic{defarrows=A}; & \pic{defarrows=B}; \\
    \pic{defarrows=C}; & \pic{defarrows=DEF};\\
  };
\end{tikzpicture}

Some pics with arguments that specify the ``main node style'':\\
\begin{tikzpicture}[auto]  
  \pic [local bounding box=picgreen] {addarrows={draw=green,fill=green!20,ellipse}/My text/purple};
  \pic [below right=of picgreen,local bounding box=picyellow] {addarrows={draw=orange,fill=yellow,ellipse}/My text/red};
  % NB: If you want to specify a specific node inside the pic, you can always try to specify
  % a name through a new argument
  \draw[-latex,dashed] (picgreen) -- (picyellow);
\end{tikzpicture}

Show that it's possible to compose ``pics styles'':\\
\begin{tikzpicture}[baseline]
  \pic{composableHorizVert={\node[name=tmpnode,fill=red!30]{A};}};
\end{tikzpicture}
+
\begin{tikzpicture}[baseline]
  \pic{composableDiag={\node[name=tmpnode,fill=red!30]{A};}};
\end{tikzpicture}
=
\begin{tikzpicture}[baseline]
  \pic{composableHorizVert={\pic{composableDiag={\node[name=tmpnode,fill=red!30]{A};}};}};
\end{tikzpicture}

\end{document}
tobiasBora
  • 8,684
  • That's because it is not possible for "any" arbitrary path. What are you trying to do? – percusse Apr 25 '18 at 15:53
  • @percusse Why isn't it possible ? Well for now I'm just trying to learn ;) My initial goal was to draw a cross accross a node, but because of the "cross out" style I can use instead a node as shown in my question. So maybe a first step would be to "double cross", by crossing in diagonal, but also east/west and north/south, without using a specific node. But more generally, any general advice would be great. – tobiasBora Apr 25 '18 at 16:04
  • I guess you could do it with path picture. –  Apr 25 '18 at 16:05
  • @marmot thank you for the idea. I tried, and indeed it looks great, but unfortunately it seems that it's not possible to go outside of the box (see my edited answer) – tobiasBora Apr 25 '18 at 16:14
  • 1
    For a tikzpicture inside a beamer frame you need fragile option: \begin{frame}[fragile]{My test}. – Ignasi Apr 25 '18 at 17:47
  • I think it would be very hard to come up with something better than @Ignasi, who IMHO has told you the most elegant method. –  Apr 25 '18 at 18:04

2 Answers2

5

The best way for adding nodes at arbitrary positions (even behind) of others nodes is the use of labels. Labels are nodes and they can have it's own style.

Your code adapted to declare c as an style for labels:

\documentclass[]{beamer}
\usepackage{tikz}
\usetikzlibrary{matrix, shapes.misc}

% My style
\tikzset{
    myboxes/.style={
        draw=#1,fill=#1!20,rounded corners,anchor=base}
}

\begin{document}

\begin{frame}[fragile]{My test}
  \begin{figure}
    \centering
    \begin{tikzpicture}[
        n/.style={myboxes=orange},
        c/.style={
            inner sep=3pt,
          draw=#1,
          thick,
          inner sep=-\pgflinewidth,
          cross out
          },
        d/.style={
            inner sep=3pt,
          draw=#1,
          minimum size=1cm,
          behind path,
          thick,
          inner sep=-\pgflinewidth,
          cross out,
          },
        ]
      \matrix[matrix of nodes, row sep=2mm](sel){
        |[n,label={[c=red]:},label={[c=green]below:}]|A\\
        |[n,label={[c=blue]right:a}]|C \\
        |[n,label={[d=green]center:behind}]|B\\
      };      
    \end{tikzpicture}
  \end{figure}
\end{frame}
\end{document}

enter image description here

Ignasi
  • 136,588
  • Thank you for the tips, labels appears to be indeed very practical. But I can't find a way to use them to draw path, for example to draw a dashed path between current node.south to current node.north west? – tobiasBora Apr 25 '18 at 19:44
  • @tobiasBora Can't you use path picture for that? –  Apr 25 '18 at 20:44
  • @tobiasBora marmot's solution to Why fill covers append after command elements? can be applied to independent nodes although I'm not able to apply it to nodes in matrices. – Ignasi Apr 26 '18 at 07:54
  • @marmot : path picture has two drawbacks: first it seems that the drawing is behind the node (even if there may be some dirty tricks involving foreground to partially solve it), and also it's not possible to go outside of the bounding box if I'm not wrong. – tobiasBora Apr 26 '18 at 11:19
  • @Ignasi : Indeed this solution seems to be the best so far, but it's unusable for matrices because of the foreground/backgroud layer, and I really like matrices... I updated my question, if someone has an idea... – tobiasBora Apr 26 '18 at 11:59
  • 1
    @tobiasBora Did you consider to use pics? you can join whatever you want under a name and it's possible to use in matrix (see https://tex.stackexchange.com/a/204167/1952) – Ignasi Apr 26 '18 at 12:14
  • @tobiasBora I think you might want to accept this answer and ask a new question, in which you show what you have and specify all boundary conditions, see the discussion here. –  Apr 26 '18 at 13:33
  • @Ignasi Hum thank you very much, pics are indeed great and solve most of my problems so far (see my edited answer), and I can always "fake" a node so that pics looks more or less like style (the only problem is that I don't see how to "embedd" pics styles). Thank you very much, if you want you can create a new answer so that I can accept it. (and also you can copy/get inspired by the code I paste, no problem) – tobiasBora Apr 27 '18 at 14:16
  • @marmot Well I don't see what else I would ask in a new question, since my initial question is already very general, and just asks for the bests ways to add nodes and paths in a node style. After, because tikz is not perfect, there are several solutions, but I don't see the point to create a new question just for that. Also, Ignasi solved most of my problem, the only "problem" is that I don't see how to combine pics together, for example if a pic is supposed to cross horizontally, and if another pic is crossing vertically, can I cross both horizontally and vertically. Else everything rocks! – tobiasBora Apr 27 '18 at 14:21
  • In fact I found a simple way to compose them, so I guess that the problem is more or less solved now, even if pics are a bit eavier than usual styles, they are quite easy to work with! Thank you very much, and do not hesitate to write an answer and copy/paste my code so that I can accept your answer. – tobiasBora Apr 27 '18 at 14:40
  • @tobiasBora Kpym already did it! – Ignasi Apr 27 '18 at 16:04
2

You have all the elements in your question :

  • add some part of the path using append after command
  • this added path could be a pic
  • inside the pic you can draw on a particular layer (background, main, forground)
  • if you do not want the added drawing to modify the bounding box, you can use pgfinterruptboundingbox environment.

Here is an example of t style that draws a pic on top and behind of the node:

\documentclass[tikz,border=7pt]{standalone}
\usetikzlibrary{backgrounds,matrix}
\tikzset{
  a/.pic = {
    \begin{pgfinterruptboundingbox}
      \node[black,rotate=45,fill=white,draw] (over) {over the node};
      \draw[bend left,-latex] (over.east) to[out=90,looseness=7] (#1.east);
      \begin{scope}[on background layer]
        \node[black,rotate=-45,scale=2] {behind the node};
      \end{scope}
    \end{pgfinterruptboundingbox}
  },
  t/.style={
    append after command={
      (\tikzlastnode.center) pic{a=\tikzlastnode}
    }
  }
}
\begin{document}
  \begin{tikzpicture}
    \filldraw[red] (0,3) circle (1pt) node[t,draw,fill=yellow,scale=3]{Node};
    \matrix[matrix of nodes, row sep=2mm,n/.style={text width=16mm,fill=blue!10},draw=purple](sel){
        |[n]|A\\
        |[n,t]|C \\
        |[n]|B\\
      };
  \path (-5,-5) (5,5);
  \end{tikzpicture}
\end{document}

enter image description here

Kpym
  • 23,002
  • Thank you, I think that it solve all my problems, and thanks for the pgfinterruptboundingbox trick! – tobiasBora Apr 28 '18 at 16:32
  • And by the way, do you know if there is an equivalent of pgfinterruptboundingbox for path picture? I don't understand why it can't go outside of the bounding box even when I use this option. – tobiasBora Apr 28 '18 at 16:37
  • And also, note that as it it's not "composable" (in the sens that you cannot assign two styles to the same picture), except if you use the "keep name" trick, that basically assign at the node creation the name \fixnode to the current node. To do so, you can get inspiration from my question, or simply add a new style keep name/.style={prefix after command={\pgfextra{\let\fixname\tikzlastnode}}}, add keep name at the beginning of each list of style (like [keep name,n,t]), and replace the \tikzlastnode by \fixname in the style definition. If you have a better idea, please tell me! – tobiasBora Apr 28 '18 at 16:50
  • 1
    @tobiasBora path picture clip using the current path so whatever the bonding box is, it clips the drawing. But if I remember well, you can go outside path picture by drawing on another (for example background) layer, because clipping is limited to the current layer. – Kpym Apr 28 '18 at 16:53