Graphviz点,不同形状的自指箭头

Graphviz点,不同形状的自指箭头,graphviz,dot,Graphviz,Dot,我正在尝试创建一个finate状态机,其中包含一些到特定节点的环回 digraph g{ rankdir=TB; forcelabels=true; ranksep=1.5; pad=0.5; nodesep=2; node[shape=circle]; A[label="Wait for call\n0 from above"]; B[label="

我正在尝试创建一个finate状态机,其中包含一些到特定节点的环回

digraph g{
      rankdir=TB;
      forcelabels=true;
      ranksep=1.5;
      pad=0.5;
      nodesep=2;
    
      node[shape=circle];
    
      A[label="Wait for call\n0 from above"];
      B[label="Wait for\nACK 0 || ACK 1"];
      E[label="Wait for call\n1 from above"];
      F[label="Wait for\nACk 2 || ACK 3"];
    
      C[label="Wait for ACK 0"];
      D[label="Wait for ACK 1"];
      G[label="Wait for ACK 2"];
      H[label="Wait for ACk 3"];
    
      A -> B[label=" rdt_send(data)  \n  sndpkt=make_pkt(0,data,checksum)  \n  udt_send(sndpkt)  \n  start_timer 1 and 2  \n"];
     
      B -> D[xlabel="  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,0)  \n  stop_timer 1"];
      B -> C[xlabel="  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,1)  \n  stop_timer 2"];
      C -> E[xlabel="  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,0)  \n  stop_timer 2"];
      D -> E[label= "  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,1)  \n  stop_timer 1"];
    
      C:nw -> C:nw[constraint=none,xlabel="PLACEHOLDER"];
      C:sw -> C:sw[constraint=none,xlabel="PLACEHOLDER"];
    
      D:ne -> D:ne[constraint=none,xlabel="PLACEHOLDER"];
      D:se -> D:se[constraint=none,xlabel="PLACEHOLDER"];
    
      E -> F[constraint=none, xlabel="  \n  rdt_send(data)  \n  sndpkt=make_pkt(1,data,checksum)  \n  udt_send(sndpkt)\n  start_timer "];
     
      F -> G[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,3)  \n  stop_timer "];
      F -> H[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,2)  \n  stop_timer "];
      G -> A[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,3)  \n  stop_timer "];
      H -> A[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,2)  \n  stop_timer "];
    
      G:sw -> G:sw[constraint=none,xlabel="PLACEHOLDER"];
      G:nw -> G:nw[constraint=none,xlabel="PLACEHOLDER"];
    
      H:se -> H:se[constraint=none,xlabel="PLACEHOLDER"];
      H:ne -> H:ne[constraint=none,xlabel="PLACEHOLDER"];
    
      A:w -> A:w[label="  rdt_rcv(rcvpkt)  \n  ᐱ"];
    
      B:n -> B:n[label="  rdt_rcv(rcvpkt)  \n  && corrupt(rcvpkt)  \n  ||  isAck(rcvpkt,1)  \n  ᐱ "];
      B:e -> B:e[label="  timeout  \n  udt_send(sndpkt)  \n  start_timer  \n "];
    
      E:e -> E:e[label="  rdt_rcv(rcvpkt)\nᐱ "];
    
      F:s -> F:s[label="  rdt_rcv(rcvpkt)  \n  && corrupt(rcvpkt)  \n  || isAck(rcvpkt,0)  \n  ᐱ "]; 
      F:w -> F:w[label="  timeout  \n  udt_send(sndpkt)  \n  start_timer "];
    
      {rank=same;A;B}
      {rank=same;C,D,G,H}
      {rank=same;E;F}
}
然后我使用命令

dot -Tsvg in.dot -o out.svg
这将生成以下图表


请注意四个中间节点上的不同环回边及其标签位置。即使我增加了nodesep属性,边缘也似乎被拉伸了。如何有效地解决此问题?

令人惊讶的是,增加nodesep也会增加循环大小。
以下内容减少了节点集,添加了一个不可见的节点(和两条边),并向(部分)边标签添加了一些空格

digraph g{
  rankdir=TB;
  forcelabels=true;
  ranksep=1.5;
  pad=0.5;
  nodesep=1;  // was 2

  node[shape=circle];

  A[label="Wait for call\n0 from above"];
  B[label="Wait for\nACK 0 || ACK 1"];
  E[label="Wait for call\n1 from above"];
  F[label="Wait for\nACk 2 || ACK 3"];

  C[label="Wait for ACK 0"];
  D[label="Wait for ACK 1"];
  G[label="Wait for ACK 2"];
  H[label="Wait for ACk 3"];

  A -> B[label=" rdt_send(data)  \n  sndpkt=make_pkt(0,data,checksum)  \n  udt_send(sndpkt)  \n  start_timer 1 and 2  \n"];
 
  B -> D[xlabel="  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,0)  \n  stop_timer 1"];
  B -> C[xlabel="  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,1)  \n  stop_timer 2"];
  C -> E[xlabel="  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,0)  \n  stop_timer 2"];
  D -> E[label= "  rdt_rcv(rcvpkt) && \n notcorrupt(rcvpkt) \n && isAck(rcvpkt,1)  \n  stop_timer 1"];

  C:nw -> C:nw[constraint=none,xlabel="1PLACEHOLDER"];
  C:sw -> C:sw[constraint=none,xlabel="2PLACEHOLDER       "];  // spaces

  D:ne -> D:ne[constraint=none,xlabel="    3PLACEHOLDER"];     // spaces
  D:se -> D:se[constraint=none,xlabel="4PLACEHOLDER"];

  E -> F[constraint=none, xlabel="  \n  rdt_send(data)  \n  sndpkt=make_pkt(1,data,checksum)  \n  udt_send(sndpkt)\n  start_timer "];
 
  F -> G[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,3)  \n  stop_timer "];
  F -> H[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,2)  \n  stop_timer "];
  G -> A[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,3)  \n  stop_timer "];
  H -> A[xlabel="  rdt_rcv(rcvpkt) \n  && notcorrupt(rcvpkt)  \n  && isAck(rcvpkt,2)  \n  stop_timer "];

  G:sw -> G:sw[constraint=none,xlabel="5PLACEHOLDER"];
  G:nw -> G:nw[constraint=none,xlabel="6PLACEHOLDER"];

  H:se -> H:se[constraint=none,xlabel="     7PLACEHOLDER"]; // spaces
  H:ne -> H:ne[constraint=none,xlabel="8PLACEHOLDER"];

  A:w -> A:w[label="  rdt_rcv(rcvpkt)  \n  ᐱ"];

  B:n -> B:n[label="  rdt_rcv(rcvpkt)  \n  && corrupt(rcvpkt)  \n  ||  isAck(rcvpkt,1)  \n  ᐱ "];
  B:e -> B:e[label="  timeout  \n  udt_send(sndpkt)  \n  start_timer  \n "];

  E:e -> E:e[label="  rdt_rcv(rcvpkt)\nᐱ "];

  F:s -> F:s[label="  rdt_rcv(rcvpkt)  \n  && corrupt(rcvpkt)  \n  || isAck(rcvpkt,0)  \n  ᐱ "]; 
  F:w -> F:w[label="  timeout  \n  udt_send(sndpkt)  \n  start_timer "];

  {rank=same;A;B}
  {rank=same;C,D,G,H   BOGUS[style=invis]}
  {rank=same;E;F}
  edge [style=invis]
  H -> BOGUS -> C
}

鉴于此: