package d2isometricimg

import (
	
	
	
	
	

	
	
	
	
)

// A plain contour is owned by geometry. Decorative source borders retain
// their texture, including border-label apertures and structured documents.
func nativeClassicRim( d2isometric.Node) bool {
	 := .Metadata.Original
	if .Opacity < 1 || nativePaint(.Stroke, "#263c4e").A < 255 || .DoubleBorder || .StrokeDash != 0 || .Label != "" && label.FromString(.LabelPosition).IsBorder() {
		return false
	}
	switch .Type {
	case d2target.ShapeClass, d2target.ShapeSQLTable, d2target.ShapeText, d2target.ShapeCode, d2target.ShapeImage:
		return false
	}
	// A transparent body has no physical walls from which to recover its rim.
	return !strings.EqualFold(.Fill, "none") && nativePaint(.Fill, "#dce5eb").A != 0
}

const (
	classicInkWeld            = 1e-6
	maxClassicInkTriangles    = 100_000
	maxClassicInkSegments     = 100_000
	maxClassicInkSupportFaces = 256
	maxClassicInkSupportWork  = 5_000_000
	classicInkDepthBias       = .001
)

type classicInkKey [3]int64
type classicInkPair struct{ a, c classicInkKey }
type classicInkFace struct {
	normal       Vec
	third        Vec
	cap          bool
	edgeNormals  [2]Vec
	interpolated bool
}
type classicInkEdge struct {
	a, c  Vec
	faces []classicInkFace
}
type classicInkSegment struct {
	a, c    Vec
	support []*classicInkFacet
}

// Projected finite supporting faces keep centered ink on its own physical
// surface. Extending infinite side planes can put rear-copy ink in front of
// the primary object, especially after hierarchy relief compresses the gap.
type classicInkFacet struct {
	points [3]Vec
	bias   float64
	keys   [3]classicInkKey
	normal Vec
}

func ( *classicInkFacet) ( Vec) (float64, bool) {
	, ,  := .points[0], .points[1], .points[2]
	// Ink support controls mesh clipping as well as depth. Round each product
	// before cancellation so fused arithmetic cannot move a point across a
	// supporting edge or change the triangulation of its clipped patch.
	 := float64((.Y-.Y)*(.X-.X)) + float64((.X-.X)*(.Y-.Y))
	if math.Abs() < 1e-14 {
		return 0, false
	}
	 := (float64((.Y-.Y)*(.X-.X)) + float64((.X-.X)*(.Y-.Y))) / 
	 := (float64((.Y-.Y)*(.X-.X)) + float64((.X-.X)*(.Y-.Y))) / 
	 := 1 -  - 
	if  < -1e-8 ||  < -1e-8 ||  < -1e-8 {
		return 0, false
	}
	return float64(*.Z) + float64(*.Z) + float64(*.Z) + .bias, true
}

func classicInkKeyOf( Vec) classicInkKey {
	return classicInkKey{int64(math.Round(.X / classicInkWeld)), int64(math.Round(.Y / classicInkWeld)), int64(math.Round(.Z / classicInkWeld))}
}
func classicInkLess(,  classicInkKey) bool {
	for  := range  {
		if [] != [] {
			return [] < []
		}
	}
	return false
}

