package routing

import (
	
	
	
	
	
	
	

	

	
)

// Orthogonal Visibility Graph
type OVG struct {
	NodesInsideBoundingBox []*layoutgraph.Node
	Nodes                  []*OVGNode
	OccupiedPoints         map[geo.Point]*OVGNode
	Edges                  []*OVGEdge
	VerticalEdges          map[float64][]*OVGEdge
	HorizontalEdges        map[float64][]*OVGEdge
	Ports                  map[*layoutgraph.Node][]*OVGNode
	Centers                map[*layoutgraph.Node]*OVGNode

	fixedOverlapsMu    sync.Mutex
	fixedOverlapsCache []fixedOverlapsCacheEntry
	buildGuard         *ovgBuildGuard
}

type fixedOverlapsCacheEntry struct {
	graph    *layoutgraph.Graph
	nodes    layoutgraph.Nodes
	overlaps map[*layoutgraph.Node]struct{}
}

func ( *OVG) ( *OVGNode) {
	.Nodes = append(.Nodes, )
	.OccupiedPoints[*.Point] = 
}

func ( *OVG) ( *OVGNode) *OVGNode {
	if ,  := .OccupiedPoints[*.Point];  {
		return 
	} else {
		.AddNodeUnchecked()
		return 
	}
}

func ( *OVG) (,  *OVGNode) *OVGEdge {
	if .IsNodeCenter && !.isPort() || .IsNodeCenter && !.isPort() {
		// only ports can connect to center
		return nil
	}

	 := NewOVGEdge(, )
	.Edges = append(.Edges, )
	.addEdge()
	.addEdge()
	if .isVertical() {
		.VerticalEdges[.X] = append(.VerticalEdges[.X], )
	} else if .isHorizontal() {
		.HorizontalEdges[.Y] = append(.HorizontalEdges[.Y], )
	}
	// this distance doesn't change as the OVG nodes don't move around
	// so it is safe to precompute it here and just use it later on during edge routing
	.Distance = geo.EuclideanDistance(
		.X,
		.Y,
		.X,
		.Y,
	)
	return 
}

func ( *OVG) ( *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	 := make([]*OVGNode, 0)

	for ,  := range .Nodes {
		if  := .step();  != nil {
			return 
		}
		if len(.Edges) > 0 {
			 = append(, )
		}
	}

	.Nodes = 
	return .check()
}

func ( []*layoutgraph.Node) *OVG {
	return &OVG{
		NodesInsideBoundingBox: ,
		Ports:                  map[*layoutgraph.Node][]*OVGNode{},
		Centers:                map[*layoutgraph.Node]*OVGNode{},
		OccupiedPoints:         map[geo.Point]*OVGNode{},
		VerticalEdges:          map[float64][]*OVGEdge{},
		HorizontalEdges:        map[float64][]*OVGEdge{},
	}
}

func newBuildOVG( []*layoutgraph.Node,  *ovgBuildGuard) *OVG {
	 := NewOVG()
	.buildGuard = 
	return 
}

func ( *OVG) () {
	.OccupiedPoints = make(map[geo.Point]*OVGNode, len(.Nodes))
	for ,  := range .Nodes {
		.OccupiedPoints[*.Point] = 
	}
}

func buildOVGFromGraphWithLimits( context.Context,  *layoutgraph.Graph,  []*layoutgraph.Node,  ovgBuildLimits) (*OVG, error) {
	,  := newOVGBuildGuard(, )
	if  != nil {
		return nil, 
	}
	return buildOVGFromGraphWithGuard(, , )
}

func buildOVGFromGraphWithGuard( *layoutgraph.Graph,  []*layoutgraph.Node,  *ovgBuildGuard) (*OVG, error) {
	if  := .check();  != nil {
		return nil, 
	}
	,  := checkedOVGSliceCapacity(len(.Nodes), len())
	if  != nil {
		return nil, 
	}
	 := make([]*layoutgraph.Node, 0, )
	 = append(, ...)

	var  []*OVG
	 := make(map[*layoutgraph.Hierarchy]struct{})
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return nil, 
		}
		if .Hierarchy == nil {
			 = append(, )
		} else if ,  := [.Hierarchy]; ! {
			[.Hierarchy] = struct{}{}
			,  := newOVGForHierarchy(, .Hierarchy, )
			if  != nil {
				return nil, 
			}
			 = append(, )
		}
	}

	 := newBuildOVG(, )
	if  := .addPorts(, );  != nil {
		return nil, 
	}
	for ,  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		if  := .mergePorts(, );  != nil {
			return nil, 
		}
	}
	if  := .addNodesIntersections(, );  != nil {
		return nil, 
	}
	for ,  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		if  := .mergeNonPorts(, );  != nil {
			return nil, 
		}
	}

	if  := .addEdgesNodes(, );  != nil {
		return nil, 
	}
	if  := .addTreeNodes(, );  != nil {
		return nil, 
	}
	, ,  := .tightBoundingBox(layoutgraph.Nodes(.NodesInsideBoundingBox))
	if  != nil {
		return nil, 
	}
	if  := .addNewBoundaryLayers(, , , );  != nil {
		return nil, 
	}
	if  := .addPortConnectionNodesAtBoundaries(, , , );  != nil {
		return nil, 
	}
	if  := .addCornerNodes(, , , );  != nil {
		return nil, 
	}
	if  := .addTunnels(, );  != nil {
		return nil, 
	}

	// TODO if any OVG nodes are too close to container edge, split them
	if  := .connectNodes(, );  != nil {
		return nil, 
	}
	if  := .connectPortsToCenter();  != nil {
		return nil, 
	}
	if  := .removeIsolatedNodes();  != nil {
		return nil, 
	}
	if  := .mapNodesToContainer(, );  != nil {
		return nil, 
	}
	if  := .flagNodesNearPorts();  != nil {
		return nil, 
	}
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return nil, 
		}
		.Index = 
	}

	return , nil
}

