A超椭圆是一种闭合曲线,可用作椭圆和矩形之间的“中间”形状。可以通过参数控制其“圆度”。我发现它是典型的“圆角”矩形的不错替代品。
可以使用贝塞尔曲面片方便地近似曲线。在“Metafont 书”中,Knuth 定义了以下近似值(第 267 页),在实际应用中与真正的超椭圆无异:
“超椭圆”的五个参数是右、上、左、下和超度。
def superellipse(expr r,t,l,b,s)= r{up}...(s[xpart t,xpart r],s[ypart r,ypart t]){t-r}... t{left}...(s[xpart t,xpart l],s[ypart l,ypart t]){l-t}... l{down}...(s[xpart b,xpart l],s[ypart l,ypart b]){b-l}... b{right}...(s[xpart b,xpart r],s[ypart r,ypart b]){r-b}...cycle enddef;
下面的metapost
代码提供了一种更方便的方法,通过包含超椭圆的矩形的四个角来指定超椭圆:
def my_superellipse(expr sw, nw, ne, se, factor) =
superellipse(1/2[se,ne], 1/2[nw,ne], 1/2[nw,sw], 1/2[sw,se], factor)
enddef;
使用示例(metapost代码):
beginfig(1)
z1=(0,0); % z1, z2, z3 and z4 are four corners of a rectangle
z3=(40,20);
x2=x1; x4=x3; y2=y3; y4=y1;
% Draw a red circle at those points
for i = 1 upto 4:
draw z[i] withpen pencircle scaled 2 withcolor red;
endfor
% Draw the rectangle through those points
draw z1 -- z2 -- z3 -- z4 -- cycle withpen pencircle scaled .1;
% Draw the superellipse, with 0.9 "superness" (the closer to 1, the more squarish)
draw my_superellipse(z1,z2,z3,z4, .9);
% Some text inside
label(btex \LaTeX etex, 1/2[z1,z3]);
endfig;
结果如下:
我认为这对节点形状来说是一个很好的补充tikz
,但我不知道如何将 metapost 语法转换为超椭圆曲线所需的控制点,而且我没有定义新节点形状的经验tikz
。
还要注意,结果节点有明显的north
、south
和锚点,但找到等的坐标会比较棘手,更不用说更通用的锚点east
了。west
north west
node.angle
更新
关于该问题的一些澄清:
超椭圆的公式众所周知(请参阅问题第一行链接的 Wikipedia 文章),但我宁愿不绘制该公式,因为它需要确定采样频率并进行所有计算。我感兴趣的是使用
tikz
诸如..
或 之类的语法to
或pgf
诸如 之类的基元来绘制近似值\pgfpathcurveto
。这只需要计算控制点。我知道
hobby
Andrew Stacey 的软件包(我确实是将 metapost 语法子集转换为 的 python 脚本的作者tikz
,这才是一切的开始)。但是我认为这个解决方案有点过头了,因为它包含 metapost 语法的通用解析器,而超椭圆曲线具有很强的对称性,可以大大简化计算(尽管我仍然不明白如何简化)。最终目标是定义一个新的节点形状。这需要使用低级
pgf
原语,我不知道hobby
包与该级别有何关系pgf/tikz
。
所提解决方案的比较
为了将提出的解决方案与
metapost
上述代码绘制的参考超椭圆进行比较,我获得了曲线的明确控制点。
下面的代码\metapostsuperellipse
在语法中定义了一个包含该路径的宏tikz
,并定义了一个适当尺寸(40x20 pt)的矩形节点,可以用提出的解决方案进行装饰。
\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{calc}
% These numbers were computed by metapost, and obtained using tracingchoices option
\newcommand{\metapostsuperellipse}{
(40,10) .. controls (40,13.43742) and (39.99951,18.00012)
.. (37.99988,18.99994) .. controls (36.00024,19.99976) and (26.11214,20)
.. (20,20)..controls (13.88786,20) and (3.99976,19.99976)
.. (2.00012,18.99994) .. controls (0.00049,18.00012) and (0,13.43742)
.. (0,10)..controls (0,6.56258) and (0.00049,1.99988)
.. (2.00012,1.00006) .. controls (3.99976,0.00024) and (13.88786,0)
.. (20,0)..controls (26.11214,0) and (36.00024,0.00024)
.. (37.99988,1.00006) .. controls (39.99951,1.99988) and (40,6.56258)
.. (40,10);
}
% Solutions proposed by Qrrbrbirlbel
\newcommand*{\superellipse}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\pgfmathsetmacro\looseness{#3}
\path[#1]
(#2.east) .. controls ($(#2.east)!\looseness!(#2.north east)$) and ($(#2.north)!\looseness!(#2.north east)$).. (#2.north)
.. controls ($(#2.north)!\looseness!(#2.north west)$) and ($(#2.west)!\looseness!(#2.north west)$).. (#2.west)
.. controls ($(#2.west)!\looseness!(#2.south west)$) and ($(#2.south)!\looseness!(#2.south west)$).. (#2.south)
.. controls ($(#2.south)!\looseness!(#2.south east)$) and ($(#2.east)!\looseness!(#2.south east)$).. (#2.east)
;
}
\newcommand*{\superellipseA}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\pgfmathsetmacro\looseness{2*#3}
\path[curve to, looseness=\looseness, #1]
let \p1 = (#2.east),
\p2 = (#2.north),
\n1 = {\x1-\x2}, % distance in x direction
\n2 = {\y2-\y1} % distance in y direction
in
(#2.east) to[out=90, in=0, out max distance=\n2, in max distance=\n1]
(#2.north) to[out=180, in=90, out max distance=\n1, in max distance=\n2]
(#2.west) to[out=270, in=180, out max distance=\n2, in max distance=\n1]
(#2.south) to[out=0, in=270, out max distance=\n1, in max distance=\n2] (#2.east);
}
\newcommand*{\superellipseB}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\pgfmathsetmacro\looseness{2*#3}
\path[curve to, looseness=\looseness, #1]
let \p1 = (#2.east),
\p2 = (#2.north),
\n1 = {min({\x1-\x2},{\y2-\y1})} % minimum of distances
in
(#2.east) to[out=90, in=0, max distance=\n1]
(#2.north) to[out=180, in=90, max distance=\n1]
(#2.west) to[out=270, in=180, max distance=\n1]
(#2.south) to[out=0, in=270, max distance=\n1] (#2.east);
}
\tikzset{node box/.style={rectangle, minimum height=20pt, minimum width=40pt, draw, ultra thin, anchor=south west, green}
}
% Pictures to compare proposed solutions with reference (in red)
\begin{tikzpicture}[x=1pt, y=1pt]
\node[node box] (sample) {};
\draw[red, opacity=.5] \metapostsuperellipse;
\superellipse[draw, opacity=0.6, very thin]{sample}{.9}
\end{tikzpicture}
\begin{tikzpicture}[x=1pt, y=1pt]
\node[node box] (sample) {};
\draw[red, opacity=.5] \metapostsuperellipse;
\superellipseA[draw, opacity=0.6, very thin]{sample}{.9}
\end{tikzpicture}
\begin{tikzpicture}[x=1pt, y=1pt]
\node[node box] (sample) {};
\draw[red, opacity=.5] \metapostsuperellipse;
\superellipseB[draw, opacity=0.6, very thin]{sample}{.9}
\end{tikzpicture}
\end{document}
这使:
答案1
这是一个使用超椭圆的参数表示的节点形状。所有标准锚点和边界锚点均已定义。矩形度使用键控制superellipse parameter
。值为1
则为菱形。
\tikz{
\foreach \parameter in {0.4,0.6,0.8,1,2,...,10}
\node [minimum width=4cm, minimum height=2cm, draw, red,text=black, superellipse, superellipse parameter=\parameter] (a) {};
\begin{tikzpicture}
\node [minimum width=4cm, minimum height=2cm, draw, superellipse, superellipse parameter=4] (a) {};
\foreach \angle in {5,10,...,360}
\draw [orange] (a.\angle) -- (\angle:5cm);
\end{tikzpicture}
\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric, intersections}
\makeatletter
% fixed exp function.
%
\makeatletter
\let\pgfmath@function@exp\relax % undefine old exp function
\pgfmathdeclarefunction{exp}{1}{%
\begingroup
\pgfmath@xc=#1pt\relax
\pgfmath@yc=#1pt\relax
\ifdim\pgfmath@xc<-9pt
\pgfmath@x=1sp\relax
\else
\ifdim\pgfmath@xc<0pt
\pgfmath@xc=-\pgfmath@xc
\fi
\pgfmath@x=1pt\relax
\pgfmath@xa=1pt\relax
\pgfmath@xb=\pgfmath@x
\pgfmathloop%
\divide\pgfmath@xa by\pgfmathcounter
\pgfmath@xa=\pgfmath@tonumber\pgfmath@xc\pgfmath@xa%
\advance\pgfmath@x by\pgfmath@xa
\ifdim\pgfmath@x=\pgfmath@xb
\else
\pgfmath@xb=\pgfmath@x
\repeatpgfmathloop%
\ifdim\pgfmath@yc<0pt
\pgfmathreciprocal@{\pgfmath@tonumber\pgfmath@x}%
\pgfmath@x=\pgfmathresult pt\relax
\fi
\fi
\pgfmath@returnone\pgfmath@x%
\endgroup
}
\let\pgfmath@function@pow\relax % undefine old exp function
\pgfmathdeclarefunction{pow}{2}{%
\begingroup%
\pgfmath@xa=#1pt%
\pgfmath@xb=#2pt%
\ifdim\pgfmath@xa=0pt
\pgfmath@x=0pt\relax
\else
\afterassignment\pgfmath@x%
\expandafter\c@pgfmath@counta\the\pgfmath@xb\relax%
\ifnum\c@pgfmath@counta<0\relax%
\c@pgfmath@counta=-\c@pgfmath@counta%
\pgfmathreciprocal@{#1}%
\pgfmath@xa=\pgfmathresult pt\relax%
\fi
\ifdim\pgfmath@x=0pt\relax%
\pgfmath@x=1pt\relax%
\pgfmathloop%
\ifnum\c@pgfmath@counta>0\relax%
\ifodd\c@pgfmath@counta%
\pgfmath@x=\pgfmath@tonumber{\pgfmath@x}\pgfmath@xa%
\fi
\ifnum\c@pgfmath@counta>1\relax%
\pgfmath@xa=\pgfmath@tonumber{\pgfmath@xa}\pgfmath@xa%
\fi%
\divide\c@pgfmath@counta by2\relax%
\repeatpgfmathloop%
\else%
\pgfmathln@{#1}%
\pgfmath@x=\pgfmathresult pt\relax%
\pgfmath@x=\pgfmath@tonumber{\pgfmath@xb}\pgfmath@x%
\pgfmathexp@{\pgfmath@tonumber{\pgfmath@x}}%
\pgfmath@x=\pgfmathresult pt\relax%
\fi%
\fi
\pgfmath@returnone\pgf@x%
\endgroup%
}
\pgfkeys{
/pgf/superellipse parameter/.store in=\pgf@superellipse@param,
/pgf/superellipse parameter/.default=2,
/pgf/superellipse parameter
}
\newcommand{\pointonsuperellipse}[3]{ % cornerpoint, parameter, directionpoint
\pgf@process{#1}
\edef\size@x{\the\pgf@x}%
\edef\size@y{\the\pgf@y}%
\pgfintersectionofpaths
{
\pgfpathmoveto{\centerpoint}
\pgfpathlineto{
\pgfpointborderrectangle{#3}{#1}
}
\pgfpathclose
}
{
\pgfplothandlercurveto
\pgfplotfunction{\x}{-180,-170,...,170}{
\pgfpoint{
abs(1 * cos(\x))^(2/#2)*( (cos(\x)>0)*2-1 ) * \size@x
}{
abs(1 * sin(\x))^(2/#2)*( (sin(\x)>0)*2-1 ) * \size@y
}
}
\pgfpathclose
}
\pgfpointintersectionsolution{1}
}
\makeatletter
\pgfdeclareshape{superellipse}
%
% Draws a circle around the text
%
{
\savedmacro\superellipseparameter{\edef\superellipseparameter{\pgf@superellipse@param}}
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y=.5\ht\pgfnodeparttextbox%
\advance\pgf@y by-.5\dp\pgfnodeparttextbox%
}
\savedanchor\radius{%
%
% Caculate ``height radius''
%
\pgf@y=.5\ht\pgfnodeparttextbox%
\advance\pgf@y by.5\dp\pgfnodeparttextbox%
\pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}%
\advance\pgf@y by\pgf@yb%
%
% Caculate ``width radius''
%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}%
\advance\pgf@x by\pgf@xb%
%
% Adjust
%
\pgf@x=1.4142136\pgf@x%
\pgf@y=1.4142136\pgf@y%
%
% Adjust hieght, if necessary
%
\pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/minimum height}}%
\ifdim\pgf@y<.5\pgf@yc%
\pgf@y=.5\pgf@yc%
\fi%
%
% Adjust width, if necessary
%
\pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/minimum width}}%
\ifdim\pgf@x<.5\pgf@xc%
\pgf@x=.5\pgf@xc%
\fi%
%
% Add outer sep
%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
\advance\pgf@x by\pgf@xb%
\advance\pgf@y by\pgf@yb%
}
\savedmacro\test{\def\test{2}}
%
% Anchors
%
\anchor{center}{\centerpoint}
\anchor{mid}{\centerpoint\pgfmathsetlength\pgf@y{.5ex}}
\anchor{base}{\centerpoint\pgf@y=0pt}
\anchor{north}
{
\pgf@process{\radius}
\pgf@ya=\pgf@y%
\pgf@process{\centerpoint}
\advance\pgf@y by\pgf@ya
}
\anchor{south}
{
\pgf@process{\radius}
\pgf@ya=\pgf@y%
\pgf@process{\centerpoint}
\advance\pgf@y by-\pgf@ya
}
\anchor{west}
{
\pgf@process{\radius}
\pgf@xa=\pgf@x%
\pgf@process{\centerpoint}
\advance\pgf@x by-\pgf@xa
}
\anchor{mid west}
{%
\pgf@process{\radius}
\pgf@xa=\pgf@x%
\pgf@process{\centerpoint}
\advance\pgf@x by-\pgf@xa%
\pgfmathsetlength\pgf@y{.5ex}
}
\anchor{base west}
{%
\pgf@process{\radius}
\pgf@xa=\pgf@x%
\pgf@process{\centerpoint}
\advance\pgf@x by-\pgf@xa%
\pgf@y=0pt
}
\anchor{north west}
{
\pgf@process{\radius}
\def\angle{135}
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgfpoint{
abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
}{
abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
}}
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@process{\centerpoint}
\advance\pgf@x by \pgf@xb
\advance\pgf@y by \pgf@yb
}
\anchor{south west}
{
\pgf@process{\radius}
\def\angle{-135}
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgfpoint{
abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
}{
abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
}}
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@process{\centerpoint}
\advance\pgf@x by \pgf@xb
\advance\pgf@y by \pgf@yb
}
\anchor{east}
{%
\pgf@process{\radius}
\pgf@xa=\pgf@x%
\pgf@process{\centerpoint}
\advance\pgf@x by\pgf@xa
}
\anchor{mid east}
{%
\pgf@process{\radius}
\pgf@xa=\pgf@x%
\pgf@process{\centerpoint}
\advance\pgf@x by\pgf@xa%
\pgfmathsetlength\pgf@y{.5ex}
}
\anchor{base east}
{%
\pgf@process{\radius}
\pgf@xa=\pgf@x%
\pgf@process{\centerpoint}
\advance\pgf@x by\pgf@xa%
\pgf@y=0pt
}
\anchor{north east}
{
\pgf@process{\radius}
\def\angle{45}
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgfpoint{
abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
}{
abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
}}
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@process{\centerpoint}
\advance\pgf@x by \pgf@xb
\advance\pgf@y by \pgf@yb
}
\anchor{south east}
{
\pgf@process{\radius}
\def\angle{-45}
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgfpoint{
abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
}{
abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
}}
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@process{\centerpoint}
\advance\pgf@x by \pgf@xb
\advance\pgf@y by \pgf@yb
}
\anchorborder{
\edef\externalx{\the\pgf@x}%
\edef\externaly{\the\pgf@y}%
\pgf@process{\radius}%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pointonsuperellipse{\pgfpoint{\pgf@xa}{\pgf@ya}}{\superellipseparameter}{\pgfpoint{\externalx}{\externaly}}
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\centerpoint%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
}
\backgroundpath
{
\pgf@process{\radius}%
\pgfutil@tempdima=\pgf@x%
\pgfutil@tempdimb=\pgf@y%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
\advance\pgfutil@tempdima by-\pgf@xb%
\advance\pgfutil@tempdimb by-\pgf@yb%
{
\pgftransformshift{\centerpoint}
\pgfplothandlercurveto
\pgfplotfunction{\x}{-180,-170,...,170}{
\pgfpoint{
abs(1 * cos(\x))^(2/\pgf@superellipse@param)*( (cos(\x)>0)*2-1 ) * \pgfutil@tempdima
}{
abs(1 * sin(\x))^(2/\pgf@superellipse@param)*( (sin(\x)>0)*2-1 ) * \pgfutil@tempdimb
}
}
\pgfpathclose
\pgfgetpath\test
\pgfusepath{stroke}
}
}
}
\def\n{3}
\begin{document}
\begin{tikzpicture}
\node [minimum width=4cm, minimum height=2cm, draw, superellipse, superellipse parameter=4] (a) {};
\foreach \angle in {5,10,...,360}
\draw [orange] (a.\angle) -- (\angle:5cm);
\end{tikzpicture}
\end{document}
答案2
这不是我问这个问题时得到的答案。我是在看到 Qrrbrbirlbel 的尝试后才想到这个答案的。
我注意到,提出的解决方案仅使用四个点来绘制曲线,而原始的 metapost 路径遍历了八个点,它们是矩形的east
、north
和锚点,以及根据这些锚点和给定的“超性”计算出的四个额外的中间点。这些额外的点靠近角落west
south
因此我尝试tikz
简单地使用经过这八个点的曲线plot
。smooth cycle
\newcommand*{\superellipse}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\coordinate (ne) at ($(#2.center)!#3!(#2.north east)$);
\coordinate (nw) at ($(#2.center)!#3!(#2.north west)$);
\coordinate (sw) at ($(#2.center)!#3!(#2.south west)$);
\coordinate (se) at ($(#2.center)!#3!(#2.south east)$);
\path[#1]
plot[smooth cycle,tension=0.4] coordinates{
(#2.east) (ne) (#2.north) (nw) (#2.west) (sw) (#2.south) (se)};
}
令我惊讶的是,匹配几乎完美,无需计算任何控制点或进入或退出角度!我只需要将默认值更改tension
为0.4
。
然而,对于不同的“超值”,这种解决方案仍然不令人满意,因为tension
应该根据超值的变化而变化,而这种变化仍然未知。例如,当超值趋近于 1 时,张力应该趋近于 0。
此外,仍需完成一些工作才能将这个想法变成 tikz 形状。我不确定是否plot
可以在该级别使用关键字。
更新
我带着完全特别指定方程得到tension
曲线所需的 ,对于任何给定的 0.5 到 1.0 之间的“超度”。当超度为 1 时,结果应为矩形,这需要tension=0
。当超度为 0.5 时,结果应为菱形,这也需要tension=0
。对于任何 0.5 到 1 之间的超度,曲线应为或多或少圆润的超椭圆。
由于在 superness=0.5 和 superness=1.0 时张力应为零,但在两者之间应为正值,因此我设想抛物线段可以给出适当的值。尝试使用抛物线,当0.4
superness 为 0.9 时,该抛物线给出的值接近,因为发现该值可以产生良好的结果。结果方程为:
tension = -9.6 * superness^2 + 14.4 * superness - 4.8
根据要求,该方程在超度为 0.5 和 1.0 时得出张力为 0,在超度 = 0.75 时得出最大值 (0.6)。这特别指定方程对于 0.5 到 1.0 之间的任何超度显然都会给出良好的结果,如下面的代码所示:
\newcommand*{\superellipse}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\coordinate (ne) at ($(#2.center)!#3!(#2.north east)$);
\coordinate (nw) at ($(#2.center)!#3!(#2.north west)$);
\coordinate (sw) at ($(#2.center)!#3!(#2.south west)$);
\coordinate (se) at ($(#2.center)!#3!(#2.south east)$);
\pgfmathparse{-9.6*#3*#3 + 14.4*#3 - 4.8} % <--------- Magic numbers!
\xdef\tension{\pgfmathresult}
\path[#1]
plot[smooth cycle, tension=\tension] coordinates{
(#2.east) (ne) (#2.north) (nw) (#2.west) (sw) (#2.south) (se)};
% \node {\tension};
}
\tikzset{node box/.style={rectangle, minimum height=20pt, minimum width=40pt, draw, ultra thin, anchor=south west, green}
}
% Pictures to compare proposed solutions with reference (in red)
\begin{tikzpicture}[x=1pt, y=1pt]
\node[node box] (sample) {};
\foreach \superness in {0.5,0.55, ..., 1.01} {
\superellipse[draw, opacity=0.6, very thin]{sample}{\superness}
}
\end{tikzpicture}
答案3
诚然,
- 我既不了解 MetaPost,也不了解它的
...
运营商, - 也不是 TikZ 的
..
操作员; - 这还不是一个形状,
- 的超性也不会
1
产生矩形(实际上,的超性1
产生的输出比产生的输出更像您的图像.9
)。
任何 都不起作用.anchor
。
不过,.<angle>
可以通过将超椭圆的路径与路径相交来伪造(<node>.center) -- (<angle>:∞)
(∞
可以用矩形的最大“半径”代替(max(<width>,<height>)/2
)。
代码
\documentclass[tikz,border=2pt]{standalone}
\usetikzlibrary{calc}
\newcommand*{\superellipse}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\pgfmathsetmacro\looseness{#3}
\path[#1]
(#2.east) .. controls ($(#2.east)!\looseness!(#2.north east)$) and ($(#2.north)!\looseness!(#2.north east)$).. (#2.north)
.. controls ($(#2.north)!\looseness!(#2.north west)$) and ($(#2.west)!\looseness!(#2.north west)$).. (#2.west)
.. controls ($(#2.west)!\looseness!(#2.south west)$) and ($(#2.south)!\looseness!(#2.south west)$).. (#2.south)
.. controls ($(#2.south)!\looseness!(#2.south east)$) and ($(#2.east)!\looseness!(#2.south east)$).. (#2.east)
;
}
\newcommand*{\superellipseA}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\pgfmathsetmacro\looseness{2*#3}
\path[curve to, looseness=\looseness, #1]
let \p1 = (#2.east),
\p2 = (#2.north),
\n1 = {\x1-\x2}, % distance in x direction
\n2 = {\y2-\y1} % distance in y direction
in
(#2.east) to[out=90, in=0, out max distance=\n2, in max distance=\n1]
(#2.north) to[out=180, in=90, out max distance=\n1, in max distance=\n2]
(#2.west) to[out=270, in=180, out max distance=\n2, in max distance=\n1]
(#2.south) to[out=0, in=270, out max distance=\n1, in max distance=\n2] (#2.east);
}
\newcommand*{\superellipseB}[3][draw]{% #1 = styles
% #2 = node
% #3 = superness
\pgfmathsetmacro\looseness{2*#3}
\path[curve to, looseness=\looseness, #1]
let \p1 = (#2.east),
\p2 = (#2.north),
\n1 = {min({\x1-\x2},{\y2-\y1})} % minimum of distances
in
(#2.east) to[out=90, in=0, max distance=\n1]
(#2.north) to[out=180, in=90, max distance=\n1]
(#2.west) to[out=270, in=180, max distance=\n1]
(#2.south) to[out=0, in=270, max distance=\n1] (#2.east);
}
\begin{document}
\begin{tikzpicture}
\node[ultra thin,draw,text=black!10] (n) {\LaTeX};
\superellipse[draw,red,opacity=.8] {n}{.9}
\superellipseA[draw,green,opacity=.6]{n}{.9}
\superellipseB[draw,blue,opacity=.4] {n}{1}
\end{tikzpicture}
\end{document}