package intermediate
import (
"sort"
"github.com/d2lang/elk-go/internal/core"
basegraph "github.com/d2lang/elk-go/internal/graph"
"github.com/d2lang/elk-go/internal/layered"
)
const partitionConstraintEdgePriority = 1000
func PreprocessPartitions (arena *layered .LGraphArena , graphID layered .LGraphID ) {
partitionedNodes := make ([]layered .LNodeID , 0 )
for _ , nodeID := range arena .Graph (graphID ).LayerlessNodes {
if basegraph .Has (&arena .Node (nodeID ).Properties , core .PartitioningPartition ) {
partitionedNodes = append (partitionedNodes , nodeID )
}
}
var reverse []layered .LEdgeID
for _ , sourceNodeID := range arena .Graph (graphID ).LayerlessNodes {
sourceNode := arena .Node (sourceNodeID )
sourcePartition , sourcePartitioned := basegraph .TryGet (
&sourceNode .Properties , core .PartitioningPartition )
if !sourcePartitioned {
continue
}
for _ , edgeID := range arena .NodeOutgoingEdges (sourceNodeID ) {
edge := arena .Edge (edgeID )
if edge .Target == nil || arena .Port (*edge .Target ).Node == nil {
continue
}
targetNode := arena .Node (*arena .Port (*edge .Target ).Node )
targetPartition , targetPartitioned := basegraph .TryGet (
&targetNode .Properties , core .PartitioningPartition )
if targetPartitioned && sourcePartition > targetPartition ||
!targetPartitioned && reachesLowerPartition (
arena , sourceNodeID , sourcePartition , partitionedNodes ) {
reverse = append (reverse , edgeID )
}
}
}
for _ , edgeID := range reverse {
arena .ReverseEdge (graphID , edgeID , true )
priority := partitionConstraintEdgePriority
if existing , present := basegraph .TryGet (
&arena .Edge (edgeID ).Properties , layered .PriorityDirection ); present {
priority += existing
}
basegraph .Set (&arena .Edge (edgeID ).Properties , layered .PriorityDirection , priority )
}
}
func reachesLowerPartition(
arena *layered .LGraphArena ,
source layered .LNodeID ,
sourcePartition int ,
partitionedNodes []layered .LNodeID ,
) bool {
lowerPartitions := make (map [layered .LNodeID ]struct {})
for _ , nodeID := range partitionedNodes {
partition := basegraph .Get (&arena .Node (nodeID ).Properties , core .PartitioningPartition )
if partition < sourcePartition {
lowerPartitions [nodeID ] = struct {}{}
}
}
queue := []layered .LNodeID {source }
visited := map [layered .LNodeID ]struct {}{source : {}}
for len (queue ) != 0 {
current := queue [0 ]
queue = queue [1 :]
if _ , found := lowerPartitions [current ]; found {
return true
}
for _ , edgeID := range arena .NodeOutgoingEdges (current ) {
target := arena .EdgeTargetNode (edgeID )
if _ , found := visited [target ]; found {
continue
}
visited [target ] = struct {}{}
queue = append (queue , target )
}
}
return false
}
func ProcessPartitionsBeforeLayering (arena *layered .LGraphArena , graphID layered .LGraphID ) {
partitionNodes := make (map [int ][]layered .LNodeID )
partitionIDs := make ([]int , 0 )
for _ , nodeID := range arena .Graph (graphID ).LayerlessNodes {
partition , present := basegraph .TryGet (
&arena .Node (nodeID ).Properties , core .PartitioningPartition )
if !present {
continue
}
if _ , seen := partitionNodes [partition ]; !seen {
partitionIDs = append (partitionIDs , partition )
}
partitionNodes [partition ] = append (partitionNodes [partition ], nodeID )
}
if len (partitionIDs ) < 2 {
return
}
sort .Ints (partitionIDs )
for index := 0 ; index +1 < len (partitionIDs ); index ++ {
connectPartitions (arena ,
partitionNodes [partitionIDs [index ]],
partitionNodes [partitionIDs [index +1 ]])
}
}
func connectPartitions(
arena *layered .LGraphArena ,
first , second []layered .LNodeID ,
) {
for _ , sourceNodeID := range first {
sourcePortID := arena .AddPort ()
arena .SetPortNode (sourcePortID , &sourceNodeID )
arena .SetPortSide (sourcePortID , layered .EastSide )
basegraph .Set (&arena .Port (sourcePortID ).Properties , layered .PartitionDummy , true )
for _ , targetNodeID := range second {
targetPortID := arena .AddPort ()
arena .SetPortNode (targetPortID , &targetNodeID )
arena .SetPortSide (targetPortID , layered .WestSide )
basegraph .Set (&arena .Port (targetPortID ).Properties , layered .PartitionDummy , true )
edgeID := arena .AddEdge ()
basegraph .Set (&arena .Edge (edgeID ).Properties , layered .PartitionDummy , true )
arena .SetEdgeSource (edgeID , &sourcePortID )
arena .SetEdgeTarget (edgeID , &targetPortID )
}
}
}
func PostprocessPartitions (arena *layered .LGraphArena , graphID layered .LGraphID ) {
for _ , layerID := range arena .Graph (graphID ).Layers {
for _ , nodeID := range arena .Layer (layerID ).Nodes {
ports := append ([]layered .LPortID (nil ), arena .Node (nodeID ).Ports ...)
for _ , portID := range ports {
if basegraph .Get (&arena .Port (portID ).Properties , layered .PartitionDummy ) {
arena .SetPortNode (portID , nil )
}
}
}
}
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .