我是使用 tikz 绘制流程图的新手。我需要绘制类似以下的流程图:
但是,我无法获得肘部箭头。我需要获得不与其他矩形相交的肘部箭头。这是我的尝试。
这是代码。请帮我用最简单的方法画出这样的流程图。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric, arrows}
\usetikzlibrary{matrix,shapes,positioning,chains}
\tikzstyle{course} = [rectangle, rounded corners,
minimum width=2cm,
minimum height=1cm,
text centered,
draw=black,
fill=white!30]
\tikzstyle{arrow} = [thick,->,>=stealth]
\begin{document}
\begin{tikzpicture}[node distance=3cm]
% Freshman
\node (CHEM101) [course] {CHEM 101};
\node (ENGL101) [course, right of=CHEM101] {ENGL 101};
\node (MATH101) [course, right of=ENGL101] {MATH 101};
\node (PHYS101) [course, right of=MATH101] {PHYS 101};
\node (PE101) [course, right of=PHYS101] {PE 101};
\node (ENGL102) [course, below of=CHEM101] {ENGL 102};
\draw [arrow] (ENGL101) -- (ENGL102);
\end{tikzpicture}
\end{document}
答案1
您可以使用矩阵来定位节点。然后您可以定义一些绘制连接的宏。此处实现的宏测试箭头是水平的还是垂直的,并相应地选择颜色。对于垂直箭头,它允许您移动箭头附着到上部节点(start shift
)、下部节点(target shift
)的点以及两者的整体移动(shift
)。nudge
您可以使用微调此类箭头的角点。它还允许您避开一些节点,并“智能地”选择避开的节点左侧或右侧的路径。在矩阵中,节点有名称m-<row>-<column>
,第 1 列由学期占用。该命令的语法\Arrow
是
\Arrow[<options>]{<start>}{<target>}
其中起点和目标由 给出<row>-<column>
,但要避免的节点必须输入为(m-<row>-<column>)
。几乎可以肯定的是,宏不允许您执行最终想要执行的所有操作,但您可以通过添加更多选项来升级它。
\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{calc,fit,matrix}
\begin{document}
\begin{tikzpicture}[Arrow/.cd,start shift/.initial=0pt,target shift/.initial=0pt,%
shift/.initial=0pt,% overall shift
nudge/.initial={(0pt,0pt)},
avoid/.code=\ArrowAvoidtrue\def\ArrowAvoidnodes{#1},
sep/.initial=1ex,
/tikz/.cd,vc/.style={thick,blue!80,-stealth,rounded corners=0.5pt},
hc/.style={thick,orange,-stealth,rounded corners=0.5pt}
]
\newif\ifArrowAvoid
\newcommand{\Arrow}[3][]{\begingroup\ArrowAvoidfalse
\tikzset{Arrow/.cd,#1}%
\ifArrowAvoid
\node[fit=\ArrowAvoidnodes,inner sep=\pgfkeysvalueof{/tikz/Arrow/sep}](Avoid){};
\draw[vc] let
\p1=(Avoid),
\p2=([xshift=\pgfkeysvalueof{/tikz/Arrow/shift}+\pgfkeysvalueof{/tikz/Arrow/start shift}]m-#2.south),
\p3=([xshift=\pgfkeysvalueof{/tikz/Arrow/shift}+\pgfkeysvalueof{/tikz/Arrow/target shift}]m-#3.north),
\n1={int(scalar(ifthenelse(0.5*\x2+0.5*\x3>\x1,1,0)))} in
\ifcase\n1\relax
(\p2) |- (Avoid.north west) -- (Avoid.south west) -| (\p3)
\or
(\p2) |- (Avoid.north east) -- (Avoid.south east) -| (\p3)
\fi;
\else
\draw let \p1=($(m-#3)-(m-#2)$) in
\ifdim\y1=0pt\relax
[hc] (m-#2) -- (m-#3)
\else
let \p2=([xshift=\pgfkeysvalueof{/tikz/Arrow/shift}+\pgfkeysvalueof{/tikz/Arrow/start shift}]m-#2.south),
\p3=([xshift=\pgfkeysvalueof{/tikz/Arrow/shift}+\pgfkeysvalueof{/tikz/Arrow/target shift}]m-#3.north),
\p4=\pgfkeysvalueof{/tikz/Arrow/nudge},
\n1={int(ifthenelse(\x1==\x2,0,1))} in
\ifcase\n1\relax
[vc] (\p2) -- (\p3)
\or
[vc] (\p2) |- ($(0.5*\x2+0.5*\x3+\x4,0.5*\y2+0.5*\y3+\y4)$) -| (\p3)
\fi
\fi ;
\fi
\endgroup}
\matrix[matrix of nodes,
cells={nodes={text width=6em,align=center,font=\sffamily,draw=blue,rounded corners=2pt,minimum height=2.5em}},
column 1/.style={nodes in empty cells,
nodes={node font=\tiny,draw=none,yshift=2ex,
execute at begin node=\ifodd\pgfmatrixcurrentrow
{1\textsuperscript{st}\\ semester}
\else
{2\textsuperscript{nd}\\ semester}
\fi}},
column sep=1.5em,row sep=2.5em](m){
& MATH 101 & PHYS 101 & CHEM 101 & ENG 101 & IAS 111 & \\
& MATH 102 & PHYS 102 & ICS 104 & ENG 102 & PE 101 & IAS 121 \\
& MATH 201 & EE 301 & COE 202 & ENG 214 & ISE 201 & IAS 121 \\
& MATH 208 & EE 203 & EE 207 & EE 207 & EE 213 & EE 271 & COE 201 \\
};
\Arrow{1-2}{1-3}
\Arrow{2-3}{3-4}
\Arrow[start shift=1ex,target shift=-1ex,nudge={(0,0.5ex)}]{2-3}{3-5}
\Arrow[shift=-1ex,avoid={(m-2-2)}]{1-2}{3-2}
\Arrow[shift=1ex,avoid={(m-2-5)}]{1-5}{3-5}
\Arrow[shift=1ex,avoid={(m-2-6)(m-3-6)}]{1-6}{4-6}
\end{tikzpicture}
\end{document}
答案2
以下是一些想法:
放置节点
grid placement
节点通过库的在线放置graphs
。这需要手动调整节点之间的距离(通过x
和y
键),因为它们只是放置在整数步长处X和是轴。
节点名称*
用于跳过网格中的放置。该值\cols
需要在 TikZ 图片内设置。它将用于设置网格放置以及绘制车道。
多个同名节点(例如*
)将被命名为*
、*'
、*''
等。除了*
“节点”(旨在多次使用)之外,您的图表实际上有几个节点不止一次。由于我没有重新创建完整的图表,因此在下面的代码中,这仅适用于超宽节点EE 3XX Lab
。
矩阵会更好,但要通过内容名称引用这些节点并非易事。图表将名称和内容设置为相同的值(如果没有其他指定)。
车道
这些是绘制的大约网格的行。
循环设置方式允许以下列表:
FRESHMAN/{1st, 2nd}, SOPHOMORE/{1st, 2nd},
JUNIOR/{1st, 2nd}, SUMMER/., SENIOR/{1st, 2nd}
(来自 SUMMER)意味着.
不会排版任何实际的“学期”标签(参见图disable when .
)。
节点设置(图 1)
网格上的每个节点都将获得 6 个额外的锚点add anchor to node
,其中顶部三个(in 1
、in 2
、in 3
),底部三个(out
)。这是因为第一个图中的所有节点都有outs = 3, ins = 3
一个选项。
由于我们无法在设置网格时直接创建这些锚点(即坐标转换为的锚点),因此我们使用了库label
中特别考虑的graphs
。
这也是我们需要两个的原因graphs
。
如果需要更多输出或输入锚点,可以在节点的选项中指定它们:MATH 102[outs=5]
。
连接节点(图2)
第二张图use existing nodes
(即来自上一步的图)现在可以连接这些节点。
这里我们使用三种连接:
- 一条直线将会像往常一样连接起来。
|-|
来自库的连接。ext.paths.ortho
我们可以使用 指定两端的锚点oi = <out number>:<in number>
。elbow
使用以下值和键的连接:
out
:out
锚点(默认为中间的锚点,2
因为所有节点默认都设置了三个这样的锚点),in: the
in` 锚点(同上),oi = <out number>:<in number>
,设置前两个,|
指定弯头连接的垂直车道,这些0
为第一列和第二列之间的空间等等,该值-1
代表第一列左边的垂直车道。下面的代码中没有用到这个键,因为这个
elbow
键的设置方式是,你可以在连接的选项中直接输入垂直车道的值elbow
,即elbow = {2, oi = 1:2}
与elbow = {| = 2, oi = 1:2}
ok
并ik
指定起始节点后第一个扭结的比率(o乌特钾第二个为目标节点前最后一个扭结的比例。这些比率的设置使得
0
扭结位于节点的边界,而1
将其直接放置在学期车道的水平线上。via
并via distance
为垂直连接指定水平偏移。使用 的默认值
via distance = 0.05
,以下内容相同:| = 2, via = 1 | = 2.05
因为 2 + 1 × 0.05 = 2.05
需要注意的是,这些值可以在图形范围开始时设置,即
{[/tikz/elbow/|=3] c1 -> [elbow] c2, c3 -> [elbow] c4 }
并且这两个连接都将使用已经设置的值。
在以前的版本中,起点不是使用固定的锚点等out 1
,而是也可以指定为一个比率(即,南边或北边的中间)。out 2
0.5
我不知道哪一个更好。
您始终可以设置outs = 11, ins = 11
将 11 个锚点放置在 ¹/₁₂、²/₁₂、…、¹¹/₁₂ 处,其中与设置的out 2
相同或与下的相同。out 1
outs = 5
out 3
out 1
outs = 3
代码
\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta, calc, graphs, ext.paths.ortho}
\makeatletter
\def\@firstofgobble@until@relax#1#2\relax{#1}
\newcommand*\iftikznamestartswithstar{\if*\expandafter\@firstofgobble@until@relax\tikzgraphnodename\relax}
\tikzset{% https://tex.stackexchange.com/a/676090/16595
add anchor to node/.code n args={3}{%
\edef\tikz@temp##1{% \tikz@pp@name/\tikzlastnode needs to be expanded
\noexpand\pgfutil@g@addto@macro\expandafter\noexpand\csname pgf@sh@ma@\tikz@pp@name{#1}\endcsname{%
\def\expandafter\noexpand\csname pgf@anchor@\csname pgf@sh@ns@\tikz@pp@name{#1}\endcsname @#2\endcsname{##1}}}%
\tikz@temp{#3}},
add anchor to node*/.code n args={3}{% 1: node, 2: anchor, 3: coordinate
\begingroup
\pgfsettransform{\csname pgf@sh@nt@\tikz@pp@name{#1}\endcsname}%
\pgf@process{\pgfpointanchor{\tikz@pp@name{#3}}{center}}%
\pgfkeysalso{/tikz/add anchor to node/.expanded={#1}{#2}{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}}%
\endgroup}}
\pgfqkeys{/pgf/foreach}{% https://tex.stackexchange.com/a/110823/16595
global remember/.code=\pgfutil@append@tomacro{\pgffor@remember@code}{\gdef\noexpand#1{#1}}}
\newcommand*\tikzelbowset{\pgfqkeys{/tikz/elbow}}
\tikzelbowset{
label/.style n args={4}{
shape=coordinate,name=elbow@temp,anchor=center,tikz@label@post/.code 2 args=,
at={($(\tikzlastnode.#2 west)!elbowAbs(#3,#4)!(\tikzlastnode.#2 east)$)},
append after command/.expanded={[add anchor to node*={\tikzlastnode}{#1 #3}{elbow@temp}]}},
via distance/.initial=.05,
out/.initial=2, in/.initial=2, oi/.style args={#1:#2}{/tikz/elbow/out={#1},/tikz/elbow/in={#2}},
ok/.initial=.5, ik/.initial=.5, via/.initial=0,
|/.initial=0, .unknown/.code=\pgfkeyssetevalue{/tikz/elbow/|}{\pgfkeyscurrentname},
.style={to path={\pgfextra{\tikzelbowset{#1}}
coordinate (elbow@out) at (\tikztostart .out \pgfkeysvalueof{/tikz/elbow/out})
coordinate (elbow@in) at (\tikztotarget.in \pgfkeysvalueof{/tikz/elbow/in})
coordinate (elbow@out kink) at (elbow@out|-{$(\tikztostart.south)!\pgfkeysvalueof{/tikz/elbow/ok}!([shift=(up:.5)]\tikztostart.center)$})
coordinate (elbow@in kink) at (elbow@in|-{$(\tikztotarget.north)!1-\pgfkeysvalueof{/tikz/elbow/ik}!([shift=(down:.5)]\tikztotarget.center)$})
coordinate (elbow@) at ({\pgfkeysvalueof{/tikz/elbow/|}+.5+\pgfkeysvalueof{/tikz/elbow/via distance}*\pgfkeysvalueof{/tikz/elbow/via}},0)
(elbow@out) -- (elbow@out kink) -- (elbow@|-elbow@out kink) -- (elbow@|-elbow@in kink) \tikztonodes -- (elbow@in kink) -- (elbow@in)}}}
\tikzset{
elbows/.code=\tikzelbowset{#1},
outs/.style={/tikz/elbow/temp/.style={label={[/tikz/elbow/label={out}{south}{##1}{#1}]}},/tikz/elbow/temp/.list={1,...,#1}},
ins/.style ={/tikz/elbow/temp/.style={label={[/tikz/elbow/label={in} {north}{##1}{#1}]}},/tikz/elbow/temp/.list={1,...,#1}}}
\pgfset{declare function={elbowRel(\i,\n)=(\i-(\n-1)/2); elbowAbs(\i,\n)=\i/(\n+1);}}
\begin{document}
\begin{tikzpicture}[
x=3cm, y=-2cm,
disable when ./.style={coordinate},
gen/.style={fill=yellow!50},
gen width/.initial=.9cm,
sem width/.initial=2cm,
subj/.style={draw, rounded corners, minimum width=1.9cm, minimum height=1cm},
]
\newcommand*\cols{7}
\foreach[count=\row from 0, remember=\row as \lastRow (initially -1)] \GEN/\SEM in {
FRESHMAN/{1st, 2nd}, SOPHOMORE/{1st, 2nd}, JUNIOR/{1st, 2nd}, SUMMER/., SENIOR/{1st, 2nd}}{
\let\firstRow\row \let\row\lastRow
\foreach[remember=\row as \lastRow (initially \row), global remember=\lastRow] \SEMESTER in \SEM {
\pgfmathtruncatemacro\row{\lastRow+1}
\draw[shift=(up:\row)] (-.75,-.5) coordinate (@) rectangle ++(\cols+.5,1)
(@) rectangle node[disable when \SEMESTER/.try, align=center]{\SEMESTER\\Semester}
++(-\pgfkeysvalueof{/tikz/sem width},1) coordinate (@);}
\draw[gen] (@) rectangle node[rotate=90]{\GEN} ([xshift=-\pgfkeysvalueof{/tikz/gen width}]0,\firstRow-.5-|@);
\let\row\lastRow}
\graph[
grid placement, wrap after=\cols, branch up, % because y is inverted
nodes={subj, \iftikznamestartswithstar coordinate\fi, outs=3, ins=3},
fresh nodes,
]{% * = empty place in the grid
MATH 101, PHYS 101, CHEM 101, ENGL 101, IAS 121, *, *,
MATH 102[outs=5], PHYS 102, ICS 104, ENGL 102, PE 101, IAS 111, *,
MATH 201, EE 201[outs=5], COE 202, ENGL 214, ISE 291, *, *,
MATH 208, EE 203, EE 272, EE 207, EE 213, EE 271, COE 292,
PHYS 305, EE 303, EE 315, EE 390, EE 360, EE 3XX Lab, CGS 392,
EE 340, EE 370, EE 311, EE 380, EE 3XX Lab, BUS 200, *,
*, *, EE 399,
};% one graph ends here because all the labels that create anchors need to be processed
\path[>={Triangle[scale=.75]}, thick, ortho/install shortcuts, rounded corners] graph[
use existing nodes,
oi/.style args={#1:#2}{left anchor=out #1,right anchor=in #2},
or/.style={/tikz/ortho/ratio={#1}}]{
{[edges=yellow!80!black] MATH 101 -> PHYS 101, MATH 102 -> PHYS 102, EE 203 -> EE 272, EE 213 -> EE 271},
{
MATH 101 -> MATH 102 -> MATH 201,
PHYS 101 -> PHYS 102 -> EE 201,
ENGL 101 -> ENGL 102 -> ENGL 214,
% EE 201 ->[oi=1:2,|-|] EE 203,
EE 201 -> EE 203,
MATH 208 -> PHYS 305 -> EE 340,
EE 203 -> EE 303,
EE 311 -> EE 399,
},
MATH 102 ->[elbow={-1, oi=1:1}] MATH 208,
MATH 201 ->[elbow={-1, via=1, oi=1:1}] PHYS 305,
MATH 102 ->[oi=4:1, |-|, or=.75] EE 201,
MATH 102 ->[oi=5:1, |-|, or=.6] ISE 291,
PHYS 102 ->[elbow={0, via=-1, oi=1:3}] PHYS 305,
EE 203 ->[elbow={0, via=1, oi=1:1}] EE 370,
PHYS 102 ->[oi=3:2, |-|, or=.75] COE 202,
EE 201 ->[oi=4:1, |-|, or=.75] EE 207,
EE 201 ->[oi=5:2, |-|, or=.6] EE 213,
ICS 104 ->[oi=3:3, |-|, or=.25] ISE 291,
ICS 104 ->[elbow={2, ok=.75, oi=2:1}] EE 390,
};
\end{tikzpicture}
\end{document}