10

In this example, the bit representation of BIOS font from '@' to 'G' is in two forms. The first is a sequence of 1s and 0s separated by commas. The second is a string of 1s and 0s crammed into tokens. Truth be told, I want to have a single macro which generates a tikzpicture from the second form. This is as far as I got, and I need help to finish it.

Running 'pdflatex BIOS.tex' with this as its source does the right thing with the first form and fails to output the second form. The code is actually broken, but the goal is to have indices (\xi,\yi) and a \chr to place a filled circle for each \chr in each \row.

Please help me finish this. I don't know what to try and no search result worked.

\documentclass[12pt]{article}

\usepackage{tikz}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% TODO make this work by enumerating each character with \xi
\makeatletter
\def\dowithchar#1#2{%
    \begingroup%
    \def\myspace{}% defined with local scope
    \@tfor\chr:=#1\do{%
        \myspace\ifx\chr{X} \fill (0.18*\xi,-0.18*#2) circle (0.08) \fi;%
        \let\myspace\space%
    }%
    \endgroup%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\makeatother

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \dowithchar\row\yi
    }
\end{tikzpicture}

\end{document}
jlettvin
  • 317

4 Answers4

7

TikZ has the parser module for that purpose. It allows you to parse a string of characters and do something with them.

\documentclass[12pt]{article}

\usepackage{tikz}
\usepgfmodule{parser}
\newcounter{pft}
\pgfparserdef{ASCIIswitch}{initial}{the letter X}% 
{\stepcounter{pft}%
\fill (0.18*\number\value{pft},0) circle [radius=0.08];}%
\pgfparserdef{ASCIIswitch}{initial}{the letter O}% 
{\stepcounter{pft}}%
\pgfparserdef{ASCIIswitch}{initial}{the character ;}% 
{\pgfparserswitch{final}}%

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

Comma-separated list:

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle [radius=0.08] \fi;
        }
    }
\end{tikzpicture}

\bigskip

ASCII strings parsed by parser module:

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
       \begin{scope}[yshift=-0.18*\yi*1cm]
        \setcounter{pft}{0}
        \edef\temp{\noexpand\pgfparserparse{ASCIIswitch}\row;}
        \temp
       \end{scope}
    }
\end{tikzpicture}

\end{document}

enter image description here

ADDENDUM: As for your question in the comments whether you can split the string into a comma-separated list of atoms: of course, you almost had the code yourself.

\documentclass[12pt]{article}

\usepackage{tikz}

\makeatletter
\def\splitstring#1#2->#3;{%
      \edef#3{#1}\@tfor\chr:=#2\do{%
        \edef#3{#3,\chr}%
      }%
}
\makeatother
\begin{document}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\splitstring \row->\myrow;
        \foreach\chr [count=\xi] in \myrow {\def\myX{X}
            \ifx\chr\myX \fill (0.18*\xi,-0.18*\yi) circle[radius=0.08] \fi;
        }
    }
\end{tikzpicture}
\end{document}
  • Wow! It looks like you nailed it, but I must learn an entirely unfamiliar part of latex to understand what you did. This is wonderful, and thank you.

    A follow-on question is "why does latex make this so hard?" I could do this quickly, simply, and easily in any of a dozen different programming languages.

    Perhaps I shall write a preprocessor to generate latex so that these kinds of operations are more easily done. Perhaps a Python library which can be used by authors to write source generators. Kind of like a mediawiki syntax reader that outputs latex.

    Nevertheless... THANK YOU!

    – jlettvin Feb 25 '20 at 15:06
  • 1
    @jlettvin I think that the answer why LaTeX makes it so hard is that it is really a typesetting system and rather old. Given this it is actually very versatile. Python and company are much newer, and, more importantly, designed to tackle such tasks. (BTW, circle (0.08) is deprecated by now.) –  Feb 25 '20 at 15:12
  • I appreciate the solutions, and I am trying them out. What I had hoped for, and searched for days to find, was a macro that would split a string into characters suitable for use with \foreach\chr [count=\xi] in \split{\row}{do something}. I became despondent over the days of search. Is there any chance of showing how that might be done? – jlettvin Feb 25 '20 at 15:21
  • @jlettvin Sure. I added that to the answer as an addendum. –  Feb 25 '20 at 15:39
  • @jlettvin Another fundamental reason while “simple programming tasks” often appear so difficult to do in TeX to experienced non-TeX programmers, is that TeX is a macro language. Tokens get replaced by other tokens, which in turn get replaced by others etc. until we have an unexpandable token. Then, depending on that token, TeX does this or that. Most programming languages don't work this way at all. In all macro languages I've seen (e.g., m4), doing “moderately complex things” appears very difficult when you are not well versed in the system. One really needs a lot of practice to... – frougon Feb 25 '20 at 16:01
  • @jlettvin become comfortable with macro langages, when one has only worked with more “conventional ones” before. It is a bit like starting to program for the first time. :-) Forget about usual programming patterns; rather, think about the stream of tokens that TeX sees, eats, digests... hope this helps. – frougon Feb 25 '20 at 16:03
  • The splitspring addendum is GOLD! This should be documented in a reliably searched location. I am lost in admiration at the parsimony and elegance. You have no idea how consequential this one macro will be to my writing. Thank you. – jlettvin Feb 25 '20 at 16:08
  • @jlettvin You are welcome! I would be surprised if something like this did not exist somewhere already. There are repeated attempts to collect such helpers somewhere (etoolbox, sparse, etc.), yet they are arguably not documented as well as they could. The package documentations are largely great, but there is, to my knowledge, no resource written by an objective that I am aware of that tells one in simple terms "if you have this problem you need to load that package and use this-and-that macro". –  Feb 25 '20 at 16:17
  • Now I can construct voxel-art 3D images from arrays of arrays of strings. Time to put up the bunting. – jlettvin Feb 25 '20 at 16:21
