我正在为我们正在使用的系统创建依赖关系图,以显示什么会对什么产生影响。这很好用,但我有很多交叉点(可能还会有更多),这可能会使阅读和跟踪变得非常复杂。
我现在正尝试为这些交叉点画桥,类似于TikZ 中两条线的交点实际上并未连接,而是自动化的,因为否则会花费太多时间。
\documentclass[a4paper]{scrartcl} [10pt,letterpaper]
\usepackage{tikz}
\usetikzlibrary{calc,shapes.geometric, arrows,positioning}
\usepackage{xcolor}
\begin{document}
\begin{figure}
\tikzstyle{ele} = [rectangle, rounded corners, minimum width=2.427cm, minimum height=1.5cm, align=center, draw=black, fill=yellow!50]
\begin{tikzpicture}[node distance=3.927cm]
%Nodes
\node (Person) [ele] {Person};
\node (Club) [ele, right =of Person] {Club};
\node (Member) [ele, below = of Person] {Member};
\node (Bal) [ele, right = of Club] {Balance};
\node (MP) [ele, below = of Bal] {Membership \\ Package};
\node (Group) [ele, below = of Club] {Group};
\node (Level) [ele, below = of MP] {Level};
\node (Rec) [ele, below = of Group] {Recognition};
\node (Pref) [ele, below = of Member] {Preference};
%Lines
\path (Person) edge [->,thick] (Member);
\path (Member) edge [->,thick] (Group);
\path (Pref) edge [->,thick] (Group);
\path (Group) edge [<->,thick] (Rec);
\path (Club) edge [->,thick] (Level);
\path (MP) edge [->,thick] (Level);
\path (Group) edge [<->,thick] (Level);
\path (Club) edge [->,thick] (MP);
\path (Group) edge [->,thick] (MP);
\path (Group) edge [<->,thick] (Bal);
\path (Club) edge [->,thick] (Bal);
\path (Bal) edge [->,thick] (Rec);
\path (Club) edge [->,thick] (Member);
\path (MP) edge [->,thick,bend left=45] (Member);
\path (Club) edge [->,thick] (Group);
\path (MP) edge [->,thick] (Bal);
\path (Club) edge [->,thick,bend right=45] (Rec);
\path (Level) edge [->,thick] (Rec);
\path (Member) edge [->,thick,bend right=20] (Rec);
\path (MP) edge [->,thick] (Person);
\end{tikzpicture}
\end{figure}
\end{document}
如果有人有更好的想法,我也愿意接受这些依赖关系的其他彻底展示。
答案1
以下是使用最新版本的解决方案spath3
库(在撰写本文时需要开发版本,但经过更多测试后将上传到 CTAN)。
我已经对代码进行了注释以解释发生了什么,但基本思想如下:
- 定义并保存所有边缘
- 遍历所有边对以找到交点,并在该点的其中一条边中插入断点。
- 加宽这些间隙并在间隙中插入一个圆弧,确保圆弧指向“向上”(这是需要开发版本的部分)。
- 现在重新遍历所有边对并再次找到交点(由于插入了圆弧,这些交点自之前以来已经移动了),并在其他小路。
- 将这些间隙稍微加宽一些。
- 渲染所有路径。
我稍微修改了路径以确保没有三重交叉(因为添加弧时它们看起来很愚蠢)。
结果如下:
代码如下:
\documentclass[a4paper]{scrartcl} [10pt,letterpaper]
%\url{https://tex.stackexchange.com/q/414124/86}
\usepackage{tikz}
\usetikzlibrary{
spath3,
intersections,
calc,
shapes.geometric,
arrows,positioning
}
\usepackage{xcolor}
% We're going to do a lot of iterating over the edges so we define a
% comma-separated list of them.
\def\Edges{%
Person/Member,%
Member/Group,%
Pref/Group,%
Group/Rec,%
Club/Level,%
MP/Level,%
Group/Level,%
Club/MP,%
Group/MP,%
Group/Bal,%
Club/Bal,%
Bal/Rec,%
Club/Member,%
MP/Member,%
Club/Group,%
MP/Bal,%
Club/Rec,%
Level/Rec,%
Member/Rec,%
MP/Person%
}
\begin{document}
\begin{figure}
\tikzstyle{ele} = [rectangle, rounded corners, minimum width=2.427cm, minimum height=1.5cm, align=center, draw=black, fill=yellow!50]
\begin{tikzpicture}[node distance=3.927cm]
%Nodes
\node (Person) [ele] {Person};
\node (Club) [ele, right =of Person] {Club};
\node (Member) [ele, below = of Person] {Member};
\node (Bal) [ele, right = of Club] {Balance};
\node (MP) [ele, below = of Bal] {Membership \\ Package};
\node (Group) [ele, below = of Club] {Group};
\node (Level) [ele, below = of MP] {Level};
\node (Rec) [ele, below = of Group] {Recognition};
\node (Pref) [ele, below = of Member] {Preference};
% Most of the edges will be lines, so we define them all as lines initially
% and then overwrite the ones that are curves.
% As we're defining them in a \foreach loop we have to work globally.
\foreach \source/\target in \Edges {
\path[spath/save global=\source-\target] (\source) -- (\target);
}
% Now overwrite the ones that are meant to be curved
\path[spath/save global=MP-Member] (MP) to[bend left=45] (Member);
\path[spath/save global=Club-Rec] (Club) to[bend right=45] (Rec);
\path[spath/save global=Member-Rec] (Member) to[bend right=20] (Rec);
% It is best to avoid triple intersections so these two are also
% made into curves (they were straight in the original diagram)
\path[spath/save global=Group-Bal] (Group) to[bend left=20] (Bal);
\path[spath/save global=Bal-Rec] (Bal) to[bend left=5] (Rec);
% We now iterate through the list of edges and break each where it
% intersects with others. For each pair we only want to break
% one of the edges, so when we consider an edge we only intersect
% it with edges that came earlier in the list. To achieve this,
% after examining an edge we add it to the list \PreEdges that
% and we intersect each edge only with those in \PreEdges
\def\PreEdges{}
\foreach \sourceA/\targetA in \Edges
{
\foreach \sourceB/\targetB in \PreEdges
{
% Split the first path where it meets the second path
\tikzset{
spath/split globally at intersections with={\sourceA-\targetA}{\sourceB-\targetB}
};
}
% Now add the first path to the list of paths to intersect against
% The \if is so that we don't get an empty entry at the start of
% the list.
% (This would be a bit simpler in LaTeX3)
\if\PreEdges\relax\relax
\xdef\PreEdges{%
\sourceA/\targetA
}
\else
\xdef\PreEdges{%
\PreEdges,%
\sourceA/\targetA
}
\fi
}
% At the intersection breaks we want to make a small gap and insert
% an arc. This next line defines the arc (it will be scaled and
% transformed to fit in the gap so the actual size doesn't matter)
\path[spath/save=arc] (0,0) arc[radius=1cm, start angle=180, delta angle=-180];
% Now we iterate through the paths, adding gaps and then splicing in
% the arc.
\foreach \source/\target in \Edges
{
\tikzset{
spath/insert gaps globally after components={\source-\target}{8pt},
spath/join components globally upright with={\source-\target}{arc},
}
}
% This deals with the over paths, now we need to break the under paths
% where they intersect with the (new) over paths. So we do our
% intersection double loop again, only with the paths in the opposite
% order in the splitting command.
\def\PreEdges{}
\foreach \sourceA/\targetA in \Edges
{
\foreach \sourceB/\targetB in \PreEdges
{
\tikzset{
spath/split globally at intersections with={\sourceB-\targetB}{\sourceA-\targetA}
};
}
\if\PreEdges\relax\relax
\xdef\PreEdges{%
\sourceA/\targetA
}
\else
\xdef\PreEdges{%
\PreEdges,%
\sourceA/\targetA
}
\fi
}
% Our last loop inserts gaps in the new breaks and renders each edge.
\foreach \source/\target in \Edges
{
\tikzset{
spath/insert gaps after components={\source-\target}{4pt},
}
\draw[spath/use=\source-\target,thick,->];
}
\end{tikzpicture}
\end{figure}
\end{document}