我想定义一个带有许多锚点的新节点(或者可能是新形状)。具体来说,我需要一个矩形,矩形的顶部和底部最多有 15 个锚点。这是因为我想轻松地绘制如下内容:
红点只是可能的锚点位置的示例,它们不应该出现在最终图片中。如果可能的话,所有边缘都应该垂直于节点(但我想这必须作为边缘中的一个选项添加)。
\node[mynode,minimum size=5mm] (A1) at (0,0) {Some Text};
\node[mynode,minimum size=5mm,rotate=-45] (A2) at (-1,-1) {};
\draw (A1.t4) -- (A2.b1);
%OR
%\path (A1.t4) edge [out=90,in=90] (A2.b1);
其中 t4 和 b1 应该代表锚点(如果我理解语法正确的话)。
这是因为我想画出类似http://elishapeterson.wikidot.com/tikz:diagrams
我已经发现了一些类似的东西,但我无法修改它以使其适合我,例如: https://tex.stackexchange.com/a/75515/58947 https://tex.stackexchange.com/a/33156/58947
我认为新的矩形节点可以继承标准矩形的所有结构,但添加 15 个锚点,例如顶部从 t1 到 t15,底部从 b1 到 b15。但欢迎任何建议!!梦想是边缘自动连接到更近的锚点。或者,更好的是,两个锚点之间的距离根据连接到节点的边数而变化(即,如果只有 6 条边连接到节点的顶部,它们将相隔 l/7,其中 l 是矩形长边的长度)
答案1
下面的示例定义了一个新的形状rectangle16
。它从形状继承了锚点rectangle
,并将锚点t0
(= north west
) 添加到t16
(= north east
),并将b0
(= south west
) 添加到b16
(= south east
)。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{topaths}
\usetikzlibrary{calc}
\usetikzlibrary{hobby}
\makeatletter
\newcommand*{\define@anchor@t}[2]{%
\anchor{t#1}{%
\pgf@process{\southwest}%
\pgf@xa=\pgf@x
\pgf@process{\northeast}%
\pgf@x=\dimexpr\pgf@xa + (\pgf@x-\pgf@xa)*#1/#2\relax
}%
}
\newcommand*{\define@anchor@b}[2]{%
\anchor{b#1}{%
\pgf@process{\northeast}%
\pgf@xa=\pgf@x
\pgf@process{\southwest}%
\pgf@x=\dimexpr\pgf@x + (\pgf@xa-\pgf@x)*#1/#2\relax
}%
}
\pgfdeclareshape{rectangle16}{%
\inheritsavedanchors[from=rectangle]
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\inheritanchor[from=rectangle]{mid}
\inheritanchor[from=rectangle]{mid west}
\inheritanchor[from=rectangle]{mid east}
\inheritanchor[from=rectangle]{base}
\inheritanchor[from=rectangle]{base west}
\inheritanchor[from=rectangle]{base east}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south east}
\inheritbackgroundpath[from=rectangle]
\count@=0 %
\@whilenum\count@<17 \do{%
\expandafter\define@anchor@t\expandafter{\the\count@}{16}%
\expandafter\define@anchor@b\expandafter{\the\count@}{16}%
\advance\count@\@ne
}%
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[draw,rectangle16] (N)
{Rectangular node with additional anchors at the top and bottom};
\foreach \i in {0, ..., 16} {
\draw[<-, node font=\footnotesize]
(N.t\i) -- ++(0, .5) node[above] {t\i}
;
\draw[<-, node font=\footnotesize]
(N.b\i) -- ++(0, -.5) node[below] {b\i}
;
}
\node[draw, rectangle16] (ST) at (N.center |- 0, -3) {Some Text};
\draw
\foreach \i in {1, ..., 14} { (ST.t\i) -- ++(0, .5) }
(ST.south east) ++(-2, -1)
node[
rectangle16,
draw,
rotate=-45,
minimum width=15mm,
minimum height=5mm,
] (R) {}
(ST.b1) to (R.t4)
;
\draw[use Hobby shortcut]
(ST.t15)
-- ([out angle=90]$(ST.t15) + (0, .1)$)
.. ($(ST.east) + (.5, 0)$)
.. ([in angle=-90]$(ST.b15) + (0, -.1)$)
-- (ST.b15)
;
\fill[
red,
radius=.5pt,
]
\foreach \i in {0, ..., 16} {
\foreach \tb in {t, b} {
(R.\tb\i) circle[]
}
}
;
\end{tikzpicture}
\end{document}
概括
下面的示例\declareshaperectxy{<h>}{<v>}
用两个参数定义了矩形四条边的水平和垂直锚点数,并用名称 定义了形状
rectangle <h>x<v>
。例如,可以使用\declareshaperectxy{16}{8}
形状之后,它为顶部、底部、左侧和右侧rectangle 16x8
提供了附加锚点。t0
t16
b0
b16
l0
l8
r0
r8
\documentclass{article}
\usepackage{tikz}
\makeatletter
\newcommand*{\rectxy@anchor@top}[2]{%
\anchor{t#1}{%
\pgf@process{\southwest}%
\pgf@xa=\pgf@x
\pgf@process{\northeast}%
\pgf@x=\dimexpr\pgf@xa + (\pgf@x-\pgf@xa)*#1/#2\relax
}%
}
\newcommand*{\rectxy@anchor@bottom}[2]{%
\anchor{b#1}{%
\pgf@process{\northeast}%
\pgf@xa=\pgf@x
\pgf@process{\southwest}%
\pgf@x=\dimexpr\pgf@x + (\pgf@xa-\pgf@x)*#1/#2\relax
}%
}
\newcommand*{\rectxy@anchor@left}[2]{%
\anchor{l#1}{%
\pgf@process{\northeast}%
\pgf@ya=\pgf@y
\pgf@process{\southwest}%
\pgf@y=\dimexpr\pgf@y + (\pgf@ya-\pgf@y)*#1/#2\relax
}%
}
\newcommand*{\rectxy@anchor@right}[2]{%
\anchor{r#1}{%
\pgf@process{\southwest}%
\pgf@ya=\pgf@y
\pgf@process{\northeast}%
\pgf@y=\dimexpr\pgf@ya + (\pgf@y-\pgf@ya)*#1/#2\relax
}%
}
\newcommand*{\declareshaperectxy}[2]{%
\pgfdeclareshape{rectangle #1x#2}{%
\inheritsavedanchors[from=rectangle]
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\inheritanchor[from=rectangle]{mid}
\inheritanchor[from=rectangle]{mid west}
\inheritanchor[from=rectangle]{mid east}
\inheritanchor[from=rectangle]{base}
\inheritanchor[from=rectangle]{base west}
\inheritanchor[from=rectangle]{base east}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south east}
\inheritbackgroundpath[from=rectangle]
\count@=\m@ne
\@whilenum\count@<#1 \do{%
\advance\count@\@ne
\expandafter\rectxy@anchor@top\expandafter{\the\count@}{#1}%
\expandafter\rectxy@anchor@bottom\expandafter{\the\count@}{#1}%
}%
\count@=\m@ne
\@whilenum\count@<#2 \do{%
\advance\count@\@ne
\expandafter\rectxy@anchor@left\expandafter{\the\count@}{#2}%
\expandafter\rectxy@anchor@right\expandafter{\the\count@}{#2}%
}%
}%
}
\makeatother
\declareshaperectxy{16}{8}
\declareshaperectxy{5}{3}
\begin{document}
\begin{tikzpicture}
\node[
draw,
rectangle 16x8,
minimum width=80mm,
minimum height=30mm,
] (N) {};
\foreach \i in {0, ..., 16} {
\draw[<-, node font=\footnotesize]
(N.t\i) -- ++(0, .5) node[above] {t\i}
;
\draw[<-, node font=\footnotesize]
(N.b\i) -- ++(0, -.5) node[below] {b\i}
;
}
\foreach \i in {0, ..., 8} {
\draw[<-, node font=\footnotesize]
(N.l\i) -- ++(-.5, 0) node[left] {l\i}
;
\draw[<-, node font=\footnotesize]
(N.r\i) -- ++(.5, 0) node[right] {r\i}
;
}
\node[
draw,
rectangle 5x3,
minimum width=80mm,
minimum height=30mm,
at={(0, -55mm)},
] (N) {};
\foreach \i in {0, ..., 5} {
\draw[<-, node font=\footnotesize]
(N.t\i) -- ++(0, .5) node[above] {t\i}
;
\draw[<-, node font=\footnotesize]
(N.b\i) -- ++(0, -.5) node[below] {b\i}
;
}
\foreach \i in {0, ..., 3} {
\draw[<-, node font=\footnotesize]
(N.l\i) -- ++(-.5, 0) node[left] {l\i}
;
\draw[<-, node font=\footnotesize]
(N.r\i) -- ++(.5, 0) node[right] {r\i}
;
}
\end{tikzpicture}
\end{document}
答案2
您可以使用标准锚点来定义新点calc
。
例如我们的节点名为a
,因此我们可以定义:
($(a.north west)!.1!(a.north east)$)
这将计算左上角和右上角之间的一个点,该点的坐标用数字表示,从 0 到 1。当然,您可以通过书写来增加这个数字!.47!
,这样每边实际上就可以有 100 个坐标。
我说“几乎”是因为除非路径非常细(不太可能),否则同时使用所有 100 条路径是不可行的。不过它们应该足够你使用了。这里有一个动画展示它们(你只能看到 98 条,因为第一条和最后一条出现在节点边界之外,我将它们排除在外):
输出
代码
\documentclass[tikz,margin=10pt]{standalone}
\usetikzlibrary{calc, positioning}
\begin{document}
\begin{tikzpicture}
\node[draw] (a) {Some text};
\draw ($(a.north west)!.1!(a.north east)$) --++ (0,1);
\draw ($(a.north west)!.2!(a.north east)$) --++ (0,1);
\draw ($(a.north west)!.3!(a.north east)$) --++ (0,1);
\draw ($(a.north west)!.4!(a.north east)$) --++ (0,1);
\draw ($(a.north west)!.42!(a.north east)$) --++ (0,1);
\draw (a.north) --++ (0,1);
\draw ($(a.north west)!.6!(a.north east)$) --++ (0,1);
\draw ($(a.north west)!.7!(a.north east)$) --++ (0,1);
\draw ($(a.north west)!.8!(a.north east)$) --++ (0,1);
\draw[rounded corners=6pt] ($(a.north west)!.9!(a.north east)$) --++ (0,.2) --++ (.5,0)
--++ (0,-.9) --++ (-.5,0) -- ($(a.south west)!.9!(a.south east)$);
\node[draw,below left=2 and .5 of a, rotate=-35] (b) {Other text};
\draw ($(b.north west)!.3!(b.north east)$) edge[bend right] ($(a.south west)!.8!(a.south east)$);
\end{tikzpicture}
\end{document}
答案3
这里有一个通用的使用通用锚点的解决方案。
这样做的好处是它适用于每种形状,而无需定义新的形状。
我们定义一个宏(以及将参数转发给宏的\pgfDeclareGenericAnchorsLinear
键),它接受五个参数:define linear anchors
- 锚(我们称之为A),
- 另一个锚点(我们称之为乙),
- 一个整数(应该已经被评估),
- 新锚点的前缀,以及
- PGFmath 函数名称。
PGFmath 函数必须有两个且只能有两个参数(参见示例)。
如果 PGFmath 函数求值为0
,则锚点A将被选中。如果评估结果为 1,则锚点乙calc
将被选中,其工作原理与
($(<node>.A)!<value>!(<node>.B)$)
此解决方案最适合具有相同数量附加锚点的节点(例如,顶部六个,底部五个,左侧两个)。
如果要逐个节点定义多个附加锚点,则必须进行额外的工作(即将该数字与节点本身一起存储)。
代码
\documentclass[tikz]{standalone}
\makeatletter
\newcommand*\pgfDeclareGenericAnchorsLinear[5]{%
% #1 = anchor 1
% #2 = anchor 2
% #3 = max number (1 ... #3)
% #4 = name
% #5 = function name
\pgfmathloop
\csname pgfmath#5@\endcsname{\pgfmathcounter}{#3}%
\edef\pgf@temp{%
\noexpand\pgfdeclaregenericanchor{#4\space \pgfmathcounter}{%
\noexpand\pgfpointlineattime
{+\pgfmathresult}%
{\noexpand\pgf@sh@reanchor{########1}{#1}}%
{\noexpand\pgf@sh@reanchor{########1}{#2}}%
}}%
\pgf@temp
\ifnum\pgfmathcounter<#3\relax
\repeatpgfmathloop}
\makeatother
\pgfset{
define linear anchors/.code n args=5{\pgfDeclareGenericAnchorsLinear{#1}{#2}{#3}{#4}{#5}},
declare function={
funcNormalPadding(\i,\n)=\i/(\n+1);
funcHalfPadding(\i,\n)=(\i-.5)/(\n);
}}
\tikzset{
define linear anchors={north west}{north east}{6}{top} {funcNormalPadding},
define linear anchors={south west}{south east}{6}{bottom half}{funcHalfPadding},
define linear anchors={south west}{south east}{6}{bottom} {funcNormalPadding},
}
\begin{document}
\begin{tikzpicture}[mynode/.style={draw}]
\node[mynode,minimum size=5mm] (A1) at (0,0) {Some Text};
\node[mynode,minimum size=5mm,rotate=-45] (A2) at (-1,-1) {Teeext};
\foreach \v in {1,...,6}
\draw (A1.top \v) -- ++ (up:1) (A2.bottom half \v) -- ++ (45:-.25);
\draw (A1.bottom 1) to[out=-90, in=45, looseness=.5] (A2.top 4);
\end{tikzpicture}
\end{document}