| // Copyright ©2019 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 layout |
| |
| import ( |
| "gonum.org/v1/gonum/graph" |
| "gonum.org/v1/gonum/spatial/r2" |
| ) |
| |
| // LayoutR2 implements graph layout updates and representations. |
| type LayoutR2 interface { |
| // IsInitialized returns whether the Layout is initialized. |
| IsInitialized() bool |
| |
| // SetCoord2 sets the coordinates of the node with the given |
| // id to coords. |
| SetCoord2(id int64, coords r2.Vec) |
| |
| // Coord2 returns the coordinated of the node with the given |
| // id in the graph layout. |
| Coord2(id int64) r2.Vec |
| } |
| |
| // NewOptimizerR2 returns a new layout optimizer. If g implements LayoutR2 the layout |
| // will be updated into g, otherwise the OptimizerR2 will hold the graph layout. A nil |
| // value for update is a valid no-op layout update function. |
| func NewOptimizerR2(g graph.Graph, update func(graph.Graph, LayoutR2) bool) OptimizerR2 { |
| l, ok := g.(LayoutR2) |
| if !ok { |
| l = make(coordinatesR2) |
| } |
| return OptimizerR2{ |
| g: g, |
| layout: l, |
| Updater: update, |
| } |
| } |
| |
| // coordinatesR2 is the default layout store for R2. |
| type coordinatesR2 map[int64]r2.Vec |
| |
| func (c coordinatesR2) IsInitialized() bool { return len(c) != 0 } |
| func (c coordinatesR2) SetCoord2(id int64, pos r2.Vec) { c[id] = pos } |
| func (c coordinatesR2) Coord2(id int64) r2.Vec { return c[id] } |
| |
| // OptimizerR2 is a helper type that holds a graph and layout |
| // optimization state. |
| type OptimizerR2 struct { |
| g graph.Graph |
| layout LayoutR2 |
| |
| // Updater is the function called for each call to Update. |
| // It updates the OptimizerR2's spatial distribution of the |
| // nodes in the backing graph. |
| Updater func(graph.Graph, LayoutR2) bool |
| } |
| |
| // Coord2 returns the location of the node with the given |
| // ID. The returned value is only valid if the node exists |
| // in the graph. |
| func (g OptimizerR2) Coord2(id int64) r2.Vec { |
| return g.layout.Coord2(id) |
| } |
| |
| // Update updates the locations of the nodes in the graph |
| // according to the provided update function. It returns whether |
| // the update function is able to further refine the graph's |
| // node locations. |
| func (g OptimizerR2) Update() bool { |
| if g.Updater == nil { |
| return false |
| } |
| return g.Updater(g.g, g.layout) |
| } |
| |
| // LayoutNodeR2 implements the GraphR2 interface. |
| func (g OptimizerR2) LayoutNodeR2(id int64) NodeR2 { |
| n := g.g.Node(id) |
| if n == nil { |
| return NodeR2{} |
| } |
| return NodeR2{Node: n, Coord2: g.Coord2(id)} |
| } |
| |
| // Node returns the node with the given ID if it exists |
| // in the graph, and nil otherwise. |
| func (g OptimizerR2) Node(id int64) graph.Node { return g.g.Node(id) } |
| |
| // Nodes returns all the nodes in the graph. |
| func (g OptimizerR2) Nodes() graph.Nodes { return g.g.Nodes() } |
| |
| // From returns all nodes that can be reached directly |
| // from the node with the given ID. |
| func (g OptimizerR2) From(id int64) graph.Nodes { return g.g.From(id) } |
| |
| // HasEdgeBetween returns whether an edge exists between |
| // nodes with IDs xid and yid without considering direction. |
| func (g OptimizerR2) HasEdgeBetween(xid, yid int64) bool { return g.g.HasEdgeBetween(xid, yid) } |
| |
| // Edge returns the edge from u to v, with IDs uid and vid, |
| // if such an edge exists and nil otherwise. The node v |
| // must be directly reachable from u as defined by the |
| // From method. |
| func (g OptimizerR2) Edge(uid, vid int64) graph.Edge { return g.g.Edge(uid, vid) } |