[Tex/LaTex] Drawing Brillouin Zones in TikZ

tikz-pgf

I'm trying to get TikZ to draw Brillouin Zones for two dimensional lattices, but I've not had much luck so far with anything I've tried.
Brillouin zones are, basically, the area of space around a point in the lattice which is closest to that point, and so are similar to Voronoi diagams.
You can see what these look like in this image, which has the first, second, and third Brillouin zones drawn on:
first three brillouin zones
(source: eelvex.net)

If I were to draw this by hand I'd construct the first Brillouin zone by drawing a line from the centre point in the lattice out to each of the points a distance of 1 (taking the lattice points to be separated by a unit distance) from the centre point, so that would be the ones north, east, south and, west of it. Then I'd take the perpendicular bisectors (in physics parlance these are Bragg planes) of each of these lines. The first Brillouin zone would then be the area which is enclosed by the perpendicular bisectors (and is the grey area shaded on the image above). The higher zones are constructed in the same way; a slightly variant explanation is given on this site. It also has code in Asymptote, but it seems to just be hard-coded in.

Is there a way that this can be done in TikZ, and is there a way that it can be done both for any given lattice, and for any number of Brillouin zones?
I've tried a few approaches, without success, including various attempts to do it with Lua (which I'm very unproficient in), but the only thing I really have working is a way of drawing the lattice points, which I've included here for a square lattice, but it's not too difficult to modify this to give a hexagonal lattice.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
%%% For a square lattice
\begin{tikzpicture}
\foreach \x in {-2, ..., 2}{
    \foreach \y in {-2, ..., 2}{
        \fill [black] (\x, \y) circle (0.1);
    }   
}
\end{tikzpicture}
\end{document}

Best Answer

Is there a way that this can be done in TikZ, and is there a way that it can be done both for any given lattice, and for any number of Brillouin zones?

Short answer: Yes!

Longer answer: No! This task can be computationally really expensive for high values. You should separate the calculation from display because (I suppose) you don't want to start from scratch each and every time when you compile. And if you do that separately, why don't you use a better suited tool for that which can easily store and use previously calculated stuff for any given lattice?!


Longer answer on the "Yes" part. Here's algorithm which might just work and can be implemented in LaTeX (but really shouldn't). It's a bit "hacky" because this description doesn't really imply a "closed formula" for coloring (and maybe there isn't one).

I think the following should work. And this statement is based on the inductive nature of the algorithm.

  1. Calculate the perpendicular bisectors.
  2. Calculate the intersection (if there is one) for every pair of bisectors.
  3. Create a data structure which is indexed by the intersections and has a list for each with the lines going through it.
  4. Create an edge list from the inner square (k=0 square). Let k=1.
  5. Based on the edge list and intersections (here comes the endless fun with vectors and their cross products) start on every edge on the hull CW (clockwise) (CCW if k is even), and go leftmost (rightmost if k is even) each and every time, until you get back. Now you a have an area for k. Do that for every edge. Store the new (hull) edge list, and use that in the next iteration. You can decide where can you go from an intersection and which is the leftmost (rightmost) using the index created in step 3.
  6. Increase k by one and repeat 5-6 as long as you want.

This can be improved algorithmically in several ways but should get you started.

After you've succesfully calculated the data points, you could just draw the shapes using tikz commands like this: \fill[red] (0,-1) -- (1,0) -- (0,1) -- (-1,0) -- cycle;

Another possibility is to have the lines and calculate a color pixel-by-pixel for the (bitmap) image. This can be much better if you have a large k but a small picture.


I've offered a bounty on your question hoping for a better approach than the above. No luck.