// For each tala.Node, get the ports and add them as OVG Nodes
func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	// If all of a container's children have no connections, there's no need to consider them for pathing
	 := make(map[*layoutgraph.Node]struct{})
	for ,  := range .Containers {
		if  := .step();  != nil {
			return 
		}
		 := false
		for ,  := range  {
			if  := .step();  != nil {
				return 
			}
			if len(.Edges) > 0 {
				 = true
				break
			}
		}
		if ! {
			for ,  := range  {
				if  := .step();  != nil {
					return 
				}
				[] = struct{}{}
			}
		}
	}

	// Add ports
	for ,  := range .NodesInsideBoundingBox {
		if  := .step();  != nil {
			return 
		}
		if ,  := [];  {
			continue
		}
		// most shapes have 12 ports (3 on each side)
		.Ports[] = make([]*OVGNode, 0, 12)
		for ,  := range .SnapPointPercentages() {
			if  := .step();  != nil {
				return 
			}
			for ,  := range  {
				if  := .step();  != nil {
					return 
				}
				,  := .addPoint(, geo.NewPoint(.TopLeft.X+math.Round(.Width*.XPercentage), .TopLeft.Y+math.Round(.Height*.YPercentage)))
				if  != nil {
					return 
				}
				.Ports[] = append(.Ports[], )
				var  geo.Orientation
				if  == 0 {
					 = geo.Top
				} else if  == 1 {
					 = geo.Left
				} else if  == 2 {
					 = geo.Bottom
				} else if  == 3 {
					 = geo.Right
				}
				.addPortOwner(, , false)
			}
		}

		 := .Ports[]
		for ,  := range .CenterPortIndices() {
			if  := .step();  != nil {
				return 
			}
			[].setCenterPort()
		}
	}
	return .check()
}

// For all OVG Nodes added from ports, take their x/y cartesian product to create a grid of OVG Nodes
func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	 := make(map[float64]struct{})
	 := make(map[float64]struct{})

	for ,  := range .Nodes {
		if  := .step();  != nil {
			return 
		}
		[.Point.X] = struct{}{}
		[.Point.Y] = struct{}{}
	}

	return .addIntersections(, , , )
}

func ( *OVG) ( *layoutgraph.Graph, ,  map[float64]struct{},  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	,  := checkedOVGIntersectionCount(len(), len())
	if  != nil {
		return 
	}
	// Reserve the complete Cartesian product before allocating its orderings or
	// entering the loop. This makes a limit failure deterministic and cheap.
	if  := .reserveCandidates();  != nil {
		return 
	}
	if  := .reserveWork();  != nil {
		return 
	}

	 := make([]float64, 0, len())
	for  := range  {
		if  := .check();  != nil {
			return 
		}
		 = append(, )
	}
	if  := .reserveSortWork(len());  != nil {
		return 
	}
	slices.Sort()

	 := make([]float64, 0, len())
	for  := range  {
		if  := .check();  != nil {
			return 
		}
		 = append(, )
	}
	if  := .reserveSortWork(len());  != nil {
		return 
	}
	slices.Sort()

	 := layoutgraph.Nodes(.Nodes)
	,  := .fixedOverlapsForBuild(, , )
	if  != nil {
		return 
	}
	,  := newOVGPortIndex(.Ports, , , )
	if  != nil {
		return 
	}
	,  := newOVGPointProximityIndex(, , )
	if  != nil {
		return 
	}

	// TODO don't have cross section of port nodes from container to descendants
	// Cross section of all the port nodes
	for ,  := range  {
		if  := .check();  != nil {
			return 
		}
		for ,  := range  {
			if  := .check();  != nil {
				return 
			}
			// Can't be too close to any port or node
			,  := .tooClose(, , layoutgraph.MinPortClearance, )
			if  != nil {
				return 
			}
			if  {
				continue
			}
			// Charge candidate inspection before doing any geometry, but keep the
			// candidate on the stack until it survives both rejection filters.
			if  := .step();  != nil {
				return 
			}
			 := geo.Point{X: , Y: }
			,  := .pointNear(, , )
			if  != nil {
				return 
			}
			if  {
				continue
			}
			 := OVGNode{Point: &}
			,  := .hasUnobstructedLineToPorts(, , 2, )
			if  != nil {
				return 
			}
			if  {
				 := NewOVGNode(geo.NewPoint(, ))
				if ,  := .addNode(, );  != nil {
					return 
				}
			}
		}
	}
	return .check()
}

