我们经常需要画点。每个人都有自己喜欢的方法来画点。
你的是什么?
您是否使用节点、图片、标记......?以某种风格?
一些背景
在 tikz/pgf 手册中,经常通过 来完成此操作\tikz\fill circle (2pt);
。
在密钥的示例中,/tikz/insert path
我们可以看到此代码
\tikz [c/.style={insert path={circle[radius=2pt]}}]
\draw (0,0) -- (1,1) [c] -- (3,2) [c];
还有一个例子密钥处理程序 /.pic
(第 255 页)产生相同的实心圆。
这些方法的优点是非常简单,但对我来说并不好,因为:
- 点的外观取决于路径命令操作(绘制、填充……)。
- 当您缩放图像时,线宽不会缩放(字体大小也不会缩放),但点会缩放(
em
例如,您可以通过输入尺寸轻松克服这个问题)。 - 如果在绘制点之后绘制一条线,则该线将绘制在该点上。
和更多 ...
我正在寻找
下面是我希望仅使用一个“point”定义(或使用多个定义但具有一致的语法)就能完成的事情的列表。
对于每个需求,我都进行了测试,并获得了预期的结果。在测试中,你可以用你喜欢的语法替换“point”。
1) 点必须正确缩放。对于我来说,不清楚大小是否应该与线宽或字体大小成比例。最好的办法可能是使用线宽来调整大小,然后如果需要的话(参见第 3 点)设置大小,em
如果我们想要字体缩放兼容性。你怎么看?
\begin{tikzpicture}
\foreach[count=\i] \w in {ultra thin, thin, ultra thick} {
\draw[yshift=-\i em, \w] (0,0) -- (.5,0) "point" -- (1,0);
}
\foreach[count=\i] \s in {.2, .5, 1} {
\draw[xshift=1.5cm, yshift=-\i em, scale=\s] (0,0) -- (.5,0) "point" -- (1,0);
}
\end{tikzpicture}
2) 我们应该能够轻松地为点添加样式。例如,我们应该能够说“画一个粗红点”。
(测试见下一点)
3) 可以设置点的绘制、填充和不透明度,inherit
在这种情况下,这些参数是从范围/路径继承的。但只有绘制应该inherit
默认设置为,其他默认值(个人喜好)应该是填充=白色和不透明度=1。
\begin{tikzpicture}[scale=2, very thick]
\filldraw[draw opacity=.5, draw=red, fill opacity=.3, densely dotted]
(0,0) "point" -- (.5,0) "ultra thick point filled in green" -- (.5,.5) "point with inherited draw, fill and opacity" -- cycle;
\end{tikzpicture}
4)该点可以轻松命名以便使用coordinate
,并且如果用名称绘制该点,则该点node
应该指向节点的中心而不是节点本身。
\begin{tikzpicture}
\draw[very thick] (0,1) "point" -- (1,0) "thick point filled in green with name=A";
\draw[ultra thick, purple] (0,0) "point" -- (A);
\end{tikzpicture}
5) 点可用于我们通常可以使用其他命令绘制的任何情况。前面的示例已经说明了这一点,但如果我们可以将其与和一起使用,那就更好了\node at
(\coordinate at
这并不明显,因为at
不会更改当前坐标)。
(测试见下一点)
6)点绘制在任意线的顶部(在前景层上)。
\begin{tikzpicture}
\node[left] {A} at (0,1) "ultra thick point";
\coordinate["thick point"] (B) at (1,0);
\draw (A) -- (B);
\end{tikzpicture}
7)解决方案必须是非黑客的解决方案,以便“点”的定义可以(希望)与 tikz 的未来版本兼容。
我个人不完整的解决方案是什么
我会将其作为答案发布。
答案1
方法 I(使用节点)
\tikzset{
every point/.style = {circle, inner sep={.75\pgflinewidth}, opacity=1, draw, solid, fill=white},
point/.style={insert path={node[every point, #1]{}}}, point/.default={},
point name/.style = {insert path={coordinate (#1)}},
}
还有一些额外的东西:
\tikzset{
colored point/.style = {point={fill=#1}},
inherit/.style = {point/.style={insert path={node[circle, inner sep={.75\pgflinewidth}, draw, fill, #1]{}}}}
}
满足1.
像这样的造型满足 2
[point={fill=red, very thick}]
部分满足 3。我不知道如何定义
draw opacity=inherit
或fill=inherit
。我定义了新样式,它将通过删除和inherit
来重新定义整个,但这很丑陋 ;)。point
opacity=1
fill=white
部分满足 4:您可以使用
point name=A
。我希望能够使用引号来表达类似的意思[point={red, "A"}]
,但我不知道该怎么做。几乎满足 5 :我们几乎可以将 [point] 放在任何地方,例如
(A) [point]
、 或node[point, above]{A}
或coordinate[point](A)
。但不能与\coordinate at
或一起使用\node at
(除非你像这样重复自己\coordinate (A) at (1,1) (A) [point];
)失败6. 我知道有一个将节点放在层上的黑客解决方案,但这与7)相矛盾。
满足7.
所有测试的完整代码和结果
\documentclass[varwidth,border=50]{standalone}
\usepackage{tikz}
% not clear how to use layers with this method
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\tikzset{
every point/.style = {circle, inner sep={.75\pgflinewidth}, opacity=1, draw, solid, fill=white},
point/.style={insert path={node[every point, #1]{}}}, point/.default={},
colored point/.style = {point={fill=#1}},
point name/.style = {insert path={coordinate (#1)}},
inherit/.style = {point/.style={insert path={node[circle, inner sep={.75\pgflinewidth}, draw, fill, #1]{}}}}
}
\begin{document}
\begin{itemize}
% ---------------------------------
\item Test 1 : ok.\\[1em]
\begin{tikzpicture}
\foreach[count=\i] \w in {ultra thin, thin, ultra thick} {
\draw[yshift=-\i em, \w] (0,0) -- (.5,0) [point] -- (1,0);
}
\foreach[count=\i] \s in {.2, .5, 1} {
\draw[xshift=1.5cm, yshift=-\i em, scale=\s] (0,0) -- (.5,0) [point] -- (1,0);
}
\end{tikzpicture}
% ---------------------------------
\item Test 2 : ok.
% ---------------------------------
\item Test 3 : partialy ok, there is no good \texttt{inherit}.\\
\begin{tikzpicture}[scale=2, very thick]
\filldraw[draw opacity=.5, draw=red, fill opacity=.3, densely dotted]
(0,0) [point] -- (.5,0) [point={ultra thick, fill=green}] -- (.5,.5) [inherit, point] -- cycle;
\end{tikzpicture}
% ---------------------------------
\item Test 4 : almost ok (using \texttt{point name})\\
\begin{tikzpicture}
\draw[very thick] (0,1) [point] -- (1,0) [point={thick, fill=green, point name=A}];
\draw[ultra thick, purple] (0,0) [point] -- (A);
\end{tikzpicture}
% ---------------------------------
\item Test 5 : almost ok.
% ---------------------------------
\item Test 6 : fails ! (visible in test 4 too)\\
\begin{tikzpicture}
\coordinate (A) at (0,1) (A) node[point=ultra thick, left] {A};
\coordinate (B) at (1,0) (B) [thick, point];
\draw (A) -- (B);
\end{tikzpicture}
\end{itemize}
\end{document}
方法 II(使用图片)
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\tikzset{
every point/.style = {radius={\pgflinewidth}, opacity=1, draw, solid, fill=white},
pt/.pic = {
\begin{pgfonlayer}{foreground}
\path[every point, #1] circle;
\end{pgfonlayer}
},
point/.style={insert path={pic{pt={#1}}}}, point/.default={},
point name/.style = {insert path={coordinate (#1)}}
}
失败1. 我不知道如何将样式从路径继承到图片。是否有类似的样式
current path style
?满足2.同方法一。
失败3.我们可以按照方法I进行设计,但是因为(1)失败,所以(3)也失败。
部分满足4.同方法一。
失败5:因为有一个'pic' 中的错误在 PGF 3.0 中,我们无法在它之后使用节点。当此错误修复后,此方法将与本次测试中的第一种方法等效。
满足6.这是此方法的主要兴趣点。
满足7.
所有测试的完整代码和结果
\documentclass[varwidth,border=50]{standalone}
\usepackage{tikz}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\tikzset{
every point/.style = {radius={\pgflinewidth}, opacity=1, draw, solid, fill=white},
pt/.pic = {
\begin{pgfonlayer}{foreground}
\path[every point, #1] circle;
\end{pgfonlayer}
},
point/.style={insert path={pic{pt={#1}}}}, point/.default={},
colored point/.style = {point={fill=#1}},
point name/.style = {insert path={coordinate (#1)}}
}
\begin{document}
\begin{itemize}
\item Test 1 : fails for sizing from path width, scale is ok.\\[1em]
\begin{tikzpicture}
\foreach[count=\i] \w in {ultra thin, thin, ultra thick} {
\draw[yshift=-\i em, \w] (0,0) -- (.5,0) [point] -- (1,0);
}
\foreach[count=\i] \s in {.2, .5, 1} {
\draw[xshift=1.5cm, yshift=-\i em, scale=\s] (0,0) -- (.5,0) [point] -- (1,0);
}
\end{tikzpicture}
\item Test 2 : partialy ok, there is no \texttt{inherit} (at all).
\item Test 3 : fails ! Can't inherit style from path.\\
\begin{tikzpicture}[scale=2, very thick, densely dotted]
\filldraw[draw opacity=.5, draw=red, fill opacity=.3]
(0,0) [point] -- (.5,0) [point={ultra thick, fill=green}] -- (.5,.5) [point] -- cycle;
\end{tikzpicture}
\item Test 4 : almost ok (using \texttt{point name})\\
\begin{tikzpicture}
\draw[very thick] (0,1) [point] -- (1,0) [point={thick, fill=green, point name=A}];
\draw[ultra thick, purple] (0,0) [point] -- (A);
\end{tikzpicture}
\item Test 5 : fails ! (can't put node after [point] )
\item Test 6 : ok.\\
\begin{tikzpicture}
\path (0,1) node[left] {A} coordinate (A) [point=ultra thick];
\coordinate (B) at (1,0) (B) [thick, point];
\draw (A) -- (B);
\end{tikzpicture}
\end{itemize}
\end{document}
答案2
笔记:我已经根据这个答案创建了一个小型 tikz 库,并将
nicepoints
其命名为GitHub。
最后,我找到了一种方法来绘制满足所有条件的点(对于颜色继承,方法略有不同),甚至更多。
在给您完整的解决方案之前,让我们从故事的开头开始。
最简单的方法
我刚刚意识到表达观点的最佳方式可能是使用“点” .
:
\tikz\draw[very thin,red] (0,0) -- node{.} (1,0);
我认为颜色的继承方式非常有用。点的颜色与文本颜色相同。
\tikz\draw[very thin,red,text=violet] (0,0) -- node{.} node[above]{A} (1,0);
但是像这样的点太小,无法画出较粗的线条。
\tikz\draw[very thick,red,text=violet] (0,0) -- node{.} (1,0);
我们可以通过使用它来扩展它line width
,并通过创建一种样式来自动化这一切。
\tikzset{point/.style={insert path={ node[scale=2.5*sqrt(\pgflinewidth)]{.} }}}
\tikz\draw[very thick,red,text=violet] (0,0) -- node[point,above]{A} (1,0);
选择sqrt
哪种方式取决于个人喜好:这样,对于较细的线条,点就不会太小;对于较粗的线条,点就不会太粗。实际上,这样,点的表面积与线条宽度成正比。
更复杂的点
如果我们想填充这个点,我们可以简单地在第一个点上画另一个较小的点:
\tikzset{
outer dot/.style = {scale=2.5*sqrt(\pgflinewidth)},
inner dot/.style = {scale=sqrt(\pgflinewidth),#1},inner dot/.default={white},
point/.style={insert path={ node[outer dot]{.} node[inner dot=#1]{.}}}
}
\tikz\draw[very thick,blue] (0,0) -- node[point]{} (1,0) [point=red];
尚未解决的问题是,在一个点之后绘制的线可以像这样重叠:
\begin{tikzpicture}[scale=2]
\draw[very thick,blue] (0,0) -- node[point]{} (1,0) [point=red];
\draw[red] (0,0) -- node[point]{} (1,.2);
\draw[very thick] (0,.2) -- (1,0);
\end{tikzpicture}
解决方案
我们想要的是将点放在顶层,这样在它之后绘制的任何线都位于点之下。我们可以这样做,pgfonlayer
但有两个困难:
自动插入 的方法并不多
pgfonlayer
。我们可以使用一些 hack(但我不想要这个)。我们可以使用pic
,但在这种情况下,如果我们想在点之后使用节点,我们必须修复 TikZ 3.0.0 的 bug。我知道的第三个选项是使用path picture
。
这里有一些棘手的事情:如果我们path picture
通过改变图层在里面绘制一些东西,那么绘图不会被剪裁,因为剪裁是应用于初始图层的。当我们改变图层时,“几乎”所有东西都会被重置:线宽、绘制和填充颜色、不透明度。但当我写下这个问题,文本颜色和不透明度不会因图层更改而重置。所以我们唯一需要注意的是
line width
。但这并不难,因为我们pgflinewidth
可以在图层更改之前保存,然后在新图层上使用它。
以下是对初始问题的解决方案的“非引用”版本:
\pgfdeclarelayer{points}
\pgfsetlayers{main,points}
\tikzset{
set point size/.code={\pgfmathsetmacro{\pointsize}{sqrt(\pgflinewidth)}},
point size/.style={set point size/.prefix style={line width=#1}},
every dot/.style = {inner sep=0, outer sep=0,font=},
outer dot/.style = {every dot, scale=2.5*\pointsize},
inner dot/.style = {every dot, scale=\pointsize, text=#1}, inner dot/.default={white},
point fill/.style = {inner dot/.default={#1}},
point coordinate/.style={insert path={coordinate(#1)}},
point/.style={insert path={
node[inner sep=0, overlay, every point/.try, #1, set point size,
path picture={
\begin{pgfonlayer}{points}
\node[outer dot]{.} node[inner dot]{.};
\end{pgfonlayer}
}
]{}
}
}
}
这里有一个测试:
\begin{tikzpicture}[scale=2]
\draw[blue] (0,0)[point] -- node[thick,point={name=A},below]{$A$} (1,0) [point=red];
\draw[red] (0,0) -- node[point]{} (1,.2) [point=thick];
\draw[very thick] (0,.2)[point={point coordinate=B, label=$B$}] -- (1,0);
\draw[green] (A)--(B);
\end{tikzpicture}
如何 ...
- 如何使用
point
风格?- 就像
(1,1) [point]
, - 或者在另一个节点内像这样
node[point,below]{$A$}
。
- 就像
- 如何在线段的中间放置一个点?我们不能使用
-- [point]
语法,但我们可以:- 像这样放入另一个节点
-- node[point]{}
- 或者在片段结束后,像这样
--(1,1)[point=midway]
- 像这样放入另一个节点
- 如何设置
draw
一个点的颜色?就像我们设置所有节点的文本颜色。- 如果像这样在路径上设置颜色,
\path[red] ...
那么它将被路径上的点继承(因为在这种情况下也设置了文本颜色)。
\tikz\draw[red] (0,0. -- (1,0) [point=midway];
- 如果我们仅在路径/范围上设置绘制/填充颜色,而不设置文本颜色,则点默认为黑色(与文本一样)。
\tikz\path[draw=red] (0,0) -- (1,0) [point=midway];
- 我们可以像以下示例一样指定点颜色:
- 如果像这样在路径上设置颜色,
\begin{tikzpicture}[scale=2] \绘制[填充=蓝色!14] (0,0)[点] -- (1,0)[点=蓝色] -- 节点[红色,点,左]{红点和文本} (1,1)节点[点,上方,红色]{仅红色文本} -- (0,1) 节点[点=红色,下方]{仅红点}; \结束{tikzpicture}
- 如何设置
fill
一个点的颜色?通过使用point fill
密钥。- 如果我们想为路径上的所有点设置它,我们可以这样做
\path[point fill=red] ...
- 如果我们想将其设置为一个点,我们可以这样做
[point={point fill=red}]
。
- 如果我们想为路径上的所有点设置它,我们可以这样做
\tikz\path[超厚,红色,点填充=蓝色] (0,0)[点](.5,0)[点={点填充=绿色}](1,0)[点];
- 如何设置点大小?该点从路径的线宽继承其大小。
- 为了改变一个点,我们可以使用
[point=very thick]
例如 - 要为范围内的所有点设置它,而不考虑线宽,我们可以使用
point size=.8pt
例如
- 为了改变一个点,我们可以使用
最后,我们可以用来every point
设置默认点样式。
附加内容:引用点
如果我们想要说出point="A"
并将坐标设置为并在点旁边显示(A)
文本,我们可以使用该库。$A$
quotes
这是这些点的完整代码quoted
。
\usetikzlibrary{quotes}
\pgfdeclarelayer{points}
\pgfsetlayers{main,points}
\tikzset{
set point size/.code={\pgfmathsetmacro{\pointsize}{sqrt(\pgflinewidth)}},
point size/.style={set point size/.prefix style={line width=#1}},
every dot/.style = {inner sep=0, outer sep=0,font=},
outer dot/.style = {every dot, scale=2.5*\pointsize},
inner dot/.style = {every dot, scale=\pointsize, text=#1}, inner dot/.default={white},
point fill/.style = {inner dot/.default={#1}},
point coordinate/.style={insert path={coordinate(#1)}},
quotes mean point/.style={'/.style={empty label/.style={node contents=}},
node quotes mean/.try={point coordinate=##1,
label={[direction shorthands, every label quotes/.try, ##2,
node contents=\ensuremath{##1}, empty label/.try]}}},
point/.style={quotes mean point, insert path={
node[inner sep=0, overlay, every point/.try, #1, set point size,
path picture={
\begin{pgfonlayer}{points}
\node[outer dot]{.} node[inner dot]{.};
\end{pgfonlayer}
}]{}
}
}
}
如何使用引述要点
- 当您说时
[point="B"red]
,首先coordinate(B)
插入然后label={[red]$B$}
使用相当于的。 - 如果使用
[point="B"']
(与'
),则$B$
不会显示(添加空标签),但coordinate(B)
会插入。 - 如果我们只想要一个没有坐标的标签,我们可以使用
[point={label=$B$}]
或node[point,above]{$B$}
。
让我们以“引用要点”的一个例子来结束这个简短的回答:
\begin{tikzpicture}[scale=2]
\draw[fill=yellow!30,very thick]
(0,0) [point="A"] -- (1,0) [point={"B"',blue}]
-- node[red,point="C"left]{} (1,1) [point="D"{above,red}]
-- (0,1) [point={red,"E"}];
\draw[thick,purple] (E) -- (B) to[bend right] (D) edge[bend right] (C) [point=near start];
\end{tikzpicture}
更新:我们可以\point
这样定义
\def\point[#1] at (#2){\path (#2) [point={#1}]}
然后像这样使用它:
\point["A"below] at (1,1);
更新2:在 PaulGaborit 的评论之后,我添加了every dot
重置字体的样式。这样,如果我们使用的字体的点不在节点的中间,我们有两个选择:
- 强制将“点”字体作为标准字体,
- 或者进行一些移动(以 em 为单位)以将其置于中心。
例如我们可以输入:
\tikzset{
every dot/.style={inner sep=0, outer sep=0,
node font=\usefont{T1}{lmr}{m}{n}\fontsize{10pt}{0pt}\selectfont}
}