graphviz: circular layout while preserving node order

前端 未结 2 681
春和景丽
春和景丽 2020-12-29 11:48

Hey
I want to plot a graph of 128 nodes (labeled 1 to 128) in graphviz using circular layout. Circo does this, but I want the nodes to be placed in order of their label

相关标签:
2条回答
  • 2020-12-29 12:12

    I think, the only solution is to use a neato' layout and the pos attribut.

    To do what you want, I start by creating a small Ruby script to calculate all nodes positions :

    radius = 20
    
    (1..128).each do |i|
      x = Math.cos(((Math::PI*2)/128.0)*i.to_f)*radius
      y = Math.sin(((Math::PI*2)/128.0)*i.to_f)*radius
      puts "  #{i}[label=\"#{i}\", pos=\"#{x},#{y}!\", shape = \"square\"];"
    end
    

    Then, I put the result in the graphviz script :

    digraph G {
      layout="neato"
      1[label="1", pos="19.9759091241034,0.98135348654836!", shape = "square"];
      2[label="2", pos="19.9036945334439,1.96034280659121!", shape = "square"];
      3[label="3", pos="19.7835301992956,2.93460948910723!", shape = "square"];
      4[label="4", pos="19.6157056080646,3.90180644032256!", shape = "square"];
      5[label="5", pos="19.4006250638909,4.85960359806528!", shape = "square"];
      6[label="6", pos="19.1388067146442,5.80569354508925!", shape = "square"];
      7[label="7", pos="18.8308813036604,6.7377970678444!", shape = "square"];
      8[label="8", pos="18.4775906502257,7.6536686473018!", shape = "square"];
      9[label="9", pos="18.0797858624689,8.55110186860564!", shape = "square"];
      10[label="10", pos="17.6384252869671,9.42793473651995!", shape = "square"];
      11[label="11", pos="17.1545722000054,10.2820548838644!", shape = "square"];
      12[label="12", pos="16.6293922460509,11.111404660392!", shape = "square"];
      13[label="13", pos="16.0641506296129,11.9139860898487!", shape = "square"];
      14[label="14", pos="15.4602090672547,12.6878656832729!", shape = "square"];
      15[label="15", pos="14.8190225070992,13.4311790969404!", shape = "square"];
      16[label="16", pos="14.142135623731,14.1421356237309!", shape = "square"];
      17[label="17", pos="13.4311790969404,14.8190225070992!", shape = "square"];
      18[label="18", pos="12.6878656832729,15.4602090672547!", shape = "square"];
      19[label="19", pos="11.9139860898487,16.0641506296129!", shape = "square"];
      20[label="20", pos="11.111404660392,16.6293922460509!", shape = "square"];
      21[label="21", pos="10.2820548838644,17.1545722000054!", shape = "square"];
      22[label="22", pos="9.42793473651996,17.6384252869671!", shape = "square"];
      23[label="23", pos="8.55110186860564,18.0797858624689!", shape = "square"];
      24[label="24", pos="7.6536686473018,18.4775906502257!", shape = "square"];
      25[label="25", pos="6.7377970678444,18.8308813036604!", shape = "square"];
      26[label="26", pos="5.80569354508925,19.1388067146442!", shape = "square"];
      27[label="27", pos="4.85960359806528,19.4006250638909!", shape = "square"];
      28[label="28", pos="3.90180644032257,19.6157056080646!", shape = "square"];
      29[label="29", pos="2.93460948910723,19.7835301992956!", shape = "square"];
      30[label="30", pos="1.96034280659122,19.9036945334439!", shape = "square"];
      31[label="31", pos="0.981353486548363,19.9759091241034!", shape = "square"];
      32[label="32", pos="1.22464679914735e-15,20.0!", shape = "square"];
      33[label="33", pos="-0.98135348654836,19.9759091241034!", shape = "square"];
      34[label="34", pos="-1.96034280659121,19.9036945334439!", shape = "square"];
      35[label="35", pos="-2.93460948910723,19.7835301992956!", shape = "square"];
      36[label="36", pos="-3.90180644032256,19.6157056080646!", shape = "square"];
      37[label="37", pos="-4.85960359806528,19.4006250638909!", shape = "square"];
      38[label="38", pos="-5.80569354508924,19.1388067146442!", shape = "square"];
      39[label="39", pos="-6.7377970678444,18.8308813036604!", shape = "square"];
      40[label="40", pos="-7.65366864730179,18.4775906502257!", shape = "square"];
      41[label="41", pos="-8.55110186860564,18.0797858624689!", shape = "square"];
      42[label="42", pos="-9.42793473651995,17.6384252869671!", shape = "square"];
      43[label="43", pos="-10.2820548838644,17.1545722000054!", shape = "square"];
      44[label="44", pos="-11.111404660392,16.6293922460509!", shape = "square"];
      45[label="45", pos="-11.9139860898487,16.0641506296129!", shape = "square"];
      46[label="46", pos="-12.6878656832729,15.4602090672547!", shape = "square"];
      47[label="47", pos="-13.4311790969404,14.8190225070992!", shape = "square"];
      48[label="48", pos="-14.1421356237309,14.142135623731!", shape = "square"];
      49[label="49", pos="-14.8190225070992,13.4311790969404!", shape = "square"];
      50[label="50", pos="-15.4602090672547,12.6878656832729!", shape = "square"];
      51[label="51", pos="-16.0641506296129,11.9139860898487!", shape = "square"];
      52[label="52", pos="-16.6293922460509,11.111404660392!", shape = "square"];
      53[label="53", pos="-17.1545722000054,10.2820548838644!", shape = "square"];
      54[label="54", pos="-17.6384252869671,9.42793473651996!", shape = "square"];
      55[label="55", pos="-18.0797858624689,8.55110186860564!", shape = "square"];
      56[label="56", pos="-18.4775906502257,7.6536686473018!", shape = "square"];
      57[label="57", pos="-18.8308813036604,6.73779706784441!", shape = "square"];
      58[label="58", pos="-19.1388067146442,5.80569354508925!", shape = "square"];
      59[label="59", pos="-19.4006250638909,4.85960359806528!", shape = "square"];
      60[label="60", pos="-19.6157056080646,3.90180644032257!", shape = "square"];
      61[label="61", pos="-19.7835301992956,2.93460948910724!", shape = "square"];
      62[label="62", pos="-19.9036945334439,1.96034280659122!", shape = "square"];
      63[label="63", pos="-19.9759091241034,0.98135348654836!", shape = "square"];
      64[label="64", pos="-20.0,2.44929359829471e-15!", shape = "square"];
      65[label="65", pos="-19.9759091241034,-0.981353486548354!", shape = "square"];
      66[label="66", pos="-19.9036945334439,-1.96034280659121!", shape = "square"];
      67[label="67", pos="-19.7835301992956,-2.93460948910723!", shape = "square"];
      68[label="68", pos="-19.6157056080646,-3.90180644032257!", shape = "square"];
      69[label="69", pos="-19.4006250638909,-4.85960359806528!", shape = "square"];
      70[label="70", pos="-19.1388067146442,-5.80569354508924!", shape = "square"];
      71[label="71", pos="-18.8308813036604,-6.7377970678444!", shape = "square"];
      72[label="72", pos="-18.4775906502257,-7.65366864730179!", shape = "square"];
      73[label="73", pos="-18.0797858624689,-8.55110186860564!", shape = "square"];
      74[label="74", pos="-17.6384252869671,-9.42793473651995!", shape = "square"];
      75[label="75", pos="-17.1545722000054,-10.2820548838644!", shape = "square"];
      76[label="76", pos="-16.6293922460509,-11.111404660392!", shape = "square"];
      77[label="77", pos="-16.0641506296129,-11.9139860898487!", shape = "square"];
      78[label="78", pos="-15.4602090672547,-12.6878656832729!", shape = "square"];
      79[label="79", pos="-14.8190225070992,-13.4311790969404!", shape = "square"];
      80[label="80", pos="-14.142135623731,-14.1421356237309!", shape = "square"];
      81[label="81", pos="-13.4311790969404,-14.8190225070992!", shape = "square"];
      82[label="82", pos="-12.6878656832729,-15.4602090672547!", shape = "square"];
      83[label="83", pos="-11.9139860898487,-16.0641506296129!", shape = "square"];
      84[label="84", pos="-11.111404660392,-16.6293922460509!", shape = "square"];
      85[label="85", pos="-10.2820548838644,-17.1545722000054!", shape = "square"];
      86[label="86", pos="-9.42793473651996,-17.6384252869671!", shape = "square"];
      87[label="87", pos="-8.55110186860565,-18.0797858624689!", shape = "square"];
      88[label="88", pos="-7.65366864730181,-18.4775906502257!", shape = "square"];
      89[label="89", pos="-6.7377970678444,-18.8308813036604!", shape = "square"];
      90[label="90", pos="-5.80569354508925,-19.1388067146442!", shape = "square"];
      91[label="91", pos="-4.85960359806528,-19.4006250638909!", shape = "square"];
      92[label="92", pos="-3.90180644032257,-19.6157056080646!", shape = "square"];
      93[label="93", pos="-2.93460948910725,-19.7835301992956!", shape = "square"];
      94[label="94", pos="-1.96034280659121,-19.9036945334439!", shape = "square"];
      95[label="95", pos="-0.981353486548361,-19.9759091241034!", shape = "square"];
      96[label="96", pos="-3.67394039744206e-15,-20.0!", shape = "square"];
      97[label="97", pos="0.981353486548353,-19.9759091241034!", shape = "square"];
      98[label="98", pos="1.9603428065912,-19.9036945334439!", shape = "square"];
      99[label="99", pos="2.93460948910724,-19.7835301992956!", shape = "square"];
      100[label="100", pos="3.90180644032257,-19.6157056080646!", shape = "square"];
      101[label="101", pos="4.85960359806528,-19.4006250638909!", shape = "square"];
      102[label="102", pos="5.80569354508924,-19.1388067146442!", shape = "square"];
      103[label="103", pos="6.73779706784439,-18.8308813036604!", shape = "square"];
      104[label="104", pos="7.6536686473018,-18.4775906502257!", shape = "square"];
      105[label="105", pos="8.55110186860564,-18.0797858624689!", shape = "square"];
      106[label="106", pos="9.42793473651995,-17.6384252869671!", shape = "square"];
      107[label="107", pos="10.2820548838644,-17.1545722000054!", shape = "square"];
      108[label="108", pos="11.111404660392,-16.6293922460509!", shape = "square"];
      109[label="109", pos="11.9139860898487,-16.0641506296129!", shape = "square"];
      110[label="110", pos="12.6878656832729,-15.4602090672547!", shape = "square"];
      111[label="111", pos="13.4311790969404,-14.8190225070992!", shape = "square"];
      112[label="112", pos="14.1421356237309,-14.142135623731!", shape = "square"];
      113[label="113", pos="14.8190225070992,-13.4311790969404!", shape = "square"];
      114[label="114", pos="15.4602090672547,-12.6878656832729!", shape = "square"];
      115[label="115", pos="16.0641506296129,-11.9139860898487!", shape = "square"];
      116[label="116", pos="16.6293922460509,-11.111404660392!", shape = "square"];
      117[label="117", pos="17.1545722000054,-10.2820548838644!", shape = "square"];
      118[label="118", pos="17.6384252869671,-9.42793473651996!", shape = "square"];
      119[label="119", pos="18.0797858624689,-8.55110186860565!", shape = "square"];
      120[label="120", pos="18.4775906502257,-7.65366864730181!", shape = "square"];
      121[label="121", pos="18.8308813036604,-6.7377970678444!", shape = "square"];
      122[label="122", pos="19.1388067146442,-5.80569354508925!", shape = "square"];
      123[label="123", pos="19.4006250638909,-4.85960359806528!", shape = "square"];
      124[label="124", pos="19.6157056080646,-3.90180644032257!", shape = "square"];
      125[label="125", pos="19.7835301992956,-2.93460948910725!", shape = "square"];
      126[label="126", pos="19.9036945334439,-1.96034280659121!", shape = "square"];
      127[label="127", pos="19.9759091241034,-0.981353486548362!", shape = "square"];
      128[label="128", pos="20.0,-4.89858719658941e-15!", shape = "square"];
    
      25->42 
      25->71 
      26->25 
      26->40 
      27->30 
      29->25 
      29->26 
      29->27 
      29->30 
      29->32 
      29->40 
      29->80 
      32->39 
      33->28 
      33->44 
      33->74 
      37->34 
      37->66 
      37->69 
      38->60 
      38->107 
      40->100 
      47->30 
      48->35 
      48->36 
      50->35 
      50->63 
      51->50 
      51->96 
      52->50 
      53->51 
      53->96 
      59->50 
      59->51 
      59->52 
      59->60 
      60->50 
      60->63 
      60->95 
      67->74 
      67->114 
      68->74 
      70->74 
      70->126 
      71->74 
      71->86 
      72->70 
      75->39 
      77->81 
      79->73 
      80->84 
      82->78 
      82->114 
      86->115 
      87->115 
      87->121 
      91->69 
      91->87 
      96->30 
      96->114 
      101->107 
      102->108 
      107->75 
      107->78 
      108->95 
      108->103 
      111->80 
      111->114 
      114->128 
      115->114 
      118->128 
      119->103 
      121->72 
      123->116 
      125->80 
      126->122 
      128->96 
    }
    
    0 讨论(0)
  • 2020-12-29 12:16

    Generating your own node positions is the best solution outside of coming up with a better algorithm or adding weighting to circo by altering the graphviz source.

    However, it does defeat the purpose of generating arbitrary graphs with graphviz. This script will use graphviz itself to generate a circle of arbitrary size with user defined formatting, hardcode the position, and then add edges across the center.

    #!/bin/bash
    # loopgen.sh- generates a plain graphviz loop then hardcodes it and adds to it
    # input file format -
    #   num of nodes
    #   prefixes for the generated file (format information, labels)
    #   (blank)
    #   postfixes for the final file (extra connections, inputscale)
    # output - graph with nodes0 to nodex
    file=$(<$1)
    # trim filename to function name
    fun=${1##*/}
    fun=${fun%%.*}
    # gen is generation function
    gen="digraph $fun
    {
        layout=circo;"
    # fin is final function
    fin=""
    # get the number of inputs
    num=$(head -n 1 <<< "$file")
    if [ $num -lt 2 ]; then
        echo "Bad number of inputs."
        exit -1
    fi
    # increment the lines of the file
    file=$(tail -n +2 <<< "$file")
    # add all lines up to the first blank line
    gen="$gen
        $(printf "$file" | awk '!p;/^$/{p=1}')"
    # remove all lines before the first blank line
    file=$(printf "$file" | awk '/^$/{p=1}p')
    # begin producing character-based nodes
    i=1
    gen="$gen
        node0 "
    while [ $i -lt $num ]; do
        gen="$gen -> node$i"
        let i=i+1
    done
    #finish the loop
    gen="$gen -> node0;
    }"
    # generate, replace circo layout reference, make positions absolute
    gen=$(printf "$gen" | dot |
    sed -e 's/layout=circo/layout=neato/' -e 's/\(pos=".*\)"/\1!"/g')
    # remove trailing brace
    gen=$(printf "$gen" | head -n -1)
    
    fin="/* generated with loopgen.sh */
        $gen
        $file
    }"
    
    printf "$fin"
    exit 0
    

    Here is an example file.

    6
    node0 [label="bop it"];
    node1 [label="twist it"];
    node2 [label="pull it"];
    node3 [label="flick it"];
    node4 [label="spin it"];
    node5 [label="throw it away"];
    
    node2 -> node5 [constraint=false,weight=0];
    // this keeps the program from running out of memory
    inputscale=72
    

    Running

    loopgen.sh input | neato -Tpng > output.png
    

    Results in

    output.png

    Where normally, the same layout would result in

    malformed.png

    0 讨论(0)
提交回复
热议问题