当\myCoordinate
接受 MANDATORY 参数时,以下工作有效:
\documentclass{standalone}
\usepackage{tikz}
\newcommand\myCoordinate[1]{#1}
\newcommand\myPath[1][(1,1)]{\path[draw=red]\myCoordinate{#1}--(1,0);}
\begin{document}
\begin{tikzpicture}
\myPath
\end{tikzpicture}
\end{document}
\myCoordinate
但当接受可选参数时则不然(用以下代码替换上面的定义)\myCoordinate
:\myPath
% modify \myCoordinate to accept optional argument and set default value for that argument to (0,0)
\newcommand\myCoordinate[1][(0,0)]{#1}
% replace curly braces (for mandatory argument) with square brackets (for optional argument)
\newcommand\myPath[1][(1,1)]{\path[draw=red]\myCoordinate[#1]--(1,0);}
% you can replace \myPath definition with following one so that optional argument of \myCoordinate reverts to default (but it still doesn't work)
%\newcommand\myPath[1][(1,1)]{\path[draw=red]\myCoordinate--(1,0);}
从产生的错误(放弃这条路径。你忘了分号吗?)来看,tikz
这是因为宏的扩展出了问题而发出的抱怨。而那个扩展问题是由 的可选参数引起的\myCoordinate
。
我需要\myCoordinate
在接受单个参数(即可选参数)的情况下工作。我需要使用尽可能简单的代码来实现这一点。
附注:要了解如何在命令接受的所有可选参数中仅将值发送给特定的可选参数,请参阅这。
另一种方法同样失败。
当将 (而不是\myCoordinate
内部\myPath
)作为参数\myCoordinate
传递给时\myPath
,如果 的参数\myCoordinate
是可选的,它会再次失败:
\documentclass{standalone}
\usepackage{tikz}
% following \myCoordinate accepts optional argument and fails when passed as an argument to \myPath
\newcommand\myCoordinate[1][0,0]{#1}
\newcommand\myPath[1][(1,1)]{\path[draw=red]{#1}--(1,0);}
\begin{document}
\begin{tikzpicture}
% neither of the two following lines work when \myCoordinate accepts optional argument
%\myPath[\myCoordinate] % fails
%\myPath[\myCoordinate[(0,0)]] % fails
\end{tikzpicture}
\end{document}
但是,当 from above 的定义\myCoordinate
和 from above 的使用\myPath
更改为以下内容时,它再次起作用:
\newcommand\myCoordinate[1]{#1} % works
\myPath % works
%\myPath[\myCoordinate{(0,0)}] % works
因此,可选参数版本\myCoordinate
既不能直接在内部使用,也不能将其值作为参数\myPath
传递。\myPath
我对此的评论答案由@marmot 提供。
newcommand
与或相比,将(pgf)键值对作为路径样式的参数传递存在相当大的缺点NewDocumentCommand
(至少在您的答案中显示的形式,不确定是否可以更正):
- 太长:与 相比
\myLine[1][north]
,\path[myLine={subtract=1,anchor=north}]
它就像同时引用 3 个单独的命令(\myLine
、\subtract
、\anchor
或\myLine[\subtract[1]][\anchor[north]]
) - 因此,重命名(如果需要)
subtract
and/oranchor
将需要替换每个传递了参数的样式中出现的每一个subtract
and/or (整个文档中可能有数十或数百个,甚至更多):与仅在内部使用的重命名and/or相比(在文档序言中)anchor
myLine
\subtract
\anchor
newcommand
\myLine
newcommand
如果可能的话,解决方案应该是(使用值,而不是键值,参数---依赖于作为参数传递的值的位置或顺序,而不是它们的键)以下内容:修改tikzset
代码以替换\path[myLine={subtract=1,anchor=north}]
类似于\path[myLine={1,north}]
每个参数都是可选的(意味着以下所有内容均有效并按预期工作\path[myLine]
,\path[myLine={1}]
和\path[myLine={north}]
)。
答案1
pgf 键的理念恰恰是为了避免所有这些可选参数并被它们弄糊涂。与其重写所有命令并记住哪个参数意味着什么,在我看来,使用键要简单得多。在下面的示例中,如果您想使用所有默认选项绘制路径,只需执行
\path[L];
如果你想减 1,用蓝色代替红色,用南代替北,那么
\path[L={subtract=1,anchor=south,color=blue}];
你可以根据需要重新排列按键,
\draw[L={color=yellow,subtract=0,anchor=south}];
也可以。此方法的另一个优点是向下兼容,也就是说,如果您稍后决定需要添加新键,旧命令仍将有效没有需要加载新的包。
完整示例:
\documentclass[tikz,border=3.14mm]{standalone}
\newcounter{myC}
\tikzset{L/.style={/utils/exec=\tikzset{bp/.cd,#1},insert path={[draw=\pgfkeysvalueof{/tikz/bp/color}]
(\the\numexpr\number\value{myC}-\pgfkeysvalueof{/tikz/bp/subtract}\relax.\pgfkeysvalueof{/tikz/bp/anchor}\space west)
--(\the\numexpr\number\value{myC}-\pgfkeysvalueof{/tikz/bp/subtract}\relax.\pgfkeysvalueof{/tikz/bp/anchor}\space east)}},
bp/.cd,
subtract/.initial=0,
anchor/.initial=north,
color/.initial=red}
\begin{document}
\begin{tikzpicture}[mystep/.code={\stepcounter{myC}}]
\path node[draw,mystep](1)at (\number\value{myC},0){one};
\path[L];
\path node[draw,mystep](2)at (\number\value{myC},0){two};
\path[L];
\path[L={subtract=1,anchor=south,color=blue}];
\draw[L={color=yellow,subtract=0,anchor=south}];
\end{tikzpicture}
\end{document}
代码mystep
复制自安德鲁的回答。
附录:回复我回答中的问题评论。使用关键过滤器可以完成类似\path[myLine={north,1}];
工作。但是,在你的情况下,这需要更多的思考,因为数字也是有效的锚点。那么 Ti 会如何钾Z 知道这1
不是锚点而是减法键吗?(我个人认为这个建议不是经过深思熟虑的,但当然也有经过深思熟虑的版本,例如,如果你说\node[circle,red,...
你不必说shape=circle,color=red
因为 Ti钾Z 会帮你解决这个问题。但在这种情况下不存在歧义。)
答案2
我不清楚您要做什么,但我不认为您需要两个宏,并且下面的代码可能会满足您的要求:
\documentclass[tikz, border=5mm]{standalone}
\usepackage{xparse}
\newcounter{myC}
\NewDocumentCommand{\myL}{O{north} O{0}}{%
\def\myC{\the\numexpr(\themyC-#2)}
\path[draw=red](\myC.#1 west)--(\myC.#1 east);
}
\begin{document}
\begin{tikzpicture}[mystep/.code={\stepcounter{myC}}]
\path node[draw,mystep](1)at (\themyC,0){one};
\myL
\path node[draw,mystep](2)at (\themyC,0){two};
\myL
\end{tikzpicture}
\end{document}
输出如下:
特别注意使用.code
语句来步进计数器。我还添加了一些坐标,用于将节点放置在路径语句中,否则所有内容都会相互叠加。
答案3
解析其参数的方式pgf
依赖于一定数量的“前瞻”代码,该代码接收输入并一步一步处理以提取可能的操作。这是基于只有有限范围的材料才能出现在pgf
/TikZ 语句中的任意位置的想法。
在这里,寻找可选参数涉及赋值(\let
),而 的内部不会处理pgf
。在 TeX 级别,\let
是不可扩展的,因此两种可能的路线pgf
都失败了:它既不是已知情况,也不是可以扩展的东西。结果将是一个无限循环,除非pgf
有一个循环计数器(最初设置为 100 步),当用尽时会导致出现错误的替代路径。
因此,这是 TikZ 工作方式的一个基本限制/设计决策。其他答案已经以其他方式解决了这个问题,但您不能在这里使用带有可选参数的宏(至少在没有尝试重写 TikZ 解析器的重要部分的情况下)。
仅带有强制参数的命令不会有让他们内心“隐藏”着一项任务,这就是他们能在这里工作的原因。(同样,人们需要观看完全任意的内容。)
答案4
何时\myCoordinate
使用里面 \myPath
就像在您的初始代码中一样,它具有可选参数的能力是没有意义的。 没有从不\myPath
将参数传递给 inner 的代码路径开始\myCoordinate
。 也就是说,没有办法调用\myPath
以使 inner\myCoordinate
回退到其默认参数。
因此,一个选项是为内部命令提供一个不带可选参数的替代命令\myCoordinate
。用户级\myCoordinate
也可以使用此替代命令,这样就不会出现代码重复的情况。
这将导致如下代码:
\documentclass{standalone}
%\url{https://tex.stackexchange.com/q/490400/86}
\usepackage{tikz}
\newcommand\myCoordinate[1][(0,0)]{\myCoordinateAux{#1}}
\newcommand\myCoordinateAux[1]{#1}
\newcommand\myPath[1][(1,1)]{\path[draw=red]\myCoordinateAux{#1}--(1,0);}
\begin{document}
\begin{tikzpicture}
\myPath
\end{tikzpicture}
\end{document}
您可以使用常用\makeatletter ... \makeatother
代码使用户无法访问内部命令。