Macro for drawing stacks of colored horizontal rectangles

With expl3 and tikz.

Commands

  • \fancyblock receives a 2-dim array to construct blocks. Fill color is random by default (use random=false to change it), and you can set the path style by [<style>] or set the fill color quickly by |<color>|.
\fancyblock[
  at={(8, 0)}, name=b, random=false,
  transpose, y=0.5cm,
  block={
    very thick,
    line width=1pt,
    draw=teal,
  }
]{
  {2, 1, 1},
  {1, [fill=red]2, |green|1},
  {1, 3, {[line width=2pt, draw=black]|teal|1}}
}
  • \randomblock receives an 1-dim array to draw random blocks, with the length of every row be the corresponding element of the array.
\randomblock{4, 5, 4}
  • \randomrectangle receives two number, which are the width and height of the rectangle that is composed by multiple random blocks.
\randomrectangle{4}{7}

enter image description here

Complete Code

\documentclass[tikz, border=1cm]{standalone}
\usepackage{xparse}

\ExplSyntaxOn
\makeatletter

\tl_new:N   \l__at_tl
\tl_new:N   \l__name_tl
\tl_new:N   \l__anchor_tl
\tl_new:N   \l__block_style_tl
\dim_new:N  \l__block_wd_dim
\dim_new:N  \l__x_coor_dim
\dim_new:N  \l__y_coor_dim
\dim_new:N  \l__block_x_unit_dim
\dim_new:N  \l__block_y_unit_dim
\int_new:N  \l__remain_int
\int_new:N  \l__temp_int
\bool_new:N \l__random_bool
\bool_new:N \l__transpose_bool

