Desenhe um caminho circulado para um nó em tikz-cd

Desenhe um caminho circulado para um nó em tikz-cd

Gostaria de desenhar uma seta circulada de um nó apontando para si mesmo em um diagrama comutativo. O que consegui alcançar até agora é o seguinte:

insira a descrição da imagem aqui

Existe alguma maneira de fazer os loops parecerem um círculo real?

Abaixo está um MWE.

\documentclass{article}
\usepackage{tikz-cd}
\begin{document}

\begin{tikzcd}[column sep=large]
    C & D
    \arrow["{F}", bend left = 30, from=1-1, to=1-2]
    \arrow["{G}", bend left = 30, from=1-2, to=1-1]
    \arrow[""{name=0, anchor=center, inner sep=0}, "{G} \,\circ\, {F}", from=1-1, to=1-1, out=-145, in=145, loop, distance=4em]
    \arrow[""{name=1, anchor=center, inner sep=0}, "{F} \,\circ\, {G}", from=1-2, to=1-2, out=35, in=-35, loop, distance=4em]
    \arrow["\theta", shorten <=3pt, shorten >=3pt, Rightarrow, from=0, to=1-1]
    \arrow["\phi"', shorten <=3pt, shorten >=3pt, Rightarrow, from=1, to=1-2]
\end{tikzcd}

\end{document}

Responder1

Conforme explicado na seção 3.2 do tikz-cdmanual, pode-se usar to pathpara ajustar caminhos. Para encontrar o ângulo inicial e final apropriado do arco, pode-se medir o nó com a calcbiblioteca. As setas duplas também são desenhadas através de to path. Esses caminhos são horizontais por construções. Para preservar os rótulos das arestas, é necessário adicionar \tikztonodesao argumento de to path.

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}
\begin{document}
\def\myradius{1.5em}
\begin{tikzcd}[column sep=large]
    C & D
    \arrow["{F}", bend left = 30, from=1-1, to=1-2]
    \arrow["{G}", bend left = 30, from=1-2, to=1-1]
    \arrow["{G} \,\circ\, {F}","\mathstrut"{name=0, anchor=center, inner sep=0}, from=1-1, to=1-1,to path={%
        let \p1=($(\tikztostart.north)-(\tikztostart.south)$),\n1={scalar(asin(0.5*\y1/\myradius))} in 
        (\tikztostart.south)arc[start angle=-\n1,end angle=-360+\n1,radius=\myradius]\tikztonodes (\tikztotarget)}]
    \arrow["{F} \,\circ\, {G}","\mathstrut"{name=1, anchor=center, inner sep=0}, from=1-2, to=1-2,to path={%
    let \p1=($(\tikztostart.north)-(\tikztostart.south)$),\n1={scalar(asin(0.5*\y1/\myradius))} in 
    (\tikztostart.north)arc[start angle=180-\n1,end angle=-180+\n1,radius=\myradius]\tikztonodes (\tikztotarget)}]
    \arrow["\theta", shorten <=3pt, shorten >=3pt, Rightarrow, from=0, to=1-1, to path={(\tikztostart|-\tikztotarget) -- (\tikztotarget)\tikztonodes}]
    \arrow["\phi"', shorten <=3pt, shorten >=3pt, Rightarrow, from=1, to=1-2, to path={(\tikztostart|-\tikztotarget) -- (\tikztotarget)\tikztonodes}]
\end{tikzcd}

\end{document}

insira a descrição da imagem aqui

Responder2

Abaixo está uma versão ligeiramente modificada da resposta aceita. Nas setas horizontais, alinhei a seta com a altura \tikztostartem vez de \tikztotargetpara torná-la mais alta. Porém, para obter o ponto final da seta corretamente, adicionei manualmente um deslocamento horizontal de 3 pontos à coordenada, o que parece bastante feio no código.

insira a descrição da imagem aqui

\documentclass{article}
\usepackage{tikz-cd}
\usetikzlibrary{calc}
\begin{document}

\begin{tikzcd}[column sep=large]
    C & D
    \arrow["{F}", bend left = 30, from=1-1, to=1-2]
    \arrow["{G}", bend left = 30, from=1-2, to=1-1]
    \arrow["{G} \,\circ\, {F}","\mathstrut"{name=0, anchor=center, inner sep=0}, from=1-1, to=1-1,
        to path={let \p1=($(\tikztostart.north)-(\tikztostart.south)$),\n1={scalar(asin(0.5*\y1/1.5em))} in
            (\tikztostart.south) arc[start angle=-\n1,end angle=-360+\n1,radius=1.5em]\tikztonodes (\tikztotarget)}]
    \arrow["{F} \,\circ\, {G}","\mathstrut"{name=1, anchor=center, inner sep=0}, from=1-2, to=1-2,
        to path={let \p1=($(\tikztostart.north)-(\tikztostart.south)$),\n1={scalar(asin(0.5*\y1/1.5em))} in
            (\tikztostart.north) arc[start angle=180-\n1,end angle=-180+\n1,radius=1.5em]\tikztonodes (\tikztotarget)}]
    \arrow["\theta", shorten <=3pt, shorten >=3pt, Rightarrow, from=0, to=1-1, to path={(\tikztostart) -- ($(\tikztotarget|-\tikztostart)+(-3pt,0)$) \tikztonodes }]
    \arrow["\phi"', shorten <=3pt, shorten >=3pt, Rightarrow, from=1, to=1-2, to path={(\tikztostart) -- ($(\tikztotarget|-\tikztostart)+(3pt,0)$) \tikztonodes }]
\end{tikzcd}

\end{document}

Responder3

TikZ-CD usa o asymmetrical rectangleque tem sua âncora central não no centro vertical real, mas em uma distância fixa ( axis_height) acima da linha de base, de modo que as setas entre os nós da mesma linha sejam desenhadas como setas no modo matemático. (Isso também faz com que eles estejam sempre na horizontal.)

Neste código o segmento circular é desenhado de forma que passe pelo centro do início (e não toque na âncora norte ou sul). Para isso são utilizadas spath3as bibliotecas e . intersectionsFazer isso matematicamente para qualquer direção que não seja horizontal/vertical envolve muita matemática para eu querer descobrir isso. Tecnicamente, a spath3biblioteca é usada apenas para remover o segmento do círculo que fica dentro do nó inicial, os ângulos inicial e final apropriados poderiam ter sido determinados a partir do conhecimento dos pontos de interseção.

Adicionei duas maneiras ao TikZ-CD de usar o circle around rect nodeestilo:

  1. circle = <padding>( <padding>o padrão é .5exaproximadamente metade das seções internas dos nós em um TikZ-CD)

    Se o preenchimento for zero, isso determinará o raio de tal forma que o círculo tocaria apenas a coordenada/nó alvo.

  2. circle to = <angle>desenha um círculo ao redor da coordenada inicial que “aponta” na direção <angle>e tem diâmetro circle to distance.

Código

\documentclass[tikz]{standalone}
\usepackage{tikz-cd}
\usetikzlibrary{calc, intersections, spath3}
\tikzset{
  circle around rect node/.style n args={3}{insert path={%
    % #1 = node name, #2 = angle, #3 = distance
    (#1.center) edge[
      path only, spath/save global=carn@circle,
      to path={arc[start angle={(180-(#2))}, delta angle=360, radius={(#3)/2}]}](#1)
    (#1.south west) edge[
      path only, spath/save global=carn@rect,
      to path={rectangle(\tikztotarget)}](#1.north east)
    [spath/.cd,
      split at intersections with={carn@circle}{carn@rect},
      remove components={carn@circle}{1,3},
      use=carn@circle]}}}
\tikzcdset{
  circle to distance/.initial=3em,
  circle to/.style={to path={
    [circle around rect node/.expanded={\tikztostart}{#1}
      {\pgfkeysvalueof{/tikz/commutative diagrams/circle to distance}}]
    \tikztonodes}},
  circle/.default=.5ex,
  circle/.style={
    execute at begin to={%
      \path[path only](\tikztostart)
        --coordinate[at end](tikzcd@circleend)(\tikztotarget);},
    to path={
      let \p{tikzcd@diff} = ($(tikzcd@circleend)-(\tikztostart)$) in
      [circle around rect node/.expanded={\tikztostart}
                                         {atan2(\y{tikzcd@diff},\x{tikzcd@diff})}
                                         {veclen(\p{tikzcd@diff})+(#1)}]
      \tikztonodes}}}
\tikzset{
  cd/.code=\tikzcdset{#1},
  cd node/.style={font=,cd=every cell,name={#1}}}
\begin{document}

\begin{tikzcd}[
  column sep=large, bend angle=30,
%  /tikz/column 1/.append style={cd={column sep=normal}},
%  /tikz/column 3/.append style={cd={column sep=normal}},
]
  G \circ F \arrow[r, "\theta", Rightarrow]
& C         \arrow[r, "F", bend left]
            \arrow[l, circle]
& D         \arrow[l, "G", bend left]
            \arrow[r, circle]
& F \circ G \arrow[l, "\phi"', Rightarrow]
\end{tikzcd}

\begin{tikzcd}[column sep=large, bend angle=30]
  C \arrow[r, "F", bend left]
    \arrow[circle to=180, "G \circ F"' cd node=GF]
    \arrow[Rightarrow, shorten <=.8ex, from=GF, "\theta"]
& D \arrow[l, "G", bend left]
    \arrow[circle to=  0, "F \circ G"' cd node=FG]
    \arrow[Rightarrow, shorten <=.8ex, from=FG, "\phi"']
\end{tikzcd}

\begin{tikzpicture}
\node[draw] (A) {A};
\foreach[count=\i] \ang in {0, 45, ..., 359}
  \draw[red!\i0!blue, circle around rect node={A}{\ang}{1cm}];
\end{tikzpicture}
\end{document}

Saída

insira a descrição da imagem aqui

insira a descrição da imagem aqui

informação relacionada