package d2isometricimg

import (
	
	
	

	
	
	
)

const (
	nativeBarrelSegments  = 72
	nativeUprightSegments = 72
)

// Flatten only the crown needed by the source's existing print allocation.
// This is part of the barrel, with one material and no attached label plate.
// The remaining profile is stretched vertically to retain the original full
// height; the footprint and every print rectangle stay unchanged.
func nativeQueueCrown( d2isometric.Node) float64 {
	if .Size.X <= 0 || .Size.Z <= 0 {
		return 1
	}
	 := nativeFaceSource(, .Fill)
	 := label.FromString(.Metadata.Original.LabelPosition)
	 := nativeNodeLabelSurface(, , 0)
	 := 0.
	 := func( labelSurface) {
		if .width > 0 && .depth > 0 {
			 = max(, math.Abs(.center.Z-.Position.Z)+.depth/2)
		}
	}
	if .Metadata.Original.Icon != nil {
		 := nativeNodeIconSurface(, , 0)
		 := .Size.Z / float64(.Height)
		,  := surfaceIconLayout(, .Metadata.Original, , "node")
		()
		if !.IsOutside() && !.IsBorder() {
			 = 
			if .Metadata.Original.LabelHeight > 0 {
				.depth = min(.depth, float64(.Metadata.Original.LabelHeight)**1.06)
			}
		}
	}
	if .Metadata.Original.Label != "" && !.IsOutside() && !.IsBorder() {
		()
	}
	if  <= 0 {
		return 1
	}
	 = min(1, (+min(.02, .Size.Z*.02))/(.Size.Z/2))
	// Snap outward to a tessellation vertex so the entire authored print area
	// is supported, including the sliver between ideal ellipse and polygon.
	 := math.Ceil(math.Asin()/(2*math.Pi/nativeBarrelSegments)) * (2 * math.Pi / nativeBarrelSegments)
	return max(0, math.Cos())
}

func nativeBarrelPoint( int, , ,  float64) (,  float64) {
	 := float64() * 2 * math.Pi / nativeBarrelSegments
	return  * (1 + min(math.Cos(), )) / (1 + ), math.Sin() *  / 2
}

func nativeUprightPoint( int,  bool) (float64, float64) {
	if  {
		// D2's hexagon has slightly off-center side points.
		 := [6][2]float64{{1, 2*(43.6/87.3) - 1}, {.5, 1}, {-.5, 1}, {-1, 2*(43.6/87.3) - 1}, {-.5, -1}, {.5, -1}}[%6]
		return [0], [1]
	}
	 := float64() * 2 * math.Pi / nativeUprightSegments
	return math.Cos(), math.Sin()
}

// Source ports lie on the D2 outline, which can extend beyond a solid's curved
// wall near the board plane. Keep the source port as a path
// vertex and add only the short inward segment needed to reach the real solid.
// All exterior bends, source metadata, and route elevations remain untouched.
func nativeSolidContactRoutes( context.Context,  []d2isometric.Edge,  []d2isometric.Node,  [][]Vec,  ...map[string]float64) ([][]Vec, error) {
	if  == nil {
		return nil, fmt.Errorf("native solid contacts require a context")
	}
	if len() != len() {
		return nil, fmt.Errorf("native solid contacts require one route per edge")
	}
	 := make(map[string]d2isometric.Node)
	for ,  := range  {
		if nativeSolidNode() && .Opacity > 0 {
			[.ID] = 
		}
	}
	 := func( d2isometric.Node, ,  Vec) (Vec, bool) {
		 := .Y
		if len() > 0 {
			// The cap stays at its existing print height while a lower
			// supporting plate extends the body downward. Intersect in the
			// original body's coordinates, then restore the route's plane.
			if  := [0][.BoardID];  < 0 {
				.Y = hierarchyNodeExtension(, ).inverseY(.Y)
			}
		}
		,  := nativeSolidContact(, , )
		.Y = 
		return , 
	}
	 := make([][]Vec, len())
	for ,  := range  {
		if  := .Err();  != nil {
			return nil, 
		}
		[] = 
		if len() < 2 || [].Opacity <= 0 {
			continue
		}
		 := len() - 1
		,  := [0], []
		var ,  bool
		if ,  := [[].Source];  {
			,  = (, [0], nsub([0], [1]))
		}
		if ,  := [[].Target];  {
			,  = (, [], nsub([], [-1]))
		}
		if ! && ! {
			continue
		}
		[] = make([]Vec, 0, len()+2)
		if  {
			[] = append([], )
		}
		[] = append([], ...)
		if  {
			[] = append([], )
		}
	}
	return , nil
}

func nativeSolidContact( d2isometric.Node, ,  Vec) (Vec, bool) {
	if .Type == d2target.ShapeQueue {
		return nativeQueueContact(, , )
	}
	return nativeUprightContact(, , )
}

