• [Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题


    [Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题

    http://www.cppblog.com/shly/archive/2015/12/09/212443.html

    [Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题

    UGUI Image已Sprite为主,而简单的合并图集可以使用自带的SpritePacker。
    而当在使用AssetBundle的时候情况有些不同,会造成每个AB都会引入完整的Sprite图集,显然就增加了AB的大小,重复资源。
    为了解决这个问题我们可以手动合并图集,那么方案也有多种,这里我们可以编辑器自带的API来实现一个小的图集合并工具。
     private static bool CombineSpritesHelper(string path, string dpath, string name, int padding)
        {
            string[] paths = AssetDatabase.FindAssets("t:sprite", new string[] { path });
            List<Sprite> spriteList = new List<Sprite>();
            List<Texture2D> texList = new List<Texture2D>();

            foreach (var o in paths)
            {
                Sprite s = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(o), typeof(Sprite)) as Sprite;
                if (null != s)
                {
                    spriteList.Add(s);
                    texList.Add(s.texture);
                }
            }

            if (texList.Count > 0)
            {
                Texture2D tex = new Texture2D(1024, 1024, TextureFormat.ARGB32, true);
                Rect[] uvs = UITexturePacker.PackTextures(tex, texList.ToArray(), 4, 4, padding, 1024);
                if (null == uvs)
                {
                    EditorUtility.DisplayDialog(path, "图集超过1024,需要分组成多张图集", "点击退出");
                    Object.DestroyImmediate(tex);
                    tex = null;
                    return false;
                }
                else
                {
                    List<SpriteMetaData> metaList = new List<SpriteMetaData>();
                    for (int i = 0; i < uvs.Length; ++i)
                    {
                        SpriteMetaData data = new SpriteMetaData();
                        data.alignment = (int)SpriteAlignment.Center;
                        data.border = spriteList[i].border;
                        data.name = spriteList[i].name;
                        data.pivot = spriteList[i].pivot;
                        data.rect = new Rect(uvs[i].x * tex.width, uvs[i].y * tex.height, uvs[i].width * tex.width, uvs[i].height * tex.height);
                        metaList.Add(data);
                    }

                    //string dpath = path.Substring(0, path.Length - obj.name.Length) + "SpriteSet";
                    if (!System.IO.Directory.Exists(dpath))
                    {
                        System.IO.Directory.CreateDirectory(dpath);
                    }

                    string file = dpath + "/" + name + ".png";
                    if (System.IO.File.Exists(file))
                    {
                        System.IO.File.Delete(file);
                    }
                    System.IO.File.WriteAllBytes(file, tex.EncodeToPNG());

                    AssetDatabase.ImportAsset(file, ImportAssetOptions.ForceUpdate);
                    TextureImporter importer = AssetImporter.GetAtPath(file) as TextureImporter;
                    importer.spritesheet = metaList.ToArray();
                    importer.spriteImportMode = SpriteImportMode.Multiple;
                    importer.textureType = TextureImporterType.Sprite;
                    importer.textureFormat = TextureImporterFormat.ARGB32;
                    importer.mipmapEnabled = true;
                    importer.mipmapFilter = TextureImporterMipFilter.BoxFilter;
                    importer.assetBundleName = "ui_image/" + name.ToLower();
                    AssetDatabase.ImportAsset(file);
                    AssetDatabase.Refresh();
                }
            }
            return true;
        }

        [MenuItem("Tool/Combine Sprites")]
        [MenuItem("Assets/Tool/Combine Sprites")]
        public static void CombineSprites()
        {
            EditorUtility.DisplayProgressBar("Combine Sprites", "Initializing ", 0);
            try
            {
                Object obj = Selection.activeObject;
                string path = AssetDatabase.GetAssetPath(obj.GetInstanceID());
                string dpath = path.Substring(0, path.Length - obj.name.Length) + "SpriteSet";

                if (System.IO.Directory.Exists(path))
                {
                    string[] directories = System.IO.Directory.GetDirectories(path);
                    int count = 0;
                    if (directories.Length > 0)
                    {
                        foreach (var directory in directories)
                        {
                            count++;
                            EditorUtility.DisplayProgressBar("Combine Sprites", string.Format("combing {0}", count), (float)(count) / (directories.Length));
                            if (!CombineSpritesHelper(directory, dpath, string.Concat(obj.name, "_", count.ToString()), 1))
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        EditorUtility.DisplayProgressBar("Combine Sprites", "combing 0", 1);
                        CombineSpritesHelper(path, dpath, obj.name, 1);
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e);
            }
            EditorUtility.ClearProgressBar();
        }

    使用方法很简单,可以修改源码中的路径,可以将多个Single Sprite合成一个Multi Sprite。然后在供,UGUI使用,注意最好保存好合并前的源文件,不然可能造成新增图片打图集后对应不上从而造成引用丢失。

    补充UITextPacker.cs :引用自NGUI
    /*
        Based on the Public Domain MaxRectsBinPack.cpp source by Jukka Jylänki
        https://github.com/juj/RectangleBinPack/

        Ported to C# by Sven Magnus
        This version is also public domain - do whatever you want with it.
    */
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;

    public class UITexturePacker
    {
        public int binWidth = 0;
        public int binHeight = 0;
        public bool allowRotations;

        public List<Rect> usedRectangles = new List<Rect>();
        public List<Rect> freeRectangles = new List<Rect>();

        public enum FreeRectChoiceHeuristic
        {
            RectBestShortSideFit, ///< -BSSF: Positions the rectangle against the short side of a free rectangle into which it fits the best.
            RectBestLongSideFit, ///< -BLSF: Positions the rectangle against the long side of a free rectangle into which it fits the best.
            RectBestAreaFit, ///< -BAF: Positions the rectangle into the smallest free rect into which it fits.
            RectBottomLeftRule, ///< -BL: Does the Tetris placement.
            RectContactPointRule ///< -CP: Choosest the placement where the rectangle touches other rects as much as possible.
        };

        public UITexturePacker (int width, int height, bool rotations)
        {
            Init(width, height, rotations);
        }

        public void Init (int width, int height, bool rotations)
        {
            binWidth = width;
            binHeight = height;
            allowRotations = rotations;

            Rect n = new Rect();
            n.x = 0;
            n.y = 0;
            n.width = width;
            n.height = height;

            usedRectangles.Clear();

            freeRectangles.Clear();
            freeRectangles.Add(n);
        }

        private struct Storage
        {
            public Rect rect;
            public bool paddingX;
            public bool paddingY;
        }

        public static Rect[] PackTextures (Texture2D texture, Texture2D[] textures, int width, int height, int padding, int maxSize)
        {
            if (width > maxSize || height > maxSize) return null;
            if (width > maxSize || height > maxSize) { int temp = width; width = height; height = temp; }
            
            // Force square by sizing up
            /*
            if (NGUISettings.forceSquareAtlas)
            {
                if (width > height)
                    height = width;
                else if (height > width)
                    width = height;
            }
            */
            UITexturePacker bp = new UITexturePacker(width, height, false);
            Storage[] storage = new Storage[textures.Length];

            for (int i = 0; i < textures.Length; i++)
            {
                Texture2D tex = textures[i];
                if (!tex) continue;

                Rect rect = new Rect();

                int xPadding = 1;
                int yPadding = 1;

                for (xPadding = 1; xPadding >= 0; --xPadding)
                {
                    for (yPadding = 1; yPadding >= 0; --yPadding)
                    {
                        rect = bp.Insert(tex.width + (xPadding * padding), tex.height + (yPadding * padding),
                            UITexturePacker.FreeRectChoiceHeuristic.RectBestAreaFit);
                        if (rect.width != 0 && rect.height != 0) break;

                        // After having no padding if it still doesn't fit -- increase texture size.
                        else if (xPadding == 0 && yPadding == 0)
                        {
                            return PackTextures(texture, textures, width * (width <= height ? 2 : 1),
                                height * (height < width ? 2 : 1), padding, maxSize);
                        }
                    }
                    if (rect.width != 0 && rect.height != 0) break;
                }

                storage[i] = new Storage();
                storage[i].rect = rect;
                storage[i].paddingX = (xPadding != 0);
                storage[i].paddingY = (yPadding != 0);
            }

            texture.Resize(width, height);
            texture.SetPixels(new Color[width * height]);

            // The returned rects
            Rect[] rects = new Rect[textures.Length];

            for (int i = 0; i < textures.Length; i++)
            {
                Texture2D tex = textures[i];
                if (!tex) continue;

                Rect rect = storage[i].rect;
                int xPadding = (storage[i].paddingX ? padding : 0);
                int yPadding = (storage[i].paddingY ? padding : 0);
                Color[] colors = tex.GetPixels();

                // Would be used to rotate the texture if need be.
                if (rect.width != tex.width + xPadding)
                {
                    Color[] newColors = tex.GetPixels();

                    for (int x = 0; x < rect.width; x++)
                    {
                        for (int y = 0; y < rect.height; y++)
                        {
                            int prevIndex = ((int)rect.height - (y + 1)) + x * (int)tex.width;
                            newColors[x + y * (int)rect.width] = colors[prevIndex];
                        }
                    }

                    colors = newColors;
                }

                texture.SetPixels((int)rect.x, (int)rect.y, (int)rect.width - xPadding, (int)rect.height - yPadding, colors);
                rect.x /= width;
                rect.y /= height;
                rect.width = (rect.width - xPadding) / width;
                rect.height = (rect.height - yPadding) / height;
                rects[i] = rect;
            }
            texture.Apply();
            return rects;
        }

        public Rect Insert (int width, int height, FreeRectChoiceHeuristic method)
        {
            Rect newNode = new Rect();
            int score1 = 0; // Unused in this function. We don't need to know the score after finding the position.
            int score2 = 0;
            switch (method)
            {
                case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break;
                case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break;
                case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); break;
                case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break;
                case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break;
            }

            if (newNode.height == 0)
                return newNode;

            int numRectanglesToProcess = freeRectangles.Count;
            for (int i = 0; i < numRectanglesToProcess; ++i)
            {
                if (SplitFreeNode(freeRectangles[i], ref newNode))
                {
                    freeRectangles.RemoveAt(i);
                    --i;
                    --numRectanglesToProcess;
                }
            }

            PruneFreeList();

            usedRectangles.Add(newNode);
            return newNode;
        }

        public void Insert (List<Rect> rects, List<Rect> dst, FreeRectChoiceHeuristic method)
        {
            dst.Clear();

            while (rects.Count > 0)
            {
                int bestScore1 = int.MaxValue;
                int bestScore2 = int.MaxValue;
                int bestRectIndex = -1;
                Rect bestNode = new Rect();

                for (int i = 0; i < rects.Count; ++i)
                {
                    int score1 = 0;
                    int score2 = 0;
                    Rect newNode = ScoreRect((int)rects[i].width, (int)rects[i].height, method, ref score1, ref score2);

                    if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2))
                    {
                        bestScore1 = score1;
                        bestScore2 = score2;
                        bestNode = newNode;
                        bestRectIndex = i;
                    }
                }

                if (bestRectIndex == -1)
                    return;

                PlaceRect(bestNode);
                rects.RemoveAt(bestRectIndex);
            }
        }

        void PlaceRect (Rect node)
        {
            int numRectanglesToProcess = freeRectangles.Count;
            for (int i = 0; i < numRectanglesToProcess; ++i)
            {
                if (SplitFreeNode(freeRectangles[i], ref node))
                {
                    freeRectangles.RemoveAt(i);
                    --i;
                    --numRectanglesToProcess;
                }
            }

            PruneFreeList();

            usedRectangles.Add(node);
        }

        Rect ScoreRect (int width, int height, FreeRectChoiceHeuristic method, ref int score1, ref int score2)
        {
            Rect newNode = new Rect();
            score1 = int.MaxValue;
            score2 = int.MaxValue;
            switch (method)
            {
                case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break;
                case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break;
                case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1);
                score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better.
                break;
                case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break;
                case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break;
            }

            // Cannot fit the current rectangle.
            if (newNode.height == 0)
            {
                score1 = int.MaxValue;
                score2 = int.MaxValue;
            }

            return newNode;
        }

        /// Computes the ratio of used surface area.
        public float Occupancy ()
        {
            ulong usedSurfaceArea = 0;
            for (int i = 0; i < usedRectangles.Count; ++i)
                usedSurfaceArea += (uint)usedRectangles[i].width * (uint)usedRectangles[i].height;

            return (float)usedSurfaceArea / (binWidth * binHeight);
        }

        Rect FindPositionForNewNodeBottomLeft (int width, int height, ref int bestY, ref int bestX)
        {
            Rect bestNode = new Rect();
            //memset(bestNode, 0, sizeof(Rect));

            bestY = int.MaxValue;

            for (int i = 0; i < freeRectangles.Count; ++i)
            {
                // Try to place the rectangle in upright (non-flipped) orientation.
                if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
                {
                    int topSideY = (int)freeRectangles[i].y + height;
                    if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = width;
                        bestNode.height = height;
                        bestY = topSideY;
                        bestX = (int)freeRectangles[i].x;
                    }
                }
                if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
                {
                    int topSideY = (int)freeRectangles[i].y + width;
                    if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = height;
                        bestNode.height = width;
                        bestY = topSideY;
                        bestX = (int)freeRectangles[i].x;
                    }
                }
            }
            return bestNode;
        }

        Rect FindPositionForNewNodeBestShortSideFit (int width, int height, ref int bestShortSideFit, ref int bestLongSideFit)
        {
            Rect bestNode = new Rect();
            //memset(&bestNode, 0, sizeof(Rect));

            bestShortSideFit = int.MaxValue;

            for (int i = 0; i < freeRectangles.Count; ++i)
            {
                // Try to place the rectangle in upright (non-flipped) orientation.
                if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
                {
                    int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);
                    int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);
                    int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);
                    int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert);

                    if (shortSideFit < bestShortSideFit || (shortSideFit == bestShortSideFit && longSideFit < bestLongSideFit))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = width;
                        bestNode.height = height;
                        bestShortSideFit = shortSideFit;
                        bestLongSideFit = longSideFit;
                    }
                }

                if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
                {
                    int flippedLeftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);
                    int flippedLeftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);
                    int flippedShortSideFit = Mathf.Min(flippedLeftoverHoriz, flippedLeftoverVert);
                    int flippedLongSideFit = Mathf.Max(flippedLeftoverHoriz, flippedLeftoverVert);

                    if (flippedShortSideFit < bestShortSideFit || (flippedShortSideFit == bestShortSideFit && flippedLongSideFit < bestLongSideFit))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = height;
                        bestNode.height = width;
                        bestShortSideFit = flippedShortSideFit;
                        bestLongSideFit = flippedLongSideFit;
                    }
                }
            }
            return bestNode;
        }

        Rect FindPositionForNewNodeBestLongSideFit (int width, int height, ref int bestShortSideFit, ref int bestLongSideFit)
        {
            Rect bestNode = new Rect();
            //memset(&bestNode, 0, sizeof(Rect));

            bestLongSideFit = int.MaxValue;

            for (int i = 0; i < freeRectangles.Count; ++i)
            {
                // Try to place the rectangle in upright (non-flipped) orientation.
                if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
                {
                    int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);
                    int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);
                    int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);
                    int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert);

                    if (longSideFit < bestLongSideFit || (longSideFit == bestLongSideFit && shortSideFit < bestShortSideFit))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = width;
                        bestNode.height = height;
                        bestShortSideFit = shortSideFit;
                        bestLongSideFit = longSideFit;
                    }
                }

                if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
                {
                    int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);
                    int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);
                    int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);
                    int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert);

                    if (longSideFit < bestLongSideFit || (longSideFit == bestLongSideFit && shortSideFit < bestShortSideFit))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = height;
                        bestNode.height = width;
                        bestShortSideFit = shortSideFit;
                        bestLongSideFit = longSideFit;
                    }
                }
            }
            return bestNode;
        }

        Rect FindPositionForNewNodeBestAreaFit (int width, int height, ref int bestAreaFit, ref int bestShortSideFit)
        {
            Rect bestNode = new Rect();
            //memset(&bestNode, 0, sizeof(Rect));

            bestAreaFit = int.MaxValue;

            for (int i = 0; i < freeRectangles.Count; ++i)
            {
                int areaFit = (int)freeRectangles[i].width * (int)freeRectangles[i].height - width * height;

                // Try to place the rectangle in upright (non-flipped) orientation.
                if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
                {
                    int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);
                    int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);
                    int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);

                    if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = width;
                        bestNode.height = height;
                        bestShortSideFit = shortSideFit;
                        bestAreaFit = areaFit;
                    }
                }

                if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
                {
                    int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);
                    int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);
                    int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);

                    if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit))
                    {
                        bestNode.x = freeRectangles[i].x;
                        bestNode.y = freeRectangles[i].y;
                        bestNode.width = height;
                        bestNode.height = width;
                        bestShortSideFit = shortSideFit;
                        bestAreaFit = areaFit;
                    }
                }
            }
            return bestNode;
        }

        /// Returns 0 if the two intervals i1 and i2 are disjoint, or the length of their overlap otherwise.
        int CommonIntervalLength (int i1start, int i1end, int i2start, int i2end)
        {
            if (i1end < i2start || i2end < i1start)
                return 0;
            return Mathf.Min(i1end, i2end) - Mathf.Max(i1start, i2start);
        }

        int ContactPointScoreNode (int x, int y, int width, int height)
        {
            int score = 0;

            if (x == 0 || x + width == binWidth)
                score += height;
            if (y == 0 || y + height == binHeight)
                score += width;

            for (int i = 0; i < usedRectangles.Count; ++i)
            {
                if (usedRectangles[i].x == x + width || usedRectangles[i].x + usedRectangles[i].width == x)
                    score += CommonIntervalLength((int)usedRectangles[i].y, (int)usedRectangles[i].y + (int)usedRectangles[i].height, y, y + height);
                if (usedRectangles[i].y == y + height || usedRectangles[i].y + usedRectangles[i].height == y)
                    score += CommonIntervalLength((int)usedRectangles[i].x, (int)usedRectangles[i].x + (int)usedRectangles[i].width, x, x + width);
            }
            return score;
        }

        Rect FindPositionForNewNodeContactPoint (int width, int height, ref int bestContactScore)
        {
            Rect bestNode = new Rect();
            //memset(&bestNode, 0, sizeof(Rect));

            bestContactScore = -1;

            for (int i = 0; i < freeRectangles.Count; ++i)
            {
                // Try to place the rectangle in upright (non-flipped) orientation.
                if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)
                {
                    int score = ContactPointScoreNode((int)freeRectangles[i].x, (int)freeRectangles[i].y, width, height);
                    if (score > bestContactScore)
                    {
                        bestNode.x = (int)freeRectangles[i].x;
                        bestNode.y = (int)freeRectangles[i].y;
                        bestNode.width = width;
                        bestNode.height = height;
                        bestContactScore = score;
                    }
                }
                if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)
                {
                    int score = ContactPointScoreNode((int)freeRectangles[i].x, (int)freeRectangles[i].y, height, width);
                    if (score > bestContactScore)
                    {
                        bestNode.x = (int)freeRectangles[i].x;
                        bestNode.y = (int)freeRectangles[i].y;
                        bestNode.width = height;
                        bestNode.height = width;
                        bestContactScore = score;
                    }
                }
            }
            return bestNode;
        }

        bool SplitFreeNode (Rect freeNode, ref Rect usedNode)
        {
            // Test with SAT if the rectangles even intersect.
            if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||
                usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)
                return false;

            if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x)
            {
                // New node at the top side of the used node.
                if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height)
                {
                    Rect newNode = freeNode;
                    newNode.height = usedNode.y - newNode.y;
                    freeRectangles.Add(newNode);
                }

                // New node at the bottom side of the used node.
                if (usedNode.y + usedNode.height < freeNode.y + freeNode.height)
                {
                    Rect newNode = freeNode;
                    newNode.y = usedNode.y + usedNode.height;
                    newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
                    freeRectangles.Add(newNode);
                }
            }

            if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y)
            {
                // New node at the left side of the used node.
                if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width)
                {
                    Rect newNode = freeNode;
                    newNode.width = usedNode.x - newNode.x;
                    freeRectangles.Add(newNode);
                }

                // New node at the right side of the used node.
                if (usedNode.x + usedNode.width < freeNode.x + freeNode.width)
                {
                    Rect newNode = freeNode;
                    newNode.x = usedNode.x + usedNode.width;
                    newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
                    freeRectangles.Add(newNode);
                }
            }

            return true;
        }

        void PruneFreeList ()
        {
            for (int i = 0; i < freeRectangles.Count; ++i)
                for (int j = i + 1; j < freeRectangles.Count; ++j)
                {
                    if (IsContainedIn(freeRectangles[i], freeRectangles[j]))
                    {
                        freeRectangles.RemoveAt(i);
                        --i;
                        break;
                    }
                    if (IsContainedIn(freeRectangles[j], freeRectangles[i]))
                    {
                        freeRectangles.RemoveAt(j);
                        --j;
                    }
                }
        }

        bool IsContainedIn (Rect a, Rect b)
        {
            return a.x >= b.x && a.y >= b.y
                && a.x + a.width <= b.x + b.width
                && a.y + a.height <= b.y + b.height;
        }
    }

    个人分类: 
  • 相关阅读:
    EJB3.0
    EJB
    到底EJB是什么
    EJB到底是什么,真的那么神秘吗??
    ssh和ejb的区别
    struts2的一些小问题
    Struts2中ValueStack结构和总结
    Java 监听器
    浅谈cookie 和session 的区别
    理解Cookie和Session机制
  • 原文地址:https://www.cnblogs.com/nafio/p/9284204.html
Copyright © 2020-2023  润新知