14

I am drawing a graph of sorts in TikZ, and I have several connected nodes in a cycle, and I would like to shade the area bounded by paths between those nodes. My problem is that the shading is not consistent with the arcs between the nodes (I have drawn the arcs between the nodes separately). My code looks like this:

\documentclass{article}
\usepackage{tikz}
\tikzset{state/.style={circle,draw=black, very thick,inner sep=3pt}}

\begin{document}

\begin{tikzpicture}

\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\draw (a) to [bend right=30] (b);
\draw (a) to [bend left=30] (d);
\draw (b) to [bend right=30] (c);
\draw (c) to [bend right=30] (d);
\path[fill=gray!50,opacity=.5] (a) to [bend right=30] (b) to [bend right=30] (c) to [bend right=30] (d) to [bend right=30] (a);

\end{tikzpicture}

\begin{tikzpicture}

\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\draw (a) to [bend right=30] (b);
\draw (a) to [bend left=30] (d);
\draw (b) to [bend right=30] (c);
\draw (c) to [bend right=30] (d);
\path[fill=gray!50,opacity=.5] (0,0) to [bend right=30] (2,0) to [bend right=30] (2,-1.5) to [bend right=30] (0,-1.4) to [bend right=30] (0,0);

\end{tikzpicture}

\end{document}

I tried two approaches. The first, I used node names as anchors for the shading, but this does not cover all the area bounded by my curves. It seems that the shading is using the outer edge of the nodes as anchors than the center. The second, I used the coordinates of the nodes as anchors, but this covers too much. As far as I can tell, this is caused by (a) the drawn arcs between the nodes having a shorter distance to bend the same amount, and (b) the radii of the nodes not being all equal, due to the varying size of the characters. The result is this:

enter image description here

I would like to keep the drawn arcs between nodes as they are (even though they are not symmetric across the central axes), and would like to shade the area bounded by those paths and the outer boundaries of my nodes, in effect this:

enter image description here

Any tips in this regard are appreciated!

3 Answers 3

15

You should try to draw arcs and lines from the center of the nodes. This can be done with the help of positioning library of tikz.

Code

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\tikzset{state/.style={circle,draw=black, very thick,inner sep=3pt,fill=white}}

\begin{document}

\begin{tikzpicture}

\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\draw (a.center) to [bend right=30] (b.center);
\draw (a.center) to [bend left=30] (d.center);
\draw (b.center) to [bend right=30] (c.center);
\draw (c.center) to [bend right=30] (d.center);
\path[fill=gray!50,opacity=.5] (a.center) to [bend right=30] (b.center) to [bend right=30] (c.center) to [bend right=30] (d.center) to [bend right=30] (a.center);

\end{tikzpicture}

\begin{tikzpicture}
\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\draw (a.center) to [bend right=30] (b.center);
\draw (a.center) to [bend left=30] (d.center);
\draw (b.center) to [bend right=30] (c.center);
\draw (c.center) to [bend right=30] (d.center);
\path[fill=gray!50,opacity=.5] (0,0) to [bend right=30] (2,0) to [bend right=30] (2,-1.5) to [bend right=30] (0,-1.5) to [bend right=30] (0,0);

\end{tikzpicture}

\end{document}

This produces

enter image description here

The problem here is the curves appear over the nodes. This is due to the sequence of their drawing. This indicates that you should draw the nodes later. But if we do so, tikz won't know the points (a.center) etc and hence an error is thrown out. The remedy will be to draw the nodes twice (before and after drawing the curves and fill the nodes with white color) like the below:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\tikzset{state/.style={circle,draw=black, very thick,inner sep=3pt,fill=white}}

\begin{document}

