package quality

import (
	

	
	
	
)

// measureGeometry counts occupied rectangular interiors. Container containment
// is intentional, and an edge may cross a boundary enclosing either endpoint.
// Curved routes and nonrectangular silhouettes require renderer geometry and are
// deliberately not treated as invalid based only on their bounding boxes.
func measureGeometry( *layoutgraph.Graph,  *Metrics,  *limits.WorkGuard) error {
	for ,  := range .Nodes {
		if  := .Step();  != nil {
			return 
		}
		if .IsInvisible || !.IsRectangular() {
			continue
		}
		for ,  := range .Nodes[+1:] {
			if  := .Step();  != nil {
				return 
			}
			if .IsInvisible || !.IsRectangular() || !boxesOverlapWithPadding(.Box, .Box, 0) {
				continue
			}
			,  := relatedNodes(, , )
			if  != nil {
				return 
			}
			if ! {
				.NodeOverlaps++
			}
		}
		for ,  := range .Edges {
			if  := .Step();  != nil {
				return 
			}
			if .IsInvisible || .IsCurve {
				continue
			}
			,  := endpointAncestor(, , )
			if  != nil {
				return 
			}
			if  && .From !=  && .To !=  {
				continue
			}
			for  := 1;  < len(.Points); ++ {
				if  := .Step();  != nil {
					return 
				}
				// The first/last approach may begin inside a shape before
				// border tracing. Later reentry into an endpoint is an obstacle.
				if  == 1 && .From ==  ||  == len(.Points)-1 && .To ==  {
					continue
				}
				if segmentEntersBox(.Box, .Points[-1], .Points[]) {
					.RouteObstructions++
					break // One obstruction per edge/node, independent of subdivision.
				}
			}
		}
	}
	return nil
}

func relatedNodes(,  *layoutgraph.Node,  *limits.WorkGuard) (bool, error) {
	,  := evaluationIsDescendantOf(, , )
	if  ||  != nil {
		return , 
	}
	return evaluationIsDescendantOf(, , )
}

func endpointAncestor( *layoutgraph.Edge,  *layoutgraph.Node,  *limits.WorkGuard) (bool, error) {
	,  := evaluationIsDescendantOf(.From, , )
	if  ||  != nil {
		return , 
	}
	return evaluationIsDescendantOf(.To, , )
}

// An open-rectangle slab intersection: touching or following a border does not
// occlude the interior. This also handles diagonal segments without sampling.
func segmentEntersBox( geo.Box, ,  *geo.Point) bool {
	if .Width <= 0 || .Height <= 0 || .X == .X && .Y == .Y {
		return false
	}
	,  := 0.0, 1.0
	for ,  := range [][4]float64{
		{.X, .X - .X, .TopLeft.X, .TopLeft.X + .Width},
		{.Y, .Y - .Y, .TopLeft.Y, .TopLeft.Y + .Height},
	} {
		, , ,  := [0], [1], [2], [3]
		if  == 0 {
			if  <=  ||  >=  {
				return false
			}
			continue
		}
		,  := (-)/, (-)/
		if  >  {
			,  = , 
		}
		,  = math.Max(, ), math.Min(, )
		if  >=  {
			return false
		}
	}
	return  < 
}