PO/Library/PackageCache/com.unity.2d.common@4.0.3/Editor/ImagePacker/ImagePackNode.cs

214 lines
7.7 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Common
{
internal interface IImagePackNodeVisitor
{
void Visit(ImagePackNode node);
}
class CollectEmptyNodePositionVisitor : IImagePackNodeVisitor
{
public List<RectInt> emptyAreas = new List<RectInt>();
public void Visit(ImagePackNode node)
{
if (node.imageId == -1)
{
emptyAreas.Add(node.rect);
}
}
}
class CollectPackNodePositionVisitor : IImagePackNodeVisitor
{
public CollectPackNodePositionVisitor()
{
positions = new Vector2Int[0];
}
public void Visit(ImagePackNode node)
{
if (node.imageId != -1)
{
if (positions.Length < node.imageId + 1)
{
var p = positions;
Array.Resize(ref p, node.imageId + 1);
positions = p;
}
positions[node.imageId].x = node.rect.x;
positions[node.imageId].y = node.rect.y;
}
}
public Vector2Int[] positions { get; private set; }
}
internal class ImagePackNode
{
public ImagePackNode left;
public ImagePackNode right;
public RectInt rect;
public Vector2Int imageWidth;
public int imageId = -1;
public void AcceptVisitor(IImagePackNodeVisitor visitor)
{
visitor.Visit(this);
if (left != null)
left.AcceptVisitor(visitor);
if (right != null)
right.AcceptVisitor(visitor);
}
public void AdjustSize(int oriWidth, int oriHeight, int deltaW, int deltaH, out int adjustx, out int adjusty)
{
adjustx = adjusty = 0;
int adjustXleft = 0, adjustYleft = 0, adjustXRight = 0, adjustYRight = 0;
if (imageId == -1 || left == null)
{
if (rect.x + rect.width == oriWidth)
{
rect.width += deltaW;
adjustx = deltaW;
}
if (rect.y + rect.height == oriHeight)
{
rect.height += deltaH;
adjusty = deltaH;
}
}
else
{
left.AdjustSize(oriWidth, oriHeight, deltaW, deltaH, out adjustXleft, out adjustYleft);
right.AdjustSize(oriWidth, oriHeight, deltaW, deltaH, out adjustXRight, out adjustYRight);
adjustx = Mathf.Max(adjustXleft, adjustXRight);
rect.width += adjustx;
adjusty = Mathf.Max(adjustYleft, adjustYRight);
rect.height += adjusty;
}
}
public bool TryInsert(ImagePacker.ImagePackRect insert, int padding, out Vector2Int remainingSpace)
{
remainingSpace = Vector2Int.zero;
int insertWidth = insert.rect.width + padding * 2;
int insertHeight = insert.rect.height + padding * 2;
if (insertWidth > rect.width || insertHeight > rect.height)
return false;
if (imageId == -1)
{
remainingSpace.x = rect.width - insertWidth;
remainingSpace.y = rect.height - insertHeight;
}
else
{
Vector2Int spaceLeft, spaceRight;
bool insertLeft, insertRight;
ImagePackNode tryLeft, tryRight;
tryLeft = left;
tryRight = right;
if (left == null && !SplitRects(this, insert, padding, out tryLeft, out tryRight))
{
return false;
}
insertLeft = tryLeft.TryInsert(insert, padding, out spaceLeft);
insertRight = tryRight.TryInsert(insert, padding, out spaceRight);
if (insertLeft && insertRight)
{
remainingSpace = spaceLeft.sqrMagnitude < spaceRight.sqrMagnitude ? spaceLeft : spaceRight;
}
else if (insertLeft)
remainingSpace = spaceLeft;
else if (insertRight)
remainingSpace = spaceRight;
else
return false;
}
return true;
}
static bool SplitRects(ImagePackNode node, ImagePacker.ImagePackRect insert, int padding, out ImagePackNode left, out ImagePackNode right)
{
// Find the best way to split the rect based on a new rect
left = right = null;
var tryRects = new[]
{
new ImagePackNode(), new ImagePackNode(),
new ImagePackNode(), new ImagePackNode()
};
tryRects[0].rect = new RectInt(node.rect.x + node.imageWidth.x, node.rect.y, node.rect.width - node.imageWidth.x, node.rect.height);
tryRects[1].rect = new RectInt(node.rect.x, node.rect.y + node.imageWidth.y, node.imageWidth.x, node.rect.height - node.imageWidth.y);
tryRects[2].rect = new RectInt(node.rect.x, node.rect.y + node.imageWidth.y, node.rect.width, node.rect.height - node.imageWidth.y);
tryRects[3].rect = new RectInt(node.rect.x + node.imageWidth.x, node.rect.y, node.rect.width - node.imageWidth.x, node.imageWidth.y);
float smallestSpace = float.MinValue;
for (int i = 0; i < tryRects.GetLength(0); ++i)
{
//for (int j = 0; j < tryRects.GetLength(1); ++j)
{
Vector2Int newSpaceLeft;
if (tryRects[i].TryInsert(insert, padding, out newSpaceLeft))
{
if (smallestSpace < newSpaceLeft.sqrMagnitude)
{
smallestSpace = newSpaceLeft.sqrMagnitude;
int index = i / 2 * 2;
left = tryRects[index];
right = tryRects[index + 1];
}
}
}
}
return left != null;
}
public bool Insert(ImagePacker.ImagePackRect insert, int padding)
{
int insertWidth = insert.rect.width + padding * 2;
int insertHeight = insert.rect.height + padding * 2;
if (insertWidth > rect.width || insertHeight > rect.height)
return false;
if (imageId == -1)
{
imageId = insert.index;
imageWidth = new Vector2Int(insertWidth, insertHeight);
}
else
{
if (left == null && !SplitRects(this, insert, padding, out left, out right))
{
return false;
}
// We assign to the node that has a better fit for the image
Vector2Int spaceLeft, spaceRight;
bool insertLeft, insertRight;
insertLeft = left.TryInsert(insert, padding, out spaceLeft);
insertRight = right.TryInsert(insert, padding, out spaceRight);
if (insertLeft && insertRight)
{
if (spaceLeft.sqrMagnitude < spaceRight.sqrMagnitude)
left.Insert(insert, padding);
else
right.Insert(insert, padding);
}
else if (insertLeft)
left.Insert(insert, padding);
else if (insertRight)
right.Insert(insert, padding);
else
return false;
}
return true;
}
}
}