// Copyright (c) 2024 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 unzipping ports ELK Layered's post-crossing-minimization layer // splitting processors.
package unzipping import ( basegraph ) type layerInsertion struct { layer layered.LayerID index int } // AlternateLayers ports ELK 0.12.0's AlternatingLayerUnzipper. It divides a // layer into sublayers while preserving its decided node/edge order, inserting // long-edge dummies and temporary placeholders where the split requires them. func ( *layered.LGraphArena, layered.LGraphID) { := .Graph() := append([]layered.LayerID(nil), .Layers...) := 1 := make([]layerInsertion, 0) for , := range { := layerSplitProperty(, ) := resetOnLongEdgesProperty(, ) := minimizeEdgeLengthProperty(, ) if && shouldKeepLayerUnsplit(, , ) { continue } if len(.Layer().Nodes) < { continue } := make([]layered.LayerID, 1, ) [0] = for := 0; < -1; ++ { := .AddLayer() = append(, layerInsertion{ layer: , index: + + , }) = append(, ) } += - 1 := len(.Layer().Nodes) for , , := 0, 0, 0; < ; , , = +1, +1, +1 { := .Layer().Nodes[] if .Node().Type != layered.NonShiftingPlaceholderNode { += shiftNode(, , , %, ) } else { -- -- } if && .Node().Type == layered.LongEdgeNode { = -1 } } } for , := range { := .Graph().Layers = append(, 0) copy([.index+1:], [.index:]) [.index] = .layer .Graph().Layers = } for , := range .Graph().Layers { := .Layer().Nodes := [:0] for , := range { := .Node().Type if != layered.PlaceholderNode && != layered.NonShiftingPlaceholderNode { = append(, ) } } .Layer().Nodes = } } func layerSplitProperty( *layered.LGraphArena, layered.LayerID) int { := math.MaxInt := false for , := range .Layer().Nodes { if , := basegraph.TryGet( &.Node().Properties, layered.LayerUnzippingLayerSplit, ); { = true = min(, ) } } if ! { return 2 } return } func resetOnLongEdgesProperty( *layered.LGraphArena, layered.LayerID) bool { for , := range .Layer().Nodes { if , := basegraph.TryGet( &.Node().Properties, layered.LayerUnzippingResetOnLongEdges, ); && ! { return false } } return true } func minimizeEdgeLengthProperty( *layered.LGraphArena, layered.LayerID) bool { for , := range .Layer().Nodes { if , := basegraph.TryGet( &.Node().Properties, layered.LayerUnzippingMinimizeEdgeLength, ); && { return true } } return false } func shouldKeepLayerUnsplit( *layered.LGraphArena, layered.LGraphID, layered.LayerID, ) bool { := 0.0 := 0.0 := .Layer().Nodes for , := range { := .Node() = math.Max(, .Size.X) += .Size.Y } /= float64(len()) := .Graph() += math.Max( 2*basegraph.Get(&.Properties, layered.SpacingEdgeNodeBetweenLayers), math.Max( float64(len())*basegraph.Get(&.Properties, layered.SpacingEdgeEdgeBetweenLayers), basegraph.Get(&.Properties, layered.SpacingNodeNodeBetweenLayers), ), ) += math.Max( basegraph.Get(&.Properties, layered.SpacingNodeNode), basegraph.Get(&.Properties, layered.SpacingEdgeNode), ) return / >= float64(len())/4 } func shiftNode( *layered.LGraphArena, layered.LGraphID, []layered.LayerID, int, int, ) int { := .Layer([0]).Nodes[] if > 0 { .SetNodeLayer(, &[]) } := 0 := .NodeIncomingEdges() for , := 0, len()-1; < ; , = +1, -1 { [], [] = [], [] } for , := range { := for := 0; < ; ++ { := createDummyNode(, , ) insertNode(, , [], +) = intermediate.SplitEdgeAtDummy(, , ) } if > 0 { ++ } } if len() == 0 { for := 0; < ; ++ { := .AddNode() .Node().Type = layered.PlaceholderNode insertNode(, , [], +) } if > 0 { ++ } } := false for , := range append([]layered.LEdgeID(nil), .NodeOutgoingEdges()...) { := for := + 1; < len(); ++ { := createDummyNode(, , ) .SetNodeLayer(, &[]) = intermediate.SplitEdgeAtDummy(, , ) } if { for := 0; <= ; ++ { := .AddNode() .Node().Type = layered.NonShiftingPlaceholderNode insertNode(, , [], +1) } ++ } = true } if > 0 { return - 1 } return 0 } func insertNode( *layered.LGraphArena, layered.LNodeID, layered.LayerID, int, ) { if > len(.Layer().Nodes) { .SetNodeLayer(, &) } else { .SetNodeLayerAt(, &, ) } } func createDummyNode( *layered.LGraphArena, layered.LGraphID, layered.LEdgeID, ) layered.LNodeID { := .AddNode() := .Node() .Type = layered.LongEdgeNode basegraph.Set(&.Properties, layered.OriginalElement, layered.LEdgeOrigin()) basegraph.Set(&.Properties, layered.PortConstraintsOption, layered.PortConstraintsFixedPosition) return }