// Weld only positions: lighting-normal and UV seams must not become ink.
// Real creases and silhouette transitions survive; planar triangulation and
// the gentle facets of a cylinder do not. The raster depth test handles
// occlusion by other parts of this object and by other objects.
func classicInkSegments( context.Context,  d2isometric.Node,  []Triangle,  ...float64) ([]classicInkSegment, error) {
	if len() > maxClassicInkTriangles {
		return nil, fmt.Errorf("isometric outline exceeds %d input triangles", maxClassicInkTriangles)
	}
	 := nativeCameraAxes()
	 := .direction
	 := make(map[classicInkPair]*classicInkEdge)
	 := make(map[classicInkPair]classicInkSegment)
	 := make(map[classicInkKey][]*classicInkFacet)
	 := make(map[classicInkPair][3]classicInkKey)
	type  struct {
		     float64
		 *classicInkFacet
	}
	var  []
	 := func( Vec) Vec { return nv(ndot(, .right), ndot(, .up), ndot(, )) }
	 := nativeSolidNode()
	for ,  := range  {
		if %1024 == 0 {
			if  := .Err();  != nil {
				return nil, 
			}
		}
		if .Material == nil || .Material.Color.A == 0 || .NoDepthWrite || !.CastShadow {
			continue
		}
		, ,  := .V[0].Position, .V[1].Position, .V[2].Position
		 := ncross(nsub(, ), nsub(, ))
		if nlen() < 1e-12 {
			continue
		}
		 = nunit()
		if ndot(, nadd(nadd(.V[0].Normal, .V[1].Normal), .V[2].Normal)) < 0 {
			 = nmul(, -1)
		}
		 := &classicInkFacet{points: [3]Vec{(), (), ()}, bias: .DepthBias,
			keys: [3]classicInkKey{classicInkKeyOf(), classicInkKeyOf(), classicInkKeyOf()}, normal: }
		for ,  := range []Vec{, , } {
			 := classicInkKeyOf()
			[] = append([], )
		}
		// Canonical caps are transparent texture quads, not the shape's actual
		// contour. Their open sidewall boundary supplies the exact outline.
		if ! && .Material.Texture != nil && math.Abs(.Y) > .999999 {
			 = append(, {.Y, })
			continue
		}
		if .DepthBias != 0 {
			continue
		}
		 :=  && ((.Type == d2target.ShapeQueue && math.Abs(.X) > .999999) || (.Type != d2target.ShapeQueue && .Y > .999999))
		 := [3]Vec{, , }
		// Analytic vertex normals describe the smooth surface, while mesh
		// edges only describe its tessellation. Interpolate the view-tangent
		// contour through each triangle instead of inking a stair-step path
		// along alternating latitude/longitude edges of a sphere.
		// Singly curved walls repeat one normal along a straight generatrix.
		// Their geometric silhouette is already straight and joins the exact
		// polygonal rim. Only doubly curved patches need an interpolated
		// contour to avoid latitude/longitude stair steps.
		 := ndot(.V[0].Normal, .V[1].Normal) < 1-1e-8 && ndot(.V[0].Normal, .V[2].Normal) < 1-1e-8 && ndot(.V[1].Normal, .V[2].Normal) < 1-1e-8
		if  {
			var  []Vec
			 := func( Vec) {
				for ,  := range  {
					if classicInkKeyOf() == classicInkKeyOf() {
						return
					}
				}
				 = append(, )
			}
			for ,  := range  {
				 := ( + 1) % 3
				,  := ndot(.V[].Normal, ), ndot(.V[].Normal, )
				if math.Abs() < 1e-10 {
					()
				}
				if * < 0 {
					(nlerp(, [], /(-)))
				}
			}
			if len() == 2 {
				,  := [0], [1]
				,  := classicInkKeyOf(), classicInkKeyOf()
				if classicInkLess(, ) {
					, , ,  = , , , 
				}
				if  !=  {
					 := classicInkPair{, }
					[] = classicInkSegment{a: , c: }
					[] = [3]classicInkKey{classicInkKeyOf(), classicInkKeyOf(), classicInkKeyOf()}
				}
			}
		}
		for ,  := range  {
			 := [(+1)%3]
			,  := .V[].Normal, .V[(+1)%3].Normal
			,  := classicInkKeyOf(), classicInkKeyOf()
			if  ==  {
				continue
			}
			if classicInkLess(, ) {
				, , ,  = , , , 
				,  = , 
			}
			 := classicInkPair{, }
			 := []
			if  == nil {
				 = &classicInkEdge{a: , c: }
				[] = 
			}
			// Nonmanifold/overlaid faces are not additional structural edges.
			if len(.faces) < 3 {
				.faces = append(.faces, classicInkFace{, [(+2)%3], , [2]Vec{, }, })
			}
		}
	}
	 := make([]classicInkPair, 0, len())
	for  := range  {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		if [].a != [].a {
			return classicInkLess([].a, [].a)
		}
		return classicInkLess([].c, [].c)
	})
	 := nativeClassicRim()
	 := 0.
	if len() > 0 && [0] > 0 && captionFinite([0]) {
		 = [0]
	}
	 := 0
	var  error
	 := func( []classicInkKey,  float64, ,  Vec) []*classicInkFacet {
		 := make(map[*classicInkFacet]bool)
		var  []*classicInkFacet
		 := func( *classicInkFacet) bool {
			if [] {
				return true
			}
			if len() >= maxClassicInkSupportFaces {
				return false
			}
			[] = true
			 = append(, )
			return true
		}
		for ,  := range  {
			for ,  := range [] {
				if !() {
					return nil
				}
			}
		}
		// A centered stroke can cover several short facets next to a curved
		// silhouette. Endpoint adjacency alone leaves its inner half behind
		// the next facet. Walk only the local, finite, smoothly connected
		// surface within the ribbon's bounds. Sharp creases prevent jumping
		// from a rear wall to a nearer cap; separate copies never share keys.
		if  > 0 {
			,  := (), ()
			,  := min(.X, .X)-2*, min(.Y, .Y)-2*
			,  := max(.X, .X)+2*, max(.Y, .Y)+2*
			for  := 0;  < len(); ++ {
				 := []
				for ,  := range .keys {
					for ,  := range [] {
						++
						if %1024 == 0 {
							if  := .Err();  != nil {
								 = 
								return nil
							}
						}
						if  > maxClassicInkSupportWork {
							 = fmt.Errorf("isometric outline support exceeds %d adjacency checks", maxClassicInkSupportWork)
							return nil
						}
						if [] || ndot(.normal, .normal) < math.Cos(math.Pi/6)-1e-8 {
							continue
						}
						, ,  := .points[0], .points[1], .points[2]
						if max(.X, .X, .X) <  || min(.X, .X, .X) >  || max(.Y, .Y, .Y) <  || min(.Y, .Y, .Y) >  {
							continue
						}
						if !() {
							return nil
						}
					}
				}
			}
		}
		for ,  := range  {
			if . >= -1e-8 && . <= +.00051 {
				if !(.) {
					return nil
				}
			}
		}
		return 
	}
	var  []classicInkSegment
	for ,  := range  {
		if %1024 == 0 {
			if  := .Err();  != nil {
				return nil, 
			}
		}
		 := []
		if len(.faces) > 2 {
			continue
		}
		, ,  := false, false, false
		for ,  := range .faces {
			 := ndot(.normal, )
			,  =  ||  > 1e-7,  ||  < -1e-7
			 =  || .cap
		}
		 := len(.faces) == 1 && math.Abs(.a.Y-.c.Y) < classicInkWeld && .faces[0].third.Y < .a.Y-classicInkWeld
		if ! && ( || ) {
			continue
		}
		 := len(.faces) == 2 && ndot(.faces[0].edgeNormals[0], .faces[1].edgeNormals[0]) > 1-1e-6 && ndot(.faces[0].edgeNormals[1], .faces[1].edgeNormals[1]) > 1-1e-6
		 := len(.faces) == 2 && .faces[0].interpolated && .faces[1].interpolated
		 := (len(.faces) == 1 && ( || )) ||  &&  && !( && )
		if len(.faces) == 2 && ! &&  && ndot(.faces[0].normal, .faces[1].normal) < math.Cos(math.Pi/6)-1e-8 {
			 = true
		}
		if  {
			 := math.Inf(1)
			if  {
				 = .a.Y
			}
			 := ([]classicInkKey{.a, .c}, , .a, .c)
			if  == nil {
				if  != nil {
					return nil, 
				}
				return nil, fmt.Errorf("isometric outline exceeds %d local supporting faces", maxClassicInkSupportFaces)
			}
			 = append(, classicInkSegment{.a, .c, })
			if len() > maxClassicInkSegments {
				return nil, fmt.Errorf("isometric outline exceeds %d segments", maxClassicInkSegments)
			}
		}
	}
	for ,  := range  {
		 := []
		.support = ([:], math.Inf(1), .a, .c)
		if .support == nil {
			if  != nil {
				return nil, 
			}
			return nil, fmt.Errorf("isometric outline exceeds %d local supporting faces", maxClassicInkSupportFaces)
		}
		 = append(, )
	}
	if len() > maxClassicInkSegments {
		return nil, fmt.Errorf("isometric outline exceeds %d segments", maxClassicInkSegments)
	}
	sort.Slice(, func(,  int) bool {
		,  := classicInkKeyOf([].a), classicInkKeyOf([].a)
		if  !=  {
			return classicInkLess(, )
		}
		return classicInkLess(classicInkKeyOf([].c), classicInkKeyOf([].c))
	})
	return , nil
}

