// Copyright (c) 2016 Kiel University and others.
// Copyright (c) 2026 The elk-go Authors.
//
// 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 graph implements ELK's mutable graph as deterministic typed arenas.
package graph import type NodeID uint32 type PortID uint32 type EdgeID uint32 type LabelID uint32 type SectionID uint32 // ShapeID identifies anything an edge may connect to. type ShapeID struct { Node NodeID Port PortID Kind ShapeKind } type ShapeKind uint8 const ( NodeShape ShapeKind = iota PortShape ) func ( NodeID) ShapeID { return ShapeID{Node: , Kind: NodeShape} } func ( PortID) ShapeID { return ShapeID{Port: , Kind: PortShape} } // ElementID identifies anything that may own labels. type ElementID struct { Node NodeID Port PortID Edge EdgeID Label LabelID Kind ElementKind } type ElementKind uint8 const ( NodeElement ElementKind = iota PortElement EdgeElement LabelElement ) func ( NodeID) ElementID { return ElementID{Node: , Kind: NodeElement} } func ( PortID) ElementID { return ElementID{Port: , Kind: PortElement} } func ( EdgeID) ElementID { return ElementID{Edge: , Kind: EdgeElement} } func ( LabelID) ElementID { return ElementID{Label: , Kind: LabelElement} } // Shape stores parent-relative geometry. Presence records which values were // explicitly present in imported JSON before layout. type Shape struct { X, Y, Width, Height float64 Presence ShapePresence } type ShapePresence uint8 const ( HasX ShapePresence = 1 << iota HasY HasWidth HasHeight ) func ( Shape) () geometry.Point { return geometry.Point{X: .X, Y: .Y} } func ( Shape) () geometry.Rectangle { return geometry.Rectangle{X: .X, Y: .Y, Width: .Width, Height: .Height} } func ( *Shape) (, float64) { .X, .Y = , ; .Presence |= HasX | HasY } func ( *Shape) (, float64) { .Width, .Height = , .Presence |= HasWidth | HasHeight } type Node struct { Identifier string IdentifierPresent bool Shape Shape Properties PropertyMap Labels []LabelID Parent *NodeID Children []NodeID Ports []PortID ContainedEdges []EdgeID OutgoingEdges []EdgeID IncomingEdges []EdgeID } type Port struct { Identifier string IdentifierPresent bool Shape Shape Properties PropertyMap Labels []LabelID Parent NodeID OutgoingEdges []EdgeID IncomingEdges []EdgeID } type Label struct { Identifier string IdentifierPresent bool Text string TextPresent bool Shape Shape Properties PropertyMap Labels []LabelID Parent ElementID } type Edge struct { Identifier string IdentifierPresent bool Properties PropertyMap Labels []LabelID ContainingNode *NodeID Sources []ShapeID Targets []ShapeID Sections []SectionID } type EdgeSection struct { Identifier string IdentifierPresent bool Properties PropertyMap Parent EdgeID Start, End geometry.Point StartPresent, EndPresent bool BendPoints geometry.Chain BendPointsPresent bool OutgoingShape *ShapeID IncomingShape *ShapeID OutgoingSections []SectionID IncomingSections []SectionID } // Model owns every element. Arena order is creation order and is never // derived from map iteration. type Model struct { Nodes []Node Ports []Port Edges []Edge Labels []Label Sections []EdgeSection Root NodeID } func () *Model { return &Model{Nodes: []Node{{}}, Root: 0} } func ( *Model) ( NodeID) *Node { return &.Nodes[int()] } func ( *Model) ( PortID) *Port { return &.Ports[int()] } func ( *Model) ( EdgeID) *Edge { return &.Edges[int()] } func ( *Model) ( LabelID) *Label { return &.Labels[int()] } func ( *Model) ( SectionID) *EdgeSection { return &.Sections[int()] } func ( *Model) ( *NodeID) NodeID { := NodeID(len(.Nodes)) .Nodes = append(.Nodes, Node{Parent: copyNodeID()}) if != nil { .Node(*).Children = append(.Node(*).Children, ) } return } func ( *Model) ( NodeID) PortID { := PortID(len(.Ports)) .Ports = append(.Ports, Port{Parent: }) .Node().Ports = append(.Node().Ports, ) return } func ( *Model) ( ElementID, string) LabelID { := LabelID(len(.Labels)) .Labels = append(.Labels, Label{Parent: , Text: }) switch .Kind { case NodeElement: .Node(.Node).Labels = append(.Node(.Node).Labels, ) case PortElement: .Port(.Port).Labels = append(.Port(.Port).Labels, ) case EdgeElement: .Edge(.Edge).Labels = append(.Edge(.Edge).Labels, ) case LabelElement: .Label(.Label).Labels = append(.Label(.Label).Labels, ) } return } func ( *Model) ( *NodeID) EdgeID { := EdgeID(len(.Edges)) .Edges = append(.Edges, Edge{ContainingNode: copyNodeID()}) if != nil { .Node(*).ContainedEdges = append(.Node(*).ContainedEdges, ) } return } func ( *Model) ( EdgeID, ShapeID) { .Edge().Sources = append(.Edge().Sources, ) if .Kind == NodeShape { .Node(.Node).OutgoingEdges = append(.Node(.Node).OutgoingEdges, ) } else { .Port(.Port).OutgoingEdges = append(.Port(.Port).OutgoingEdges, ) } } func ( *Model) ( EdgeID, ShapeID) { .Edge().Targets = append(.Edge().Targets, ) if .Kind == NodeShape { .Node(.Node).IncomingEdges = append(.Node(.Node).IncomingEdges, ) } else { .Port(.Port).IncomingEdges = append(.Port(.Port).IncomingEdges, ) } } func ( *Model) ( EdgeID) SectionID { := SectionID(len(.Sections)) .Sections = append(.Sections, EdgeSection{Parent: }) .Edge().Sections = append(.Edge().Sections, ) return } func ( *Model) ( ShapeID) NodeID { if .Kind == NodeShape { return .Node } return .Port(.Port).Parent } func ( *Model) ( ShapeID) *Shape { if .Kind == NodeShape { return &.Node(.Node).Shape } return &.Port(.Port).Shape } func ( *Model) ( ElementID) *PropertyMap { switch .Kind { case NodeElement: return &.Node(.Node).Properties case PortElement: return &.Port(.Port).Properties case EdgeElement: return &.Edge(.Edge).Properties case LabelElement: return &.Label(.Label).Properties default: panic("unknown graph element kind") } } func ( *Model) ( NodeID) []NodeID { := make([]NodeID, 0) := append([]NodeID(nil), .Node().Children...) for len() > 0 { := [0] = [1:] = append(, ) := .Node().Children if len() > 0 { := make([]NodeID, 0, len()+len()) = append(, ...) = append(, ...) = } } return } func ( *Model) (, NodeID) bool { for := .Node().Parent; != nil; = .Node(*).Parent { if * == { return true } } return false } func ( *Model) ( NodeID) int { := 0 for := .Node().Parent; != nil; = .Node(*).Parent { ++ } return } func ( *Model) (, NodeID) (NodeID, bool) { := make(map[NodeID]struct{}) for := &; != nil; = .Node(*).Parent { [*] = struct{}{} } for := &; != nil; = .Node(*).Parent { if , := [*]; { return *, true } } return 0, false } func ( *Model) ( EdgeID) (NodeID, bool) { := .Edge() := make([]NodeID, 0, len(.Sources)+len(.Targets)) for , := range .Sources { = append(, .ShapeNode()) } for , := range .Targets { = append(, .ShapeNode()) } if len() == 0 { panic("edge must have at least one endpoint") } if len() == 1 { := .Node([0]).Parent if == nil { return 0, false } return *, true } := true for , := range [1:] { if != [0] { = false break } } if { := .Node([0]).Parent if == nil { return 0, false } return *, true } := [0] for , := range [1:] { if == || .IsDescendant(, ) { continue } var bool , = .LowestCommonAncestor(, ) if ! { return 0, false } } return , true } func ( *Model) ( EdgeID) { , := .BestEdgeContainment() := .Edge().ContainingNode if != nil && (! || * != ) { .Node(*).ContainedEdges = removeEdge(.Node(*).ContainedEdges, ) } if ! { .Edge().ContainingNode = nil return } if == nil || * != { .Edge().ContainingNode = copyNodeID(&) .Node().ContainedEdges = append(.Node().ContainedEdges, ) } } // FirstSection returns an edge's first route section. Resetting preserves that // section's identity while replacing the complete pre-existing route. func ( *Model) ( EdgeID, bool) SectionID { if len(.Edge().Sections) == 0 { return .AddSection() } := .Edge().Sections[0] if { .Edge().Sections = .Edge().Sections[:1] := .Section() , , := .Identifier, .IdentifierPresent, .Parent * = EdgeSection{Identifier: , IdentifierPresent: , Parent: } } return } func ( *Model) ( SectionID) geometry.Chain { := .Section() := make(geometry.Chain, 0, len(.BendPoints)+2) = append(, .Start) = append(, .BendPoints...) = append(, .End) return } func copyNodeID( *NodeID) *NodeID { if == nil { return nil } := * return & } func removeEdge( []EdgeID, EdgeID) []EdgeID { for , := range { if == { return append([:], [+1:]...) } } return }