# Improving TikZ user interface

I am looking to improve both on the concept as well as the user interface for code used to produce visual progress charts. The figure represents activities and progress for the construction of high-rise developments. A white square means the activity has not started, a green square an activity is completed and a red square an activity that is constrained for some reason and cannot be completed.

The user interface is as follows:

\floor{Level 47}{0}{0}{0}{0}{2}
\floor{Level 46}{0}{0}{0}{2}{2}
...
\floor{Level 08}{0}{0}{1}{1}{1}


This is highly repetitive, but as progress has to be reported on a floor basis I couldn't think of anything simpler and would welcome alternative solutions (0=white, 1=done, 2=constrained).

A MWE is shown below:

\documentclass[a4paper, oneside, justified=true, sfsidenotes]{tufte-book} % for a
\usepackage[utf8]{inputenc} % set input encoding to utf8
\usepackage{lipsum}
\usepackage{xcolor}
\usepackage{pgfplots}

%% Define some colors
\definecolor{activity1}{rgb}{0,1.0,0}
\definecolor{activity2}{rgb}{0,0.9,0}
\definecolor{activity3}{rgb}{0,0.85,0}
\definecolor{activity4}{rgb}{0,0.75,0}
\definecolor{activity5}{rgb}{0,0.65,0}

%% Color helper routine
\def\getcolor#1#2{%
\def\zero{0}\def\one{1}\def\two{2}
\if\zero#1 \gdef\statuscolor{white} \else \gdef\statuscolor{#2}\fi
\if\two#1  \gdef\statuscolor{red} \fi
}

\gdef\ascale{0.8}

