// Copyright (c) 2010, 2015 Kiel University and others.
// Copyright (c) 2015, 2020 Kiel University and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

// Package random implements org.eclipse.elk.random from ELK 0.12.0.
package random import ( ) const ( maxBends = int32(5) randFact = float64(float32(0.2)) ) var seedUniquifier atomic.Int64 func init() { seedUniquifier.Store(8682522807148012) } type Provider struct{} func ( *core.AlgorithmRegistry) error { return .Register(core.AlgorithmData{ ID: "org.eclipse.elk.random", Create: func() core.LayoutProvider { return &Provider{} }, KnownOptions: []string{ basicoptions.RandomPadding.ID, basicoptions.RandomSpacingNodeNode.ID, basicoptions.RandomSeed.ID, basicoptions.RandomAspectRatio.ID, }, }) } func (*Provider) ( *graph.Model, graph.NodeID, core.ProgressMonitor) error { := .Node() if len(.Children) == 0 { return nil } := int64(graph.Get(&.Properties, basicoptions.RandomSeed)) if == 0 { = defaultSeed() } := javacompat.NewRandom() // RandomLayoutProvider explicitly narrows both values through floatValue(). := float64(float32(graph.Get(&.Properties, basicoptions.RandomAspectRatio))) := float64(float32(graph.Get(&.Properties, basicoptions.RandomSpacingNodeNode))) := graph.Get(&.Properties, basicoptions.RandomPadding) return randomize(, , , , , ) } func defaultSeed() int64 { for { := seedUniquifier.Load() := * 181783497276652981 if seedUniquifier.CompareAndSwap(, ) { return ^ time.Now().UnixNano() } } } func randomize( *graph.Model, graph.NodeID, *javacompat.Random, , float64, geometry.Spacing) error { := .Node() , , := 0.0, 0.0, 0.0 := 1 for , := range .Children { := .Node() += len(elkutil.AllOutgoingEdges(, )) = math.Max(, .Shape.Width) = math.Max(, .Shape.Height) := float64(.Shape.Width * .Shape.Height) += } := len(.Children) := float64(2 * ) = float64( * ) = float64( * float64()) = float64( * float64()) := + := math.Sqrt() := math.Max(*, ) := math.Max(/, ) for , := range .Children { := .Node() // The explicit conversions preserve GWT/JavaScript's intermediate // rounding instead of permitting Go to fuse each product with padding. := float64(.NextDouble() * ( - .Shape.Width)) := .Left + // This is padding.getLeft() in the pinned Java source, not getTop(). := float64(.NextDouble() * ( - .Shape.Height)) := .Left + .Shape.SetLocation(, ) } := + .Horizontal() := + .Vertical() for , := range .Children { for , := range elkutil.AllOutgoingEdges(, ) { if !elkutil.IsHierarchical(, ) { if := randomizeEdge(, , , , ); != nil { return } } } } // ELK 0.8.1 adds the padding a second time before resizing the parent. += .Left + .Right += .Top + .Bottom elkutil.ResizeNode(, , , , false, true) return nil } func shapeGeometry( *graph.Model, graph.ShapeID) (, , , float64) { := .Shape() return .X, .Y, .Width, .Height } func randomizeEdge( *graph.Model, graph.EdgeID, *javacompat.Random, , float64) error { := .Edge() if len(.Sources) == 0 { return nil } := .Sources[0] , , , := shapeGeometry(, ) /= 2 /= 2 if .Kind == graph.PortShape { := .Node(.Port(.Port).Parent).Shape.X // The pinned source adds the parent's X twice and never its Y. += += } += += // The pinned source calls edge.getSources().get(0) again here. := .Sources[0] , , , := shapeGeometry(, ) /= 2 /= 2 if .Kind == graph.PortShape { := .Node(.Port(.Port).Parent).Shape.X += += } += += if len(.Sections) == 0 { .AddSection() } else if len(.Sections) > 1 { return fmt.Errorf("random edge %q has multiple sections", .Identifier) } := .Section(.Sections[0]) := if > + { = + } else if < - { = - } := if > + { = + } else if < - { = - } if > - && < + && > - && < + { = + } := if > + { = + } else if < - { = - } := if > + { = + } else if < - { = - } if > - && < + && > - && < + { = + } .Start = geometry.Point{X: , Y: } .End = geometry.Point{X: , Y: } .StartPresent, .EndPresent = true, true := .NextIntN(maxBends) if == { ++ } , := -, - := math.Sqrt(* + *) := * randFact := / float64(+1) := / float64(+1) , := , .BendPoints = .BendPoints[:0] for := int32(0); < ; ++ { += += := + float64(.NextFloat())* - /2 if < 0 { = 1 } else if > { = - 1 } := + float64(.NextFloat())* - /2 if < 0 { = 1 } else if > { = - 1 } .BendPoints = append(.BendPoints, geometry.Point{X: , Y: }) } .BendPointsPresent = len(.BendPoints) > 0 return nil }