6

I'm trying to draw a diagram and my code is getting too cluttered and difficult to read. For code style purposes, is it possible to define a node in one place and mess with its positioning later?

Suppose I have something simple like this:

\documentclass{standalone}
\usepackage{tikz}

\makeatletter
\tikzstyle{mystyle} = [rectangle,rounded corners,fill=blue!20]
\makeatother

\begin{document}
    \begin{tikzpicture}[font=\sffamily]    
        \node[mystyle](node1) at (0, 0) {A};
        \node[mystyle](node2) [left of=node1] {B};
    \end{tikzpicture}
\end{document}

What I want to be able to do is:

\begin{tikzpicture}[font=\sffamily]    
    \node[mystyle](node1) {A};
    \node[mystyle](node2) {B};

    \somecommand (node1) at (0, 0)
    \somecommand (node2) [left of=node1] 

\end{tikzpicture}

Is that possible?

Or maybe turn something like this:

\begin{tikzpicture}[font=\sffamily]

\matrix  {
    \node[mystyle](node1){A}; & \node[mystyle](node3){D}; \\
    \node[mystyle](node2){B}; & \node[mystyle](node4){C}; \\
};

\end{tikzpicture}

into something like this:

\begin{tikzpicture}[font=\sffamily]

\node[mystyle](node1){A};
\node[mystyle](node2){B};
\node[mystyle](node3){C};
\node[mystyle](node4){D};

\matrix  {
    \something (node1); & \something (node3); \\
    \something (node2); & \something (node4); \\
};

\end{tikzpicture}

Is there any way to do this?

3 Answers3

5

I am not sure what you are really doing so this may be completely unhelpful. If so, just say and I can delete it.

You can use pics to define small pictures which you can reuse later over and over.

Technically, you could create a pic for each node you want and then call the relevant code at the relevant point. However, this is really an abuse of the syntax, I think. If you are creating many similar nodes, however, it would make perfect sense to do this.

Be warned that, although pics are quite node-like, they are not perfectly node-like so this may not work well at all in your actual document.

\documentclass[border=5pt,mult,tikz]{standalone}

