diff --git a/AUTHORS b/AUTHORS index bea29f3c..988e2165 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,6 +29,7 @@ Iakov Davydov Jalem Raj Rohit James Bell James Bowman +James Holmes <32bitkid@gmail.com> Janne Snabb Jeff Juozapaitis Jeremy Atkinson diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 142e2545..54a07030 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -36,6 +36,7 @@ Iakov Davydov Jalem Raj Rohit James Bell James Bowman +James Holmes <32bitkid@gmail.com> Janne Snabb Jeff Juozapaitis Jeremy Atkinson diff --git a/graph/encoding/dot/encode.go b/graph/encoding/dot/encode.go index 55b96e31..4664c10f 100644 --- a/graph/encoding/dot/encode.go +++ b/graph/encoding/dot/encode.go @@ -41,7 +41,12 @@ type Attributers interface { // to the the DOT node port to be used by the edge, compass corresponds // to DOT compass point to which the edge will be aimed. type Porter interface { + // FromPort returns the port and compass for the + // From node of a graph.Edge. FromPort() (port, compass string) + + // ToPort returns the port and compass for the + // To node of a graph.Edge. ToPort() (port, compass string) } @@ -219,9 +224,14 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool } else { p.writeNode(n) } - e, edgeIsPorter := g.Edge(nid, tid).(Porter) + e := g.Edge(nid, tid) + porter, edgeIsPorter := e.(Porter) if edgeIsPorter { - p.writePorts(e.FromPort()) + if e.From().ID() == nid { + p.writePorts(porter.FromPort()) + } else { + p.writePorts(porter.ToPort()) + } } if isDirected { @@ -241,7 +251,11 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool p.writeNode(t) } if edgeIsPorter { - p.writePorts(e.ToPort()) + if e.From().ID() == nid { + p.writePorts(porter.ToPort()) + } else { + p.writePorts(porter.FromPort()) + } } if a, ok := g.Edge(nid, tid).(encoding.Attributer); ok { diff --git a/graph/encoding/dot/encode_test.go b/graph/encoding/dot/encode_test.go index eb0a7703..d92fb7df 100644 --- a/graph/encoding/dot/encode_test.go +++ b/graph/encoding/dot/encode_test.go @@ -256,14 +256,6 @@ func (e portedEdge) From() graph.Node { return e.from } func (e portedEdge) To() graph.Node { return e.to } func (e portedEdge) Weight() float64 { return 0 } -// TODO(kortschak): Figure out a better way to handle the fact that -// headedness is an undefined concept in undirected graphs. We sort -// nodes by ID, so lower ID nodes are always from nodes in undirected -// graphs. We can probably do this in the printer, but I am leaving -// this here as a WARNING. -// Maybe the approach should be to document that for undirected graphs -// the low ID node should be returned by the FromPort and the high ID -// by the ToPort calls. func (e portedEdge) FromPort() (port, compass string) { return e.fromPort, e.fromCompass } @@ -1054,14 +1046,7 @@ var encodeTests = []struct { {from: 2, to: 3}: {fromPort: "Zwei", fromCompass: "e"}, {from: 2, to: 4}: {fromPort: "Two", fromCompass: "w", toPort: "Four", toCompass: "w"}, {from: 3, to: 4}: {toPort: "Four", toCompass: "w"}, - - // This definition is reversed (see comment above at portedEdge - // definition) so that 4 gets the from port. This is a result - // of the fact that we sort nodes by ID, so the lower node - // will be always be printed first when the graph is undirected, - // thus becoming the from port, but we define the edges here - // from a directed adjacency list. - {from: 4, to: 0}: {fromCompass: "s", toPort: "Four", toCompass: "_"}, + {from: 4, to: 0}: {fromPort: "Four", fromCompass: "_", toCompass: "s"}, }, ), diff --git a/graph/encoding/dot/example_test.go b/graph/encoding/dot/example_test.go new file mode 100644 index 00000000..0e57f299 --- /dev/null +++ b/graph/encoding/dot/example_test.go @@ -0,0 +1,47 @@ +// Copyright ©2018 The Gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dot_test + +import ( + "fmt" + + "gonum.org/v1/gonum/graph/encoding/dot" + "gonum.org/v1/gonum/graph/simple" +) + +type edgeWithPorts struct { + simple.Edge + fromPort, toPort string +} + +func (e *edgeWithPorts) FromPort() (string, string) { + return e.fromPort, "" +} + +func (e *edgeWithPorts) ToPort() (string, string) { + return e.toPort, "" +} + +func ExamplePorter() { + g := simple.NewUndirectedGraph() + g.SetEdge(&edgeWithPorts{ + Edge: simple.Edge{simple.Node(1), simple.Node(0)}, + fromPort: "p1", + toPort: "p2", + }) + + result, _ := dot.Marshal(g, "", "", " ", true) + fmt.Print(string(result)) + + // Output: + // strict graph { + // // Node definitions. + // 0; + // 1; + // + // // Edge definitions. + // 0:p2 -- 1:p1; + // } +}