package d2isometricimg

import (
	
	

	
	
	
	libcolor 
)

// Surface decoration must not select a different physical shape. In
// particular, a patterned cylinder is still a barrel, not an extruded drawing
// of the cylinder icon. Source X/Z dimensions remain the solid's footprint.
func nativeSolidNode( d2isometric.Node) bool {
	if .Container {
		return false
	}
	switch .Type {
	case d2target.ShapeCylinder, d2target.ShapeQueue, d2target.ShapeCircle, d2target.ShapeOval, d2target.ShapeHexagon:
		return true
	}
	return false
}

func nativeSolidHeight( d2isometric.Node) float64 {
	 := max(.04, .Size.Y)
	if .Metadata.Original.ThreeDee {
		 := .01
		if .Metadata.Original.Width > 0 {
			 = .Size.X / float64(.Metadata.Original.Width)
		}
		 += d2target.THREE_DEE_OFFSET * 
	}
	return 
}

type nativeSolidPaint struct {
	cap, ink, wall  *Material
	inkFit          float64
	shadowFillAlpha *uint8
}

func ( *meshBuilder) ( d2isometric.Node, ,  string) nativeSolidPaint {
	 := nativeFaceSource(, )
	.Type = 
	// A cap and its unwrapped patterned wall share the node's original
	// allocation. Decorating a volume cannot double the face texture budget.
	 := .faceMaxPixels
	 := .FillPattern != "" && .FillPattern != "none"
	if  &&  > 0 {
		.faceMaxPixels = max(1, /2)
	}
	defer func() { .faceMaxPixels =  }()
	var ,  *image.RGBA
	if nativeClassicRim() {
		// Full physical rim ink is emitted by classicInkEdges. Do not carve
		// opacity out of the lower fill for a texture layer that is omitted.
		, _, _ = .nativeFaceLayers()
	} else {
		, , _ = .nativeFaceLayers(, .Opacity)
	}
	 := nativeMaterial("white", .68, 0, .Opacity)
	.Texture, .Vector = , nativeVectorForTexture(.ctx, )
	if  == nil {
		.Color.A = 0
	}
	 := nativeMaterial("white", .68, 0, .Opacity)
	.Texture, .Vector, .Unlit = , nativeVectorForTexture(.ctx, ), true
	if  == nil {
		.Color.A = 0
	}
	// A barrel's crown points upward. Let its actual normals and lighting
	// establish tone, using the same source material as the upright end cap.
	 := nativeMaterial(, .68, 0, .Opacity)
	if  && .err == nil {
		 := 
		.Type = d2target.ShapeRectangle
		.Text = d2target.Text{}
		.LabelPosition = ""
		.Stroke, .StrokeWidth, .StrokeDash = "transparent", 0, 0
		.DoubleBorder, .BorderRadius = false, 0
		,  := .nativeFace()
		 = nativeMaterial("white", .68, 0, .Opacity)
		.Texture, .Vector = , nativeVectorForTexture(.ctx, )
		if  == nil {
			.Color.A = 0
		}
	}
	var  *uint8
	if  := nativeMaterial(, .68, 0, .Opacity).Color.A;  > 0 {
		// This texture paints an existing filled cap. Its rasterized perimeter
		// is not a physical hole that can weaken the wall's cast silhouette.
		 = &
	}
	if nativePaint(, "transparent").A == 255 {
		.svgSolidTexture = true
		.svgSolidTexture = true
		.Vector = nativeVectorSolidCap(.Vector, nativePaint(, "transparent"))
		if .Vector != nil && libcolor.IsGradient() {
			.Vector.capBackground = nil
			.Vector.capPaint, .err = scenepaint.Gradient()
		}
	}
	return nativeSolidPaint{cap: , ink: , wall: , inkFit: .solidInkFit(,  == d2target.ShapeHexagon), shadowFillAlpha: }
}

// A centered SVG stroke is an offset curve, not another exact ellipse. Its
// antialiasing fringe can also extend a fraction of a pixel beyond that curve.
// Fit the complete painted extent inside the actual convex cap polygon, with
// no geometry expansion, stroke thickening, or unsupported decal corners.
// Only row endpoints need testing: every cap is convex. Work is bounded by the
// already-budgeted texture pixels and O(height) contour tests.
func ( *meshBuilder) ( *image.RGBA,  bool) float64 {
	if  == nil {
		return 1
	}
	 := .Bounds()
	 := 1.
	 := func(,  float64) {
		if ! {
			 = max(, math.Hypot(, )/math.Cos(math.Pi/nativeUprightSegments))
			return
		}
		for  := 0;  < 6; ++ {
			,  := nativeUprightPoint(, true)
			,  := nativeUprightPoint(+1, true)
			,  := -, -
			 = max(, (*+*)/(*+*))
		}
	}
	for  := .Min.Y;  < .Max.Y; ++ {
		if (-.Min.Y)%64 == 0 {
			if  := .ctx.Err();  != nil {
				.err = 
				return 1
			}
		}
		,  := .Max.X, .Min.X
		for  := .Min.X;  < .Max.X; ++ {
			if .Pix[.PixOffset(, )+3] != 0 {
				,  = min(, ), max(, +1)
			}
		}
		if  <=  {
			continue
		}
		for ,  := range []int{, } {
			for ,  := range []int{,  + 1} {
				(2*float64(-.Min.X)/float64(.Dx())-1, 2*float64(-.Min.Y)/float64(.Dy())-1)
			}
		}
	}
	return 1 / 
}

// The same source paint is applied to a convex cap and a rounded sidewall.
// Curved normals, sharp rims and directional light establish volume;
// there is no 2D cylinder outline or synthetic hardware base underneath it.
func ( *meshBuilder) ( d2isometric.Node) {
	 := .Fill
	if nativeToken(.Metadata.Original.Fill) && !.FillExplicit {
		 = "#dce5eb"
	}
	 := .Type
	if  == d2target.ShapeCylinder ||  == d2target.ShapeQueue ||  == d2target.ShapeCircle {
		 = d2target.ShapeOval
	}
	 := .solidPaint(, , )
	if .err != nil {
		return
	}
	,  := .Size.X, .Size.Z
	 := nativeSolidHeight()
	 := .Position.Y - .Size.Y/2
	 := .scale
	if  <= 0 {
		 = .01
	}
	for  := 1;  >= 0; -- {
		if  == 1 && !.Metadata.Original.Multiple {
			continue
		}
		 := nv(.Position.X+float64()*d2target.MULTIPLE_OFFSET*,
			+.nodeSupportDrop-float64()*min(.08, *.18), .Position.Z-float64()*d2target.MULTIPLE_OFFSET*)
		if .Type == d2target.ShapeQueue {
			.solidBarrel(, , , -.nodeSupportDrop, , nativeQueueCrown())
		} else {
			.solidUpright(, , , -.nodeSupportDrop, .Type == d2target.ShapeHexagon, )
		}
	}
	// Plan support from the final painted content, after icon/text partitioning.
	// Keep the original cylinder/queue inner box and compiled label metrics.
	 := nativeFaceSource(, )
	 := len(.triangles)
	.canonicalNodeContent(, , , +)
	if .Type != d2target.ShapeQueue {
		.solidContentSupport(, , , , )
	}
}

func ( *meshBuilder) ( d2isometric.Node,  string, ,  float64,  int) {
	if .err != nil ||  == len(.triangles) {
		return
	}
	,  := .Position.X-.Size.X/2, .Position.X+.Size.X/2
	,  := .Position.Z-.Size.Z/2, .Position.Z+.Size.Z/2
	, , ,  := math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)
	 := make(map[*Material][]Triangle)
	 := []*Material{}
	for ,  := range .triangles[:] {
		if !.NoDepthWrite || .Material == nil || .Material.Texture == nil {
			continue
		}
		if ,  := [.Material]; ! {
			 = append(, .Material)
		}
		[.Material] = append([.Material], )
	}
	for ,  := range  {
		, , ,  := math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)
		for ,  := range [] {
			for ,  := range .V {
				, , ,  = min(, .Position.X), min(, .Position.Z), max(, .Position.X), max(, .Position.Z)
			}
		}
		 := solidTextureInk(.Texture)
		if .Empty() {
			continue
		}
		 := .Texture.Bounds()
		,  := +(-)*float64(.Min.X-.Min.X)/float64(.Dx()), +(-)*float64(.Min.Y-.Min.Y)/float64(.Dy())
		,  := +(-)*float64(.Max.X-.Min.X)/float64(.Dx()), +(-)*float64(.Max.Y-.Min.Y)/float64(.Dy())
		, , ,  = max(, ), max(, ), min(, ), min(, )
		if  <=  ||  <=  {
			continue
		} // authored outside captions stay outside
		, , ,  = min(, ), min(, ), max(, ), max(, )
	}
	if  <=  ||  <=  {
		return
	}
	 := false
	if ! && .Type != d2target.ShapeHexagon {
		for ,  := range []float64{, } {
			for ,  := range []float64{, } {
				,  := (-.Position.X)/(.Size.X/2), (-.Position.Z)/(.Size.Z/2)
				 := math.Cos(math.Pi / nativeUprightSegments)
				 =  || *+* > *
			}
		}
	}
	if ! {
		return
	}
	, , ,  = max(, -.025), max(, -.025), min(, +.025), min(, +.025)
	 := min(.02, *.15)
	 := append([]Triangle(nil), .triangles[:]...)
	.triangles = .triangles[:]
	.box(nv((+)/2, +-/2, (+)/2), nv(-, , -), nativeMaterial(, .68, 0, .Opacity), .008)
	// Keep the label last for outside-caption clearance and its link regions.
	.triangles = append(.triangles, ...)
}

func solidTextureInk( image.Image) image.Rectangle {
	 := .Bounds()
	, , ,  := .Max.X, .Max.Y, .Min.X, .Min.Y
	for  := .Min.Y;  < .Max.Y; ++ {
		for  := .Min.X;  < .Max.X; ++ {
			 := false
			if ,  := .(*image.RGBA);  {
				 = .Pix[.PixOffset(, )+3] > 3
			} else {
				, , ,  := .At(, ).RGBA()
				 =  > 3*257
			}
			if  {
				, , ,  = min(, ), min(, ), max(, +1), max(, +1)
			}
		}
	}
	if  <=  ||  <=  {
		return image.Rectangle{}
	}
	return image.Rect(, , , )
}

func ( nativeSolidPaint) (,  Vec, ,  float64) Vertex {
	// The native viewport includes the complete centered outline. Map that
	// viewport to the cap, so the fan cannot slice the outer half off its ink.
	 := .inkFit
	if  <= 0 {
		 = 1
	}
	return Vertex{Position: , Normal: , U: .5 + (-.5)/, V: .5 + (-.5)/}
}

func ( *meshBuilder) (, ,  Vertex,  nativeSolidPaint) {
	 := len(.triangles)
	.triangle(, , , .cap, true)
	for  := ;  < len(.triangles); ++ {
		.triangles[].ShadowFillAlpha = .shadowFillAlpha
	}
	 = len(.triangles)
	.triangle(, , , .ink, false)
	for  := ;  < len(.triangles); ++ {
		// A raster depth bias separates coincident printed ink without raising
		// it off the cap or changing the physical footprint/bounds.
		.triangles[].DepthBias = .0005
	}
}

func ( *meshBuilder) ( Vec, , ,  float64,  bool,  nativeSolidPaint) {
	 := nativeUprightSegments
	if  {
		 = 6
	}
	 := func( int) (float64, float64) { return nativeUprightPoint(, ) }
	for  := 0;  < ; ++ {
		 := func( int,  float64) Vertex {
			,  := ()
			,  := /(/2), /(/2)
			if  {
				,  := ()
				,  := ( + 1)
				,  = (-)*/2, (-)*/2
			}
			return Vertex{Position: nadd(, nv(*/2, , */2)), Normal: nunit(nv(, 0, )), U: float64() / float64(), V: 1 - /}
		}
		, , ,  := (, 0), (+1, 0), (+1, ), (, )
		.triangle(, , , .wall, true)
		.triangle(, , , .wall, true)
		 := func( int) Vertex {
			,  := ()
			return .capVertex(nadd(, nv(*/2, , */2)), nv(0, 1, 0), (+1)/2, (+1)/2)
		}
		 := .capVertex(nadd(, nv(0, , 0)), nv(0, 1, 0), .5, .5)
		.solidCapTriangle(, (+1), (), )
	}
}

// Queue is a horizontal barrel with real end caps and an integrated flat
// print crown. Cap and wall meet at one sharp rim; the shared edge pass inks it.
func ( *meshBuilder) ( Vec, , ,  float64,  nativeSolidPaint,  ...float64) {
	 := 1.
	if len() > 0 {
		 = max(0, min(1, [0]))
	}
	for  := 0;  < nativeBarrelSegments; ++ {
		,  := nativeBarrelPoint(, , , )
		,  := nativeBarrelPoint(+1, , , )
		 := math.Abs(-) < 1e-12 && math.Abs(-) < 1e-12
		 := func( int,  float64) Vertex {
			 := float64() * 2 * math.Pi / nativeBarrelSegments
			,  := nativeBarrelPoint(, , , )
			 := nunit(nv(0, math.Cos()/(/(1+)), math.Sin()/(/2)))
			if  {
				 = nv(0, 1, 0)
			}
			return Vertex{Position: nadd(, nv(, , )), Normal: , U: / + .5, V: float64() / nativeBarrelSegments}
		}
		, , ,  := (, -/2), (+1, -/2), (+1, /2), (, /2)
		.triangle(, , , .wall, true)
		.triangle(, , , .wall, true)
		for ,  := range []float64{-1, 1} {
			 := func( int) Vertex {
				 := float64() * 2 * math.Pi / nativeBarrelSegments
				,  := nativeBarrelPoint(, , , )
				return .capVertex(nadd(, nv(*/2, , )), nv(, 0, 0), (math.Sin()+1)/2, (1-math.Cos())/2)
			}
			,  := (), (+1)
			 := .capVertex(nadd(, nv(*/2, /2, 0)), nv(, 0, 0), .5, .5)
			if  > 0 {
				.solidCapTriangle(, , , )
			} else {
				.solidCapTriangle(, , , )
			}
		}
	}
}