固定 TiKZ 图片

固定 TiKZ 图片

是否可以将 锚定TiKZ-pic在内部锚点上?

我或多或少知道如何使用TiKZ pics,也知道它们是围绕原点绘制的。我想确定的是,不可能使用另一个内部坐标作为原点。

作为示例,考虑下面的代码。

\tikzset{
    mytest/.pic = {
         \node  (-A) at (0,0) {A};
         \node  (-B) at (0,1) {B};
             \node[fit=(-A) (-B),draw] (-C) {};
    }
}

它定义了一个包含三个节点的 pic:ABCA.center原点pic。命令如下

\draw pic (T) at (2,1) {mytest};

A.center用绘制图片(2,1)

现在,我想绘制这个pic,但选择B.center(或任何其他内部锚点,如C.120)作为锚点(图片原点)。可以吗?

如果你需要的话,这里有一些完整的代码:

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{fit}

\tikzset{
    mytest/.pic = {
         \node  (-A) at (0,0) {A};
         \node  (-B) at (0,1) {B};
             \node[fit=(-A) (-B),draw] (-C) {};
    }
}

\begin{document}
\begin{tikzpicture}

\draw[blue!20] (0,0) grid (3,3);

\draw pic (S) at (0,0) {mytest};

\draw pic (T) at (2,1) {mytest};

\draw (S-A.north) to [out=90,in=-90] (T-B.south);

\end{tikzpicture}
\end{document}

问题更新:

  • 我想要一个通用的解决方案。对于这个特殊情况,我知道节点B.center1cm上方A.center,并且很容易移动整个画面,但如果原点必须位于C.120或,则移动它会更加困难C.100-|B.35。还请考虑一些pic具有可变大小节点的情况。
  • 这个例子只是一个例子。我并不是想解决某个具体问题。这个问题更多的是……概念性的(?)。
  • 答案可能是no, it's not possible。但是,请给出一些解释,就像我总是问我的学生那样 ;-)

答案1

pic显然,在s中界定相关代码范围的想法是可行的。

以下是一个例子:

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{fit}

\tikzset{pic shift/.store in=\shiftcoord,
    pic shift={(0,0)},
    mytest/.pic = {
             \begin{scope}[shift={\shiftcoord}]
         \node  (-A) at (0,0) {A};
         \node  (-B) at (0,1) {B};
             \node[fit=(-A) (-B),draw] (-C) {};
         \end{scope}
    }
}

\begin{document}
\begin{tikzpicture}

\draw[blue!20] (0,0) grid (3,3);

\draw pic (S) at (0,0) {mytest};

\draw pic (T) at (2,1) {mytest};

\draw (S-A.north) to [out=90,in=-90] (T-B.south);

\end{tikzpicture}
\begin{tikzpicture}

\draw[blue!20] (0,0) grid (3,3);

\draw[pic shift={(1,1)}] pic (S) at (0,0) {mytest};

\draw pic (T) at (2,1) {mytest};

\draw (S-A.north) to [out=90,in=-90] (T-B.south);

\end{tikzpicture}
\end{document}

未经移位的结果:

在此处输入图片描述

移动第一张图片时(S):

在此处输入图片描述

编辑

试图回答最后 Ignasi 的评论:我还可以使用您的代码将任何内部图片坐标放置在所需的位置吗?

答案很可能是:是的,在某种程度上。这意味着在 tikzpicture 中,在引用图片时,您实际上正在考虑其原点,即放置在坐标 中的任何内容(0,0)。在当前情况下,节点A。因此,您可以根据此点移动整个图片,但我怀疑您不能将任何内部图片坐标放置在所需位置。您可以将任何图片坐标放置在所需位置,方法是将图片相对于其原点进行移动。

让我们考虑以下例子:

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{fit,positioning}

