我想在 Latex 文档中描述分层状态机 (HSM)。我已经在 Qt 的状态机框架中实现了我想要描述的状态机。这样您就知道我在说什么了。
我不需要描述状态机框架的所有功能(例如,我没有使用历史状态)。例如,如果我可以重现这一点(http://doc.qt.io/qt-5/images/statemachine-button-nested.png) 来自 Qt 文档的图像,这足以满足我的需要。
我并不关心图像是 tex 文件本身的一部分(例如 Tikz)还是由外部程序生成的。但它应该是矢量图形。
我已经研究过 TikZ 并且可能可以用它来完成,但现在感觉很麻烦:
\documentclass{scrbook}
\usepackage{tikz}
\usetikzlibrary{arrows, shapes, 3d, calc, fit, positioning}
\begin{document}
\begin{tikzpicture}[round/.style={rounded corners=1.5mm,minimum width=1cm,inner sep=2mm,above right,draw,align=left,text width=15mm}]
\node[round] (rotLeft) at (-3,-1) {rotation left};
\node[round] (rotRight) at (-3,1) {rotation right};
\node[round,fit=(rotLeft)(rotRight)] (ident) {identify};
\node[round] (pause) at (0,1) {pause};
\node[round] (observe) at (0,0) {observe};
\node[round] (origin) at (0,-1) {to origin};
\node[round] (left) at (3,-1) {left};
\node[round] (right) at (3,1) {right};
\node[round] (neutral) at (7,0) {neutral};
\node[round,fit=(left)(right)(neutral)] (running) {running};
\draw[-latex, bend left] ($(pause.north east) + (-0.5,0.3)$) coordinate (temp) to (pause);
\fill (temp) circle (0.1);
\draw[-latex, bend left] ($(rotLeft.north east) + (0,0.3)$) coordinate (temp2) to (rotLeft);
\fill (temp2) circle (0.1);
\draw[-latex, bend left] ($(neutral.north east) + (-0.5,0.3)$) coordinate (temp3) to (neutral);
\fill (temp3) circle (0.1);
\draw[-latex, bend left] (left) to (right);
\draw[-latex, bend left] (right) to (left);
\draw[-latex, bend left] (left) to (neutral);
\draw[-latex, bend left] (neutral) to (right);
\draw[-latex, bend left] (neutral) to (left);
\draw[-latex, bend left] (right) to (neutral);
\draw[-latex, bend left] (pause) to (running);
\draw[-latex, bend left] (pause) to[in=-135,out=-90] (ident);
\draw[-latex, bend left] (pause) to (observe);
\draw[-latex, bend left] (observe) to (origin);
\draw[-latex, bend left] (origin) to (origin);
\draw[-latex, bend left] (origin) to (pause);
\draw[-latex, bend left] (running) to (origin);
\draw[-latex, bend left] (ident) to[out=-60,in=-90] (origin);
\draw[-latex, bend left] (rotLeft) to (rotRight);
\draw[-latex, bend left] (rotRight) to (rotLeft);
\end{tikzpicture}
\end{document}
但结果也确实不太理想。
也许我错过了什么。我对 Tikz 还很陌生。
我考虑过 graphviz (dot),它更合我的口味,因为它特别处理所有布局内容(将哪个节点放置在何处以及边的路径)。但它不支持嵌套节点。
任何其他建议都值得欢迎。
谢谢
索拉坦
PS:好吧,我说的“漂亮”是什么意思?在我看来,这张图中丑陋的部分是:
- 穿过节点的边(从“到原点”或“暂停”)。
- 边无需相互交叉(例如,节点“左”和“中性”之间)。
我想,当深入研究 Tikz 时,这些问题可以得到解决。但我真正看重 graphviz 的一点是,我不需要关心所有这些布局内容。我只需描述存在哪些节点以及它们与谁互连,gaphviz 会完成其余所有工作。如果它能处理嵌套节点就好了……
- 此外,节点“identify”和“running”的名称与边缘重叠也不太好看。也许它们可以写在两个节点的顶部?!?
也许我的要求太多了,但是考虑到 HSM 的使用频率相当高,我认为应该有一个更易于使用的解决方案来在广泛的开源领域中描述它们。
答案1
看起来(至少稍微)好一些了吗?
上图的代码来自您的 MWE。在此,我将节点定位机制从绝对定位更改为 TikZ 库提供的相对定位positioning
。在节点样式中,我省略了最小宽度,定义了最小高度并增加了inner xsep
距离。
对于节点之间的箭头,使用边。这大大缩短了代码。它们的样式在一个地方定义,因此可以轻松更改。希望在所有这些更改中我没有忽略任何东西。
\documentclass[border=5mm,
tikz,
preview]{standalone}
\usetikzlibrary{arrows.meta, bending, calc, fit, positioning, shapes}
\begin{document}
\begin{tikzpicture}[auto,
node distance = 22mm and 17mm,
every node/.style = {draw, rounded corners=1.5mm,
inner ysep=2mm, inner xsep=4mm,
minimum height=6ex,
% font=\bfseries,
text width=13mm, align=center},
]
%---
\linespread{0.8}
%-------
\node (rotLeft) {rotation left};
\node (rotRight) [above=of rotLeft] {rotation right};
\node (ident) [fit=(rotLeft)(rotRight)] {identify};
%
\node (pause) [right=of rotRight] {pause};
\node (observe) [right= of $(rotLeft.east)!0.5!(rotRight.east)$]
{observe};
\node (origin) [right=of rotLeft] {to origin};
%
\node (left) [right=of pause] {left};
\node (right) [right=of origin] {right};
\node (neutral) [right=of $(left)!0.5!(right)$] {neutral};
\node (running) [fit=(left)(right)(neutral)] {};
\node[draw=none,above=7mm of neutral] {running};
%
\coordinate[above left=5mm and 2mm of rotLeft.north east] (temp1);
\coordinate[above left=5mm and 2mm of pause.north east] (temp2);
\coordinate[above left=5mm and 2mm of neutral.north east] (temp3);
\path[{Circle[length=2mm,flex]}-{Latex[flex]}, bend left]
(temp1) edge (rotLeft.north east)
(temp2) edge (pause.north east)
(temp3) edge (neutral.north east);
%
\draw[-{Latex[length=3mm]}] ([yshift=-1mm] origin.east) -- + (0,3mm);
% edges
\path[draw, -{Latex[]}, bend left, looseness=1.3]
(rotLeft) edge (rotRight)
(rotRight) edge (rotLeft)
%---
(pause.north) edge[bend right] (ident.north)
(ident.south) edge[bend right] (origin.south)
%---
(origin.north west) edge (pause.south west)
(pause.south east) edge (observe.north east)
(observe.south east) edge (origin.north east)
%---
(left) edge (right)
(right) edge (left)
%
(left) edge (neutral)
([yshift=1mm] neutral.west) edge ([xshift= 7mm] left.south)
([xshift=7mm] right.north) to ([yshift=-1mm] neutral.west)% exception!?
(neutral) edge (right)
%---
(pause) edge (running)
(running) edge (origin);
\end{tikzpicture}
\end{document}