3

I have the following piece of TikZ code and I was wondering if it can be optimized? (I am actually sure that it can be!)

The main issue is this: When I rotate the first batch of nodes surrounded by the ellipse to create the second batch, the ellipse is out of place. (It is not where I expect it to be. I am not sure which point is fixed in the rotation.) I have to tweak it blindly to move it to the desired place.

The same goes for the labels "Community 2" and "Community 3". The rotated coordinates don't seem to match what I expect, and I have to blindly adjust them with limited success.

On an unrelated note: I have also experimented with for-loops, but since I want the edges in each community to be slightly different, it seems that a direct copy-paste is easier?

Other optimizations are also welcome.

code

\documentclass[tikz]{standalone}

\begin{document}
\begin{tikzpicture}[scale = 1,node distance = 10mm]

\tikzset{
  every node/.append style={circle, thick,
    inner sep=0pt, minimum size = 3mm},
  every label/.append style={red},
  c1/.style={draw=blue!50,fill=blue!20},
  c2/.style={draw=green!80,fill=green!40},
  c3/.style={draw=red!80,fill=red!40}
}   

\filldraw[rotate=30,blue!10] (0.8,0.1) ellipse (30pt and 25pt);
\node at (1,1.7) {Community 1};
\node[c1] (1) at (0,0) {};
\node[c1] (2) at (1,1)  {}
edge (1);
\node[c1] (3) at (0.7,0.2) {}
edge (2)
edge (1);
\node[c1] (4) at (0.2,0.7) {}
edge (3)
edge (2);
\node[c1] (5) at (1.3,0.5) {}
edge (2)
edge (4);

\begin{scope}[yshift=-2cm,rotate around={-40:(0,0)}]
\filldraw[rotate=30,green!10] (0.8,0.1) ellipse (30pt and 25pt);
\node at (1.2,-0.2) {Community 2};
\node[c2] (A) at (0,0) {};
\node[c2] (B) at (1,1)  {}
edge (A);
\node[c2] (C) at (0.7,0.2) {}
edge (B)
edge (A);
\node[c2] (D) at (0.2,0.7) {}
edge (C)
edge (B);
\node[c2] (E) at (1.3,0.5) {}
edge (C)
edge (D);
\end{scope}

\begin{scope}[xshift=2cm,yshift=-0.5cm,rotate around={-40:(0,0)}]
\filldraw[rotate=30,red!10] (0.8,0.1) ellipse (30pt and 25pt);   
\node[c3] (a) at (0,0) {};
\node[c3] (b) at (1,1)  {}
edge (a);
\node[c3] (c) at (0.7,0.2) {}
edge (b)
edge (a);
\node[c3] (d) at (0.2,0.7) {}
edge (a)
edge (b);
\node[c3] (e) at (1.3,0.5) {}
edge (c)
edge (d);
\node[above of=b] {Community 3};
\end{scope}

\draw (3) -- (A);
\draw (4) -- (D);
\draw (5) -- (a);
\draw (c) -- (E);
\draw (e) -- (B);

\end{tikzpicture}
\end{document}

Output

enter image description here

2
  • 1
    As it stands, your code is not a complete minimal working example (MWE) as it doesn't have a preamble. It would also be useful to have a screenshot of what it looks like. It's a little tricky to get a sense of what you are asking without compiling the code. Apr 4, 2013 at 14:46
  • OK. I have edited it to make it a standalone example and added a picture of the output. Part of the code is not showing as code for some reason.
    – passerby51
    Apr 5, 2013 at 2:19

2 Answers 2

3

You should define a macro for the drawing so that you can reuse it:

enter image description here

Notes:

  • Since I do not know the intent of the drawing and how carefully you want to chose placement and connectivity, I based it on the given code.
  • One thing to keep in mind when doing repeated things is that each task should be thought of in a similar manner. For instance in placing the node text you use absolute coordinates twice, and third time you use a relative placement. I decided to chose absolute position but that may not be ideal. This of course made the placement of the third node label somewhat of a guess by trial and error. See Relative Placement of Labels section below which may be a better option.
  • The nodes are labelled as A<color>, ... D<color> (where <color> is the third parameter to \MyNodes) so that they can be systematically named and then referenced as desired outside of the drawing of the nodes.

Code: Fixed Placement of Labels

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

\begin{document}
\begin{tikzpicture}[scale = 1,node distance = 10mm]

\tikzset{
  every node/.append style={circle, thick,
    inner sep=0pt, minimum size = 3mm},
  every label/.append style={red},
  c1/.style={draw=blue!50,fill=blue!20},
  c2/.style={draw=green!80,fill=green!40},
  c3/.style={draw=red!80,fill=red!40}
} 