\begin{tikzpicture}
\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\draw (a.center) to [bend right=30] (b.center);
\draw (a.center) to [bend left=30] (d.center);
\draw (b.center) to [bend right=30] (c.center);
\draw (c.center) to [bend right=30] (d.center);
\path[fill=gray!50,opacity=.5] (0,0) to [bend right=30] (2,0) to [bend right=30] (2,-1.5) to [bend right=30] (0,-1.5) to [bend right=30] (0,0);
\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\end{tikzpicture}

\end{document}

Which looks inappropriate!

As suggested by Claudio Fiandrino the backgrounds library can be used to push some objects to the background.

Code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,backgrounds}
\tikzset{state/.style={circle,draw=black, very thick,inner sep=3pt,fill=white,minimum size=4ex}}

\begin{document}

\begin{tikzpicture}

\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\begin{pgfonlayer}{background}
\draw (a.center) to [bend right=30] (b.center);
\draw (a.center) to [bend left=30] (d.center);
\draw (b.center) to [bend right=30] (c.center);
\draw (c.center) to [bend right=30] (d.center);
\path[fill=gray!50,opacity=.5] (a.center) to [bend right=30] (b.center) to [bend right=30] (c.center) to [bend right=30] (d.center) to [bend right=30] (a.center);
\end{pgfonlayer}
\end{tikzpicture}

\begin{tikzpicture}
\node[state] (a) at (0,0) {$a$};
\node[state] (b) at (2,0) {$b$};
\node[state] (c) at (2,-1.5) {$c$};
\node[state] (d) at (0,-1.5) {$d$};
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\begin{pgfonlayer}{background}
\draw (a.center) to [bend right=30] (b.center);
\draw (a.center) to [bend left=30] (d.center);
\draw (b.center) to [bend right=30] (c.center);
\draw (c.center) to [bend right=30] (d.center);
\path[fill=gray!50,opacity=.5] (0,0) to [bend right=30] (2,0) to [bend right=30] (2,-1.5) to [bend right=30] (0,-1.5) to [bend right=30] (0,0);
\end{pgfonlayer}
\end{tikzpicture}

\end{document}

From this, we get

enter image description here

Another approach I suggest you is to use coordinates and draw everything using them in the proper sequence in which the objects are to be overlay-ed. Further, the size of letters a and b are not same. Hence your circles will have varying radius. This can be fixed by either adding phantom space or defining the minimum size for the node. The code will be:

\documentclass{article}
\usepackage{tikz}
\tikzset{state/.style={circle,draw=black,fill=white, very thick,inner sep=3pt,minimum size=4ex}}

\begin{document}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (2,0);
\coordinate (c) at (2,-1.5);
\coordinate (d) at (0,-1.5);
\draw (a) to [bend right=30] (b);
\draw (a) to [bend left=30] (d);
\draw (b) to [bend right=30] (c);
\draw (c) to [bend right=30] (d);
\path[draw,fill=gray!50,opacity=.5] (a) to [bend right=30] (b) to [bend right=30] (c) to [bend right=30] (d) to [bend right=30] (0,0);
\draw (a) to (b);
\draw (a) to (d);
\draw (b) to (c);
\draw (c) to (d);
\node[state] at (a)  {$a$};
\node[state] at (b)  {$b$};
\node[state] at (c)  {$c$};
\node[state] at (d)  {$d$};

\end{tikzpicture}

\end{document}

enter image description here

3
  • Danke schon! New command \coordinate learned. Oct 23, 2012 at 1:36
  • Nice, +1. But you can also add a different approach: the backgrounds library. In your first code, by simply enclosing inside \begin{pgfonlayer}{background}...\end{pgfonlayer} the commands drawing the gray shape you'll get the same final result. Oct 23, 2012 at 7:05
  • @ClaudioFiandrino: Nice suggestion. Added to the answer. Thank you :)
    – user11232
    Oct 23, 2012 at 7:45
11

Somewhat more compact.

\documentclass[tikz]{standalone}
\tikzset{state/.append style={circle,draw=black,fill=white,
                              minimum size=4ex,very thick,inner sep=3pt}}
