encoding/dot: fix ports on undirected graphs

This commit is contained in:
J. Holmes
2018-04-15 19:13:11 -06:00
committed by Dan Kortschak
parent 7b0fe8702c
commit b2e9df857d
5 changed files with 67 additions and 19 deletions

View File

@@ -29,6 +29,7 @@ Iakov Davydov <iakov.davydov@unil.ch>
Jalem Raj Rohit <jrajrohit33@gmail.com> Jalem Raj Rohit <jrajrohit33@gmail.com>
James Bell <james@stellentus.com> James Bell <james@stellentus.com>
James Bowman <james.edward.bowman@gmail.com> James Bowman <james.edward.bowman@gmail.com>
James Holmes <32bitkid@gmail.com>
Janne Snabb <snabb@epipe.com> Janne Snabb <snabb@epipe.com>
Jeff Juozapaitis <jjjuozap@email.arizona.edu> Jeff Juozapaitis <jjjuozap@email.arizona.edu>
Jeremy Atkinson <jchatkinson@gmail.com> Jeremy Atkinson <jchatkinson@gmail.com>

View File

@@ -36,6 +36,7 @@ Iakov Davydov <iakov.davydov@unil.ch>
Jalem Raj Rohit <jrajrohit33@gmail.com> Jalem Raj Rohit <jrajrohit33@gmail.com>
James Bell <james@stellentus.com> James Bell <james@stellentus.com>
James Bowman <james.edward.bowman@gmail.com> James Bowman <james.edward.bowman@gmail.com>
James Holmes <32bitkid@gmail.com>
Janne Snabb <snabb@epipe.com> Janne Snabb <snabb@epipe.com>
Jeff Juozapaitis <jjjuozap@email.arizona.edu> Jeff Juozapaitis <jjjuozap@email.arizona.edu>
Jeremy Atkinson <jchatkinson@gmail.com> Jeremy Atkinson <jchatkinson@gmail.com>

View File

@@ -41,7 +41,12 @@ type Attributers interface {
// to the the DOT node port to be used by the edge, compass corresponds // 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. // to DOT compass point to which the edge will be aimed.
type Porter interface { type Porter interface {
// FromPort returns the port and compass for the
// From node of a graph.Edge.
FromPort() (port, compass string) FromPort() (port, compass string)
// ToPort returns the port and compass for the
// To node of a graph.Edge.
ToPort() (port, compass string) ToPort() (port, compass string)
} }
@@ -219,9 +224,14 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
} else { } else {
p.writeNode(n) p.writeNode(n)
} }
e, edgeIsPorter := g.Edge(nid, tid).(Porter) e := g.Edge(nid, tid)
porter, edgeIsPorter := e.(Porter)
if edgeIsPorter { if edgeIsPorter {
p.writePorts(e.FromPort()) if e.From().ID() == nid {
p.writePorts(porter.FromPort())
} else {
p.writePorts(porter.ToPort())
}
} }
if isDirected { if isDirected {
@@ -241,7 +251,11 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
p.writeNode(t) p.writeNode(t)
} }
if edgeIsPorter { 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 { if a, ok := g.Edge(nid, tid).(encoding.Attributer); ok {

View File

@@ -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) To() graph.Node { return e.to }
func (e portedEdge) Weight() float64 { return 0 } 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) { func (e portedEdge) FromPort() (port, compass string) {
return e.fromPort, e.fromCompass return e.fromPort, e.fromCompass
} }
@@ -1054,14 +1046,7 @@ var encodeTests = []struct {
{from: 2, to: 3}: {fromPort: "Zwei", fromCompass: "e"}, {from: 2, to: 3}: {fromPort: "Zwei", fromCompass: "e"},
{from: 2, to: 4}: {fromPort: "Two", fromCompass: "w", toPort: "Four", toCompass: "w"}, {from: 2, to: 4}: {fromPort: "Two", fromCompass: "w", toPort: "Four", toCompass: "w"},
{from: 3, to: 4}: {toPort: "Four", toCompass: "w"}, {from: 3, to: 4}: {toPort: "Four", toCompass: "w"},
{from: 4, to: 0}: {fromPort: "Four", fromCompass: "_", toCompass: "s"},
// 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: "_"},
}, },
), ),

View File

@@ -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;
// }
}