如何创建具有多种形状(如 UML 图中最终状态)的自己的 TikZ 节点?
\tikzstyle{umlfinal} = [...]
在 tikz 图片环境中,这非常简单:
\documentclass{minimal}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[node distance=1.5cm]
\fill[black] (0,0) circle (7mm);
\draw[black] (0,0) circle (10mm);
\end{tikzpicture}
\end{document}
但是我怎样才能创建这两个圆的节点呢?
答案1
最简单的方法可能是将您的形状定义为pic
:
\documentclass[border=3.14,tikz]{standalone}
\usepackage{tikz}
\tikzset
{
UML/end/.pic={%
\draw[black]
% reduce code duplication, we could also put the \fill here
pic{UML/aux/end}
circle[radius=\pgfkeysvalueof{/tikz/UML/end/radius}];
}
,UML/end/radius/.initial = 1cm
,UML/end/.style={%
append after command={pic{UML/aux/end}},
% we use the node shape for the outer circle, that way you can also draw
% connections and use the nodes anchors
draw, circle, minimum width=2*\pgfkeysvalueof{/tikz/UML/end/radius},
inner sep=0pt,
}
,UML/aux/end/.pic={%
\fill[black] circle[radius=.7*\pgfkeysvalueof{/tikz/UML/end/radius}];
}
}
\begin{document}
\begin{tikzpicture}[node distance=1.5cm]
% works
\node[UML/end]{};
% bug
\node at (0, -3)[UML/end]{};
% works
\draw (2, 0) pic{UML/end}
(4, 0) node[UML/end]{};
\end{tikzpicture}
\end{document}
使用label
而不是append after command
并以正确的方式设置第二个节点,我们可以得到一个与 和 一起使用的简单positioning
版本at
:
\documentclass[border=3.14,tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\tikzset
{
UML/end/radius/.initial = 1cm
,UML/end/.style={
draw, circle, minimum width=2*\pgfkeysvalueof{/tikz/UML/end/radius},
label={
[fill, circle, minimum width=1.4*\pgfkeysvalueof{/tikz/UML/end/radius}]%
center:%
}
}
}
\begin{document}
\begin{tikzpicture}[node distance=1.5cm]
\node[UML/end](foo){};
\node[UML/end, below=of foo]{};
\node[UML/end] at (4, 0) {};
\end{tikzpicture}
\end{document}
以下在pgf
名为 -layer 处定义一个新形状,并使用它来创建可与库的语法和方法一起UMLend
使用的节点样式。at
positioning
代码大部分是circ
形状的复制,只添加了两行,即:
\pgfpathcircle{\centerpoint}{.7\pgfutil@tempdima}%
\pgfusepath{fill}%
我不完全确定这是否完全安全,但我猜应该是的。
完整代码:
\documentclass[border=3.14,tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\tikzset{UML/end/.style={UMLend, draw, minimum width=2cm}}
\makeatletter
% This is a copy of PGF's definition of circle
\pgfdeclareshape{UMLend}
%
% Draws a circle around the text
%
{%
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y=.5\ht\pgfnodeparttextbox%
\advance\pgf@y by-.5\dp\pgfnodeparttextbox%
}%
\saveddimen\radius{%
%
% Calculate ``height radius''
%
\pgf@ya=.5\ht\pgfnodeparttextbox%
\advance\pgf@ya by.5\dp\pgfnodeparttextbox%
\pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}%
\advance\pgf@ya by\pgf@yb%
%
% Calculate ``width radius''
%
\pgf@xa=.5\wd\pgfnodeparttextbox%
\pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}%
\advance\pgf@xa by\pgf@xb%
%
% Calculate length of radius vector:
%
\pgf@process{\pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}}%
\ifdim\pgf@x>\pgf@y%
\c@pgf@counta=\pgf@x%
\ifnum\c@pgf@counta=0\relax%
\else%
\divide\c@pgf@counta by 255\relax%
\pgf@xa=16\pgf@xa\relax%
\divide\pgf@xa by\c@pgf@counta%
\pgf@xa=16\pgf@xa\relax%
\fi%
\else%
\c@pgf@counta=\pgf@y%
\ifnum\c@pgf@counta=0\relax%
\else%
\divide\c@pgf@counta by 255\relax%
\pgf@ya=16\pgf@ya\relax%
\divide\pgf@ya by\c@pgf@counta%
\pgf@xa=16\pgf@ya\relax%
\fi%
\fi%
\pgf@x=\pgf@xa%
%
% If necessary, adjust radius so that the size requirements are
% met:
%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/minimum width}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/minimum height}}%
\ifdim\pgf@x<.5\pgf@xb%
\pgf@x=.5\pgf@xb%
\fi%
\ifdim\pgf@x<.5\pgf@yb%
\pgf@x=.5\pgf@yb%
\fi%
%
% Now, add larger of outer separations.
%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
\ifdim\pgf@xb<\pgf@yb%
\advance\pgf@x by\pgf@yb%
\else%
\advance\pgf@x by\pgf@xb%
\fi%
}%
%
% Anchors
%
\anchor{center}{\centerpoint}%
\anchor{mid}{\centerpoint\pgfmathsetlength\pgf@y{.5ex}}%
\anchor{base}{\centerpoint\pgf@y=0pt}%
\anchor{north}{\centerpoint\advance\pgf@y by\radius}%
\anchor{south}{\centerpoint\advance\pgf@y by-\radius}%
\anchor{west}{\centerpoint\advance\pgf@x by-\radius}%
\anchor{east}{\centerpoint\advance\pgf@x by\radius}%
\anchor{mid west}{\centerpoint\advance\pgf@x by-\radius\pgfmathsetlength\pgf@y{.5ex}}%
\anchor{mid east}{\centerpoint\advance\pgf@x by\radius\pgfmathsetlength\pgf@y{.5ex}}%
\anchor{base west}{\centerpoint\advance\pgf@x by-\radius\pgf@y=0pt}%
\anchor{base east}{\centerpoint\advance\pgf@x by\radius\pgf@y=0pt}%
\anchor{north west}{
\centerpoint
\pgf@xa=\radius
\advance\pgf@x by-0.707107\pgf@xa
\advance\pgf@y by0.707107\pgf@xa
}%
\anchor{south west}{
\centerpoint
\pgf@xa=\radius
\advance\pgf@x by-0.707107\pgf@xa
\advance\pgf@y by-0.707107\pgf@xa
}%
\anchor{north east}{
\centerpoint
\pgf@xa=\radius
\advance\pgf@x by0.707107\pgf@xa
\advance\pgf@y by0.707107\pgf@xa
}%
\anchor{south east}{
\centerpoint
\pgf@xa=\radius
\advance\pgf@x by0.707107\pgf@xa
\advance\pgf@y by-0.707107\pgf@xa
}%
\anchorborder{%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\edef\pgf@marshal{%
\noexpand\pgfpointborderellipse
{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}
{\noexpand\pgfqpoint{\radius}{\radius}}%
}%
\pgf@marshal%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\centerpoint%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
}%
%
% Background path
%
\backgroundpath{%
\pgfutil@tempdima=\radius%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
\ifdim\pgf@xb<\pgf@yb%
\advance\pgfutil@tempdima by-\pgf@yb%
\else%
\advance\pgfutil@tempdima by-\pgf@xb%
\fi%
%%% This is added
\pgfpathcircle{\centerpoint}{.7\pgfutil@tempdima}%
\pgfusepath{fill}%
%%%
\pgfpathcircle{\centerpoint}{\pgfutil@tempdima}%
}%
}%
\makeatother
\begin{document}
\begin{tikzpicture}[node distance=1.5cm]
\node[UML/end](foo){};
\node[UML/end, below=of foo]{};
\node[UML/end] at (4, 0) {};
\end{tikzpicture}
\end{document}