package frame
{
import flash.geom.*;
import flash.display.*;
import util.*;
public class FramePacker
{
public const MaxSize:int = 2048;
private var sheets:Array; // [RectanglePacker]
private var frames:Array; // [FrameInfo]
public function GetSheets():Array
{
return sheets;
}
public function FramePacker(frames:Array)
{
this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frameData != null; }, this);
this.frames.sortOn("frameData", Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); });
}
private function GetMaxSize(frames:Array):Point
{
if (frames.length == 0) return new Point();
return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frameData.width; }, this)),
Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frameData.height; }, this)));
}
private function GetArea(frames:Array):int
{
var area = 0;
for (var i in frames)
{
if (frames[i] is FrameInfo)
{
area += frames[i].frameData.width * frames[i].frameData.height;
}
if (frames[i] is BitmapData || frames[i] is Rectangle)
{
area += frames[i].width * frames[i].height;
}
}
return area;
}
public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames
private var callback;
public function Pack(callback)
{
this.callback = callback;
sheets = new Array();
var index = 0;
while (index < frames.length)
{
var now:Array = frames.slice(index);
var max:Point = GetMaxSize(now);
var area:int = GetArea(now);
var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2));
var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent));
var sizeY:int = sizeX;
var i;
var startIndex = index;
var nodes:Array = new Array();
while (startIndex == index)
{
nodes.length = 0;
var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY);
//var sorted:Array = now.sortOn("frameData", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); });
//for (var sortedI = 0; sortedI < now.length; sortedI++)
for (i = 0; i < now.length; i++)
{
//i = sorted[sortedI];
var node:RectangleNode = sheet.Insert(now[i].frameData.width, now[i].frameData.height);
if (node == null)
{
break;
}
nodes.push(node);
}
if (nodes.length == now.length)
{
// stop the loop, attempt the 75% check here
index += nodes.length;
}
else
{
if (sizeX == sizeY)
{
sizeX *= 2;
}
else
{
sizeY *= 2;
}
if (sizeX > MaxSize || sizeY > MaxSize)
{
if (nodes.length == 0)
{
Exporter.Instance.Print("Failure: item too large to pack on sheet");
callback(false);
return;
}
index += nodes.length;
}
}
}
for (i = startIndex; i < index; i++)
{
frames[i].sheetIndex = sheets.length;
frames[i].rect = nodes[i - startIndex].Rect;
}
sheets.push(sheet);
}
callback(true);
}
}
}