最简单的方法

最简单的方法

我们经常需要画点。每个人都有自己喜欢的方法来画点。

你的是什么?

您是否使用节点、图片、标记......?以某种风格?

一些背景

在 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=inheritfill=inherit。我定义了新样式,它将通过删除和inherit来重新定义整个,但这很丑陋 ;)。pointopacity=1fill=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但有两个困难:

  1. 自动插入 的方法并不多pgfonlayer。我们可以使用一些 hack(但我不想要这个)。我们可以使用pic,但在这种情况下,如果我们想在点之后使用节点,我们必须修复 TikZ 3.0.0 的 bug。我知道的第三个选项是使用path picture
    这里有一些棘手的事情:如果我们path picture通过改变图层在里面绘制一些东西,那么绘图不会被剪裁,因为剪裁是应用于初始图层的。

  2. 当我们改变图层时,“几乎”所有东西都会被重置:线宽、绘制和填充颜色、不透明度。但当我写下这个问题,文本颜色和不透明度不会因图层更改而重置。所以我们唯一需要注意的是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}

在此处输入图片描述

如何 ...

  1. 如何使用point风格?
    • 就像(1,1) [point]
    • 或者在另一个节点内像这样node[point,below]{$A$}
  2. 如何在线段的中间放置一个点?我们不能使用-- [point]语法,但我们可以:
    • 像这样放入另一个节点-- node[point]{}
    • 或者在片段结束后,像这样--(1,1)[point=midway]
  3. 如何设置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}

在此处输入图片描述

  1. 如何设置fill一个点的颜色?通过使用point fill密钥。
    • 如果我们想为路径上的所有点设置它,我们可以这样做\path[point fill=red] ...
    • 如果我们想将其设置为一个点,我们可以这样做[point={point fill=red}]
  \tikz\path[超厚,红色,点填充=蓝色]
    (0,0)[点](.5,0)[点={点填充=绿色}](1,0)[点];

在此处输入图片描述

  1. 如何设置点大小?该点从路径的线宽继承其大小。
    • 为了改变一个点,我们可以使用[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}
}

相关内容