package routing

// Slingshot is an optimization to attempt a rule-based routing before falling back to BFS search
// gravitational slingshot: https://en.wikipedia.org/wiki/Gravity_assist
//
// TERMINOLOGY:
// ============
//
//   - Vertical Launch:
//     In any diagonal orientation, there are two L shapes
//     A "vertical launch" is the one that starts vertically, so in the TopLeft orientation below, describes route 1
//
//   - Anchor:
//     The anchor is the OVG node used to turn/slingshot, "a" below
//
// . ┌────────┐
// . │        │     2
// . │        ├─────────┐
// . │   s    │         │
// . │        │         │
// . └───┬────┘         │
// .     │              │
// .     │              ▼
// .   1 │          ┌────────┐
// .     │          │        │
// .     │          │        │
// .     a─────────►│   t    │
// .                │        │
// .                └────────┘

import (
	
	
	

	
	
	
	
)

// launch simulates the route blasting off from source, going around the anchor, and landing on target
// It returns the distance if successful, with 0.0 meaning it crashed into a node along the way
func ( *ovgEdgeRouter) (, ,  *OVGNode,  *layoutgraph.Edge) (float64, error) {
	 := layoutgraph.Nodes(.ovg.NodesInsideBoundingBox)

	.Points = []*geo.Point{.Point, .Point}
	,  := routeIntersectsNodeGuarded(, , .work)
	if  != nil {
		return 0, 
	}
	if  {
		return 0.0, nil
	}

	.Points = []*geo.Point{.Point, .Point}
	,  = routeIntersectsNodeGuarded(, , .work)
	if  != nil {
		return 0, 
	}
	if  {
		return 0.0, nil
	}

	 := geo.EuclideanDistance(
		.X,
		.Y,
		.X,
		.Y,
	) + geo.EuclideanDistance(
		.X,
		.Y,
		.X,
		.Y,
	)

	return , nil
}

func ( *ovgEdgeRouter) ( bool,  geo.Orientation,  *layoutgraph.Node) []*OVGNode {
	 := .ovg

	 := geo.NONE
	switch  {
	case geo.TopLeft:
		if  {
			 = geo.Bottom
		} else {
			 = geo.Right
		}
	case geo.TopRight:
		if  {
			 = geo.Bottom
		} else {
			 = geo.Left
		}
	case geo.BottomLeft:
		if  {
			 = geo.Top
		} else {
			 = geo.Right
		}
	case geo.BottomRight:
		if  {
			 = geo.Top
		} else {
			 = geo.Left
		}
	}
	if  == geo.NONE {
		return nil
	}

	var  []*OVGNode
	 := make(map[*OVGNode]struct{}, len(.Ports[]))
	for ,  := range .Ports[] {
		if ,  := [];  {
			continue
		}
		[] = struct{}{}
		if .hasPortDirection(, ) {
			 = append(, )
		}
	}
	return 
}

func ( *ovgEdgeRouter) ( bool,  geo.Orientation,  *layoutgraph.Node) []*OVGNode {
	 := .ovg

	 := geo.NONE
	switch  {
	case geo.TopLeft:
		if  {
			 = geo.Left
		} else {
			 = geo.Top
		}
	case geo.TopRight:
		if  {
			 = geo.Right
		} else {
			 = geo.Top
		}
	case geo.BottomLeft:
		if  {
			 = geo.Left
		} else {
			 = geo.Bottom
		}
	case geo.BottomRight:
		if  {
			 = geo.Right
		} else {
			 = geo.Bottom
		}
	}
	if  == geo.NONE {
		return nil
	}

	var  []*OVGNode
	 := make(map[*OVGNode]struct{}, len(.Ports[]))
	for ,  := range .Ports[] {
		if ,  := [];  {
			continue
		}
		[] = struct{}{}
		if .hasPortDirection(, ) {
			 = append(, )
		}
	}
	return 
}

