whispervis/graph.go
2018-10-22 20:40:27 +02:00

85 lines
1.9 KiB
Go

package main
import (
"encoding/json"
"errors"
"io"
"runtime"
"github.com/divan/graphx/graph"
"github.com/gopherjs/vecty"
)
// UpdateGraph starts graph layout simulation.
func (p *Page) UpdateGraph() {
p.loader.Reset()
p.loaded = false
vecty.Rerender(p)
config := p.forceEditor.Config()
p.loader.SetSteps(config.Steps)
for i := 0; i < config.Steps; i++ {
p.layout.UpdatePositions()
p.loader.Inc()
vecty.Rerender(p.loader)
runtime.Gosched()
}
p.loaded = true
// TODO(divan): remove previous objects
p.webgl.RemoveObjects()
p.webgl.CreateObjects(p.layout.Positions(), p.layout.Links())
vecty.Rerender(p)
}
// ApplyForces applies current forces to the objects, and runs
// a single simulation run to update positions.
func (p *Page) ApplyForces() {
fc := p.forceEditor.Config()
p.layout.SetConfig(fc.Config)
p.layout.UpdatePositions()
p.webgl.updatePositions()
p.webgl.rt.Disable()
}
// GraphFromJSON is a custom version of graphx JSON importer, as we want to use
// some additional fields (Description).
// TODO(divan): that's probably can be done better within the limits of graphx library.
func GraphFromJSON(r io.Reader) (*graph.Graph, string, error) {
// decode into temporary struct to process
var res struct {
Description string `json:"description"`
Nodes []*graph.BasicNode `json:"nodes"`
Links []*struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"links"`
}
err := json.NewDecoder(r).Decode(&res)
if err != nil {
return nil, "", err
}
if len(res.Nodes) == 0 {
return nil, "", errors.New("empty graph")
}
// convert links IDs into indices
g := graph.NewGraphMN(len(res.Nodes), len(res.Links))
for _, node := range res.Nodes {
g.AddNode(node)
}
for _, link := range res.Links {
err := g.AddLink(link.Source, link.Target)
if err != nil {
return nil, "", err
}
}
g.UpdateCache()
return g, res.Description, nil
}