我想要一个可以产生以下两个示例的环境,其中图片不是动画。
我希望环境能够自动处理数字的数量。
这适用于显示逐步解释的图形,例如一个有限马尔可夫链的简化。从 LaTeX 的角度来看,我只想输入:
\begin{stepByStep}
\step Fig.1
\step Fig.2
\step Fig.3
\step Fig.4
\end{stepByStep}
\begin{stepByStep}
\step Fig.1
\step Fig.2
\step Fig.3
\step Fig.4
\step Fig.5
\step Fig.6
\step Fig.7
\end{stepByStep}
答案1
以下是可以为您完成此操作的解决方案,但语法略有修改。您一次指定两个步骤,其中一个可以为空白:
\begin{stepByStep}
\step{\FigA}{\FigB}
\step{}{\FigD}
\end{stepByStep}
\begin{stepByStep}
\step{\FigA}{\FigB}
\step{\FigC}{\FigD}
\step{\FigD}{}
\end{stepByStep}
它将各个图形放置在tabular
环境中,并用于\tikzmark
在适当的点之间绘制箭头。因此,它应该能够处理不同高度和长度的图形,但我没有进行广泛的测试。下面是最后一个案例的输出。
定制:
定义了一些参数以便您轻松进行调整:
\WidthOfArrow
指定水平箭头尺寸(最小值)\HeightOfArrow
指定垂直箭头的大小\ArrowImageSep
指定箭头和图像之间的空间
笔记:
- 这需要运行两次。第一次用于确定线条的起点和终点,第二次用于绘制线条。
- 不进行检查以确保只有最后一行具有空白参数。
- 不进行任何检查以确保只有适当的参数可以为空。例如,如果第二行是最后一行,则只有第一个参数可以为空。但是如果第三行是最后一行,则只有第二个参数可以为空。
参考:
- 我在用包裹
adjustbox
和此解决方案用于垂直居中表格单元格。 - 解决方案来自测量并保留表格行之间的长度是用于访问跨行各种测量值的方法。
\tikzmark
来自Andrew Stacey 的回答并允许您记住文档中的特定点。\newtoggle
这里的解决方案使用了包裹etoolbox
,因为我觉得这种语法更易读。这可以适用于评估条件的许多其他选项,例如LaTeX 条件表达式。
进一步增强:
垂直箭头有点太长,这是个小问题。我通过设置添加了一个临时解决方案来修复此问题
\VerticalArrowAdjust
,但这不是必需的,或者至少应该能够计算出精确的值。如果此语法不适合您,您可以对其进行调整以获得您想要的精确语法。
代码:
\documentclass{article}
\usepackage[demo]{graphicx}% Add [demo] option if don't have figures
\usepackage{adjustbox}
\usepackage{etoolbox}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand*{\WidthOfArrow}{1.5cm}% Horizontal Arrow size (minimum)
\newcommand*{\HeightOfArrow}{1.5cm}% Vertical Arrow size
\newcommand*{\ArrowImageSep}{1.5pt}% Space between arrow and image
\newcommand*{\VerticalArrowAdjust}{8.5pt}% Kludge
%------------------ Should not need to adjust below this
\newlength{\VerticalSep}%
\pgfmathsetlength{\VerticalSep}{\HeightOfArrow + 2*\ArrowImageSep}%
\newtoggle{FirstRow}% No vertical arrow in this case
% We need to have the sizes of up to four pictures
\newlength{\HeightA}%
\newlength{\HeightB}%
\newlength{\HeightC}%
\newlength{\HeightD}%
\newlength{\WidthA}%
\newlength{\WidthB}%
\newlength{\WidthC}%
\newlength{\WidthD}%
\newcommand{\tikzmark}[1]{\tikz[overlay,remember picture] \node (#1) {};}
% https://tex.stackexchange.com/questions/46418/measure-and-retain-lengths-between-tabular-rows
\makeatletter
\newcommand*{\MeasureAndPlaceFigure}[3]{%#=1Fig, #2=Width, #3=Length
\setbox\z@\hbox{#1}%
\global\csname#2\endcsname\wd\z@%
\global\csname#3\endcsname\ht\z@%
\adjustbox{width=!,height=!,valign=m}{\box\z@}%
}%
\newcommand*{\@stepAB}[2]{% A --> B
\MeasureAndPlaceFigure{#1}{WidthA}{HeightA}%
\tikzmark{rightA}&&\tikzmark{leftB}%
\MeasureAndPlaceFigure{#2}{WidthB}{HeightB}%
\\[\VerticalSep]% Skip to next line
%
\ifdim\HeightB=0pt% Need both images to have horizontal arrrow
\else%
\DrawHorizontalArrow{rightA}{leftB}%
\fi%
%
\iftoggle{FirstRow}{}{% Vertical arrow only if NOT first row
\DrawVerticalArrow{rightC}{-\WidthC}{\HeightC}{rightA}{-\WidthA}{\HeightA}%
}%
\global\togglefalse{FirstRow}%
\let\step\@stepCD% Next invocation of \step will call stepCD
}%
\newcommand*{\@stepCD}[2]{% C <-- D
\MeasureAndPlaceFigure{#1}{WidthC}{HeightC}%
\tikzmark{rightC}&&\tikzmark{leftD}%
\MeasureAndPlaceFigure{#2}{WidthD}{HeightD}%
\\[\VerticalSep]% Skip to next line
\ifdim\HeightC=0pt\relax% Need both images to have horizontal arrrow
\else%
\DrawHorizontalArrow{leftD}{rightC}%
\fi%
\iftoggle{FirstRow}{}{% Vertical arrow only if NOT first row
\DrawVerticalArrow{leftB}{\WidthB}{\HeightB}{leftD}{\WidthD}{\HeightD}%
}%
\global\togglefalse{FirstRow}%
\let\step\@stepAB% Next invocation of \step will call stepAB
}%
\let\step\@stepAB% First invocation of \step will call \@stepAB
\makeatother
\newcounter{CurrentSate}%
\newenvironment{stepByStep}{%
\setcounter{CurrentSate}{0}%
\global\toggletrue{FirstRow}% No vertical arrow until 2nd row
\begin{tabular}{@{}c@{} p{\WidthOfArrow} @{}c@{}}%
}{%
\end{tabular}%
}%
\newcommand*{\DrawHorizontalArrow}[3][]{%
\tikz[overlay,remember picture]{%
\draw[ultra thick, ->, red,
shorten <=\ArrowImageSep, shorten >=\ArrowImageSep, #1]
($(#2)$) --
($(#3)$);
}%
}%
\newcommand*{\DrawVerticalArrow}[7][]{%
\tikz[overlay,remember picture]{%
\draw[ultra thick, ->, red,
shorten <=\ArrowImageSep, shorten >=\ArrowImageSep, #1]
($(#2)+0.5*(#3,-#4+\VerticalArrowAdjust)$) --
($(#5)+0.5*(#6,#7+\VerticalArrowAdjust)$);
}%
}%
\def\FigA{\includegraphics[width=2.0cm,height=1.0cm]{images/EiffelWide}}%
\def\FigB{\includegraphics[width=1.5cm,height=3.0cm]{images/EiffelTall}}%
\def\FigC{\includegraphics[width=3.0cm,height=1.5cm]{images/EiffelWide}}%
\def\FigD{\includegraphics[width=2.0cm,height=4.0cm]{images/EiffelTall}}%
\begin{document}
\section*{Test 1}
\begin{stepByStep}
\step{\FigA}{\FigB}
\step{\FigC}{\FigD}
\end{stepByStep}
%
\section*{Test 2}
\begin{stepByStep}
\step{\FigA}{\FigB}
\step{}{\FigD}
\end{stepByStep}
%
\section*{Test 3}
\begin{stepByStep}
\step{\FigA}{\FigB}
\step{\FigC}{\FigA}
\step{\FigD}{}
\end{stepByStep}
\end{document}