如何借助 tikz 来说明扫视?

如何借助 tikz 来说明扫视?

当我们阅读时,我们的眼睛会进行所谓的扫视,也就是在“音节”之间进行“跳跃”(实际上是从一个单词存在跳到另一个单词存在(或者从一个单词图像跳到另一个单词图像——抱歉,我不知道正确的术语是什么)。我找到了一个描述这种情况的草图(版本 B):

在此处输入图片描述音节

我的问题是:如何使用 tikz 在自己的文本上重建扫视?有没有办法自动从单词存在跳转到单词存在,还是我必须手动设置文本中的节点?背景:我有机会做一个演讲,想说明为什么太长的行不便于阅读(典型的 A4 纸设置)。我还想说明为什么像 Tufte 课程中那样较短的行便于阅读(“但这不是浪费纸张吗?”“不。如果只是因为你的行太长而没人想读你的论文,那就浪费纸张了!”)

答案1

此解决方案至少存在一个问题:扫视间箭头不足。已编辑,通过禁用scalerel包的数学样式保存功能可大大提高效率。虽然这通常是一项很棒的功能,但如果嵌套 3 个scalerel调用(如 中所示)\saccade,则每次调用都会构建 4^3 个数学选择框。

宏是

\saccade{text}提供文本上的扫视弧线

\Comment[alignment]{comment text}{\saccade-group}在扫视之上发表评论

\Ssaccade{starting text}开始扫视

\Esaccade{ending text}结束扫视

\labelsaccade[lines of depth]{label}提供左侧标签,分配垂直深度

\nosaccade{text}避免扫视弧

\noSsaccade{text}避免在文本行开头出现扫视弧

\dashsaccade{text}(不幸的是,虚线的宽度会随着文本长度而改变)。

该宏 \saccade实际上有一个可选参数,用户可以忽略,但被\Ssaccade和使用\Esaccade

如果扫视的水平对齐不正确,OP 可以告诉我,然后我可以进行调整。

\documentclass{article}
\usepackage{scalerel,trimclip,xcolor,graphicx}
\usepackage[usestackEOL]{stackengine}
\renewcommand\stacktype{L}
\renewcommand\useanchorwidth{T}
\def\Frown{\addstackgap[.2pt]{$\mkern-.9mu\frown\mkern-.7mu$}}
\newcommand\saccade[2][\Frown]{\let\mathchoice\relax%
  \stackon[\dimexpr\ht\strutbox+1pt\relax]{\bfseries#2}{%
    \stretchto{%
      \scaleto{%
        \scalerel*[\widthof{\bfseries#2}]{#1}%
                  {\rule[-\textheight/2]{1ex}{\textheight}}%
      }{\textheight}%
    }{2.5ex}%
  }\ignorespaces%
}
\newcommand\Ssaccade[1]{\saccade[\clipbox{4pt 0pt .1pt 0pt}{\Frown}]{\hspace{9ex}#1}}
\newcommand\Esaccade[1]{\saccade[\clipbox{.1pt 0pt 4pt 0pt}{\Frown}]{#1\hspace{9ex}}}
\newcommand\Comment[3][c]{\renewcommand\stackalignment{#1}%
  \stackon[2\baselineskip]{#3}{\small\itshape\Longstack[l]{#2}}\ignorespaces}
\newcommand\labelsaccade[2][2]{\rule[-#1\baselineskip]{0pt}{1pt}%
  \raisebox{\baselineskip}{\Large #2\hspace{1cm}}\ignorespaces}
\newcommand\crossrule[2]{\hspace{#1}\rule[.5pt]{#2}{1pt}}
\def\dashFrown{\vphantom{\Frown}\setbox0=\hbox{%
  \stackinset{c}{}{b}{}{\textcolor{white}{\rotatebox{150}{\crossrule{1.4ex}{.4ex}}}}{%
  \stackinset{c}{}{b}{}{\textcolor{white}{\rotatebox{30}{\crossrule{1.3ex}{.4ex}}}}{%
  \stackinset{c}{}{b}{-1pt}{\textcolor{white}{\rotatebox{90}{\crossrule{1ex}{.3ex}}}}{%
    \Frown%
}}}}\smash{\box0}}
\newcommand\nosaccade[1]{\textbf{#1}\ignorespaces}
\newcommand\noSsaccade[1]{\hspace{9ex}\textbf{#1}\ignorespaces}
\newcommand\dashsaccade[1]{\saccade[\dashFrown]{#1}}
%%% THE FOLLOWING DISABLES MATH-MODE PRESERVATION OF scalerel, WHICH MAKES
%%% NESTED scalerel MACROS MUCH MORE EFFICIENT BY ELIMINATING NESTED \mathchoice
\makeatletter
  \edef\m@switch{T}\LMex=1ex\relax\LMpt=1pt\relax%
  \renewcommand\ThisStyle[1]{\ifmmode\def\@mmode{T}#1\else\def\@mmode{F}#1\fi}
\makeatother
%%%
\begin{document}
\labelsaccade{A}
\Comment[l]{Entry\\saccade}{\Ssaccade{Ji}}
\Comment{Minisaccades (without estim.)}
{\saccade{m dr}\saccade{es-s}\saccade{eX w}\saccade{ell y}\saccade{es-t}\saccade{er-d}}
\Comment[r]{Exit\\saccade}{\Esaccade{ay}}

\labelsaccade{B}
\Comment[l]{Entry\\saccade}{\Ssaccade{Ji}}
\Comment{Minisaccades (with correct estim.)}
{\saccade{m dr}\saccade{es-sed w}\saccade{ell y}\saccade{es-t}\saccade{er-d}}
\Comment[r]{Exit\\saccade}{\Esaccade{ay}}

\labelsaccade{C}
\Comment[l]{Entry\\saccade}{\Ssaccade{Ji}}
\Comment{Minisaccades (with bad estim.)}
{\saccade{m dr}\saccade{es-ses w}\saccade{ell y}\saccade{es-t}
\stackunder[2\baselineskip]{\dashsaccade{er-d}\nosaccade{ay}}{%
  \llap{\rotatebox{50}{$\leftarrow$}}\small\itshape\Longunderstack[l]{\rule{9ex}{2pt}\kern1ex\\Conflict\\detected}}}

\labelsaccade{D}
\noSsaccade{Jim dr}
\saccade{es-sed w}\saccade{ell y}\saccade{es-t}\saccade{er-d}
\Comment[r]{Exit\\saccade}{\Esaccade{ay}}
\end{document}

在此处输入图片描述

答案2

有一个解决方案的想法,但还可以做很多工作来使宏/样式更加灵活和可重用:

\documentclass[border=4pt,tikz]{standalone}
\usetikzlibrary{calc}
\tikzset{
  minisaccade/.style={inner xsep=0pt},
  labelsaccade/.style={font=\itshape\small, align=left, inner xsep=0pt},
  connectsaccade/.style={},
}
\newcommand{\placeseq}[3]{%
  \node (#2) at (#1) {#2\strut};
  \path (#2.south east) +(5.5em,0) coordinate (#2init);
  \foreach \elem[remember=\elem as \lastelem (initially init)] in {#3} {%
    \node[minisaccade,anchor=south west] at (#2\lastelem.south east) 
      (#2\elem) {\strut\elem};
  }
}
\newcommand{\entryto}[3][]{%
  \draw[#1] (#2#3.north) to[out=90,in=0] +(-1em,0.75em) -- +(-5em,0)
    node[labelsaccade,above,anchor=south west] {Entry\\saccade}%
}
\newcommand{\exitfrom}[3][]{%
  \draw[#1] (#2#3.north) to[out=90,in=180] +(1em,0.75em) -- +(5em,0)
    node[labelsaccade,above,anchor=south east] {Exit\\saccade}%
}
\newcommand{\connect}[4][]{%
  \draw[connectsaccade,#1] (#2#3.north) 
    to[out=90,in=180] ($(#2#3.north)!0.5!(#2#4.north)+(0,0.75em)$) 
    to[out=0,in=90] (#2#4.north)
}
\newcommand{\labelover}[5][]{%
  \node[labelsaccade,anchor=south,#1] 
    at ($(#2#3.north west)!0.5!(#2#4.north east)+(0,0.75em)$) {#5}
}

\begin{document}
\begin{tikzpicture}
 % Sequence A
  \placeseq{0,0}{A}{Jim, ,dres,--,seX, ,well, ,yes,--,ter,--,day}
  \connect{A}{Jim}{dres};
  \connect{A}{dres}{seX};
  \connect{A}{seX}{well};
  \connect{A}{well}{yes};
  \connect{A}{yes}{ter};
  \connect{A}{ter}{day};
  \entryto {A}{Jim};
  \exitfrom {A}{day};
  \labelover{A}{Jim}{day}{Minisaccades (without estim.)};
 % Sequence B
  \placeseq{0,-2}{B}{Jim, ,dres,--,sed, ,well, ,yes,--,ter,--,day}
  \connect{B}{Jim}{dres};
  \connect{B}{dres}{well};
  \connect{B}{well}{yes};
  \connect{B}{yes}{ter};
  \connect{B}{ter}{day};
  \entryto {B}{Jim};
  \exitfrom {B}{day};
  \labelover{B}{Jim}{day}{Minisaccades (without correct estim.)};
 % Sequence C
  \placeseq{0,-4}{C}{Jim, ,dres,--,ses, ,well, ,yes,--,ter,--,day}
  \connect{C}{Jim}{dres};
  \connect{C}{dres}{well};
  \connect{C}{well}{yes};
  \connect{C}{yes}{ter};
  \connect[dashed]{C}{ter}{day};
  \entryto {C}{Jim};
  \labelover{C}{Jim}{day}{Minisaccades (with bad estim.)};
 % Sequence D
  \placeseq{0,-7}{D}{Jim, ,dres,--,sed, ,well, ,yes,--,ter,--,day}
  \connect{D}{dres}{sed};
  \connect{D}{sed}{well};
  \connect{D}{well}{yes};
  \connect{D}{yes}{ter};
  \connect{D}{ter}{day};
  \exitfrom {D}{day};
  \draw[-latex,shorten >=5pt] (Cter) to[out=-90,in=90] 
    node[labelsaccade,pos=0.25,below right] {Conflict\\detected} (Ddres);
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案3

并不完美,但基本上只是演示了文本的解析,因此可以说......

\begin{tikzpicture}
\saccadetext{Jim dres-ses him-self to-day and yes-ter-day I think}

\draw [dashed] (self.north)  to [saccade]         (to.north);
\draw [red]    (day-1.south) to [saccade=-0.5cm] (day-2.south);

\foreach \j [evaluate={\i=int(\j-1);}] in {2,...,5,7,8,...,13}
   \draw (syllable-\i.north) to [saccade] (syllable-\j.north);

\end{tikzpicture}

...生产:

在此处输入图片描述

每个节点都以其音节名称命名,重复的音节会自动添加后缀,因此上述示例将导致名为day-1和 的节点day-2。此外,每个音节都可以称为syllable-1syllable-2等等。

对于比英语更“令人兴奋”的语言来说,它很可能会失败。

\documentclass[tikz,border=20]{standalone}
\usetikzlibrary{chains}
\makeatletter
\newcount\syllablecount

\def\parsesaccadetext#1{\syllablecount=0\relax\@parsesaccadetext#1 @stop@}
\def\@stop@{@stop@}
\def\@parsesaccadetext#1 {%
  \let\@next=\relax%
  \def\@tmp{#1}%
  \ifx\@tmp\@stop@%
  \else%
    \let\@next=\@parsesaccadetext%
    \@parsesaccadesyllable#1-@stop@-%
    \let\@next=\@parsesaccadetext%
  \fi%
  \@next%
}

\def\@parsesaccadesyllable#1-{%
  \def\@tmp{#1}%
  \ifx\@tmp\@stop@%
    \let\@next=\relax%
  \else%
    \ifx\@next\@parsesaccadesyllable%
      \dosaccadehyphen%
    \fi%
    \dosaccadesyllable{#1}%
    \let\@next=\@parsesaccadesyllable%
  \fi%
  \@next%
}

\def\getnodename#1{%
  \expandafter\ifx\csname saccade@syllable#1\endcsname\relax
    \expandafter\def\csname saccade@syllable#1\endcsname{1}%
    \edef\nodename{\saccadeprefix#1}%
  \else
    \ifnum\csname saccade@syllable#1\endcsname=1%
      \pgfnodealias{\saccadeprefix#1-1}{\saccadeprefix#1}%
     \fi
     \count0=\csname saccade@syllable#1\endcsname\relax%
     \advance\count0 by 1\relax%
     \expandafter\edef\csname saccade@syllable#1\endcsname{\the\count0}%
     \edef\nodename{\saccadeprefix#1-\the\count0}%
  \fi
}
\def\dosaccadesyllable#1{%
  \getnodename{#1}%
  \node [on chain, anchor=base west, inner xsep=.5ex, every syllable/.try] 
    (\nodename) {#1};
  \advance\syllablecount by1\relax%
  \pgfnodealias{\saccadeprefix syllable-\the\syllablecount}{\nodename}%
}
\def\dosaccadehyphen{%
  \node [on chain, anchor=base west, inner xsep=-.5ex] {-};
}

\tikzset{%
  saccade prefix/.store in=\saccadeprefix,
  saccade prefix=,
  saccade/.style={
    to path={
      (\tikztostart) .. controls ++(90:#1) and ++(90:#1) .. (\tikztotarget)
    }
  },
  saccade/.default=0.25cm,
  every syllable/.style={inner ysep=0pt, execute at begin node=\strut}
}
\newcommand\saccadetext[2][]{
    \begin{scope}[start chain=going {at=(\tikzchainprevious.base east)},#1]
      \parsesaccadetext{#2}
    \end{scope}
}
\begin{document}
\begin{tikzpicture}

\saccadetext{Jim dres-ses him-self to-day and yes-ter-day I think}

\draw [dashed] (self.north)  to [saccade]         (to.north);
\draw [red]    (day-1.south) to [saccade=-0.5cm] (day-2.south);

\foreach \j [evaluate={\i=int(\j-1);}] in {2,...,5,7,8,...,13}
   \draw (syllable-\i.north) to [saccade] (syllable-\j.north);

\end{tikzpicture}

\end{document}

相关内容