Table with small circles on each intersection/corner

I'm a LyX user and have a small question -

How do I incorporate to my a document a table consists only of the numbers {0,1}, with borders between the cells, and with small colored circles on the intersections/corners that indicates if the number of 1's touching the intersection/corner is odd or even.

Thanks!

-
Oh, I'll compute it myself... :-) –  Amihai Zivan Feb 21 '13 at 14:31
Thank you for posting this question. It served me as an excuse to finally do some stuff with lualatex, and I've found it too much fun! –  JLDiaz Feb 21 '13 at 18:28

Update!

I decided to learn a bit of lua and translate to LuaLaTeX my pythonic solution. You can jump directly to the section LuaLaTeX if you are not interested in python.

Original answer

I would use tikz to draw the grid, put the 1's and 0's on it, and painting the small circles. However it remains the problem of computing the color of each circle.

Although this can be surely done in pure latex, I think it is a daunting task, because LaTeX does not provide data structures to hold bidimiensional arrays which are needed for this problem. I think that a LuaTeX solution (mixed with tikz output) would be the best way, but I lack the skills to provide such a solution. I would be happy to see one.

In the meantime, I wrote python script which outputs the required tikz code.

Data:

data = [
[ 0, 1, 0, 0, 0, 1 ],
[ 1, 1, 1, 0, 1, 1 ],
[ 0, 1, 0, 1, 1, 0 ],
[ 0, 0, 1, 1, 0, 0 ],
[ 1, 0 ,0, 0, 1, 1 ]
]


Code (python, sorry):

# Put here your data. You can add/remove rows and columns as required
# The code will self-adapt to the size of this matrix.
# Only requirement is that all rows should have the same length
data = [
[ 0, 1, 0, 0, 0, 1 ],
[ 1, 1, 1, 0, 1, 1 ],
[ 0, 1, 0, 1, 1, 0 ],
[ 0, 0, 1, 1, 0, 0 ],
[ 1, 0 ,0, 0, 1, 1 ]
]

# A little cheating...
# Next table will be equal to the one named "data", but padded
# with zeros at its contour
padded_data = []
width = len(data[0])+2
padded_data.append([0]*width)  # First row of zeros
for row in data:
padded_data.append([0] + row + [0]) # Middle rows
padded_data.append([0]*width)  # Last row of zeros

# This simplifies the implementation of the next fundamental function
def parity(y, x):
"""
This function computes the parity of a intersection/corner.
The intersection is located at the edge between rows and colums
so that (y,x) is the intersection of the line which separates
rows (y-1) and (y), and the line which separates columns (x-1)
and x, considering that column/row with index -1 is out of the matrix,
so that (0,0) represents the upper left corner of the table.
(in other words, the first cell of the table has the four corners
(0,0), (0,1), (1,1) and (1,0) in clockwise order).

The parity of an intersection/corner is defined as the parity
of the sum of the cells which have that corner. For intersections
"inside" the matrix, each one has four "touching" cells.
For intersections in the border of the matrix, each one has
two "touching" cells, except for the intersections at the border
of the matrix which have a single "touching" cell.
"""
# We use padded data instead of data to make easier the computations
s = padded_data[y][x] + padded_data[y][x+1] + \
padded_data[y+1][x] + padded_data[y+1][x+1]
return s%2

# Given the data matrix, compute parity matrix
parity_matrix = []

for y in range(len(data)+1):
row = []
for x in range(len(data[0])+1):
row.append(parity(y,x))
parity_matrix.append(row)

# All required data is available. The figure can be drawn
width=len(parity_matrix[0])
height=len(parity_matrix)

print r"\begin{tikzpicture}[y=-.5cm, x=.5cm]"

# Draw the grid
for y in range(height):
print r"\draw[lines] (0,%d) -- (%d,%d);" % (y, width-1, y)
for x in range(width):
print r"\draw[lines] (%d,0) -- (%d,%d);" % (x, x, height-1)

# Draw the coloured dots at the intersections
for y in range(height):
for x in range(width):
if parity_matrix[y][x] == 1:
style="odd"
else:
style="even"
print r"\fill[%s] (%d,%d) circle(3pt);" % (style, x, y)