func undershot( *OVGNode,  *layoutgraph.Node,  geo.Orientation,  bool) bool {
	switch  {
	case geo.TopLeft:
		if  && .Y < .TopLeft.Y {
			return true
		} else if ! && .X < .TopLeft.X {
			return true
		}
	case geo.TopRight:
		if  && .Y < .TopLeft.Y {
			return true
		} else if ! && .X > .TopLeft.X+.Width {
			return true
		}
	case geo.BottomLeft:
		if  && .Y > .TopLeft.Y+.Height {
			return true
		} else if ! && .X < .TopLeft.X {
			return true
		}
	case geo.BottomRight:
		if  && .Y > .TopLeft.Y+.Height {
			return true
		} else if ! && .X > .TopLeft.X+.Width {
			return true
		}
	}
	return false
}

// overshot returns true if n is past the target, given the orientation of the source.
// E.g., if the arrow is here, there is no need to keep searching for L-shape or S-shape routes
// . ┌───┐
// . │ s │
// . └─┬─┘
// .   │
// .   │   ┌───┐
// .   │   │ t │
// .   │   └───┘
// .   ▼
//
// L routes can keep going until n is completely past t
// S routes are overshot when n reaches any part of t
func overshot( *OVGNode,  *layoutgraph.Node,  geo.Orientation, ,  bool) bool {
	switch  {
	case geo.TopLeft:
		if  {
			if  && .Y > .TopLeft.Y+.Height {
				return true
			}
			if ! && .Y > .TopLeft.Y {
				return true
			}
		} else {
			if  && .X > .TopLeft.X+.Width {
				return true
			}
			if ! && .X > .TopLeft.X {
				return true
			}
		}
	case geo.TopRight:
		if  {
			if  && .Y > .TopLeft.Y+.Height {
				return true
			}
			if ! && .Y > .TopLeft.Y {
				return true
			}
		} else {
			if  && .X < .TopLeft.X {
				return true
			}
			if ! && .X < .TopLeft.X+.Width {
				return true
			}
		}
	case geo.BottomLeft:
		if  {
			if  && .Y < .TopLeft.Y {
				return true
			}
			if ! && .Y < .TopLeft.Y+.Height {
				return true
			}
		} else {
			if  && .X > .TopLeft.X+.Width {
				return true
			}
			if ! && .X > .TopLeft.X {
				return true
			}
		}
	case geo.BottomRight:
		if  {
			if  && .Y < .TopLeft.Y {
				return true
			}
			if ! && .Y < .TopLeft.Y+.Height {
				return true
			}
		} else {
			if  && .X < .TopLeft.X {
				return true
			}
			if ! && .X < .TopLeft.X+.Width {
				return true
			}
		}
	}
	return false
}

func isOnFlightPlan(,  *OVGNode,  geo.Orientation,  bool) bool {
	switch  {
	case geo.TopLeft:
		if  && .X == .X && .Y > .Y {
			return true
		} else if ! && .Y == .Y && .X > .X {
			return true
		}
	case geo.TopRight:
		if  && .X == .X && .Y > .Y {
			return true
		} else if ! && .Y == .Y && .X < .X {
			return true
		}
	case geo.BottomLeft:
		if  && .X == .X && .Y < .Y {
			return true
		} else if ! && .Y == .Y && .X > .X {
			return true
		}
	case geo.BottomRight:
		if  && .X == .X && .Y < .Y {
			return true
		} else if ! && .Y == .Y && .X < .X {
			return true
		}
	}
	return false
}

