使用 TikZ 描绘音高水平

使用 TikZ 描绘音高水平

在我最近关于语调,我鼓起勇气提出一个相关的问题。在前一个问题中,特定的音高水平被连接起来以产生语调轮廓,但对于声调语言(单词中的每个音节通常都有一个与之相关的特定音高),需要一种不同的方法,其中音高水平由小水平线表示。我已经有几种方法可以解决这个问题,现在我根据 Matthew Leingang 对先前问题的方法创建了一个新的 TikZ 方法,但我相信可以改进。下面的 MWE 列出了这些选项,使用了非洲班图语言 kiKongo 的示例。

%!TEX TS-program = xelatex
%!TEX encoding = UTF-8 Unicode

\documentclass{article}

\usepackage{fontspec}
\defaultfontfeatures{Mapping=tex-text, Scale=MatchLowercase}
\setmainfont{Charis SIL}

\usepackage{tikz}
\usetikzlibrary{backgrounds}
\newcommand{\tikzpitch}[1]{\tikz[overlay, remember picture, baseline] \coordinate (#1);}%
\tikzstyle{pitchlevel}=[x=1mm, y=1mm, yshift=-7mm, semithick]

% Charis SIL pitchlevel glyphs
\newcommand \pitch[1]{\char"F1F#1}  % use \pitch1..9 to generate the 9 pitch-level marks fromthe glyphs F1F{1..9}

% John Frampton's pitchlevels code
\usepackage{pstricks}
\newdimen\dimpla
\newdimen\dimplb
\newdimen\dimplc
\def\1{\contourpitch{-2.5ex}{-2.5ex}}
\def\2{\contourpitch{-2.1ex}{-2.1ex}}
\def\3{\contourpitch{-1.7ex}{-1.7ex}}
\def\4{\contourpitch{-1.3ex}{-1.3ex}}
\def\5{\contourpitch{-.9ex}{-.9ex}}
\def\6{\contourpitch{-.5ex}{-.5ex}}
% level pitches
\def\contourpitch #1#2#3{\setbox0=\hbox{#3}\dimpla=.5\wd0 \dimplb=.5\wd0
\advance\dimpla by -.25em
\advance\dimplb by .25em
\dimplc=#1
\ifdim#2<#1 \dimplc=#2 \fi
\dimplc=-\dimplc
\advance\dimplc by 1ex
\psline(\dimpla,#1)(\dimplb,#2)
\vrule width0pt depth\the\dimplc #3}
% glides
\def\pitchup{\contourpitch{-2.5ex}{-1.7ex}}
\def\pitchdown{\contourpitch{-1.7ex}{-2.5ex}}
\def\steepup{\contourpitch{-2.5ex}{-.5ex}}

% format fbox
\setlength\fboxsep{1pt}


\begin{document}

% generate glides in Charis SIL
\fontspec[Renderer=Graphite]{Charis SIL}

\section{Pitchlevel marking using xelatex}

In Africanist linguistics, pitchlevel marks are often used so that the pitches can be discussed without drawing premature conclusions as to how these pitches should be represented in the eventual tone-marking.

\subsection{Word-side (inline) marking}

The example of \textbf{ibuuna [~\pitch1\, \pitch9\pitch9\, \pitch1~]} (\textit{so}) can be used to show inline marking of individual words.  This solution uses the pitchlevel glyphs in Charis SIL.  Glide variants are possible: 
[~\pitch1\, \pitch9\, \pitch1\pitch5~] 
[~\pitch1\, \pitch9\, \pitch1\pitch1\pitch5~] 
[~\pitch1\, \pitch9\, \pitch1\pitch1\pitch5\pitch5~] 
[~\pitch1\, \pitch9\, \pitch1\pitch5\pitch5~] 

\bigskip

\noindent An alternative solution to the same issue of showing \noindent \textbf{ibuuna} [ \tikz\draw[x=1mm, y=1mm, semithick](0,0)--(1.5,0) (2.5,2)--(5,2) (6,0)--(7.5,0); ] uses TikZ.  Glide variants are also possible here:
[ \tikz\draw[x=1mm, y=1mm, semithick](0,0)--(1.5,0) (2.5,2)--(5,2) (6,0)--(7.5,1); ]
[ \tikz\draw[x=1mm, y=1mm, semithick](0,0)--(1.5,0) (2.5,2)--(5,2) (6,0)--(7.5,0)--(8.5,1); ]
[ \tikz\draw[x=1mm, y=1mm, semithick](0,0)--(1.5,0) (2.5,2)--(5,2) (6,0)--(7.5,0)--(8.5,1)--(10,1); ]
[ \tikz\draw[x=1mm, y=1mm, semithick](0,0)--(1.5,0) (2.5,2)--(5,2) (6,0)--(7.5,1)--(9,1); ]


\subsection{Parallel marking}

\subsubsection{Using Charis font features}

\noindent ibuuna · basiidi kilumbu · ki-bakweenda kuna-kiziitu \\
\pitch1~~\pitch9\pitch9~~\pitch1~~~\pitch1~~\pitch9~~\pitch7~~\pitch5~\pitch3~~~~~\pitch1\pitch5 ~~~~\pitch1~~~\pitch1~~~~\pitch9\pitch9~~~\pitch8~~~\pitch4~~\pitch1~~~\pitch5~\pitch5~\fbox{\pitch1\pitch5} \\
\textit{so · they set aside a day · on which they would go to the in-laws \dots}

\subsubsection{Using pstricks}

\noindent\1ib\5u\5un\1a · b\1as\5i\5id\4i k\3il\2umb\pitchup u · k\1i-b\1akw\5e\5end\4a k\3un\1a-k\3iz\3i\3it\fbox{\pitchup u}\\
\textit{so · they set aside a day · on which they would go to the in-laws \dots}

\subsubsection{Using TikZ}

\noindent\begin{tikzpicture}[remember picture]
\node{\tikzpitch{1}ib\tikzpitch{2}u\tikzpitch{3}un\tikzpitch{4}a · b\tikzpitch{5}as\tikzpitch{6}iid\tikzpitch{7}i k\tikzpitch{8}il\tikzpitch{9}umb\tikzpitch{10}u · k\tikzpitch{11}i-b\tikzpitch{12}akw\tikzpitch{13}eend\tikzpitch{14}a k\tikzpitch{15}un\tikzpitch{16}a-k\tikzpitch{17}iz\tikzpitch{18}iit\tikzpitch{19}u}; \\
\draw[pitchlevel] (1 |- 0,0)--+(2,0) (2 |- 0, 4.5)--+(2,0) (3 |- 0, 4.5)--+(2,0) (4 |- 0, 0)--+(2,0) (5 |- 0,0)--+(2,0) (6 |- 0, 4.5)--+(2,0) (7 |- 0, 4)--+(2,0) (8 |- 0, 3)--+(2,0) (9 |- 0,1.5)--+(2,0) (10 |- 0, 0)--+(1,0.5)--+(2,1.5) (11 |- 0,0)--+(2,0) (12 |- 0, 0)--+(2,0) (13 |- 0, 4.5)--+(3,0) (14 |- 0, 3.5)--+(2,0) (15 |- 0,3)--+(2,0) (16 |- 0, 1)--+(2,0) (17 |- 0, 3)--+(2,0) (18 |- 0, 3)--+(2,0) (19 |- 0,0)--+(1,0.5)--+(2,1.5);
\end{tikzpicture}\\
\mbox{\textit{so · they set aside a day · on which they would go to the in-laws \dots}}

\end{document}

在此处输入图片描述

内联标记(1.1)很简单。关于并行标记(1.2)的三个版本:

tipa1.2.1:Charis SIL 只能与 XeTeX 一起使用。(对于 LaTeX,可以使用包和一些附加代码的替代方法- 请参阅这个问题。)这似乎是目前唯一包含这些字形的字体(在其私人使用区),但实际上这不太可能成为问题。主要问题是,就像在语调问题中一样,需要使用反复试验来将字形隔开以匹配单词。一个好处是可以使用它\fbox来突出显示特定的音调。

1.2.2:该pstricks代码由 的作者 John Frampton 友情提供expex。主要优点是它将音高级别嵌入文本中,使示例更容易写出。\fbox可用于突出显示音高(尽管它也会标记字母,这在视觉上不太美观)。音高级别线也比其他两个选项“更清晰”。与此相反,它的编译速度较慢,并且当前代码不允许在单词侧(内联)进行音高标记。(它似乎也不适用于 LaTeX,但也许这只是我的看法。)

1.2.3:这是我通过骚扰 Matthew Leingang 的语调问题解决方案整理出来的(在 mwibrow 更全面的代码上,我甚至不知道从哪里开始!)。我所做的就是获取坐标,然后从该坐标向右 2 毫米处画一条线。(理想情况下,我会在坐标的每一侧画 1 毫米,但该怎么做呢?)这实际上效果很好 - 特别是,可以更详细地指定滑行。但是,就目前而言,由于 ,它需要两次编译remember picture,而且我无法突出显示音高。此外,出于某种原因,kiKongo 文本似乎略有缩进。

所以所有这些都有效,但我对任何改进建议都很感兴趣,特别是整理 TikZ 解决方案,因为它适用于 LaTeX 和 XeTeX,并且与字体无关。

答案1

编辑添加一些修复和内联使用的示例(可能应该与宏绑定)。

通过对语调问题的代码进行一些额外的添加(我在这里提供了整个代码,因为它包含许多修复),可以添加标记。不确定字体是否独立,可能不是目前的情况。毫无疑问,如果不进行额外的工作,对于从右到左的语言来说,这将是垃圾。

基本思想是将符号与标记关联起来,然后在字符排版后解析这些标记。主要目的是能够将标记符号对齐在文本上方,这看起来比将标记放在文本中更清晰一些。

\contour[contour marks={0.55.0..0.55.?..3.2..+..0..0..5|..!..3.2..3.33.+}]
                       {ibuuna basiidi kilumbu ki-bakweenda kuna-kiziitu};

下面显示的例子在语言上并不正确,但只是说明了它能做什么。

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{fit}

\newdimen\contourraise
\newdimen\contourspacetokenwidth
\newdimen\contourtokenkern
\newcount\lasttokennumber
\newcount\currenttokennumber
\newcount\contourmarkcount
\newcount\contourtokenunderlinestate
\newbox\contourbox
\makeatletter

\tikzset{
    tight fit/.style={
        inner sep=0pt,
        outer sep=0pt,
    },
    %
    %
    % How far above the reference anchor of the text,
    contour raise/.code=\pgfmathsetlength\contourraise{#1},
    contour reference anchor/.store in=\contourreferenceanchor,
    contour reference anchor=base east,
    % The `scale' for the values in the contour height specification
    contour scale/.store in=\contourscale,
    contour scale=3pt,
    % The prefix for the contour marks.
    contour mark prefix/.store in=\contourmarkprefix,
    contour mark prefix=contour,
    % The style for the contour path
    contour/.style={
        draw, 
        rounded corners=1ex,
    },
    % The style for the token nodes
    every contour token/.style={
        anchor=base west, 
        tight fit,
    },
    contour underline/.style={
        draw
    },
    % The character to insert a mark (use with care)
    contour mark character/.store in=\contourmarkchar,
    contour mark character=|,
    % Want to change the code for contour marks? Use this key.
    contour mark code/.store in=\contourmarkcode,
    % Want to change the code for tokens? Use this key.
    contour token code/.store in=\contourtokencode,
    % Want to change the code for drawing the contour? Use this  key.
    contour code/.store in=\contourcode,
    %
    % Default stuff
    contour mark code={%
        \coordinate (\contourmarkprefix-\the\contourmarkcount)
          at ([yshift=\contourraise, y=\contourscale,               
          shift={(0,\currentcontourheight)}]token-\the\currenttokennumber.\contourreferenceanchor);
    },
    contour token code={%
        \node [every contour token/.try] at 
        ([xshift=\contourtokenkern]token-\the\lasttokennumber.base east) 
            (token-\the\currenttokennumber) {\token};
    },
    contour code={
        \draw [contour] (\contourmarkprefix-1)
            \foreach \y in {2,...,\the\contourmarkcount}{ -- 
                    (\contourmarkprefix-\y) };                  
    },
    contour marks/.style={
        contour mark list={#1},
        contour code={
             \draw [y=\contourscale, contour] \contourpath;                  
         },
         contour mark code={%
            \coordinate (@a) at ([yshift=\contourraise]token-\the\currenttokennumber.base west);
            \coordinate (@b) at ([yshift=\contourraise]token-\the\currenttokennumber.base east);
            \node [tight fit, fit={(@a) (@b)}] (\contourmarkprefix-\the\contourmarkcount) {};
        },
    },
    % Don't draw the contour.
    tokens only/.style={
        contour code={}
    },
    %
    % Only draw the contour (but the space is still used for the tokens)
    contour only/.style={
        every contour token/.append style={
            execute at begin node={\setbox\contourbox=\hbox\bgroup},
            execute at end node=\egroup\phantom{\box\contourbox}%
        },
        underline/.style={
            draw=none
        }
    },
    %
    % Make tokens follow the contour marks.
    tokens follow contour/.style={
        tokens only,
        contour token code={%
            \node [every contour token/.try, y=\contourscale] at 
                ([xshift=\contourtokenkern]token-\the\lasttokennumber.base east |- 
                0,\currentcontourheight) 
                (token-\the\currenttokennumber) {\token};
        },
    },
    % What style to use when drawing underline
    underline/.style={
        draw
    },
    % The underline is drawn along the south side of a node which 
    % takes this style.
    underline token/.style={
        inner ysep=1pt
    },
    % When grouping tokens (e.g., for putting box around)
    % this style is applied to a node that is fitted around the group
    token group/.style={
        inner xsep=1pt,
        inner ysep=2pt,
        rounded corners=2pt
    },
    % Draw boxes around tokens groups.
    box tokens/.style={
        token group/.append style={
            draw
        }
    },  
    % Change the width of the spaces.
    space token width/.code=\pgfmathsetlength\contourspacetokenwidth{#1},
    space token width=0.125cm,
    contour mark list/.store in=\@contourmarklist%
}




\def\at@{@}

\let\@contourmarklist=\@empty

\def\contour{%
    \pgfutil@ifnextchar[{\contour@opts}{\contour@opts[]}}
\def\contour@opts[#1]{%
    \pgfutil@ifnextchar x{\contour@@opts[#1]}{\contour@@opts[#1]}}
\def\contour@@opts[#1]#2;{%
    \begin{scope}[#1]
        \coordinate (token-0);
        \currenttokennumber=0\relax%
        \lasttokennumber=0\relax%
        \contourmarkcount=0\relax%
        \def\lastcontourheight{0}%
        \contourtokenunderlinestate=0\relax%
        \let\lastcontourtoken=\relax%
        \contourtokenkern=0pt\relax%
        \def\contourpath{}%
        \@contour#2@%
}


% Must check for a spaces
\def\@contour{\futurelet\@token\@checkforspace}

\def\@uscore{_}
\def\@checkforspace{%
    \ifx\@token\pgfutil@sptoken%
        \let\@next=\@replacespace%
    \else%
        \if\@token\contourmarkchar%
            \let\@next=\@contour@insertmark
        \else%
            \if\@token\@uscore
                \let\@next=\@contourtoggleunderline%
            \else%
                \let\@next=\@@contour%
            \fi%
        \fi%
    \fi%
    \@next%
}

\def\@contourtoggleunderline#1{%
    \advance\contourtokenunderlinestate by1\relax
    \ifnum\contourtokenunderlinestate>3\relax%
        \contourtokenunderlinestate=0\relax%
    \fi%
    \@contour%
}

\def\@contour@insertmark{%
    \afterassignment\@@contour@insertmark\let\@token=%
}

\def\@@contour@insertmark{%
    \futurelet\@token\@@@contour@insertmark}%



\def\@@@contour@insertmark{%
    \if\@token[%
        \let\@next=\@@@@contour@insertmark%
    \else%
        \let\currentcontourheight=\lastcontourheight%
        \let\@next=\@@@@@contour@insertmark%
    \fi%
    \@next%
}


\def\@@@@contour@insertmark[#1]{%
    \def\@tmp{#1}%
    \ifx\@tmp\@empty%
        \let\currentcontourheight=\lastcontourheight%
    \else%
        \def\currentcontourheight{#1}%
    \fi%
    \@@@@@contour@insertmark}

\def\@@@@@contour@insertmark{%
    \advance\contourmarkcount by1\relax%
     % Code for inserting mark
    \contourmarkcode%
    \let\lastcontourheight=\currentcontourheight%
    \@contour}

\def\contourspacetoken{{\hbox to \contourspacetokenwidth{\hfill}}}

\def\@replacespace#1{%
    \@contour\contourspacetoken#1%
}

\def\@@countour@afterlatenode{%
    \pgf@x=\pgfpositionnodelatermaxx\relax%
    \advance\pgf@x by-\pgfpositionnodelaterminx\relax%
    \global\edef\@contournodewidth{\the\pgf@x}%
}

\def\@@contour#1{%
    \def\@token{#1}%
    \if\@token\at@%
        \@contourdounderline%
        \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{}{%
            \node [tight fit, fit={(tokengroup)}, token group/.try] {};
            \global\let\pgf@sh@ns@tokengroup=\relax%
        }%
        \let\@next=\@@@contour%
    \else%
        \lasttokennumber=\currenttokennumber%
        \advance\currenttokennumber by1%
        \contourtokenkern=0pt\relax%
        \ifnum\currenttokennumber>1\relax%
            %
            % Take care of kerning.
            % 
            % First get the width of the last and current token in the same hbox.
            %
            \let\pgfpositionnodelaterbox=\contourbox
            \pgfpositionnodelater\@@countour@afterlatenode%
            \def\token{\lastcontourtoken\@token}%
            \begingroup%
                \tikzset{every contour token/.append style={tight fit}}%
                \contourtokencode%
            \endgroup%
            \let\@contourkerntmp=\@contournodewidth%
            % 
            % Now subtract the width of last and current token in separate boxes.
            %
            \def\token{\hbox{\lastcontourtoken}\hbox{\@token}}%
            \begingroup%
                    \tikzset{every contour token/.append style={tight fit}}%
                    \contourtokencode%
            \endgroup%
            \pgfmathsetlength\contourtokenkern{\@contourkerntmp-\@contournodewidth}%
            \pgfpositionnodelater\relax%
        \fi%
        %
        % OK, now actually typset the current token
        %
        \let\token=\@token%
        \contourtokencode%
        \let\lastcontourtoken=\token%
        % Manage underline state
        \@contourdounderline%
        \def\@@token{\contourspacetoken}%
        \ifx\@token\@@token%
            \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{}{%
                \pgfutil@ifundefined{pgf@sh@ns@underline}{}{%
                    \node [tight fit, fit={(tokengroup) (underline)}] 
                    (tokengroup) 
                {};}%
                \node [tight fit, fit={(tokengroup)}, token group/.try] {};
                \global\let\pgf@sh@ns@tokengroup=\relax%
            }%
        \else
            \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{%
                \node [tight fit, 
                fit={(token-\the\currenttokennumber)}] 
                (tokengroup) {};
            }{%
                \node [tight fit, 
                fit={(token-\the\currenttokennumber) 
                (tokengroup)}] 
                (tokengroup){};
            }%
        \fi%
        \let\@next=\@contour
        %
    \fi%
    \@next%
}

\def\@contourdounderline{%
    \ifcase\contourtokenunderlinestate%
     \or
         \node [tight fit, fit={(token-\the\currenttokennumber)}] 
         (underline) {};
         \contourtokenunderlinestate=2\relax%
     \or%
            \node [tight fit,fit={(token-\the\currenttokennumber) (underline)}]
            (underline) {};
     \or%
            \node [tight fit, fit={(underline)}, underline token/.try] 
            (underline) {};
         \draw [underline/.try]
                    (underline.south west) -- (underline.south east);
            \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{}{%
                 \node [tight fit, fit={(tokengroup) (underline)}] 
                 (tokengroup) {};%
                 \node [tight fit, fit={(tokengroup)}, token group/.try] {};
                 \global\let\pgf@sh@ns@tokengroup=\relax%
                 \global\let\pgf@sh@ns@underline=\relax%
             }
         \contourtokenunderlinestate=0\relax
     \fi%
}
\def\@@@contour{%
    \ifx\@contourmarklist\@empty%
    \else%
        \@contourdolist%
    \fi%
    \ifnum\contourmarkcount>1
        % Code for drawing contour
        \contourcode%
    \fi%
    \end{scope}%
    \ignorespaces%
}



\def\@contourstackpop{%
    \let\@contourstackitem=\@empty%
    \ifx\@contourstack\@empty%
    \else%
        \expandafter\@@contourstackpop\@contourstack\@@contourstackpop%
    \fi%
}


\def\@@contourstackpop#1#2\@@contourstackpop{%
    \def\@contourstackitem{#1}%
    \ifx\@contourstackitem\@empty%
        \def\@contourstackitem{#2}%
        \let\@contourstack=\@empty%
    \else%
        \def\@contourstack{#2}%
    \fi%
}

\def\@contourdolist{%
    \let\@contourstack=\@contourmarklist%
    \let\@contourstacklastitem=\@empty%
    \let\contourpath=\@empty%
    \edef\contourtotaltokens{\the\currenttokennumber}%
    \currenttokennumber=0\relax%
    \contourmarkcount=0\relax%
    \@@contourdolist%
}

\def\@@contourdolist{%
    \@contourstackpop%
    \advance\currenttokennumber by1\relax%
    \ifx\@contourstackitem\@empty%
        \let\@next=\relax%
    \else%
        \expandafter\ifx\csname contourcontourpathcommand@\@contourstackitem @\endcsname\relax%
        \else%
            \advance\contourmarkcount by1\relax%
            \let\currentcontourheight=\@contourstackitem%
            \contourmarkcode%
            \def\contourmarkstart{\contourmarkprefix-\the\contourmarkcount.west}%
            \def\contourmarkend{\contourmarkprefix-\the\contourmarkcount.east}%         
            \edef\contourpath{\contourpath \csname contourcontourpathcommand@\@contourstackitem @\endcsname}%
        \fi%
        \let\@next=\@@contourdolist%
        \let\@contourstacklastitem=\@contourstackitem%
    \fi
    \@next%
}


% \contourcontourpathcommand{<symbol>}{<contour path command code>}
% \contourmarkstart and \contourmarkend are setup as the
% left and right points of the charactor at zero contour height.
\def\contourcontourpathcommand#1{\expandafter\def\csname contourcontourpathcommand@#1@\endcsname}

% \contourmark{<symbol>}{<mark start height>}{<mark end height>}
%

\def\contourmark#1#2#3{%
    \contourcontourpathcommand{#1}{([shift={(0,#2)}]\contourmarkstart) -- ([shift={(0,#3)}]\contourmarkend)}
}

\makeatother

% Separator. 
\contourcontourpathcommand{.}{}

\contourmark{0}{0}{0}
\contourmark{1}{1}{1}
\contourmark{2}{2}{2}
\contourmark{3}{3}{3}
\contourmark{4}{4}{4}
\contourmark{5}{5}{5}
\contourmark{+}{0}{1}

\contourcontourpathcommand{!}{
    (\contourmarkstart) .. controls ++(0,2) and ++(0,2) .. (\contourmarkend)
}

\contourcontourpathcommand{?}{
    (\contourmarkstart) .. controls ++(0,-1) and ++(0,-1) .. ([shift={(0,2)}]\contourmarkend)
}
\contourcontourpathcommand{|}{
    % Continue previous path
    -- ([shift={(0, 3)}]\contourmarkend)
}



\begin{document}

\begin{tabular}{c}
\begin{tikzpicture}
\contour[contour/.append style={rounded corners=0}, contour raise=-0.75cm,
    contour marks={0.55.0..0.55.?..3.2..+..0..0..5|..!..3.2..3.33.+}]
                  {ibuuna basiidi kilumbu ki-bakweenda kuna-kiziitu};

\end{tikzpicture}
\\[0.5cm]
in line marking: ibuuna [\tikz[baseline={(0,0.25ex)}]%  
\contour[contour only, contour scale=2ex/6,
    contour marks={0.55.0}]
                  {ibuuna};]

\end{tabular}
\end{document}

在此处输入图片描述

相关内容