\begin{document}

  \tikzset{
    font=\sffamily,
    mystyle/.style = {rectangle, rounded corners, fill=blue!20},
  }

  \begin{tikzpicture}% abuse of pics?
    [
      node1/.pic={
        \node [mystyle, pic actions] (node1) {A};
      },
      node2/.pic={
        \node [mystyle, pic actions] (node2) {B};
      },
    ]
    \pic at (0,0) {node1};
    \pic [left of=node1] {node2};
  \end{tikzpicture}

  \begin{tikzpicture}% more sensible use of pics?
    [
      my node/.pic={
        \node [mystyle] {#1};
      },
    ]

    \matrix  {
      \pic {my node={A}}; & \pic {my node={D}}; \\
      \pic {my node={B}}; & \pic {my node={C}}; \\
    };

  \end{tikzpicture}

\end{document}

If you need to refer to pics in order to draw lines between them, for example, you cannot handle things quite so straightforwardly as if they were nodes because they will not automatically get anchors in the way that nodes do. However, you can use a special syntax to add named coordinates to your pic which you can use later. The way this works is that you add particular named coordinates in the definition e.g. (-my point) (notice the hyphen) and then you pass a name to the pic as you would to a node e.g. (my pic). You can then refer to your named point as e.g. my pic-my point. As I understand it, this is less efficient than using actual nodes but that may not be a huge concern unless your picture is very large when the use of pics could slow compilation time noticeably.

Here's an example which adds some named coordinates to the definition of my node and then uses them to place red circles which correspond to various anchors of the node defined in the pic itself. This is obviously just for purposes of illustration: in practice, you would no doubt not want small red circles everywhere! But the point is you can use these coordinates to e.g. draw lines between the nodes or whatever.

  \begin{tikzpicture}% more sensible use of pics?
    [
      my node/.pic={
        \node (-center) [mystyle] {#1};
      \foreach \i in {north,south,east,west,north west,north east,south west,south east}
        \coordinate (-\i) at (-center.\i);
      },
    ]

    \matrix  {
      \pic (a) {my node={A}}; & \pic (d) {my node={D}}; \\
      \pic (b) {my node={B}}; & \pic (c) {my node={C}}; \\
    };

    \foreach \i in {a,b,c,d}
      \foreach \j in  {north,south,east,west,north west,north east,south west,south east,center}
        \path [fill=red] (\i-\j) circle (.5pt);;

  \end{tikzpicture}

naming pic coordinates

cfr
  • 198,882
  • Hey there, is there a way to uniquely identify the \pic instances of nodes such that I can draw lines between each of them? I am drawing a network diagram and this code is perfect for drawing data storage devices (need many of them), but I need a way to identify each one uniquely. If that can be done, I will be a happy camper. The code looks so nice and organized. Thanks in advance for your input! – Jonathan Komar Nov 22 '14 at 13:47
  • 2
    @macmadness86 Please see edited answer. Note that the red circles are just to demonstrate the idea - you would e.g. draw arrows between these points rather than giving your nodes measles! – cfr Nov 22 '14 at 16:06
  • Good information and ideas! However, I think you misunderstood my question. I would like to create two instances of a pic and draw line between them. So for your example, I would created two A's and be able to identify them uniquely. – Jonathan Komar Nov 22 '14 at 16:24
  • 1
    @macmadness86 That's fine. The A is just the content of the node. The a or whatever is the name. So just say \pic (a1) {my node={A}}; and \pic (a2) {my node={A}}; or whatever. You can have unique names for pics whose nodes get the same content. Maybe I just don't understand what you are asking but I'm not seeing the problem. – cfr Nov 22 '14 at 16:41
  • If I write \pic (a1) at (0,0) {node1={Node1Text}}; and try to reference with relative positioning for another node like so: \pic (a2) [right of=a1] {node2={Node2Text}};, I get the error "no shape named a1 is known." – Jonathan Komar Nov 22 '14 at 20:59
  • Also, maybe you could also shed some light on what pic actions means. I cannot find the page with documentation on this in the TikZ Manual. – Jonathan Komar Nov 22 '14 at 21:03
  • @macmadness86 It just passes options through. So if you write \pic [blue] ... blue won't do anything by default. But if the definition of the pic includes, say, \draw [red, pic actions] ... then whatever is drawn will be blue rather than red. – cfr Nov 22 '14 at 21:41
  • 1
    This might seem stupid, but I couldn't figure out how to type/send a message in the chat room. I still don't know how. – Jonathan Komar Nov 23 '14 at 07:38
  • Well I am just going to ask this here, because the chat room is not intuitive (no obvious place to insert response text). When I apply another tikzset style to the \pic, I end up with a no bounding box error. I wish I could put this in a chat. \tikzset{% 'shareStyle/.style = {% draw, drop shadow={opacity=0.5}, chamfered rectangle, chamfered rectangle angle=60, chamfered rectangle sep=.5cm, top color=orange!40, bottom color=orange!70, minimum height=1cm, inner sep=1cm}, font=\sffamily, }%` – Jonathan Komar Nov 23 '14 at 09:03
  • 1
    @macmadness86 I can't get it to work with a shadow either. I think you should ask this as a new question as it seems distinct from your initial question and will be more likely to get attention that way. (Link to this one so people can see the background if they want.) Your style works fine without the shadow. Agree about the chat interface. – cfr Nov 23 '14 at 12:04
  • Ok. Interesting. I might open a question on that topic in the future. But going back to your example: you added coordinates manually. Is there a way to add them similarly to how rpapa does it?: http://tex.stackexchange.com/a/47856/13552 – Jonathan Komar Nov 23 '14 at 15:18
  • @macmadness86 You could do that in a pic, yes. But I'm not sure how you want to use that here since you are dealing with nodes rather than paths. That is, you could draw paths in your pic and assign coordinates using the pos syntax but you cannot, as far as I know, do something similar for standard nodes. (You could assign them by drawing a path around the node after creating it, if you wanted to. Just make sure that the names of the coordinates you define in the pic start with hyphens so that you can add the prefix you assign for each pic instance.) Or...? Maybe I don't understand? – cfr Nov 23 '14 at 16:53
3

You could also just define your own macro that builds the \node content.. On the left is the manually specified examples you provided and on the right are the ones obtained by calling \Node:

enter image description here

Notes:

Code:

\documentclass{article}
\usepackage{tikz}

\tikzset{mystyle/.style={rectangle,rounded corners,fill=blue!20}}

\newcommand{\Node}[2][]{% % #1 = node options % #2 = node text \node[mystyle,#1] (node1) at (0,0) {#2} }%

\begin{document} \begin{tikzpicture}[font=\sffamily]
\nodemystyle at (0, 0) {A}; \nodemystyle [left of=node1] {B}; \end{tikzpicture}
\hspace*{2.0cm} \begin{tikzpicture}[font=\sffamily]
\Node{A}; \Node[left of=node1]{B}; \end{tikzpicture}

\bigskip\par
\begin{tikzpicture}[font=\sffamily]    
    \matrix  {
        \node[mystyle](node1){A}; & \node[mystyle](node3){D}; \\
        \node[mystyle](node2){B}; & \node[mystyle](node4){C}; \\
    };
\end{tikzpicture}
\hspace*{2.0cm}
\begin{tikzpicture}[font=\sffamily]    
    \matrix  {
        \Node{A}; & \Node{D}; \\
        \Node{B}; & \Node{C}; \\
    };
\end{tikzpicture}

\end{document}

Peter Grill
  • 223,288
3

Styles can greatly improve readability.

Here's a proof of concept using only styles.

The idea is to define a <name-of-node>pos style for each named node so that you can specify positioning by defining this style using keys like at or left etc, separately from the point of creation of the nodes.

\documentclass{standalone}
\usepackage{tikz}

\tikzset{
  mynode/.style = {
    rectangle,
    rounded corners,
    fill=blue!20,
    name=#1,
    #1pos/.try
  },
  position of/.style args={#1 is #2}{
    #1pos/.style={#2}
  }
}

\begin{document}

    \begin{tikzpicture}[
      font=\sffamily,
      position of=node1 is {at={(0,0)}},
      position of=node2 is {left of=node1}
    ]
      \node[mynode=node1] {A};
      \node[mynode=node2] {B};
    \end{tikzpicture}

\end{document}

Here the style position of is only used to improve readability over node1pos/.style={...}.

If you prefer to specify coordinates after creation (not necessary) you can wrap your figure in a macro definition:

\documentclass{standalone}
\usepackage{tikz}

\tikzset{
  mynode/.style = {
    rectangle,
    rounded corners,
    fill=blue!20,
    name=#1,
    #1pos/.try
  },
  % Only necessary for option 2
  position of/.style args={#1 is #2}{
    #1pos/.style={#2}
  }
}

\begin{document}

  \newcommand{\makepicture}[1][]{
    \begin{tikzpicture}[font=\sffamily,#1]
        \node[mynode=node1] {A};
        \node[mynode=node2] {B};
    \end{tikzpicture}
  }

  % Option 1
  \makepicture[
    node1pos/.style={at={(0,0)}},
    node2pos/.style={left of=node1}
  ]

  % Option 2
  \makepicture[
    position of=node1 is {at={(0,0)}},
    position of=node2 is {left of=node1}
  ]

\end{document}
Bordaigorl
  • 15,135
  • I believe that the left=of node (with the positioning library) is now preferred to the older syntax. (But I'm not sure what the advantages are.) – cfr Nov 22 '14 at 22:50
  • @cfr yes that's deprecated but I wanted to stick to the OP's example, so the crucial points were more obvious – Bordaigorl Nov 23 '14 at 02:19