3

I am writing a report that includes enzyme images such as this:

enter image description here

In the \caption I would like to include a colour guide so that key components/atoms can be described in colour (this report is not being published so with respect to colour blindness this is not an issue). I am currently using a command like this:

\newcommand\ColourGuide{%
Colour code: \textcolor{violet}{metal ion}, \textcolor{Green3}{residue}, \textcolor{teal}{ligand}, \textcolor{Yellow3}{substrate}, \textcolor{DarkOrange1}{phosphorus}, \textcolor{red}{oxygen} and \textcolor{RoyalBlue4}{nitrogen}%
}

However, with some images not all of the coloured components are present so it would not be good to include redundant colour codes. How can I create a sentence based upon input? For example:

\ColourGuide{M}{R}{S}{O}{N}

would produce:

Colour code: metal ion, residue, substrate, oxygen and nitrogen.

in the colours specified. I am using lualatex and looked at using lua (which would be nice) but I do not know how to interact the two properly (nor efficiently), how could the arguments be extracted for example like in Python with input[0]? I would appreciate any advice or solutions via any method but lua would be nice so I can start learning it and applying it to other commands, I use lualatex frequently.

MWE:

\documentclass{book}

\usepackage[x11names]{xcolor}

\newcommand\ColourGuide{% Colour code: \textcolor{violet}{metal ion}, \textcolor{Green3}{residue}, \textcolor{teal}{ligand}, \textcolor{Yellow3}{substrate}, \textcolor{DarkOrange1}{phosphorus}, \textcolor{red}{oxygen} and \textcolor{RoyalBlue4}{nitrogen}% }

\begin{document}

\ColourGuide{}

\end{document}

How it looks:

enter image description here

(I am not sure what tags to put on this)

JamesT
  • 3,169
  • I would use a comma list as argument, and you can various example how to process that here. – Ulrike Fischer Mar 01 '23 at 13:39
  • @UlrikeFischer I don't think I have ever used a comma list before, I didn't know that, thanks for the advice. Do you mean like the solution here: https://tex.stackexchange.com/questions/139176/how-do-i-process-a-comma-separated-list-passed-through-a-command#139188 – JamesT Mar 01 '23 at 13:42
  • You can use xstring to parse the commas, or see https://tex.stackexchange.com/questions/233085/basics-of-parsing?r=SearchResults&s=1%7C39.4796 You can also use \ColourGuide{MRSON} and pull off the letters one at a time. – John Kormylo Mar 01 '23 at 14:55
  • @JohnKormylo is there a way to get the total tokens values (as in 3 or 5) so that commas can be used and the final one can be separated with and #1.. I would appreciate if there is an existing answer like that I cannot find one – JamesT Mar 01 '23 at 15:26

4 Answers4

3

Good attempt in your answer, but…

\documentclass{book}

\usepackage[x11names]{xcolor}

\ExplSyntaxOn