6

You can use expl3: no need for fancy indexing, which is provided out of the box with “indexed maps”.

\documentclass[12pt]{article}

\usepackage{tikz}
\usepackage{xparse}

\newcommand{\makedot}[2]{%
  \fill (0.18*#1,-0.18*#2) circle (0.08);
}

\ExplSyntaxOn
\NewDocumentCommand{\ASCIIfont}{O{}m}
 {
  \begin{tikzpicture}[#1]
  \jlettvin_asciifont:n { #2 }
  \end{tikzpicture}
 }

\seq_new:N \l__jlettvin_asciifont_rows_seq
\seq_new:N \l__jlettvin_asciifont_row_seq

\cs_new_protected:Nn \jlettvin_asciifont:n
 {
  \seq_set_from_clist:Nn \l__jlettvin_asciifont_rows_seq { #1 }
  \seq_indexed_map_function:NN \l__jlettvin_asciifont_rows_seq \__jlettvin_asciifont_row:nn
 }

\cs_new_protected:Nn \__jlettvin_asciifont_row:nn
 {% #1 = row index, #2 = row
  \seq_set_split:Nnn \l__jlettvin_asciifont_row_seq { } { #2 }
  \seq_indexed_map_inline:Nn \l__jlettvin_asciifont_row_seq
   {% ##1 = column index, ##2 = item
    \str_if_eq:nnT { ##2 } { X }
     {
      \makedot{##1}{#1}
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
}

\end{document}

The input is split at commas and each row is processed taking into account its number (y-coordinate); the row is again split into items (at nothing), and the same is done taking into account the item number (x-coordinate).

The command \ASCIIfont has an optional argument for specifying options to the tikzpicture.

The trailing row of zeros doesn't produce vertical space, but that's a problem also with the other implementation.

enter image description here

egreg
  • 1,121,712
  • I have seen much expl3 in my search, but I find the syntax and sheer volume of code intimidating. I want to learn how to read and code like this. Can you recommend an online tutorial? – jlettvin Feb 25 '20 at 15:31
  • @jlettvin I've found very clarifying texdoc expl3 (a dozen pages introduction) and then texdoc interface3 (which is a sort of reference) – Matteo Gamboz Feb 26 '20 at 08:32
5

Here are two ways to fix your code (quite different from the one given by Schrödinger's cat).

Probably, your most important “mistake” is that you needed to expand the \row macro before feeding the result to \dowithchar, in the second picture. Also, I don't see the purpose of your \myspace macro, so I removed it.

The second problem is that you needed \if rather than \ifx (well, \ifx could be used, but not this way). What happens with your \ifx test is that it compares the \chr macro with an opening brace { of category code 1 (beginning of group): the comparison is always false; that is why the second picture was invisible. \ifx compares the next two tokens, it never expands them. On the other hand, \if expands what follows until it has two unexpandable tokens; then, it compares their character codes (this is a bit simplified: there are special rules for unexpandable control sequence tokens and active characters—see the TeXbook p. 209 or specialized questions).

With \pgfmathtruncatemacro

Here, inside \dowithchar, \xi is a macro, just as in the first picture.

\documentclass{article}
\usepackage{tikz}

\makeatletter
\def\dowithchar#1#2{%
  \begingroup
    \def\xi{1}
      \@tfor\chr:=#1\do{%
        \if X\chr \fill (0.18*\xi,-0.18*#2) circle (0.08); \fi
        \pgfmathtruncatemacro{\xi}{\xi+1}
      }%
  \endgroup
}
\makeatother

\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\bigskip
\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\dowithchar\expandafter{\row}\yi
    }
\end{tikzpicture}

\end{document}

With a TeX \count register

Here, we use a TeX \count register via a \countdef token to count the columns in \ASCIIfont (second picture). The \countdef token is \myXi. We could reuse the same name \xi, but I prefer using a different name to avoid confusion since in the first picture, \xi is a macro (a \countdef token is unexpandable; in particular, it is not a macro).

\documentclass{article}
\usepackage{tikz}

\newcount\myXi

\makeatletter
\def\dowithchar#1#2{%
  \myXi=1                     % <--- don't remove the space
  \@tfor\chr:=#1\do{%
    \if X\chr \fill (0.18*\myXi,-0.18*#2) circle (0.08); \fi
    \advance \myXi by 1       % <--- don't remove the space
  }%
}
\makeatother

\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\bigskip
\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\dowithchar\expandafter{\row}\yi
    }
\end{tikzpicture}

\end{document}

Output

The output is the same in both cases:

enter image description here

frougon
  • 24,283
  • 1
  • 32
  • 55
  • I learned a lot reading your response and code. I have only one question... What is the reasoning for "do not remove space?" – jlettvin Feb 25 '20 at 15:33
  • Because this is in places where TeX reads a (with explicitly-written low-level TeX commands: counter assignment and counter \advance). The grammar for is peculiar (see TeXbook p. 269). One very important aspect is that if TeX has only seen digits so far while it is reading a , it will continue looking for the rest and expand tokens as it goes. A space token terminates this search and is automatically removed (it is part of the syntax of a ). ... – frougon Feb 25 '20 at 15:39
  • This way, you can be sure TeX won't expand further tokens from the input stream for the being read, possibly taking unwanted digits as part of it. This is explained here and certainly in many other places. :-) – frougon Feb 25 '20 at 15:44
  • Regarding my first comment: TeXnically, \advance \myXi by 1 is also a “counter assignment.” In partic., it is not an operation that can be performed while \edef is expanding its braced argument, and it can't be triggered by \expandafter either. In short, it can't be done in expansion-only contexts (for completeness, LuaTeX can perform assignments in expansion-only contexts when \directlua is used, but forget about this for now). – frougon Feb 25 '20 at 16:16
  • I was so impressed with the responses you gave me that I will ask for one slight improvement which, also, eludes me. I want to take two characters at a time instead of 1. I've been searching but failed to find an example, or sufficient documentation. The salient line is this one...

    @tfor\chr:=#1\do{%

    – jlettvin Feb 25 '20 at 21:53
  • @jlettvin Yet, it is clearly more rewarding here to give a fish than to teach fishing. You probably want something like \newcommand*{\foo}[2]{\ifthisistheend \let\next=\relax\else ... \let\next=\foo\fi \next} (tail recursion). You should read Ulrich Diez's answer and most probably upvote it (what he writes is always interesting). – frougon Feb 26 '20 at 00:16
  • I am happy to learn fishing. But I have an impediment here that could not be overcome by any amount of reading. 'Tis true that I am trying to churn out an amateur science paper on my own, so the short cuts do help. Nevertheless, I accept your lead, and will go figure out this hint you gave. Thank you. – jlettvin Feb 26 '20 at 03:06
4

For the sake of having fun you can implement your own \romannumeral0-expansion-based tail-recursive-loop-macro for interspersing the tokens that form the expansion of \row with commas. (If you do this, a bit of expansion-control is needed to get all tokens into correct order, but that might be a nice and relaxing exercise.) Then you can use the comma-list based \foreach..[count=...]...-loop instead of the non-delimited-argument-based \@tfor-loop.

\documentclass[12pt]{article}

\usepackage{tikz}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\newcommand\insertcommas[3]{%
  % #1 tokens to put in front of brace-nested comma-list
  % #2 comma-list accumulated so far
  % #3 either the appended \insertcommas or an element from non-comma-list
  \romannumeral0%
  \ifx\insertcommas#3\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
  {\expandafter\@gobbletwo\insertcommas{#1}{#2#3,}}{ #1{#2}}%
}%
\newcommand\exchange[2]{#2#1}%
\newcommand\dowithchar[2]{%
    % #1 = the token \row needs to be expanded before \insertcommas can be applied.
    \expandafter\exchange\expandafter{#1}{\insertcommas{\foreach\chr [count=\xi] in }{}}\insertcommas{%
       % You have nullfont inside tikzpicture-environment, so \space has no effect inside
       % tikzpicture-environment, so it is not clear to me why you want it, but here
       % you get it:
       \ifnum\xi>1 \space\fi
       %%%%%%%%
       \expandafter\ifx\expandafter X\chr\fill(0.18*\xi,-0.18*#2) circle (0.08) \fi;%
    }%
}%
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{%
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \dowithchar\row\yi
    }
\end{tikzpicture}

\end{document}
Ulrich Diez
  • 28,770
  • Thank you. I am learning much from your answers. I am making rapid progress both in my writings and my TeX. – jlettvin Feb 26 '20 at 03:17
  • @jlettvin I am happy to hear that. I hope you also have fun. ;-) By the way: TeXbook, chapter 6 "Running TeX" says about fun and errors: "Error messages can be terrifying when you aren’t prepared for them; but they can be fun when you have the right attitude. Just remember that you really haven’t hurt the computer’s feelings, and that nobody will hold the errors against you. Then you’ll find that running TeX might actually be a creative experience instead of something to dread." Imho that kind of sayings really sucks when grappling with (La)TeX while working/writing towards a deadline. ;-) – Ulrich Diez Feb 26 '20 at 18:38