La forma más fácil

La forma más fácil

Muy a menudo necesitamos sacar puntos. Cada uno de nosotros tiene su método favorito para hacer esto.

Lo que es tuyo ?

¿Utilizas nodos, fotografías, marcas,...? ¿En un estilo?

Algunos antecedentes

En el manual de tikz/pgf esto lo hace muy a menudo \tikz\fill circle (2pt);.

En el ejemplo de la clave /tikz/insert pathpodemos ver este código.

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

También hay un ejemplo para elmanejador de claves /.pic(p.255) para producir el mismo círculo relleno.

Estos métodos tienen la ventaja de ser realmente simples, pero no son buenos para mí porque:

  • La apariencia del punto depende de la acción del comando de ruta (dibujar, rellenar, ...).
  • Cuando escalas la imagen, el ancho de la línea no se escala (ni el tamaño de fuente), pero los puntos sí se escalan (puedes superar esto fácilmente poniendo el tamaño, empor ejemplo).
  • Cuando dibujas una línea después de dibujar el punto, la línea se dibuja sobre el punto.

Y más ...

Lo que estoy buscando

Aquí hay una lista de cosas que me gustaría poder hacer con una sola definición de "punto" (o con más de una definición pero con una sintaxis consistente).

Para cada requisito di la prueba para pasar con el resultado esperado. En la prueba puedes reemplazar "punto" por tu sintaxis favorita.

1) Los puntos deben escalarse correctamente. Y para mí esto no está claro si el tamaño debe ser proporcional al ancho de línea o al tamaño de fuente. Probablemente lo mejor es dimensionarlo usando el ancho de línea y luego, si es necesario, poder (ver punto 3) establecer el tamaño emsi queremos compatibilidad de escala de fuente. Qué opinas ?

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

ingrese la descripción de la imagen aquí

2) Deberíamos poder diseñar puntos fácilmente. Por ejemplo, podríamos decir algo como "dibujar un punto rojo grueso".

(ver el siguiente punto para la prueba)

3) Se pueden establecer el dibujo, el relleno y la opacidad de los puntos, inheriten cuyo caso estos parámetros se heredan del alcance/ruta. Pero solo el dibujo debe establecerse inheritde forma predeterminada, los otros valores predeterminados (gusto personal) deben ser relleno=blanco y opacidad=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}

ingrese la descripción de la imagen aquí

4) El punto se puede nombrar fácilmente en el lugar para usarlo coordinatey si el punto se dibuja con nodeel nombre debe apuntar al centro del nodo y no al nodo en sí.

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

ingrese la descripción de la imagen aquí

5) Los puntos se pueden usar en todas las situaciones en las que normalmente podemos usar otro comando para dibujarlos. Esto ya está ilustrado con los ejemplos anteriores, pero sería bueno si pudiéramos usarlo con \node aty \coordinate at(lo cual no es obvio porque atno cambia la coordenada actual).

(ver el siguiente punto para la prueba)

6) Los puntos se dibujan encima de cualquier línea (en la capa de primer plano).

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

ingrese la descripción de la imagen aquí

7) La solución no debe ser complicada, de modo que la definición de "punto" pueda ser (con suerte) compatible con futuras versiones de tikz.

¿Cuál es mi solución personal incompleta?

Lo publicaré como respuesta.

Respuesta1

Método I(usando nodo)

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

y algunas cosas extra:

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

  • Satisface a 2 con un estilo como este[point={fill=red, very thick}]

  • Cumple parcialmente 3. No sé definir draw opacity=inherito fill=inherit. Defino un nuevo estilo inheritque redefinirá todo pointeliminando opacity=1y fill=white, pero esto es feo;).

  • Satisface parcialmente 4: puedes usar point name=A. Me gustaría poder usar comillas para decir algo así, [point={red, "A"}]pero no sé cómo hacerlo.

  • Casi satisface 5: podemos poner [punto] casi en cualquier lugar, como (A) [point], o node[point, above]{A}, o coordinate[point](A). Pero no se puede usar con \coordinate ato \node at(excepto si repites así \coordinate (A) at (1,1) (A) [point];)

  • FALLAel 6. Lo séhay una solución hacky para poner un nodo en una capa, pero esto está en contradicción con 7).

  • Satisface 7.

