package labeling

import (
	
	
	

	
	
	

	
)

const (
	// SharedSegmentClearance reserves space around shared route segments where
	// an edge label would otherwise be ambiguous.
	SharedSegmentClearance  = 2.5 * label.PADDING
	placementSearchInterval = 0.025
)

// Place chooses positions for every movable node label, icon, and routed edge
// label in graph.
func ( context.Context,  *layoutgraph.Graph) error {
	return place(, , maxLabelPlacementWorkUnits)
}

func place( context.Context,  *layoutgraph.Graph,  int64) ( error) {
	const  = "PlaceLabels"
	if  := layoutgraph.ValidatePositionedGraphSelection(, , , nil);  != nil {
		return 
	}
	 := captureLabelPlacement(, nil)
	defer func() {
		if  := recover();  != nil {
			.restore()
			panic()
		}
		if  != nil {
			.restore()
		}
	}()

	,  := newLabelPlacementWorkGuard(, , )
	if  != nil {
		return 
	}
	 := .step

	var  []*layoutgraph.Node

	// add all arrowhead labels to placedFakeNodes
	for ,  := range .Edges {
		if  := ();  != nil {
			return 
		}
		if  := .SourceArrowheadLabel;  != nil {
			,  := positionedArrowheadLabel(, false, )
			if  != nil {
				return 
			}
			 := &layoutgraph.Node{
				Box:   .Box,
				D2ID:  new(.Text),
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}
		if  := .TargetArrowheadLabel;  != nil {
			,  := positionedArrowheadLabel(, true, )
			if  != nil {
				return 
			}
			 := &layoutgraph.Node{
				Box:   .Box,
				D2ID:  new(.Text),
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}
	}
	for ,  := range .Edges {
		if  := ();  != nil {
			return 
		}
		if .Label != nil && (.IsLoop() || .Label.PositionFixed()) {
			,  := edgeLabelTopLeft(, .Label.Position, .Label.Width, .Label.Height, )
			if  != nil {
				return 
			}
			 := &layoutgraph.Node{
				Box: geo.Box{
					TopLeft: ,
					Width:   .Label.Width,
					Height:  .Label.Height,
				},
				D2ID:  new(.Label.Text),
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}
	}

	// reposition node labels if label is on the same side as a used port or if there is an edge that passes nearby
	for ,  := range .Nodes {
		if  := ();  != nil {
			return 
		}
		 := len(.Containers[.Container]) + len(.Containers[])
		if  > 0 {
			--
		}
		 := make([]*layoutgraph.Node, 0, )
		for ,  := range .Containers[.Container] {
			if  := ();  != nil {
				return 
			}
			if  !=  {
				 = append(, )
			}
		}
		if .IsContainer() {
			for ,  := range .Containers[] {
				if  := ();  != nil {
					return 
				}
				 = append(, )
			}
		}

		,  := collectLabelPlacementAncestors(, )
		if  != nil {
			return 
		}

		var  label.Position
		var  *layoutgraph.Node
		 := math.Inf(1)
		if .Icon != nil && !.IsImage() {
			if !.Icon.PositionFixed() {
				 := labelPositionPreferences()
				for ,  := range  {
					if  := ();  != nil {
						return 
					}
					 := .IconSize()
					 := &layoutgraph.Node{
						Box: geo.Box{
							TopLeft: .LabelTopLeft(, , ),
							Width:   ,
							Height:  ,
						},
						Graph: ,
					}
					.SetShape(shape.SQUARE_TYPE)

					 := 

					if .Label != nil && .Label.PositionFixed() {
						 := &layoutgraph.Node{
							Box: geo.Box{
								TopLeft: .LabelTopLeft(.Label.Position, .Label.Width, .Label.Height),
								Width:   .Label.Width,
								Height:  .Label.Height,
							},
							D2ID:  new(.Label.Text),
							Graph: ,
						}
						.SetShape(shape.SQUARE_TYPE)
						 = append(, )
					}

					,  := nodeOverlapCount(, , 0, )
					if  != nil {
						return 
					}
					,  := edgeOverlapCount(, .Edges, 0, )
					if  != nil {
						return 
					}
					,  := nodeOverlapCount(, , 0, )
					if  != nil {
						return 
					}
					 := scoreNodeLabelOverlaps(, , )
					if  <  {
						 = 
						 = 
						 = 
						if  == 0 {
							break
						}
					}
				}
			} else {
				 := .IconSize(.Icon.Position)
				 := &layoutgraph.Node{
					Box: geo.Box{
						TopLeft: .LabelTopLeft(.Icon.Position, , ),
						Width:   ,
						Height:  ,
					},
					Graph: ,
				}
				.SetShape(shape.SQUARE_TYPE)
				 = 
				 = .Icon.Position
			}
			if  := ();  != nil {
				return 
			}
			 = append(, )
			.Icon.Position = 
		}

		if .Label == nil || .Label.PositionFixed() {
			continue
		}

		if .Icon != nil && !.IsImage() {
			 := .IconSize(.Icon.Position)
			 := &layoutgraph.Node{
				Box: geo.Box{
					TopLeft: .LabelTopLeft(.Icon.Position, , ),
					Width:   ,
					Height:  ,
				},
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}

		 = math.Inf(1)
		var  label.Position
		 := labelPositionPreferenceTranches()
		for ,  := range  {
			if  := ();  != nil {
				return 
			}
			// For each tranch, if there's multiple positions equally good, break ties with
			// slightly larger fake label box, so that the label goes on the side with the most empty space
			var  []label.Position
			var  []*layoutgraph.Node
			for ,  := range  {
				if  := ();  != nil {
					return 
				}
				if  ==  {
					continue
				}
				 := &layoutgraph.Node{
					Box: geo.Box{
						TopLeft: .LabelTopLeft(, .Label.Width, .Label.Height),
						Width:   .Label.Width,
						Height:  .Label.Height,
					},
					D2ID:  new(.Label.Text),
					Graph: ,
				}
				.SetShape(shape.SQUARE_TYPE)
				// if an inside label escapes the node it is a bad placement
				if !.IsOutside() {
					// inside labels must fit inside node with label.PADDING on each side
					.PadLabelCandidate(label.PADDING)
					if !layoutgraph.LabelBoxFits(.InnerBox(), .GetBox()) {
						continue
					}
					.PadLabelCandidate(-label.PADDING)
				}

				// it must be label.PADDING-1 so that it does not overlap with edges at the node border
				//      │
				// ┌────▼─────┐
				// │  label   │
				// │          │
				// └──────────┘
				,  := nodeOverlapCount(, , label.PADDING, )
				if  != nil {
					return 
				}
				,  := partialNodeOverlapCount(, , label.PADDING, )
				if  != nil {
					return 
				}
				,  := edgeOverlapCount(, .Edges, label.PADDING-1, )
				if  != nil {
					return 
				}
				,  := nodeOverlapCount(, , label.PADDING, )
				if  != nil {
					return 
				}
				 := scoreNodeLabelOverlaps(+, , )
				if  <  {
					 = 
					 = 
					 = 
					if .IsOutside() && .IsOutside() {
						 = []label.Position{}
						 = []*layoutgraph.Node{}
					} else {
						 = []label.Position{}
						 = []*layoutgraph.Node{}
					}
				} else if  ==  && compareLabelPositions(, , ) == 0 && .IsOutside() && .IsOutside() {
					 = append(, )
					 = append(, )
				}
			}
			if len() > 1 {
				 := math.Inf(1)
				var  label.Position
				var  *layoutgraph.Node
				for ,  := range  {
					if  := ();  != nil {
						return 
					}
					 := &layoutgraph.Node{
						Box: geo.Box{
							TopLeft: .LabelTopLeft(, .Label.Width*2, .Label.Height*2),
							Width:   .Label.Width * 2,
							Height:  .Label.Height * 2,
						},
						D2ID:  new(.Label.Text),
						Graph: ,
					}
					.SetShape(shape.SQUARE_TYPE)
					// if an inside label escapes the node it is a bad placement
					if !.IsOutside() {
						// inside labels must fit inside node with label.PADDING on each side
						.PadLabelCandidate(label.PADDING)
						if !layoutgraph.LabelBoxFits(.InnerBox(), .GetBox()) {
							continue
						}
						.PadLabelCandidate(-label.PADDING)
					}

					,  := nodeOverlapCount(, , label.PADDING, )
					if  != nil {
						return 
					}
					,  := partialNodeOverlapCount(, , label.PADDING, )
					if  != nil {
						return 
					}
					,  := edgeOverlapCount(, .Edges, label.PADDING-1, )
					if  != nil {
						return 
					}
					,  := nodeOverlapCount(, , label.PADDING, )
					if  != nil {
						return 
					}
					 := scoreNodeLabelOverlaps(+, , )
					if  <  {
						 = 
						 = 
						 = []
					}
				}
				 = 
				 = 
			}
		}
		if  := ();  != nil {
			return 
		}
		 = append(, )

		.Label.Position = 
	}

	// we want to find the best edge label position, avoiding overlapping with other nodes, edges, labels and shared edge segments
	// create fake nodes around shared segments to avoid having labels placed in these positions
	,  := findSharedSegmentsChecked(.Edges, .step)
	if  != nil {
		return 
	}
	 := make([]*layoutgraph.Node, 0, len())
	for ,  := range  {
		if  := ();  != nil {
			return 
		}
		 := .Start.Copy()
		 := .End.X - .Start.X
		 := .End.Y - .Start.Y
		// generate a box around the shared segments
		// │  │  │
		// shared│
		// segment
		// │  │  │
		// │  │  │
		// │  │  │
		// │  │  │
		// └──┴──┘
		// |-----| fake box
		// |--| SharedSegmentClearance
		if .End.X == .Start.X {
			// vertical
			 = 2 * SharedSegmentClearance
			.X -= SharedSegmentClearance
		} else {
			// horizontal
			 = 2 * SharedSegmentClearance
			.Y -= SharedSegmentClearance
		}
		 := &layoutgraph.Node{
			Box: geo.Box{
				TopLeft: ,
				Width:   ,
				Height:  ,
			},
			D2ID:  new(fmt.Sprintf("fake_shared_segment_%d", )),
			Graph: ,
		}
		.SetShape(shape.SQUARE_TYPE)
		 = append(, )
	}

	 := make([]*layoutgraph.Edge, 0, len(.Edges))
	for ,  := range .Edges {
		if  := ();  != nil {
			return 
		}
		if .Label == nil || .IsLoop() || .Label.PositionFixed() {
			continue
		}
		 = append(, )
	}

	,  = sortLabelPlacementEdges(, )
	if  != nil {
		return 
	}

	for ,  := range  {
		if  := ();  != nil {
			return 
		}
		, , ,  := findBestEdgeLabelPosition(, , , , )
		if  != nil {
			return 
		}
		 = append(, )
		.Label.Position = 
		.LabelPercentage = 
	}

	return .check()
}

// PlaceNewEdges chooses label positions only for edges, preserving all
// existing label placements in graph as obstacles.
func ( context.Context,  *layoutgraph.Graph,  []*layoutgraph.Edge) error {
	return placeNewEdges(, , , maxLabelPlacementWorkUnits)
}

func placeNewEdges( context.Context,  *layoutgraph.Graph,  []*layoutgraph.Edge,  int64) ( error) {
	const  = "PlaceNewEdgeLabels"
	if  := layoutgraph.ValidatePositionedGraphSelection(, , , );  != nil {
		return 
	}
	 := captureLabelPlacement(, )
	defer func() {
		if  := recover();  != nil {
			.restore()
			panic()
		}
		if  != nil {
			.restore()
		}
	}()

	,  := newLabelPlacementWorkGuard(, , )
	if  != nil {
		return 
	}
	 := .step

	var  []*layoutgraph.Node

	 := make(map[*layoutgraph.Edge]struct{})
	for ,  := range  {
		if  := ();  != nil {
			return 
		}
		[] = struct{}{}
	}

	// Reserve existing and caller-fixed edge labels, plus all arrowhead labels.
	for ,  := range .Edges {
		if  := ();  != nil {
			return 
		}
		if  := .SourceArrowheadLabel;  != nil {
			,  := positionedArrowheadLabel(, false, )
			if  != nil {
				return 
			}
			 := &layoutgraph.Node{
				Box:   .Box,
				D2ID:  new(.Text),
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}
		if  := .TargetArrowheadLabel;  != nil {
			,  := positionedArrowheadLabel(, true, )
			if  != nil {
				return 
			}
			 := &layoutgraph.Node{
				Box:   .Box,
				D2ID:  new(.Text),
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}

		if .Label == nil || .IsLoop() {
			continue
		}
		if ,  := [];  && !.Label.PositionFixed() {
			continue
		}

		,  := edgeLabelTopLeft(, .Label.Position, .Label.Width, .Label.Height, )
		if  != nil {
			return 
		}
		 := &layoutgraph.Node{
			Box: geo.Box{
				TopLeft: ,
				Width:   .Label.Width,
				Height:  .Label.Height,
			},
			D2ID:  new(.Label.Text),
			Graph: ,
		}
		.SetShape(shape.SQUARE_TYPE)
		 = append(, )
	}

	for ,  := range .Edges {
		if  := ();  != nil {
			return 
		}
		if .IsLoop() && .Label != nil {
			,  := edgeLabelTopLeft(, .Label.Position, .Label.Width, .Label.Height, )
			if  != nil {
				return 
			}
			 := &layoutgraph.Node{
				Box: geo.Box{
					TopLeft: ,
					Width:   .Label.Width,
					Height:  .Label.Height,
				},
				D2ID:  new(.Label.Text),
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}
	}

	// reposition node labels if label is on the same side as a used port or if there is an edge that passes nearby
	for ,  := range .Nodes {
		if  := ();  != nil {
			return 
		}
		if .Icon != nil && !.IsImage() {
			 := .IconSize(.Icon.Position)
			 := &layoutgraph.Node{
				Box: geo.Box{
					TopLeft: .LabelTopLeft(.Icon.Position, , ),
					Width:   ,
					Height:  ,
				},
				Graph: ,
			}
			.SetShape(shape.SQUARE_TYPE)
			 = append(, )
		}

		if .Label == nil || .Label.PositionFixed() {
			continue
		}

		 := &layoutgraph.Node{
			Box: geo.Box{
				TopLeft: .LabelTopLeft(.Label.Position, .Label.Width, .Label.Height),
				Width:   .Label.Width,
				Height:  .Label.Height,
			},
			D2ID:  new(.Label.Text),
			Graph: ,
		}
		.SetShape(shape.SQUARE_TYPE)
		 = append(, )
	}

	// we want to find the best edge label position, avoiding overlapping with other nodes, edges, labels and shared edge segments
	// create fake nodes around shared segments to avoid having labels placed in these positions
	,  := findSharedSegmentsChecked(.Edges, .step)
	if  != nil {
		return 
	}
	 := make([]*layoutgraph.Node, 0, len())
	for ,  := range  {
		if  := ();  != nil {
			return 
		}
		 := .Start.Copy()
		 := .End.X - .Start.X
		 := .End.Y - .Start.Y
		// generate a box around the shared segments
		// │  │  │
		// shared│
		// segment
		// │  │  │
		// │  │  │
		// │  │  │
		// │  │  │
		// └──┴──┘
		// |-----| fake box
		// |--| SharedSegmentClearance
		if .End.X == .Start.X {
			// vertical
			 = 2 * SharedSegmentClearance
			.X -= SharedSegmentClearance
		} else {
			// horizontal
			 = 2 * SharedSegmentClearance
			.Y -= SharedSegmentClearance
		}
		 := &layoutgraph.Node{
			Box: geo.Box{
				TopLeft: ,
				Width:   ,
				Height:  ,
			},
			D2ID:  new(fmt.Sprintf("fake_shared_segment_%d", )),
			Graph: ,
		}
		.SetShape(shape.SQUARE_TYPE)
		 = append(, )
	}

	 := make([]*layoutgraph.Edge, 0, len())
	for ,  := range  {
		if  := ();  != nil {
			return 
		}
		if .Label == nil || .IsLoop() || .Label.PositionFixed() {
			continue
		}
		 = append(, )
	}

	,  = sortLabelPlacementEdges(, )
	if  != nil {
		return 
	}

	for ,  := range  {
		if  := ();  != nil {
			return 
		}
		, , ,  := findBestEdgeLabelPosition(, , , , )
		if  != nil {
			return 
		}
		 = append(, )
		.Label.Position = 
		.LabelPercentage = 
	}
	return .check()
}

func findBestEdgeLabelPosition( *layoutgraph.Edge,  *layoutgraph.Graph, ,  []*layoutgraph.Node,  *labelPlacementWorkGuard) (*layoutgraph.Node, label.Position, float64, error) {
	// we don't want to count the edge itself as overlapping, we already prefer outside labels
	 := make([]*layoutgraph.Edge, 0, len(.Edges))
	for ,  := range .Edges {
		if  := .step();  != nil {
			return nil, label.Unset, 0, 
		}
		if  ==  {
			continue
		}
		 = append(, )
	}

	 := make([]label.Position, 0, len(edgeLabelPreferenceOrder)+3)
	if .Label.Position.IsUnlocked() {
		// prefer the unlocked positions before trying other positions
		if .Label.Position == label.UnlockedMiddle {
			 = append(, label.UnlockedMiddle)
			 = append(, label.UnlockedTop)
			 = append(, label.UnlockedBottom)
		} else {
			 = append(, .Label.Position)
			 = append(, .Label.Position.Mirrored())
			 = append(, label.UnlockedMiddle)
		}
		 = append(, edgeLabelPreferenceOrder...)
	} else if .IsClusterEdge() {
		,  := isClusterPathSharedPlacement(, )
		if  != nil {
			return nil, label.Unset, 0, 
		}
		if ! {
			 = append(, edgeLabelPreferenceOrder...)
			 = append(, label.UnlockedTop)
			 = append(, label.UnlockedBottom)
			 = append(, label.UnlockedMiddle)
		} else {
			// Give preference for symmetrical placements for clustered nodes
			// .         label     ┌──────────┐
			// .        ┌──────────┤          │
			// .        │          │          │
			// .        │          └──────────┘
			// . ───────┤
			// .        │          ┌──────────┐
			// .        │          │          │
			// .        └──────────┤          │
			// .          label    └──────────┘
			if .From.Cluster != nil {
				, ,  := clusterLabelPlacementOrientation(.To, .From, .From.Cluster, )
				if  != nil {
					return nil, label.Unset, 0, 
				}
				switch  {
				case geo.Right, geo.Top:
					if float64() < float64(len(.From.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedTop)
					} else if float64() > float64(len(.From.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedBottom)
					}
				case geo.Left, geo.Bottom:
					if float64() < float64(len(.From.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedBottom)
					} else if float64() > float64(len(.From.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedTop)
					}
				}
			} else {
				, ,  := clusterLabelPlacementOrientation(.From, .To, .To.Cluster, )
				if  != nil {
					return nil, label.Unset, 0, 
				}
				switch  {
				case geo.Left, geo.Bottom:
					if float64() < float64(len(.To.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedTop)
					} else if float64() > float64(len(.To.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedBottom)
					}
				case geo.Right, geo.Top:
					if float64() < float64(len(.To.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedBottom)
					} else if float64() > float64(len(.To.Cluster.Nodes)-1)/2. {
						 = append(, label.UnlockedTop)
					}
				}
			}
			 = append(, label.UnlockedMiddle)
			 = append(, edgeLabelPreferenceOrder...)
		}
	} else {
		 = append(, edgeLabelPreferenceOrder...)
		 = append(, label.UnlockedTop)
		 = append(, label.UnlockedBottom)
		 = append(, label.UnlockedMiddle)
	}

	var ,  layoutgraph.Nodes
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return nil, label.Unset, 0, 
		}
		,  := isLabelPlacementDescendantOf(.From, , )
		if  != nil {
			return nil, label.Unset, 0, 
		}
		,  := isLabelPlacementDescendantOf(.To, , )
		if  != nil {
			return nil, label.Unset, 0, 
		}
		if  != .From &&  != .To && ( || ) {
			 = append(, )
		} else {
			 = append(, )
		}
	}

	var  label.Position
	var  float64
	var  *layoutgraph.Node
	 := math.Inf(1)

	 := func( label.Position) error {
		if  := .step();  != nil {
			return 
		}
		,  := edgeLabelTopLeft(, , .Label.Width, .Label.Height, )
		if  != nil {
			return 
		}
		 := &layoutgraph.Node{
			Box: geo.Box{
				TopLeft: ,
				Width:   .Label.Width,
				Height:  .Label.Height,
			},
			D2ID:  new(.Label.Text),
			Graph: ,
		}
		.SetShape(shape.SQUARE_TYPE)

		 := layoutgraph.Edges(.Edges)
		if .IsOnEdge() {
			 = layoutgraph.Edges()
		}

		// Especially don't want labels of a cluster edge path intersecting other cluster edge paths
		// So count the other cluster edges as double
		if .From.Cluster != nil {
			 = append(layoutgraph.Edges(nil), ...)
			for ,  := range  {
				if  := .step();  != nil {
					return 
				}
				if .From.Cluster == .From.Cluster {
					 = append(, )
				}
			}
		} else if .To.Cluster != nil {
			 = append(layoutgraph.Edges(nil), ...)
			for ,  := range  {
				if  := .step();  != nil {
					return 
				}
				if .To.Cluster == .To.Cluster {
					 = append(, )
				}
			}
		}

		// it only counts as overlapping an ancestor if it is partially overlapping
		, ,  := nodeOverlapArea(, , label.PADDING, true, )
		if  != nil {
			return 
		}
		, ,  := nodeOverlapArea(, , label.PADDING, false, )
		if  != nil {
			return 
		}
		,  := edgeOverlapCount(, , label.PADDING, )
		if  != nil {
			return 
		}
		,  := edgeOverlapCount(, , 0, )
		if  != nil {
			return 
		}
		 :=  + int(math.Ceil(float64()*0.5))
		,  := nodeOverlapCount(, , label.PADDING, )
		if  != nil {
			return 
		}
		,  := nodeOverlapCount(, , 0, )
		if  != nil {
			return 
		}
		 :=  + int(math.Ceil(float64()*0.5))
		,  := nodeOverlapCount(, , 0, )
		if  != nil {
			return 
		}
		,  := nodeOverlapCount(, , label.PADDING, )
		if  != nil {
			return 
		}
		 := scoreEdgeLabelOverlaps(
			.Area(),
			+,
			+,
			,
			,
			,
			,
		)

		if  <  {
			 = 
			 = 
			 = .LabelPercentage
			 = 
		}
		return nil
	}

	 := 0.0
	 := false
	// pick the label position based on how many nodes, edges and already placed labels overlap
	for ,  := range  {
		if  := .step();  != nil {
			return nil, label.Unset, 0, 
		}
		if  == 0 {
			break
		}
		if .IsUnlocked() {
			if .LabelPercentage != 0 {
				// if label percentage was set on the edge during layout, use it
				if  := ();  != nil {
					return nil, label.Unset, 0, 
				}
			} else {
				if ! {
					var  error
					,  = routeLengthPlacement(, )
					if  != nil {
						return nil, label.Unset, 0, 
					}
					 = true
				}
				 := labelPercentageSearchRange(, )
				for  := .start;  < .end;  += placementSearchInterval {
					if  := .step();  != nil {
						return nil, label.Unset, 0, 
					}
					.LabelPercentage = 
					if  := ();  != nil {
						return nil, label.Unset, 0, 
					}
					if  == 0 {
						break
					}
				}
				.LabelPercentage = 0
			}
		} else {
			if  := ();  != nil {
				return nil, label.Unset, 0, 
			}
		}
	}

	return , , , .check()
}

func labelPercentageSearchRange( *layoutgraph.Edge,  float64) searchRange {
	 := .Label.Height / 2.
	 := .Label.Width / 2.
	if .From.Cluster != nil {
		// if the edge is coming from a cluster node
		// use only the first segment extension to find the placement
		// .          range.end    range.start
		// .               ├──────────┤
		// .                          ┌──────────┐
		// .               ┌──────────┤          │
		// .               │          │          │
		// . ┌─────┐       │          └──────────┘
		// . │     ◄───────┤
		// . │     │       │          ┌──────────┐
		// . └─────┘       │          │          │
		// .               └──────────┤          │
		// .                          └──────────┘
		 := .Points[0]
		 := .Points[1]
		if .X == .X && math.Abs(.Y-.Y) >  {
			 := (math.Abs(.Y-.Y) - ) / 
			return searchRange{start: 0, end: math.Min(1, )}
		} else if math.Abs(.X-.X) >  {
			 := (math.Abs(.X-.X) - ) / 
			return searchRange{start: 0, end: math.Min(1, )}
		}
	} else if .To.Cluster != nil {
		// if the edge is going to a cluster node
		// use only the last segment extension to find the placement
		// .        range.start    range.end
		// .               ├──────────┤
		// .                          ┌──────────┐
		// .               ┌──────────►          │
		// .               │          │          │
		// . ┌─────┐       │          └──────────┘
		// . │     ├───────┤
		// . │     │       │          ┌──────────┐
		// . └─────┘       │          │          │
		// .               └──────────►          │
		// .                          └──────────┘
		 := .Points[len(.Points)-1]
		 := .Points[len(.Points)-2]
		if .X == .X && math.Abs(.Y-.Y) >  {
			 := ( - math.Abs(.Y-.Y) + ) / 
			return searchRange{start: math.Max(0, ), end: 1}
		} else if math.Abs(.X-.X) >  {
			 := ( - math.Abs(.X-.X) + ) / 
			return searchRange{start: math.Max(0, ), end: 1}
		}
	}
	return searchRange{start: 0, end: 1}
}

// findSharedSegmentsChecked finds the longest shared vertical/horizontal segments
// Below is an example for horizontal segments, but the same applies for vertical ones
// Note that the segments are shown in different rows to make it easier to understand (in reality, they must be on the same Y)
// Input segments:
// .                                         ───────────
// .      ───────                      ─────────
// . ──────       ────────  ──────   ─────
// . Result:
// .      ─                            ─────────
// just the small             shared segment among the 3 segments above
// shared segment
func findSharedSegmentsChecked( []*layoutgraph.Edge,  func() error) ([]*geo.Segment, error) {
	if  != nil {
		if  := ();  != nil {
			return nil, 
		}
	}
	 := make(map[float64][]*geo.Segment)
	 := make(map[float64][]*geo.Segment)

	// group vertical/horizontal segments by Y/X
	for ,  := range  {
		if  != nil {
			if  := ();  != nil {
				return nil, 
			}
		}
		for  := 0;  < len(.Points)-1; ++ {
			if  != nil {
				if  := ();  != nil {
					return nil, 
				}
			}
			 := geo.NewSegment(.Points[].Copy(), .Points[+1].Copy())
			if .Start.X == .End.X && .Start.Y == .End.Y {
				continue
			}
			if .Start.X == .End.X {
				if .End.Y < .Start.Y {
					// swap so segments are always top to bottom
					.End.Y, .Start.Y = .Start.Y, .End.Y
				}
				[.Start.X] = append([.Start.X], )
			} else if .Start.Y == .End.Y {
				if .End.X < .Start.X {
					// swap so segments are always left to right
					.End.X, .Start.X = .Start.X, .End.X
				}
				[.Start.Y] = append([.Start.Y], )
			}
		}
	}

	 := make([]*geo.Segment, 0, max(len(), len()))
	 := func( map[float64][]*geo.Segment,  bool) error {
		 := func( *geo.Point) float64 {
			if  {
				return .X
			}
			return .Y
		}
		 := func( *geo.Point,  float64) {
			if  {
				.X = 
			} else {
				.Y = 
			}
		}

		for ,  := range  {
			if  != nil {
				if  := ();  != nil {
					return 
				}
			}
			if len() == 1 {
				continue
			}
			// Sort segments from starting positions (assumes segments are top to
			// bottom and left to right). The merge sort can stop between every
			// comparison; sort.Slice cannot propagate cancellation.
			if  := sortSharedSegmentGroup(, , );  != nil {
				return 
			}

			 := [0]
			var  *geo.Segment
			for  := 1;  < len(); ++ {
				if  != nil {
					if  := ();  != nil {
						return 
					}
				}
				 := []
				// segments overlap
				if (.End) > (.Start) {
					// A long trunk can contain disjoint shared intervals.
					// Keep its exclusive label corridor between them available.
					if  != nil && (.Start) > (.End) {
						 = append(, )
						 = nil
					}
					if  == nil {
						 = geo.NewSegment(
							.Start.Copy(),
							.Start.Copy(),
						)
						// update the appropriate coordinate
						// curr  :   ─────────
						// prev  : ─────
						// shared:   ───
						(.End, math.Min((.End), (.End)))
					} else {
						// extend the current ongoing overlap
						// shared before: ───
						// prev         : ─────────
						// curr         :         ───────────
						// shared now   : ─────────
						 := math.Min((.End), (.End))
						(.End, math.Max((.End), ))
					}
				} else if  != nil {
					// there's no need to extend the segment, so close it
					 = append(, )
					 = nil
				}
				// keep prev as the longest segment
				if (.End) > (.End) {
					 = 
				}
			}
			if  != nil {
				// just in case the last segment had an overlap
				 = append(, )
			}
		}
		return nil
	}

	if  := (, false);  != nil {
		return nil, 
	}
	if  := (, true);  != nil {
		return nil, 
	}

	return , nil
}

func scoreEdgeLabelOverlaps(
	,  float64,
	, ,
	, ,  int,
) float64 {
	 := ( / ) * 2.
	// avoid as much as possible overlapping with other labels
	 += float64() * 10
	 += float64()
	// overlapping with other edges is worse than placing the label on a shared segment
	 += float64() * 2
	 += float64() * 2
	 += float64()
	return 
}

// scoreNodeLabelOverlaps ranks a node-label candidate. A label-label overlap
// is deliberately twice as costly as a node or edge overlap.
func scoreNodeLabelOverlaps(, ,  int) float64 {
	return float64( +  + 2*)
}

func isClusterPathSharedPlacement( *layoutgraph.Edge,  *labelPlacementWorkGuard) (bool, error) {
	return isClusterPathSharedChecked(, .step)
}

// isClusterPathSharedChecked checks if a given cluster edge has a shared path with the other cluster edges.
func isClusterPathSharedChecked( *layoutgraph.Edge,  func() error) (bool, error) {
	if  != nil {
		if  := ();  != nil {
			return false, 
		}
	}
	var  []*layoutgraph.Node
	var  *layoutgraph.Node
	 := false
	if .From.Cluster != nil {
		 = .From.Cluster.Nodes
		 = .To
		 = true
	} else if .To.Cluster != nil {
		 = .To.Cluster.Nodes
		 = .From
	} else {
		return false, nil
	}

	var ,  *geo.Point
	if  {
		// if the edge is coming from a cluster, only the last segment can be shared
		 = .Points[len(.Points)-1]
		 = .Points[len(.Points)-2]
	} else {
		// if the edge is going to a cluster, only the first segment can be shared
		 = .Points[0]
		 = .Points[1]
	}
	for ,  := range  {
		if  != nil {
			if  := ();  != nil {
				return false, 
			}
		}
		for ,  := range .Edges {
			if  != nil {
				if  := ();  != nil {
					return false, 
				}
			}
			if  ==  {
				continue
			}
			if .Adjacent() !=  {
				// only checks edges to the same connected node
				continue
			}
			var ,  *geo.Point
			if  {
				 = .Points[len(.Points)-1]
				 = .Points[len(.Points)-2]
			} else {
				 = .Points[0]
				 = .Points[1]
			}
			if .X == .X && .X == .X && .X == .X {
				// both vertical and at same X
				if math.Max(.Y, .Y) >= math.Min(.Y, .Y) &&
					math.Max(.Y, .Y) >= math.Min(.Y, .Y) {
					// The closed intervals overlap.
					return true, nil
				}
			} else if .Y == .Y && .Y == .Y && .Y == .Y {
				// both horizontal and at same Y
				if math.Max(.X, .X) >= math.Min(.X, .X) &&
					math.Max(.X, .X) >= math.Min(.X, .X) {
					// The closed intervals overlap.
					return true, nil
				}
			}
		}
	}

	return false, nil
}