Advertisement
SarahNorthway

AS3 Binary Tree Bin Packing Algorithm

Sep 14th, 2014
806
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package rebuild3.game.starling
  2. {
  3.     import flash.display.DisplayObject;
  4.     import flash.display.DisplayObjectContainer;
  5.  
  6.     /**
  7.      * Atlas packing algorithm for SarahDynamicAtlas.
  8.      * From javascript at http://codeincomplete.com/posts/2011/5/7/bin_packing/
  9.      * https://github.com/jakesgordon/bin-packing/blob/master/js/packer.growing.js
  10.      *
  11.      * @author jakesgordon and Sarah Northway
  12.      */
  13.     public class SarahAtlasLayouter
  14.     {
  15.         /**
  16.          * Changes the x and y of the DisplayObjects in items to fit in an atlas atlasWidth wide
  17.          * and as tall as necessary.
  18.          * @param items an Array of DisplayObjects
  19.          * @param atlasWidth the target width of the atlas eg 1024, 2048 or 4096
  20.          * @param gutterSize space to put between items
  21.          */
  22.         public static function layoutItems(items:Array, atlasWidth:int = 2048, gutterSize:int = 1):void
  23.         {
  24.             if (items.length == 0) {
  25.                 return;
  26.             }
  27.  
  28.             items.sort(function(first:DisplayObject, second:DisplayObject):int {
  29.                 return second.height - first.height;
  30.             });
  31.  
  32.             //var startingWidth:int = Math.ceil(items[0].width) + gutterSize;
  33.             var startingWidth:int = atlasWidth;
  34.             var startingHeight:int = Math.ceil(items[0].height) + gutterSize;
  35.             root = new AtlasNode(0, 0, startingWidth, startingHeight);
  36.  
  37.             for each (var item:DisplayObject in items) {
  38.                 var width:int = Math.ceil(item.width) + gutterSize;
  39.                 var height:int = Math.ceil(item.height) + gutterSize;
  40.                 var itemName:String = (item as DisplayObjectContainer).getChildAt(0).name;
  41.  
  42.                 var node:AtlasNode = findNode(root, width, height);
  43.                 if (node != null) {
  44.                     splitNode(node, width, height);
  45.                     item.x = node.x;
  46.                     item.y = node.y;
  47.                 } else {
  48.                     //node = growNode(width, height);
  49.                     node = growDown(width, height);
  50.                     if (node == null) {
  51.                         trace("SarahAtlasLayouter failed, could not grow??", root.width, root.height);
  52.                         return;
  53.                     }
  54.                     item.x = node.x;
  55.                     item.y = node.y;
  56.                 }
  57.             }
  58.  
  59.             root = null;
  60.         }
  61.  
  62.         private static function findNode(localRoot:AtlasNode, width:int, height:int):AtlasNode
  63.         {
  64.             if (localRoot.used) {
  65.                 var newNode:AtlasNode = findNode(localRoot.right, width, height);
  66.                 if (newNode != null) {
  67.                     return newNode;
  68.                 }
  69.                 return findNode(localRoot.down, width, height);
  70.             } else if ((width <= localRoot.width) && (height <= localRoot.height)) {
  71.                 return localRoot;
  72.             } else {
  73.                 return null;
  74.             }
  75.         }
  76.  
  77.         private static function splitNode(node:AtlasNode, width:int, height:int):void
  78.         {
  79.             node.used = true;
  80.             // sarah changed this line so down node does not overlap with right
  81.             //node.down = new AtlasNode(node.x, node.y + height, node.width, node.height - height);
  82.             node.down = new AtlasNode(node.x, node.y + height, width, node.height - height);
  83.             node.right = new AtlasNode(node.x + width, node.y, node.width - width, node.height);
  84.         }
  85.  
  86.         private static function growDown(width:int, height:int):AtlasNode
  87.         {
  88.             var downNode:AtlasNode = new AtlasNode(0, root.height, root.width, height);
  89.             var newRoot:AtlasNode = new AtlasNode(0, 0, root.width, root.height + height, true, downNode, root);
  90.             root = newRoot;
  91.  
  92.             var node:AtlasNode = findNode(root, width, height);
  93.             if (node != null) {
  94.                 splitNode(node, width, height);
  95.                 return node;
  96.             }
  97.  
  98.             // can never happen...?
  99.             return null;
  100.         }
  101.  
  102.         /*private static function growNode(width:int, height:int):AtlasNode
  103.         {
  104.             var canGrowDown:Boolean  = (width <= root.width);
  105.             var canGrowRight:Boolean = (height <= root.height);
  106.  
  107.             // attempt to keep square-ish by growing right when height is much greater than width
  108.             var shouldGrowRight:Boolean = canGrowRight && (root.height >= (root.width + width));
  109.  
  110.             // attempt to keep square-ish by growing down  when width  is much greater than height
  111.             var shouldGrowDown:Boolean = canGrowDown && (root.width >= (root.height + height));
  112.  
  113.             if (shouldGrowRight) {
  114.                 return growRight(width, height);
  115.             } else if (shouldGrowDown) {
  116.                 return growDown(width, height);
  117.             } else if (canGrowRight) {
  118.                 return growRight(width, height);
  119.             } else if (canGrowDown) {
  120.                 return growDown(width, height);
  121.             } else {
  122.                 return null; // need to ensure sensible root starting size to avoid this happening
  123.             }
  124.         }*/
  125.  
  126.         /*private static function growRight(width:int, height:int):AtlasNode
  127.         {
  128.             var rightNode:AtlasNode = new AtlasNode(root.width, 0, width, root.height);
  129.             var newRoot:AtlasNode = new AtlasNode(0, 0, root.width + width, root.height, true, root, rightNode);
  130.             root = newRoot;
  131.  
  132.             var node:AtlasNode = findNode(root, width, height);
  133.             if (node != null) {
  134.                 splitNode(node, width, height);
  135.                 return node;
  136.             }
  137.  
  138.             // can never happen...?
  139.             return null;
  140.         }*/
  141.  
  142.         /** Temp data while running layoutItems */
  143.         private static var root:AtlasNode;
  144.     }
  145. }
  146.  
  147. class AtlasNode
  148. {
  149.     public function AtlasNode(x:int, y:int, width:int, height:int,
  150.         used:Boolean = false, down:AtlasNode = null, right:AtlasNode = null)
  151.     {
  152.        this.x = x;
  153.        this.y = y;
  154.        this.width = width;
  155.        this.height = height;
  156.        this.used = used;
  157.        this.down = down;
  158.        this.right = right;
  159.     }
  160.  
  161.     public function toString():String
  162.     {
  163.         return "(" + x + "," + y + " - " + (x + width) + "," + (y + height + ")");
  164.     }
  165.  
  166.     public var x:int;
  167.     public var y:int;
  168.     public var width:int;
  169.     public var height:int;
  170.     public var used:Boolean;
  171.     public var down:AtlasNode;
  172.     public var right:AtlasNode;
  173. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement