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:
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-cd
manual, pode-se usar to path
para ajustar caminhos. Para encontrar o ângulo inicial e final apropriado do arco, pode-se medir o nó com a calc
biblioteca. 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 \tikztonodes
ao 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}
Responder2
Abaixo está uma versão ligeiramente modificada da resposta aceita. Nas setas horizontais, alinhei a seta com a altura \tikztostart
em vez de \tikztotarget
para 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.
\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 rectangle
que 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 spath3
as bibliotecas e . intersections
Fazer isso matematicamente para qualquer direção que não seja horizontal/vertical envolve muita matemática para eu querer descobrir isso. Tecnicamente, a spath3
biblioteca é 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 node
estilo:
circle = <padding>
(<padding>
o padrão é.5ex
aproximadamente 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.
circle to = <angle>
desenha um círculo ao redor da coordenada inicial que “aponta” na direção<angle>
e tem diâmetrocircle 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}