8

I have been trying to fill an grid with "binary" type data, so you iterate through an array that is 2D and these 1s and 0s decide if a location is filled or not. This is my first foray into programming, as such, in LaTeX.

I can successfully create fill a single box with the macro that I made, but I have not been able to automate it. \gridbox is my macro, and it will put a box where I want it. As an example of what I want to do, I have an array here:

\def\BITARRAY{{
{1,0,0,1},
{1,0,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,0,1}
}} 

I have the \foreach working so I can look at each piece of data. I want to test it so that if there's a "1", I put the the box at the location.

The problem: my \ifthenelse does not like my macro. The minimal TeX code follows, sorry that it's long, but I decided to leave my comments in it.

\documentclass[journal,letterpaper]{IEEEtran}
\usepackage{fp}   
\usepackage{tikz,ifthen}  

\begin{document}
\newcommand\bitfieldwidth{4}  
\newcommand\numberofrounds{5}

\makeatletter
%divide the column width by the bitfield width to figure out how many boxes we need
\FPeval{\blockwidth}{((\strip@pt\columnwidth) / \bitfieldwidth)}
%truncate the decimals
\FPtrunc\blockwidth{\blockwidth}{0}
%get a whole number, but then truncate it anyway
\FPmul\gridwidth{\bitfieldwidth}{\blockwidth}
\FPtrunc\gridwidth{\gridwidth}{0}
\FPmul\gridheight{\numberofrounds}{\blockwidth}
\FPtrunc\gridheight{\gridheight}{0}
\makeatother