// For each edge in tala.Edges, add nodes on the edge perimeter (bounding box) and half-way through the bounding box
func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	 := layoutgraph.Nodes(.Nodes)
	,  := .fixedOverlapsForBuild(, , )
	if  != nil {
		return 
	}
	,  := newOVGPortIndex(.Ports, , , )
	if  != nil {
		return 
	}
	for ,  := range .Edges {
		if  := .step();  != nil {
			return 
		}
		if .IsLoop() {
			continue
		}
		if .From.Hierarchy != nil && .To.Hierarchy != nil && .To.Hierarchy == .From.Hierarchy {
			// if the edge is between two nodes in the same hierarchy, it was already handled
			// by the hierarchical OVG routine
			// though, there can be edges between nodes in two different hierarchies
			// in that case, these points must be created
			continue
		}
		,  := .perimeterPoints(.From, .To, )
		if  != nil {
			return 
		}
		,  := .halfwayPoints(.From, .To, )
		if  != nil {
			return 
		}
		 = append(, ...)
		for ,  := range  {
			if  := .step();  != nil {
				return 
			}
			if  := .step();  != nil {
				return 
			}
			,  := .pointNearGraphNode(, )
			if  != nil {
				return 
			}
			if  {
				continue
			}
			 := OVGNode{Point: }
			,  := .hasUnobstructedLineToPorts(, , 1, )
			if  != nil {
				return 
			}
			if  {
				 := NewOVGNode()
				if ,  := .addNode(, );  != nil {
					return 
				}
			}
		}
	}
	return .check()
}

func ( *OVG) (,  *layoutgraph.Node,  *ovgBuildGuard) ([]*geo.Point, error) {
	 := make([]*geo.Point, 0)
	// Perimeter points
	 := math.Min(.TopLeft.Y, .TopLeft.Y) - overshootAmount
	 := math.Max(.TopLeft.Y+.Height, .TopLeft.Y+.Height) + overshootAmount
	 := math.Min(.TopLeft.X, .TopLeft.X) - overshootAmount
	 := math.Max(.TopLeft.X+.Width, .TopLeft.X+.Width) + overshootAmount

	for ,  := range []*layoutgraph.Node{, } {
		if  := .step();  != nil {
			return nil, 
		}
		,  := .portsByOrientation(, , geo.Top)
		if  != nil {
			return nil, 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(.Point.X, ))
		}
		,  := .portsByOrientation(, , geo.Bottom)
		if  != nil {
			return nil, 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(.Point.X, ))
		}
		,  := .portsByOrientation(, , geo.Left)
		if  != nil {
			return nil, 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(, .Point.Y))
		}
		,  := .portsByOrientation(, , geo.Right)
		if  != nil {
			return nil, 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(, .Point.Y))
		}
	}

	 = append(, geo.NewPoint(, ))
	 = append(, geo.NewPoint(, ))
	 = append(, geo.NewPoint(, ))
	 = append(, geo.NewPoint(, ))
	return , .check()
}

// add points halfway between node and otherNode
func ( *OVG) (,  *layoutgraph.Node,  *ovgBuildGuard) ([]*geo.Point, error) {
	 := make([]*geo.Point, 0)
	 := .Orientation()
	if  == geo.NONE {
		return , .check()
	}
	// Halfway points
	// Fill from node to otherNode, which means if node is to the left of otherNode, we are filling rightwards, from node
	 := false
	 := false
	 := false
	 := false
	switch  {
	case geo.Top:
		 = true
	case geo.TopLeft:
		 = true
		 = true
	case geo.TopRight:
		 = true
		 = true
	case geo.Bottom:
		 = true
	case geo.BottomLeft:
		 = true
		 = true
	case geo.BottomRight:
		 = true
		 = true
	case geo.Left:
		 = true
	case geo.Right:
		 = true
	}

	 := .TopLeft.Copy()
	 := geo.NewPoint(math.Round(.X+.Width), math.Round(.Y+.Height))

	 := .TopLeft.Copy()
	 := geo.NewPoint(math.Round(.X+.Width), math.Round(.Y+.Height))

	// all cluster nodes should use the same halfway points based on the whole cluster
	if .Cluster != nil {
		var  error
		, ,  = .tightBoundingBox(layoutgraph.Nodes(.Cluster.Nodes))
		if  != nil {
			return nil, 
		}
	}
	if .Cluster != nil {
		var  error
		, ,  = .tightBoundingBox(layoutgraph.Nodes(.Cluster.Nodes))
		if  != nil {
			return nil, 
		}
	}

	 := .Ports[]
	 := .Ports[]
	if .Sequence != nil {
		var  error
		, ,  = .tightBoundingBox(layoutgraph.Nodes(.Sequence.Nodes))
		if  != nil {
			return nil, 
		}
		for ,  := range .Sequence.Nodes {
			if  := .step();  != nil {
				return nil, 
			}
			if  !=  {
				 = append(, .Ports[]...)
			}
		}
	}
	if .Sequence != nil {
		var  error
		, ,  = .tightBoundingBox(layoutgraph.Nodes(.Sequence.Nodes))
		if  != nil {
			return nil, 
		}
		for ,  := range .Sequence.Nodes {
			if  := .step();  != nil {
				return nil, 
			}
			if  !=  {
				 = append(, .Ports[]...)
			}
		}
	}

	if  {
		 := (.Y + .Y) / 2
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(.Point.X, ))
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(.Point.X, ))
		}
	}
	if  {
		 := (.Y + .Y) / 2
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(.Point.X, ))
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(.Point.X, ))
		}
	}
	if  {
		 := (.X + .X) / 2
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(, .Point.Y))
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(, .Point.Y))
		}
	}
	if  {
		 := (.X + .X) / 2
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(, .Point.Y))
		}
		for ,  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, geo.NewPoint(, .Point.Y))
		}
	}

	// These are the corner points for the halfway fill points
	if  &&  {
		 := (.X + .X) / 2
		 := (.Y + .Y) / 2
		 = append(, geo.NewPoint(, ))
	}

	if  &&  {
		 := (.X + .X) / 2
		 := (.Y + .Y) / 2
		 = append(, geo.NewPoint(, ))
	}

	if  &&  {
		 := (.X + .X) / 2
		 := (.Y + .Y) / 2
		 = append(, geo.NewPoint(, ))
	}

	if  &&  {
		 := (.X + .X) / 2
		 := (.Y + .Y) / 2
		 = append(, geo.NewPoint(, ))
	}
	return , .check()
}

