package d2raster

import (
	
	
	

	
)

// effectRenderBounds tightens an effect layer around the actual stroke
// segments crossing this destination. A long connection can have a wide full
// frame bounding box while occupying only a narrow part of a horizontal band.
// Fills and filtered nodes retain their established conservative bounds.
func effectRenderBounds( context.Context,  *preparedNode,  image.Rectangle) (image.Rectangle, error) {
	if  := .Err();  != nil {
		return image.Rectangle{}, 
	}
	if  == nil || .opacity == 0 {
		return image.Rectangle{}, nil
	}
	 := .bounds.Intersect()
	if .Empty() || len(.filters) != 0 {
		return , nil
	}
	var  image.Rectangle
	if  := .primitive;  != nil {
		if .vector != nil {
			var  error
			,  = (, .vector, )
			if  != nil {
				return image.Rectangle{}, 
			}
		} else if .fill == nil && .stroke != nil && .image == nil {
			var  error
			,  = strokeRegionBounds(, , )
			if  != nil {
				return image.Rectangle{}, 
			}
		} else {
			 = .bounds.Intersect()
		}
	}
	for ,  := range .children {
		,  := (, , )
		if  != nil {
			return image.Rectangle{}, 
		}
		 = unionRect(, )
	}
	return .Intersect(), nil
}

func strokeRegionBounds( context.Context,  *preparedPrimitive,  image.Rectangle) (image.Rectangle, error) {
	 := .bounds.Intersect()
	if .Empty() || math.Abs(float64(.referenceBounds.Min.X)) > 1<<20 || math.Abs(float64(.referenceBounds.Min.Y)) > 1<<20 || !boundedRegionPoint(d2scene.Point{X: .transform.E, Y: .transform.F}) {
		return , nil
	}
	 := .transform.MaxScale()
	 := strokeRegionExtent(.stroke, .transform) + 1
	if !finite() ||  > 1<<20 {
		return , nil
	}
	,  := math.Inf(1), math.Inf(1)
	,  := math.Inf(-1), math.Inf(-1)
	 := float64(.Min.Y) - 
	 := float64(.Max.Y) + 
	 := 0
	for ,  := range .strokeRuns {
		 := len(.points) - 1
		if .closed {
			++
		}
		for  := 0;  < ; ++ {
			if &255 == 0 {
				if  := .Err();  != nil {
					return image.Rectangle{}, 
				}
			}
			++
			 := .points[]
			 := .points[(+1)%len(.points)]
			if !boundedStrokeLocalPoint(, ) || !boundedStrokeLocalPoint(, ) {
				return , nil
			}
			 := .transform.Point()
			 := .transform.Point()
			if !boundedRegionPoint() || !boundedRegionPoint() {
				return , nil
			}
			if max(.Y, .Y) <  || min(.Y, .Y) >  {
				continue
			}
			,  := 0.0, 1.0
			,  := .X-.X, .Y-.Y
			if  != 0 {
				,  := (-.Y)/, (-.Y)/
				,  = max(0, min(, )), min(1, max(, ))
			}
			,  := .X+*, .X+*
			,  := .Y+*, .Y+*
			,  = min(, , ), min(, , )
			,  = max(, , ), max(, , )
		}
	}
	if math.IsInf(, 1) {
		return image.Rectangle{}, nil
	}
	 = max(-, float64(.Min.X))
	 = max(-, float64(.Min.Y))
	 = min(+, float64(.Max.X))
	 = min(+, float64(.Max.Y))
	if  >=  ||  >=  {
		return image.Rectangle{}, nil
	}
	return image.Rect(int(math.Floor()), int(math.Floor()), int(math.Ceil()), int(math.Ceil())).Intersect(), nil
}

func boundedRegionPoint( d2scene.Point) bool {
	return finitePoint() && math.Abs(.X) <= 1<<20 && math.Abs(.Y) <= 1<<20
}

// addCircle uses cubic Beziers, whose control points lie outside the true
// circle. Their convex hull remains a conservative envelope after any affine
// transform; the true radius alone can miss rotated large caps and joins.
func strokeRegionExtent( *preparedStroke,  d2scene.Matrix) float64 {
	 := strokeExtent(, )
	if .cap == d2scene.CapRound || .join == d2scene.JoinRound {
		const  = 0.5522847498307936 // Keep aligned with addCircle.
		 = max(, .width/2*.MaxScale()*math.Hypot(1, ))
	}
	return 
}

func boundedStrokeLocalPoint( d2scene.Point,  float64) bool {
	// Stroke outlines add offsets in local coordinates before transformation.
	// Bound those intermediate magnitudes as well as final device coordinates:
	// large cancelling products otherwise lose more than the one-pixel inset.
	return finitePoint() && max(math.Abs(.X), math.Abs(.Y))* <= 1<<40
}