我想创建一个doublecirc
具有两个节点部分的多节点形状,比如text
和lower
,由两个圆圈组成,每个圆圈内都有文本。
(显然有更简单的方法可以做到这一点 - 但这是一个 MWE,我想要的形状更复杂。)
我希望部分圆的颜色lower
能够从中继承text
,但可以使用可选的 pgfkey 来更改它:
\node[name=a,shape=doublecirc,style={draw},red] % circles+text red
\node[name=b,shape=doublecirc,style={draw},draw=red]% circles red
\node[name=c,shape=doublecirc,style={draw},draw=red, /tikz/lower color = blue] % lower circle blue
我找到了一种基于扩展的方法来做到这一点
Loop Space 的解决方案另一个问题。然而,结果比我想象的要复杂得多,本质上大约pgfkeys。(具体来说,我使用了etoolbox
包命令\ifdefstring
,并为我的密钥分配了一个“虚拟”初始颜色(empty
)。)
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgf,tikz}
\usetikzlibrary{arrows,shapes.arrows,shapes.geometric,
shapes.multipart,backgrounds,decorations.pathmorphing,positioning,fit,
shapes.callouts}
\makeatletter
\newbox\pgfnodepartlowerbox
\newbox\pgfnodeparttextbox
\pgfkeys{/tikz/lower color/.initial = empty}% Default value indicates undefined
\pgfdeclareshape{doublecirc}{%
\nodeparts{text,lower}%
\savedmacro\lowercolor{%
\edef\tst{\pgfkeysvalueof{/tikz/lower color}}%
\ifdefstring{\tst}{empty}% comparison command from etoolbox
{\ifx\tikz@strokecolor\pgfutil@empty%if not defined
\def\lowercolor{.}%
\else%
\def\lowercolor{\tikz@strokecolor}%
\fi}%
{\def\lowercolor{\expandafter\pgfkeysvalueof{/tikz/lower color}}}%if defined
}
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y=.5\ht\pgfnodeparttextbox%
}
\savedanchor\lower{%
\pgf@x=.5\wd\pgfnodepartlowerbox%
\pgf@y=.5\ht\pgfnodepartlowerbox%
\advance\pgf@y by -2.0\ht\pgfnodeparttextbox%
\advance\pgf@x by 2.0\wd\pgfnodeparttextbox%
}
\anchor{center}{\centerpoint}%
\anchor{lower}{\lower}
\backgroundpath{
\pgfpathcircle{\centerpoint}{\wd\pgfnodeparttextbox}%
\pgfusepath{stroke}%
\pgfsetstrokecolor{\lowercolor}%
\pgfpathcircle{\lower}{1.5\wd\pgfnodepartlowerbox}%
\pgfusepath{stroke}%
}
\anchorborder{\centerpoint}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[name=a,shape=doublecirc,style={draw},red] at (0,0) {A\nodepart{lower}{B}}; % Inherit color from .
\node[name=b,shape=doublecirc,style={draw},draw=red] at (2,0) {A\nodepart{lower}{B}}; % Inherit color from draw
\node[name=c,shape=doublecirc,style={draw},draw=red, /tikz/lower color = blue] at (4,0) {A\nodepart{lower}{B}}; % Specify color with parameter
\end{tikzpicture}
\end{document}
有没有更简单的方法可以做到这一点pgfkeys
?
一个特定的技术问题是,我没有找到一种优雅的方式来初始化我的 pgfkey ( lower color
),同时让密钥保持未定义状态。至少不是以一种我可以用某种形式检测到的方式\if
(但我发现 TeX 条件令人困惑:我不完全理解为什么我需要\edef
在上面的代码中使用)
在回答另一个问题时@percusse 介绍了一个
.initial without value
处理程序看起来很有希望。但是,这需要重新定义\pgfkeys@notset
和,\pgfkeysvalueof
我担心这会使创建带有形状声明的独立包变得更加困难(?)
我还尝试使用与我的 pgfkey 关联的宏或其他处理程序,但发现这很令人困惑。(请参阅存储值与宏的讨论这里和这里)
我猜测以这种方式处理颜色可能对其他多部分节点形状有用。
(作者注:这是我第一次发布问题,因此,如果我无意中违反了协议或者这个问题之前已经得到回答,请原谅。)
答案1
首先,祝贺你提出了这个好问题。让我先列出一些次要的评论。
- 您不需要 etoolbox 进行比较。
- 您也不需要加载任何库。
- 然后我不确定你是否
\expandafter
需要\def\lowercolor{\expandafter\pgfkeysvalueof{/tikz/lower color}}
。
这是精简版。我实际上不知道可以这样访问颜色,所以我学到了一些东西。
\documentclass{article}
\usepackage{tikz}
\makeatletter
\newbox\pgfnodepartlowerbox
\newbox\pgfnodeparttextbox
\pgfkeys{/tikz/lower color/.initial=empty}% Default value indicates undefined
\pgfdeclareshape{doublecirc}{%
\nodeparts{text,lower}%
\savedmacro\lowercolor{%
\edef\tst{\pgfkeysvalueof{/tikz/lower color}}%
\def\tempty{empty}
\ifx\tst\tempty\relax
\ifx\tikz@strokecolor\pgfutil@empty%if not defined
\def\lowercolor{.}%
\else%
\def\lowercolor{\tikz@strokecolor}%
\fi%
\else
\def\lowercolor{\pgfkeysvalueof{/tikz/lower color}}%if defined
\fi}
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y=.5\ht\pgfnodeparttextbox%
}
\savedanchor\lower{%
\pgf@x=.5\wd\pgfnodepartlowerbox%
\pgf@y=.5\ht\pgfnodepartlowerbox%
\advance\pgf@y by -2.0\ht\pgfnodeparttextbox%
\advance\pgf@x by 2.0\wd\pgfnodeparttextbox%
}
\anchor{center}{\centerpoint}%
\anchor{lower}{\lower}
\backgroundpath{
\pgfpathcircle{\centerpoint}{\wd\pgfnodeparttextbox}%
\pgfusepath{stroke}%
\pgfsetstrokecolor{\lowercolor}%
\pgfpathcircle{\lower}{1.5\wd\pgfnodepartlowerbox}%
\pgfusepath{stroke}%
}
\anchorborder{\centerpoint}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[name=a,shape=doublecirc,style={draw},red] at (0,0) {A\nodepart{lower}{B}}; % Inherit color from .
\node[name=b,shape=doublecirc,style={draw},draw=red] at (2,0) {A\nodepart{lower}{B}}; % Inherit color from draw
\node[name=c,shape=doublecirc,style={draw},draw=red, /tikz/lower color = blue] at (4,0) {A\nodepart{lower}{B}}; % Specify color with parameter
\end{tikzpicture}
\end{document}
寻求答案(?):如果我没记错的话,你可以进一步简化。你只想在设置了键的情况下更改颜色。这表明
\documentclass{article}
\usepackage{tikz}
\makeatletter
\newbox\pgfnodepartlowerbox
\newbox\pgfnodeparttextbox
\pgfkeys{/tikz/lower color/.initial=\pgfutil@empty}% Default value indicates undefined
\pgfdeclareshape{doublecirc}{%
\nodeparts{text,lower}%
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y=.5\ht\pgfnodeparttextbox%
}
\savedanchor\lower{%
\pgf@x=.5\wd\pgfnodepartlowerbox%
\pgf@y=.5\ht\pgfnodepartlowerbox%
\advance\pgf@y by -2.0\ht\pgfnodeparttextbox%
\advance\pgf@x by 2.0\wd\pgfnodeparttextbox%
}
\anchor{center}{\centerpoint}%
\anchor{lower}{\lower}
\backgroundpath{
\pgfpathcircle{\centerpoint}{\wd\pgfnodeparttextbox}%
\pgfusepath{stroke}%
\edef\tikz@temp{\pgfkeysvalueof{/tikz/lower color}}%
\ifx\tikz@temp\pgfutil@empty\relax
\else
\pgfsetstrokecolor{\tikz@temp}%
\fi
\pgfpathcircle{\lower}{1.5\wd\pgfnodepartlowerbox}%
\pgfusepath{stroke}%
}
\anchorborder{\centerpoint}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[name=a,shape=doublecirc,style={draw},red] at (0,0) {A\nodepart{lower}{B}}; % Inherit color from .
\node[name=b,shape=doublecirc,style={draw},draw=red] at (2,0) {A\nodepart{lower}{B}}; % Inherit color from draw
\node[name=c,shape=doublecirc,style={draw},draw=red,lower color = blue] at (4,0) {A\nodepart{lower}{B}}; % Specify color with parameter
\node[name=d,shape=doublecirc,style={draw},red] at (6,0) {A\nodepart[blue]{lower}{B}}; % Specify color with parameter
\end{tikzpicture}
\end{document}
編輯:使用\pgfutil@empty
和进一步简化\tikz@temp
。
编辑:至于你的额外要求:这是可以做到的,但是,我只能通过以下方式来实现使密钥全球化。我把它放在这个单独的代码中,因为我不确定我是否建议这样做。如您所见,现在最后一部分也变成了绿色。(当然,这可以通过在绘制背景路径后重置颜色来纠正。我的观点是,这个看似简单的附加功能似乎需要一些可能在其他地方适得其反的技巧。)
\documentclass{article}
\usepackage{tikz}
\makeatletter
\newbox\pgfnodepartlowerbox
\newbox\pgfnodeparttextbox
\pgfkeys{/tikz/lower color/.initial=\pgfutil@empty,
/tikz/nodepart draw/.code={\begingroup\globaldefs=1\relax%from https://tex.stackexchange.com/a/371193/121799
\tikzset{lower color=#1}
\endgroup}}% Default value indicates undefined
\pgfdeclareshape{doublecirc}{%
\nodeparts{text,lower}%
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y=.5\ht\pgfnodeparttextbox%
}
\savedanchor\lower{%
\pgf@x=.5\wd\pgfnodepartlowerbox%
\pgf@y=.5\ht\pgfnodepartlowerbox%
\advance\pgf@y by -2.0\ht\pgfnodeparttextbox%
\advance\pgf@x by 2.0\wd\pgfnodeparttextbox%
}
\anchor{center}{\centerpoint}%
\anchor{lower}{\lower}
\backgroundpath{
\pgfpathcircle{\centerpoint}{\wd\pgfnodeparttextbox}%
\pgfusepath{stroke}%
\edef\tikz@temp{\pgfkeysvalueof{/tikz/lower color}}%
\ifx\tikz@temp\pgfutil@empty\relax
\else
\pgfsetstrokecolor{\tikz@temp}%
\fi
\pgfpathcircle{\lower}{1.5\wd\pgfnodepartlowerbox}%
\pgfusepath{stroke}%
}
\anchorborder{\centerpoint}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[name=a,shape=doublecirc,style={draw},red] at (0,0) {A\nodepart{lower}{B}}; % Inherit color from .
\node[name=b,shape=doublecirc,style={draw},draw=red] at (2,0)
{A\nodepart[nodepart draw=green]{lower}{B}}; % Inherit color from draw
\node[name=c,shape=doublecirc,style={draw},draw=red,lower color = blue] at (4,0) {A\nodepart{lower}{B}}; % Specify color with parameter
\node[name=d,shape=doublecirc,style={draw},red] at (6,0) {A\nodepart[blue]{lower}{B}}; % Specify color with parameter
\end{tikzpicture}
\end{document}