我正在尝试构建一个相当复杂的鱼骨图,其中包含许多分支和子分支,如下所示。有没有一种相对简单的方法来重新创建它,还是我最好只使用 Inkscape 或其他东西?
编辑:以下是我迄今为止尝试过的 MWE。有几个问题:
- 绝对定位非常耗费人力,并且不允许以后进行任何调整,因为我需要更改每个节点
- 六个外部类别应位于文本边缘的最边缘
- 我不知道如何在不使用坐标的情况下将箭头绘制到其他箭头上
- 当规模扩大时,代码很快就会变得非常混乱
- 所有子箭头的角度应该相同
编辑2:我已经接近我想要的了,但是子分支仍然困扰着我。
梅威瑟:
\documentclass[10pt,headinclude]{scrbook}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[left=32.5mm, right=25mm, top=25mm, bottom=20mm, showframe]{geometry}
\usepackage{caption}
\usepackage{xcolor}
\definecolor{mgelb}{RGB}{255, 187, 0}
\definecolor{mblau}{RGB}{10, 59, 104}
\definecolor{mturkis}{RGB}{0, 171, 183}
\definecolor{mrot}{RGB}{255, 70, 70}
\definecolor{mgrun}{RGB}{41, 175, 0}
\definecolor{mlila}{RGB}{136, 55, 155}
\definecolor{mgrau1}{RGB}{230, 230, 230}
\usepackage{tikz}
\tikzset{>=stealth}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usepackage{tikzpagenodes}
\begin{document}
\begin{figure}
\footnotesize
\begin{tikzpicture}[->, remember picture]
\node (bq) [anchor=south, yshift=13pt+\abovecaptionskip, rectangle, draw, fill=mgrau1] at (current page text area.south) {\small Bauteilqualität};
\node[anchor=west] (met) at ($(current page text area.north west)-(0,5)$) {\small\color{mturkis}Methode};
\node (bau) at ($(met)+(5,3)$) {\color{mturkis}Bauteil};
\node (bel) at ($(met)+(3,4)$) {\color{mturkis}Belichtung};
\node[anchor=west] (mes) at (current page text area.west) {\small\color{mrot}Messung};
\node[anchor=west] (men) at ($(current page text area.south west)+(0,5)$) {\small\color{mlila}Mensch};
\node[anchor=east] (mas) at ($(current page text area.north east)-(0,5)$) {\small\color{mgelb}Maschine};
\node[anchor=east] (mit) at (current page text area.east) {\small\color{mblau}Mitwelt};
\node[anchor=east] (mat) at ($(current page text area.south east)+(0,5)$) {\small\color{mgrun}Material};
\draw[ultra thick] (current page text area.north) -- (bq);
\draw[very thick, mturkis] (met.east) -- ($(met-|bq)-(0.8pt,0)$);
\draw[thick, mturkis] (bau.south) -- ($(met-|bq)-(0.8pt,0)$);
\draw[very thick, mrot] (mes.east) -- ($(mes-|bq)-(0.8pt,0)$);
\draw[very thick, mlila] (men.east) -- ($(men-|bq)-(0.8pt,0)$);
\draw[very thick, mgelb] (mas.west) -- ($(mas-|bq)+(0.8pt,0)$);
\draw[very thick, mblau] (mit.west) -- ($(mit-|bq)+(0.8pt,0)$);
\draw[very thick, mgrun] (mat.west) -- ($(mat-|bq)+(0.8pt,0)$);
\end{tikzpicture}
\caption{Test caption for vertical spacing}
\end{figure}
\end{document}
答案1
只需一点点工作就可以创建一个宏\subbranch
,例如
\subbranch{met}{2cm}{foobar}{1}{a,b,c,d}
制作一个属于该met
分支的子分支,从主干开始 2 厘米,带有标签foobar
,放置在分支上方(-1
将其放置在分支下方),有叶子a,b,c,d
。
很有可能可以定义更好/更简单的界面,但这至少可以作为一个例子。
\documentclass[10pt,headinclude]{scrbook}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[left=32.5mm, right=25mm, top=25mm, bottom=20mm, showframe]{geometry}
\usepackage{caption}
\usepackage{xcolor}
\definecolor{mgelb}{RGB}{255, 187, 0}
\definecolor{mblau}{RGB}{10, 59, 104}
\definecolor{mturkis}{RGB}{0, 171, 183}
\definecolor{mrot}{RGB}{255, 70, 70}
\definecolor{mgrun}{RGB}{41, 175, 0}
\definecolor{mlila}{RGB}{136, 55, 155}
\definecolor{mgrau1}{RGB}{230, 230, 230}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usetikzlibrary{arrows.meta}
\tikzset{>={Stealth[length=12pt,width=6pt]}}
\usepackage{tikzpagenodes}
% first some setup:
% a counter to get the total number of "leaves" on the subbranches
\newcounter{ListCounter}
% two dimensions to save some x-coordinates
\newdimen\XMid
\newdimen\XBranch
% a length to set the separation between the leaves
\pgfmathsetlengthmacro{\LeafSeparation}{12pt}
% an angle defining the offset of the branches from vertical
\pgfmathsetmacro{\SubBranchSlant}{30}
%then define a macro to draw a subbranch
\newcommand\subbranch[6][]{%
% arguments:
% #1 optional, not currently used
% #2 name of branch node (e.g. met, mes, etc. for your case)
% #3 distance along branch from center
% #4 text for label of subbranch
% #5 +1 or -1, defines if the subbranch is above or below the branch
% #6 list of leaves
%
%count the number of leaves, save value in \NoElem
\setcounter{ListCounter}{0}
\foreach [count=\i] \j in {#6}{\stepcounter{ListCounter}}
\pgfmathsetmacro{\NoElem}{\arabic{ListCounter}}
% now calculate some values to determine branch length and angles:
% \LabelAngle defines whether the branch label goes above or below the end of the line
\pgfmathsetmacro{\LabelAngle}{#5*90}
% \StemLength is the length of the subbranch, depends on the leaf separation and number of leaves
\pgfmathsetlengthmacro{\StemLength}{(\NoElem + 1) * \LeafSeparation}
% extract x-coord of horizontal center of diagram (bq-node)
\pgfextractx\XMid{\pgfpointanchor{bq}{center}}
% extract x-coord of the branch node
\pgfextractx\XBranch{\pgfpointanchor{#2}{center}}
% define the position of the leaves
\pgfmathsetmacro\LeafAngle{ifthenelse(sign(\XMid-\XBranch)<0, 0, 180)}
% now draw the branch
\draw [<-, shorten <=0.8pt, #2]
let
\p1=(#2), \p2=(bq.north), \n1={sign(\x2-\x1)}, \n2={#5*90 + #5*\n1*\SubBranchSlant}
in
($(#2-|bq)!#3!(#2)$) coordinate (a) --
++(\n2:\StemLength) coordinate[label={[align=center]\LabelAngle:#4}] (b);
% finally add the leaves
\foreach [count=\i] \t in {#6}
\draw [Stealth-] ($(a)!\LeafSeparation*\i!(b)$) -- ++(\LeafAngle:12pt) node[anchor=\LeafAngle+180] {\t};
}
\begin{document}
\begin{figure}
\footnotesize
\begin{tikzpicture}[->, remember picture,
% for convenience, define styles with the same names as the node
% names used for the branch nodes. The styles only have the color
% of the branches
met/.style={mturkis}, mes/.style={mrot}, men/.style={mlila},
mas/.style={mgelb}, mit/.style={mblau}, mat/.style={mgrun}
]
\begin{scope}[every node/.style={font=\small}]
\node (bq) [anchor=south, yshift=13pt+\abovecaptionskip, rectangle, draw, fill=mgrau1] at (current page text area.south) {Bauteilqualität};
\foreach [count=\i] \leftnode/\nodelabel in {Methode/met/,Messung/mes,Mensch/men}
\node[right, \nodelabel] (\nodelabel) at
($(current page text area.north west)!0.25*\i!(bq.north -| current page text area.west)$) {\leftnode};
\foreach [count=\i] \leftnode/\nodelabel in {Maschine/mas,Mitwelt/mit,Material/mat}
\node[left, \nodelabel] (\nodelabel) at
($(current page text area.north east)!0.25*\i!(bq.north -| current page text area.east)$) {\leftnode};
\end{scope}
\draw[ultra thick] (current page text area.north) -- (bq);
\foreach \nd in {met,mes,men,mas,mit,mat}
\draw[very thick, \nd, shorten >=0.8pt] (\nd) -- (\nd -| bq);
\subbranch{met}{2cm}{foo\\bar}{1}{a,b,c,d}
\subbranch{met}{4cm}{Rabbits}{-1}{a,b,c,d}
\subbranch{mas}{2cm}{Lipsum}{1}{a,b,c,d,e,f,g}
\subbranch{mas}{2cm}{Ducks}{-1}{a,b,c,d,e,f,g}
\end{tikzpicture}
\caption{Test caption for vertical spacing}
\end{figure}
\end{document}
答案2
碰巧的是,我一直在研究一种类似的格式,我对其进行了扩展以解决这个问题。代码虽然很冗长,但相对简单明了,并带有注释。
\documentclass{article}
\usepackage{tikz}
\usepackage[margin=0.5in]{geometry}
\usepackage{fontspec}
\usepackage{xparse}
\usepackage{keyval}
\usepackage{varwidth}
\usetikzlibrary{positioning,calc,arrows.meta}
\newlength{\xmove}
\newlength{\ymove}
\def\spinecolor{black}
\makeatletter
\define@key{fishbone}{xmoveit}{\setlength{\xmove}{#1}}
\define@key{fishbone}{ymoveit}{\setlength{\ymove}{#1}}
\define@key{fishbone}{spinecolor}{\def\spinecolor{#1}}
\makeatother
%% https://tex.stackexchange.com/questions/545308/tikz-scope-and-xshift-in-a-macro-issues/545318#545318
%% How many entries
\makeatletter
\pgfmathdeclarefunction{Dim}{1}{%
\begingroup%
\pgfutil@tempcnta0%
\@for\pgfutil@tempa:=#1\do{\advance\pgfutil@tempcnta1}%
\edef\pgfmathresult{\the\pgfutil@tempcnta}%
\pgfmathsmuggle\pgfmathresult
\endgroup%
}
\makeatother
%% Formats the text of the heads used on the spines
\NewDocumentCommand{\makehead}{m}{%
\begin{varwidth}{1in}
\linespread{0.8}\selectfont%Tighten line spacing in multiline heads
\centering
#1
\end{varwidth}%
}
%% Sets up the angle and spacing of the elements on the ribs:
\NewDocumentCommand{\setscale}{mm}{% 1=scale; 2=angle
\pgfmathsetmacro{\xdiff}{#1*cos(#2)}
\pgfmathsetmacro{\ydiff}{#1*sin(#2)}
}
%% |=====8><-----| %%
%% For the following 4 macros: Optional argument is for options: spinecolor=<a defined color> and
%% xmoveit and ymoveit are used to move the spines horizontally and vertically.
%% The first mandatory argument is a comma-separated list of the elements on the spine; if
%% there are textual commas in the elements, those commas must be hidden with braces {,}.
%% The second mandatory argument is the heading of the spine -- see examples below.
%% Note that spinecolor, once changed, stays in effect until changed again.
%% Note, too, that the effect of ymove, once changed, remains in effect until it is reset.
\NewDocumentCommand{\rldmakespine}{O{}mm}{%% Right to left, headed down
\pgfmathsetmacro{\maxitems}{Dim("{#2}")}
\setkeys{fishbone}{#1}
\begin{scope}[xshift=\xmove,yshift=\ymove]%
\foreach \N [count=\M from 1] in {#2}
{%
\node[anchor=west,inner xsep=0pt,xshift=10pt] (X) at (\M*\xdiff,\M*\ydiff)
{\strut\N};
\draw[thick,-{Stealth[]}] (X.west) -- ++(-8pt,0);
}%
\draw[ultra thick,{Stealth[]}-,\spinecolor] (0,0) --
(\maxitems*\xdiff,\maxitems*\ydiff)coordinate(head);
\node[anchor=south,above =3pt of head,\spinecolor]{\makehead{#3}};
\end{scope}%
}
\NewDocumentCommand{\lrdmakespine}{O{}mm}{%% Spine left to right, headed down
\pgfmathsetmacro{\maxitems}{Dim("{#2}")}
\setkeys{fishbone}{#1}
\begin{scope}[xshift=\xmove,yshift=\ymove]%
\foreach \N [count=\M from 1] in {#2}
{%
\node[anchor=east,inner xsep=0pt,xshift=-10pt] (X) at (-\M*\xdiff,\M*\ydiff)
{\strut\N};
\draw[thick,-{Stealth[]}] (X.east) -- ++(8pt,0);
}%
\draw[{Stealth[]}-,ultra thick,\spinecolor] (0,0) --
(-\maxitems*\xdiff,\maxitems*\ydiff)coordinate(head);
\node[anchor=south,above =3pt of head,\spinecolor]{\makehead{#3}};
\end{scope}%
}
\NewDocumentCommand{\rlumakespine}{O{}mm}{%% Spine right to left, headed up
\pgfmathsetmacro{\maxitems}{Dim("{#2}")}
\setkeys{fishbone}{#1}
\begin{scope}[xshift=\xmove,yshift=\ymove]%
\foreach \N [count=\M from 1] in {#2}
{%
\node[anchor=west,inner xsep=0pt,xshift=10pt] (X) at (\M*\xdiff,-\M*\ydiff)
{\strut\N};
\draw[thick,-{Stealth[]}] (X.west) -- ++(-8pt,0);
}%
\draw[{Stealth[]}-,ultra thick,\spinecolor] (0,0) --
(\maxitems*\xdiff,-\maxitems*\ydiff)coordinate(head);
\node[anchor=north,below =3pt of head,\spinecolor]{\makehead{#3}};
\end{scope}%
}
\NewDocumentCommand{\lrumakespine}{O{}mm}{%% Spine left to right, headed up
\pgfmathsetmacro{\maxitems}{Dim("{#2}")}
\setkeys{fishbone}{#1}
\begin{scope}[xshift=\xmove,yshift=\ymove]%
\foreach \N [count=\M from 1] in {#2}
{%
\node[anchor=east,inner xsep=0pt,xshift=-10pt] (X) at (-\M*\xdiff,-\M*\ydiff)
{\strut\N};
\draw[thick,-{Stealth[]}] (X.east) -- ++(8pt,0);
}%
\draw[{Stealth[]}-,ultra thick,\spinecolor] (0,0) --
(-\maxitems*\xdiff,-\maxitems*\ydiff)coordinate(head);
\node[anchor=north,below =3pt of head,\spinecolor]{\makehead{#3}};
\end{scope}%
}
%% |=====8><-----| %%
%% Set default
\setscale{0.475}{60}
\begin{document}
%\setscale{0.475}{75}
\begin{tikzpicture}
%% Major vertical central rule
\draw[ultra thick,-{Stealth[]}] (0,2in) -- (0,-6in)node[anchor=north,draw,thick,fill=lightgray] {Bauteilqualit\"at};
%% Major horizontal rules (the backbone)
\draw[-{Stealth[]},ultra thick,cyan] (-2.5in,0)node[anchor=east]{Methode} -- (-0.05in,0);
\draw[{Stealth[]}-,ultra thick,yellow!80!red](0.05in,0) -- (2.5in,0)node[anchor=west] {Maschine};
%% Ribs
\rldmakespine[xmoveit=0.2in,ymoveit=2pt,spinecolor=yellow!80!red]{A,B,C,D}{Spine 1R}
\rldmakespine[xmoveit=.65in]{A,B,C,D,E,F, G,H,I{,} next}{Spine 2R} %% Note hidden comma {,}
\rldmakespine[xmoveit=1.8in]{max. Temperature,Anzahl Elemente,Regelstagilit\"at,Homogenit\"at,Anordnung,Isolierung,Leistung}{Heizsystem\\und mehr}
\lrdmakespine[xmoveit=-0.2in,ymoveit=2pt,spinecolor=cyan]{1,2,3,4,5,6}{Spine 1L}
\lrdmakespine[xmoveit=-.6in]{A,B,C,D,E,F, G,H,I}{Spine 2L}
\lrdmakespine[xmoveit=-1.5in]{A,B,C,D,E,F, G,H,I,J}{Spine 3L}
%%
\rlumakespine[xmoveit=0.2in,ymoveit=-2pt,spinecolor=yellow!80!red]{1,2,3,4,5,6}{Spine 1}
\rlumakespine[xmoveit=1in]{A,B,C,D,E,F, G,H,I}{Spine 2}
\rlumakespine[xmoveit=2in]{A,B,C,D,E,F, G,H,I,J}{Spine 3}
\lrumakespine[xmoveit=-0.2in,spinecolor=cyan]{1,2,3,4,5,6}{Spine 1}
\lrumakespine[xmoveit=-1in]{A,B,C,D,E,F, G,H,I}{Spine 2}
\lrumakespine[xmoveit=-2in]{A,B,C,D,E,F, G,H,I,J}{Spine 3}
%%%%
\begin{scope}[yshift=-4in]
%% Major horizontal rules (the backbone)
\draw[-{Stealth[]},ultra thick,red!70!yellow] (-2.5in,0)node[anchor=east]{Messung} -- (-0.05in,0);
\draw[{Stealth[]}-,ultra thick,blue!70!black](0.05in,0) -- (2.5in,0)node[anchor=west] {Mitwelt};
%% Ribs
\rldmakespine[xmoveit=0.2in,ymoveit=2pt,spinecolor=blue!70!black]{1,2,3,4,5,6}{Spine 1R}
\rldmakespine[xmoveit=.7in]{A,B,C,D,E,F, G,H,I}{Spine 2R}
\rldmakespine[xmoveit=1.5in]{A,B,C,D,E,F, G,H,I,J}{Spine 3R}
\lrdmakespine[xmoveit=-.2in,spinecolor=red!70!yellow]{1,2,3,4,5,6}{Spine 1L}
\lrdmakespine[xmoveit=-.7in]{A,B,C,D,E,F, G,H,I}{Spine 2L}
\lrdmakespine[xmoveit=-1.25in]{A,B,C,D,E,F, G,H,I,J}{Spine 3L}
%%
\rlumakespine[xmoveit=0.2in,ymoveit=-2pt,spinecolor=blue!70!black]{1,2,3,4,5,6}{Spine 1}
\rlumakespine[xmoveit=1in]{A,B,C,D,E,F, G,H,I}{Spine 2}
\rlumakespine[xmoveit=2in]{A,B,C,D,E,F, G,H,I,J}{Spine 3\\u.s.w.}
\lrumakespine[xmoveit=-.2in,spinecolor=red!70!yellow]{1,2,3,4,5,6}{Spine 1}
\lrumakespine[xmoveit=-1in]{A,B,C,D,E,F, G,H,I}{Spine 2}
\lrumakespine[xmoveit=-2in]{A,B,C,D,E,F, G,H,I,J}{Spine 3}
\end{scope}
\end{tikzpicture}
\end{document}