func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	 := make(map[*OVGNode]map[layoutgraph.Arrowhead]struct{})
	// If there are trees in this subgraph, we need to add ovg nodes for the specific tree edge routes
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return 
		}
		if ,  := .NodeToTree[];  {
			,  := treeEdgePathForBuild(, .Ports, , )
			if  != nil {
				return 
			}

			if ,  := .addPoint(, .SourceMidpoint);  != nil {
				return 
			}
			if ,  := .addPoint(, .TargetMidpoint);  != nil {
				return 
			}
			// if the shape centers are misaligned, treePath may use a new parent-aligned port so we add it here
			if isSentinelEdgeSource() {
				,  := .addNode(, .SourcePortNode)
				if  != nil {
					return 
				}
				if !.isPortOf() {
					.Ports[] = append(.Ports[], )
					.addPortOwner(, .SourceOrientationToTarget.GetOpposite(), false)
				}

				if ,  := [.TargetPortNode]; ! {
					[.TargetPortNode] = make(map[layoutgraph.Arrowhead]struct{})
				}
				[.TargetPortNode][.SentinelEdge.TargetArrowhead] = struct{}{}
			} else {
				,  := .addNode(, .TargetPortNode)
				if  != nil {
					return 
				}
				if !.isPortOf() {
					.Ports[] = append(.Ports[], )
					.addPortOwner(, .SourceOrientationToTarget, false)
				}

				if ,  := [.SourcePortNode]; ! {
					[.SourcePortNode] = make(map[layoutgraph.Arrowhead]struct{})
				}
				[.SourcePortNode][.SentinelEdge.SourceArrowhead] = struct{}{}
			}
		}
	}
	return .check()
}

// addNewBoundaryLayers adds new nodes at the graph boundaries
// For example, take the graph boundary below
// .	┌──*───────────────*──────┐
// .	│                         │
// .	│                         │
// .	│                         │
// .	*                         │
// .	│                         *
// .	│                         │
// .	│                         │
// .	│                         *
// .	│                         │
// .	*                         │
// .	│                         *
// .	│                         │
// .	└─────────────*───────────┘
// Results in (just the first layer, the routine will add some more)
// .	┌────*───────────────*────────┐
// .	│ ┌──*───────────────*──────┐ │
// .	│ │                         │ │
// .	│ │                         │ │
// .	│ │                         │ │
// .	* *                         │ │
// .	│ │                         * *
// .	│ │                         │ │
// .	│ │                         │ │
// .	│ │                         * *
// .	│ │                         │ │
// .	* *                         │ │
// .	│ │                         * *
// .	│ │                         │ │
// .	│ └─────────────*───────────┘ │
// .	└───────────────*─────────────┘
func ( *OVG) ( *layoutgraph.Graph, ,  *geo.Point,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	// Don't count containers for children obstruction
	// Pad boundaries with points to allow edges to reach around
	for  := 0.0;  < extraInterestingPointLayers; ++ {
		if  := .step();  != nil {
			return 
		}
		for ,  := range .Nodes {
			if  := .step();  != nil {
				return 
			}
			var  *geo.Point
			if .Point.Y == .Y {
				 = geo.NewPoint(.Point.X, .Point.Y-(ovgPadding*(+1)))
			}
			if .Point.Y == .Y {
				 = geo.NewPoint(.Point.X, .Point.Y+(ovgPadding*(+1)))
			}
			if .Point.X == .X {
				 = geo.NewPoint(.Point.X-(ovgPadding*(+1)), .Point.Y)
			}
			if .Point.X == .X {
				 = geo.NewPoint(.Point.X+(ovgPadding*(+1)), .Point.Y)
			}

			if  != nil {
				,  := .pointNearGraphNode(, )
				if  != nil {
					return 
				}
				if ! {
					if ,  := .addPoint(, );  != nil {
						return 
					}
				}
			}
		}
	}
	return nil
}