\keys_define:nn { fancyblock }
  {
    at          . tl_set:N   = \l__at_tl,
    name        . tl_set:N   = \l__name_tl,
    random      . bool_set:N = \l__random_bool,
    random      . default:n  = true,
    transpose   . bool_set:N = \l__transpose_bool,
    transpose   . default:n  = true,
    x           . dim_set:N  = \l__block_x_unit_dim,
    y           . dim_set:N  = \l__block_y_unit_dim,
    unit        . code:n     =
      {
        \dim_set:Nn \l__block_x_unit_dim { #1 }
        \dim_set:Nn \l__block_y_unit_dim { #1 }
      },
    block       . code:n     =
      {
        \tl_put_right:Nn \l__block_style_tl { ,#1 }
      },
    anchor      . choice:,
    anchor / l  . code:n     = { \tl_set:Nn \l__anchor_tl { west } },
    anchor / r  . code:n     = { \tl_set:Nn \l__anchor_tl { east } },
    anchor / t  . code:n     = { \tl_set:Nn \l__anchor_tl { north } },
    anchor / b  . code:n     = { \tl_set:Nn \l__anchor_tl { south } },
    anchor / lb . code:n     = { \tl_set:Nn \l__anchor_tl { south~west } },
    anchor / bl . code:n     = { \tl_set:Nn \l__anchor_tl { south~west } },
    anchor / lt . code:n     = { \tl_set:Nn \l__anchor_tl { north~west } },
    anchor / tl . code:n     = { \tl_set:Nn \l__anchor_tl { north~west } },
    anchor / rb . code:n     = { \tl_set:Nn \l__anchor_tl { south~east } },
    anchor / br . code:n     = { \tl_set:Nn \l__anchor_tl { south~east } },
    anchor / rt . code:n     = { \tl_set:Nn \l__anchor_tl { north~east } },
    anchor / tr . code:n     = { \tl_set:Nn \l__anchor_tl { north~east } },
  }

\NewDocumentCommand { \randomblock } { O{} m }
  {
    \generate_num_matrix:n { #2 }
    \fancyblock[#1]{\clist_use:Nn \l__matrix_clist {,}}
  }

\NewDocumentCommand { \randomrectangle } { O{} m m }
  {
    \seq_clear:N \l_tmpa_seq
    \int_step_inline:nn { #2 }
      {
        \seq_put_right:Nn \l_tmpa_seq { #3 }
      }
    \generate_num_matrix:x
      {
        \seq_use:Nn \l_tmpa_seq { , }
      }
    \fancyblock[#1]{\clist_use:Nn \l__matrix_clist {,}}
  }

\cs_new_protected:Nn \generate_num_matrix:n
  {
    \clist_clear_new:N \l__matrix_clist
    \clist_map_inline:nn { #1 }
      {
        \generate_num_seq:n { ##1 }
        \clist_put_right:Nx \l__matrix_clist
          {
            { { \clist_use:Nn \l__row_clist {,} } }
          }
      }
  }
\cs_generate_variant:Nn \generate_num_matrix:n { x }

\cs_new_protected:Nn \generate_num_seq:n
  {
    \clist_clear_new:N \l__row_clist
    \int_set:Nn \l__remain_int { #1 }
    \int_while_do:nn { \l__remain_int > 0 }
      {
        \int_set:Nn \l__temp_int {
          \int_rand:n { \l__remain_int }
        }
        \int_add:Nn \l__remain_int { -\l__temp_int }
        \clist_put_right:Nx \l__row_clist { \int_use:N \l__temp_int }
      }
  }

\NewDocumentCommand { \fancyblock } { O{} m }
  {
    \tl_clear:N \l__block_style_tl
    \keys_set:nn { fancyblock }
      {
        at        = { (0, 0) },
        block     = { draw, thick },
        unit      = 1cm,
        transpose = false,
        anchor    = lb,
        name      = block,
        random,
        #1
      }
    \draw_block_matrix:x { #2 }
  }

\cs_new_protected:Nn \draw_block_matrix:n
  {
    \clist_set:Nn \l_tmpa_clist { #1 }
    \bool_if:NTF \l__transpose_bool
      {
        \dim_zero:N \l__x_coor_dim
      }
      {
        \dim_zero:N \l__y_coor_dim
        \clist_reverse:N \l_tmpa_clist
      }
    \matrix [anchor=\l__anchor_tl] (\l__name_tl) at \l__at_tl {
      \clist_map_inline:Nn \l_tmpa_clist
        {
          \draw_row:n { ##1 }
          \bool_if:NTF \l__transpose_bool
            {
              \dim_add:Nn \l__x_coor_dim { \l__block_x_unit_dim }
            }
            {
              \dim_add:Nn \l__y_coor_dim { \l__block_y_unit_dim }
            }
        }\\
    };
  }
\cs_generate_variant:Nn \draw_block_matrix:n { x, v, f }

\cs_new_protected:Nn \draw_row:n
  {
    \bool_if:NTF \l__transpose_bool
      {
        \dim_zero:N \l__y_coor_dim
      }
      {
        \dim_zero:N \l__x_coor_dim
      }
    \clist_map_inline:nn { #1 }
      {
        \draw_block:n { ##1 }
      }
  }

\cs_new_protected:Nn \draw_block:n
  {
    \tl_clear_new:N \l__draw_block_tl
    \parse_args:n { #1 }
    \definecolor{random}{RGB}{
      \int_rand:n { 255 },
      \int_rand:n { 255 },
      \int_rand:n { 255 }
    }
    \tl_set:Nx \l_tmpb_tl
      {
        \bool_if:NTF \l__random_bool
          { fill=random }
          { }
      }
    \tl_set:Nx \l__draw_block_tl
      {
        \exp_not:N \path[
          \l__block_style_tl,
          \l_tmpb_tl,
          \seq_use:Nn \l__block_style_seq { , }]
          (\dim_use:N \l__x_coor_dim, \dim_use:N \l__y_coor_dim) --
        \bool_if:NTF \l__transpose_bool
          {
              ++(0, \dim_use:N \l__block_wd_dim) --
              ++(\dim_use:N \l__block_x_unit_dim, 0) --
              ++(0, \dim_eval:n { -\l__block_wd_dim }) -- cycle;
          }
          {
              ++(\dim_use:N \l__block_wd_dim, 0) --
              ++(0, \dim_use:N \l__block_y_unit_dim) --
              ++(\dim_eval:n { -\l__block_wd_dim }, 0) -- cycle;
          }
      }
    \tl_use:N \l__draw_block_tl
    \bool_if:NTF \l__transpose_bool
      {
        \dim_add:Nn \l__y_coor_dim { \l__block_wd_dim }
      }
      {
        \dim_add:Nn \l__x_coor_dim { \l__block_wd_dim }
      }
  }

\cs_new_protected:Nn \parse_args:n
  {
    \seq_clear_new:N \l__block_style_seq
    \fp_set:Nn \l__block_wd_fp { 1 }
    \parse_next_arg: #1\stop
  }

\cs_new_protected:Nn \parse_next_arg:
  {
    \peek_meaning_ignore_spaces:NTF [
      { \parse_style:w }
      {
        \peek_meaning_ignore_spaces:NTF |
          { \parse_fill:w }
          { \parse_len:w }
      }
  }

\cs_new_protected:Npn \parse_style:w [#1]
  {
    \seq_put_right:Nn \l__block_style_seq { #1 }
    \parse_next_arg:
  }

\cs_new_protected:Npn \parse_fill:w |#1|
  {
    \seq_put_right:Nn \l__block_style_seq { fill=#1 }
    \parse_next_arg:
  }

\cs_new_protected:Npn \parse_len:w #1\stop
  {
    \tikz@checkunit{#1}
    \legacy_if:nTF { tikz@isdimension }
      { \dim_set:Nn \l__block_wd_dim { #1 } }
      {
        \bool_if:NTF \l__transpose_bool
          {
            \dim_set:Nn \l__block_wd_dim { \l__block_y_unit_dim * #1 }
          }
          {
            \dim_set:Nn \l__block_wd_dim { \l__block_x_unit_dim * #1 }
          }
      }
  }

\makeatother
\ExplSyntaxOff

\begin{document}
\begin{tikzpicture}
\fancyblock[name=a]{
  {2, 2, 1},
  {1, 2, 1},
  {3, 1, 1}
}
\path (a.south) node [below] {\verb|\fancyblock|};

\fancyblock[
  at={(8, 0)}, name=b, random=false,
  transpose, y=0.5cm,
  block={
    very thick,
    line width=1pt,
    draw=teal,
  }
]{
  {2, 1, 1},
  {1, [fill=red]2, |green|1},
  {1, 3, {[line width=2pt, draw=black]|teal|1}}
}
\path (b.south) node [below] {\verb|\fancyblock| with options};

\randomblock[at={([yshift=1cm]a.north west)}, name=c]{4, 5, 4}
\path (c.south) node [below] {\verb|\randomblock|};

\randomrectangle[at={(c.south -| b.center)}, name=d, anchor=b]{4}{7}
\path (d.south) node [below] {\verb|\randomrectangle|};

\end{tikzpicture}
\end{document}

Here is a fairly short TikZ solution that constructs the coloured rectangles using nested \foreach statements to parse a comma separated list of rectangle lengths. With the code below the two lines

\ColouredRectangles{{4},{3,2},{2,3},{1,4}}    \qquad
\ColouredRectangles[ultra thick]{{1,3,1},{1,2,2},{1,1,3}}

produce the rectangles:

enter image description here

The colouring of the rectangles is a little cunning because this is done using the following TikZ styles:

\tikzset{
   rectangle 1/.style = {fill=white},
   rectangle 2/.style = {fill=red},
   rectangle 3/.style = {fill=green},
   rectangle 4/.style = {fill=violet},
}

When each rectangle is drawn it is given the appropriate colour by using the length of the rectangle to set its style to rectangle <length>.

Here is the full code:

\documentclass{article}
\usepackage{tikz}

\tikzset{
   % the rectangle size sets the style and hence the fill
   rectangle 1/.style = {fill=white},
   rectangle 2/.style = {fill=red},
   rectangle 3/.style = {fill=green},
   rectangle 4/.style = {fill=violet},
}

\newcommand\ColouredRectangles[2][]{%
  \begin{tikzpicture}[#1]
    \foreach \row [count=\rc] in {#2} {% loop through rows
      \xdef\offset{0} % need to remember how far we have drawn so far
      \foreach \col in \row {% loop through columns
         \draw[rectangle \col] (\offset,-\rc) rectangle ++ (\col, -1);
         \xdef\offset{\numexpr\offset+\col\relax}
      }
    }
  \end{tikzpicture}%
}

\begin{document}

    \ColouredRectangles{{4},{3,2},{2,3},{1,4}}    \qquad
    \ColouredRectangles[ultra thick]{{1,3,1},{1,2,2},{1,1,3}}

\end{document}

As the second example shows, the \ColouredRectangles command accepts an optional argument for styling of the underlying tikzpicture environment.


Based on my answer here: Can TikZ create pixel art images?

\documentclass{article}
\usepackage{xcolor}
\usepackage{stackengine}
\newlength\blocksize
\setlength\blocksize{1ex}
\newcommand\block[2]{\kern-\fboxrule\fboxsep=0pt%
  \fbox{\color{#1}\rule{%
    \dimexpr#2\blocksize+\numexpr#2-1\relax\fboxrule\relax}{\blocksize}}}
\newcommand\gr[1][1]{\block{green}{#1}}
\newcommand\rd[1][1]{\block{red}{#1}}
\newcommand\bl[1][1]{\block{blue}{#1}}
\newcommand\wh[1][1]{\block{white}{#1}}
\setstackgap{S}{-\fboxrule}
\begin{document}
\Shortstack[l]{
\rd\gr[2]\gr\rd[3]\\
\gr\bl[3]\gr\gr\\
\gr\bl\rd[2]\wh\wh\gr}
\end{document} 

enter image description here