我们可以在节点的样式定义中使用 `let`(来自 `TikZ`)吗?

我们可以在节点的样式定义中使用 `let`(来自 `TikZ`)吗?

我知道如何将库let中的命令用于命令中并修复该路径上特定节点的某些参数。但我想在节点的样式定义中使用。TikZ calcpath

作为示例,考虑如何自动修复single arrow节点之间的高度和边框旋转:

\documentclass[tikz,border=2mm]{standalone} 
\usetikzlibrary{positioning, shapes.arrows, calc}

\begin{document}

\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};
\path (a.north east) let \p1=($(b.south west)-(a.north east)$) in
    node[single arrow, draw, 
        minimum height={veclen(\x1,\y1)}, 
        shape border uses incircle,
        shape border rotate={atan2(\y1,\x1)},
        anchor=tail] at (a.north east) {};
\end{tikzpicture}
\end{document}

在此处输入图片描述

我想用类似的样式定义这个箭头节点arrow between a and b并能够使用

\node[arrow between a and b] {}; 

问题是我从未见过leta 内部有.\stylefor a node。可以做到吗?

答案1

  1. 是的,math图书馆及其/tikz/evaluate钥匙(不要混淆/pgf/foreach/evaluate):

    \tikzset{
      arrow between/.style args={#1 and #2}{
        evaluate={
          coordinate \diffPoint;
          \diffPoint=(#2.south west)-(#1.north east);},
        shape=single arrow, draw,
        at={(#1.north east)}, anchor=tail,
        shape border uses incircle,
        minimum height=veclen(\diffPoint)*1pt,
        shape border rotate={atan2(\diffPointy,\diffPointx)}}}
    
  2. 是的,通过使用let … in解析器,但停用切换回\path解析的功能。

    \makeatletter
    \tikzset{parse let/.code={\def\tikz@cc@stop@let in{}\tikz@let@command et #1in}}
    \makeatother
    \tikzset{
      arrow between'/.style args={#1 and #2}{
        parse let={\p{diffPoint}=($(#2.south west)-(#1.north east)$)},
        shape=single arrow, draw,
        at={(#1.north east)}, anchor=tail,
        shape border uses incircle,
        minimum height=veclen(\p{diffPoint})*1pt,
        shape border rotate={atan2(\y{diffPoint},\x{diffPoint})}}}
    
  3. 或者直接使用 PGF 级别的计算

    • \pgfkeyssetevalue{<key>}{<value>}的更快版本在哪里\pgfkeys{<key>/.expanded=<value>}
    • \pgfmathveclen@更快的版本在哪里\pgfmathveclen\pgfmathparse{veclen(…)因为参数不需要再次解析,并且
    • 我们重新使用全球化维度\pgf@x\pgf@y来自\pgfmathanglebetweenpoints'\pgfpointdiff的功能veclen
    \tikzset{
      arrow between''/.style args={#1 and #2}{%
        shape=single arrow, draw,
        at={(#1.north east)}, anchor=tail,
        shape border uses incircle,
        /utils/exec=%
          \pgfmathanglebetweenpoints
            {\pgfpointanchor{#1}{north east}}{\pgfpointanchor{#2}{south west}}%
          \pgfkeyssetevalue{/pgf/shape border rotate}{\pgfmathresult}%
          \pgfmathveclen@{\pgf@x}{\pgf@y}%
          \pgfkeyssetevalue{/pgf/minimum height}{\pgfmathresult pt}}}
    

当然,所有解决方案都需要做一些工作来检测所需的锚点。(是#2低于还是高于#1?在右边还是左边?当它们(几乎)垂直或水平对齐时应该使用什么锚点?)

代码

\documentclass[tikz,border=2mm]{standalone} 
\usetikzlibrary{positioning, shapes.arrows, calc, math}
% 1. math library that allows 'calc'ulations:
\tikzset{
  arrow between/.style args={#1 and #2}{
    evaluate={
      coordinate \diffPoint;
      \diffPoint=(#2.south west)-(#1.north east);},
    shape=single arrow, draw,
    at={(#1.north east)}, anchor=tail,
    shape border uses incircle,
    minimum height=veclen(\diffPoint)*1pt,
    shape border rotate={atan2(\diffPointy,\diffPointx)}}}
% 2. using the original let syntax
\makeatletter
\tikzset{parse let/.code={\def\tikz@cc@stop@let in{}\tikz@let@command et #1in}}
\makeatother
\tikzset{
  arrow between'/.style args={#1 and #2}{
    parse let={\p{diffPoint}=($(#2.south west)-(#1.north east)$)},
    shape=single arrow, draw,
    at={(#1.north east)}, anchor=tail,
    shape border uses incircle,
    minimum height=veclen(\p{diffPoint})*1pt,
    shape border rotate={atan2(\y{diffPoint},\x{diffPoint})}}}
% 3. using lower level PGF commands
\makeatletter
\tikzset{
  arrow between''/.style args={#1 and #2}{%
    shape=single arrow, draw,
    at={(#1.north east)}, anchor=tail,
    shape border uses incircle,
    /utils/exec=%
      \pgfmathanglebetweenpoints
        {\pgfpointanchor{#1}{north east}}{\pgfpointanchor{#2}{south west}}%
      \pgfkeyssetevalue{/pgf/shape border rotate}{\pgfmathresult}%
      \pgfmathveclen@{\pgf@x}{\pgf@y}%
      \pgfkeyssetevalue{/pgf/minimum height}{\pgfmathresult pt}}}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};
\node[arrow between=a and b] {};
\end{tikzpicture}

\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};
\node[arrow between'=a and b] {};
\end{tikzpicture}

\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};
\node[arrow between''=a and b] {};
\end{tikzpicture}
\end{document}

答案2

这就是您所寻找的吗?

回答这个问题该节点不在节点内部,因为可以在其中放置文本: 在此处输入图片描述

\documentclass[tikz,border=2mm]{standalone} 
\usetikzlibrary{positioning, shapes.arrows, calc}

\begin{document}

\tikzset{/tikz/.cd,
    my arrow/.code args={#1 and #2}{%
        \path (#1.north east) let \p1=($(#2.south west)-(#1.north east)$) in
            node[single arrow, draw, 
            minimum height={veclen(\x1,\y1)}, 
%               shape border uses incircle,
%               shape border 
            rotate={atan2(\y1,\x1)},
            anchor=tail] at (#1.north east) {\MyAwTxt};
    },
    my arrow text/.store in=\MyAwTxt,
    my arrow text=,
}

\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};
\node[my arrow text=bob,my arrow={a and b}]{x};

\end{tikzpicture}
\end{document}

是第一次尝试

在此处输入图片描述

\documentclass[tikz,border=2mm]{standalone} 
\usetikzlibrary{positioning, shapes.arrows, calc}

\begin{document}

\tikzset{/tikz/.cd,
    my arrow/.code args={#1 and #2}{%
        \path (#1.north east) let \p1=($(#2.south west)-(#1.north east)$) in
            node[single arrow, draw, 
            minimum height={veclen(\x1,\y1)}, 
            shape border uses incircle,
            shape border rotate={atan2(\y1,\x1)},
            anchor=tail] at (#1.north east) {};
    },
}

\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};

%\node[my arrow={a and b}] {}; % <- OP's request

\path[my arrow={a and b}] ; % <- My first idea

\end{tikzpicture}
\end{document}

答案3

我可能遗漏了一些东西,但你可以做一些类似的事情这个答案更新@Tarass 告诉我,文本应该位于箭头内,这很容易实现。如果我知道所有要求,我会很乐意实现它们。

\documentclass[tikz,border=2mm]{standalone} 
\usetikzlibrary{positioning, shapes.arrows, calc}
\usetikzlibrary{calc}
\tikzset{
  my arrow/.style n args={2}{at={($(#1)!0.5!(#2)$)},
    append after command={
      \pgfextra{\path (#1.north east) let \p1=($(#2.south west)-(#1.north east)$) in
    node[single arrow, draw, 
        minimum height={veclen(\x1,\y1)}, 
        shape border uses incircle,
        shape border rotate={atan2(\y1,\x1)},
        anchor=tail] at (a.north east) {};
    }}
  }
}
\begin{document}

\begin{tikzpicture}
\node[draw] (a) {A};
\node[draw, above right=2cm and 1cm of a] (b) {B};
\node[my arrow={a}{b}]{C};

\end{tikzpicture}
\end{document}

在此处输入图片描述

相关内容