// For each given OVG node, try to add a new node at the graph boundaries that make a direct connection to this node
func ( *OVG) ( *layoutgraph.Graph, ,  *geo.Point,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	// Don't count containers for children obstruction
	// Pad boundaries with points to allow edges to reach around
	for  := 0.0;  < extraInterestingPointLayers; ++ {
		if  := .step();  != nil {
			return 
		}
		for ,  := range .Ports {
			if  := .step();  != nil {
				return 
			}
			 := false
			 := make(map[*OVGNode]struct{}, len())
			for ,  := range  {
				if  := .step();  != nil {
					return 
				}
				if ,  := [];  {
					continue
				}
				[] = struct{}{}
				,  := .portDirectionsFor()
				var  error
				.any(func( geo.Orientation) bool {
					if  := .step();  != nil {
						 = 
						return true
					}
					 := geo.NewPoint(.Point.X, .Y-(ovgPadding*(+1)))
					 := geo.NewPoint(.Point.X, .Y+(ovgPadding*(+1)))
					 := geo.NewPoint(.X-(ovgPadding*(+1)), .Point.Y)
					 := geo.NewPoint(.X+(ovgPadding*(+1)), .Point.Y)

					for ,  := range []*geo.Point{, , , } {
						if  := .step();  != nil {
							 = 
							return true
						}
						 := false
						for ,  := range .NodesInsideBoundingBox {
							if  := .step();  != nil {
								 = 
								return true
							}
							,  := .passesThroughAllowingPorts(, .Point, , , .Ports[])
							if  != nil {
								 = 
								return true
							}
							if  {
								// We make exception for container
								 := false
								if .IsContainer() {
									,  := .isDescendantOf(, )
									if  != nil {
										 = 
										return true
									}
									if  {
										 = true
									}
								}
								if ! {
									 = true
									break
								}
							}
						}
						if  {
							continue
						}
						,  := .pointNearGraphNode(, )
						if  != nil {
							 = 
							return true
						}
						if ! {
							if ,  := .addPoint(, );  != nil {
								 = 
								return true
							}
							 = true
						}
					}
					return false
				})
				if  != nil {
					return 
				}
			}
			if ! {
				if  := .createPortsConnectionsToBoundaries(, , , , );  != nil {
					return 
				}
			}
		}
	}
	return nil
}

func ( *OVG) ( *layoutgraph.Graph, ,  *geo.Point,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	// Fill in the corners of boundary points
	for  := 0.0;  < extraInterestingPointLayers; ++ {
		if  := .step();  != nil {
			return 
		}
		 := []*geo.Point{
			{X: .X - (ovgPadding * ( + 1)), Y: .Y - (ovgPadding * ( + 1))},
			{X: .X + (ovgPadding * ( + 1)), Y: .Y - (ovgPadding * ( + 1))},
			{X: .X + (ovgPadding * ( + 1)), Y: .Y + (ovgPadding * ( + 1))},
			{X: .X - (ovgPadding * ( + 1)), Y: .Y + (ovgPadding * ( + 1))},
		}

		for ,  := range  {
			,  := .pointNearGraphNode(, )
			if  != nil {
				return 
			}
			if ! {
				if ,  := .addPoint(, );  != nil {
					return 
				}
			}
		}
	}
	return .check()
}

func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	,  := buildTunnels(, )
	if  != nil {
		return 
	}
	for ,  := range  {
		if  := .step();  != nil {
			return 
		}
		for ,  := range []*TunnelEntry{.EntryA, .EntryB} {
			if  := .step();  != nil {
				return 
			}
			 := .OVGNode
			,  := .addNode(, )
			if  != nil {
				return 
			}
			if  !=  {
				for ,  := range .portOwners() {
					if  := .step();  != nil {
						return 
					}
					.addPortMetadata(, )
				}
				.OVGNode = 
			}
			.OVGNode.IsTunnel = true
			.Ports[.Node] = append(.Ports[.Node], .OVGNode)
		}

		if ,  := .connect(, .EntryA.OVGNode, .EntryB.OVGNode);  != nil {
			return 
		}
	}
	return .check()
}

func ( *OVG) ( *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	for ,  := range .NodesInsideBoundingBox {
		if  := .step();  != nil {
			return 
		}
		,  := .newCandidateNode(.Center())
		if  != nil {
			return 
		}
		.Centers[] = 
		// We don't care that it's the center of an invisible node, since that's not special
		if !.IsInvisible {
			.IsNodeCenter = true
		}
		if  := .addNodeUnchecked(, );  != nil {
			return 
		}
		for ,  := range .Ports[] {
			if ,  := .connect(, , );  != nil {
				return 
			}
		}
	}
	return .check()
}

func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	,  := .containerRDFSOrder(, nil)
	if  != nil {
		return 
	}
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return 
			}
			if .ContainsPointOnBox(.Point) {
				.Container = 
				break
			}
		}
	}
	return nil
}