\begin{document}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (2,0);
\coordinate (c) at (2,-1.5);
\coordinate (d) at (0,-1.5);
\draw (a) rectangle (c);
\draw[bend right=30,fill=gray!50,opacity=.5] (a) to  (b) to (c) to (d) to (a);
\foreach \x in {a,...,d}{\node[state] at (\x)  {$\x$};}
\end{tikzpicture}
\end{document}

If I remember correctly you are better off with avoiding the overwrite (well actually use another name) of the state style which is used also in automata library or at least append your style.

9
  1. Declare coordinates.
  2. Draw the shaded area with the help of these coordinates.
    • The bended lines are drawn in the same path as the shading
    • fill opacity makes sure that the lines aren't shaded as well.
  3. Draw nodes.
    • fill=white is used to hide the part of the shaded area.
    • \mathmakebox[\widthof{d}][c]{\vphantom{d}\c} is used to get the same size for all nodes (and needs the mathtools package).
      You also could use a combination of minimum size and inner sep, but you don't necessarily get the real minimum size. If you specify a minimum size that is smaller than the size of the biggest node, you still get differently sized nodes. If you set it to a size greater than the biggest node, you don't get the tightest possible size (which doesn't hurt necessarily).
      Additionally, node tests from nodes that are on the same vertical position use the same base line which I find more pleasing than the .centering. (One could combine the .center solution with this, of course, see below. That solution also uses \strut instead of \vphantom.)
  4. Connect nodes

Code

\documentclass[border=2pt]{standalone}
\usepackage{mathtools}
\usepackage{tikz}
\tikzset{state/.style={circle,draw=black, very thick,inner sep=3pt}}
\begin{document}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (2,0);
\coordinate (c) at (2,-1.5);
\coordinate (d) at (0,-1.5);

\path[draw,fill=gray!50,fill opacity=.5,bend right=30] (a) to (b)
                                                           to (c)
                                                           to (d)
                                                           to (a);

\foreach \c in {a,b,c,d} {
    \node [state,fill=white] at (\c) (n\c) {$\mathmakebox[\widthof{b}][c]{\vphantom{d}\c}$};
}
\draw (na) edge (nb) 
      (na) edge (nd)
      (nb) edge (nc)
      (nc) edge (nd);
\end{tikzpicture}
\end{document}

Outputs

Output

Code with .center and \strut

\documentclass[border=2pt]{standalone}
\usepackage{mathtools}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\tikzset{state/.style={circle,draw=black, very thick,inner sep=3pt}}
\begin{document}
\begin{tikzpicture}
\foreach \c/\cx/\cy in {a/0/0,
                        b/2/0,
                        c/2/-1.5,
                        d/0/-1.5
} {
    \node [state,fill=white] at (\cx,\cy) (\c) {$\mathmakebox[\widthof{d}][c]{\strut \c}$};
}
\draw (a) edge (b) 
      (a) edge (d)
      (b) edge (c)
      (c) edge (d);
\begin{scope}[on background layer]
\draw[fill=gray!50,fill opacity=.5,bend right=30] (a.center) to (b.center)
                                                             to (c.center)
                                                             to (d.center)
                                                             to (a.center);
\end{scope}
\end{tikzpicture}
\end{document}

Output with .center and \strut

Output with .center and \strut

4
  • NICE I also like \vphantom - I was trying to use \phantom to get the node sizes equal, but it was giving me whitespace as well. Props for that. Oct 23, 2012 at 1:35
  • 2
    Nice ? no ! the correct options is minimum size with inner sep here. Oct 23, 2012 at 5:40
  • @AlainMatthes It's true, it's not nice. But minimum size has its disadvantages. Oct 23, 2012 at 11:51
  • @AlainMatthes Indeed, minimum size does actually make it look nicer. I'm just excited about \vphantom solving other unrelated positioning problems. Oct 23, 2012 at 13:32

You must log in to answer this question.

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