package dagro
import (
"fmt"
"math"
"strconv"
"sync/atomic"
"time"
)
var uniqueIDCounter atomic .Uint64
func uniqueID(prefix string ) string {
return prefix + strconv .FormatUint (uniqueIDCounter .Add (1 ), 10 )
}
func Time (name string , fn any ) (result any ) {
start := time .Now ()
defer func () {
fmt .Printf ("%s time: %dms\n" , name , time .Since (start ).Milliseconds ())
}()
return callCallable (fn )
}
func NoTime (_ string , fn any ) any { return callCallable (fn ) }
func addDummyNode(g *Graph , typ string , attrs Attrs , name string ) string {
v := name
for g .HasNode (v ) {
v = uniqueID (name )
}
attrs ["dummy" ] = typ
g .SetNode (v , attrs )
return v
}
func simplify(g *Graph ) *Graph {
simplified := NewGraph ().SetGraph (g .Graph ())
for _ , v := range g .Nodes () {
simplified .SetNode (v , g .Node (v ))
}
for _ , e := range g .Edges () {
simpleLabel := Attrs {"weight" : float64 (0 ), "minlen" : float64 (1 )}
if existing := simplified .EdgeByArgs (e .V , e .W ); existing != nil {
simpleLabel = asAttrs (existing )
}
label := asAttrs (g .Edge (e ))
simplified .SetEdge (e .V , e .W , Attrs {
"weight" : num (simpleLabel , "weight" ) + num (label , "weight" ),
"minlen" : mathMaxJS (num (simpleLabel , "minlen" ), num (label , "minlen" )),
})
}
return simplified
}
func asNonCompoundGraph(g *Graph ) *Graph {
simplified := NewGraph (GraphOptions {Multigraph : g .IsMultigraph ()}).SetGraph (g .Graph ())
for _ , v := range g .Nodes () {
if len (g .Children (v )) == 0 {
simplified .SetNode (v , g .Node (v ))
}
}
for _ , e := range g .Edges () {
simplified .SetEdgeObject (e , g .Edge (e ))
}
return simplified
}
func successorWeights(g *Graph ) map [string ]map [string ]float64 {
out := make (map [string ]map [string ]float64 , g .NodeCount ())
for _ , v := range g .Nodes () {
weights := map [string ]float64 {}
for _ , e := range g .OutEdges (v ) {
weights [e .W ] += num (asAttrs (g .Edge (e )), "weight" )
}
out [v ] = weights
}
return out
}
func predecessorWeights(g *Graph ) map [string ]map [string ]float64 {
out := make (map [string ]map [string ]float64 , g .NodeCount ())
for _ , v := range g .Nodes () {
weights := map [string ]float64 {}
for _ , e := range g .InEdges (v ) {
weights [e .V ] += num (asAttrs (g .Edge (e )), "weight" )
}
out [v ] = weights
}
return out
}
func intersectRect(rect Attrs , point Point ) (Point , error ) {
x , y := num (rect , "x" ), num (rect , "y" )
dx , dy := point .X -x , point .Y -y
w , h := num (rect , "width" )/2 , num (rect , "height" )/2
if !jsTruthyNumber (dx ) && !jsTruthyNumber (dy ) {
if w == 0 || h == 0 {
return Point {X : x , Y : y }, nil
}
return Point {}, fmt .Errorf ("not possible to find intersection inside of the rectangle" )
}
var sx , sy float64
if !jsTruthyNumber (dx ) {
if dy < 0 {
h = -h
}
sx , sy = 0 , h
} else if !jsTruthyNumber (dy ) {
if dx < 0 {
w = -w
}
sx , sy = w , 0
} else if math .Abs (dy )*w > math .Abs (dx )*h {
if dy < 0 {
h = -h
}
sx , sy = h *dx /dy , h
} else {
if dx < 0 {
w = -w
}
sx , sy = w , w *dy /dx
}
return Point {X : x + sx , Y : y + sy }, nil
}
func buildLayerMatrix(g *Graph ) [][]string {
max := maxRank (g )
if max < 0 || math .IsInf (max , -1 ) {
return nil
}
layers := make ([][]string , int (max )+1 )
for _ , v := range g .Nodes () {
node := asAttrs (g .Node (v ))
if !has (node , "rank" ) || !has (node , "order" ) {
continue
}
rank , order := integer (node , "rank" ), integer (node , "order" )
for len (layers [rank ]) <= order {
layers [rank ] = append (layers [rank ], "" )
}
layers [rank ][order ] = v
}
return layers
}
func normalizeRanks(g *Graph ) {
min := math .Inf (1 )
for _ , v := range g .Nodes () {
node := asAttrs (g .Node (v ))
if has (node , "rank" ) {
min = lodashMinJS (min , num (node , "rank" ))
}
}
for _ , v := range g .Nodes () {
node := asAttrs (g .Node (v ))
if has (node , "rank" ) {
node ["rank" ] = num (node , "rank" ) - min
}
}
}
func removeEmptyRanks(g *Graph ) {
offset := math .Inf (1 )
for _ , v := range g .Nodes () {
node := asAttrs (g .Node (v ))
if has (node , "rank" ) {
offset = lodashMinJS (offset , num (node , "rank" ))
}
}
var layers [][]string
for _ , v := range g .Nodes () {
node := asAttrs (g .Node (v ))
if !has (node , "rank" ) {
continue
}
rankValue := num (node , "rank" ) - offset
if math .IsNaN (rankValue ) || math .IsInf (rankValue , 0 ) ||
rankValue < 0 || rankValue != math .Trunc (rankValue ) || rankValue >= 4294967295 {
continue
}
rank := int (rankValue )
for len (layers ) <= rank {
layers = append (layers , nil )
}
layers [rank ] = append (layers [rank ], v )
}
delta := 0.0
factor := num (asAttrs (g .Graph ()), "nodeRankFactor" )
for i , vs := range layers {
if vs == nil && math .Mod (float64 (i ), factor ) != 0 {
delta --
} else if delta != 0 {
for _ , v := range vs {
node := asAttrs (g .Node (v ))
node ["rank" ] = num (node , "rank" ) + delta
}
}
}
}
func addBorderNode(g *Graph , prefix string , values ...float64 ) string {
node := Attrs {"width" : float64 (0 ), "height" : float64 (0 )}
if len (values ) >= 2 {
node ["rank" ], node ["order" ] = values [0 ], values [1 ]
}
return addDummyNode (g , "border" , node , prefix )
}
func maxRank(g *Graph ) float64 {
max := math .Inf (-1 )
for _ , v := range g .Nodes () {
node := asAttrs (g .Node (v ))
if has (node , "rank" ) {
max = lodashMaxJS (max , num (node , "rank" ))
}
}
return max
}
func setEdgePreservingName(g *Graph , v , w string , label any , e Edge ) {
if e .HasName {
g .SetEdge (v , w , label , e .Name )
} else {
g .SetEdge (v , w , label )
}
}
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 .