TikZ:使节点高度跨越其他几个节点

TikZ:使节点高度跨越其他几个节点

如何使 Z 节点跨越 ABCD 列的总高度?

节点

我当前的代码是:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,positioning}

\begin{document}
\begin{tikzpicture}[node distance=5pt,every node/.style={draw, minimum width=100pt}]
  \node(A)             {A};
  \node(B)[below=of A] {B};
  \node(C)[below=of B] {C};
  \node(D)[below=of C] {D};

  \path let \p1=(A.north), \p2=(D.south) in
    node[minimum height={\y2-\y1},right=of A] {Z};
\end{tikzpicture}
\end{document}

由于某种原因,计算出的最小高度不起作用。
我在帖子中读到

我不应该使用minimum heightbuttext height来代替(但我不明白为什么)。然而,这也无济于事。

答案1

两种解决方案,无论是使用fit还是使用calc,都有各自的缺点。fit解决方案需要使用 进行修复inner ysep,更重要的是,outer ysepcalc解决方案需要在实际节点之外进行额外的调整(使用\path let … in)。

让我们将它们结合起来。fit密钥基本上会扫描给定的所有坐标/节点。如果遇到节点,则分别扫描节点的锚点、和westeast如果没有,则给定的点就是:一维坐标。最后,将四个维度设置为高度和最低点northsouthX值。我们在画布平面上只有一个矩形。

我们可以利用这四个维度

  • 计算跨度垂直或水平长度和
  • 创建伪节点。与 类似path picture bounding box,将在本地创建 。这使得它非常适合与已经定义好的键(如和 )fit bounding box一起使用。positionleftright

这个答案介绍了库的四个键和一个增强键positioningof当然仅限于)。

关键在于:

  • fit bounding boxfit bounding box:在给定节点周围创建一个名为的矩形节点。语法类似fitkey。此节点是本地定义的。如果您想在以下路径中使用它,请在 内使用它\tikzset

  • span verticalspan horizontal:它们分别在给定的节点上发出minimum height或(与 相同的语法)。minimum widthfit

  • spanspan vertical并且span horizontal(无需扫描坐标/节点两次)

按键的增强positioning工作类似:

