Skip to content

Commit

Permalink
Merge pull request #20 from nulab/dev-19/empty-nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
vibridi authored Jun 21, 2024
2 parents f979d30 + a39a1a7 commit 6b1bd1f
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 219 deletions.
44 changes: 34 additions & 10 deletions autolayout.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package autog

import (
"slices"

"github.com/nulab/autog/graph"
ig "github.com/nulab/autog/internal/graph"
imonitor "github.com/nulab/autog/internal/monitor"
Expand All @@ -27,8 +29,7 @@ func Layout(source graph.Source, opts ...Option) graph.Layout {
}

// populate the graph struct from the graph source
g := &ig.DGraph{}
source.Populate(g)
g := from(source)

if layoutOpts.params.NodeFixedSizeFunc != nil {
for _, n := range g.Nodes {
Expand All @@ -43,20 +44,43 @@ func Layout(source graph.Source, opts ...Option) graph.Layout {

// return only relevant data to the caller
out := graph.Layout{
Nodes: make([]graph.Node, len(g.Nodes)),
Edges: make([]graph.Edge, len(g.Edges)),
Nodes: make([]graph.Node, 0, len(g.Nodes)),
Edges: make([]graph.Edge, 0, len(g.Edges)),
}
for i, n := range g.Nodes {
if n.IsVirtual {
for _, n := range g.Nodes {
if n.IsVirtual && !layoutOpts.output.keepVirtualNodes {
continue
}
out.Nodes[i] = graph.Node{ID: n.ID, Size: n.Size}
out.Nodes = append(out.Nodes, graph.Node{
ID: n.ID,
Size: n.Size,
})
// todo: clients can't reliably tell virtual nodes from concrete nodes
}
if !layoutOpts.output.keepVirtualNodes {
out.Nodes = slices.Clip(out.Nodes)
}
for i, e := range g.Edges {
out.Edges[i] = graph.Edge{

for _, e := range g.Edges {
out.Edges = append(out.Edges, graph.Edge{
FromID: e.From.ID,
ToID: e.To.ID,
Points: e.Points,
ArrowHeadStart: e.ArrowHeadStart,
}
})
}
return out
}

func from(source graph.Source) *ig.DGraph {
switch t := source.(type) {
case *ig.DGraph:
// special case for when the graph source is already a DGraph
// this happens only during unit testing
return t
default:
g := &ig.DGraph{}
source.Populate(g)
return g
}
}
10 changes: 10 additions & 0 deletions autolayout_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ type options struct {
p4 positioning.Alg
p5 routing.Alg
params graph.Params
output output
monitor imonitor.Monitor
}

type output struct {
keepVirtualNodes bool
}

var defaultOptions = options{
p1: cbreaking.Greedy,
p2: layering.NetworkSimplex,
Expand All @@ -37,6 +42,11 @@ var defaultOptions = options{
BrandesKoepfLayout: -1,
},
monitor: nil,
output: defaultOutputOptions,
}

var defaultOutputOptions = output{
keepVirtualNodes: false,
}

type Option func(*options)
6 changes: 6 additions & 0 deletions autolayout_options_params.go → autolayout_options_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ func WithMonitor(monitor imonitor.Monitor) Option {
o.monitor = monitor
}
}

func WithKeepVirtualNodes(keep bool) Option {
return func(o *options) {
o.output.keepVirtualNodes = keep
}
}
10 changes: 6 additions & 4 deletions graph/edge.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package graph

type Edge struct {
ID string

// Identifier of this edge's source node
FromID string
// Identifier of this edge's target node
ToID string
// Slice of points required to draw this edge.
// Depending on the routing algorithm, the slice can be interpreted differently:
//
// in case of edges made of straight segments (options autog.WithEdgeRoutingStraight, autog.WithEdgeRoutingPieceWise, autog.WithEdgeRoutingOrtho),
// in case of edges made of straight segments (options EdgeRoutingStraight, EdgeRoutingPieceWise, EdgeRoutingOrtho),
// the slice contains the start point, any number of intermediate bend points and the end point.
// If the option autog.WithEdgeRoutingStraight was chosen, the slice will have length 2 and include only the start and end points;
// If the option EdgeRoutingStraight was chosen, the slice will have length 2 and include only the start and end points;
//
// in case of curved edges, the slice contains the control points of a piece-wise cubic bezier spline, i.e. its length
// is a multiple of 4, and it must be processed in chunks of 4 points each.
Expand Down
8 changes: 5 additions & 3 deletions internal/graph/dgraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ type DGraph struct {
Layers Layers
}

// Generate implements graph.Source by returning itself as a compatibility step
func (g *DGraph) Populate(dst *DGraph) {
*dst = *g
func (g *DGraph) Populate(*DGraph) {
// DGraph implements graph.Source to facilitate unit testing.
// It is not meant to do anything because *DGraph can be obtained from graph.Source
// via type assertion.
// todo: instead of having a dummy implementation, a wrapper could be used instead in unit tests
}

func (g *DGraph) GetNodes() []*Node {
Expand Down
202 changes: 0 additions & 202 deletions internal/phase3/wmedian_test.go

This file was deleted.

6 changes: 6 additions & 0 deletions internal/testfiles/adjacency_lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ var issues1and4 = [][]string{
{"N2", "Nd"},
}

var simpleVirtualNodes = [][]string{
{"N1", "N2"},
{"N2", "N3"},
{"N1", "N3"},
}

var cacooArch = [][]string{
{"gql", "acc"},
{"gql", "dia"},
Expand Down
11 changes: 11 additions & 0 deletions internal/testfiles/bugfix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,15 @@ func TestCrashers(t *testing.T) {
}
}
})

t.Run("output layout empty nodes", func(t *testing.T) {
src := graph.EdgeSlice(simpleVirtualNodes)
layout := autog.Layout(
src,
autog.WithPositioning(autog.PositioningVAlign),
autog.WithEdgeRouting(autog.EdgeRoutingNoop),
)
assert.Len(t, layout.Nodes, 3)
assert.Len(t, layout.Edges, 3)
})
}
Loading

0 comments on commit 6b1bd1f

Please sign in to comment.