// If s and t exist in a vacuum, the choice of 1 and 2 is indistinguishable
// But consider the x's
// If s and t are connected to those x's, route 1 likely impedes on their routes, while route 2 likely won't
// So routes which are less likely to take up the routing space of other nodes are preferred
//
//	┌────────┐
//	│        │     2
//	│        ├─────────┐
//	│   s    │         │
//	│        │         │
//	└───┬────┘         │
//	    │              │
//
// x     │              ▼
//
//	   1 │          ┌────────┐
//	     │          │        │
//	x    │          │        │
//	     └─────────►│   t    │
//	                │        │
//	                └────────┘
func preferLaunchingVertically(,  *layoutgraph.Node,  geo.Orientation) ( bool,  bool) {
	 := 0
	 := 0

	var  []geo.Orientation
	var  []geo.Orientation
	var  []geo.Orientation
	var  []geo.Orientation

	switch  {
	case geo.TopLeft:
		 = []geo.Orientation{geo.BottomLeft, geo.Bottom}
		 = []geo.Orientation{geo.Left, geo.BottomLeft}
		 = []geo.Orientation{geo.TopRight, geo.Right}
		 = []geo.Orientation{geo.Top, geo.TopRight}
	case geo.TopRight:
		 = []geo.Orientation{geo.BottomRight, geo.Bottom}
		 = []geo.Orientation{geo.Right, geo.BottomRight}
		 = []geo.Orientation{geo.TopLeft, geo.Left}
		 = []geo.Orientation{geo.Top, geo.TopLeft}
	case geo.BottomLeft:
		 = []geo.Orientation{geo.TopLeft, geo.Top}
		 = []geo.Orientation{geo.Left, geo.TopLeft}
		 = []geo.Orientation{geo.BottomRight, geo.Right}
		 = []geo.Orientation{geo.Bottom, geo.BottomRight}
	case geo.BottomRight:
		 = []geo.Orientation{geo.TopRight, geo.Top}
		 = []geo.Orientation{geo.Right, geo.TopRight}
		 = []geo.Orientation{geo.BottomLeft, geo.Left}
		 = []geo.Orientation{geo.Bottom, geo.BottomLeft}
	}

	for ,  := range .Edges {
		 := .Adjacent()
		if  ==  {
			continue
		}
		 := .Orientation()
		if  == geo.NONE {
			continue
		}
		if slices.Contains(, ) {
			++
		}
		if slices.Contains(, ) {
			++
		}
	}

	for ,  := range .Edges {
		 := .Adjacent()
		if  ==  {
			continue
		}
		 := .Orientation()
		if  == geo.NONE {
			continue
		}
		if slices.Contains(, ) {
			++
		} else if slices.Contains(, ) {
			++
		}
	}

	// Even if there is no distinction, we don't just want to always prefer one or the other, as that can lead to weird looking graphs
	// So we use node ID as a pseudo-random tiebreak
	if  ==  {
		return .ID < .ID, false
	}
	return  < , true
}

func ( *ovgEdgeRouter) (,  *OVGNode) ( []*OVGNode,  bool,  error) {
	 := []*OVGNode{}
	 := 
	for {
		if  := .work.step();  != nil {
			return nil, false, 
		}
		 := 
		for ,  := range .Edges {
			if  := .work.step();  != nil {
				return nil, false, 
			}
			 := .Adjacent()
			 := false
			if .X == .X && .X == .X {
				 = .Y < .Y && .Y < .Y || .Y > .Y && .Y > .Y
			} else if .Y == .Y && .Y == .Y {
				 = .X < .X && .X < .X || .X > .X && .X > .X
			}
			if  {
				 = 
				break
			}
		}
		if  ==  {
			return nil, false, nil
		}
		if  ==  {
			break
		}
		 = append(, )
	}
	return , true, nil
}

type RouteChecker struct {
	mem     map[*OVGNode]map[*OVGNode]bool
	compute func(from, to *OVGNode) (bool, error)
}

func newFallibleRouteChecker( func(,  *OVGNode) (bool, error)) *RouteChecker {
	return &RouteChecker{
		mem:     make(map[*OVGNode]map[*OVGNode]bool),
		compute: ,
	}
}
func ( RouteChecker) (,  *OVGNode) ( bool,  bool) {
	if ,  := .mem[];  {
		if ,  := .mem[][];  {
			return , true
		}
	}
	return false, false
}
func ( RouteChecker) (,  *OVGNode) ( bool,  error) {
	if ,  := .cacheCheck(, );  {
		return , nil
	}
	if ,  := .mem[]; ! {
		.mem[] = make(map[*OVGNode]bool)
	}
	.mem[][],  = .compute(, )
	if  != nil {
		return false, 
	}
	return .mem[][], nil
}

func ( *ovgEdgeRouter) ( *layoutgraph.Edge,  []*OVGNode) ( float64,  error) {
	if .SourceArrowheadLabel == nil && .TargetArrowheadLabel == nil {
		return 0, nil
	}

	 := make([]*geo.Point, 0, len()-2)
	for  := 1;  < len()-1; ++ {
		if  := .work.step();  != nil {
			return 0, 
		}
		 = append(, [].Point)
	}

	,  := filterEdgeAncestorsGuarded(, .graph.Nodes, .work)
	if  != nil {
		return 0, 
	}

	if  := .SourceArrowheadLabel;  != nil {
		 := labeling.PositionArrowheadLabel(, false, )
		,  := positionedArrowheadLabelCostGuarded(*, , .positionedLabels, .routes, nil, .work)
		if  != nil {
			return 0, 
		}
		 += 
	}
	if  := .TargetArrowheadLabel;  != nil {
		 := labeling.PositionArrowheadLabel(, true, )
		,  := positionedArrowheadLabelCostGuarded(*, , .positionedLabels, .routes, nil, .work)
		if  != nil {
			return 0, 
		}
		 += 
	}
	return , nil
}

func positionedArrowheadLabelCost( labeling.PositionedArrowheadLabel,  []*layoutgraph.Node,  []labeling.PositionedArrowheadLabel,  []*Route,  []*layoutgraph.Edge) ( float64) {
	for ,  := range  {
		if .Edge == .Edge && .IsTarget == .IsTarget {
			continue
		}
		if .Box.Overlaps(.Box) {
			if .Text == .Text {
				continue
			}
			return math.Inf(1)
		}
	}

	 := .Edge.From.Graph
	 := &layoutgraph.Node{
		Box:   .Box,
		Graph: ,
	}
	 += 4 * .TurnCost() * float64(.NodeOverlapCount(, label.PADDING))

	 := 0
	// during edge routing we work with routes, afterwards we use edges
	for ,  := range  {
		if .GEdge == .Edge {
			continue
		}
		for  := 1;  < len(.OVGNodes); ++ {
			if .OverlapsLine(.OVGNodes[-1].Point, .OVGNodes[].Point, 0) {
				++
				break
			}
		}
	}
	for ,  := range  {
		if  == .Edge {
			continue
		}
		for  := 1;  < len(.Points); ++ {
			if .OverlapsLine(.Points[-1], .Points[], 0) {
				++
				break
			}
		}
	}
	 += .TurnCost() * float64()
	return 
}

func ( *ovgEdgeRouter) ( context.Context,  *layoutgraph.Edge) ([]*OVGNode, float64, error) {
	if  := .bindWork();  != nil {
		return nil, 0, 
	}
	if  := .work.step();  != nil {
		return nil, 0, 
	}
	 := .From
	 := .To

	if .Cluster != nil || .Cluster != nil {
		return nil, 0.0, nil
	}

	 := .Orientation()
	if  == geo.NONE {
		// nolint: nilerr
		return nil, 0.0, nil
	}
	if .HasTableColumn() {
		return nil, 0.0, nil
	}
	if !.IsDiagonal() {
		return nil, 0.0, nil
	}

	 := newFallibleRouteChecker(func(,  *OVGNode) (bool, error) {
		var  []*layoutgraph.Edge
		,  := .findOverlappingRoutes(, )
		if  != nil {
			return false, 
		}
		for ,  := range  {
			if  := .work.reserveProduct(uint64(len(.OVGNodes))+1, 2);  != nil {
				return false, 
			}
			 = append(, .GEdge)
			if .IsDirected() && .isOpposingColinear(, ) {
				return true, nil
			}
		}
		,  := edgeCanOverlapEdgesGuarded(, , nil, nil, .work)
		if  != nil {
			return false, 
		}
		return len() > 0 && !, nil
	})

	 := newFallibleRouteChecker(func(,  *OVGNode) (bool, error) {
		return .edgeSet.intersectsWithGuarded(NewOVGEdge(, ), .work)
	})

	var  []*OVGNode
	var  float64

	 := []bool{false, true}
	if  := .work.reserveSum(uint64(len(.Edges)), uint64(len(.Edges)));  != nil {
		return nil, 0, 
	}
	,  := preferLaunchingVertically(, , )
	if  {
		 = []bool{true, false}
	}

	if !.IsBetweenTableColumns() {
		// as we route column to column, there's no way to get L shaped routes between tables
		var  error
		, ,  = .findLShapedRoute(
			,
			.routes,
			,
			,
			,
			,
			,
			,
			,
		)
		if  != nil {
			return nil, 0, 
		}
		if  != math.Inf(1) {
			 += .turnCost
			return , , nil
		}
	}

	, ,  := .findSShapedRoute(
		,
		,
		,
		,
		,
		,
		,
		,
	)
	return , , 
}

func ( *ovgEdgeRouter) (
	 context.Context,
	 []*Route,
	 []bool,
	 bool,
	 bool,
	 *layoutgraph.Edge,
	 geo.Orientation,
	 *RouteChecker,
	 *RouteChecker,
) ([]*OVGNode, float64, error) {
	if  := .bindWork();  != nil {
		return nil, 0, 
	}
	if  := .work.step();  != nil {
		return nil, 0, 
	}
	 := .From
	 := .To

	 := .ovg.Centers[]
	 := .ovg.Centers[]

	var  []*OVGNode
	 := math.Inf(1)

	 := layoutgraph.NewEdge(, )

	for ,  := range  {
		if  := .work.step();  != nil {
			return nil, 0, 
		}
		if  && .FromTableColumnIndex != nil {
			// this can't happen
			continue
		}
		var ,  []*OVGNode
		if .FromTableColumnIndex == nil {
			if  := .work.add(uint64(len(.ovg.Ports[])));  != nil {
				return nil, 0, 
			}
			 = .findLaunchings(, , )
		}
		if .ToTableColumnIndex == nil {
			if  := .work.add(uint64(len(.ovg.Ports[])));  != nil {
				return nil, 0, 
			}
			 = .findLandings(, , )
		}
		if .HasTableColumn() {
			, ,  := .tablePorts(, , , true)
			if  != nil {
				return nil, 0, 
			}
			for  := range  {
				 = append(, )
			}
			for  := range  {
				 = append(, )
			}
		}

		for ,  := range  {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			 := 
			for {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				var  *OVGNode
				for ,  := range .Edges {
					if  := .work.step();  != nil {
						return nil, 0, 
					}
					 := .Adjacent()

					if isOnFlightPlan(, , , ) {
						 = 
						break
					}
				}
				if  == nil || overshot(, , , , true) {
					break
				}
				,  := .check(, )
				if  != nil {
					return nil, 0, 
				}
				if  {
					break
				}
				 = 
				if undershot(, , , ) {
					continue
				}
				for ,  := range  {
					if  := .work.step();  != nil {
						return nil, 0, 
					}
					if  && .Y != .Y {
						continue
					}
					if ! && .X != .X {
						continue
					}

					,  := .launch(, , , )
					if  != nil {
						return nil, 0, 
					}
					if  == 0.0 {
						continue
					}

					,  := .check(, )
					if  != nil {
						return nil, 0, 
					}
					if  {
						 += .crossingCost
					}
					,  = .check(, )
					if  != nil {
						return nil, 0, 
					}
					if  {
						 += .crossingCost
					}

					if  {
						if  && ! {
							 += .crossingCost / 2.0
						}
						if ! &&  {
							 += .crossingCost / 2.0
						}
					}
					if  <  {
						 := []*OVGNode{}
						, ,  := .fillPathGuarded(, )
						if  != nil {
							return nil, 0, 
						}
						if ! {
							continue
						}
						 = append(, ...)
						, ,  = .fillPathGuarded(, )
						if  != nil {
							return nil, 0, 
						}
						if ! {
							continue
						}
						 = append(, )
						// We already checked share for sPort to anchor
						 := false
						for  := 0;  < len()-2; ++ {
							if  := .work.step();  != nil {
								return nil, 0, 
							}
							,  := [], [+1]
							,  := .check(, )
							if  != nil {
								return nil, 0, 
							}
							if  {
								 = true
								break
							}
						}
						if  {
							continue
						}
						 = append(, ...)
						 = append(, )

						// even if it is ok to share with a route, we don't want the exact same route
						 := false
						for ,  := range  {
							if  := .work.step();  != nil {
								return nil, 0, 
							}
							if nonNilEquals(&.FromPort, .Point) && nonNilEquals(&.ToPort, .Point) && len(.OVGNodes) == len() {
								 := true
								// check if points between ports are equal
								for  := 2;  < len()-2; ++ {
									if  := .work.step();  != nil {
										return nil, 0, 
									}
									if !nonNilEquals(.OVGNodes[].Point, [].Point) {
										 = false
										break
									}
								}
								if  {
									 = true
									break
								}
							}
						}
						if  {
							continue
						}

						,  := .arrowheadLabelOverlapPenalty(, )
						if  != nil {
							return nil, 0, 
						}
						 += 
						if  <  {
							 = 
							 = 
						}
					}
				}
			}
		}
	}

	return , , nil
}

func ( *ovgEdgeRouter) (
	 context.Context,
	 []bool,
	 bool,
	 bool,
	 *layoutgraph.Edge,
	 geo.Orientation,
	 *RouteChecker,
	 *RouteChecker,
) ([]*OVGNode, float64, error) {
	if  := .bindWork();  != nil {
		return nil, 0, 
	}
	if  := .work.step();  != nil {
		return nil, 0, 
	}
	 := .From
	 := .To

	 := .ovg.Centers[]
	 := .ovg.Centers[]

	var  []*OVGNode
	 := math.Inf(1)

	 := layoutgraph.NewEdge(, )

	 := layoutgraph.Nodes(.ovg.NodesInsideBoundingBox)
	for ,  := range  {
		if  := .work.step();  != nil {
			return nil, 0, 
		}
		var ,  []*OVGNode
		if  && .FromTableColumnIndex != nil {
			// this can't happen
			continue
		}
		if .FromTableColumnIndex == nil {
			if  := .work.add(uint64(len(.ovg.Ports[])));  != nil {
				return nil, 0, 
			}
			 = .findLaunchings(, , )
		}
		if .ToTableColumnIndex == nil {
			if  := .work.add(uint64(len(.ovg.Ports[])));  != nil {
				return nil, 0, 
			}
			 = .findLandings(!, , )
		}
		if .HasTableColumn() {
			, ,  := .tablePorts(, , , true)
			if  != nil {
				return nil, 0, 
			}
			for  := range  {
				 = append(, )
			}
			for  := range  {
				 = append(, )
			}
		}

		 := make(map[*OVGNode]*OVGNode)
		 := make(map[*OVGNode]*OVGNode)
		var  []*OVGNode
		var  []*OVGNode

		for ,  := range  {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			 := 
			for {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				var  *OVGNode
				for ,  := range .Edges {
					if  := .work.step();  != nil {
						return nil, 0, 
					}
					 := .Adjacent()

					if isOnFlightPlan(, , , ) {
						 = 
						break
					}
				}
				if  == nil {
					break
				}
				if overshot(, , , , false) {
					break
				}
				,  := .check(, )
				if  != nil {
					return nil, 0, 
				}
				if  {
					break
				}

				[] = 
				 = append(, )
				 = 
			}
		}

		for ,  := range  {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			 := 
			for {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				var  *OVGNode
				for ,  := range .Edges {
					if  := .work.step();  != nil {
						return nil, 0, 
					}
					 := .Adjacent()

					if isOnFlightPlan(, , .GetOpposite(), ) {
						 = 
						break
					}
				}
				if  == nil {
					break
				}
				if overshot(, , .GetOpposite(), , false) {
					break
				}
				,  := .check(, )
				if  != nil {
					return nil, 0, 
				}
				if  {
					break
				}

				[] = 
				 = append(, )
				 = 
			}
		}

		 := idealTurnAxes(, )
		// Prefer anchors closer to the midpoint
		if  := .work.reserveSort(len());  != nil {
			return nil, 0, 
		}
		slices.SortStableFunc(, func(,  *OVGNode) int {
			var ,  float64
			if  {
				 = math.Abs(.Y - [1].val)
				 = math.Abs(.Y - [1].val)
			} else {
				 = math.Abs(.X - [0].val)
				 = math.Abs(.X - [0].val)
			}
			switch {
			case  < :
				return -1
			case  < :
				return 1
			default:
				return 0
			}
		})

		for ,  := range  {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			 := []
			for ,  := range  {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				 := []
				if  && .Y != .Y {
					continue
				}
				if ! && .X != .X {
					continue
				}
				 := 0.0
				 := [][]*OVGNode{
					{, },
					{, },
					{, },
				}
				 := false
				for ,  := range  {
					if  := .work.step();  != nil {
						return nil, 0, 
					}
					.Points = []*geo.Point{[0].Point, [1].Point}
					,  := routeIntersectsNodeGuarded(, , .work)
					if  != nil {
						return nil, 0, 
					}
					if  {
						 = true
						break
					}
					 += geo.EuclideanDistance([0].X, [0].Y, [1].X, [1].Y)
					,  := .check([0], [1])
					if  != nil {
						return nil, 0, 
					}
					if  {
						 += .crossingCost
					}
				}
				if  {
					continue
				}
				if  {
					if  && ! {
						 += .crossingCost / 2.0
					}
					if ! &&  {
						 += .crossingCost / 2.0
					}
				}
				if  <  {
					 := []*OVGNode{}
					, ,  := .fillPathGuarded(, )
					if  != nil {
						return nil, 0, 
					}
					if ! {
						continue
					}
					 = append(, ...)
					, ,  = .fillPathGuarded(, )
					if  != nil {
						return nil, 0, 
					}
					if ! {
						continue
					}
					// add this just for the share check
					 = append(, )
					// We already checked share for sPort to sAnchor and tPort to tAnchor
					 := false
					for  := 0;  < len()-2; ++ {
						if  := .work.step();  != nil {
							return nil, 0, 
						}
						,  := [], [+1]
						,  := .check(, )
						if  != nil {
							return nil, 0, 
						}
						if  {
							 = true
							break
						}
					}
					if  {
						continue
					}
					 = append(, [:len()-1]...)
					, ,  = .fillPathGuarded(, )
					if  != nil {
						return nil, 0, 
					}
					if ! {
						continue
					}
					 = append(, ...)
					 = append(, )
					 = append(, )

					,  := .arrowheadLabelOverlapPenalty(, )
					if  != nil {
						return nil, 0, 
					}
					 += 
					if  <  {
						 = 
						 = 
					}
				}
			}
		}
	}

	if  != math.Inf(1) {
		 += .turnCost * 2
	}

	return , , nil
}