// connectNodes connects all OVG nodes to the nearest node on the left, top, right and bottom if the edge
// doesn't go through any graph node.
//
// | This routine works as:
// | 1. Collects all nodes in each row (same Y coordinate) and column (same X coordinate)
// | 2. For each of these X/Y coordinates, sort the nodes from smallest to largest using the opposite coordinate
// | 	- Nodes on the same column (X coordinate) are sorted by the Y coordinate so they go from top to bottom
// | 3. Connect nodes in sequence (node[i] with node[i+1]) as these are the nearest ones in the orthogonal graph
// | 	- Only connect if the edge doesn't go through any other node in the Graph
//
// Based on Sweep Line Algorithm
// https://en.wikipedia.org/wiki/Sweep_line_algorithm
func ( *OVG) ( *layoutgraph.Graph,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	if uint64(len(.Edges)) > .edges {
		if  := .reserveEdges(uint64(len(.Edges)) - .edges);  != nil {
			return 
		}
	}
	// Create edges
	 := map[float64][]*OVGNode{}
	 := map[float64][]*OVGNode{}
	 := uint64(0)
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return 
		}
		// Tunnels can only connect to each other (and to their node centers)
		if .IsTunnel {
			continue
		}

		// sequence steps should only connect from certain sides
		,  := .isRestrictedSequencePort()
		if  != nil {
			return 
		}
		if  {
			continue
		}

		++
		[.Y] = append([.Y], )
		[.X] = append([.X], )
	}

	 := func( map[float64][]*OVGNode) ([]float64, error) {
		var  []float64
		for  := range  {
			if  := .step();  != nil {
				return nil, 
			}
			 = append(, )
		}
		if  := .reserveSortWork(len());  != nil {
			return nil, 
		}
		slices.Sort()

		return , nil
	}

	// Each eligible node appears in one horizontal and one vertical sweep.
	// The two sweeps therefore add at most (n-h) + (n-v) edges.
	// *---*---*---*---*
	// |   |   |   |   |
	// *---*---*---*---*
	// |   |   |   |   |
	// *---*---*---*---*
	// |   |   |   |   |
	// *---*---*---*---*
	 := uint64(len())
	 := uint64(len())
	,  := checkedOVGEdgeCapacity(uint64(len(.Edges)), , , , .limits.edges, maxIntAsUint64())
	if  != nil {
		return 
	}
	// we need to copy existing edges (e.g., we already connect the nodes when adding tunnels)
	 := make([]*OVGEdge, len(.Edges), )
	copy(, .Edges)
	.Edges = 
	 := layoutgraph.Nodes(.NodesInsideBoundingBox)
	,  := .fixedOverlapsForBuild(, , )
	if  != nil {
		return 
	}
	,  := ()
	if  != nil {
		return 
	}
	for ,  := range  {
		if  := .step();  != nil {
			return 
		}
		if  := .connectNodesOnSameLine([], , true, , );  != nil {
			return 
		}
	}

	,  := ()
	if  != nil {
		return 
	}
	for ,  := range  {
		if  := .step();  != nil {
			return 
		}
		if  := .connectNodesOnSameLine([], , false, , );  != nil {
			return 
		}
	}
	return nil
}

// connectNodesOnSameLine connects nodes on the same horizontal/vertical line.
// The nodes MUST be on the same line, so they are sorted and the connection is just connecting to the next
// node in the list.
// See connectNodes for more details around the algorithm
func ( *OVG) ( []*OVGNode,  float64,  bool,  map[*layoutgraph.Node]struct{},  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}

	var  []*layoutgraph.Node
	for ,  := range .NodesInsideBoundingBox {
		if  := .step();  != nil {
			return 
		}
		if ,  := [];  {
			continue
		}
		if  && .TopLeft.Y <=  && .TopLeft.Y+.Height >=  {
			 = append(, )
		} else if ! && .TopLeft.X <=  && .TopLeft.X+.Width >=  {
			 = append(, )
		}
	}
	if  := .reserveSortWork(len());  != nil {
		return 
	}

	sort.Slice(, func(,  int) bool {
		if  {
			return [].TopLeft.X < [].TopLeft.X
		}
		return [].TopLeft.Y < [].TopLeft.Y
	})

	if  := .reserveSortWork(len());  != nil {
		return 
	}
	sort.Slice(, func(,  int) bool {
		if  {
			return [].X < [].X
		}
		return [].Y < [].Y
	})

	 := 0
	for  := 0;  < len()-1; ++ {
		if  := .step();  != nil {
			return 
		}
		 := []
		 := [+1]
		,  := .isMisdirectedPortPair(, , , )
		if  != nil {
			return 
		}
		if  {
			continue
		}

		 := true
		for  := ;  < len(); ++ {
			if  := .step();  != nil {
				return 
			}
			 := []
			if ( && .TopLeft.X > .X) || (! && .TopLeft.Y > .Y) {
				// here, rectangle is after closest to the right or to the bottom
				// since intersectCandidates is sorted, no other nodes will intersect, so we can skip the test
				break
			}

			 := func(,  *OVGNode) (bool, error) {
				 := true
				var  error
				.portDirectionsForObstacle().any(func( geo.Orientation) bool {
					,  := .passesThroughAllowingPorts(, .Point, .Point, , .Ports[])
					if  != nil {
						 = 
						return true
					}
					if ! {
						 = false
						return true
					}
					return false
				})
				return , 
			}
			,  := (, )
			if  != nil {
				return 
			}
			,  := (, )
			if  != nil {
				return 
			}
			if  &&  {
				if .Sequence != nil {
					// sequences will have top and bottom ports overlapped by previous sequence nodes, but this overlap shouldn't prevent a connection with that port
					if ! {
						if .TopLeft.Y == .Y || .TopLeft.Y+.Height == .Y {
							continue
						}
					}
				}
				// Passing through a container is okay ...
				 := false
				if .IsContainer() {
					// ... if at least one of the nodes is within the container
					if .ContainsPointOnBox(.Point) || .ContainsPointOnBox(.Point) {
						 = true
					}
				}

				if ! {
					 = false
					break
				}
			} else {
				// this node didn't intersect with the current OVG, so we don't need to
				// check any other nodes prior to it for the next OVG nodes
				// (the loop is on top of sorted nodes)
				 = 
			}
		}

		if  {
			if ,  := .connect(, , );  != nil {
				return 
			}
		}
	}
	return nil
}