如果后面的部分of以左括号开头,则(它充当通常行为(仅放置)和围绕一个或多个节点的切换。这意味着,放置是相对于围绕所有给定节点的矩形框而言的。

如果后面的部分以、 a或 aof开头,则创建的节点也将垂直 ( )、水平或垂直和水平地适合给定的节点(如果只有一个节点,则可以省略括号,如下所示)|-+|-+( )

\node[span vertical=(A)(B), above=of -X] {y};

查看所有给出的示例,并改变每个节点的形状,看看它如何影响不同的节点形状。


我之前想到的解决方案实际上取决于你想要跨越的所有节点都具有相同的水平范围,并且你必须准确给出最上层和最下层的节点(后者可以轻松解决,但库fit已经很好地处理了这个问题)。它也使用了类似的东西,right=of <upper node>.north east, anchor=north west但 TikZ 化了,带有north rightcorner north right(用于圆圈)键,来自我的positioning-plus图书馆在某些情况下这可能还是有帮助的。

实际的fit键使用text widthtext heighttext depth来设置新节点的尺寸(这也产生了新节点不能比拟合节点更细的问题)。当然,现在 sinner sep仍然处于活动状态,而minimum height只是最小值(这意味着如果您在其中写入的内容超过其承载能力,它将被拉伸,而原始fit键实际上并不适合在创建的节点中放置文本)。

代码

\documentclass[tikz,convert=false]{standalone}
\usetikzlibrary{fit,positioning,shapes}
\makeatletter
\def\pgfutil@firstofmany#1#2\pgf@stop{#1}
\def\pgfutil@secondofmany#1#2\pgf@stop{#2}
\def\tikz@lib@place@of@#1#2#3{%
  \def\pgf@tempa{fit bounding box}%
  \edef\pgf@temp{\expandafter\pgfutil@firstofmany#2\pgf@stop}
  \if\pgf@temp(%
    \tikz@lib@place@fit@scan{#2}{0}%
  \else\if\pgf@temp|
      \expandafter\tikz@lib@place@fit@scan\expandafter{\pgfutil@secondofmany#2\pgf@stop}{1}%
    \else\ifx\pgf@temp\tikz@activebar
        \expandafter\tikz@lib@place@fit@scan\expandafter{\pgfutil@secondofmany#2\pgf@stop}{1}%
      \else\if\pgf@temp-
          \expandafter\tikz@lib@place@fit@scan\expandafter{\pgfutil@secondofmany#2\pgf@stop}{2}%
        \else\if\pgf@temp+
            \expandafter\tikz@lib@place@fit@scan\expandafter{\pgfutil@secondofmany#2\pgf@stop}{3}%
          \else
            \def\pgf@tempa{#2}%
          \fi
        \fi
      \fi
    \fi
  \fi
  \expandafter\tikz@scan@one@point\expandafter\tikz@lib@place@remember\expandafter(\pgf@tempa)%
  \iftikz@shapeborder%
    % Ok, this is relative to a border.
    \iftikz@lib@ignore@size%
      \edef\tikz@node@at{\noexpand\pgfpointanchor{\tikz@shapeborder@name}{center}}%
      \def\tikz@anchor{center}%
    \else%
      \edef\tikz@node@at{\noexpand\pgfpointanchor{\tikz@shapeborder@name}{#3}}%
    \fi%
  \fi%
  \edef\tikz@lib@place@nums{#1}%
}
\def\tikz@lib@place@fit@scan#1#2{
  \pgf@xb=-16000pt\relax%
  \pgf@xa=16000pt\relax%
  \pgf@yb=-16000pt\relax%
  \pgf@ya=16000pt\relax%
  \if\pgfutil@firstofmany#1\pgf@stop(%
    \tikz@lib@fit@scan#1\pgf@stop%
  \else
    \tikz@lib@fit@scan(#1)\pgf@stop
  \fi
  \ifdim\pgf@xa>\pgf@xa
    % shouldn't happen
  \else
     \expandafter\def\csname pgf@sh@ns@fit bounding box\endcsname{rectangle}%
     \expandafter\edef\csname pgf@sh@np@fit bounding box\endcsname{%
       \def\noexpand\southwest{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
       \def\noexpand\northeast{\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}%
     }%
     \expandafter\def\csname pgf@sh@nt@fit bounding box\endcsname{{1}{0}{0}{1}{0pt}{0pt}}%
     \expandafter\def\csname pgf@sh@pi@fit bounding box\endcsname{\pgfpictureid}%
     \ifcase#2\relax
     \or % 1 = vertical
       \pgf@y=\pgf@yb%
       \advance\pgf@y by-\pgf@ya%
       \edef\pgf@marshal{\noexpand\tikzset{minimum height={\the\pgf@y-2*(\noexpand\pgfkeysvalueof{/pgf/outer ysep})}}}%
       \pgf@marshal
     \or % 2 = horizontal
       \pgf@x=\pgf@xb%
       \advance\pgf@x by-\pgf@xa%
       \edef\pgf@marshal{\noexpand\tikzset{minimum width={\the\pgf@x-2*(\noexpand\pgfkeysvalueof{/pgf/outer xsep})}}}%
       \pgf@marshal
     \or % 3 = both directions
       \pgf@y=\pgf@yb%
       \advance\pgf@y by-\pgf@ya%
       \pgf@x=\pgf@xb%
       \advance\pgf@x by-\pgf@xa%
       \edef\pgf@marshal{\noexpand\tikzset{minimum height={\the\pgf@y-2*(\noexpand\pgfkeysvalueof{/pgf/outer ysep})},minimum width={\the\pgf@x-2*(\noexpand\pgfkeysvalueof{/pgf/outer xsep})}}}%
       \pgf@marshal
     \fi
  \fi
}
\tikzset{
  fit bounding box/.code={\tikz@lib@place@fit@scan{#1}{0}},
  span vertical/.code={\tikz@lib@place@fit@scan{#1}{1}},
  span horizontal/.code={\tikz@lib@place@fit@scan{#1}{2}},
  span/.code={\tikz@lib@place@fit@scan{#1}{3}}}

\makeatother
\begin{document}
\begin{tikzpicture}[node distance=5pt,every node/.style={draw,minimum width=50pt}]
  \node(A)             {A};
  \node(B)[below=of A, xshift=.5cm] {B};
  \node(C)[below=of B, xshift=-1cm] {C};
  \node(D)[below=of C, xshift=.75cm] {D};

  \node[right=of |(A)(B)(D)]          {Z};

  \node[left=of |(A)(B), ultra thick] {Y};
  \node[left=of |(C)(D), minimum width=75pt] (X) {X};

  \node[span vertical=(A)(B), above=of -X] {y};

  \node[below=of -(A)(B)(C)(D)] {0};
\end{tikzpicture}

\begin{tikzpicture}[node distance=5pt,every node/.style={draw, ellipse, minimum width=25pt}]
  \node(A)             {A};
  \node(B)[below=of A, xshift=.5cm] {B};
  \node(C)[below=of B, xshift=-1cm] {C};
  \node(D)[below=of C, xshift=.75cm] {D};

  \node[right=of |(A)(B)(D)]          {Z};

  \node[left=of |(A)(B), ultra thick] {Y};
  \node[left=of |(C)(D), minimum width=50pt]          (X) {X};

  \node[span vertical=(A)(B), above=of -X] {y};

  \node[below=of -(A)(B)(C)(D)] {0};
\end{tikzpicture}
\end{document}

输出

在此处输入图片描述


在此处输入图片描述

答案2

正如 Qrrbrbirlbel 指出的那样,问题在于我切换了\y1\y2
在 TikZ 中,“up” 表示增加的坐标,与 CSS 相反。解决方案如下:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,positioning}

\begin{document}
\begin{tikzpicture}[node distance=5pt,every node/.style={draw, minimum width=100pt}]
  \node(A)             {A};
  \node(B)[below=of A] {B};
  \node(C)[below=of B] {C};
  \node(D)[below=of C] {D};

  \path let \p1=(A.north), \p2=(D.south) in
    node[minimum height={\y1-\y2-\pgflinewidth},
         right=of A.north east,anchor=north west] {Z};
\end{tikzpicture}
\end{document}

请注意,我们需要减去线宽,并指定锚点以便正确放置。


Claudio 展示了另一种解决这个问题的方法,使用fit图书馆:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,fit}

\begin{document}
\begin{tikzpicture}[node distance=5pt,
                    every node/.style={draw, minimum width=100pt, outer sep=0pt}]

  \node(A)             {A};
  \node(B)[below=of A] {B};
  \node(C)[below=of B] {C};
  \node(D)[below=of C] {D};

  \node(Z)[fit=(A)(D),right=of A.north east,anchor=north west, inner sep=0] {Z};

\end{tikzpicture}

\end{document}

请注意我outer sep=0pt在这里如何使用来解决线宽不匹配的问题。


结果

节点

相关内容