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