// Note: assumes first node is left(IsHorizontal=true) or above(IsHorizontal=false) second
func ( *OVG) (,  *OVGNode,  bool,  *ovgBuildGuard) (bool, error) {
	if  := .check();  != nil {
		return false, 
	}
	 := .portOwners()
	 := .portOwners()
	if len() == 0 || len() == 0 {
		return false, .check()
	}

	// A shared point can represent ports of several touching nodes. Reject the
	// connection only if every possible owner pair is misdirected; one valid
	// interpretation is enough to make the OVG segment usable.
	for ,  := range  {
		if  := .step();  != nil {
			return false, 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return false, 
			}
			if  ==  {
				return false, nil
			}

			// Since first is left/above second, at least one pair of roles must
			// face each other (not the other way around).
			 := .directions.has(geo.Bottom) && .directions.has(geo.Top)
			if  {
				 = .directions.has(geo.Right) && .directions.has(geo.Left)
			}
			// ports must face each other unless it is a descendant and an ancestor
			,  := .isDescendantOf(, )
			if  != nil {
				return false, 
			}
			,  := .isDescendantOf(, )
			if  != nil {
				return false, 
			}
			if  ||  ||  {
				return false, nil
			}
		}
	}
	return true, .check()
}

// createPortsConnectionsToBoundaries will create paths for isolated graph nodes.
// If the ports of a given node don't have a clear path to the graph boundaries we might not be able to trace a route to this node.
// This function tries to fix this case by adding a padded port node and nodes at the graph boundaries that they can connect to.
//
// Take the example below where `n` ports don't have a clear path to the boudaries (only 1 shown here).
// .	┌─────────────────────────────────────────┐
// .	│  ┌──────┐       ┌──────┐      ┌──────┐  │
// .	│  │      │       │      │      │      │  │
// .	│  │      │       │      │      │      │  │
// .	│  │      │       │      │      │      │  │
// .	│  └──────┘       └──────┘      └──────┘  │
// .	│                                         │
// .	│                                         │
// .	│  ┌──────┐       ┌─*──*─┐      ┌──────┐  │
// .	│  │      │       *      *      │      │  │
// .	│  │      │       │   n  │      │      │  │
// .	│  │      │       *      *      │      │  │
// .	│  └──────┘       └─*──*─┘      └──────┘  │
// .	│                                         │
// .	│                                         │
// .	│  ┌──────┐       ┌──────┐      ┌──────┐  │
// .	│  │      │       │      │      │      │  │
// .	│  │      │       │      │      │      │  │
// .	│  │      │       │      │      │      │  │
// .	│  └──────┘       └──────┘      └──────┘  │
// .	└─────────────────────────────────────────┘
//
// In this case, route search won't be able to trace a route to `n`.
// To relax it a bit, we pad `n` ports and create some nodes at the graph boundaries that might turn into an OVG edge later on
// if there no other graph nodes in between them.
// Here is the expected result (only one boundary layer shown)
// . 	┌──────────────*────────────*─────────────┐
// . 	│  ┌──────┐       ┌──────┐      ┌──────┐  │
// . 	│  │      │       │      │      │      │  │
// . 	│  │      │       │      │      │      │  │
// . 	│  │      │       │      │      │      │  │
// . 	│  └──────┘       └──────┘      └──────┘  │
// . 	│                                         │
// . 	*                   *  *                  *
// . 	│  ┌──────┐       ┌─*──*─┐      ┌──────┐  │
// . 	│  │      │    *  *      *  *   │      │  │
// . 	│  │      │       │   n  │      │      │  │
// . 	│  │      │    *  *      *  *   │      │  │
// . 	│  └──────┘       └─*──*─┘      └──────┘  │
// . 	*                   *  *                  *
// . 	│                                         │
// . 	│  ┌──────┐       ┌──────┐      ┌──────┐  │
// . 	│  │      │       │      │      │      │  │
// . 	│  │      │       │      │      │      │  │
// . 	│  │      │       │      │      │      │  │
// . 	│  └──────┘       └──────┘      └──────┘  │
// . 	└──────────────*────────────*─────────────┘
func ( *OVG) ( *layoutgraph.Node,  []*OVGNode, ,  *geo.Point,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	 := make(map[*OVGNode]struct{}, len())
	for ,  := range  {
		if  := .step();  != nil {
			return 
		}
		if ,  := [];  {
			continue
		}
		[] = struct{}{}
		,  := .portDirectionsFor()
		var  error
		.any(func( geo.Orientation) bool {
			if  := .step();  != nil {
				 = 
				return true
			}
			var  *geo.Point
			switch  {
			case geo.Top:
				 = geo.NewPoint(.X, .Y-layoutgraph.MinPortClearance)
			case geo.Bottom:
				 = geo.NewPoint(.X, .Y+layoutgraph.MinPortClearance)
			case geo.Left:
				 = geo.NewPoint(.X-layoutgraph.MinPortClearance, .Y)
			case geo.Right:
				 = geo.NewPoint(.X+layoutgraph.MinPortClearance, .Y)
			default:
				return false
			}
			,  := .addPoint(, )
			 = 
			if  != nil {
				return true
			}

			// add nodes at the OVG boundary to connect with this new node
			for  := 1;  < extraInterestingPointLayers+1; ++ {
				if  := .step();  != nil {
					 = 
					return true
				}
				switch  {
				case geo.Top, geo.Bottom:
					// left boundary
					if ,  := .addPoint(, geo.NewPoint(.X-(ovgPadding*float64()), .Y));  != nil {
						 = 
						return true
					}
					// right boundary
					if ,  := .addPoint(, geo.NewPoint(.X+(ovgPadding*float64()), .Y));  != nil {
						 = 
						return true
					}
				case geo.Left, geo.Right:
					// top boundary
					if ,  := .addPoint(, geo.NewPoint(.X, .Y-(ovgPadding*float64())));  != nil {
						 = 
						return true
					}
					// bottom boundary
					if ,  := .addPoint(, geo.NewPoint(.X, .Y+(ovgPadding*float64())));  != nil {
						 = 
						return true
					}
				}
			}
			return false
		})
		if  != nil {
			return 
		}
	}
	return .check()
}

