graphToDOT renders the included nodes and the edges among them as undirected DOT for a force-directed layout: a solid dark canvas with light, wrapped labels and kind colors, so the result is actually legible.
(g *knowledge.Graph, include map[string]bool, title string)
| 112 | // DOT for a force-directed layout: a solid dark canvas with light, wrapped |
| 113 | // labels and kind colors, so the result is actually legible. |
| 114 | func graphToDOT(g *knowledge.Graph, include map[string]bool, title string) string { |
| 115 | var b strings.Builder |
| 116 | b.WriteString("graph knowledge {\n") |
| 117 | fmt.Fprintf(&b, " label=%q; labelloc=t; fontsize=18; fontname=\"sans-serif\"; fontcolor=\"#e6e6e6\";\n", title) |
| 118 | b.WriteString(" bgcolor=\"#1e1e2e\"; overlap=prism; overlap_scaling=2; sep=\"+14\"; splines=true; K=0.9;\n") |
| 119 | b.WriteString(" node [style=\"rounded,filled\", shape=box, penwidth=0, fontname=\"sans-serif\", fontsize=11, fontcolor=\"#ffffff\", margin=\"0.14,0.07\"];\n") |
| 120 | b.WriteString(" edge [color=\"#56566e\", penwidth=1.0];\n") |
| 121 | |
| 122 | for _, n := range g.Nodes() { |
| 123 | if !include[n.ID] { |
| 124 | continue |
| 125 | } |
| 126 | label := strings.TrimSpace(n.Title) |
| 127 | if label == "" { |
| 128 | label = n.ID |
| 129 | } |
| 130 | fmt.Fprintf(&b, " %q [label=%q, fillcolor=%q];\n", n.ID, wrapLabel(truncateForLog(label, 48), graphVizLabelWrap), kindColor(n.Kind)) |
| 131 | } |
| 132 | |
| 133 | seen := make(map[string]bool) |
| 134 | for _, n := range g.Nodes() { |
| 135 | if !include[n.ID] { |
| 136 | continue |
| 137 | } |
| 138 | for _, nb := range g.Neighbors(n.ID) { |
| 139 | if !include[nb.ID] { |
| 140 | continue |
| 141 | } |
| 142 | a, c := n.ID, nb.ID |
| 143 | if a > c { |
| 144 | a, c = c, a |
| 145 | } |
| 146 | key := a + "|" + c |
| 147 | if seen[key] { |
| 148 | continue |
| 149 | } |
| 150 | seen[key] = true |
| 151 | pw := 1.0 + math.Min(nb.Weight, 4.0) |
| 152 | fmt.Fprintf(&b, " %q -- %q [penwidth=%.1f];\n", a, c, pw) |
| 153 | } |
| 154 | } |
| 155 | b.WriteString("}\n") |
| 156 | return b.String() |
| 157 | } |
| 158 | |
| 159 | // wrapLabel breaks a label on word boundaries to at most width characters per |
| 160 | // line and at most three lines (the rest elided), so long fact titles do not |