// Trace deterministic chains so a dash never restarts at a triangulation
// vertex. Branches are separate paths; ordinary corners share one miter.
func classicInkPaths( []classicInkSegment) [][]classicInkSegment {
	 := make(map[classicInkKey][]int)
	for ,  := range  {
		[classicInkKeyOf(.a)] = append([classicInkKeyOf(.a)], )
		[classicInkKeyOf(.c)] = append([classicInkKeyOf(.c)], )
	}
	 := make([]classicInkKey, 0, len())
	for  := range  {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool { return classicInkLess([], []) })
	 := make([]bool, len())
	var  [][]classicInkSegment
	 := func( classicInkKey,  int) {
		var  []classicInkSegment
		,  := , 
		for ![] {
			[] = true
			 := []
			if classicInkKeyOf(.a) !=  {
				.a, .c = .c, .a
			}
			 = append(, )
			 = classicInkKeyOf(.c)
			if len([]) != 2 {
				break
			}
			 = [][0]
			if [] {
				 = [][1]
			}
		}
		 = append(, )
	}
	for ,  := range  {
		if len([]) == 2 {
			continue
		}
		for ,  := range [] {
			if ![] {
				(, )
			}
		}
	}
	for ,  := range  {
		if ![] {
			(classicInkKeyOf(.a), )
		}
	}
	return 
}

