Я пытаюсь присвоить ячейке таблицы значение цвета в оттенках серого на основе ее числового значения. Пока что у меня есть следующий код
\documentclass{article}
\usepackage{fp,xcolor,colortbl}
\FPeval{\resb}{0.5}
\newcommand{\he}[1]{%
\FPeval{\resa}{2 * #1}%
\cellcolor[gray]{\resa}%
#1
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Таблицы содержат все числа с плавающей точкой. Ошибка, которую я получаю:
Undefined control sequence.
<argument> \resa
Можно ли использовать результат FPeval для раскрашивания ячейки?
решение1
Вы могли бы добавить строку
\xdef\resa{\resa}%%
в ваш код. Но все равно ничего не скомпилируется правильно, потому что ваш множитель выводит вас за пределы диапазона от 0 до 1.
\documentclass{article}
\usepackage{fp,xcolor,colortbl}
\FPeval{\resb}{0.5}
\newcommand{\he}[1]{%
\FPeval{\resa}{2 * #1}%
\xdef\resa{\resa}%%
\cellcolor[gray]{\resa}%
#1
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Чтобы получить компилируемую версию, я написал
\documentclass{article}
\usepackage{fp,xcolor,colortbl}
\FPeval{\resb}{0.5}
\newcommand{\he}[1]{%
\FPeval{\resa}{2 * #1}%
\xdef\resa{\resa}%%
\ifdim\resa pt>1pt\relax
\gdef\resa{1}%%
\fi
\cellcolor[gray]{\resa}%
#1
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
решение2
Следующий пример решает эту проблему путем расширения \resa
до того, как \cellcolor
будет расширен и рассмотрены его аргументы.
Вторая проблема заключается в том, что диапазон цветовой модели gray
находится в пределах от 0 до 1 включительно. Значения 0,8 и 1,0 превышают этот диапазон при умножении на 2. Поэтому пример проверяет результат и ограничивает его до 1, если необходимо.
\documentclass{article}
\usepackage{fp,xcolor,colortbl}
\newcommand{\he}[1]{%
\FPeval{\resa}{2 * #1}%
\ifdim\resa pt>1pt %
\def\resa{1}%
\fi
\edef\processme{\noexpand\cellcolor[gray]{\resa}}%
\processme
#1%
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
И версия, где вся математика делается средствами fp
. Также цвет текста переключается на белый, если цвет фона становится слишком темным, чтобы сохранить читаемость.
\documentclass{article}
\usepackage{fp,xcolor,colortbl}
\newcommand{\he}[1]{%
\FPeval{\resa}{max(0, min(1, 2 * #1))}%
\edef\processme{\noexpand\cellcolor[gray]{\resa}}%
\processme
\FPiflt{\resa}{0.5}%
\color{white}%
\fi
#1%
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Тем не менее, есть возможности для улучшения. Результат числа имеет много ненужных нулей на обоих концах, которые можно сократить, например:
0.200000000000000000 ⇒ .2
0.400000000000000000 ⇒ .4
1.000000000000000000 ⇒ 1
Нули в конце можно удалить с \FPclip
помощью 0.2
, 0.4
и 1
.
Пакет thepdfnumber
идет на шаг дальше и обеспечивает \thepdfnumber
сокращение десятичных чисел на обоих концах, чтобы получить .2
, .4
и 1
. Еще лучше, \thepdfnumberNormZeroOne
он заботится об условии диапазона для значений серого:
\documentclass{article}
\usepackage{fp,xcolor,colortbl}
\usepackage{thepdfnumber}
\newcommand{\he}[1]{%
\FPeval{\resa}{2 * #1}%
\edef\processme{%
\noexpand\cellcolor[gray]{\thepdfnumberNormZeroOne\resa}%
}%
\processme
\FPiflt{\resa}{0.5}%
\color{white}%
\fi
#1%
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
решение3
Более простое решение с использованием expl3
и его мощного fp
модуля.
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\he}{m}
{
\cellcolor[gray]{ \fp_eval:n { min ( 2*#1, 1 ) } }
#1
}
\NewDocumentCommand{\hetest}{m}
{
\cellcolor[gray]{ \fp_eval:n { min ( 2*#1, 1 ) } }
\textcolor{red}{#1 ~ -- ~ \fp_eval:n { min ( 2*#1, 1 ) }}
}
\ExplSyntaxOff
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\qquad
\begin{tabular}{| c | c | c |}
\hline
\hetest{0.1} & \hetest{0.2} & \hetest{0.3} \\
\hline
\hetest{0.5} & \hetest{0.8} & \hetest{1.0} \\
\hline
\end{tabular}
\end{document}
Команда \hetest
также выводит (красным цветом) аргумент и вычисленное значение цвета для целей тестирования.
Вариант 1
Аргумент печатается белым, если уровень серого ниже 0,5.
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\he}{m}
{
\cellcolor[gray]{ \fp_eval:n { min ( 2 * #1 , 1 ) } }
\fp_compare:nT { 2 * #1 < 0.5 } { \color{white} }
#1
}
\ExplSyntaxOff
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Вариант 2
То же самое, что и раньше, но значения хранятся в fp
переменных; это может быть удобно в случае, если вычисления более трудоемкие, из соображений производительности.
Здесь я определяю синтаксический сахар, \__ecker_cellcolor:n
чтобы \cellcolor[gray]{...}
с помощью варианта мы могли расширить значение во время вызова, используя f
расширение.
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\he}{m}
{
\ecker_he:n { #1 }
}
\fp_new:N \l_ecker_resa_fp
\fp_new:N \l_ecker_resb_fp
\cs_new_protected:Npn \ecker_he:n #1
{
\fp_set:Nn \l_ecker_resa_fp { 2 * #1 }
\fp_set:Nn \l_ecker_resb_fp { min ( \l_ecker_resa_fp, 1 ) }
\__ecker_cellcolor:f { \l_ecker_resb_fp }
\fp_compare:nT { \l_ecker_resb_fp < 0.5 } { \color{white} }
#1
}
\cs_new_protected:Npn \__ecker_cellcolor:n #1
{
\cellcolor[gray]{ \fp_eval:n { #1 } }
}
\cs_generate_variant:Nn \__ecker_cellcolor:n { f }
\ExplSyntaxOff
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Вывод вариантов 1 и 2
решение4
Простое решение с использованием xintexpr
модуля из пакета xint
:
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xintexpr}
\newcommand\he[1]{%
\cellcolor[gray]{\xinttheiexpr[2] min(2*#1, 1)\relax}%
#1%
}
\newcommand\hetest[1]{%
\cellcolor[gray]{\xinttheiexpr[2] min(2*#1, 1)\relax}%
\textcolor{red}{#1 -- \xinttheiexpr[2] min(2*#1, 1)\relax}%
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\qquad
\begin{tabular}{| c | c | c |}
\hline
\hetest{0.1} & \hetest{0.2} & \hetest{0.3} \\
\hline
\hetest{0.5} & \hetest{0.8} & \hetest{1.0} \\
\hline
\end{tabular}
\end{document}
Макросы \xinttheiexpr [2]
, которые используются для создания чисел с фиксированной точкой с двумя цифрами после десятичной точки.
NB: это работает, потому что, как оказалось, \cellcolor
макрос в какой-то момент расширяет свой аргумент, поэтому нам не нужно делать это заранее. Смотрите комментарии в Варианте2 ниже, как можно было бы действовать, если бы используемый макрос был менее щадящим, чем \cellcolor
.
Вариант 1
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xintexpr}
\newcommand{\he}[1]{%
\cellcolor[gray]{\xinttheiexpr[2] min ( 2 * #1 , 1 ) \relax}%
\xintifboolexpr{ 2 * #1 < 0.5 }{\color{white}}{}%
#1%
}
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Вариант 2
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xintexpr}
\makeatletter
\newcommand{\he}[1]{\ecker@he{#1}}
\protected\def\ecker@he #1{%
\xdef\ecker@he@resa{\xintexpr 2*#1 \relax}%
\xdef\ecker@he@resb{\xintexpr min(\ecker@he@resa,1) \relax}%
\cellcolor[gray]{\xinttheiexpr[2] \ecker@he@resb \relax}%
\xintifboolexpr{ \ecker@he@resb < 0.5 }{\color{white}}{}%
#1%
}
% if \cellcolor did not f-expand its argument we would have used something
% like, perhaps,
% \protected\def\ecker@cellcolor@f #1{%
% \expandafter\ecker@cellcolor@n\expandafter{\romannumeral-`0#1}%
% }
% \protected\def\ecker@cellcolor@n #1{%
% \cellcolor[gray]{#1}%
% }
% and the call would have been
% \ecker@cellcolor@f{\xinttheiexpr[2] \ecker@he@resb \relax}
% We need the iexpr[2] encapsulation because variable \ecker@he@resb
% was defined with \xintexpr, variant:
% \xdef\ecker@he@resb{\xintiexpr[2] min(\ecker@he@resa,1) \relax}%
% Then we would have done
% \ecker@cellcolor@f{\xintthe\ecker@he@resb}
% We
\makeatother
\begin{document}
\begin{tabular}{| c | c | c |}
\hline
\he{0.1} & \he{0.2} & \he{0.3} \\
\hline
\he{0.5} & \he{0.8} & \he{1.0} \\
\hline
\end{tabular}
\end{document}
Производит то же самое, что и Вариант 1.