是否可以将 锚定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:A
,B
和C
为A.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.center
在1cm
上方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
这扩展了答案克劳迪奥·菲安德里诺它使用了\gettikzxy
Andrew 的。
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}