How does one enumerate and apply a macro to every character in a string token?
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}
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}
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.
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: