Tikz - 缩放和定位可重复使用部件的更好方法

Tikz - 缩放和定位可重复使用部件的更好方法

我有一些 Tikz 图形,我将它们用在多个其他图形中。现在我将它们放在一个范围内,并使用 xshift/yshift/scale 将它们放置在我想要的位置。我有一个例子。我需要一个 LaTeX 文档中的图形,其 \linewidth 为 336pt,因此我想以此宽度处理我的图片。我首先创建一个所需宽度和比例的框架。接下来我想在其中放置一些带有一些内容的框。其中一个框包含一个火柴人,我也将其用在其他图形中。我当前的解决方案如下所示:

以下是一个例子:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}[node distance=0pt]

    % Stickman
    \begin{scope}[xshift=38pt, yshift=105pt, anchor=sout west, scale=0.45,local bounding box=person]
        \draw[thin, line cap=round] (0.05,1) -- (-0.05, 1.1); % hair1
        \draw[thin, line cap=round] (-0.05,1) -- (-0.2, 1.12); % hair1
        \draw[thick] (0,0) circle [radius=1]; % head

        \draw[thin] plot [smooth,tension=1.5] coordinates{(-0.7,0.45) (-0.5,0.52) (-0.4,0.52)}; % leyebrow
        \draw[fill=black] (-0.5,0.25) ellipse (0.1 and 0.15);
        \draw[thin] plot [smooth,tension=1.5] coordinates{(0.3,0.40) (0.1,0.50) (-0.1,0.50)}; % leyebrow
        \draw[fill=black] (0.1,0.20) ellipse (0.1 and 0.15);

        \draw[thick] plot [smooth,tension=1.5] coordinates{(-0.75,-0.4) (-0.2,-0.6) (0.3,-0.53)}; % mouth

        \draw[thick] (-0.15,-1) .. controls (-0.4,-2.5) .. (-0.5,-4); % torso

        \draw[thick] (-0.2,-1.3) -- (0.35, -2.55) -- (1.5, -2.2); % rarm
        \draw[thick,rotate around={110:(1.5, -2.2)}] (1.5, -2.4) ellipse (0.1 and 0.2);
        \draw[thick] (-0.2,-1.3) -- (-1.55, -2.55) -- (-1.75, -4.1); % rarm
        \draw[thick,rotate around={-5:(-1.75, -4.1)}] (-1.75, -4.3) ellipse (0.1 and 0.2);

        \draw[thick] (-0.5,-4) -- (0.7, -5.3) -- (1.4,-6.8); % rleg
        \draw[thick,rotate around={25:(1.4,-6.8)}] (1.65,-6.8) ellipse (0.25 and 0.1);
        \draw[thick] (-0.5,-4) -- (-0.95,-5.8) -- (-2.3,-7); % rleg
        \draw[thick,rotate around={-35:(-2.3,-7)}] (-2.05,-7) ellipse (0.25 and 0.1);
    \end{scope}

    \tikzstyle{box}=[draw, very thick, minimum height=120pt] % box style
    % Stickman Box
    \node[box, anchor=south west, minimum width=60pt] (FrameInput) at (5pt, 5pt) {};
    \node[above=of FrameInput] (xy) {Input};

    % Second box
    \node[box, minimum width=120pt, right=of FrameInput, xshift=15pt] (FrameNextStep) {};
    \node[above=of FrameNextStep] (xy) {NextStep};

    % Box Connections
    \path[->, very thick] (FrameInput) edge (FrameNextStep);

    % Picture Frame
    \draw[thick] (0,0) rectangle (336pt, 150pt); % frame
\end{tikzpicture}
\end{document}


得到如下图像: 在此处输入图片描述

我只是想知道是否有更好的方法来解决这个问题。例如,通过指定我的火柴人应该具有等于 FrameInput 框的最大宽度/高度,而无需手动调整范围的比例参数。这同样适用于 x/y 移位,是否有更好/更优雅的方式来定位它?

答案1

  1. 计算环境中原始火柴人的宽度和高度以及左下角锚点pgfinterruptboundingbox
  2. 计算移位偏移和比例值
  3. 画出正确的火柴人,左下角位于(0, 0)

以下是代码:

\documentclass[border=1cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, calc}
\makeatletter
\tikzset{
  stickman height/.store in=\stickman@height,
  stickman height=2cm,
  stickman x sep/.store in=\stickman@x@sep,
  stickman x sep=3pt,
  stickman y sep/.store in=\stickman@y@sep,
  stickman y sep=3pt,
}
\gdef\@stickman{
  \draw[thin, line cap=round] (0.05,1) -- (-0.05, 1.1); % hair1
  \draw[thin, line cap=round] (-0.05,1) -- (-0.2, 1.12); % hair1
  \draw[thick] (0,0) circle [radius=1]; % head

  \draw[thin] plot [smooth,tension=1.5] coordinates{(-0.7,0.45) (-0.5,0.52) (-0.4,0.52)}; % leyebrow
  \draw[fill=black] (-0.5,0.25) ellipse (0.1 and 0.15);
  \draw[thin] plot [smooth,tension=1.5] coordinates{(0.3,0.40) (0.1,0.50) (-0.1,0.50)}; % leyebrow
  \draw[fill=black] (0.1,0.20) ellipse (0.1 and 0.15);

  \draw[thick] plot [smooth,tension=1.5] coordinates{(-0.75,-0.4) (-0.2,-0.6) (0.3,-0.53)}; % mouth

  \draw[thick] (-0.15,-1) .. controls (-0.4,-2.5) .. (-0.5,-4); % torso

  \draw[thick] (-0.2,-1.3) -- (0.35, -2.55) -- (1.5, -2.2); % rarm
  \draw[thick,rotate around={110:(1.5, -2.2)}] (1.5, -2.4) ellipse (0.1 and 0.2);
  \draw[thick] (-0.2,-1.3) -- (-1.55, -2.55) -- (-1.75, -4.1); % rarm
  \draw[thick,rotate around={-5:(-1.75, -4.1)}] (-1.75, -4.3) ellipse (0.1 and 0.2);

  \draw[thick] (-0.5,-4) -- (0.7, -5.3) -- (1.4,-6.8); % rleg
  \draw[thick,rotate around={25:(1.4,-6.8)}] (1.65,-6.8) ellipse (0.25 and 0.1);
  \draw[thick] (-0.5,-4) -- (-0.95,-5.8) -- (-2.3,-7); % rleg
  \draw[thick,rotate around={-35:(-2.3,-7)}] (-2.05,-7) ellipse (0.25 and 0.1);
}
\newlength\pre@y@max
\newlength\pre@y@min
\newlength\pre@other
\tikzset{
  stickman/.pic={
    \begin{pgfinterruptboundingbox}
    \begin{scope}[opacity=0, local bounding box=pre]
      \@stickman
    \end{scope}
    \path (pre.north);
    \pgfgetlastxy{\pre@other}{\pre@y@max}
    \path (pre.south);
    \pgfgetlastxy{\pre@other}{\pre@y@min}
    \pgfmathparse{(\stickman@height - 2*\stickman@y@sep)/(\pre@y@max - \pre@y@min)}
    \global\let\temp@scale\pgfmathresult
    \end{pgfinterruptboundingbox}
    \draw[line width=1pt] let \p1 = (pre.south west), \p2 = (pre.north west), \p3 = (pre.north east) in
      let \p{anchor} = ($(\stickman@x@sep,\stickman@y@sep)-\temp@scale*(pre.south west)$) in
      let \p{other} = ($2*(\stickman@x@sep,\stickman@y@sep)+\temp@scale*(\p3)-\temp@scale*(\p1)$) in
      coordinate (shift) at (\p{anchor})
      coordinate (clip) at (\p{other});
    \begin{scope}[local bounding box=now]
      \draw[line width=1pt] (0, 0) rectangle (clip);
      \begin{scope}[scale=\temp@scale, shift=(shift)]
        \@stickman
      \end{scope}
    \end{scope}
    \coordinate (-north) at (now.north);
    \coordinate (-east) at (now.east);
    \coordinate (-west) at (now.west);
    \coordinate (-south east) at (now.south east);
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}[node distance=0pt]
  \pic [stickman height=3cm] (a) at (0, 0) {stickman};
  \pic [stickman height=3cm, xshift=1cm, stickman x sep=10pt] (b) at (a-south east) {stickman};
  \draw[->, very thick] (a-east) -- (b-west);
  \node[above] at (a-north) {Input};
  \node[above] at (b-north) {NextStep};
\end{tikzpicture}
\end{document}

在此处输入图片描述

相关内容