% this creates a box at the appropriate location
% It just uses the index and not a location, so it can scale with 
% the type of grid width
\newcommand{\gridbox}[2]{  %you pass the x,y of the lower location.
\fill [orange] ( #1 * \blockwidth pt , -  #2 * \blockwidth pt ) rectangle (#1 * \blockwidth + \blockwidth pt, - #2 * \blockwidth + \blockwidth pt)
}

\def\BITARRAY{{
{1,0,0,1},
{1,0,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,0,1}
}} 

\begin{tikzpicture}
\draw[step=\blockwidth pt,gray,very thin] (0pt,0pt) grid (\gridwidth pt,-   \gridheight pt);
\foreach \y in {1,...,\numberofrounds}{  %the y is going down the array, the x is left to right
    \foreach \x in {1,...,\bitfieldwidth}{
    %\pgfmathtruncatemacro{\resultbit}{int(\BITARRAY[\y-1][\x-1])}
    %\ifthenelse{\value{1}<1}{\gridbox{\y}{\x};}{\gridbox{\y}{\x};};
    }
}
\gridbox{1}{2};
\end{tikzpicture}

\end{document}

question: How must I format the \ifthenelse macro to take my macro that will draw a box. With that \ifthenelse commented out, I can show that the grid box macro works. The image follows. the macro works

Any suggestions would be greatly appreciated.

5

You can't say

\ifthenelse{\value{1}<1}{...

because 1 is not a counter and \value{} takes the name of a counter as its argument. This is why you get strange errors complaining about not being about to use \else after \the. The value of the counter mycounter is typeset using \themycounter so \value{1} tries something like \the1 which doesn't make sense.

If you replace the 1 with the name of a counter, it will work. For example,

 \ifthenelse{\value{page}<1}{%

works because page is a counter. Obviously it isn't a very interesting counter for this purpose as the condition with either be satisfied for every case or fail for every case, since the page number won't vary within the picture.

Note that, since you are using TikZ anyway, it would probably be easier to use TikZ's facilities for maths. However, I wanted to demonstrate how to minimally modify the code, so I've tried to change it as little as possible for the most part.

\documentclass[tikz,border=10pt,multi]{standalone}
\usepackage{fp}
\usepackage{ifthen}

\begin{document}
\newcommand\bitfieldwidth{4}
\newcommand\numberofrounds{5}

\makeatletter
%divide the column width by the bitfield width to figure out how many boxes we need
\FPeval{\blockwidth}{((\strip@pt\columnwidth) / \bitfieldwidth)}
%truncate the decimals
\FPtrunc\blockwidth{\blockwidth}{0}
%get a whole number, but then truncate it anyway
\FPmul\gridwidth{\bitfieldwidth}{\blockwidth}
\FPtrunc\gridwidth{\gridwidth}{0}
\FPmul\gridheight{\numberofrounds}{\blockwidth}
\FPtrunc\gridheight{\gridheight}{0}
\makeatother

% this creates a box at the appropriate location
% It just uses the index and not a location, so it can scale with
% the type of grid width
\newcommand{\gridbox}[2]{%you pass the x,y of the lower location.
\fill [orange] (#1*\blockwidth pt, -#2*\blockwidth pt) rectangle ++(-\blockwidth pt,\blockwidth pt)}

\def\BITARRAY{{%
{1,0,0,1},
{1,0,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,0,1}%
}}

\begin{tikzpicture}
  \draw[step=\blockwidth pt,gray,very thin] (0pt,0pt) grid (\gridwidth pt,-\gridheight pt);
  \foreach \y in {1,...,\numberofrounds}{%the y is going down the array, the x is left to right
    \foreach \x in {1,...,\bitfieldwidth}{%
      \pgfmathtruncatemacro{\resultbit}{int(\BITARRAY[\y-1][\x-1])}
      \ifthenelse{\resultbit<1}{%
        \gridbox{\x}{\y};
      }{};
    }
  }
  \gridbox{1}{2};
\end{tikzpicture}

\end{document}

orange blocks in grid

7

The macro \BITARRAY has already almost the form, how it can be easily used by TikZ's \foreach. The additional surrounding braces are not needed and there should not be a space after the last element.

First the cells with values 1 are filled. During the loop, the values of \x and \y are stored in global variables to have the maximum values after the loops. \foreach works inside a group, thus the meaning of \x and \y are lost afterwards.

Then the grid is drawn. Since the rows are drawn from the top, negative values are used for the y coordinate.

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \def\BITARRAY{
    {1,0,0,1},
    {1,0,0,0},
    {0,1,0,0},
    {0,1,1,0},
    {0,0,0,1}%
  }
  \fill[orange]
    \foreach \row [count=\y] in \BITARRAY {
      \foreach \cell [count=\x] in \row {
        \ifnum\cell=1 %
          (\x-1, -\y+1) rectangle ++(1, -1)
        \fi
        \pgfextra{%
          \global\let\maxx\x
          \global\let\maxy\y
        }%
      }
    }
  ;
  \draw[thin] (0, 0) grid[step=1] (\maxx, -\maxy);
\end{tikzpicture}
\end{document}

Result

  • I like this one. Can we put the bit value in the square also? – tjt263 Aug 4 at 21:37
  • @tjt263 Add the line (\x - .5, -\y + .5) node[text=black] {\cell} between \fi and \pgfextra. – Heiko Oberdiek Aug 9 at 19:51
4

A TeX-y solution (built on Heiko's) using \csname, since your data contains only two different values :

\documentclass{article}
\usepackage{tikz}
\begin{document}

\expandafter\def\csname box0\endcsname#1#2{}
\expandafter\def\csname box1\endcsname#1#2{\fill[orange] (#1-1, -#2+1) rectangle ++(1, -1);}

\begin{tikzpicture}
  \def\BITARRAY{
    {1,0,0,1},
    {1,0,0,0},
    {0,1,0,0},
    {0,1,1,0},
    {0,0,0,1}%
  }

    \foreach \row [count=\y] in \BITARRAY {
      \foreach \cell [count=\x] in \row {
        \csname box\cell\endcsname{\x}{\y}
        \pgfextra{%
          \global\let\maxx\x
          \global\let\maxy\y
        }%
      }
    }
  \draw[thin] (0, 0) grid[step=1] (\maxx, -\maxy);
\end{tikzpicture}
\end{document}
  • Although I accepted the other one, mainly because it was closest to my understanding of how I wanted things to work; your version is by far the fastest. On my matrix of 64x32 bits, this renders in seconds whereas the other renders in many minutes. – b degnan Jun 19 '16 at 1:35
  • 1
    @b degnan I did some benchmarking with a 100x100 array : Heiko's solution took 7.14 s, mine 7.82 s. I had to interrupt cfr's solution after a few minutes. Even when setting macros \bitfieldwidth and \numberofroundsto small values, it took a horrendously long time to compile. It seems to me that pgf's array access [] is catastrophic for long arrays. – Christoph Frings Jun 19 '16 at 10:19
3

For fun, here is a LuaTeX based solution. I am using ConTeXt + MetaPost because I am more familiar with those, but the code could be translated to LaTeX + TikZ (or LaTeX + MetaPost).

\startluacode
  local metafun = context.metafun

  thirddata = thirddata or {}
  thirddata.ShowBitArray = function(bitarray)
      local hsize    = string.todimen(tex.get("hsize"))
      local nofcells = #bitarray[1]
      local width    = hsize/nofcells

      local drawCell = function(x,y,bit)
        if bit == 1 then
          metafun("fill cell shifted (%d*width, -%d*width) withcolor \\MPcolor{orange};", x, y)
        end
        metafun("draw cell shifted (%d*width, -%d*width) ; ", x, y)
      end

      metafun.start()
      metafun("newnumeric width; width := %s;", number.topoints(width))
      metafun("newpath cell; cell := unitsquare xyscaled(width, width);")
      for i = 1, #bitarray do 
        local cell = bitarray[i]
        for j = 1, #cell do 
          local value = cell[j]
          drawCell(j,i,value)
        end
      end
      metafun.stop()
  end
\stopluacode

\define[1]\ShowBitArray
    {\ctxlua{thirddata.ShowBitArray({#1})}}

\starttext
\ShowBitArray{{1,0,0,1}, {1,0,0,0}, {0,1,0,0}, {0,1,1,0}, {0,0,0,1} } 
\stoptext

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.