// Intersect the actual upright mesh's horizontal cross-section, including its
// polygonal tessellation. D2 cylinder side ports need this because their
// source outline has straight side walls,
// while the 3D footprint is elliptical. The short connector remains inside the
// compiled footprint, and does not alter the source endpoint or its tangent.
func nativeUprightContact( d2isometric.Node, ,  Vec) (Vec, bool) {
	if !captionFinite(.Size.X, .Size.Y, .Size.Z, .Position.X, .Position.Y, .Position.Z, .X, .Y, .Z, .X, .Z) || .Size.X <= 0 || .Size.Z <= 0 {
		return , false
	}
	, ,  := .Size.X, .Size.Z, nativeSolidHeight()
	 := hierarchyNodeRelief()
	 := .Position.Y - .Size.Y/2
	 := (.Y - ) / 
	if  <= 0 ||  >=  {
		return , false
	}
	 := math.Hypot(.X, .Z)
	if  <= 1e-9 {
		return , false
	}
	 := nv(.X/, 0, .Z/)
	,  := .X-.Position.X, .Z-.Position.Z
	 := .01
	if .Metadata.Original.Width > 0 {
		 =  / float64(.Metadata.Original.Width)
	}
	 := min(.05, max(1e-7, float64(.StrokeWidth)*/2))
	if math.Abs() > /2+ || math.Abs() > /2+ {
		return , false
	}
	 := 1.
	,  := nativeUprightSegments, .Type == d2target.ShapeHexagon
	if  {
		 = 6
	}
	 := func( int) (float64, float64) {
		,  := nativeUprightPoint(, )
		return  *  / 2 * ,  *  / 2 * 
	}
	,  := 0., math.Hypot(, )+2*
	for  := 0;  < ; ++ {
		,  := ()
		,  := ( + 1)
		,  := -, -
		,  := *(-)+*(-), *.X+*.Z
		if math.Abs() < 1e-12 {
			if  < -1e-10 {
				return , false
			}
			continue
		}
		 :=  / 
		if  > 0 {
			 = min(, )
		} else {
			 = max(, )
		}
		if  > +1e-10 {
			return , false
		}
	}
	if  <= 1e-7 || !captionFinite() {
		return , false
	}
	 := nadd(, nmul(, ))
	.Y = .Y
	return , true
}

// Clip a horizontal ray against the actual convex polygonal barrel, including
// its flat print crown and full end caps. An ideal ellipse intersection can
// otherwise stop outside the tessellated wall.
func nativeQueueContact( d2isometric.Node, ,  Vec) (Vec, bool) {
	if !captionFinite(.Size.X, .Size.Y, .Size.Z, .Position.X, .Position.Y, .Position.Z, .X, .Y, .Z, .X, .Z) || .Size.X <= 0 || .Size.Z <= 0 {
		return , false
	}
	, ,  := .Size.X, .Size.Z, nativeSolidHeight()
	 := hierarchyNodeRelief()
	,  := .Position.Y-.Size.Y/2, *
	if .Y <=  || .Y >= + {
		return , false
	}
	 := math.Hypot(.X, .Z)
	if  <= 1e-9 {
		return , false
	}
	 := nv(.X/, 0, .Z/)
	,  := .X-.Position.X, .Z-.Position.Z
	// An authored stroke may place its port just outside the fill footprint.
	// Reject unrelated external points instead of extending a route across
	// arbitrary free space to reach a distant component.
	 := .01
	if .Metadata.Original.Width > 0 {
		 =  / float64(.Metadata.Original.Width)
	}
	 := min(.05, max(1e-7, float64(.StrokeWidth)*/2))
	if math.Abs() > /2+ || math.Abs() > /2+ {
		return , false
	}
	,  := 0., math.Hypot(, )+2*
	 := func(,  float64) bool {
		if math.Abs() < 1e-12 {
			return  >= -1e-10
		}
		 :=  / 
		if  > 0 {
			 = min(, )
		} else {
			 = max(, )
		}
		return  <= +1e-10
	}
	if !(.X, /2-) || !(-.X, /2+) {
		return , false
	}
	 := nativeQueueCrown()
	for  := 0;  < nativeBarrelSegments; ++ {
		,  := nativeBarrelPoint(, , , )
		,  := nativeBarrelPoint(+1, , , )
		,  := -, -
		 := *(-(.Y-)) + *(-)
		if !(*.Z, ) {
			return , false
		}
	}
	if  <= 1e-7 || !captionFinite() {
		return , false
	}
	// The exact boundary is shared with the solid mesh. An epsilon would move
	// an arrow tip under the material and is unnecessary for these same planes.
	 := nadd(, nmul(, ))
	.Y = .Y
	return , true
}