使用 \tikzmark 将 algpseudocode 行括起来

使用 \tikzmark 将 algpseudocode 行括起来

我使用 algpseudocode 包来编写代码,我想在代码的某些行周围画出方框。这个答案非常接近我想要的。这个想法是用 标记代码的第一行和最后一行\tikzmark,然后在获得的坐标周围画一个框。这种方法的问题在于它只适用于以 开头的行\State。例如,当我\tikzmark在 if 语句后使用 时,它不会标记行的开头,而是标记更靠右的某个位置。

以下是一个例子:

\documentclass{article}
\usepackage{algpseudocode} 
\usepackage{tikz}
\usetikzlibrary{fit,tikzmark}

\newcommand\drawCodeBox[2]{%
    \begin{tikzpicture}[remember picture,overlay]
        \coordinate (start) at ([yshift=1.7ex]pic cs:#1);
        \coordinate (end) at ([yshift=-0.3ex]pic cs:#2);
        \node[inner sep=2pt,draw=red,fit=(start) (end)] {};
    \end{tikzpicture}%
}

\begin{document}
\begin{algorithmic}
    \State \tikzmark{s1}%
    $x \gets 0$\tikzmark{e1}
    \drawCodeBox{s1}{e1}
    \State $y \gets 1$
    \If{$x = y$}\tikzmark{s2}
        \State PANIC
    \EndIf\tikzmark{e2}
    \drawCodeBox{s2}{e2}
\end{algorithmic}
\end{document}

这是我得到的:

我得到了什么

期望的结果是:

我想要的是

答案1

要坚持你的\drawCodeBox宏观,你必须位置s\tikzmark位于适当的位置。我们可以将它放在 a 内,然后将其(水平)明确堆叠,使其与设置 ( ) 的元素完全一致\tikzmark{e2},从而向右移动。\makebox[0pt][r]\EndIf\algorithmicend\ \algorithmicif

在此处输入图片描述

\documentclass{article}

\usepackage{algpseudocode,tikz}

\usetikzlibrary{fit,tikzmark}

\newcommand\drawCodeBox[2]{%
  \begin{tikzpicture}[remember picture,overlay]
    \coordinate (start) at ([yshift=1.7ex]pic cs:#1);
    \coordinate (end) at ([yshift=-0.3ex]pic cs:#2);
    \node[inner sep=2pt,draw=red,fit=(start) (end)] {};
  \end{tikzpicture}%
}

\begin{document}

\begin{algorithmic}
  \State \tikzmark{s1}%
  $x \gets 0$\tikzmark{e1}
  \drawCodeBox{s1}{e1}
  \State $y \gets 1$
  \If{$x = y$}\tikzmark{s2}
    \State PANIC
  \EndIf\makebox[0pt][r]{\tikzmark{e2}\phantom{\algorithmicend\ \algorithmicif}}
  \drawCodeBox{s2}{e2}
\end{algorithmic}

\end{document}

答案2

根据 Werner 对其答案的评论,我定义了\BoxedState、、\BoxedIf\BoxedWhile,它们在行首设置标记并绘制一个框。可选参数设置框的样式。该命令\EndBox设置框的下端,\SetBoxEast可用于在框中最长的线的末尾设置右端。如果最后一行是最长的,\SetBoxEast则可以省略。

计数器tmkcount用于保持标记的唯一性。框在其内容之前绘制,因此可以填充。限制包括:

  • 该盒子不适合非常高的首行。
  • 假设框内的任何线都不会比第一条线更靠左。

代码和示例:

\documentclass{article}
\usepackage{algpseudocode,tikz}
\usetikzlibrary{fit,tikzmark}

\newcounter{tmkcount}
\tikzset{%
    tikzmark suffix={-\thetmkcount},%
    defaultCodeBox/.style={draw=red}%
}

\newcommand{\drawCodeBox}[4]{%
    \begin{tikzpicture}[remember picture,overlay]
        \coordinate (start) at ([yshift=1.4ex]pic cs:#2);
        \coordinate (middle) at (pic cs:#3);
        \coordinate (end) at ([yshift=-0.2ex]pic cs:#4);
        \node[inner sep=2pt,#1,fit=(start) (middle) (end)] {};
    \end{tikzpicture}%
}

\newcommand{\BeginBox}[1]{%
    \drawCodeBox{#1}{beginCB}{middleCB}{endCB}%
    \tikzmark{beginCB}\tikzmark{middleCB}%
}

\newcommand{\SetBoxEast}{%
    \unskip%
    \tikzmark{middleCB}%
}

\newcommand{\EndBox}{%
    \unskip%
    \tikzmark{endCB}%
    \stepcounter{tmkcount}%
}

\newcommand{\BoxedState}[1][defaultCodeBox]{\State\BeginBox{#1}\ignorespaces}

\algdef{S}[WHILE]{BoxedWhile}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicwhile\ #2\ \algorithmicdo}%
\algdef{S}[FOR]{BoxedFor}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicfor\ #2\ \algorithmicdo}%
\algdef{S}[FOR]{BoxedForAll}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicforall\ #2\ \algorithmicdo}%
\algdef{S}[LOOP]{BoxedLoop}[1][defaultCodeBox]{\BeginBox{#1}\algorithmicloop}%
\algdef{S}[REPEAT]{BoxedRepeat}[1][defaultCodeBox]{\BeginBox{#1}\algorithmicrepeat}%
\algdef{S}[IF]{BoxedIf}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicif\ #2\ \algorithmicthen}%
\algdef{C}[IF]{IF}{BoxedElsIf}[2][defaultCodeBox]{\BeginBox{#1}\algorithmicelse\ \algorithmicif\ #2\ \algorithmicthen}%
\algdef{Ce}[ELSE]{IF}{BoxedElse}{EndIf}[1][defaultCodeBox]{\BeginBox{#1}\algorithmicelse}%


\begin{document}
\begin{algorithmic}
    \BoxedState $x \gets 0$
    \State $y \gets x + 1$\EndBox
    \If{$x < y$}
        \State $x \gets x + 1$
    \BoxedElse[fill=yellow]
        \State do some complicated stuff\SetBoxEast
        \State $y \gets y + 1$\EndBox
    \EndIf
\end{algorithmic}
\end{document}

结果

相关内容