Самый простой способ

Самый простой способ

Очень часто нам нужно рисовать точки. У каждого из нас есть свой любимый метод делать это.

Что твое ?

Используете ли вы узлы, рисунки, отметки, ... ? В стиле ?

Некоторые фоны

В руководстве 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" на ваш любимый синтаксис.

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) Draw, fill и opacity точек могут быть установлены в, inheritв этом случае эти параметры наследуются от scope/path. Но только draw должен быть установлен inheritпо умолчанию, другие значения по умолчанию (по личному вкусу) должны быть fill=white и opacity=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

Метод 1(используя узел)

\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: мы можем поставить [точку] почти где угодно, например (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}

введите описание изображения здесь


Метод 2(используя картинку)

\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. Я не знаю как наследовать стили от path к pic. Есть ли какой-то стиль типа current path style?

  • Удовлетворяет 2. То же, что и метод I.

  • НЕУДАЧНОна 3. Мы можем стилизовать как в методе I, но поскольку (1) не выполняется, (3) не выполняется.

  • Частично удовлетворяет 4. То же, что и метод I.

  • НЕУДАЧНОна 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. Мы можем использовать некоторые хаки (но я этого не хочу). Мы можем использовать pic, но в этом случае нам придется исправить ошибку TikZ 3.0.0, если мы хотим иметь возможность использовать узлы после точки. И третий вариант, который я знаю, — использовать 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}[масштаб=2]
    \draw[fill=синий!14]
      (0,0) [точка]
        -- (1,0) [точка=синий]
        -- узел[красный,точка,левый]{красная точка и текст}
           (1,1) узел[точка,выше,красный]{только красный текст}
        -- (0,1) узел[точка=красная,ниже]{только красная точка};
  \end{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}
}

Связанный контент