我有一个 LaTeX 文档,其中有一个名为“composition”的二元运算符。通常,它写成一个带圆圈的字母,其中字母作为参数传递:\comp{x}
。我可以使用类似于以下的技术来实现良好的效果如何给操作符加上圆圈?。
但是,有时运算符的内容会变得很大,例如,某些计算会渲染为几个字母长:\comp{x + y + z}
。在这种情况下,我不想要一个直径大到足以容纳文本的巨大圆圈。我希望运算符延伸成一个菱形,其长度取决于内容的长度。因此,如果内容只有一个字母,它只是一个圆圈中的一个字母。如果它是几个字母,它会将圆圈水平扩展为包含内容的胶囊。
我完全控制了构建环境和软件包选择。文档会很长(超过 200 页),因此构建时间是一个问题。但是,机器非常强大,因此“重”解决方案可能没问题。运算符在数学模式下使用,偶尔用作上标/下标,但永远不会变得很小。我该如何实现这个目标?
答案1
这个答案处理 TeX/LaTeX 的数学排版问题。起点是 JLDiaz 的解决方案,它基于 Andrew Swann 的答案。
\comp
应该是二元运算符(\mathbin
)。的内容
\comp
设置为数学。如果在下标、分数内使用数学,则数学会自动调整大小。
tikz
解决方案切换到文本模式并返回数学。我们需要知道哪种样式处于活动状态(\displaystyle
、\textstyle
、\scriptstyle
、\scriptscriptstyle
)。LaTeX\mathpalette
有助于设置此样式(它使用 TeX 的\mathchoice
)。数学符号周围可以添加空格,由 控制
\mathsurround
。当我们从文本切换到数学符号时,这些空格不能添加。LaTeX 可以\m@th
帮到这种情况,因为它设置\mathsurround
为0pt
。我从 切换到
ex
以mu
获得更好的缩放行为,并使用 dimen 寄存器来设置包括和在内tikz
的参数。inner sep
line width
在脚本样式中,TeX 不会在二元运算符周围设置额外的空间。但恕我直言,如果相邻符号比其内容更靠近框,它看起来会有点奇怪。因此,在脚本样式中添加了一半细的空格。在显示和文本样式中添加了四分之一细的空格。
\nonscript
忽略脚本样式中的以下数学胶水。此外
minimum width
,还添加了一条隐形规则来改善微小或空内容的行为。\comp
有一个可选参数,可以将选项传递给节点设置。
文件:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\usepackage{color}
\makeatletter
\newcommand*\comp[2][]{%
\ensuremath{%
\mathbin{%
\mathpalette{\comp@aux{#1}}{#2}%
}%
}%
}
\newdimen\comp@unit
\newcommand*{\comp@aux}[3]{%
#2%
\mskip.5\thinmuskip\nonscript\mskip-.25\thinmuskip
\begingroup
\sbox0{%
$%
\m@th % \mathsurround=0pt
#2% \displaystyle, \textstyle, ...
\mkern9mu %
$%
}%
\edef\x{\endgroup
\comp@unit=\the\wd0 %
}\x
\tikz[baseline=(char.base)]{%
\node[
rectangle,
draw,
minimum height=2\comp@unit,
minimum width=2\comp@unit,
rounded corners=\comp@unit,
inner sep=.33\comp@unit,
line width=.05\comp@unit,
#1%
] (char) {%
$%
\m@th % \mathsurround=0pt
#2% \displaystyle, \textstyle, ...
\rule{0pt}{\comp@unit}%
#3%
$%
};%
}%
\mskip.5\thinmuskip\nonscript\mskip-.25\thinmuskip
}
\makeatother
\begin{document}
$a \oplus b \ominus c$
$a \comp{x} b \comp{y} c \comp{} d \comp{Z} e \comp{\cdot} f$
$a \comp{x+y+z} b \comp{\displaystyle\int_0^\infty \frac{x}{z}\,\mathrm{d}x} c$
$a \comp{x} b^{c \comp{y} d}
\frac{e \comp{z} f}{g \comp{2x} h_{i \comp{2y} j}}$
$a \comp[red]{r} b \comp[green]{g} c \comp[blue]{b} d$
\end{document}
答案2
使用tikz
Andrew Swann 的答案中的方法,但使用圆角矩形而不是椭圆形,并仔细设置角半径和矩形高度,您可以得到一个很好的近似值:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\thispagestyle{empty}
\usetikzlibrary{shapes}
\newcommand*\compel[1]{\tikz[baseline=(char.base)]{
\node[rectangle,draw,minimum height=3ex, minimum width=3ex, rounded corners=1.5ex] (char) {#1};}}
\compel{x} \compel{\( x+y+z \)}
\end{document}
答案3
调整答案以制作 \textcircled 数字的好方法?可以使用ellipse
来自的形状tikz
。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\newcommand*\compel[1]{\tikz[baseline=(char.base)]{
\node[ellipse,draw,inner sep=0.5pt] (char) {#1};}}
\begin{document}
\compel{x} \compel{\( x+y+z \)}
\end{document}
形状cylinder
与你的愿望非常接近
\newcommand*\compcy[1]{\tikz[baseline=(char.base)]{
\node[cylinder,draw,inner sep=0.5pt] (char) {#1}; }}
\compcy{x} \compcy{\( x+y+z \)}
因此人们可以疯狂地尝试修改该代码:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\makeatletter
% Keys for shape flatcircle
%
% /pgf/shape aspect : Ratio between the x and y radii of the flatcircle end.
% /pgf/flatcircle uses custom fill : Use a custom fill for the flatcircle.
% /pgf/flatcircle end fill : Custom color for the flatcircle end.
% /pgf/flatcircle body fill : Custom color for the flatcirclebody.
%
\newif\ifpgfflatcircleusescustomfill
\pgfkeys{/pgf/.cd,
flatcircle uses custom fill/.is if=pgfflatcircleusescustomfill,
flatcircle end fill/.initial=white,
flatcircle body fill/.initial=white
}
\pgfdeclareshape{flatcircle}{%
\savedmacro\getflatcirclepoints{%
\pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/inner xsep}}%
\pgf@x\pgf@xc%
\advance\[email protected]\wd\pgfnodeparttextbox%
\pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y\pgf@yc%
\advance\[email protected]\ht\pgfnodeparttextbox%
\advance\[email protected]\dp\pgfnodeparttextbox%
\ifpgfshapeborderusesincircle%
\pgfmathsetmacro\rotate{\pgfkeysvalueof{/pgf/shape border rotate}}%
\ifdim\pgf@x<\pgf@y%
\pgf@x\pgf@y%
\else%
\pgf@y\pgf@x%
\fi%
\[email protected]\pgf@x%
\[email protected]\pgf@y%
\else%
\pgfmathmod{\pgfkeysvalueof{/pgf/shape border rotate}}{360}%
\ifdim\pgfmathresult pt<0pt\relax%
\pgfmathadd@{\pgfmathresult}{360}%
\fi%
\pgfmathsetcount\c@pgf@counta{+\pgfmathresult}%
\advance\c@pgf@counta45\relax%
\divide\c@pgf@counta90\relax%
\multiply\c@pgf@counta90\relax%
\edef\rotate{\the\c@pgf@counta}%
\ifnum\c@pgf@counta=90\relax%
\pgf@xa\pgf@x%
\pgf@x\pgf@y%
\pgf@y\pgf@xa%
\pgf@yc\pgf@xc%
\else%
\ifnum\c@pgf@counta=270\relax%
\pgf@xa\pgf@x%
\pgf@x\pgf@y%
\pgf@y\pgf@xa%
\pgf@yc\pgf@xc%
\fi%
\fi%
\fi%
\addtosavedmacro\rotate%
\pgf@xa\pgf@x%
\pgf@ya\pgf@y%
\pgfutil@tempdima\pgfshapeaspect\pgf@ya%
\pgfutil@tempdimb\pgf@ya%
%
% Adjust for minimum height.
%
\pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/minimum width}}%
\ifdim\pgfutil@tempdimb<.5\pgf@xc\relax%
\[email protected]\pgf@xc%
\pgf@ya\pgfutil@tempdimb%
\fi%
%
% Calculate how far the node contents can extend into the flatcircle bottom.
%
\pgf@yb\pgfutil@tempdimb%
\advance\pgf@yb-\pgf@yc%
\pgfmathdivide@{\pgfmath@tonumber{\pgf@yb}}{\pgfmath@tonumber{\pgfutil@tempdimb}}%
\pgfmathasin@{\pgfmathresult}%
\pgfmathcos@{\pgfmathresult}%
\let\angle\pgfmathresult%
\pgf@xb\pgfmathresult\pgfutil@tempdima%
%
% Adjust for minimum width.
%
\[email protected]\pgflinewidth%
\advance\[email protected]\pgf@xa%
\advance\[email protected]\pgfutil@tempdima%
\advance\pgf@x-\pgf@xb%
\pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/minimum height}}%
\ifdim\pgf@x<\pgf@xc%
\advance\pgf@xc-\pgf@x%
\advance\[email protected]\pgf@xc%
\fi%
%
% Add the larger of the outer sep to the radii.
%
\pgf@x\pgfutil@tempdima\relax%
\pgf@y\pgfutil@tempdimb\relax%
\pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/outer ysep}}%
\ifdim\pgf@xc>\pgf@yc%
\advance\pgf@x\pgf@xc%
\advance\pgf@y\pgf@xc%
\edef\outersep{\the\pgf@xc}%
\else%
\advance\pgf@x\pgf@yc%
\advance\pgf@y\pgf@yc%
\edef\outersep{\the\pgf@yc}%
\fi%
\edef\xradius{\the\pgf@x}%
\edef\yradius{\the\pgf@y}%
\addtosavedmacro\xradius%
\addtosavedmacro\yradius%
\addtosavedmacro\outersep%
%
\pgfextract@process\flatcirclecenter{%
\pgf@x\pgfutil@tempdima%
\advance\[email protected]\pgflinewidth%
\advance\pgf@x\pgf@xb%
\[email protected]\pgf@x%
\pgf@y0pt%
}%
\addtosavedmacro\flatcirclecenter%
%
\pgfextract@process\beforetop{%
\pgf@x\pgf@xa%
\advance\pgf@x-\pgf@xb%
% \advance\pgf@x\pgfutil@tempdima%
% \advance\[email protected]\pgflinewidth%
\pgf@y\pgf@ya%
}%
\pgfextract@process\afterbottom{%
\pgf@x-\pgf@xa%
\advance\pgf@x\pgf@xb%
\pgf@y\pgf@ya%
}%
\addtosavedmacro\beforetop%
\addtosavedmacro\afterbottom%
\pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/outer ysep}}%
\pgfextract@process\beforetopanchor{%
\beforetop%
\advance\pgf@y\pgf@yc%
}%
\pgfextract@process\afterbottomanchor{%
\afterbottom%
\advance\pgf@y\pgf@yc%
}%
\addtosavedmacro\beforetopanchor%
\addtosavedmacro\afterbottomanchor%
%
\beforetopanchor%
\advance\pgf@x\xradius\relax%
\ifdim\pgf@x>\pgf@y%
\edef\externalradius{\the\pgf@x}%
\else%
\edef\externalradius{\the\pgf@y}%
\fi%
\addtosavedmacro\externalradius%
}
\savedanchor\centerpoint{%
\[email protected]\wd\pgfnodeparttextbox%
\[email protected]\ht\pgfnodeparttextbox%
\advance\[email protected]\dp\pgfnodeparttextbox%
}%
\savedanchor\midpoint{%
\[email protected]\wd\pgfnodeparttextbox%
\pgfmathsetlength\pgf@y{+0.5ex}%
}%
\savedanchor\basepoint{%
\[email protected]\wd\pgfnodeparttextbox%
\pgf@y0pt%
}%
\anchor{center}{\centerpoint}
\anchor{shape center}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{\pgfpointadd{\flatcirclecenter}{\centerpoint}}%
{\centerpoint}{\rotate}%
}%
\anchor{mid}{\midpoint}%
\anchor{mid east}{%
\getflatcirclepoints%
\let\pgf@flatcircle@referencepoint\midpoint%
\pgf@anchor@flatcircle@border{\pgfqpoint{\externalradius}{0pt}}%
}%
\anchor{mid west}{%
\getflatcirclepoints%
\let\pgf@flatcircle@referencepoint\midpoint%
\pgf@anchor@flatcircle@border{\pgfqpoint{-\externalradius}{0pt}}%
}%
\anchor{base}{\basepoint}%
\anchor{base east}{%
\getflatcirclepoints%
\let\pgf@flatcircle@referencepoint\basepoint%
\pgf@anchor@flatcircle@border{\pgfqpoint{\externalradius}{0pt}}%
}%
\anchor{base west}{%
\getflatcirclepoints%
\let\pgf@flatcircle@referencepoint\basepoint%
\pgf@anchor@flatcircle@border{\pgfqpoint{-\externalradius}{0pt}}%
}%
\anchor{north}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{0pt}{\externalradius}}%
}%
\anchor{south}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{0pt}{-\externalradius}}%
}%
\anchor{east}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{\externalradius}{0pt}}%
}%
\anchor{west}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{-\externalradius}{0pt}}%
}%
\anchor{north east}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{\externalradius}{\externalradius}}%
}%
\anchor{south west}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{-\externalradius}{-\externalradius}}%
}%
\anchor{south east}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{\externalradius}{-\externalradius}}%
}%
\anchor{north west}{%
\getflatcirclepoints%
\pgf@anchor@flatcircle@border{\pgfqpoint{-\externalradius}{\externalradius}}%
}%
\anchor{before top}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{\pgfpointadd{\beforetopanchor}{\centerpoint}}{\centerpoint}{\rotate}%
}
\anchor{top}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{%
\pgfpointadd{%
\beforetop%
\pgf@y0pt\relax%
\advance\pgf@x\xradius\relax%
}{\centerpoint}}{\centerpoint}{\rotate}%
}
\anchor{after top}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{\pgfpointadd{\beforetopanchor\pgf@y-\pgf@y}{\centerpoint}}{\centerpoint}{\rotate}%
}
\anchor{before bottom}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{\pgfpointadd{\afterbottomanchor\pgf@y-\pgf@y}{\centerpoint}}{\centerpoint}{\rotate}%
}
\anchor{bottom}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{%
\pgfpointadd{%
\afterbottom%
\pgf@y0pt\relax%
\advance\pgf@x-\xradius\relax%
}{\centerpoint}}{\centerpoint}{\rotate}%
}
\anchor{after bottom}{%
\getflatcirclepoints%
\pgfmathrotatepointaround{\pgfpointadd{\afterbottomanchor}{\centerpoint}}{\centerpoint}{\rotate}%
}
\backgroundpath{%
\getflatcirclepoints%
{%
\pgf@x\xradius\relax%
\advance\pgf@x-\outersep\relax%
\edef\xradius{\the\pgf@x}%
\pgf@y\yradius\relax%
\advance\pgf@y-\outersep\relax%
\edef\yradius{\the\pgf@y}%
\pgftransformshift{\centerpoint}%
\pgftransformrotate{\rotate}%
\pgfpathmoveto{\afterbottom}%
\pgfpatharc{90}{270}{\xradius and \yradius}%
\pgfpathlineto{\beforetop\pgf@y-\pgf@y}%
\pgfpatharc{-90}{90}{\xradius and \yradius}%
\pgfpathclose%
% \pgfpathmoveto{\beforetop}%
% \pgfpatharc{90}{270}{\xradius and \yradius}%
}%
}%
\behindbackgroundpath{%
\ifpgfflatcircleusescustomfill%
\getflatcirclepoints%
\pgf@x\xradius\relax%
\advance\pgf@x-\outersep\relax%
\edef\xradius{\the\pgf@x}%
\pgf@y\yradius\relax%
\advance\pgf@y-\outersep\relax%
\edef\yradius{\the\pgf@y}%
{%
\pgftransformshift{\centerpoint}%
\pgftransformrotate{\rotate}%
\pgfpathmoveto{\afterbottom}%
\pgfpatharc{90}{270}{\xradius and \yradius}%
\pgfpathlineto{\beforetop\pgf@y-\pgf@y}%
\pgfpatharc{270}{90}{\xradius and \yradius}%
\pgfpathclose%
\expandafter\pgfsetfillcolor\expandafter{\pgfkeysvalueof{/pgf/flatcircle body fill}}%
\pgfusepath{fill}%
%
\pgfpathmoveto{\beforetop}%
\pgfpatharc{90}{-270}{\xradius and \yradius}%
\pgfpathclose
\expandafter\pgfsetfillcolor\expandafter{\pgfkeysvalueof{/pgf/flatcircle end fill}}%
\pgfusepath{fill}%
}%
\fi%
}%
\anchorborder{%
\pgfextract@process\externalpoint{}%
\getflatcirclepoints%
\pgfutil@ifundefined{pgf@flatcircle@referencepoint}{\let\referencepoint\centerpoint}{%
\let\referencepoint\pgf@flatcircle@referencepoint}%
\pgfextract@process\externalpoint{%
\externalpoint%
\pgf@xa\pgf@x%
\pgf@ya\pgf@y%
\referencepoint%
\advance\pgf@x\pgf@xa%
\advance\pgf@y\pgf@ya%
}%
\pgfmathanglebetweenpoints{\centerpoint}{\externalpoint}%
\pgfmathsubtract@{\pgfmathresult}{\rotate}%
\ifdim\pgfmathresult pt<0pt\relax%
\pgfmathadd@{\pgfmathresult}{360}%
\fi%
\let\externalangle\pgfmathresult%
%
\pgfmathanglebetweenpoints{\referencepoint}{\pgfpointadd{\afterbottomanchor}{\centerpoint}}%
\ifdim\externalangle pt<\pgfmathresult pt\relax%
\pgfmathanglebetweenpoints{\referencepoint}{\pgfpointadd{\beforetopanchor}{\centerpoint}}%
\ifdim\externalangle pt<\pgfmathresult pt\relax%
\pgfmathrotatepointaround{%
\pgfmathpointintersectionoflineandarc%
{\pgfmathrotatepointaround{\externalpoint}{\centerpoint}{-\rotate}}%
{\pgfmathrotatepointaround{\referencepoint}{\centerpoint}{-\rotate}}%
{%
\beforetop%
\pgf@xa\pgf@x%
\centerpoint%
\advance\pgf@x\pgf@xa%
}%
{0}{90}{\xradius and \yradius}%
}{\centerpoint}{\rotate}%
\else%
\pgfpointintersectionoflines{%
\pgfmathrotatepointaround{\pgfpointadd{\afterbottomanchor}{\centerpoint}}%
{\centerpoint}{\rotate}}{%
\pgfmathrotatepointaround{\pgfpointadd{\beforetopanchor}{\centerpoint}}%
{\centerpoint}{\rotate}}%
{\referencepoint}{\externalpoint}%
\fi%
\else%
\pgfmathanglebetweenpoints{\referencepoint}{\pgfpointadd{\afterbottomanchor\pgf@y-\pgf@y}{\centerpoint}}%
\ifdim\externalangle pt>\pgfmathresult pt\relax%
\pgfmathanglebetweenpoints{\referencepoint}{\pgfpointadd{\beforetopanchor\pgf@y-\pgf@y}{\centerpoint}}%
\ifdim\externalangle pt>\pgfmathresult pt\relax%
\pgfmathrotatepointaround{%
\pgfmathpointintersectionoflineandarc%
{\pgfmathrotatepointaround{\externalpoint}{\centerpoint}{-\rotate}}%
{\pgfmathrotatepointaround{\referencepoint}{\centerpoint}{-\rotate}}%
{%
\beforetop%
\pgf@xa\pgf@x%
\centerpoint
\advance\pgf@x\pgf@xa%
}%
{270}{360}{\xradius and \yradius}%
}{\centerpoint}{\rotate}%
\else%
\pgfpointintersectionoflines{%
\pgfmathrotatepointaround{\pgfpointadd{\afterbottomanchor\pgf@y-\pgf@y}{\centerpoint}}%
{\centerpoint}{\rotate}}{%
\pgfmathrotatepointaround{\pgfpointadd{\beforetopanchor\pgf@y-\pgf@y}{\centerpoint}}%
{\centerpoint}{\rotate}}%
{\referencepoint}{\externalpoint}%
\fi%
\else%
\pgfmathrotatepointaround{%
\pgfmathpointintersectionoflineandarc%
{\pgfmathrotatepointaround{\externalpoint}{\centerpoint}{-\rotate}}%
{\pgfmathrotatepointaround{\referencepoint}{\centerpoint}{-\rotate}}%
{%
\afterbottom%
\pgf@xa\pgf@x%
\centerpoint
\advance\pgf@x\pgf@xa%
}%
{90}{270}{\xradius and \yradius}%
}{\centerpoint}{\rotate}%
\fi%
\fi%
}
}
\makeatother
\newcommand*\comp[1]{\tikz[baseline=(char.base)]{
\node[flatcircle,draw,inner ysep=1pt,inner xsep=0pt] (char) {#1};}}
\begin{document}
\comp{x} \comp{\( x+y+z \)} \comp{y} \comp{\( \int \)}
\end{document}
这只是圆柱形状的代码pgflibraryshapes.geometric.code.tex
,其中几行被注释掉了。我在整个过程cylinder
中用替换了flatcircle
,注释掉了在圆柱右侧绘制左点半圆的行,注释掉了将\pgfextract@process\beforetop
圆柱右侧向右移动的移动命令,并将校正符号翻转为\pgf@x
。\pgf@xb
最后,在调用此代码时,分别在 x 和 y 方向上设置内部分离,在第一种情况下会产生一个圆形。代码很长,因为它包括设置锚点、允许旋转等所有 pgf 材料。
答案4
对于初步近似,请参见下面的代码
\documentclass{article}
\usepackage{etoolbox}
\usepackage{tikz}
\newlength{\tempA}
\newlength{\tempB}
\newcommand{\comp}[1]{%
\settowidth{\tempA}{M}%
\settowidth{\tempB}{\ensuremath{#1}}%
\ifdimcomp{\tempA}{<}{\tempB}{\inlozenge{#1}}{\incircle{#1}}%
}
\newcommand{\incircle}[1]{\tikz[baseline]\draw[anchor=base] (0,0) node[draw,circle]{\ensuremath{#1}};}
\newcommand{\inlozenge}[1]{\tikz[baseline]\draw[anchor=base] (0,0) node[draw,rounded corners]{\ensuremath{#1}};}
\begin{document}
$\comp{m}_{\comp{x+x}}+\comp{A=B}$
\end{document}