// flagNodesNearPorts finds OVG Nodes that are lined up and too close to ports as they should be penalized during edge routing
// The algorithm works as:
// - for each port
// -   for each lined up adjacent
// -      if adj is not too close: continue
// -      else: flag adj as too close and follow its edges as other nodes can be too close
func ( *OVG) ( *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	for ,  := range .Ports {
		if  := .step();  != nil {
			return 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return 
			}
			// 5 is just a rough estimate for the port + 4 adjacent nodes
			 := make(map[*OVGNode]struct{}, 5)
			[] = struct{}{}
			 := make([]*OVGNode, 0, 5)
			for ,  := range .Edges {
				if  := .step();  != nil {
					return 
				}
				 := .adjacent()
				if .X != .X && .Y != .Y {
					continue
				}
				 = append(, )
			}
			for len() > 0 {
				if  := .step();  != nil {
					return 
				}
				 := [0]
				 = [1:]
				if ,  := [];  {
					continue
				}
				[] = struct{}{}

				// only check non-ports, or ports of other nodes
				// but, if adj is a port of the current graph node, also follow its adjacents
				if !.isPortOf() {
					if math.Abs(.Y-.Y) >= nodeProximityThreshold {
						// the current node is not too close to the port, so no need to investigate the adjacents
						continue
					}
					if math.Abs(.X-.X) >= nodeProximityThreshold {
						// the current node is not too close to the port, so no need to investigate the adjacents
						continue
					}

					if .IsNearPort == nil {
						.IsNearPort = make(map[*layoutgraph.Node]struct{})
					}
					// `curr` is near the port
					.IsNearPort[] = struct{}{}
				}

				// follow other nodes, as they be near too
				for ,  := range .Edges {
					if  := .step();  != nil {
						return 
					}
					 := .adjacent()
					if .X != .X && .Y != .Y {
						continue
					}
					 = append(, )
				}
			}
		}
	}
	return .check()
}

type serializedOVGNode struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

type serializedOVGEdge struct {
	From serializedOVGNode `json:"from"`
	To   serializedOVGNode `json:"to"`
}

type serializedOVG struct {
	Nodes []serializedOVGNode `json:"nodes"`
	Edges []serializedOVGEdge `json:"edges"`
}

var _ json.Marshaler = &OVG{}
var _ json.Unmarshaler = &OVG{}

func ( *OVG) () ([]byte, error) {
	 := serializedOVG{}

	 := func(,  *OVGNode) bool {
		if .X == .X {
			return .Y < .Y
		}
		return .X < .X
	}

	 := make([]*OVGNode, len(.Nodes))
	copy(, .Nodes)
	sort.Slice(, func(,  int) bool {
		return ([], [])
	})
	for ,  := range  {
		.Nodes = append(.Nodes, serializedOVGNode{
			X: .X,
			Y: .Y,
		})
	}

	 := make([]*OVGEdge, len(.Edges))
	copy(, .Edges)
	sort.Slice(, func(,  int) bool {
		if [].From == [].From {
			return ([].To, [].To)
		}
		return ([].From, [].From)
	})
	for ,  := range  {
		.Edges = append(.Edges, serializedOVGEdge{
			From: serializedOVGNode{
				X: .From.X,
				Y: .From.Y,
			},
			To: serializedOVGNode{
				X: .To.X,
				Y: .To.Y,
			},
		})
	}
	return json.Marshal()
}

func ( *OVG) ( []byte) error {
	var  serializedOVG
	if  := json.Unmarshal(, &);  != nil {
		return 
	}

	* = *NewOVG(nil)
	 := make(map[geo.Point]*OVGNode)
	for ,  := range .Nodes {
		 := NewOVGNode(geo.NewPoint(.X, .Y))
		.AddNodeUnchecked()
		[*.Point] = 
	}

	for ,  := range .Edges {
		 := *geo.NewPoint(.From.X, .From.Y)
		 := *geo.NewPoint(.To.X, .To.Y)

		 := []
		 := []
		if  == nil ||  == nil {
			return fmt.Errorf("OVG edge references a missing endpoint: %v -> %v", , )
		}
		.Connect(, )
	}

	return nil
}

func ( *OVG) ( *OVG,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	for ,  := range .Ports {
		if  := .reserveWork(uint64(len()) + 1);  != nil {
			return 
		}
		 := make([]*OVGNode, len())
		for ,  := range  {
			,  := .addNode(, )
			if  != nil {
				return 
			}
			if  !=  {
				for ,  := range .portOwners() {
					if  := .step();  != nil {
						return 
					}
					.addPortMetadata(, )
				}
			}
			[] = 
		}
		.Ports[] = 
	}
	if  := .reserveWork(uint64(len(.NodesInsideBoundingBox)));  != nil {
		return 
	}
	.NodesInsideBoundingBox = append(.NodesInsideBoundingBox, .NodesInsideBoundingBox...)
	return .check()
}

func ( *OVG) ( *OVG,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return 
		}
		if !.isPort() {
			if ,  := .addNode(, );  != nil {
				return 
			}
		}
	}
	return .check()
}