// Invoke after hierarchy transforms. Offsets are measured in the camera
// plane, so relief, orientation and lighting cannot change the stroke weight.
func ( *meshBuilder) ( d2isometric.Node,  []Triangle) {
	if .err != nil {
		return
	}
	// Source captions remain the final triangles of a node. Insert the ink
	// before those decals without changing the physical mesh's order.
	,  := len(.triangles), 0
	for  := len() - 1;  >= 0; -- {
		if  := []; .NoDepthWrite && .Material != nil && .Material.Texture != nil {
			++
		} else {
			break
		}
	}
	defer func() {
		if  == 0 || len(.triangles) ==  {
			return
		}
		 := append([]Triangle(nil), .triangles[-:]...)
		copy(.triangles[-:], .triangles[:])
		copy(.triangles[len(.triangles)-:], )
	}()
	 = nativeClassicNode()
	// A translucent source object is one compositing group. Its existing
	// compensated cap texture retains that coverage; a second physical ink
	// layer would apply the object's opacity twice at the border.
	if .StrokeWidth <= 0 || .Opacity < 1 || strings.EqualFold(.Stroke, "none") {
		return
	}
	 := nativePaint(.Stroke, "#263c4e")
	.A = uint8(math.Round(float64(.A) * min(1, .Opacity)))
	if .A < 255 {
		return
	}
	 := .scale
	if  <= 0 {
		 = .01
		if .Metadata.Original.Width > 0 {
			 = .Size.X / float64(.Metadata.Original.Width)
		}
	}
	 := float64(.StrokeWidth) *  / 2
	,  := classicInkSegments(.ctx, , , )
	if  != nil {
		.err = 
		return
	}
	 := nativeMaterial(.Stroke, 1, 0, .Opacity)
	.Unlit, .svgContour = true, true
	,  := 0., 0.
	if .StrokeDash > 0 {
		,  = svg.GetStrokeDashAttributes(float64(.StrokeWidth), .StrokeDash)
		,  = *, *
		if !captionFinite(, ) ||  <= 0 ||  <= 0 {
			,  = *4, *4
		}
	}
	var  []classicInkSegment
	 := func( []classicInkSegment) {
		if len() == 0 {
			return
		}
		.classicInkRibbon(, , )
		 = append(, [0])
		 := [len()-1]
		 = append(, classicInkSegment{.c, .a, .support})
	}
	 := 0
	for ,  := range classicInkPaths() {
		if  == 0 {
			()
			continue
		}
		 := 0.
		var  []classicInkSegment
		for ,  := range  {
			 := nsub(.c, .a)
			 := nativeCameraAxes().direction
			 := nlen(nsub(, nmul(, ndot(, ))))
			if  < 1e-10 {
				continue
			}
			for  := 0.;  < -1e-10; {
				++
				if  > maxClassicInkSegments {
					.err = fmt.Errorf("isometric dashed outline exceeds %d segments", maxClassicInkSegments)
					return
				}
				 :=  < 
				 :=  +  - 
				if  {
					 =  - 
				}
				 := min(, +)
				if  {
					 = append(, classicInkSegment{nlerp(.a, .c, /), nlerp(.a, .c, /), .support})
				}
				 +=  - 
				 = 
				if  &&  >= -1e-10 {
					()
					 = nil
					 = 
				}
				if  >= +-1e-10 {
					 = 0
				}
			}
		}
		()
	}
	.classicInkJunctions(, , , )
}

