Skip to content

Commit

Permalink
feat: make the graph more deterministic by sorting the vertices (#1009)
Browse files Browse the repository at this point in the history
Signed-off-by: Harikrishnan Balagopal <[email protected]>
  • Loading branch information
HarikrishnanBalagopal authored Apr 4, 2023
1 parent 61c0965 commit 2aeae57
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 56 deletions.
2 changes: 1 addition & 1 deletion cmd/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func graphHandler(flags graphFlags) {
logrus.Fatalf("failed to decode the json file at path %s . Error: %q", graphFilePath, err)
}
nodes, edges := graphutils.GetNodesAndEdges(graph)
graphutils.DfsUpdatePositions(nodes, edges)
graphutils.BfsUpdatePositions(nodes, edges)
webGraph := graphtypes.GraphT{Nodes: nodes, Edges: edges}
if flags.outputPath != "" {
webBytes, err := json.Marshal(webGraph)
Expand Down
99 changes: 44 additions & 55 deletions graph/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package graph

import (
"fmt"
"sort"

"github.com/konveyor/move2kube/common"
graphtypes "github.com/konveyor/move2kube/types/graph"
"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -60,78 +62,65 @@ func GetNodesAndEdges(graph graphtypes.Graph) ([]graphtypes.Node, []graphtypes.E
return nodes, edges
}

// DfsUpdatePositions updates the positions of the nodes using a layered layout algorithm that utilizes Depth First Search.
func DfsUpdatePositions(nodes []graphtypes.Node, edges []graphtypes.EdgeT) {
visited := map[string]bool{}
adjMat := map[string]map[string]bool{}
xsPerIteration := map[int]int{}
// BfsUpdatePositions updates the positions of the nodes using a layered layout algorithm that utilizes Breadth First Search.
func BfsUpdatePositions(nodes []graphtypes.Node, edges []graphtypes.EdgeT) {
adjMat := map[string]map[string]struct{}{}
for _, edge := range edges {
if adjMat[edge.Source] == nil {
adjMat[edge.Source] = map[string]bool{}
adjMat[edge.Source] = map[string]struct{}{}
}
adjMat[edge.Source][edge.Target] = true
adjMat[edge.Source][edge.Target] = struct{}{}
}
dfsHelper(nodes, edges, visited, adjMat, "v-0", xsPerIteration, 0, 0, 0)

// handle islands

for i, node := range nodes {
if visited[node.Id] {
// the bread first search algorithm
queue := []string{"v-0"}
visited := map[string]struct{}{"v-0": {}}
xsPerIteration := map[int]int{}
for len(queue) > 0 {
// pop a vertex from the queue
current := queue[0]
queue = queue[1:]
// add its neighbours to the queue for processing later
neighbours := adjMat[current]
sortedNotVisitedNeighbours := []string{}
for neighbour := range neighbours {
if _, ok := visited[neighbour]; !ok {
sortedNotVisitedNeighbours = append(sortedNotVisitedNeighbours, neighbour)
visited[neighbour] = struct{}{}
}
}
sort.StringSlice(sortedNotVisitedNeighbours).Sort()
queue = append(queue, sortedNotVisitedNeighbours...)
// calculate the new position for the current node
idx := common.FindIndex(nodes, func(n graphtypes.Node) bool { return n.Id == current })
if idx < 0 {
logrus.Errorf("failed to find a node for the vertex with id '%s'", current)
continue
}
logrus.Errorf("found an unvisited node: %+v", node)

node := nodes[idx]
iteration := node.Position.Y

newX := 0
if oldX, ok := xsPerIteration[iteration]; ok {
newX = oldX + 200
}
xsPerIteration[iteration] = newX
node.Position.X = newX

newX := xsPerIteration[iteration]
xsPerIteration[iteration] = newX + 200
newY := iteration * 200
node.Position.X = newX
node.Position.Y = newY

nodes[i] = node
nodes[idx] = node
}
}

func dfsHelper(nodes []graphtypes.Node, edges []graphtypes.EdgeT, visited map[string]bool, adjMat map[string]map[string]bool, currVertId string, xsPerIteration map[int]int, parentIteration, parentX, parentY int) {
visited[currVertId] = true
newX := 0
newY := 0
iteration := 0
// handle islands
for i, node := range nodes {
if node.Id != currVertId {
if _, ok := visited[node.Id]; ok {
continue
}

iteration = node.Position.Y

if iteration == parentIteration {
// mandatory/on-demand passthrough
newX = parentX
newY = parentY + 100
} else {
newX = 0
if oldX, ok := xsPerIteration[iteration]; ok {
newX = oldX + 200
}
xsPerIteration[iteration] = newX
newY = iteration * 200
}

logrus.Errorf("found an unvisited node: %+v", node)
visited[node.Id] = struct{}{}
// calculate the new position for the current node
iteration := node.Position.Y
newX := xsPerIteration[iteration]
xsPerIteration[iteration] = newX + 200
newY := iteration * 200
node.Position.X = newX
node.Position.Y = newY
nodes[i] = node
break
}
for neighbourVertId, ok := range adjMat[currVertId] {
if !ok || visited[neighbourVertId] {
// no edge from the current vertex to this neighbour vertex OR this neighbour has already been visited
continue
}
dfsHelper(nodes, edges, visited, adjMat, neighbourVertId, xsPerIteration, iteration, newX, newY)
}
}

0 comments on commit 2aeae57

Please sign in to comment.