# Put the numbers at the cells
for y in range(len(data)):
for x in range(len(data[0])):
print r"\node[digit] at (%f, %f) {%d};" % (x+0.5, y+0.5, data[y][x])

# That's it!
print r"\end{tikzpicture}"


Latex document:

\documentclass{article}
\usepackage{tikz}
\tikzset{
even/.style =  {fill=green, opacity=0.5},
odd/.style = {fill=red, opacity=0.5},
digit/.style = {font=\ttfamily},
lines/.style = {black}
}
\begin{document}
\input mygrid.tex
\end{document}


How:

Save the python code as compute-grid.py, and the .tex code as document.tex, for example. At a terminal do:

$python compute_grid.py > mygrid.tex$ pdflatex document.tex


LuaLaTeX

The above python code can be easily ported to Lua (although the array indexes starting in 1 instead of 0 gave me some headaches). Since LuaLaTeX can execute Lua code embedded in a latex document, the following solution can be coded completly in a single file, but I preferd to separate it in two files in orde to "declutter" the main document.

This is the main document:

\documentclass{article}
\usepackage{fontspec}
\usepackage{luacode}
\usepackage{tikz}

\tikzset{
digit/.style = {font=\ttfamily},
line/.style  = {black},
even/.style  = {fill=green, opacity=.5},
odd/.style   = {fill=red, opacity=.5}
}

\luaexec{ dofile("luafunctions.lua") }
\def\DrawGrid#1{ \luaexec{ data = {#1}; draw_grid(data) } }

\begin{document}
\begin{tikzpicture}[x=0.5cm, y=-0.5cm]
\DrawGrid{
{ 0, 1, 0, 0, 0, 1 },
{ 1, 1, 1, 0, 1, 1 },
{ 0, 1, 0, 1, 1, 0 },
{ 0, 0, 1, 1, 0, 0 },
{ 1, 0 ,0, 0, 1, 1 }
}
\end{tikzpicture}
\end{document}


This is the content of luafunctions.lua file:

function pad_array(data)
-- Create the padded array
padded = {}
width  = #data[1] + 2
height = #data    + 2
rowzeros = {}
for i=1,width,1 do
rowzeros[i] = 0
end
padded[1] = rowzeros
padded[height] = rowzeros
for i,v in ipairs(data) do
row = {}
row[1] = 0
for j,n in ipairs(v) do
row[j+1] = n
end
row[width] = 0
padded[i+1] = row
end
return padded
end

function parity(data, y, x)
-- Returns the parity of the intersection (y,x)
return(data[y][x] + data[y+1][x+1] + data[y+1][x] + data[y][x+1])%2
end

function compute_parity_matrix(data)
-- Computes the parity matrix
m = {}
padded = pad_array(data)
width = #padded[1]
height = #padded
for y=1,height-1,1 do
m[y]={}
for x=1,width-1,1 do
m[y][x] = parity(padded, y, x)
end
end
return m
end

function draw_grid(d)
-- Print the numbers
for y,v in ipairs(d) do
for x,n in ipairs(v) do
tex.print(string.format("\\node[digit] at (%f,%f) {%d};", x-.5, y-.5, n))
end
end

-- Draw the grid around
width = #d[1]
height = #d

for y=0,height,1 do
tex.print(string.format("\\draw[line] (0,%d) -- (%d,%d);", y, width, y))
end
for x=0,width,1 do
tex.print(string.format("\\draw[line] (%d,0) -- (%d,%d);", x,x,height))
end

-- compute parity matrix
pm = compute_parity_matrix(d)

-- use it to draw dots at intersections
for y, row in ipairs(pm) do
for x, parity in ipairs(row) do
if parity==1 then
style = "odd"
else
style = "even"
end
tex.print(string.format("\\fill[%s] (%d,%d)  circle(3pt);", style, x-1, y-1))
end
end
end


After compiling the main document with lualatex command, you get a pdf with the same result than in the python version:

-
Awesome! Notice that for each row and column of intersections there's a even number of reds. :-) –  Amihai Zivan Feb 21 '13 at 18:17
@AmihaiZivan I've updated the answer with a LuaLaTeX version –  JLDiaz Feb 21 '13 at 18:31
It would also be possible to paste the original Python solution into a TeX document, using my pythontex package. –  G. Poore Feb 21 '13 at 23:04