private void PackUVRects(ref Dictionary <int, Rect> a_rRectsToPackDict, out Dictionary <int, PackedRect> a_rPackedUVRectsDict, out int a_iAtlasWidth, out int a_iAtlasHeight)
    {
        bool bAllPacked;
        int  iMaximumAtlasSize = (int)m_eMaximumAtlasSize;

        // Estimate atlas size for this loop
        Uni2DEditorTextureAtlasPacker.EstimateMinAtlasSize(a_rRectsToPackDict.Values, iMaximumAtlasSize, m_iPadding, out a_iAtlasWidth, out a_iAtlasHeight);
        Dictionary <int, Rect>        oRectsToPackCopyDict = null;
        Uni2DEditorTextureAtlasPacker oPacker = new Uni2DEditorTextureAtlasPacker(a_iAtlasWidth, a_iAtlasHeight, m_iPadding, true);

        // Looking for best atlas size
        // Save infos about the best size we'll found
        Vector2 f2BestSize = new Vector2(a_iAtlasWidth, a_iAtlasHeight);
        Dictionary <int, PackedRect> rBestResultsOutput = null;
        Dictionary <int, Rect>       rBestResultsInput  = null;
        float fBestOccupancy = float.NegativeInfinity;

        // Breadth-first search data
        Queue <Vector2> oNextSizesToTest = new Queue <Vector2>( );

        oNextSizesToTest.Enqueue(f2BestSize);

        while (oNextSizesToTest.Count != 0)
        {
            Vector2 f2AtlasSize = oNextSizesToTest.Dequeue( );

            // Create a working copy of the input dictionary
            oRectsToPackCopyDict = new Dictionary <int, Rect>(a_rRectsToPackDict);

            // Reinit bin packer
            oPacker.Init((int)f2AtlasSize.x, (int)f2AtlasSize.y, m_iPadding, true);

            // Pack the most as possible with BSSF heuristic
            bAllPacked = oPacker.Insert(oRectsToPackCopyDict, Uni2DEditorTextureAtlasPacker.FreeRectChoiceHeuristic.RectBestShortSideFit);

            if (bAllPacked)
            {
                float fOccupancy = oPacker.Occupancy( );

                // if better occupancy than current best or same occupancy and smaller surface area...
                if (fOccupancy > fBestOccupancy || (fOccupancy == fBestOccupancy && f2AtlasSize.sqrMagnitude < f2BestSize.sqrMagnitude))
                {
                    // New best
                    f2BestSize         = f2AtlasSize;
                    fBestOccupancy     = fOccupancy;
                    rBestResultsOutput = oPacker.UsedRectangles;
                    rBestResultsInput  = oRectsToPackCopyDict;
                }
            }

            // Is tested atlas size is max?
            if (f2AtlasSize.x != iMaximumAtlasSize || f2AtlasSize.y != iMaximumAtlasSize)
            {
                // Add 2 sizes to test: doubled width & doubled height
                Vector2 f2AtlasDoubleWidthSize  = new Vector2(Mathf.Min(f2AtlasSize.x * 2, iMaximumAtlasSize), f2AtlasSize.y);
                Vector2 f2AtlasDoubleHeightSize = new Vector2(f2AtlasSize.x, Mathf.Min(f2AtlasSize.y * 2, iMaximumAtlasSize));

                // Ensure we don't enqueue these sizes twice
                if (f2AtlasSize != f2AtlasDoubleWidthSize && oNextSizesToTest.Contains(f2AtlasDoubleWidthSize) == false)
                {
                    oNextSizesToTest.Enqueue(f2AtlasDoubleWidthSize);
                }

                // If sizes equal, it's a square => don't test doubled height or doubled width.
                // These cases are equal (double width square = flipped double height square)
                if (f2AtlasSize.x != f2AtlasSize.y && f2AtlasSize != f2AtlasDoubleHeightSize && oNextSizesToTest.Contains(f2AtlasDoubleHeightSize) == false)
                {
                    oNextSizesToTest.Enqueue(f2AtlasDoubleHeightSize);
                }
            }
            else if (rBestResultsOutput == null)                // Max square, no best results so far
            {
                // Set it as default best
                f2BestSize = f2AtlasSize;
                //fBestOccupancy = oPacker.Occupancy( );	// Won't be used
                rBestResultsOutput = oPacker.UsedRectangles;
                rBestResultsInput  = oRectsToPackCopyDict;
            }

            oRectsToPackCopyDict = null;
        }

        a_rRectsToPackDict   = rBestResultsInput;
        a_rPackedUVRectsDict = rBestResultsOutput;

        a_iAtlasWidth  = (int)f2BestSize.x;
        a_iAtlasHeight = (int)f2BestSize.y;

        oPacker = null;
    }
	private void PackUVRects( ref Dictionary<int, Rect> a_rRectsToPackDict, out Dictionary<int, PackedRect> a_rPackedUVRectsDict, out int a_iAtlasWidth, out int a_iAtlasHeight )
	{
		bool bAllPacked;
		int iMaximumAtlasSize = (int) m_eMaximumAtlasSize;

		// Estimate atlas size for this loop
		Uni2DEditorTextureAtlasPacker.EstimateMinAtlasSize( a_rRectsToPackDict.Values, iMaximumAtlasSize, m_iPadding, out a_iAtlasWidth, out a_iAtlasHeight );
		Dictionary<int, Rect> oRectsToPackCopyDict = null;
		Uni2DEditorTextureAtlasPacker oPacker = new Uni2DEditorTextureAtlasPacker( a_iAtlasWidth, a_iAtlasHeight, m_iPadding, true );

		// Looking for best atlas size
		// Save infos about the best size we'll found
		Vector2 f2BestSize = new Vector2( a_iAtlasWidth, a_iAtlasHeight );
		Dictionary<int, PackedRect> rBestResultsOutput = null;
		Dictionary<int, Rect> rBestResultsInput = null;
		float fBestOccupancy = float.NegativeInfinity;

		// Breadth-first search data
		Queue<Vector2> oNextSizesToTest = new Queue<Vector2>( );
		oNextSizesToTest.Enqueue( f2BestSize );

		while( oNextSizesToTest.Count != 0 )
		{
			Vector2 f2AtlasSize = oNextSizesToTest.Dequeue( );

			// Create a working copy of the input dictionary
			oRectsToPackCopyDict = new Dictionary<int, Rect>( a_rRectsToPackDict );

			// Reinit bin packer
			oPacker.Init( (int) f2AtlasSize.x, (int) f2AtlasSize.y, m_iPadding, true );

			// Pack the most as possible with BSSF heuristic
			bAllPacked = oPacker.Insert( oRectsToPackCopyDict, Uni2DEditorTextureAtlasPacker.FreeRectChoiceHeuristic.RectBestShortSideFit );

			if( bAllPacked )
			{
				float fOccupancy = oPacker.Occupancy( );

				// if better occupancy than current best or same occupancy and smaller surface area...
				if( fOccupancy > fBestOccupancy || ( fOccupancy == fBestOccupancy && f2AtlasSize.sqrMagnitude < f2BestSize.sqrMagnitude ) )
				{
					// New best
					f2BestSize         = f2AtlasSize;
					fBestOccupancy     = fOccupancy;
					rBestResultsOutput = oPacker.UsedRectangles;
					rBestResultsInput  = oRectsToPackCopyDict;
				}
			}

			// Is tested atlas size is max?
			if( f2AtlasSize.x != iMaximumAtlasSize || f2AtlasSize.y != iMaximumAtlasSize )
			{
				// Add 2 sizes to test: doubled width & doubled height
				Vector2 f2AtlasDoubleWidthSize  = new Vector2( Mathf.Min( f2AtlasSize.x * 2, iMaximumAtlasSize ), f2AtlasSize.y );
				Vector2 f2AtlasDoubleHeightSize = new Vector2( f2AtlasSize.x, Mathf.Min( f2AtlasSize.y * 2, iMaximumAtlasSize ) );

				// Ensure we don't enqueue these sizes twice
				if( f2AtlasSize != f2AtlasDoubleWidthSize && oNextSizesToTest.Contains( f2AtlasDoubleWidthSize ) == false )
				{
					oNextSizesToTest.Enqueue( f2AtlasDoubleWidthSize );
				}
				
				// If sizes equal, it's a square => don't test doubled height or doubled width.
				// These cases are equal (double width square = flipped double height square)
				if( f2AtlasSize.x != f2AtlasSize.y && f2AtlasSize != f2AtlasDoubleHeightSize && oNextSizesToTest.Contains( f2AtlasDoubleHeightSize ) == false )
				{
					oNextSizesToTest.Enqueue( f2AtlasDoubleHeightSize );
				}
			}
			else if( rBestResultsOutput == null )	// Max square, no best results so far
			{
				// Set it as default best
				f2BestSize         = f2AtlasSize;
				//fBestOccupancy = oPacker.Occupancy( );	// Won't be used
				rBestResultsOutput = oPacker.UsedRectangles;
				rBestResultsInput  = oRectsToPackCopyDict;
			}

			oRectsToPackCopyDict = null;
		}

		a_rRectsToPackDict   = rBestResultsInput;
		a_rPackedUVRectsDict = rBestResultsOutput;

		a_iAtlasWidth  = (int) f2BestSize.x;
		a_iAtlasHeight = (int) f2BestSize.y;

		oPacker = null;
	}