首页 > 解决方案 > Graphviz Dot,不同形状的自指向箭头

问题描述

我正在尝试制作一个具有到某些节点的一些环回的最终状态机。

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

这会生成以下图表

图形

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

标签: graphvizdot

解决方案


令人惊讶的是,增加nodeep也会增加循环大小。
下面减少了nodeep,添加了一个不可见的节点(和 2 条边),并为(一些)边标签添加了一些空格。

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

}

给出这个: 在此处输入图像描述


推荐阅读