% code by Andrew:
% https://tex.stackexchange.com/a/33765/13304
\makeatletter
\newcommand{\gettikzxy}[3]{%
  \tikz@scan@one@point\pgfutil@firstofone#1\relax
  \edef#2{\the\pgf@x}%
  \edef#3{\the\pgf@y}%
}
\makeatother

\tikzset{pic shift/.store in=\shiftcoord,
    pic shift={(0,0)},
    pic-a min height/.store in=\picaminheight,
    pic-a min height=0pt,
    pic-a min width/.store in=\picaminwidth,
    pic-a min width=0pt,
    mytest/.pic = {
         \begin{scope}[shift={\shiftcoord}]
         \node[minimum height=\picaminheight,
               minimum width=\picaminwidth,
               draw]  (-A) at (0,0) {A};
         \node  (-B) at (0,1) {B};
             \node[fit=(-A) (-B),draw] (-C) {};
         \end{scope}
    }
}

\begin{document}

\begin{tikzpicture}

\draw[blue!20] (0,0) grid (3,3);

\draw[pic-a min height=1cm,
 pic-a min width=1cm,
 pic shift={(1,1)}] pic (S) at (0,0) {mytest} node[coordinate] (sn){};

\gettikzxy{(S-C.east)}{\sbx}{\sby}

\draw[red,pic shift={(\sbx,\sby)}] pic (T) at (0,0) {mytest};

\gettikzxy{(S-C.south)}{\sdx}{\sdy}
\draw[blue,pic shift={(\sdx,\sdy)}] pic (T) at (0,0) {mytest};

\gettikzxy{(S-C.east)}{\sex}{\sey}
\draw[pic-a min width=0cm,green!75!black,pic shift={(\sex,\sey)}] pic (O) at (1,0) {mytest};

\end{tikzpicture}
\end{document}

这导致:

在此处输入图片描述

回想一下 的原点mytest是节点-A,我们有第一张图 (S),其尺寸增大,用黑色表示。它将是我们的参考。

感谢 Andrew 的\gettikzxy,我们可以获取 (SC.east) 的坐标。第二张mytest图片 (T),红色,放置在(0,0)(一般 tikzpicture 的) 位置,我们对其进行移位以进入 (SC.east) 坐标。正如预期的那样,(TA) 被放入(S-C.east)。图片也是如此M。但是,显然不可能根据其 C 节点锚定图片。您可以做的是利用pic shift和坐标位置来根据其 C 节点锚定图片(我承认,这并不那么优雅)。例如,绿色 (O)mytest图片根据其-C.south east在 (1,3) 中的点锚定。

答案2

这是一个tikzmark解决方案。 的要点tikzmark是它将页面上某个位置的位置写入文件,aux以便可以在下一次编译时读入。因此,为了找出 的位置pic,我们写出所需锚点的位置,然后在下一次编译时使用该位置来移动pic

当然,这会移动图片,从而移动锚点的位置。但实际上我们想要的是锚点相对于图片原点的相对位移,而这不会改变,所以我们同时写入原点和锚点。

这意味着 Shift 代码必须包含在 pic 代码中。如果您可以完全控制 pic 代码,那么这很容易。如果没有,您可以将现有 pic 包装在带有 Shift 代码的新 pic 中。

现在,我认为这是一件非常有用的事情,所以我把它添加到了最新版本中tikzmark(在撰写本文时 - 当它上传到 CTAN 时我会更新它),可从github(下载tikzmark.dtx并运行tex tikzmark.dtx)。

以下是您的代码示例。红点是图片所在的点。请注意,图片锚点在内部坐标,因此如果有图片名称则不使用它。

