TikZ 中的复杂对象:pgfkeys 范围和最佳实践

TikZ 中的复杂对象:pgfkeys 范围和最佳实践

我尝试在 TikZ 中定义一个可重用的对象,但遇到了一些麻烦。以下最小工作示例显示了该问题。

\documentclass{article}

\usepackage{tikz}
%\usepackage{trace-pgfkeys}
\usetikzlibrary{calc}

\makeatletter

\pgfkeys{
  /object/.cd,
  angle/.initial=0,
}

\def\object[#1](#2,#3)#4;{
%  \pgfkeys{/object/.cd,#1,angle/.get=\@angle}
  \pgfkeys{/object/.cd,#1}
  \node (center) at (#2,#3) {#4};
  \coordinate (#4 center) at (center);
%  \draw [rotate around={\@angle:(center)}] ($(center)+(-1,-.5)$) coordinate (#4 a) rectangle ++(2,1);
  \draw [rotate around={\pgfkeysvalueof{/object/angle}:(center)}] ($(center)+(-1,-.5)$) coordinate (#4 a) rectangle ++(2,1);
%  \def\@angle{0}
}

\makeatother

\begin{document}
\begin{tikzpicture}
  \object[](0,0){A};
  \object[angle=30](3,0){B};
  \object[](6,0){C};

  \draw (A a) -- (B center);
\end{tikzpicture}
\end{document}

mwe 的结果如下图所示。

三个物体

尽管没有给出角度,但对象 C 也旋转了。这说明我误解了 pgfkeys 的工作原理,应该加以利用。如果未给出选项角度,则希望使用默认值。如何才能正确完成?我该如何摆脱中间变量\@angle

附加问题:手册指出,“。” 不得用于坐标名称,而且它也不起作用。但是 TikZ 库定义的“对象”坐标大量使用“xy”坐标名称。上面的示例中是否有方法可以做到这一点,因为它会反映 TikZ 本身是如何做到的?

有关的:这个问题

编辑:我替换了代码中的几行,因此\@angle不再需要辅助宏,请参阅 Qrrbrbirlbel 的回答。

答案1

笔记

  • 我已经删除了该库并直接在节点上calc使用键xshift和。yshiftcenter
  • ;您的定义中的实际上\object是不需要的,因为无论如何您都会对最后一个参数进行分组。使用您的定义,即使\object[](,)text;是 也是可能的。如果没有 ,;您必须写\object[](,){text}(尽管额外的;也不会有什么坏处)。

    但是,我没有改变你的论点定义。

  • 我提供了您的宏的改进版本:

    • 我已将您的\def初始化替换为\newcommand允许第一个参数为可选的初始化。
    • 在另一个节点的帮助下,创建矩形的算法被重写。

\pgfkeys宏中的 全局设置键/object/angle。除非您进行本地更改(通过将宏包含在一组括号中{ })或备份其值,否则它将不会被更改。

线路

  \def\@angle{0}

只会改变\@angle宏,而不会改变键!

还有其他方法可以“获取” PGF 密钥的一个值。其中一种方法是使用\pgfkeysgetvalue\pgfkeysvalueof

  1. \pgfkeysgetvalue{<key>}{<macro>}将的值保存<key><macro>

    \pgfkeys{/object/.cd,#1}
    \pgfkeysgetvalue{/object/angle}\@angle
    
  2. \pgfkeysvalueof{<key>}扩展为的当前值<key>

    \pgfkeys{/object/.cd,#1} % and three lines later:
    \draw [rotate around={\pgfkeysvalueof{/object/angle}:(center)}] ([xshift=-1cm,yshift=-.5cm]center) coordinate (#4 a) rectangle ++(2,1);
    

您也可以将该值直接保存到宏中:

  1. 处理程序/.store in/.estore in(保存前展开其参数):

    \pgfkeys{
      /object/.cd,
      angle/.store in=\@angle,
      angle=0,
    }
    

    然后\@angle就可以使用而不需要额外“获取”宏里面的值。

  2. 处理程序.store in只是一种快捷方式,内部\def\@angle{#1}使用。事实上,我们.code甚至可以使用它来计算参数。在这种情况下,这没有多大意义,因为我们只使用rotate或的角度rotate around。这些键已经可用于计算(例如rotate=360-25+2*10)。

    \pgfkeys{
      /object/.cd,
      angle/.code=\pgfmathsetmacro\@angle{#1},
      angle=0,
    }
    

代码

\documentclass[tikz]{standalone}
\makeatletter
\pgfkeys{
  /object/.cd,
  angle/.initial=0,
}

\def\object[#1](#2,#3)#4;{{
  \pgfkeys{/object/.cd,#1,angle/.get=\@angle}
  \node (center) at (#2,#3) {#4};
  \coordinate (#4 center) at (center);
  \draw [rotate around={\@angle:(center)}] ([xshift=-1cm,yshift=-.5cm]center) coordinate (#4 a) rectangle ++(2,1);
}}
\makeatother

\begin{document}
\begin{tikzpicture}
  \object[](0,0){A};
  \object[angle=30](3,0){B};
  \object[](6,0){C};

  \draw (A a) -- (B center);
\end{tikzpicture}
\end{document}

输出

在此处输入图片描述

改进 (?)

节点已经理解旋转的概念,但不仅形状旋转,内容也旋转。

我的解决方案提供了两个将要创建的节点:

  • 未旋转的带有文本(在下面的示例中为浅灰色)x-<name>和名称的图像:;
  • 隐藏文本的旋转矩形,名称:<name>

其中<name>是键提供的名称,如果为空,则使用/object/name节点的内容( )。#4

旋转的节点包含文本,\phantom因此如果文本大于最小尺寸,其大小将调整为与未旋转的节点相同。

如果您只希望一个\object是矩形,请添加shape=rectangle\node命令中。否则,可能会shape使用为命令范围指定的规范。

代码

\documentclass[tikz]{standalone}
\makeatletter
\pgfkeys{
  /object/.cd,
  angle/.initial=0,
  name/.initial={},
}

\newcommand*{\object}[1][]{\@object[#1]}
\def\@object[#1](#2,#3)#4;{{
  \pgfkeys{/object/.cd,#1}
  \pgfkeysgetvalue{/object/name}\@name% saves /object/name in \@name
  \ifx\@name\pgfutil@empty\edef\@name{#4}\fi% if no name is given use #4
  \node[
    draw=lightgray,% debug
             minimum width=2cm, minimum height=1cm, name/.expand once=x-\@name] at (#2,#3) {#4};
  \node[draw,minimum width=2cm, minimum height=1cm, name/.expand once=\@name, rotate=\pgfkeysvalueof{/object/angle}] at (#2,#3) {\phantom{#4}};
}}
\makeatother

\begin{document}
\begin{tikzpicture}
  \object(0,0){A};
  \object[angle=30](3,0){B};
  \object[name=C has another name](6,0){C};

  \draw (A.south west) -- (B.center);
  \draw[red] (B.north east) -- (x-B.north west) -- (C has another name.mid west);

  \draw[blue,bend right,->] (A) edge (B)
                            (B) edge (C has another name);
\end{tikzpicture}
\end{document}

输出

在此处输入图片描述

答案2

trapezium以下是使用形状和键的完全不同的方法shape border rotate

在此处输入图片描述

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}

\tikzset{
  rotated rectangle/.style={
    shape=trapezium,trapezium angle=90,
    shape border uses incircle,shape border rotate=#1,
    minimum width=2cm,minimum height=1cm,trapezium stretches=true,
  },
}

\begin{document}
\begin{tikzpicture}
  \node[draw,rotated rectangle=0] (A) at (0,0) {A};
  \node[draw,rotated rectangle=30] (B) at (3,0) {B};
  \node[draw,rotated rectangle=0] (C) at (6,0) {C};

  \draw (A.bottom left corner) -- (B.center);
  \draw[red] (B.top right corner) --  (C.mid west);
\end{tikzpicture}
\end{document}

相关内容