El código completo de todas las pruebas y el 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}

ingrese la descripción de la imagen aquí


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)}}
}
  • FALLAen 1. No sé cómo heredar estilos de la ruta a la imagen. ¿Hay algún estilo como current path style?

  • Satisface 2. Igual que el método I.

  • FALLAen 3. Podemos diseñar como en el método I, pero como (1) falla, (3) falla.

  • Cumple parcialmente 4. Igual que el método I.

  • FALLAen 5: como hay unerror en 'foto'No podemos usar el nodo después en PGF 3.0. Cuando se solucione este error, este método será equivalente al primero de esta prueba.

  • Satisface 6. Este es el principal interés de este método.

  • Satisface 7.

El código completo de todas las pruebas y el 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}

ingrese la descripción de la imagen aquí

Respuesta2

Nota:Creé una pequeña biblioteca tikz basada en esta respuesta y la nombré nicepointsque está disponible enGitHub.

Finalmente tengo un método para dibujar puntos que satisfacen todos los criterios (de manera ligeramente diferente para la herencia de color) y aún más.
Antes de darte la solución completa, comencemos desde el principio de la historia.

La forma más fácil

Me acabo de dar cuenta de que probablemente la mejor manera de dejar claro un punto es utilizar un "punto" .:
\tikz\draw[very thin,red] (0,0) -- node{.} (1,0);
ingrese la descripción de la imagen aquí

Creo que el color se hereda de una manera muy útil. El color del punto es el mismo que el color del texto.
\tikz\draw[very thin,red,text=violet] (0,0) -- node{.} node[above]{A} (1,0); ingrese la descripción de la imagen aquí

Pero puntos como este son demasiado pequeños para líneas más gruesas.
\tikz\draw[very thick,red,text=violet] (0,0) -- node{.} (1,0); ingrese la descripción de la imagen aquí

Podemos escalarlo usando line widthy automatizar todo esto creando un 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);

ingrese la descripción de la imagen aquí

La elección sqrtes un gusto personal: de este modo, para las líneas más finas los puntos no serán demasiado pequeños, y para las más gruesas, no demasiado gruesos. En realidad, de esta forma, la superficie del punto es proporcional al ancho de la línea.

Puntos más sofisticados

Y si queremos rellenar el punto, simplemente podemos dibujar otro punto más pequeño sobre el primero:

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

ingrese la descripción de la imagen aquí

El problema que aún no está resuelto es que las líneas trazadas después de un punto pueden superponerse así:

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

ingrese la descripción de la imagen aquí

La solución

Lo que queremos es poner el punto en una capa superior, de manera que cualquier línea dibujada después quede debajo del punto. Para esto podemos usar pgfonlayerpero hay dos dificultades:

  1. No hay tantas formas de insertar automáticamente pgfonlayer. Podemos usar algunos trucos (pero no quiero esto). Podemos usar pic, pero en este caso tenemos que corregir el error de TikZ 3.0.0 si queremos poder usar nodos después del punto. Y la tercera opción que conozco es usar path picture.
    Aquí hay algo complicado: si dibujamos algo dentro path picturecambiando la capa, el dibujo no se recorta porque el recorte se aplica a la capa inicial.

  2. Cuando cambiamos de capa, "casi" todo se reinicia: el ancho de línea, los colores de dibujo y relleno, la opacidad. Pero me sorprendí gratamente al escribir.esta pregunta, que el color y la opacidad del texto no se restablecen con el cambio de capa. Entonces lo único que tenemos que cuidar es el line width. Pero esto no es tan difícil porque tenemos a nuestra disposición pgflinewidthalgo que podemos guardar antes del cambio de capa y usarlo en la nueva capa.