\newcommand*{\MyNodes}[6]{%
    % #1 = style
    % #2 = style
    % #3 = node name suffix.
    % #4 = node to connect to last node
    % #5 = label position
    % #6 = label text
    \filldraw[rotate=30,#1] (0.8,0.1) ellipse (30pt and 25pt);
    \node at #5 {#6};
    \node[#2] (A#3) at (0,0) {};
    \node[#2] (B#3) at (1,1)  {}
    edge (A#3);
    \node[#2] (C#3) at (0.7,0.2) {}
    edge (B#3)
    edge (A#3);
    \node[#2] (D#3) at (0.2,0.7) {}
    edge (C#3)
    edge (B#3);
    \node[#2] (E#3) at (1.3,0.5) {}
    edge (#4#3)
    edge (D#3);
}%  

\MyNodes{blue!10}{c1}{Blue}{B}{(1,1.7)}{Community 1}

\begin{scope}[yshift=-2cm,rotate around={-40:(0,0)}]
    \MyNodes{green!10}{c2}{Green}{C}{(1.2,-0.2)}{Community 2}
\end{scope}

\begin{scope}[xshift=2cm,yshift=-0.5cm,rotate around={-40:(0,0)}]
    \MyNodes{red!10}{c3}{Red}{C}{(0.5,1.75)}{Community 3}
\end{scope}

\draw (CBlue) -- (AGreen);
\draw (DBlue) -- (DGreen);
\draw (EBlue) -- (ARed);
\draw (CRed) -- (EGreen);
\draw (ERed) -- (BGreen);

\end{tikzpicture}
\end{document}

You can also place the node labels in a relative manner which I think is a better option. To determine where to place the labels it helps to know which labels are where, and the \Debug macro below allows you to see that. If you uncomment out the subsequent line in the MWE, the node labels will be suppressed.

enter image description here

Code: Relative Placement of Labels

\documentclass{article}
\usepackage{tikz}

\tikzset{%
  every node/.append style={circle, thick,
    inner sep=0pt, minimum size = 3mm},
  every label/.append style={red},
  c1/.style={draw=blue!50,fill=blue!20},
  c2/.style={draw=green!80,fill=green!40},
  c3/.style={draw=red!80,fill=red!40}
} 

\newcommand*{\Debug}[1]{\tiny#1}%
%\renewcommand*{\Debug}[1]{}% Comment this out for debugging

\newcommand*{\MyNodes}[6]{%
    % #1 = style
    % #2 = style
    % #3 = node name sufffix.
    % #4 = node to connect to last node
    % #5 = label position
    % #6 = label text
    \filldraw[rotate=30,#1] (0.8,0.1) ellipse (30pt and 25pt);
    \node[#2] (A#3) at (0,0) {\Debug{A}};
    \node[#2] (B#3) at (1,1)  {\Debug{B}}
    edge (A#3);
    \node[#2] (C#3) at (0.7,0.2) {\Debug{C}}
    edge (B#3)
    edge (A#3);
    \node[#2] (D#3) at (0.2,0.7) {\Debug{D}}
    edge (C#3)
    edge (B#3);
    \node[#2] (E#3) at (1.3,0.5) {\Debug{E}}
    edge (#4#3)
    edge (D#3);
    \node [#5#3] {#6};
}%  

\begin{document}
\begin{tikzpicture}[scale = 1,node distance = 10mm, thick]

\MyNodes{blue!10}{c1}{Blue}{B}{above of=D}{Community 1}

\begin{scope}[yshift=-2cm,rotate around={-40:(0,0)}]
    \MyNodes{green!10}{c2}{Green}{C}{below of=C}{Community 2} 
\end{scope}

\begin{scope}[xshift=2cm,yshift=-0.5cm,rotate around={-40:(0,0)}]
    \MyNodes{red!10}{c3}{Red}{C}{above of=B}{Community 3}
\end{scope}

\draw (CBlue) -- (AGreen);
\draw (DBlue) -- (DGreen);
\draw (EBlue) -- (ARed);
\draw (CRed)  -- (EGreen);
\draw (ERed)  -- (BGreen);

\end{tikzpicture}
\end{document}
3

Another try at an optimization. This uses PGF keys.

Disclaimer: The outcome is not exactly the same, as I have used the ellipse node instead of the ellipse path (and I had to adjust the coordinates a bit).
The advantage I saw here was the use of the label option to add the “Community” labels. Plus you can reference it for later use.

There is one command:

\drawBlob[<optional arguments](<coordinate>);

The (<coordinate>) is where (0,0) of the local coordinate system is.
As they are a few transformations going on in the picture, I'm not entirely sure where the blobs are. Note only, that without rotation, the first mini blob (A) is going to be at (<coordinate>).

You can use the styles

  • every blob picture,
  • every mini blob,
  • every blob, and
  • every mini blob edge

to customize the content. There are set up similar to the every node style. For every every <something> style there exist a <something> style that appends its element to the correspinding every style.

Furthermore, there are three more keys:

  • connect mini blobs,
  • blob name, and
  • rotate blob.

The rotate blob key rotates the whole picture (the big blob and the mini blobs) around the big blob’s center.

The value of the blob name key will get used to name the big blob (which is named after the key) and the mini blobs (which are named <value of blob name>-<char> where <char> is between A and E).

If blob name is not given (e.g. empty, if a default one is not specified), the nodes will get an internal name (by which they still can be referenced later due to an internal counter).
The counter isn't really needed when the nodes will not get referenced later …

Finally, there is connect mini blobs. This key should be given a list of mini-blobs that should get connected within the blob.
As your example seem to hint that they are always connected in the same way, I pre-set this style with

\tikzset{connect mini blobs={A/B,A/C,B/C,B/D,C/D,C/E,D/E}}

This brings out the advantage of using PGF keys. You can set default values, you can change them mid-document, you can .append settings and you can have different settings for every new blob.

You can also set mini blob <char> styles, to further customize ever mini blob.

And there's every minin blob edge, for the line internally drawn edges betwen the mini blobs.

Improvements or Maybe the fit library?

Notice how there are similar lines when drawing the mini blobs?

One enhancement would be to also customize the number and the location of these mini blobs. A few keys and a \foreach would do.

The ellipse than can—with the help of the background library—been drawn with fit (e.g. fit=(\qrr@blob@name-A)(\qrr@blob@name-B)…), though the rotation of this ellipse will have to be manually defined and the actual size would be dependent on rotate blob. Again, too much transformations going on.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{shapes.geometric}
\makeatletter
%% Setup
\tikzset{
    connect mini blobs/.store in=\qrr@blob@connections,
    connect mini blobs=,
    blob name/.store in=\qrr@blob@name,
    blob name=,
    rotate blob/.store in=\qrr@blob@rotate,
    rotate blob=0,
    % short-cut styles
    blob picture/.style={every blob picture/.append style={#1}},
    mini blob/.style={every mini blob/.append style={#1}},
    blob/.style={every blob/.append style={#1}},
    mini blob edge/.style={every mini blob edge/.append style=#1},
    % a few defaults
    every blob picture/.style={},
    every mini blob/.style={shape=circle, thick, draw, minimum size=3mm},
    every blob/.style={shape=ellipse, draw, fill, inner sep=0pt, minimum width=60pt, minimum height=50pt},
    every mini blob edge/.style={thick},
}

\newcount\c@qrr@blob@count
\newcommand*{\drawBlob}[1][]{\begingroup\tikzset{#1}\draw@blob}
\def\draw@blob(#1){%
    \ifx\qrr@blob@name\pgfutil@empty
        \edef\qrr@blob@name{qrr@mini-blob@\the\c@qrr@blob@count}%
    \fi
    \scope[absolute, every blob picture/.try]
        \node[shift={(#1)}, rotate=30+\qrr@blob@rotate, every blob/.try] (\qrr@blob@name) at (0.6,0.5) {};
        \node[every mini blob/.try, mini blob A/.try,  shift={(#1)}, ] (\qrr@blob@name-A) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 0,0)     {};
        \node[every mini blob/.try, mini blob B/.try,  shift={(#1)}, ] (\qrr@blob@name-B) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 1,1)     {};
        \node[every mini blob/.try, mini blob C/.try,  shift={(#1)}, ] (\qrr@blob@name-C) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 0.7,0.2) {};
        \node[every mini blob/.try, mini blob D/.try,  shift={(#1)}, ] (\qrr@blob@name-D) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 0.2,0.7) {};
        \node[every mini blob/.try, mini blob E/.try,  shift={(#1)}, ] (\qrr@blob@name-E) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 1.3,0.5) {};
        \foreach \qrr@blob@connection@start/\qrr@blob@connection@target in \qrr@blob@connections {
            \path[every mini blob edge/.try] (\qrr@blob@name-\qrr@blob@connection@start) edge (\qrr@blob@name-\qrr@blob@connection@target);}
    \endscope
    \endgroup
    \advance\c@qrr@blob@count\@ne
}
\makeatother

%%% Standard connections
\tikzset{connect mini blobs={A/B,A/C,B/C,B/D,C/D,C/E,D/E}} % that's always the same

%%% Custom styles
\tikzset{
  c1/.style={draw=blue!50,fill=blue!20},
  c2/.style={draw=green!80,fill=green!40},
  c3/.style={draw=red!80,fill=red!40}
}
\begin{document}
\begin{tikzpicture}
\drawBlob[
    mini blob=c1,
    blob={color=blue!10, label=above:Community 1},
    blob name=Comm1
    ](0,0)

\drawBlob[
    mini blob=c2,
    blob={color=green!10, label=below:Community 2},
    blob name=Comm2,
    rotate blob=-40
    ](0,-3)

\drawBlob[
    mini blob=c3,
    blob={color=red!10, label=above:Community 3},
    blob name=Comm3,
    rotate blob=-40
    ](2.5,-.5)

\foreach \start/\target in {1-C/2-A,1-D/2-D,1-E/3-A,3-C/2-E,3-E/2-B} \draw[every mini blob edge] (Comm\start) -- (Comm\target);
\end{tikzpicture}
\end{document}

Output

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .