package d2isometricimg

import (
	
	
	
)

// Identity is shared by all triangles of one object and immutable after mesh
// construction. Materials retain their authored paint alpha; object opacity is
// applied once to the composed object, including its individual text decals.
type nativeOpacityGroup struct {
	Opacity float64
}

// A paint owner is ordering metadata, not another opacity effect. The bool
// also gives each allocation a distinct address for stable shared identity.
type nativePaintOwner struct {
	Opaque bool
}

func ( *meshBuilder) ( int) {
	if .err != nil ||  >= len(.triangles) {
		return
	}
	var  *nativePaintOwner
	,  := false, false
	for ,  := range .triangles[:] {
		if .OpacityGroup != nil {
			return // This complete object already owns its opacity composition.
		}
		if .PaintOwner != nil {
			 = .PaintOwner
		}
		// Physical sidewalls retain source fill alpha independently of cap
		// texture margins or printed text. Transparent symbols without a solid
		// body remain in the depth-ordered transparent pass.
		if .Material != nil && !.Material.Unlit && .Material.Texture == nil && .Material.Color.A > 0 {
			 =  || .Material.Color.A == 255
			 =  || .Material.Color.A < 255
		}
	}
	if  == nil {
		 = &nativePaintOwner{Opaque:  && !}
	}
	for  := ;  < len(.triangles); ++ {
		.triangles[].PaintOwner = 
	}
}

type rasterOpacityPlan struct {
	plan       *rasterDrawPlan
	bounds     image.Rectangle
	opacity    float64
	depth      float64
	opaque     bool
	writeDepth bool
}

func ( *Raster) ( []Triangle) rasterDrawPlan {
	 := rasterDrawPlan{triangles: , paints: make(map[*Material]rasterPaint), groups: make(map[int]rasterOpacityPlan)}
	type  struct {
		  *nativeOpacityGroup
		 *nativePaintOwner
	}
	 := make(map[]int)
	 := make(map[int][]Triangle)
	for ,  := range  {
		if ,  := .paints[.Material]; ! {
			.paints[.Material] = rasterMaterial(.Material)
		}
		if .OpacityGroup == nil && .PaintOwner == nil {
			.order = append(.order, )
			continue
		}
		 := {: .OpacityGroup}
		if . == nil {
			. = .PaintOwner
		}
		,  := []
		if ! {
			 = 
			[] = 
			.order = append(.order, )
		}
		 := .groups[]
		if .OpacityGroup != nil {
			.opacity = .OpacityGroup.Opacity
		} else {
			.opacity, .opaque, .writeDepth = 1, .PaintOwner.Opaque, true
		}
		var  [3]rasterPoint
		for ,  := range .V {
			[] = .camera.project(.Position)
			.depth += rasterDot(.Position, .camera.direction)
		}
		, , ,  := rasterBounds(, .camera.width, .camera.height)
		.bounds = .bounds.Union(image.Rect(, , , ))
		.groups[] = 
		.OpacityGroup, .PaintOwner = nil, nil
		[] = append([], )
	}
	for ,  := range  {
		 := .groups[]
		 := .rasterPrepare()
		.plan = &
		// Match the ordinary comparator's sum of three vertex depths.
		.depth /= float64(len())
		.groups[] = 
	}
	 := func( int) bool {
		if ,  := .groups[];  {
			return !.opaque
		}
		 := []
		 := .paints[.Material]
		return .NoDepthWrite || .multiply || .alpha < .999 || .texture != nil
	}
	 := func( int) float64 {
		if ,  := .groups[];  {
			return .depth
		}
		 := 0.
		for ,  := range [].V {
			 += rasterDot(.Position, .camera.direction)
		}
		return 
	}
	sort.SliceStable(.order, func(,  int) bool {
		,  := .order[], .order[]
		// Native paint owners keep their substrate and decals as one unit.
		// Opaque units resolve depth first; remaining units and unowned alpha
		// primitives share one back-to-front order without pairwise exceptions.
		if () != () {
			return !()
		}
		return () && () < ()
	})
	return 
}

// Composite into a copy of the existing backdrop, then interpolate the result
// once. This is algebraically equivalent to an isolated premultiplied group,
// and reuses the existing physical shading and depth rules for its inner mesh.
// Scratch is shared across objects and never exceeds one supersampled strip.
func ( *Raster) ( *rasterWork,  *image.RGBA,  []float32,  rasterOpacityPlan,  []bool) error {
	if .opacity <= 0 || .plan == nil {
		return nil
	}
	 := .bounds.Intersect(.Rect)
	if .Empty() {
		return nil
	}
	for  := .Min.Y;  < .Max.Y;  += rasterStripRows * .aa {
		 := min(.Max.Y, +rasterStripRows*.aa)
		 := image.Rect(.Min.X, , .Max.X, )
		 := .Dx() * .Dy()
		 := int64(3)
		if .writeDepth {
			++
		}
		if  := .charge(int64() * );  != nil {
			return 
		}
		if cap(.groupPixels) < *4 {
			.groupPixels = make([]uint8, *4)
		}
		if cap(.groupDepth) <  {
			.groupDepth = make([]float32, )
		}
		 := &image.RGBA{Pix: .groupPixels[:*4], Stride: .Dx() * 4, Rect: }
		 := .groupDepth[:]
		for  := .Min.Y;  < .Max.Y; ++ {
			,  := .PixOffset(.Min.X, ), .PixOffset(.Min.X, )
			copy(.Pix[:+.Dx()*4], .Pix[:+.Dx()*4])
			 := ( - .Min.Y) * .Dx()
			 := (-.Rect.Min.Y)*.Rect.Dx() + .Min.X - .Rect.Min.X
			copy([:+.Dx()], [:+.Dx()])
		}
		if  := .drawPrepared(, , , *.plan, nil);  != nil {
			return 
		}
		for  := .Min.Y;  < .Max.Y; ++ {
			for  := .Min.X;  < .Max.X; ++ {
				,  := .PixOffset(, ), .PixOffset(, )
				 := false
				for  := 0;  < 4; ++ {
					 := .Pix[+]
					 := uint8(math.Round(float64(.Pix[+])*.opacity + float64()*(1-.opacity)))
					.Pix[+] = 
					 =  ||  != 
				}
				if  != nil &&  {
					[(/.aa)*.width+/.aa] = true
				}
			}
			if .writeDepth {
				 := ( - .Min.Y) * .Dx()
				 := (-.Rect.Min.Y)*.Rect.Dx() + .Min.X - .Rect.Min.X
				copy([:+.Dx()], [:+.Dx()])
			}
		}
	}
	return nil
}