package d2isometricimg

import (
	

	
	
	
	
	
)

func nativeCaptionPaint( *d2isometric.Scene) func(string) string {
	 := d2themescatalog.Find(0)
	if  != nil {
		 = d2themescatalog.Find(.ThemeID)
		.ApplyOverrides(.ThemeOverrides)
	}
	return func( string) string { return d2themes.ResolveThemeColor(, ) }
}

func nativeCaptionOnEdge( d2target.Connection) bool {
	return label.FromString(.LabelPosition).IsOnEdge()
}

// Compiled text metrics describe the print area independently of the route.
// Curves and lane fans subdivide legs into arbitrarily short pieces; neither
// those pieces nor neighboring captions may shrink authored text. Placement
// handles conflicts while retaining these dimensions, including multiline text.
func nativeRouteCaptionSize( d2target.Text,  bool,  float64) (,  float64) {
	// Raw API callers may omit metrics. Keep a bounded fallback instead of
	// estimating from untrusted text length; the painter still enforces its
	// source, glyph and pixel budgets. Compiled metrics take precedence.
	 = max(1, float64(float64(.LabelWidth)*)+.1)
	 := .LabelHeight
	if  <= 0 {
		 = 25
	}
	 = max(.25, float64(float64()*)+.06)
	if  {
		if .Label == "" {
			return .38, .38
		}
		// surfaceIconLayout reserves a small glyph-overhang margin and gap.
		// Include them before adding the icon lane, so even long labels keep
		// their complete print area rather than pushing the icon above them.
		 = max(, float64(float64(.LabelWidth)**1.06)+.04) + .38 + .08
		 = max(, .38)
	}
	return , 
}

// The compiled label position and percentage are semantic placement inputs.
// Apply them along the displayed planar route, retaining a flat print surface
// that follows its leg when it fits. Unspecified positions still use the
// collision-aware placer.
func nativeConnectionCaptionSurface( []Vec,  d2target.Connection, , ,  float64) (labelSurface, bool) {
	 := label.FromString(.LabelPosition)
	,  := .5, 0.
	switch  {
	case label.InsideMiddleLeft, label.OutsideTopLeft, label.OutsideBottomLeft:
		 = .25
	case label.InsideMiddleCenter, label.OutsideTopCenter, label.OutsideBottomCenter:
	case label.InsideMiddleRight, label.OutsideTopRight, label.OutsideBottomRight:
		 = .75
	case label.UnlockedTop, label.UnlockedMiddle, label.UnlockedBottom:
		 = max(0, min(1, .LabelPercentage))
	default:
		return labelSurface{}, false
	}
	switch  {
	case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight, label.UnlockedTop:
		 = -1
	case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight, label.UnlockedBottom:
		 = 1
	}
	if !captionFinite(, , , ) ||  <= 0 ||  <= 0 ||  <= 0 || len() < 2 || len() > 10000 {
		return labelSurface{}, false
	}
	 := routeLengths()
	 := [len()-1]
	if  <= 1e-9 || !captionFinite() {
		return labelSurface{}, false
	}
	 :=  * 
	for  := 1;  < len(); ++ {
		 := [] - [-1]
		if  <= 1e-9 || [] <  {
			continue
		}
		 := nunit(nsub([], [-1]))
		 := nlerp([-1], [], (-[-1])/)
		// Preserve above/below relative to the source route direction. Unlike
		// upright SVG text, rotated text has depth as its normal extent.
		 :=  * (/2 + (float64(.StrokeWidth)/2+label.PADDING)*)
		.X -= .Z * 
		.Z += .X * 
		.Y += .006
		if .X < -1e-6 || math.Abs(.X) < 1e-6 && .Z < 0 {
			 = nmul(, -1)
		}
		 := math.Atan2(.Z, .X)
		,  := nativeCaptionRouteRun(, , )
		// D2 reserves horizontal label dimensions. A short leg cannot hold
		// that entire width after rotating it toward the endpoints. Retain
		// the source X orientation when it would overrun the usable run,
		// including clearance for the raised endpoint faces. This changes
		// only print direction and restores the compiled source label anchor;
		// font size and the physical plane stay unchanged.
		if +2* > 2*min(-, -)+1e-9 {
			 = 0
			if ,  := nativeCaptionSourceCenter(, , );  {
				.X, .Z = .X, .Z
			}
		}
		return labelSurface{center: , width: , depth: , angle: }, true
	}
	return labelSurface{}, false
}

// Aggregate a direction-consistent run around the caption anchor. Curved routes
// contain many tiny tessellation segments, while elbows delimit distinct legs.
// Their individual segment lengths must never determine text orientation.
func nativeCaptionRouteRun( []Vec,  []float64,  int) (,  float64) {
	 := nunit(nsub([], [-1]))
	 := func( int) bool {
		if []-[-1] <= 1e-9 {
			return true
		}
		return ndot(, nunit(nsub([], [-1]))) >= math.Sqrt(3)/2
	}
	,  = [-1], []
	for  :=  - 1;  >= 1 && (); -- {
		 = [-1]
	}
	for  :=  + 1;  < len() && (); ++ {
		 = []
	}
	return , 
}

// Curved D2 routes retain control points for their compiled label placement.
// Using the tessellated curve's midpoint instead moves labels inward and can
// overlap two labels whose source allocations were disjoint. Translate the
// compiled label rectangle with the same source-to-scene offset as the route.
func nativeCaptionSourceCenter( []Vec,  d2target.Connection,  float64) (Vec, bool) {
	if len(.Route) < 2 || len(.Route) > 10000 {
		return Vec{}, false
	}
	for ,  := range .Route {
		if  == nil || !captionFinite(.X, .Y) || math.Abs(.X) > rasterCoordinateLimit || math.Abs(.Y) > rasterCoordinateLimit {
			return Vec{}, false
		}
	}
	 := .GetLabelTopLeft()
	if  == nil || !captionFinite(.X, .Y) {
		return Vec{}, false
	}
	return nv([0].X+(.X+float64(.LabelWidth)/2-.Route[0].X)*, 0,
		[0].Z+(.Y+float64(.LabelHeight)/2-.Route[0].Y)*), true
}