func ( *meshBuilder) ( []classicInkSegment,  float64,  *Material) {
	if len() == 0 || .err != nil {
		return
	}
	 := nativeCameraAxes()
	 := .direction
	 := make([]Vec, len())
	for ,  := range  {
		[] = nunit(ncross(, nsub(.c, .a)))
	}
	 := classicInkKeyOf([0].a) == classicInkKeyOf([len()-1].c)
	,  := make([]Vertex, len()+1), make([]Vertex, len()+1)
	for  := range  {
		,  := max(0, -1), min(len()-1, )
		if  && ( == 0 ||  == len()) {
			,  = len()-1, 0
		}
		 := nadd([], [])
		if nlen() < 1e-8 {
			 = []
		} else {
			 = nunit()
		}
		 := nmul(, /max(.5, ndot(, [])))
		 := [min(, len()-1)].a
		if  == len() {
			 = [len()-1].c
		}
		 := func( Vec) Vertex {
			// Keep the exposed half at the edge's height. A camera-facing
			// billboard can otherwise rise above a nearby cap after relief
			// compresses the copies. Moving along the view ray preserves
			// the exact projected width and source edge centerline.
			 := nadd(, )
			if math.Abs(.Y) > 1e-8 {
				 = nsub(, nmul(, .Y/.Y))
			}
			return Vertex{Position: , Normal: }
		}
		[], [] = (), (nmul(, -1))
	}
	for  := range  {
		.classicInkFace([3]Vertex{[], [], [+1]}, [].support, , )
		.classicInkFace([3]Vertex{[], [+1], [+1]}, [].support, , )
	}
}

// Intersect the ribbon with the finite supporting faces, not just at its
// endpoints. The front depth of a curved mesh is piecewise affine; a chord
// between two supported endpoints can still disappear behind an interior
// facet ridge. Each clipped patch follows exactly one actual face plane.
func ( *meshBuilder) ( [3]Vertex,  []*classicInkFacet,  *Material,  rasterCamera) {
	if .err != nil {
		return
	}
	 := func(, ,  Vertex) {
		 := len(.triangles)
		.triangle(, , , , false)
		if len(.triangles) >  {
			.triangles[].DepthBias = classicInkDepthBias
		}
	}
	([0], [1], [2])
	var  [3]Vec
	, , ,  := math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)
	for ,  := range  {
		 := .Position
		[] = nv(ndot(, .right), ndot(, .up), ndot(, .direction))
		, , ,  = min(, [].X), min(, [].Y), max(, [].X), max(, [].Y)
	}
	 := func( []Vec,  func(Vec) float64) []Vec {
		if len() == 0 {
			return nil
		}
		var  []Vec
		 := [len()-1]
		 := ()
		for ,  := range  {
			 := ()
			if ( >= 0) != ( >= 0) {
				 = append(, nlerp(, , /(-)))
			}
			if  >= 0 {
				 = append(, )
			}
			,  = , 
		}
		return 
	}
	for ,  := range  {
		, ,  := .points[0], .points[1], .points[2]
		if max(.X, .X, .X) <  || min(.X, .X, .X) >  || max(.Y, .Y, .Y) <  || min(.Y, .Y, .Y) >  {
			continue
		}
		 := float64((.X-.X)*(.Y-.Y)) - float64((.Y-.Y)*(.X-.X))
		if math.Abs() < 1e-14 {
			continue
		}
		 := 1.
		if  < 0 {
			 = -1
		}
		 := append([]Vec(nil), [:]...)
		for ,  := range .points {
			 := .points[(+1)%3]
			 = (, func( Vec) float64 {
				return  * (float64((.X-.X)*(.Y-.Y)) - float64((.Y-.Y)*(.X-.X)))
			})
		}
		// A supported patch replaces only the part that the physical face
		// occludes. The original ribbon owns its exposed outer half.
		 = (, func( Vec) float64 { ,  := .depthAt(); return  - .Z - classicInkDepthBias })
		if len() < 3 {
			continue
		}
		 := func( Vec) Vertex {
			,  := .depthAt()
			return Vertex{Position: nadd(nadd(nmul(.right, .X), nmul(.up, .Y)), nmul(.direction, )), Normal: .direction}
		}
		// A clip touching an existing vertex can return the same polygon
		// starting at either neighboring edge after floating-point rounding.
		// A stable fan anchor keeps that equivalent loop from changing mesh
		// diagonals (and later visibility splits) across FMA/non-FMA CPUs.
		 := classicInkFanAnchor()
		 := ([])
		for  := 1;  < len()-1; ++ {
			(, ([(+)%len()]), ([(++1)%len()]))
		}
	}
}

func classicInkFanAnchor( []Vec) int {
	// These keys are far finer than physical ink. They only
	// choose a starting vertex; the emitted geometry retains its exact position.
	 := func( Vec) classicInkKey {
		const  = 1 << 32
		return classicInkKey{int64(math.Round(.X * )), int64(math.Round(.Y * )), int64(math.Round(.Z * ))}
	}
	 := 0
	for  := 1;  < len(); ++ {
		if classicInkLess(([]), ([])) {
			 = 
		}
	}
	return 
}