A maneira mais fácil

A maneira mais fácil

Muitas vezes precisamos somar pontos. Cada um de nós tem seu método favorito para fazer isso.

Qual é o seu?

Você usa nós, fotos, marcas, ...? Com estilo?

Alguns planos de fundo

No manual tikz/pgf isso é feito com muita frequência por \tikz\fill circle (2pt);.

No exemplo da chave /tikz/insert pathpodemos ver este código

\tikz [c/.style={insert path={circle[radius=2pt]}}]
\draw (0,0) -- (1,1) [c] -- (3,2) [c];

Há também um exemplo paramanipulador de chaves /.pic(p.255) para produzir o mesmo círculo preenchido.

Esses métodos têm a vantagem de serem muito simples, mas não são bons para mim porque:

  • A aparência do ponto depende da ação do comando do caminho (desenhar, preencher, ...).
  • Quando você dimensiona a imagem, a largura da linha não é dimensionada (nem o tamanho da fonte), mas os pontos são dimensionados (você pode facilmente superar isso inserindo o tamanho, empor exemplo).
  • Quando você desenha uma linha depois de desenhar o ponto, a linha é desenhada sobre o ponto.

E mais ...

O que estou olhando

Aqui está uma lista de coisas que eu gostaria de poder fazer com apenas uma definição de "ponto" (ou com mais de uma definição, mas com sintaxe consistente).

Para cada requisito eu dei o teste para passar com o resultado esperado. No teste você pode substituir "ponto" pela sua sintaxe favorita.

1) Os pontos devem ser dimensionados corretamente. E para mim não está claro se o tamanho deve ser proporcional à largura da linha ou ao tamanho da fonte. Provavelmente o melhor é dimensioná-lo usando a largura da linha e então, se necessário, poder (ver ponto 3)) definir o tamanho emse quisermos uma compatibilidade de escala de fonte. O que você acha ?

\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}

insira a descrição da imagem aqui

2) Devemos ser capazes de estilizar os pontos facilmente. Por exemplo, poderíamos dizer algo como "desenhar um ponto vermelho grosso".

(veja o seguinte ponto para o teste)

3) Desenhar, preencher e opacidade dos pontos podem ser definidos, inheritcaso em que esses parâmetros são herdados do escopo/caminho. Mas apenas o desenho deve ser definido inheritpor padrão, os demais padrões (gosto pessoal) devem ser preenchimento=branco e opacidade=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}

insira a descrição da imagem aqui

4) O ponto pode ser facilmente nomeado no local para uso coordinatee se o ponto for desenhado com nodeo nome deverá apontar para o centro do nó e não para o próprio nó.

\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}

insira a descrição da imagem aqui

5) Os pontos podem ser usados ​​em todas as situações onde normalmente podemos usar outro comando para desenhá-los. Isso já está ilustrado nos exemplos anteriores, mas seria bom se pudéssemos usá-lo com \node ate \coordinate at(o que não é óbvio porque atnão altera a coordenada atual).

(veja o seguinte ponto para o teste)

6) Os pontos são desenhados no topo de qualquer linha (na camada de primeiro plano).

\begin{tikzpicture}
  \node[left] {A} at (0,1) "ultra thick point";
  \coordinate["thick point"] (B) at (1,0);
  \draw (A) -- (B);
\end{tikzpicture}

insira a descrição da imagem aqui

7) A solução deve ser não hacky, para que a definição de "ponto" possa ser (espero) compatível com versões futuras do tikz.

Qual é a minha solução pessoal incompleta

Vou publicá-lo como resposta.

Responder1

Método I(usando nó)

\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)}},
}

e algumas coisas extras:

\tikzset{
  colored point/.style = {point={fill=#1}},
  inherit/.style = {point/.style={insert path={node[circle, inner sep={.75\pgflinewidth}, draw, fill, #1]{}}}}
}
  • Satisfaz 1.

  • Satisfaz 2 com um estilo como este[point={fill=red, very thick}]

  • Satisfaz parcialmente 3. Não sei definir draw opacity=inheritou fill=inherit. Eu defino um novo estilo inheritque irá redefinir o todo pointremovendo o opacity=1and fill=white, mas isso é feio;).

  • Satisfaça parcialmente 4: você pode usar point name=A. Eu gostaria de poder usar aspas para dizer algo assim, [point={red, "A"}]mas não sei como fazer isso.

  • Quase satisfaz 5: podemos colocar [ponto] em quase qualquer lugar, como (A) [point], ou node[point, above]{A}, ou coordinate[point](A). Mas não pode ser usado com \coordinate atou \node at(exceto se você se repetir assim \coordinate (A) at (1,1) (A) [point];)

  • FALHAem 6. Eu sei dissoexiste uma solução hacky para colocar o nó na camada, mas isso está em contradição com 7).

  • Satisfaz 7.

O código completo de todos os testes e o resultado

\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}

insira a descrição da imagem aqui


Método II(usando foto)

\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)}}
}
  • FALHAem 1. Não sei como herdar estilos do caminho para a foto. Existe algum estilo como current path style?

  • Satisfaz 2. Igual ao método I.

  • FALHAem 3. Podemos estilizar como no método I, mas porque (1) falha, (3) falha.

  • Satisfaz parcialmente 4. Igual ao método I.

  • FALHAem 5: como há umbug na 'foto'não podemos usar o nó depois dele no PGF 3.0. Quando este bug for corrigido, este método será equivalente ao primeiro neste teste.

  • Satisfaz 6. Este é o principal interesse deste método.

  • Satisfaz 7.

O código completo de todos os testes e o resultado

\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}

insira a descrição da imagem aqui

Responder2

Observação:Eu criei uma pequena biblioteca tikz com base nesta resposta e nomeada nicepointsque está disponível emGitHub.

Finalmente tenho um método para desenhar pontos que satisfazem todos os critérios (de uma forma ligeiramente diferente para a herança de cores) e ainda mais.
Antes de lhe dar a solução completa, vamos começar do início da história.

A maneira mais fácil

Acabei de perceber que provavelmente a melhor maneira de deixar claro é usar um "ponto" .:
\tikz\draw[very thin,red] (0,0) -- node{.} (1,0);
insira a descrição da imagem aqui

A cor é herdada de uma forma muito útil, eu acho. A cor do ponto é igual à cor do texto.
\tikz\draw[very thin,red,text=violet] (0,0) -- node{.} node[above]{A} (1,0); insira a descrição da imagem aqui

Mas pontos como este são pequenos demais para linhas mais grossas.
\tikz\draw[very thick,red,text=violet] (0,0) -- node{.} (1,0); insira a descrição da imagem aqui

Podemos dimensioná-lo usando line widthe automatizar tudo isso criando um estilo.

\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);

insira a descrição da imagem aqui

A escolha sqrté um gosto pessoal: desta forma para linhas mais finas os pontos não são muito pequenos e para linhas mais grossas não são muito grossos. Na verdade, desta forma, a superfície do ponto é proporcional à largura da linha.

Pontos mais sofisticados

E se quisermos preencher o ponto, podemos simplesmente desenhar outro ponto menor sobre o primeiro:

\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];

insira a descrição da imagem aqui

O problema que ainda não foi resolvido é que as linhas traçadas após um ponto podem se sobrepor assim:

\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}

insira a descrição da imagem aqui

A solução

O que queremos é colocar o ponto em uma camada superior, de forma que qualquer linha desenhada depois dele fique abaixo do ponto. Para isso podemos usar pgfonlayermas existem duas dificuldades:

  1. Não existem muitas maneiras de inserir automaticamente arquivos pgfonlayer. Podemos usar alguns hacks (mas não quero isso). Podemos usar pic, mas neste caso temos que corrigir o bug do TikZ 3.0.0 se quisermos poder usar nós após o ponto. E a terceira opção que conheço é usar path picture.
    Aqui está algo complicado: se desenharmos algo dentro path picturealterando a camada, o desenho não será cortado porque o recorte será aplicado à camada inicial.

  2. Quando mudamos de camada, "quase" tudo é zerado: a largura da linha, as cores de desenho e preenchimento, a opacidade. Mas fiquei bem surpreso ao escreveressa questão, que a cor e a opacidade do texto não sejam redefinidas pela alteração da camada. Então a única coisa que temos que cuidar é o line width. Mas isso não é tão difícil porque temos à nossa disposição pgflinewidthque podemos salvar antes da mudança de camada e utilizá-lo na nova camada.

Aqui está a versão "não citada" da solução para a questão inicial:


\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}
        }
      ]{}
    }
  }
}

E um teste aqui:

\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}

insira a descrição da imagem aqui

Como ...

  1. Como usar pointo estilo?
    • simplesmente como (1,1) [point],
    • ou dentro de outro nó como este node[point,below]{$A$}.
  2. Como colocar um ponto no meio do segmento?Não podemos usar -- [point]sintaxe, mas podemos:
    • coloque dentro de outro nó como este-- node[point]{}
    • ou após o final do segmento, assim--(1,1)[point=midway]
  3. Como definir a drawcor de um ponto?Da mesma forma que definimos a cor do texto de todos os nós.
    • Se a cor for definida no caminho desta forma \path[red] ..., ela será herdada pelos pontos do caminho (porque a cor do texto também será definida neste caso).
      \tikz\draw[red] (0,0. -- (1,0) [point=midway]; insira a descrição da imagem aqui
    • Se definirmos apenas a cor de desenho/preenchimento no caminho/escopo, mas não a cor do texto, os pontos serão pretos por padrão (como o texto).
      \tikz\path[draw=red] (0,0) -- (1,0) [point=midway]; insira a descrição da imagem aqui
    • Podemos especificar a cor do ponto como no exemplo a seguir:
  \begin{tikzpicture}[escala=2]
    \draw[fill=azul!14]
      (0,0) [ponto]
        -- (1,0) [ponto=azul]
        -- node[vermelho,ponto,esquerda]{ponto vermelho e texto}
           (1,1) nó[ponto,acima,vermelho]{somente texto vermelho}
        -- (0,1) node[point=red,below]{apenas ponto vermelho};
  \end{tikzpicture}

insira a descrição da imagem aqui

  1. Como definir a fillcor de um ponto?Usando point filla chave.
    • Se quisermos defini-lo para todos os pontos do caminho, podemos fazer\path[point fill=red] ...
    • Se quisermos defini-lo para um ponto, podemos fazer [point={point fill=red}].
  \tikz\path[ultra grosso, vermelho, preenchimento de ponto=azul]
    (0,0) [ponto] (0,5,0) [ponto={ponto de preenchimento=verde}] (1,0) [ponto];

insira a descrição da imagem aqui

  1. Como definir o tamanho do ponto?O ponto herda seu tamanho da largura da linha do caminho.
    • Para alterá-lo para um ponto podemos usar [point=very thick]por exemplo
    • Para defini-lo para todos os pontos do escopo, independentemente da largura da linha, podemos usar, point size=.8ptpor exemplo

E finalmente, podemos usar every pointpara definir o estilo de ponto padrão.

Coisas extras: pontos citados

Se quisermos dizer point="A"e ter as coordenadas definidas (A)e o texto $A$exibido próximo ao ponto, podemos usar a quotesbiblioteca.

Aqui está o código completo para esses quotedpontos.


\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}
        }]{}
    }
  }
}

Como usar os pontos citados

  • Quando você diz [point="B"red], primeiro coordinate(B)é inserido e depois o equivalente a label={[red]$B$}é usado.
  • Se você usar [point="B"'](com '), então $B$não será exibido (é adicionado um rótulo vazio), mas coordinate(B)será inserido.
  • Se quisermos apenas um rótulo sem coordenada, podemos usar [point={label=$B$}]ou node[point,above]{$B$}.

Vamos terminar esta resposta curta com um exemplo de "pontos entre aspas" em uso:

\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}

insira a descrição da imagem aqui

ATUALIZAR:Podemos definir \pointassim

\def\point[#1] at (#2){\path (#2) [point={#1}]}

e então use assim:

\point["A"below] at (1,1);

ATUALIZAÇÃO 2:Após os comentários de PaulGaborit adicionei every dotum estilo que redefiniu a fonte. Desta forma, se usarmos uma fonte com um ponto que não esteja centralizado no meio do nó, teremos duas opções:

  • forçar a fonte "ponto" a ser a fonte padrão,
  • ou faça algum deslocamento (em em) para colocá-lo no centro.

Por exemplo podemos colocar:

\tikzset{
  every dot/.style={inner sep=0, outer sep=0, 
    node font=\usefont{T1}{lmr}{m}{n}\fontsize{10pt}{0pt}\selectfont}
}

informação relacionada