\gdef\floor#1#2#3#4#5#6{%
\centering
\parindent0pt
\tikzpicture[scale=\ascale]
\def\posx{0.6}
\getcolor{#2}{activity1}
\draw node [anchor=south east ]{#1};
\draw[fill=\statuscolor] (0,0) rectangle (0.5,0.5);
\getcolor{#3}{activity2}
\draw[fill=\statuscolor] (\posx,0.0) rectangle (1.1,0.5);
\getcolor{#4}{activity3}
\draw[fill=\statuscolor] (2*\posx,0.0) rectangle (1.7,0.5);
\getcolor{#5}{activity4}
\draw[fill=\statuscolor] (3*\posx,0.0) rectangle (2.3,0.5);
\getcolor{#6}{activity5}
\draw[fill=\statuscolor] (4*\posx,0.0) rectangle (2.9,0.5);
\endtikzpicture
}

\begin{document}
\input{planning}
\end{document}


The input document is:

\chapter{Planning}
\begin{marginfigure}
The Tower can be represented by a series of squares, which denote an activity. Green
is done and white is not done. There is no need to use intermediate colors as they
would add to the visual clutter.

\vspace{1cm}
\floor{Level 47}{0}{0}{0}{0}{2}
\floor{Level 46}{0}{0}{0}{2}{2}
\floor{Level 45}{0}{0}{1}{1}{1}
\floor{Level 44}{0}{0}{1}{1}{1}
\floor{Level 43}{1}{1}{1}{1}{1}
\floor{Level 42}{1}{1}{1}{1}{1}
\floor{Level 41}{0}{1}{1}{0}{1}
\floor{Level 40}{0}{1}{1}{1}{1}
\floor{Level 39}{0}{1}{1}{1}{1}
\floor{Level 38}{0}{1}{1}{1}{1}
\floor{Level 37}{0}{0}{1}{1}{1}
\floor{Level 36}{0}{0}{0}{1}{1}
\floor{Level 35}{0}{0}{0}{1}{1}
\floor{Level 34}{0}{0}{0}{1}{1}
\floor{Level 33}{0}{0}{0}{1}{1}
\floor{Level 32}{0}{0}{0}{1}{1}
\floor{Level 31}{0}{0}{0}{1}{1}
\floor{Level 30}{0}{0}{0}{1}{1}
\floor{Level 29}{1}{1}{1}{1}{1}
\floor{Level 28}{1}{1}{1}{1}{1}
\floor{Level 27}{0}{1}{1}{1}{1}
\floor{Level 26}{0}{1}{1}{1}{1}
\floor{Level 25}{0}{0}{1}{1}{1}
\floor{Level 24}{0}{0}{1}{1}{1}
\floor{Level 23}{0}{0}{1}{1}{1}
\floor{Level 22}{0}{0}{1}{1}{1}
\floor{Level 21}{0}{0}{1}{1}{1}
\floor{Level 20}{0}{0}{1}{1}{1}
\floor{Level 19}{0}{0}{1}{1}{1}
\floor{Level 18}{0}{0}{1}{1}{1}
\floor{Level 17}{0}{0}{1}{1}{1}
\floor{Level 16}{0}{0}{1}{1}{1}
\floor{Level 15}{0}{1}{1}{1}{1}
\floor{Level 14}{0}{1}{1}{1}{1}
\floor{Level 12}{0}{1}{1}{1}{1}
\floor{Level 11}{0}{1}{1}{1}{1}
\floor{Level 10}{1}{1}{1}{1}{1}
\floor{Level 09}{1}{1}{1}{1}{1}
\floor{Level 08}{1}{1}{1}{1}{1}
\caption{Shangri-la Tower Progress, each square represents one separate activity.}
\end{marginfigure}
\lipsum[1-3]


To summarize, are there any better alternatives to the user interface? What changes to the floor routine would you recommend? Where would you suggest a legend be positioned and how?

-
Other than doing the 'Level X' with a \foreach loop I don't really see much room for improvement, since the status boxes of the levels are quite random. Or are there any constraints that might be expressed algorithmically? –  Count Zero Oct 1 '11 at 19:42
@Count Zero Thanks, but a \foreach cannot be applied, since the user will still need to input the progress. All statuses are inputted by the user - as is - is error prone and hence my question here. –  Yiannis Lazarides Oct 1 '11 at 19:53
Then we agree. :) –  Count Zero Oct 1 '11 at 20:02
@Count Zero ... we do, but I am sure that there is some kaizen somewhere in there! –  Yiannis Lazarides Oct 1 '11 at 20:06
All I can think of is to put in some error checking: For example, If the last 4 parameters passed to \floor should always be increasing (ie, that saying \floor{Level 47}{1}{0}{0}{0}{2} would not be valid), you could put a check in the code to ensure that. And you could also put in check to make sure that all the floor numbers were valid and all were specified. –  Peter Grill Oct 1 '11 at 21:25

Here's my suggestion. At the start of the floor bit, turn TeX into a parser. Each line is then a whitespace separated list of 0,1,2s which turn into the floor syntax. At the start of each line, we look to see if we should stop parsing, otherwise we assume that the next line is a new floor. Since the floors are specified from top to bottom, we have to know the number of floors at the outset (though we could save that in the aux file and restore it on next run). Otherwise, the syntax seems a bit cleaner this way and thus less prone to errors. However, this doesn't do any error checking. One way to do that would be to make the newline character active and use that as a separator between the floors.

Here's the code (I included the input file into the main document).

\documentclass[a4paper, oneside, justified=true, sfsidenotes]{tufte-book} % for a
\usepackage[utf8]{inputenc} % set input encoding to utf8
\usepackage{lipsum}
\usepackage{xcolor}
\usepackage{pgfplots}

%% Define some colors
\definecolor{activity1}{rgb}{0,1.0,0}
\definecolor{activity2}{rgb}{0,0.9,0}
\definecolor{activity3}{rgb}{0,0.85,0}
\definecolor{activity4}{rgb}{0,0.75,0}
\definecolor{activity5}{rgb}{0,0.65,0}

%% Color helper routine
\def\getcolor#1#2{%
\def\zero{0}\def\one{1}\def\two{2}
\if\zero#1 \gdef\statuscolor{white} \else \gdef\statuscolor{#2}\fi
\if\two#1  \gdef\statuscolor{red} \fi
}

\gdef\ascale{0.8}
\def\posx{0.6}
\def\posy{0.5}

\newcounter{floor}
\newcommand{\NumberOfFloors}[1]{\def\MaxFloors{#1}\setcounter{floor}{#1}}

\makeatletter
\def\isfloor{%
\@ifnextchar\stopfloors{}{\floor}}
\makeatother

\gdef\floor#1 #2 #3 #4 #5 {%
\pgfmathsetmacro{\yshift}{-\posy * (\MaxFloors - \value{floor})}
\getcolor{#1}{activity1}
\node[anchor=south east] at (0,\yshift) {Level \thefloor};
\draw[fill=\statuscolor] (0,\yshift) rectangle +(0.5,0.5);
\getcolor{#2}{activity2}
\draw[fill=\statuscolor] (\posx,\yshift) rectangle +(0.5,0.5);
\getcolor{#3}{activity3}
\draw[fill=\statuscolor] (2*\posx,\yshift) rectangle +(0.5,0.5);
\getcolor{#4}{activity4}
\draw[fill=\statuscolor] (3*\posx,\yshift) rectangle +(0.5,0.5);
\getcolor{#5}{activity5}
\draw[fill=\statuscolor] (4*\posx,\yshift) rectangle +(0.5,0.5);
\isfloor}

\let\startfloors=\floor
\let\stopfloors=\relax

\begin{document}
\chapter{Planning}
\begin{marginfigure}
The Tower can be represented by a series of squares, which denote an activity. Green
is done and white is not done. There is no need to use intermediate colors as they
would add to the visual clutter.

\NumberOfFloors{47}
\begin{tikzpicture}
\startfloors
0  0  0  0  2
0  0  0  2  2
0  0  1  1  1
0  0  1  1  1
1  1  1  1  1
1  1  1  1  1
0  1  1  0  1
0  1  1  1  1
0  1  1  1  1
0  1  1  1  1
0  0  1  1  1
0  0  0  1  1
0  0  0  1  1
0  0  0  1  1
0  0  0  1  1
0  0  0  1  1
0  0  0  1  1
0  0  0  1  1
1  1  1  1  1
1  1  1  1  1
0  1  1  1  1
0  1  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  0  1  1  1
0  1  1  1  1
0  1  1  1  1
0  1  1  1  1
0  1  1  1  1
1  1  1  1  1
1  1  1  1  1
1  1  1  1  1
\stopfloors
\end{tikzpicture}
\caption{Shangri-la Tower Progress, each square represents one separate activity.}
\end{marginfigure}
\lipsum[1-3]
\end{document}


The output is pretty similar to what you posted, though slightly less compact. This is presumably due to some spacing stuff that I've introduced somewhere. That could obviously be tweaked.

-
Thanks for posting a solution. It is certainly much better than mine. There is though one issue that still needs to be addressed from a practical point of view. These charts are produced weekly (i.e progress reported weekly), so if one had to change the chart say on floor 12 you will need to manually count to locate the row - although this can be solved by adding the floor as a comment on the row. After I posted the question, also tried this: \floor 47;00011; it saved all the curly brackets. It also allows for use of labels rather than floor say "roof" or "mechanical floor". –  Yiannis Lazarides Oct 4 '11 at 10:20
@YiannisLazarides: Adapt as needed! It be possible to have an optional first argument that looked for a label, or something like that. Refining it further would need a greater understanding of how the data would be produced. –  Loop Space Oct 4 '11 at 10:42
Thanks! This is one of the last bastions of excel and I am keen to get rid of it:) –  Yiannis Lazarides Oct 4 '11 at 10:58

I have a different suggestion. You could define three macros, say \undone, \finished and \constrained, each of them taking two arguments: a number of floor and a number of task, and set a flag for that floor and that task to 0, 1 or 2. You would just start by initializing everything to "undone", and when sat task 3 on floor 15 was completed, you would put \finished{15}{3} at the end of your list. TeX would then take care about organizing the information by floors.

If you added dates when entering the information, perhaps as comments, you would also have a complete log of the progress.

It could be done in some way along these lines:

\documentclass[a4paper, oneside, justified=true, sfsidenotes]{tufte-book}
\usepackage[utf8]{inputenc} % set input encoding to utf8
\usepackage{lipsum}
\usepackage{xcolor}
\usepackage{pgfplots}

%% The commands to enter information:

\newcommand\initfloor[1]{%
\undone{#1}{1}%
\undone{#1}{2}%
\undone{#1}{3}%
\undone{#1}{4}%
\undone{#1}{5}%
}

%% Define some colors
\definecolor{activity1}{rgb}{0,1.0,0}
\definecolor{activity2}{rgb}{0,0.9,0}
\definecolor{activity3}{rgb}{0,0.85,0}
\definecolor{activity4}{rgb}{0,0.75,0}
\definecolor{activity5}{rgb}{0,0.65,0}

%% Color helper routine
\def\getcolor#1#2{%
\def\zero{0}\def\one{1}\def\two{2}
\if\zero#1 \gdef\statuscolor{white} \else \gdef\statuscolor{#2}\fi
\if\two#1  \gdef\statuscolor{red} \fi
}

\gdef\ascale{0.8}

\gdef\floor#1{%
\centering
\parindent0pt
\tikzpicture[scale=\ascale]
\def\posx{0.6}
\draw node [anchor=south east ]{\hbox to 5em{\hfil #1}};
\draw[fill=\statuscolor] (0,0) rectangle (0.5,0.5);
\draw[fill=\statuscolor] (\posx,0.0) rectangle (1.1,0.5);
\draw[fill=\statuscolor] (2*\posx,0.0) rectangle (1.7,0.5);
\draw[fill=\statuscolor] (3*\posx,0.0) rectangle (2.3,0.5);
\draw[fill=\statuscolor] (4*\posx,0.0) rectangle (2.9,0.5);
\endtikzpicture
}

\initfloor{1}
\initfloor{2}
\initfloor{third}

%% This could be in a separate file:
\finished{2}{3}
\finished{third}{5}
\constrained{1}{1}
%%

\begin{document}
\chapter{Planning}

\begin{marginfigure}
The Tower can be represented by a series of squares, which denote an activity. Green
is done and white is not done. There is no need to use intermediate colors as they
would add to the visual clutter.

\vspace{1cm}
\floor{third}
\floor{2}
\floor{1}
\end{marginfigure}
\lipsum[1-3]

\end{document}

-
Thanks. I like the idea of the log and could easily be incorporated as an optional field and picked up from a single entry field when entering the info. I had a very similar idea earlier, but with tables. Thinking aloud, it would even be easier as a list by activity rather than floor \activity{1}{3,8,32,33}. This would limit any update in the example to five lines. –  Yiannis Lazarides Oct 4 '11 at 18:31