1 var jaws = (function(jaws) {
  2 
  3   /**
  4    * @class Create and access tilebased 2D maps with very fast access of invidual tiles. "Field Summary" contains options for the TileMap()-constructor.
  5    *
  6    * @property {array} cell_size        Size of each cell in tilemap, defaults to [32,32]
  7    * @property {array} size             Size of tilemap, defaults to [100,100]
  8    * @property {function} sortFunction  Function used by sortCells() to sort cells, defaults to no sorting
  9    *
 10    * @example
 11    * var tile_map = new TileMap({size: [10, 10], cell_size: [16,16]})
 12    * var sprite = new jaws.Sprite({x: 40, y: 40})
 13    * var sprite2 = new jaws.Sprite({x: 41, y: 41})
 14    * tile_map.push(sprite)
 15    *
 16    * tile_map.at(10,10)  // []
 17    * tile_map.at(40,40)  // [sprite]
 18    * tile_map.cell(0,0)  // []
 19    * tile_map.cell(1,1)  // [sprite]
 20    *
 21    */
 22   jaws.TileMap = function TileMap(options) {
 23     if( !(this instanceof arguments.callee) ) return new arguments.callee( options );
 24 
 25     jaws.parseOptions(this, options, this.default_options);
 26     this.cells = new Array(this.size[0]);
 27 
 28     for(var col=0; col < this.size[0]; col++) {
 29       this.cells[col] = new Array(this.size[1]);
 30       for(var row=0; row < this.size[1]; row++) {
 31         this.cells[col][row] = [] // populate each cell with an empty array
 32       }
 33     }
 34   }
 35 
 36   jaws.TileMap.prototype.default_options = {
 37     cell_size: [32,32],
 38     size: [100,100],
 39     sortFunction: null
 40   }
 41 
 42   /** Clear all cells in tile map */
 43   jaws.TileMap.prototype.clear = function() {
 44     for(var col=0; col < this.size[0]; col++) {
 45       for(var row=0; row < this.size[1]; row++) {
 46         this.cells[col][row] = [];
 47       }
 48     }
 49   }
 50 
 51   /** Sort arrays in each cell in tile map according to sorter-function (see Array.sort) */
 52   jaws.TileMap.prototype.sortCells = function(sortFunction) {
 53     for(var col=0; col < this.size[0]; col++) {
 54       for(var row=0; row < this.size[1]; row++) {
 55         this.cells[col][row].sort( sortFunction )
 56       }
 57     }
 58   }
 59 
 60   /**
 61    * Push obj (or array of objs) into our cell-grid.
 62    *
 63    * Tries to read obj.x and obj.y to calculate what cell to occopy
 64    */
 65   jaws.TileMap.prototype.push = function(obj) {
 66     var that = this;
 67     if(obj.forEach) { 
 68       obj.forEach( function(item) { that.push(item) } );
 69       return obj;
 70     }
 71     if(obj.rect) {
 72       return this.pushAsRect(obj, obj.rect());
 73     }
 74     else {
 75       var col = parseInt(obj.x / this.cell_size[0]);
 76       var row = parseInt(obj.y / this.cell_size[1]);
 77       return this.pushToCell(col, row, obj);
 78     }
 79   }
 80   /** 
 81    * Push objects into tilemap.
 82    * Disregard height and width and only use x/y when calculating cell-position
 83    */
 84   jaws.TileMap.prototype.pushAsPoint = function(obj) {
 85     if(Array.isArray(obj)) { 
 86       for(var i=0; i < obj.length; i++) { this.pushAsPoint(obj[i]) }
 87       return obj;
 88     }
 89     else {
 90       var col = parseInt(obj.x / this.cell_size[0]);
 91       var row = parseInt(obj.y / this.cell_size[1]);
 92       return this.pushToCell(col, row, obj);
 93     }
 94   }
 95 
 96   /** push obj into cells touched by rect */
 97   jaws.TileMap.prototype.pushAsRect = function(obj, rect) {
 98     var from_col = parseInt(rect.x / this.cell_size[0]);
 99     var to_col = parseInt((rect.right-1) / this.cell_size[0]); // -1
100     //jaws.log("rect.right: " + rect.right + " from/to col: " + from_col + " " + to_col, true)
101 
102     for(var col = from_col; col <= to_col; col++) {
103       var from_row = parseInt(rect.y / this.cell_size[1]);
104       var to_row = parseInt((rect.bottom-1) / this.cell_size[1]); // -1
105 
106       //jaws.log("rect.bottom " + rect.bottom + " from/to row: " + from_row + " " + to_row, true)
107       for(var row = from_row; row <= to_row; row++) {
108         // console.log("pushAtRect() col/row: " + col + "/" + row + " - " + this.cells[col][row])
109         this.pushToCell(col, row, obj);
110       }
111     }
112     return obj
113   }
114 
115   /** 
116    * Push obj to a specific cell specified by col and row 
117    * If cell is already occupied we create an array and push to that
118    */
119   jaws.TileMap.prototype.pushToCell = function(col, row, obj) {
120     this.cells[col][row].push(obj);
121     if(this.sortFunction) this.cells[col][row].sort(this.sortFunction);
122     return this
123   }
124 
125   //
126   // READERS
127   // 
128 
129   /** Get objects in cell that exists at coordinates x / y  */
130   jaws.TileMap.prototype.at = function(x, y) {
131     var col = parseInt(x / this.cell_size[0]);
132     var row = parseInt(y / this.cell_size[1]);
133     // console.log("at() col/row: " + col + "/" + row)
134     return this.cells[col][row];
135   }
136 
137   /** Returns occupants of all cells touched by 'rect' */
138   jaws.TileMap.prototype.atRect = function(rect) {
139     var objects = [];
140     var items;
141 
142     try {
143       var from_col = parseInt(rect.x / this.cell_size[0]);
144       if (from_col < 0) {
145         from_col = 0;
146       }
147       var to_col = parseInt(rect.right / this.cell_size[0]);
148       if (to_col >= this.size[0]) {
149         to_col = this.size[0] - 1;
150       }
151       var from_row = parseInt(rect.y / this.cell_size[1]);
152       if (from_row < 0) {
153         from_row = 0;
154       }
155       var to_row = parseInt(rect.bottom / this.cell_size[1]);
156       if (to_row >= this.size[1]) {
157         to_row = this.size[1] - 1;
158       }
159 
160       for(var col = from_col; col <= to_col; col++) {
161         for(var row = from_row; row <= to_row; row++) {
162           this.cells[col][row].forEach( function(item, total) { 
163             if(objects.indexOf(item) == -1) { objects.push(item) }
164           })
165         }
166       }
167     }
168     catch(e) {
169       // ... problems
170     }
171     return objects
172   }
173 
174   /** Returns all objects in tile map */
175   jaws.TileMap.prototype.all = function() {
176     var all = [];
177     for(var col=0; col < this.size[0]; col++) {
178       for(var row=0; row < this.size[1]; row++) {
179         this.cells[col][row].forEach( function(element, total) {
180           all.push(element)
181         });
182       }
183     }
184     return all
185   }
186 
187   /** Get objects in cell at col / row */
188   jaws.TileMap.prototype.cell = function(col, row) {
189     return this.cells[col][row]
190   }
191 
192   /** Debugstring for TileMap() */
193   jaws.TileMap.prototype.toString = function() { return "[TileMap " + this.size[0] + " cols, " + this.size[1] + " rows]" }
194 
195   return jaws;
196 })(jaws || {});
197 
198 // Support CommonJS require()
199 if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.TileMap }
200