package d2scene

import 

// AspectRatioMatrix maps source coordinates into destination using SVG's
// preserveAspectRatio rules. Source must have positive dimensions;
// destination may be empty so zero-sized image primitives remain valid and
// paint no pixels.
func (,  Box,  AspectRatio) (Matrix, error) {
	if  := validateBox();  != nil || .Width == 0 || .Height == 0 {
		return Matrix{}, fmt.Errorf("d2scene: aspect-ratio source box must have finite positive dimensions")
	}
	if  := validateBox();  != nil {
		return Matrix{}, fmt.Errorf("d2scene: aspect-ratio destination box must have finite non-negative dimensions")
	}
	if .Align > AlignXMaxYMax || .Fit > AspectSlice {
		return Matrix{}, fmt.Errorf("d2scene: invalid aspect-ratio policy align=%d fit=%d", .Align, .Fit)
	}

	 := .Width / .Width
	 := .Height / .Height
	if !finite() || !finite() {
		return Matrix{}, fmt.Errorf("d2scene: aspect-ratio scale is non-finite")
	}
	,  := .X, .Y
	if .Align != AlignNone {
		 := 
		if  <  {
			 = 
		}
		if .Fit == AspectSlice &&  >  {
			 = 
		} else if .Fit == AspectSlice {
			 = 
		}
		,  = , 
		,  := aspectAlignmentFractions(.Align)
		// Viewport fitting commonly subtracts nearly equal dimensions. Round
		// each product explicitly so fused multiply-adds cannot introduce
		// CPU-dependent residuals into retained SVG transforms. This fixes the
		// arithmetic without quantizing authored nested transforms, whose tiny
		// coefficients can become significant under an enclosing scale.
		 := .Width - float64(.Width*)
		 := .Height - float64(.Height*)
		 = float64(.X + float64(*))
		 = float64(.Y + float64(*))
	}
	 := Matrix{
		A: ,
		D: ,
		E:  - float64(.X*),
		F:  - float64(.Y*),
	}
	if !.IsFinite() {
		return Matrix{}, fmt.Errorf("d2scene: aspect-ratio transform is non-finite")
	}
	return , nil
}

func aspectAlignmentFractions( AspectAlign) (float64, float64) {
	switch  {
	case AlignXMinYMin:
		return 0, 0
	case AlignXMidYMin:
		return .5, 0
	case AlignXMaxYMin:
		return 1, 0
	case AlignXMinYMid:
		return 0, .5
	case AlignXMidYMid:
		return .5, .5
	case AlignXMaxYMid:
		return 1, .5
	case AlignXMinYMax:
		return 0, 1
	case AlignXMidYMax:
		return .5, 1
	case AlignXMaxYMax:
		return 1, 1
	default:
		return 0, 0
	}
}