我希望能够检测出是否有特定的标记
\tikzmark
(需要使用pic cs:
)或者如果它是用\tikzmarknode
(这就pic cs:
要求不是用过的)。
下面是一个在两点之间绘制的刻意示例:一个点用 创建\tikzmark
,另一个点用 创建\tikzmarknode
。目前,第一种情况的工作方式是,\DrawPicture
调用第一个坐标pic cs:
,而第二种情况则不使用pic cs:
。
问题:如何更改\DrawPicture
宏以便两个都 \DrawPicture{MarkA}{MarkB}
并且\DrawPicture{MarkB}{MarkA}
(将两个标记反转)可以起作用吗?
我原本以为使用\iftikzmark
可以解决这个问题,但似乎\iftikzmark
只有在第二次运行时才按预期工作,而不是第一次运行(即使绘图代码是在\tikzmark
和之后调用的\tikzmarknode
。即便如此,\tikzmarknode
也会创建一个\tikzmark
,所以这里不太适合在两种情况之间进行检测。
代码:
\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\newcommand*{\DrawPicture}[2]{%
\par#1: \iftikzmark{#1}{tikzmark}{tikzmarknode}%
\par#2: \iftikzmark{#2}{tikzmark}{tikzmarknode}%
%% -------------
\begin{tikzpicture}[overlay,remember picture]
\draw [ultra thick, red, -] (pic cs:#1) to[out=120, in=50, distance=1cm] (#2);
\end{tikzpicture}%
}%
\begin{document}
\[
\tikzmark{MarkA} a + \tikzmarknode{MarkB}{b}
\]
\DrawPicture{MarkA}{MarkB}% <-- This works.
%\DrawPicture{MarkB}{MarkA}% <-- Want this also to be able to work
\end{document}
答案1
这里有很多极端情况,因此最佳解决方案取决于您希望在这些情况下发生什么。要了解极端情况,值得看看 tikzmark 和 tikzmarknode 是什么。(我相信 OP 知道这一点,所以这部分是为了更广泛的受众。)
tikzmark 记录页面上某个点的位置。为此,它必须将一些内容写入辅助文件,该文件将在下一次编译时读入。它必须之所以这样工作,是因为只有在发货后才能知道其位置。
节点记录其相对于当前 tikzpicture 中原点的锚点。如果该图片已remember picture
设置键,则使用与 tikzmark 基本相同的底层机制来记住该原点。具体而言,尽管节点的相对位置可立即获得,但要实际使用其在页面上的位置需要额外的编译。但是,节点信息仅在当前编译中定义节点后才可用(tikzmark 包确实提供了一种解决方法,但我认为这与本讨论无关)。
tikzmarknode 将这两者结合在一起,以便节点的锚点都可以以通常的方式使用,并且原点(定义节点的坐标系)可以作为 tikzmark 使用。
因此,tikzmarknode 和常规节点之间的唯一区别在于如何提供信息。使用其他命令设置 tikzmarknode 提供的所有信息也并不困难。
因此,极端情况如下:
- 定义了一个 tikzmarknode,但测试是在文档中定义它之前运行的。因此,此时唯一可用的信息是 tikzmark 的信息,而不是节点的信息。在这种情况下,测试应该做什么?
- 定义了一个 tikzmark,并且定义了一个同名的不相关节点。在这种情况下测试应该做什么?
- tikzmark 和节点的定义使得它们之间的关系就像通过 tikzmarknode 定义一样,只是没有使用确切的
\tikzmarknode
命令。在这种情况下,测试应该做什么?
基于最简单的通常是最好的,除非它坏了,这是我的第一次尝试。它使用与 Jasper 的第一个答案相同的底层信息:测试save@pt@<name>
和pgf@sh@ns@<name>
。主要区别在于,我没有嵌套测试,而是将它们分开。这样,我可以测试四个选项:
- 均为真:tikzmarknode
save@pt@<name>
真假pgf@sh@ns@<name>
难辨:tikzmarksave@pt@<name>
假但pgf@sh@ns@<name@
真:节点- 均为 false:未定义
在下面的代码中,我将其包装在一个分类函数中,但将其变成一个真正的条件很简单 - 这实际上是我在试水,看看你真正想要什么。
\documentclass{article}
%\url{https://tex.stackexchange.com/q/702241/86}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\makeatletter
\ExplSyntaxOn
\bool_new:N \l__tikzmark_is_a_tikzmark_bool
\bool_new:N \l__tikzmark_is_a_node_bool
\DeclareDocumentCommand \ClassifyNode {m}
{
\bool_set:Nn \l__tikzmark_is_a_tikzmark_bool
{
\tl_if_exist_p:c {save@pt@\tikzmark@pp@name{#1}}
}
\bool_set:Nn \l__tikzmark_is_a_node_bool
{
\tl_if_exist_p:c {pgf@sh@ns@\tikz@pp@name{#1}}
}
Mark~#1~is~
\bool_if:nTF
{
\l__tikzmark_is_a_tikzmark_bool
||
\l__tikzmark_is_a_node_bool
}
{
a~
\bool_if:NT \l__tikzmark_is_a_tikzmark_bool {tikzmark}
\bool_if:NT \l__tikzmark_is_a_node_bool {node}
}
{
not~ (yet)~ defined
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\ClassifyNode{A}\par
\ClassifyNode{B}\par
\ClassifyNode{C}
\tikzmark{A}
\ClassifyNode{A}\par
\ClassifyNode{B}\par
\ClassifyNode{C}
\tikzmarknode{B}{B}
\ClassifyNode{A}\par
\ClassifyNode{B}\par
\ClassifyNode{C}
\tikz {\node (C) {C};}
\ClassifyNode{A}\par
\ClassifyNode{B}\par
\ClassifyNode{C}
\end{document}
首次运行时输出:
Mark A is not (yet) defined
Mark B is not (yet) defined
Mark C is not (yet) defined
Mark A is not (yet) defined
Mark B is not (yet) defined
Mark C is not (yet) defined
B
Mark A is not (yet) defined
Mark B is a node
Mark C is not (yet) defined
C
Mark A is not (yet) defined
Mark B is a node
Mark C is a node
在后续运行中,情况将变为:
Mark A is a tikzmark
Mark B is a tikzmark
Mark C is not (yet) defined
Mark A is a tikzmark
Mark B is a tikzmark
Mark C is not (yet) defined
B
Mark A is a tikzmark
Mark B is a tikzmarknode
Mark C is not (yet) defined
C
Mark A is a tikzmark
Mark B is a tikzmarknode
Mark C is a node
按照使用坐标,这似乎是正确的行为,因为它告诉您每个阶段可以使用什么。因此,B
在定义之前,tikzmarknode 是无关紧要的:您只能在之后使用该事实。
更新:2023 年 7 月 12 日
下面说明了如何使用tikzmark
的功能,以便在定义之前save node
就能对 进行正确的分类。\tikzmarknode
\documentclass{article}
%\url{https://tex.stackexchange.com/q/702241/86}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\makeatletter
\ExplSyntaxOn
\bool_new:N \l__tikzmark_is_a_tikzmark_bool
\bool_new:N \l__tikzmark_is_a_node_bool
\DeclareDocumentCommand \ClassifyNode {m}
{
\bool_set:Nn \l__tikzmark_is_a_tikzmark_bool
{
\tl_if_exist_p:c {save@pt@\tikzmark@pp@name{#1}}
}
\bool_set:Nn \l__tikzmark_is_a_node_bool
{
\tl_if_exist_p:c {pgf@sh@ns@\tikz@pp@name{#1}}
}
Mark~#1~is~
\bool_if:nTF
{
\l__tikzmark_is_a_tikzmark_bool
||
\l__tikzmark_is_a_node_bool
}
{
a~
\bool_if:NT \l__tikzmark_is_a_tikzmark_bool {tikzmark}
\bool_if:NT \l__tikzmark_is_a_node_bool {node}
}
{
not~ (yet)~ defined
}
}
\ExplSyntaxOff
\makeatother
\NewDocumentCommand \ClassifyNodes {}
{
\ClassifyNode{A}\par
\ClassifyNode{B}\par
\ClassifyNode{C}\par
\ClassifyNode{D}
}
\tikzset{
save nodes to file,
restore nodes from file
}
\begin{document}
\ClassifyNodes
\tikzmark{A}
\ClassifyNodes
\tikzmarknode{B}{B}
\ClassifyNodes
\tikzmarknode{C}{C}
\SaveNode{C}
\ClassifyNodes
\tikz {\node (D) {D};}
\ClassifyNodes
\end{document}
稳定为:
Mark A is a tikzmark
Mark B is a tikzmark
Mark C is a tikzmarknode
Mark D is not (yet) defined
Mark A is a tikzmark
Mark B is a tikzmark
Mark C is a tikzmarknode
Mark D is not (yet) defined
B
Mark A is a tikzmark
Mark B is a tikzmarknode
Mark C is a tikzmarknode
Mark D is not (yet) defined
C
Mark A is a tikzmark
Mark B is a tikzmarknode
Mark C is a tikzmarknode
Mark D is not (yet) defined
D
Mark A is a tikzmark
Mark B is a tikzmarknode
Mark C is a tikzmarknode
Mark D is a node
更新:2023-12-10save node
与 结合使用的一个问题\tikzmarknode
是,我的保存节点的代码需要两个步骤:一个步骤是将节点名称添加到列表中,另一个步骤是处理该列表并将节点详细信息存储在某个位置。在 tikzpicture 中使用它时,第一步通常在节点本身上调用,然后第二步发生在图片末尾。这需要(至少)两个键:一个在安装图片结尾代码的 tikzpicture 上,另一个在要保存的每个节点上。
A\tikzmarknode
仅提供对其节点上的选项的立即访问,要获取周围的 tikzpicture 需要使用样式every tikzmarknode picture
,例如:
\tikzset{every tikzmarknode picture/.style={save nodes to file}}
\tikzmarknode%
[save node]
{C}{C}
但这与每一个 \tikzmarknode
。虽然这样做是安全的(只有在有节点需要保存时才会执行保存节点代码),但如果只想保存一两个节点,我并不太乐意这样做。
但\tikzmarknode
与一般的 tikzpictures 相比,s 非常简单,因此我们可以在定义节点后进行连接,而不必等到图片完成。所以这是一个似乎有效的密钥,如果它确实有效,那么我会将其添加到库中tikzmark
。
\ExplSyntaxOn
\tikzset{
save~ tikzmarknode/.code={
\tikz_fig_must_be_named:
\pgfkeysalso{
append~ after~ command={
\pgfextra{
\clist_gput_right:Nv \g__sn_nodes_clist {tikz@last@fig@name}
\maybe_save_nodes:
}
}
}
}
}
\ExplSyntaxOff
...
\tikzmarknode%
[save tikzmarknode]
{C}{C}
答案2
我不确定这是否能对你的具体情况有所帮助,但可以扩展一下你链接的这个答案您可以测试两件事(#1
即节点名称):
- 已保存一张图片:
\@ifundefined{save@pt@\tikzmark@pp@name{#1}
应该是false
。 - 它是一个
\node
:\@ifundefined{pgf@sh@ns@\tikzmark@pp@name{#1}}
应该是false
。
如果 1) 为假,那么它就不仅仅是一个普通的 Ti钾Z 节点。否则,如果 2) 为假,则它是\tikzmarknode
而不仅仅是\tikzmark
。
在第一个测试中使用,我们额外检查保存的图片是否由或\tikzmark@pp@name
创建。使用 创建的节点名称进行测试仍将返回。tikzmark
tikzmarknode
\node[remember picture]
true
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\makeatletter
\newcommand*{\IsTikzmarkNode}[1]{
\@ifundefined{save@pt@\tikzmark@pp@name{#1}}{
node
} {
\@ifundefined{pgf@sh@ns@\tikzmark@pp@name{#1}}{
tikzmark
} {
tikzmarknode
}
}
}
\makeatother
\begin{document}
% A is a tikzmark
foo\tikzmark{A} bar
% B is a tikzmarknode
\tikzmarknode{B}{foo}
% C is a node
\tikz{\node (C) {bar};}
\bigskip
\IsTikzmarkNode{A}
\IsTikzmarkNode{B}
\IsTikzmarkNode{C}
\end{document}
如果你想知道(因为我确实想知道):如果定义了,并且即使这个测试在范围之外进行,\@ifundefined{pgf@sh@ns@\tikzmark@pp@name{C}}
也会如此。false
\node (C) {};
\tikz
在上面的例子中,您可以简单地执行(无需另外测试它是否只是一个常规节点):
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\makeatletter
\NewDocumentCommand{\IsTikzmarkNode}{ m }{
\@ifundefined{save@pt@\tikzmark@pp@name{#1}}{
-1
} {
\@ifundefined{pgf@sh@ns@\tikzmark@pp@name{#1}}{
0
} {
1
}
}
}
\makeatother
\newcommand*{\DrawPicture}[2]{%
\begin{tikzpicture}[overlay, remember picture]
\ifnum\IsTikzmarkNode{#1} > 0\relax
\coordinate (start) at (#1);
\else
\coordinate (start) at (pic cs:#1);
\fi
\ifnum\IsTikzmarkNode{#2} > 0\relax
\coordinate (end) at (#2);
\else
\coordinate (end) at (pic cs:#2);
\fi
\draw [ultra thick, red] (start) to[bend left=90] (end);
\end{tikzpicture}%
}%
\begin{document}
\[
\tikzmark{MarkA} a + \tikzmarknode{MarkB}{b}
\]
\DrawPicture{MarkA}{MarkB}% <-- This works.
\DrawPicture{MarkB}{MarkA}% <-- Want this also to be able to work
\end{document}
好的,这花了我一段时间,但我想我知道你想要什么:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\ExplSyntaxOn
\seq_new:N \l_istikzmarknode_registeredmarks_seq
\NewDocumentCommand{\RegisterTikzmark}{ m }{
\seq_gpush:Nn \l_istikzmarknode_registeredmarks_seq { #1 }
}
\NewDocumentCommand{\IsRegisteredTikzmarkTF}{ m m m }{
\seq_if_in:NnTF \l_istikzmarknode_registeredmarks_seq { #1 } { #2 } { #3 }
}
\seq_new:N \l_istikzmarknode_registerednodes_seq
\NewDocumentCommand{\RegisterTikzmarkNode}{ m }{
\seq_gpush:Nn \l_istikzmarknode_registerednodes_seq { #1 }
}
\NewDocumentCommand{\IsRegisteredTikzmarkNodeTF}{ m m m }{
\seq_if_in:NnTF \l_istikzmarknode_registerednodes_seq { #1 } { #2 } { #3 }
}
\ExplSyntaxOff
\makeatletter
\tikzset{
save picture id/.append code={
\immediate\write\@auxout{\string\RegisterTikzmark{#1}}
\RegisterTikzmark{#1}
},
register tikzmarknode/.code={
\immediate\write\@auxout{\string\RegisterTikzmarkNode{#1}}
\RegisterTikzmarkNode{#1}
},
maybe define node/.append style={
register tikzmarknode={#1}
}
}
\newcommand*{\IsTikzmarkNode}[1]{
\IsRegisteredTikzmarkTF{#1}{
\IsRegisteredTikzmarkNodeTF{#1}{
tikzmarknode
} {
tikzmark
}
} {
\@ifundefined{pgf@sh@ns@\tikzmark@pp@name{#1}}{
undefined
} {
node
}
}
}
\makeatother
\begin{document}
\IsTikzmarkNode{A}
\IsTikzmarkNode{B}
\IsTikzmarkNode{C}
\bigskip
% A is a tikzmark
foo\tikzmark{A} bar
% B is a tikzmarknode
\tikzmarknode{B}{foo}
% C is a node
\tikz{\node (C) {bar};}
\bigskip
\IsTikzmarkNode{A}
\IsTikzmarkNode{B}
\IsTikzmarkNode{C}
\end{document}
第一次运行后将会输出:
每次运行后:
它有什么作用?首先,我定义两个序列\l_istikzmarknode_registeredmarks_seq
,并将所有s 和s\l_istikzmarknode_registerednodes_seq
存储在其中。可能还有其他方法可以直接从 PGF 代码中获取此信息。无论如何,此解决方案也适用于包含空格或破折号的节点名称。\tikzmark
\tikzmarknode
然后,我向其中附加一些代码,这些代码save picture id
实际上是针对每个\tikzmark
(以及每个\tikzmarknode
)调用的,并将\tikzmark
s 的名称存储在序列中\l_istikzmarknode_registeredmarks_seq
。我稍后可以使用它来测试字符串是否是已定义的名称\tikzmark
。我还将命令存储在 .aux 文件中的序列中,以便在第二次运行时调用它。
然后,与上面类似,我将一些代码附加到样式中maybe define node
,这些代码将针对每个样式执行\tikzmarknodes
,并将节点的名称作为参数。我将这些名称存储在序列中\l_istikzmarknode_registerednodes_seq
。我稍后可以使用它来测试字符串是否是已定义的名称\tikzmarknode
。同样,我还将命令存储在 .aux 文件中的序列中。
现在,我只需要检查给定的名称是否在两个序列之一中,对于常规的\nodes
,另外还要检查的\@ifundefined{pgf@sh@ns@\tikzmark@pp@name{#1}}
。
我不太确定,但从语义上来说,替换
register tikzmarknode/.code={
\immediate\write\@auxout{\string\RegisterTikzmarkNode{#1}}
\RegisterTikzmarkNode{#1}
},
maybe define node/.append style={
register tikzmarknode={#1}
}
经过
every tikzmarknode/.code={
\immediate\write\@auxout{\string\RegisterTikzmarkNode{\tikz@id@name}}
\RegisterTikzmarkNode{\tikz@id@name}
}
在上面的代码中。但不知何故,测试在第一次运行后就不起作用了。
答案3
我的目标是编写绘图宏,可以接受由\tikzmark
或用\tikzmarknode
没有标记每个标记如何创建,或者知道它们是否已定义。如果它们尚未定义,我可以
- 要么不符合图纸,要么
- 用于
(0,0)
坐标,因此有一个可见的指示器,显然需要另一次运行。
我使用的解决方案如下(注释掉了生成图表所需的部分,但对于遵循代码来说并非必不可少)。任何有关此方法的潜在问题的反馈都会有所帮助。
\documentclass{article}
\usepackage{amsmath}
\usepackage{xstring}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\makeatletter
\newcommand{\IfStrContains}[4]{%
\StrPosition{#1}{#2}[\@@Position]% Record position in \@@Position
\IfEq{\@@Position}{0}%
{#4}% \@@Position=0 => Did not find the target string
{#3}% \@@Position>0 => Found the target string
}%
\newcommand{\IfNodeDefined}[3]{%
%% http://tex.stackexchange.com/questions/37709/how-can-i-know-if-a-node-is-already-defined
% #1 = node name
% #2 = code to execute if node is defined
% #3 = code to execute if node is NOT yet defined
\@ifundefined{pgf@sh@ns@#1}{#3}{#2}%
}
\NewDocumentCommand{\DefineTikzmarkCoordinate}{%
%% https://tex.stackexchange.com/questions/702241/dectect-if-using-tikzmark-or-tikzmarknode
m%% #1 = name of coordinate
m%% #2 = \tikzmark name, or \tikzmarknode name
}{%
%%% #1 maybe of the form "<node name>.<anchor>" so remove text after the .
\IfStrContains{#2}{.}{%
\StrBefore{#2}{.}[\@@TikzmarkName]%
}{%
\def\@@TikzmarkName{#2}%
}%
\IfNodeDefined{\@@TikzmarkName}{% we need to access this as a node
\coordinate (#1) at (#2);
}{%
\iftikzmark{\@@TikzmarkName}{% need to use the 'pic cs' coordinate system
\coordinate (#1) at (pic cs:#2);
}{% not defined, so use the origin
\coordinate (#1) at (0,0);
}%
}%
}
\makeatother
\newcommand*{\DrawPicture}[3][]{%
%% -------------
\begin{tikzpicture}[overlay,remember picture]
\DefineTikzmarkCoordinate{Coordinate 1}{#2}%
\DefineTikzmarkCoordinate{Coordinate 2}{#3}%
%% -------
\draw [ultra thick, -, #1]
(Coordinate 1) to[out=120, in=50, distance=1cm] (Coordinate 2);
\end{tikzpicture}%
}%
\begin{document}
%\begin{minipage}{0.5\linewidth}
%The following figure has
%\begin{itemize}
%\item
% \verb|\tikzmark| to the left of $a$ and
%\item
% $b$ has a \verb|\tikzmarknode| around it.
%\end{itemize}
%\end{minipage}
%\begin{minipage}{0.3\linewidth}
\[
\tikzmark{MarkA} a + \tikzmarknode{MarkB}{b}
\]
\DrawPicture[red, -latex]{MarkA}{MarkB.north}% <-- This works.
\DrawPicture[blue,-latex, yscale=-1]{MarkB.south}{MarkA}% <-- Now, this also to work
%\end{minipage}
%
%\medskip
%The macro \verb|\DrawPicture| can draw between these two in either direction.
\end{document}