/// <summary>
        /// Calculates a width for the packed texture.
        /// </summary>
        /// <param name="sprites">Sprites to pack.</param>
        /// <param name="isPowerOfTwo">Indicates whether the width must be a power of two.</param>
        /// <returns>Width of the packed texture.</returns>
        static int CalculateWidth(Sprite[] sprites, bool isPowerOfTwo)
        {
            int[] spriteWidths = sprites.Select(sprite => sprite.Width).OrderBy(w => w).ToArray();
            int   maxWidth     = spriteWidths[spriteWidths.Length - 1];
            int   medianWidth  = spriteWidths[spriteWidths.Length / 2];

            int width = medianWidth * (int)Math.Round(Math.Sqrt(sprites.Length));

            // Ensure the width calculated is not less than the widest sprite. If it is, we use the widest sprite width instead.
            //
            width = Math.Max(width, maxWidth);

            if (isPowerOfTwo)
            {
                width = SpritePacker.CalculateNextPowerOfTwo(width);
            }

            return(width);
        }
        static void FindSpritePosition(Sprite[] sprites, int index, int textureWidth, out int x, out int y)
        {
            x = 0;
            y = 0;

            while (true)
            {
                if (!SpritePacker.IsIntersectingPreviousSprite(sprites, index, x, y, out int intersectionIndex))
                {
                    return;
                }

                // Skip to the right of the previously positioned sprite that we collided with. Howver, move down when we
                // can no longer fit on the current line.
                //
                x = sprites[intersectionIndex].X + sprites[intersectionIndex].Width;

                if (x + sprites[index].Width > textureWidth)
                {
                    x = 0;
                    y++;
                }
            }
        }
        /// <summary>
        /// Packs many individual sprites into a single sprite and records the individual sprite names and locations inside the large sprite.
        /// </summary>
        /// <param name="configuration">Settings controlling how to pack sprites.</param>
        /// <param name="spritePaths">File paths of sprites to pack.</param>
        /// <returns>Object containing the packed texture along with the names and locations of the individual sprites inside of it.</returns>
        public static PackedTexture Pack(PackConfiguration configuration, string[] spritePaths)
        {
            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }

            if (spritePaths == null)
            {
                throw new ArgumentNullException(nameof(spritePaths));
            }

            if (spritePaths.Length <= 0)
            {
                throw new ArgumentException("Must contain at least one element.", nameof(spritePaths));
            }

            Sprite[] sprites       = SpritePacker.LoadSprites(spritePaths.Distinct().ToArray(), configuration.Padding);
            int      textureWidth  = SpritePacker.CalculateWidth(sprites, configuration.IsPowerOfTwo);
            int      textureHeight = 0;

            // Find the position of each sprite in the final texture.
            //
            Sprite[] orderedSprites = SpritePacker.OrderSprites(sprites);

            for (int i = 0; i < orderedSprites.Length; i++)
            {
                SpritePacker.FindSpritePosition(orderedSprites, i, textureWidth, out int x, out int y);

                orderedSprites[i].X = x;
                orderedSprites[i].Y = y;

                // Update texture height when the just positioned sprite pushes the boundary out.
                //
                textureHeight = Math.Max(textureHeight, orderedSprites[i].Y + orderedSprites[i].Height);
            }

            // Ensure height dimension is a power of two when the user asked for as much.
            //
            if (configuration.IsPowerOfTwo)
            {
                textureHeight = SpritePacker.CalculateNextPowerOfTwo(textureHeight);
            }

            // Ensure both dimensions are square when user asked for as much.
            //
            if (configuration.IsSquare)
            {
                int maximum = Math.Max(textureWidth, textureHeight);

                textureWidth  = maximum;
                textureHeight = maximum;
            }

            // Ensure the final dimensions are within the user's limits.
            //
            if (textureWidth > configuration.MaximumWidth || textureHeight > configuration.MaximumHeight)
            {
                throw new Exception($"Packed texture dimensions ({textureWidth} x {textureHeight}) exceed the maximum requested ({configuration.MaximumWidth} x {configuration.MaximumHeight}).");
            }

            return(new PackedTexture
            {
                SpriteNamesToIndexMapping = SpritePacker.BuildNameToIndexMap(sprites),
                SpriteRectangles = SpritePacker.BuildRectangles(sprites, configuration.Padding),
                Texture = SpritePacker.BuildTexture(sprites, textureWidth, textureHeight, configuration.Padding),
            });
        }