package placement
import (
"context"
"math"
"github.com/pancsta/d2/d2layouts/d2talalayout/internal/layoutgraph"
"github.com/pancsta/d2/d2layouts/d2talalayout/internal/placementcost"
)
func BalanceSymmetry (ctx context .Context , g *layoutgraph .Graph ) error {
ctx , guard , err := layoutgraph .EnsureTransactionWorkGuard (ctx , "BalanceSymmetryTransactions" )
if err != nil {
return err
}
if len (g .Nodes ) == 0 {
return nil
}
var rollbackState *layoutgraph .GraphState
complete := false
defer func () {
if !complete && rollbackState != nil {
layoutgraph .RestoreGraphState (g , rollbackState )
}
}()
for _ , n := range g .Nodes {
if !isSimple (g , n ) {
continue
}
if len (n .Edges ) < 2 {
continue
}
sameSide := true
adjacentsM := make (map [*layoutgraph .Node ]struct {})
for i := 0 ; i < len (n .Edges )-1 ; i ++ {
e1 := n .Edges [i ]
e2 := n .Edges [i +1 ]
if e1 .FromTableColumnIndex != nil || e1 .ToTableColumnIndex != nil {
continue
}
if e2 .FromTableColumnIndex != nil || e2 .ToTableColumnIndex != nil {
continue
}
adj1 := n .Adjacent (e1 )
adj2 := n .Adjacent (e2 )
o1 := n .Orientation (adj1 )
o2 := n .Orientation (adj2 )
if !o1 .SameSide (o2 ) {
sameSide = false
break
}
adjacentsM [adj1 ] = struct {}{}
adjacentsM [adj2 ] = struct {}{}
}
if !sameSide {
continue
}
if len (adjacentsM ) < 2 {
continue
}
var adjacents layoutgraph .Nodes
for k := range adjacentsM {
adjacents = append (adjacents , k )
}
if placementcost .AxisScore (adjacents ) != 1. {
continue
}
maxArea := math .Inf (-1 )
for _ , adj := range adjacents {
maxArea = math .Max (maxArea , adj .Width *adj .Height )
}
uneven := false
for _ , adj := range adjacents {
if adj .Width *adj .Height < (0.5 * maxArea ) {
uneven = true
break
}
}
if uneven {
continue
}
isHorizontal := true
if adjacents [0 ].Orientation (adjacents [1 ]).IsVertical () {
isHorizontal = false
}
txn , err := g .NewRequestTransactionWithWorkGuard (ctx , guard , layoutgraph .TransactionOptions {AffectContainers : true })
if err != nil {
return err
}
if rollbackState == nil {
rollbackState = txn .PriorGraphState
}
txn .AddOp (func () error {
center := adjacents .Center ()
nCenter := n .Center ()
if isHorizontal {
n .MoveWithChildren (math .Floor (center .X -nCenter .X ), 0 )
} else {
n .MoveWithChildren (0 , math .Floor (center .Y -nCenter .Y ))
}
return nil
})
if err := txn .Commit (ctx ); err != nil {
if !layoutgraph .IsCandidateRejection (err ) {
return err
}
}
}
complete = true
return nil
}
func isSimple(graph *layoutgraph .Graph , node *layoutgraph .Node ) bool {
return !(node .IsContainer () || graph .IsTreeSentinel (node ) || node .IsClusterVessel () || graph .IsSequenceVessel (node ) || node .Cluster != nil || node .Hierarchy != 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 .