Aquí está la versión "sin comillas" de la solución a la pregunta 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}
        }
      ]{}
    }
  }
}

Y una prueba aquí:

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

ingrese la descripción de la imagen aquí

Cómo ...

  1. ¿Cómo utilizar pointel estilo?
    • simplemente como (1,1) [point],
    • o dentro de otro nodo como este node[point,below]{$A$}.
  2. ¿Cómo poner un punto en medio de un segmento?No podemos usar -- [point]sintaxis, pero podemos:
    • poner dentro de otro nodo como este-- node[point]{}
    • o después del final del segmento, así--(1,1)[point=midway]
  3. ¿Cómo configurar el drawcolor de un punto?Como establecemos el color del texto de todos los nodos.
    • Si el color se establece en la ruta de esta manera \path[red] ..., los puntos en la ruta lo heredan (porque el color del texto también se establece en este caso).
      \tikz\draw[red] (0,0. -- (1,0) [point=midway]; ingrese la descripción de la imagen aquí
    • Si configuramos solo el color de dibujo/relleno en la ruta/alcance, pero no el color del texto, los puntos son negros por defecto (como el texto).
      \tikz\path[draw=red] (0,0) -- (1,0) [point=midway]; ingrese la descripción de la imagen aquí
    • Podemos especificar el color del punto como en el siguiente ejemplo:
  \begin{tikzpicture}[escala=2]
    \draw[relleno=azul!14]
      (0,0) [punto]
        -- (1,0) [punto=azul]
        -- nodo[rojo,punto,izquierda]{punto rojo y texto}
           (1,1) nodo[punto,arriba,rojo]{solo texto rojo}
        -- (0,1) nodo[punto=rojo,abajo]{solo punto rojo};
  \end{tikzimagen}

ingrese la descripción de la imagen aquí

  1. ¿Cómo configurar el fillcolor de un punto?Usando point fillclave.
    • Si queremos configurarlo para todos los puntos del camino, podemos hacer\path[point fill=red] ...
    • Si queremos establecerlo para un punto, podemos hacerlo [point={point fill=red}].
  \tikz\path[ultra grueso, rojo, relleno puntual=azul]
    (0,0) [punto] (.5,0) [punto={relleno de punto=verde}] (1,0) [punto];

ingrese la descripción de la imagen aquí

  1. ¿Cómo configurar el tamaño del punto?El punto hereda su tamaño del ancho de línea del camino.
    • Para cambiarlo por un punto podemos usar [point=very thick]por ejemplo
    • Para configurarlo para todos los puntos en el alcance independientemente del ancho de línea podemos usar, point size=.8ptpor ejemplo

Y finalmente, podemos usar every pointpara establecer el estilo de punto predeterminado.

Cosas extra: puntos citados

Si queremos poder decir point="A"y establecer las coordenadas (A)y mostrar el texto $A$al lado del punto, podemos usar la quotesbiblioteca.

Aquí está el código completo para estos quotedpuntos.


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

Cómo utilizar los puntos cotizados

  • Cuando dices [point="B"red], primero coordinate(B)se inserta y luego label={[red]$B$}se usa el equivalente de.
  • Si usa [point="B"'](con '), entonces $B$no se muestra (se agrega una etiqueta vacía), pero coordinate(B)se inserta.
  • Si queremos sólo una etiqueta sin coordenadas, podemos usar [point={label=$B$}]o node[point,above]{$B$}.

Terminemos esta breve respuesta con un ejemplo de "puntos citados" en 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}

ingrese la descripción de la imagen aquí

ACTUALIZAR:Podemos definir \pointasí

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

y luego usarlo así:

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

ACTUALIZACIÓN 2:Después de los comentarios de PaulGaborit agregué every dotun estilo que restablece la fuente. De esta forma si utilizamos una fuente con un punto que no está centrado en el medio del nodo tenemos dos opciones:

  • forzar que la fuente "punto" sea la fuente estándar,
  • o hacer algún desplazamiento (en em) para ponerlo en el centro.

Por ejemplo podemos poner:

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

información relacionada