public Color readTexture(float u, float v, AddressType addressType, bool flipU = false, bool flipV = false)
        {
            BilinearCoordinates coordinates = calculateBilinearCoordinates(u, v, flipU, flipV);

            coordinates = applyAddressTypeOnBilinearCoordinates(coordinates, addressType);

            return(bilinearInterpolation(
                       readBilinearCoordinateColor(coordinates, BilinearCoordinatesType.UminVmin),
                       readBilinearCoordinateColor(coordinates, BilinearCoordinatesType.UmaxVmin),
                       readBilinearCoordinateColor(coordinates, BilinearCoordinatesType.UminVmax),
                       readBilinearCoordinateColor(coordinates, BilinearCoordinatesType.UmaxVmax),
                       coordinates.ucoef,
                       coordinates.vcoef
                       ));
        }
        public Color readBilinearCoordinateColor(BilinearCoordinates coordinates, BilinearCoordinatesType coordinateType)
        {
            switch (coordinateType)
            {
            default:
            case BilinearCoordinatesType.UminVmin:
                return(_textureArray[coordinates.umin + _width * coordinates.vmin]);

            case BilinearCoordinatesType.UmaxVmin:
                return(_textureArray[coordinates.umax + _width * coordinates.vmin]);

            case BilinearCoordinatesType.UminVmax:
                return(_textureArray[coordinates.umin + _width * coordinates.vmax]);

            case BilinearCoordinatesType.UmaxVmax:
                return(_textureArray[coordinates.umax + _width * coordinates.vmax]);
            }
        }
        public BilinearCoordinates calculateBilinearCoordinates(float u, float v, bool flipU = false, bool flipV = false)
        {
            u = Math.Abs(u);
            v = Math.Abs(v);
            u = flipU ? 1 - u : u;
            v = flipV ? 1 - v : v;

            BilinearCoordinates coordinates = new BilinearCoordinates();

            coordinates.umin  = (int)(_width * u);
            coordinates.vmin  = (int)(_height * v);
            coordinates.umax  = (int)(_width * u) + 1;  //this could end up on a different texture, when using multiple textures
            coordinates.vmax  = (int)(_height * v) + 1; //this could end up on a different texture. Three textures is max what should be blended from, if wrapping is to be supported for multiple textures
            coordinates.ucoef = Math.Abs(_width * u - coordinates.umin);
            coordinates.vcoef = Math.Abs(_height * v - coordinates.vmin);

            return(coordinates);
        }
        public BilinearCoordinates applyAddressTypeOnBilinearCoordinates(BilinearCoordinates coordinates, AddressType addressType)
        {
            // The texture is being addressed on [0,1]
            // There should be an addressing type in order to
            // determine how we should access texels when
            // the coordinates are beyond those boundaries.
            // Clamping is done by bringing anything below zero to the coordinate zero and everything beyond one, to one.
            // WrapAround is using modulus to wrap the coordinates off the end to the other side of the texture
            switch (addressType)
            {
            case AddressType.WrapV_ClampU:
                coordinates.vmin = mod(coordinates.vmin, _width);
                coordinates.vmax = mod(coordinates.vmax, _width);
                coordinates.umin = Math.Min(Math.Max(coordinates.umin, 0), _height - 1);
                coordinates.umax = Math.Min(Math.Max(coordinates.umax, 0), _height - 1);
                break;

            case AddressType.WrapU_ClampV:
                coordinates.umin = mod(coordinates.umin, _width);
                coordinates.umax = mod(coordinates.umax, _width);
                coordinates.vmin = Math.Min(Math.Max(coordinates.vmin, 0), _height - 1);
                coordinates.vmax = Math.Min(Math.Max(coordinates.vmax, 0), _height - 1);
                break;

            case AddressType.WrapAround:
                coordinates.umin = mod(coordinates.umin, _width);
                coordinates.umax = mod(coordinates.umax, _width);
                coordinates.vmin = mod(coordinates.vmin, _height);
                coordinates.vmax = mod(coordinates.vmax, _height);
                break;

            case AddressType.Clamping:
            default:
                coordinates.umin = Math.Min(Math.Max(coordinates.umin, 0), _width - 1);
                coordinates.umax = Math.Min(Math.Max(coordinates.umax, 0), _width - 1);
                coordinates.vmin = Math.Min(Math.Max(coordinates.vmin, 0), _height - 1);
                coordinates.vmax = Math.Min(Math.Max(coordinates.vmax, 0), _height - 1);
                break;
            }
            return(coordinates);
        }