\NewDocumentCommand{\ColourGuide}{m} { Color~code:~\jamest_colourguide:n { #1 } }

\seq_new:N \l__jamest_colourguide_seq

\cs_new_protected:Nn \jamest_colourguide:n { \seq_clear:N \l__jamest_colourguide_seq \tl_map_function:nN { #1 } __jamest_colourguide_add:n \seq_use:Nnnn \l__jamest_colourguide_seq { ~and~ } { ,~ } { ~and~} }

\cs_new_protected:Nn __jamest_colourguide_add:n { \seq_put_right:Nx \l__jamest_colourguide_seq { \str_case:nn { #1 } { {a}{\textcolor{red}{oxygen},~\textcolor{DarkOrange1}{phosphorus},~ \textcolor{Yellow3}{substrate},~\textcolor{Green3}{residue},~ \textcolor{RoyalBlue4}{nitrogen},~\textcolor{teal}{ligand}~ and~\textcolor{violet}{metal~ion}.} {M}{\textcolor{violet}{metal~ion}} {R}{\textcolor{Green3}{residue}} {S}{\textcolor{Yellow3}{substrate}} {O}{\textcolor{red}{oxygen}} {N}{\textcolor{RoyalBlue4}{nitrogen}} {L}{\textcolor{teal}{ligand}} {P}{\textcolor{DarkOrange1}{phosphorus}} } } }

\ExplSyntaxOff

\begin{document}

\noindent\ColourGuide{MRSON}

\noindent\ColourGuide{LPON}

\noindent\ColourGuide{a}

\end{document}

enter image description here

No counting is needed: the argument is mapped character by character and the relevant code is added to the sequence that can later be used with the suitable separators.

Perhaps better, so to avoid code duplication, is to leave the argument empty if you want the full list.

\documentclass{book}

\usepackage[x11names]{xcolor}

\ExplSyntaxOn

\NewDocumentCommand{\ColourGuide}{m} { Color~code:~ \jamest_colourguide:n { #1 } }

\seq_new:N \l__jamest_colourguide_seq

\cs_new_protected:Nn \jamest_colourguide:n { \tl_if_empty:nTF { #1 } { \jamest_colourguide:n { OPSRNLM } } { \seq_clear:N \l__jamest_colourguide_seq \tl_map_function:nN { #1 } __jamest_colourguide_add:n \seq_use:Nnnn \l__jamest_colourguide_seq { ~and~ } { ,~ } { ~and~} } }

\cs_new_protected:Nn __jamest_colourguide_add:n { \seq_put_right:Nx \l__jamest_colourguide_seq { \str_case:nn { #1 } { {M}{\textcolor{violet}{metal~ion}} {R}{\textcolor{Green3}{residue}} {S}{\textcolor{Yellow3}{substrate}} {O}{\textcolor{red}{oxygen}} {N}{\textcolor{RoyalBlue4}{nitrogen}} {L}{\textcolor{teal}{ligand}} {P}{\textcolor{DarkOrange1}{phosphorus}} } } }

\ExplSyntaxOff

\begin{document}

\noindent\ColourGuide{MRSON}

\noindent\ColourGuide{LPON}

\noindent\ColourGuide{}

\end{document}

The output is the same.

egreg
  • 1,121,712
2

Thanks to the comments, I was able to piece together a solution that is not pretty but works as required. Adding an answer by @egreg to how to determine the number of characters in the argument of a command and the linked answer @John Kormylo commented gives this:

enter image description here

\documentclass{book}

\usepackage[x11names]{xcolor} \usepackage{xstring}

\usepackage{xparse} % loads expl3

\ExplSyntaxOn

\newcounter{Chars} \newcounter{CharsConstant}

\def\CharsCount#1{% \setcounter{Chars}{\tl_count:n { #1 }}% \setcounter{CharsConstant}{\tl_count:n { #1 }}% }

\NewDocumentCommand{\punctOrAnd}{mm} { \int_compare:nTF { #1 > 1 } { \int_compare:nTF { #1 = \value{CharsConstant} } { #2 } { ,~#2 } } { \space and~#2. } }

\def\ColourGuide#1{\CharsCount{#1}Colour~code:~\scanA#1\end} \def\scanA#1{% \ifx\end#1\else \IfStrEq{#1}{a}{\textcolor{red}{oxygen},~\textcolor{DarkOrange1}{phosphorus},~\textcolor{Yellow3}{substrate},~\textcolor{Green3}{residue},~\textcolor{RoyalBlue4}{nitrogen},~\textcolor{teal}{ligand}~and~\textcolor{violet}{metal~ion}.}{}% % \IfStrEq{#1}{M}{\punctOrAnd{\value{Chars}}{\textcolor{violet}{metal~ion}}}{}% \IfStrEq{#1}{R}{\punctOrAnd{\value{Chars}}{\textcolor{Green3}{residue}}}{}% \IfStrEq{#1}{S}{\punctOrAnd{\value{Chars}}{\textcolor{Yellow3}{substrate}}}{}% \IfStrEq{#1}{O}{\punctOrAnd{\value{Chars}}{\textcolor{red}{oxygen}}}{}% \IfStrEq{#1}{N}{\punctOrAnd{\value{Chars}}{\textcolor{RoyalBlue4}{nitrogen}}}{}% \IfStrEq{#1}{L}{\punctOrAnd{\value{Chars}}{\textcolor{teal}{ligand}}}{}% \IfStrEq{#1}{P}{\punctOrAnd{\value{Chars}}{\textcolor{DarkOrange1}{phosphorus}}}{}% % \addtocounter{Chars}{-1}% \expandafter \scanA \fi }

\ExplSyntaxOff

\begin{document}

\noindent\ColourGuide{MRSON}

\noindent\ColourGuide{LPON}

\noindent\ColourGuide{a}

\end{document}

JamesT
  • 3,169
2

This uses a semicolon as a terminator. \def\CGparse#1#2; will put the first token (letter) in #1 and everything before the semicolon in #2. It calls itself recursively until #2 is \empty.

\documentclass{book}
\usepackage[x11names]{xcolor}

\newcommand\ColourGuide[1]{Colour code:{\count1=0\relax\CGparse#1;}}

\def\CGparse#1#2;{\def\A{#1}% \def\B{#2}% \ifnum\count1=0\relax \space \else \ifx\B\empty\relax { and }% \else {, }% \fi \fi \advance\count1 by 1 \def\C{M}\ifx\A\C\relax\textcolor{violet}{metal ion}\fi \def\C{R}\ifx\A\C\relax\textcolor{Green3}{residue}\fi \def\C{L}\ifx\A\C\relax\textcolor{teal}{ligand}\fi \def\C{S}\ifx\A\C\relax\textcolor{Yellow3}{substrate}\fi \def\C{P}\ifx\A\C\relax\textcolor{DarkOrange1}{phosphorus}\fi \def\C{O}\ifx\A\C\relax\textcolor{red}{oxygen}\fi \def\C{N}\ifx\A\C\relax\textcolor{RoyalBlue4}{nitrogen}\fi \ifx\B\empty\relax {. }% \else \CGparse#2; \fi}

\begin{document}

\ColourGuide{MRSON}

\end{document}

John Kormylo
  • 79,712
  • 3
  • 50
  • 120
1

There are already many good answers, and probably mine using pgfkeys is far too complicated and cumbersome, but this is what I came up with.

  • No need for lualatex (which was requested explicitly here, but maybe someone can use this somewhere else).
  • The atoms will always be listed in the same order, no matter whether you write \ColourGuide[ligand,residue] or \ColourGuide[residue,ligand]. This can be wanted or not, but I think it is good style to keep the same order.

Of course the optional arguments/keys could be shortened to e.g. \ColourGuide[L,R] with almost no effort.

Result

\documentclass{article}

% create 'if's for each atom \newif\ifmetalion \newif\ifresidue \newif\ifligand \newif\ifsubstrate \newif\ifphosphorus \newif\ifoxygen \newif\ifnitrogen

% create two counters \newcounter{numtrue} % total number of 'if's which are true \newcounter{currval} % counter (for correctly putting 'and' at the last atom)

% colors \usepackage[x11names]{xcolor}

% create pgfkeys \usepackage{pgfkeys,xcolor} \pgfkeys{ /colourguide/.is family, /colourguide/.cd, default/.style={ metalion=false, residue=false, ligand=false, substrate=false, phosphorus=false, oxygen=false, nitrogen=false, }, metalion/.is if=metalion, residue/.is if=residue, ligand/.is if=ligand, substrate/.is if=substrate, phosphorus/.is if=phosphorus, oxygen/.is if=oxygen, nitrogen/.is if=nitrogen }

% this will produce the adequate delimiter between different atoms \def\chooseDelimiter{% \stepcounter{currval}% increment counter \ifnum\value{currval}<\value{numtrue}\relax% if more than one atom remains ... ,\ % ... put ',' ... \else%
\ifnum\value{currval}=\value{numtrue}\relax% ... if only one atom remains ... \ and\ % ... put 'and' ... \else% ... if no atom remains ... .% ... put '.' \fi% \fi% }

% main command: \ColorGuide \newcommand\ColourGuide[1][]{% % do pgfkeys magic \pgfkeys{/colourguide, default, #1}% % get number of requested atoms \setcounter{currval}{1}% \setcounter{numtrue}{0}% \ifmetalion \stepcounter{numtrue}\fi% \ifresidue \stepcounter{numtrue}\fi% \ifligand \stepcounter{numtrue}\fi% \ifsubstrate \stepcounter{numtrue}\fi% \ifphosphorus\stepcounter{numtrue}\fi% \ifoxygen \stepcounter{numtrue}\fi% \ifnitrogen \stepcounter{numtrue}\fi% % build sentence Colour code:\ % \ifmetalion\textcolor{violet}{metal ion}\chooseDelimiter\fi% \ifresidue\textcolor{Green3}{residue}\chooseDelimiter\fi% \ifligand\textcolor{teal}{ligand}\chooseDelimiter\fi% \ifsubstrate\textcolor{Yellow3}{substrate}\chooseDelimiter\fi% \ifphosphorus\textcolor{DarkOrange1}{phosphorus}\chooseDelimiter\fi% \ifoxygen\textcolor{red}{oxygen}\chooseDelimiter\fi% \ifnitrogen\textcolor{RoyalBlue4}{nitrogen}\chooseDelimiter\fi% }

\begin{document} \paragraph{Works with one atom:} \ColourGuide[metalion]

\paragraph{Works with two atoms:}
\noindent\ColourGuide[residue,ligand]\\ % two atoms

\paragraph{Works with many atoms:}
\ColourGuide[metalion,residue,ligand,substrate,phosphorus,oxygen,nitrogen]\\ % many atoms

\paragraph{Keeps the given order, no matter what is the order in brackets:}
\ColourGuide[ligand, residue]

\end{document}

Οὖτις
  • 2,897
  • 1
  • 5
  • 18
  • +1 I welcome all types of answers, this could be helpful to someone in the future using tikz/pgf, thanks a lot :) – JamesT Mar 02 '23 at 09:04