\documentclass[tikz,border=2mm]{standalone}
%\url{https://tex.stackexchange.com/q/185279/86}
\usetikzlibrary{fit,tikzmark}

\tikzset{
  mytest/.pic = {
    \begin{scope}[adjust pic position]
    \node  (-A) at (0,0) {A};
    \node  (-B) at (0,1) {B};
    \node[fit=(-A) (-B),draw] (-C) {};
    \end{scope}
  }
}

\begin{document}
\begin{tikzpicture}

\draw[blue!20] (0,0) grid (3,3);

\fill[red] (0,0) circle[radius=3pt];
\fill[red] (2,1) circle[radius=3pt];

\draw[pic anchor=(-B.center)] pic (S) at (0,0) {mytest};

\draw[pic anchor=(-C.120)] pic (T) at (2,1) {mytest};

\draw (S-A.north) to [out=90,in=-90] (T-B.south);

\end{tikzpicture}
\end{document}

重新定位图片

答案3

虽然 Claudio 已经给出了完整的答案,但我认为还有一个更简单的答案,这可能是 Ti 的一个错误或未记录的功能Z(因为手册里没有)。这不是一个完整的答案,因为你不能使用全部的内部坐标,pic而仅仅是位于原点的节点的坐标。

出于好奇,我尝试将一个节点放在原点pic并通过命令访问其锚点pic,猜猜结果如何?成功了。因此,显然只需创建一个pic以节点为边界框的节点,然后您就可以将其放置在其锚点的位置!

\documentclass[tikz,border=2mm]{standalone}

\tikzset{mytest/.pic = {\node[draw, minimum height=1.5cm, minimum width=.5cm] (-box) at (0,0) {};
                   \node[above] (-A) at (-box.south) {A};
                   \node[below] (-B) at (-box.north) {B};}
}

\begin{document}
\begin{tikzpicture}

\draw[blue!20] (0,0) grid (3,3);

\draw pic[anchor=south west] (S) at (0,0) {mytest};

\draw pic[below left=1mm] (T) at (2,2) {mytest};

\draw (S-A.north) to [out=90,in=-90] (T-B.south);

\end{tikzpicture}
\end{document}

在此处输入图片描述

答案4

这扩展了答案克劳迪奥·菲安德里诺它使用了\gettikzxyAndrew 的。

pic我错过了锚定的可能性任何内部坐标,位于其他pic内部坐标。例如,将我的左下角放置pic在另一个菱形的中间pic

新解决方案:

这是使用我的新命令完成的\placePic

\placePic{<new pic name>}
         {<placement coord.>}
         {<pic type>}
         {<anchor, internal coord. extension>}
         {<pic actions>}

它要求:

  • 内部坐标在绘图之前定义
  • 要在范围内定义的内部坐标,该坐标会移动pic

绘图必须在

\ifnum\drawit = 1
    % Drawing of the pic
\fi

就像这样pic

示例图片

placement square/.pic=
{

    \def\sideLen{4mm}
    \def\flapLen{0.7mm}
    \def\fillPercentage{0.8}
    \def\cornerMarkLen{1mm}

    \begin{scope}[thick, shift={\shiftcoord}, xscale=\xscaling, yscale=\yscaling]
        
        % Coordinates must be inside of scope which does the shifting.
        % All coordinates must be defined before drawing
        \coordinate (-center) at (0,0);
        \coordinate (-left) at (-\sideLen/2-\flapLen,0);
        \coordinate (-upper) at (0,\sideLen/2+\flapLen);
        \coordinate (-right) at (\sideLen/2+\flapLen,0);
        \coordinate (-lower) at (0,-\sideLen/2-\flapLen);
        \coordinate (-upper left) at (-\sideLen/2,\sideLen/2);
        \coordinate (-upper right) at (\sideLen/2,\sideLen/2);
        \coordinate (-lower left) at (-\sideLen/2,-\sideLen/2);
        \coordinate (-lower right) at (\sideLen/2,-\sideLen/2);
        
        % The drawing
        \ifnum\drawit = 1
            % Rectangle
            \draw[pic actions] (-\sideLen/2,-\sideLen/2) rectangle +(\sideLen,\sideLen);
            \fill[pic actions, very thin, opacity=0.2] (-\sideLen/2*\fillPercentage,-\sideLen/2*\fillPercentage) 
                rectangle +(\sideLen*\fillPercentage,\sideLen*\fillPercentage);
            % Cross
            \draw[pic actions] (-center.center)
                edge (-left)
                edge (-upper)
                edge (-right)
                edge (-lower);

            \node[opacity=1] at (-center) {a};

            % Orientation mark at corner
            \draw[pic actions, green!80!black, opacity=1] ([yshift=\cornerMarkLen]-lower right.center)
                -- (-lower right.center)
                -- +(-\cornerMarkLen,0);
        \fi

    \end{scope}
}

这里有一个完整示例,展示其好处:

整个例子

\documentclass[tikz,border=2cm]{standalone}
\usepackage{tikz}
\usepackage{xcolor}
\usetikzlibrary{shapes.geometric}


% code by Andrew:
% https://tex.stackexchange.com/a/33765/13304
\makeatletter
\newcommand{\gettikzxy}[3]{%
  \tikz@scan@one@point\pgfutil@firstofone#1\relax
  \edef#2{\the\pgf@x}%
  \edef#3{\the\pgf@y}%
}
\makeatother

% #1 = name of new placement
% #2 = where to place (e.g. myA-star)
% #3 = pic type
% #4 = internal node to use as anchor (eg. -left point)
% #5 = pic actions (other settings than color e.g. xshift, opacity, ...)
\newcommand{\placePic}[5]
{
    \path pic[draw it=0] (#1-PATH) at (0,0) {#3};
    \gettikzxy{(#1-PATH#4)}{\sdx}{\sdy}
    \gettikzxy{#2}{\sbx}{\sby}
    \def\newX{\sbx - \sdx}
    \def\newY{\sby - \sdy}
    \path [draw it=1, pic shift={(\newX,\newY)}] pic[#5] (#1) at (0,0) {#3};
}

\tikzset
{
    % Relative positioning variables
    pic shift/.store in=\shiftcoord,
    pic shift={(0,0)},
    draw it/.store in=\drawit,
    draw it=1,
    xscaling/.store in=\xscaling,
    xscaling=1,
    yscaling/.store in=\yscaling,
    yscaling=1,
    %
    base rectangle/.pic=
    {
        \begin{scope}[shift={\shiftcoord}]
            \draw[very thick,pic actions] (0,0) rectangle (2,2);
            \node[draw,circle,minimum size=2mm,inner sep=0,thick,pic actions] (-circle) at (.5,.5) {};
            \node[draw,rectangle,minimum size=2mm,inner sep=0,thick,pic actions] (-square) at (.5,1.5) {};
            \node[draw,diamond,minimum size=2.5mm,inner sep=0,thick,pic actions] (-diamond) at (1.5,1.5) {};
            \node[draw,star,minimum size=2.5mm,inner sep=0,thick,pic actions] (-star) at (1.5,.5) {};
        \end{scope}
    },
    placement square/.pic=
    {

        \def\sideLen{4mm}
        \def\flapLen{0.7mm}
        \def\fillPercentage{0.8}
        \def\cornerMarkLen{1mm}

        \begin{scope}[thick, shift={\shiftcoord}, xscale=\xscaling, yscale=\yscaling]
            
            % Coordinates must be inside of scope which does the shifting. That is "shift={\shiftcoord}"
            % All coordinates must be defined before drawing
            \coordinate (-center) at (0,0);
            \coordinate (-left) at (-\sideLen/2-\flapLen,0);
            \coordinate (-upper) at (0,\sideLen/2+\flapLen);
            \coordinate (-right) at (\sideLen/2+\flapLen,0);
            \coordinate (-lower) at (0,-\sideLen/2-\flapLen);
            \coordinate (-upper left) at (-\sideLen/2,\sideLen/2);
            \coordinate (-upper right) at (\sideLen/2,\sideLen/2);
            \coordinate (-lower left) at (-\sideLen/2,-\sideLen/2);
            \coordinate (-lower right) at (\sideLen/2,-\sideLen/2);
            
            % The drawing
            \ifnum\drawit = 1
                % Rectangle
                \draw[pic actions] (-\sideLen/2,-\sideLen/2) rectangle +(\sideLen,\sideLen);
                \fill[pic actions, very thin, opacity=0.2] (-\sideLen/2*\fillPercentage,-\sideLen/2*\fillPercentage) 
                    rectangle +(\sideLen*\fillPercentage,\sideLen*\fillPercentage);
                % Cross
                \draw[pic actions] (-center.center)
                    edge (-left)
                    edge (-upper)
                    edge (-right)
                    edge (-lower);

                \node[opacity=1] at (-center) {a};

                % Orientation mark at corner
                \draw[pic actions, green!80!black, opacity=1] ([yshift=\cornerMarkLen]-lower right.center)
                    -- (-lower right.center)
                    -- +(-\cornerMarkLen,0);
            \fi

        \end{scope}
    }
}


\begin{document}
\begin{tikzpicture}
    % Help lines (grid)
    \draw[help lines,ultra thin,step=5mm] (-.5,-.5) grid (2.5,2.5);
    \draw[help lines,thin,step=10mm] (-.5,-.5) grid (2.5,2.5);

    \pic[black] (base) at (0,0) {base rectangle};

    % #1 = name of new placement
    % #2 = where to place (e.g. myA-star)
    % #3 = pic type
    % #4 = internal node to use as anchor (eg. -left point)
    % #5 = pic actions (other settings than color e.g. xshift, opacity, ...)
    \placePic{red square}
             {(base-circle)}{placement square}{-lower}
             {red, opacity=0.5}
    \placePic{cyan square}
             {(base-star)}{placement square}{-right}
             {cyan, opacity=0.5}
    \placePic{blue square}
             {(base-diamond)}{placement square}{-lower left}
             {blue, opacity=0.5,yscaling=-1}
    \placePic{orange square}
             {(base-square)}{placement square}{-left}
             {orange, opacity=0.5, xshift=2mm, xscaling=-1}

\end{tikzpicture}
\end{document}

原解决方案:

注意:我意识到这会带来一些缺陷,因为其他命令如 \fill,,edge\node 将需要额外的命令来限制 pic 可以画画。请参阅上面更新后的答案。

这是使用我的新命令完成的\placePic

\placePic{<new pic name>}
         {<placement coord.>}
         {<pic type>}
         {<anchor, internal coord. extension>}
         {<pic actions>}

要求 到pic位置不自动绘制,它应该需要选项draw。也就是说,pic不应使用 eg 创建\draw,而应使用\path不带draw选项的选项。参见例如:

placement square/.pic=
{
    \begin{scope}[shift={\shiftcoord},thick]
        \def\sideLen{4mm}

        \path[pic actions] (-\sideLen/2,-\sideLen/2) rectangle +(\sideLen,\sideLen);
        \path[pic actions] (-\sideLen/2,0) -- +(-0.7mm,0) coordinate (-left point);
        \path[pic actions] (\sideLen/2,0) -- +(0.7mm,0) coordinate (-right point);
        \path[pic actions] (0,\sideLen/2) -- +(0,0.7mm) coordinate (-upper point);
        \path[pic actions] (0,-\sideLen/2) -- +(0,-0.7mm) coordinate (-lower point);

        \coordinate (-upper left) at (-\sideLen/2,\sideLen/2);
        \coordinate (-upper right) at (\sideLen/2,\sideLen/2);
        \coordinate (-lower left) at (-\sideLen/2,-\sideLen/2);
        \coordinate (-lower right) at (\sideLen/2,-\sideLen/2);
    \end{scope}
}

这样做的代价是创建两次路径。当然,有更有效的方法可以做到这一点,但我搜索了很多次,却没有成功。

在此处输入图片描述

\documentclass[tikz,border=2cm]{standalone}
\usetikzlibrary{shapes.geometric}

% code by Andrew:
% https://tex.stackexchange.com/a/33765/13304
\makeatletter
\newcommand{\gettikzxy}[3]{%
  \tikz@scan@one@point\pgfutil@firstofone#1\relax
  \edef#2{\the\pgf@x}%
  \edef#3{\the\pgf@y}%
}
\makeatother

% #1 = name of new placement
% #2 = where to place (e.g. myA-star)
% #3 = pic type
% #4 = internal node to anchor (eg. -left point)
% #5 = pic actions
\newcommand{\placePic}[5]
{
    \path pic (#1-PATH) at (0,0) {#3};
    \gettikzxy{(#1-PATH#4)}{\sdx}{\sdy}
    \gettikzxy{#2}{\sbx}{\sby}
    \def\newX{\sbx - \sdx}
    \def\newY{\sby - \sdy}
    \path [pic shift={(\newX,\newY)}] pic[draw,#5] (#1) at (0,0) {#3};
}

\tikzset
{
    pic shift/.store in=\shiftcoord,
    pic shift={(0,0)},
    base rectangle/.pic=
    {
        \begin{scope}[shift={\shiftcoord}]
            \draw[very thick,pic actions] (0,0) rectangle (2,2);
            \node[draw,circle,minimum size=2mm,inner sep=0,thick,pic actions] (-circle) at (.5,.5) {};
            \node[draw,rectangle,minimum size=2mm,inner sep=0,thick,pic actions] (-square) at (.5,1.5) {};
            \node[draw,diamond,minimum size=2.5mm,inner sep=0,thick,pic actions] (-diamond) at (1.5,1.5) {};
            \node[draw,star,minimum size=2.5mm,inner sep=0,thick,pic actions] (-star) at (1.5,.5) {};
        \end{scope}
    },
    placement square/.pic=
    {
        \begin{scope}[shift={\shiftcoord},thick]
            \def\sideLen{4mm}
            \path[pic actions] (-\sideLen/2,-\sideLen/2) rectangle +(\sideLen,\sideLen);
            \path[pic actions] (-\sideLen/2,0) -- +(-0.7mm,0) coordinate (-left point);
            \path[pic actions] (\sideLen/2,0) -- +(0.7mm,0) coordinate (-right point);
            \path[pic actions] (0,\sideLen/2) -- +(0,0.7mm) coordinate (-upper point);
            \path[pic actions] (0,-\sideLen/2) -- +(0,-0.7mm) coordinate (-lower point);
            \coordinate (-upper left) at (-\sideLen/2,\sideLen/2);
            \coordinate (-upper right) at (\sideLen/2,\sideLen/2);
            \coordinate (-lower left) at (-\sideLen/2,-\sideLen/2);
            \coordinate (-lower right) at (\sideLen/2,-\sideLen/2);
        \end{scope}
    }
}


\begin{document}
\begin{tikzpicture}
    % Help lines (grid)
    \draw[help lines,ultra thin,step=5mm] (-.5,-.5) grid (2.5,2.5);
    \draw[help lines,thin,step=10mm] (-.5,-.5) grid (2.5,2.5);

    \pic[black] (base) at (0,0) {base rectangle};

    % #1 = name of new placement
    % #2 = where to place (e.g. myA-star)
    % #3 = pic type
    % #4 = internal node to use as anchor (eg. -left point)
    % #5 = pic actions
    \placePic{red square}
             {(base-circle)}{placement square}{-lower point}
             {red, opacity=0.5}
    \placePic{blue square}
             {(base-diamond)}{placement square}{-lower left}
             {blue, opacity=0.5}
    \placePic{green square}
             {(base-star)}{placement square}{-right point}
             {green, opacity=0.5}
    \placePic{orange square}
             {(base-square)}{placement square}{-left point}
             {orange, opacity=0.5, xshift=2mm}

\end{tikzpicture}
\end{document}

相关内容