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); } } }