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 path
podemos 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,
em
por 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 em
se 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}
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, inherit
caso em que esses parâmetros são herdados do escopo/caminho. Mas apenas o desenho deve ser definido inherit
por 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}
4) O ponto pode ser facilmente nomeado no local para uso coordinate
e se o ponto for desenhado com node
o 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}
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 at
e \coordinate at
(o que não é óbvio porque at
nã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}
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=inherit
oufill=inherit
. Eu defino um novo estiloinherit
que irá redefinir o todopoint
removendo oopacity=1
andfill=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]
, ounode[point, above]{A}
, oucoordinate[point](A)
. Mas não pode ser usado com\coordinate at
ou\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}
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}
Responder2
Observação:Eu criei uma pequena biblioteca tikz com base nesta resposta e nomeada
nicepoints
que 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);
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);
Mas pontos como este são pequenos demais para linhas mais grossas.
\tikz\draw[very thick,red,text=violet] (0,0) -- node{.} (1,0);
Podemos dimensioná-lo usando line width
e 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);
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];
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}
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 pgfonlayer
mas existem duas dificuldades:
Não existem muitas maneiras de inserir automaticamente arquivos
pgfonlayer
. Podemos usar alguns hacks (mas não quero isso). Podemos usarpic
, 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 é usarpath picture
.
Aqui está algo complicado: se desenharmos algo dentropath picture
alterando a camada, o desenho não será cortado porque o recorte será aplicado à camada inicial.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çãopgflinewidth
que 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}
Como ...
- Como usar
point
o estilo?- simplesmente como
(1,1) [point]
, - ou dentro de outro nó como este
node[point,below]{$A$}
.
- simplesmente como
- 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]
- coloque dentro de outro nó como este
- Como definir a
draw
cor 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];
- 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];
- Podemos especificar a cor do ponto como no exemplo a seguir:
- Se a cor for definida no caminho desta forma
\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}
- Como definir a
fill
cor de um ponto?Usandopoint fill
a 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}]
.
- Se quisermos defini-lo para todos os pontos do caminho, podemos fazer
\tikz\path[ultra grosso, vermelho, preenchimento de ponto=azul] (0,0) [ponto] (0,5,0) [ponto={ponto de preenchimento=verde}] (1,0) [ponto];
- 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=.8pt
por exemplo
- Para alterá-lo para um ponto podemos usar
E finalmente, podemos usar every point
para 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 quotes
biblioteca.
Aqui está o código completo para esses quoted
pontos.
\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]
, primeirocoordinate(B)
é inserido e depois o equivalente alabel={[red]$B$}
é usado. - Se você usar
[point="B"']
(com'
), então$B$
não será exibido (é adicionado um rótulo vazio), mascoordinate(B)
será inserido. - Se quisermos apenas um rótulo sem coordenada, podemos usar
[point={label=$B$}]
ounode[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}
ATUALIZAR:Podemos definir \point
assim
\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 dot
um 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}
}