@mapka/shelf-pack 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/.buildInfo.json +1 -1
- package/lib/index.d.ts +46 -80
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +356 -355
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.js +374 -378
package/src/index.js
CHANGED
|
@@ -1,32 +1,3 @@
|
|
|
1
|
-
export default ShelfPack;
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create a new ShelfPack bin allocator.
|
|
6
|
-
*
|
|
7
|
-
* Uses the Shelf Best Height Fit algorithm from
|
|
8
|
-
* http://clb.demon.fi/files/RectangleBinPack.pdf
|
|
9
|
-
*
|
|
10
|
-
* @class ShelfPack
|
|
11
|
-
* @param {number} [w=64] Initial width of the sprite
|
|
12
|
-
* @param {number} [h=64] Initial width of the sprite
|
|
13
|
-
* @param {Object} [options]
|
|
14
|
-
* @param {boolean} [options.autoResize=false] If `true`, the sprite will automatically grow
|
|
15
|
-
* @example
|
|
16
|
-
* var sprite = new ShelfPack(64, 64, { autoResize: false });
|
|
17
|
-
*/
|
|
18
|
-
function ShelfPack(w, h, options) {
|
|
19
|
-
options = options || {};
|
|
20
|
-
this.w = w || 64;
|
|
21
|
-
this.h = h || 64;
|
|
22
|
-
this.autoResize = !!options.autoResize;
|
|
23
|
-
this.shelves = [];
|
|
24
|
-
this.freebins = [];
|
|
25
|
-
this.stats = {};
|
|
26
|
-
this.bins = {};
|
|
27
|
-
this.maxId = 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
1
|
/**
|
|
31
2
|
* Bin object with `id`, `width`, `height` properties
|
|
32
3
|
* @typedef {Object} BinShorthand
|
|
@@ -37,427 +8,452 @@ function ShelfPack(w, h, options) {
|
|
|
37
8
|
|
|
38
9
|
|
|
39
10
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* @property {number} height Height of the bin
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Batch pack multiple bins into the sprite.
|
|
11
|
+
* Create a new ShelfPack bin allocator.
|
|
12
|
+
*
|
|
13
|
+
* Uses the Shelf Best Height Fit algorithm from
|
|
14
|
+
* http://clb.demon.fi/files/RectangleBinPack.pdf
|
|
50
15
|
*
|
|
51
|
-
* @param {Object[]} bins Array of requested bins - each object should have `width`, `height` (or `w`, `h`) properties
|
|
52
|
-
* @param {Object} [options]
|
|
53
|
-
* @param {boolean} [options.inPlace=false] If `true`, the supplied bin objects will be updated inplace with `x` and `y` properties
|
|
54
|
-
* @returns {Bin[]} Array of allocated Bins - each Bin is an object with `id`, `x`, `y`, `w`, `h` properties
|
|
55
|
-
* @noto Mutate input bins
|
|
56
16
|
* @example
|
|
57
|
-
* var
|
|
58
|
-
* { id: 1, w: 12, h: 12 },
|
|
59
|
-
* { id: 2, w: 12, h: 16 },
|
|
60
|
-
* { id: 3, w: 12, h: 24 }
|
|
61
|
-
* ];
|
|
62
|
-
* var results = sprite.pack(bins, { inPlace: false });
|
|
17
|
+
* var sprite = new ShelfPack(64, 64, { autoResize: false });
|
|
63
18
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
19
|
+
export default class ShelfPack {
|
|
20
|
+
/**
|
|
21
|
+
* @param {number} [w=64] Initial width of the sprite
|
|
22
|
+
* @param {number} [h=64] Initial width of the sprite
|
|
23
|
+
* @param {Object} [options]
|
|
24
|
+
* @param {boolean} [options.autoResize=false] If `true`, the sprite will automatically grow
|
|
25
|
+
*/
|
|
26
|
+
constructor(w, h, options) {
|
|
27
|
+
options = options || {};
|
|
28
|
+
this.w = w || 64;
|
|
29
|
+
this.h = h || 64;
|
|
30
|
+
this.autoResize = !!options.autoResize;
|
|
31
|
+
this.shelves = [];
|
|
32
|
+
this.freebins = [];
|
|
33
|
+
this.stats = {};
|
|
34
|
+
this.bins = {};
|
|
35
|
+
this.maxId = 0;
|
|
36
|
+
}
|
|
75
37
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Batch pack multiple bins into the sprite.
|
|
40
|
+
*
|
|
41
|
+
* @param {Object[]} bins Array of requested bins - each object should have `width`, `height` (or `w`, `h`) properties
|
|
42
|
+
* @param {Object} [options]
|
|
43
|
+
* @param {boolean} [options.inPlace=false] If `true`, the supplied bin objects will be updated inplace with `x` and `y` properties
|
|
44
|
+
* @returns {Bin[]} Array of allocated Bins - each Bin is an object with `id`, `x`, `y`, `w`, `h` properties
|
|
45
|
+
* @noto Mutate input bins
|
|
46
|
+
* @example
|
|
47
|
+
* var bins = [
|
|
48
|
+
* { id: 1, w: 12, h: 12 },
|
|
49
|
+
* { id: 2, w: 12, h: 16 },
|
|
50
|
+
* { id: 3, w: 12, h: 24 }
|
|
51
|
+
* ];
|
|
52
|
+
* var results = sprite.pack(bins, { inPlace: false });
|
|
53
|
+
*/
|
|
54
|
+
pack(bins, options) {
|
|
55
|
+
bins = [].concat(bins);
|
|
56
|
+
options = options || {};
|
|
57
|
+
|
|
58
|
+
var results = [],
|
|
59
|
+
w, h, id, allocation;
|
|
60
|
+
|
|
61
|
+
for (var i = 0; i < bins.length; i++) {
|
|
62
|
+
w = bins[i].w || bins[i].width;
|
|
63
|
+
h = bins[i].h || bins[i].height;
|
|
64
|
+
id = bins[i].id;
|
|
65
|
+
|
|
66
|
+
if (w && h) {
|
|
67
|
+
allocation = this.packOne(w, h, id);
|
|
68
|
+
if (!allocation) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (options.inPlace) {
|
|
72
|
+
bins[i].x = allocation.x;
|
|
73
|
+
bins[i].y = allocation.y;
|
|
74
|
+
bins[i].id = allocation.id;
|
|
75
|
+
}
|
|
76
|
+
results.push(allocation);
|
|
80
77
|
}
|
|
81
|
-
if (options.inPlace) {
|
|
82
|
-
bins[i].x = allocation.x;
|
|
83
|
-
bins[i].y = allocation.y;
|
|
84
|
-
bins[i].id = allocation.id;
|
|
85
|
-
}
|
|
86
|
-
results.push(allocation);
|
|
87
78
|
}
|
|
88
|
-
}
|
|
89
79
|
|
|
90
|
-
|
|
80
|
+
this.shrink();
|
|
91
81
|
|
|
92
|
-
|
|
93
|
-
}
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
94
84
|
|
|
95
85
|
|
|
96
|
-
/**
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Pack a single bin into the sprite.
|
|
88
|
+
*
|
|
89
|
+
* Each bin will have a unique identitifer.
|
|
90
|
+
* If no identifier is supplied in the `id` parameter, one will be created.
|
|
91
|
+
* Note: The supplied `id` is used as an object index, so numeric values are fastest!
|
|
92
|
+
*
|
|
93
|
+
* Bins are automatically refcounted (i.e. a newly packed Bin will have a refcount of 1).
|
|
94
|
+
* When a bin is no longer needed, use the `ShelfPack.unref` function to mark it
|
|
95
|
+
* as unused. When a Bin's refcount decrements to 0, the Bin will be marked
|
|
96
|
+
* as free and its space may be reused by the packing code.
|
|
97
|
+
*
|
|
98
|
+
* @param {number} w Width of the bin to allocate
|
|
99
|
+
* @param {number} h Height of the bin to allocate
|
|
100
|
+
* @param {number|string} [id] Unique identifier for this bin, (if unsupplied, assume it's a new bin and create an id)
|
|
101
|
+
* @returns {Bin} Bin object with `id`, `x`, `y`, `w`, `h` properties, or `null` if allocation failed
|
|
102
|
+
* @example
|
|
103
|
+
* var results = sprite.packOne(12, 16, 'a');
|
|
104
|
+
*/
|
|
105
|
+
packOne(w, h, id) {
|
|
106
|
+
var best = { freebin: -1, shelf: -1, waste: Infinity },
|
|
107
|
+
y = 0,
|
|
108
|
+
bin, shelf, waste, i;
|
|
109
|
+
|
|
110
|
+
// if id was supplied, attempt a lookup..
|
|
111
|
+
if (typeof id === 'string' || typeof id === 'number') {
|
|
112
|
+
bin = this.getBin(id);
|
|
113
|
+
if (bin) { // we packed this bin already
|
|
114
|
+
this.ref(bin);
|
|
115
|
+
return bin;
|
|
116
|
+
}
|
|
117
|
+
if (typeof id === 'number') {
|
|
118
|
+
this.maxId = Math.max(id, this.maxId);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
id = ++this.maxId;
|
|
129
122
|
}
|
|
130
|
-
} else {
|
|
131
|
-
id = ++this.maxId;
|
|
132
|
-
}
|
|
133
123
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
124
|
+
// First try to reuse a free bin..
|
|
125
|
+
for (i = 0; i < this.freebins.length; i++) {
|
|
126
|
+
bin = this.freebins[i];
|
|
137
127
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
128
|
+
// exactly the right height and width, use it..
|
|
129
|
+
if (h === bin.maxh && w === bin.maxw) {
|
|
130
|
+
return this.allocFreebin(i, w, h, id);
|
|
131
|
+
}
|
|
132
|
+
// not enough height or width, skip it..
|
|
133
|
+
if (h > bin.maxh || w > bin.maxw) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// extra height or width, minimize wasted area..
|
|
137
|
+
if (h <= bin.maxh && w <= bin.maxw) {
|
|
138
|
+
waste = (bin.maxw * bin.maxh) - (w * h);
|
|
139
|
+
if (waste < best.waste) {
|
|
140
|
+
best.waste = waste;
|
|
141
|
+
best.freebin = i;
|
|
142
|
+
}
|
|
152
143
|
}
|
|
153
144
|
}
|
|
154
|
-
}
|
|
155
145
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
146
|
+
// Next find the best shelf..
|
|
147
|
+
for (i = 0; i < this.shelves.length; i++) {
|
|
148
|
+
shelf = this.shelves[i];
|
|
149
|
+
y += shelf.h;
|
|
160
150
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
151
|
+
// not enough width on this shelf, skip it..
|
|
152
|
+
if (w > shelf.free) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// exactly the right height, pack it..
|
|
156
|
+
if (h === shelf.h) {
|
|
157
|
+
return this.allocShelf(i, w, h, id);
|
|
158
|
+
}
|
|
159
|
+
// not enough height, skip it..
|
|
160
|
+
if (h > shelf.h) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
// extra height, minimize wasted area..
|
|
164
|
+
if (h < shelf.h) {
|
|
165
|
+
waste = (shelf.h - h) * w;
|
|
166
|
+
if (waste < best.waste) {
|
|
167
|
+
best.freebin = -1;
|
|
168
|
+
best.waste = waste;
|
|
169
|
+
best.shelf = i;
|
|
170
|
+
}
|
|
180
171
|
}
|
|
181
172
|
}
|
|
182
|
-
}
|
|
183
173
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
174
|
+
if (best.freebin !== -1) {
|
|
175
|
+
return this.allocFreebin(best.freebin, w, h, id);
|
|
176
|
+
}
|
|
187
177
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
178
|
+
if (best.shelf !== -1) {
|
|
179
|
+
return this.allocShelf(best.shelf, w, h, id);
|
|
180
|
+
}
|
|
191
181
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
182
|
+
// No free bins or shelves.. add shelf..
|
|
183
|
+
if (h <= (this.h - y) && w <= this.w) {
|
|
184
|
+
shelf = new Shelf(y, this.w, h);
|
|
185
|
+
return this.allocShelf(this.shelves.push(shelf) - 1, w, h, id);
|
|
186
|
+
}
|
|
197
187
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
188
|
+
// No room for more shelves..
|
|
189
|
+
// If `autoResize` option is set, grow the sprite as follows:
|
|
190
|
+
// * double whichever sprite dimension is smaller (`w1` or `h1`)
|
|
191
|
+
// * if sprite dimensions are equal, grow width before height
|
|
192
|
+
// * accomodate very large bin requests (big `w` or `h`)
|
|
193
|
+
if (this.autoResize) {
|
|
194
|
+
var h1, h2, w1, w2;
|
|
205
195
|
|
|
206
|
-
|
|
207
|
-
|
|
196
|
+
h1 = h2 = this.h;
|
|
197
|
+
w1 = w2 = this.w;
|
|
208
198
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
199
|
+
if (w1 <= h1 || w > w1) { // grow width..
|
|
200
|
+
w2 = Math.max(w, w1) * 2;
|
|
201
|
+
}
|
|
202
|
+
if (h1 < w1 || h > h1) { // grow height..
|
|
203
|
+
h2 = Math.max(h, h1) * 2;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.resize(w2, h2);
|
|
207
|
+
return this.packOne(w, h, id); // retry
|
|
214
208
|
}
|
|
215
209
|
|
|
216
|
-
|
|
217
|
-
return this.packOne(w, h, id); // retry
|
|
210
|
+
return null;
|
|
218
211
|
}
|
|
219
212
|
|
|
220
|
-
return null;
|
|
221
|
-
};
|
|
222
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Called by packOne() to allocate a bin by reusing an existing freebin
|
|
216
|
+
*
|
|
217
|
+
* @private
|
|
218
|
+
* @param {number} index Index into the `this.freebins` array
|
|
219
|
+
* @param {number} w Width of the bin to allocate
|
|
220
|
+
* @param {number} h Height of the bin to allocate
|
|
221
|
+
* @param {number|string} id Unique identifier for this bin
|
|
222
|
+
* @returns {Bin} Bin object with `id`, `x`, `y`, `w`, `h` properties
|
|
223
|
+
* @example
|
|
224
|
+
* var bin = sprite.allocFreebin(0, 12, 16, 'a');
|
|
225
|
+
*/
|
|
226
|
+
allocFreebin(index, w, h, id) {
|
|
227
|
+
var bin = this.freebins.splice(index, 1)[0];
|
|
228
|
+
bin.id = id;
|
|
229
|
+
bin.w = w;
|
|
230
|
+
bin.h = h;
|
|
231
|
+
bin.refcount = 0;
|
|
232
|
+
this.bins[id] = bin;
|
|
233
|
+
this.ref(bin);
|
|
234
|
+
return bin;
|
|
235
|
+
}
|
|
223
236
|
|
|
224
|
-
/**
|
|
225
|
-
* Called by packOne() to allocate a bin by reusing an existing freebin
|
|
226
|
-
*
|
|
227
|
-
* @private
|
|
228
|
-
* @param {number} index Index into the `this.freebins` array
|
|
229
|
-
* @param {number} w Width of the bin to allocate
|
|
230
|
-
* @param {number} h Height of the bin to allocate
|
|
231
|
-
* @param {number|string} id Unique identifier for this bin
|
|
232
|
-
* @returns {Bin} Bin object with `id`, `x`, `y`, `w`, `h` properties
|
|
233
|
-
* @example
|
|
234
|
-
* var bin = sprite.allocFreebin(0, 12, 16, 'a');
|
|
235
|
-
*/
|
|
236
|
-
ShelfPack.prototype.allocFreebin = function (index, w, h, id) {
|
|
237
|
-
var bin = this.freebins.splice(index, 1)[0];
|
|
238
|
-
bin.id = id;
|
|
239
|
-
bin.w = w;
|
|
240
|
-
bin.h = h;
|
|
241
|
-
bin.refcount = 0;
|
|
242
|
-
this.bins[id] = bin;
|
|
243
|
-
this.ref(bin);
|
|
244
|
-
return bin;
|
|
245
|
-
};
|
|
246
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Called by `packOne() to allocate bin on an existing shelf
|
|
240
|
+
*
|
|
241
|
+
* @private
|
|
242
|
+
* @param {number} index Index into the `this.shelves` array
|
|
243
|
+
* @param {number} w Width of the bin to allocate
|
|
244
|
+
* @param {number} h Height of the bin to allocate
|
|
245
|
+
* @param {number|string} id Unique identifier for this bin
|
|
246
|
+
* @returns {Bin} Bin object with `id`, `x`, `y`, `w`, `h` properties
|
|
247
|
+
* @example
|
|
248
|
+
* var results = sprite.allocShelf(0, 12, 16, 'a');
|
|
249
|
+
*/
|
|
250
|
+
allocShelf(index, w, h, id) {
|
|
251
|
+
var shelf = this.shelves[index];
|
|
252
|
+
var bin = shelf.alloc(w, h, id);
|
|
253
|
+
this.bins[id] = bin;
|
|
254
|
+
this.ref(bin);
|
|
255
|
+
return bin;
|
|
256
|
+
}
|
|
247
257
|
|
|
248
|
-
/**
|
|
249
|
-
* Called by `packOne() to allocate bin on an existing shelf
|
|
250
|
-
*
|
|
251
|
-
* @private
|
|
252
|
-
* @param {number} index Index into the `this.shelves` array
|
|
253
|
-
* @param {number} w Width of the bin to allocate
|
|
254
|
-
* @param {number} h Height of the bin to allocate
|
|
255
|
-
* @param {number|string} id Unique identifier for this bin
|
|
256
|
-
* @returns {Bin} Bin object with `id`, `x`, `y`, `w`, `h` properties
|
|
257
|
-
* @example
|
|
258
|
-
* var results = sprite.allocShelf(0, 12, 16, 'a');
|
|
259
|
-
*/
|
|
260
|
-
ShelfPack.prototype.allocShelf = function(index, w, h, id) {
|
|
261
|
-
var shelf = this.shelves[index];
|
|
262
|
-
var bin = shelf.alloc(w, h, id);
|
|
263
|
-
this.bins[id] = bin;
|
|
264
|
-
this.ref(bin);
|
|
265
|
-
return bin;
|
|
266
|
-
};
|
|
267
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Shrink the width/height of the sprite to the bare minimum.
|
|
261
|
+
* Since shelf-pack doubles first width, then height when running out of shelf space
|
|
262
|
+
* this can result in fairly large unused space both in width and height if that happens
|
|
263
|
+
* towards the end of bin packing.
|
|
264
|
+
*/
|
|
265
|
+
shrink() {
|
|
266
|
+
if (this.shelves.length > 0) {
|
|
267
|
+
var w2 = 0;
|
|
268
|
+
var h2 = 0;
|
|
269
|
+
|
|
270
|
+
for (var j = 0; j < this.shelves.length; j++) {
|
|
271
|
+
var shelf = this.shelves[j];
|
|
272
|
+
h2 += shelf.h;
|
|
273
|
+
w2 = Math.max(shelf.w - shelf.free, w2);
|
|
274
|
+
}
|
|
268
275
|
|
|
269
|
-
|
|
270
|
-
* Shrink the width/height of the sprite to the bare minimum.
|
|
271
|
-
* Since shelf-pack doubles first width, then height when running out of shelf space
|
|
272
|
-
* this can result in fairly large unused space both in width and height if that happens
|
|
273
|
-
* towards the end of bin packing.
|
|
274
|
-
*/
|
|
275
|
-
ShelfPack.prototype.shrink = function() {
|
|
276
|
-
if (this.shelves.length > 0) {
|
|
277
|
-
var w2 = 0;
|
|
278
|
-
var h2 = 0;
|
|
279
|
-
|
|
280
|
-
for (var j = 0; j < this.shelves.length; j++) {
|
|
281
|
-
var shelf = this.shelves[j];
|
|
282
|
-
h2 += shelf.h;
|
|
283
|
-
w2 = Math.max(shelf.w - shelf.free, w2);
|
|
276
|
+
this.resize(w2, h2);
|
|
284
277
|
}
|
|
285
|
-
|
|
286
|
-
this.resize(w2, h2);
|
|
287
278
|
}
|
|
288
|
-
};
|
|
289
279
|
|
|
290
280
|
|
|
291
|
-
/**
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
281
|
+
/**
|
|
282
|
+
* Return a packed bin given its id, or undefined if the id is not found
|
|
283
|
+
*
|
|
284
|
+
* @param {number|string} id Unique identifier for this bin,
|
|
285
|
+
* @returns {Bin} The requested bin, or undefined if not yet packed
|
|
286
|
+
* @example
|
|
287
|
+
* var b = sprite.getBin('a');
|
|
288
|
+
*/
|
|
289
|
+
getBin(id) {
|
|
290
|
+
return this.bins[id];
|
|
291
|
+
}
|
|
302
292
|
|
|
303
293
|
|
|
304
|
-
/**
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Increment the ref count of a bin and update statistics.
|
|
296
|
+
*
|
|
297
|
+
* @param {Bin} bin Bin instance
|
|
298
|
+
* @returns {number} New refcount of the bin
|
|
299
|
+
* @example
|
|
300
|
+
* var bin = sprite.getBin('a');
|
|
301
|
+
* sprite.ref(bin);
|
|
302
|
+
*/
|
|
303
|
+
ref(bin) {
|
|
304
|
+
if (++bin.refcount === 1) { // a new Bin.. record height in stats historgram..
|
|
305
|
+
var h = bin.h;
|
|
306
|
+
this.stats[h] = (this.stats[h] | 0) + 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return bin.refcount;
|
|
317
310
|
}
|
|
318
311
|
|
|
319
|
-
return bin.refcount;
|
|
320
|
-
};
|
|
321
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Decrement the ref count of a bin and update statistics.
|
|
315
|
+
* The bin will be automatically marked as free space once the refcount reaches 0.
|
|
316
|
+
*
|
|
317
|
+
* @param {Bin} bin Bin instance
|
|
318
|
+
* @returns {number} New refcount of the bin
|
|
319
|
+
* @example
|
|
320
|
+
* var bin = sprite.getBin('a');
|
|
321
|
+
* sprite.unref(bin);
|
|
322
|
+
*/
|
|
323
|
+
unref(bin) {
|
|
324
|
+
if (bin.refcount === 0) {
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
322
327
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
* @returns {number} New refcount of the bin
|
|
329
|
-
* @example
|
|
330
|
-
* var bin = sprite.getBin('a');
|
|
331
|
-
* sprite.unref(bin);
|
|
332
|
-
*/
|
|
333
|
-
ShelfPack.prototype.unref = function(bin) {
|
|
334
|
-
if (bin.refcount === 0) {
|
|
335
|
-
return 0;
|
|
336
|
-
}
|
|
328
|
+
if (--bin.refcount === 0) {
|
|
329
|
+
this.stats[bin.h]--;
|
|
330
|
+
delete this.bins[bin.id];
|
|
331
|
+
this.freebins.push(bin);
|
|
332
|
+
}
|
|
337
333
|
|
|
338
|
-
|
|
339
|
-
this.stats[bin.h]--;
|
|
340
|
-
delete this.bins[bin.id];
|
|
341
|
-
this.freebins.push(bin);
|
|
334
|
+
return bin.refcount;
|
|
342
335
|
}
|
|
343
336
|
|
|
344
|
-
return bin.refcount;
|
|
345
|
-
};
|
|
346
337
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
};
|
|
338
|
+
/**
|
|
339
|
+
* Clear the sprite. Resets everything and resets statistics.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* sprite.clear();
|
|
343
|
+
*/
|
|
344
|
+
clear() {
|
|
345
|
+
this.shelves = [];
|
|
346
|
+
this.freebins = [];
|
|
347
|
+
this.stats = {};
|
|
348
|
+
this.bins = {};
|
|
349
|
+
this.maxId = 0;
|
|
350
|
+
}
|
|
361
351
|
|
|
362
352
|
|
|
363
|
-
/**
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Resize the sprite.
|
|
355
|
+
*
|
|
356
|
+
* @param {number} w Requested new sprite width
|
|
357
|
+
* @param {number} h Requested new sprite height
|
|
358
|
+
* @returns {boolean} `true` if resize succeeded, `false` if failed
|
|
359
|
+
* @example
|
|
360
|
+
* sprite.resize(256, 256);
|
|
361
|
+
*/
|
|
362
|
+
resize(w, h) {
|
|
363
|
+
this.w = w;
|
|
364
|
+
this.h = h;
|
|
365
|
+
for (var i = 0; i < this.shelves.length; i++) {
|
|
366
|
+
this.shelves[i].resize(w);
|
|
367
|
+
}
|
|
368
|
+
return true;
|
|
377
369
|
}
|
|
378
|
-
|
|
379
|
-
};
|
|
370
|
+
}
|
|
380
371
|
|
|
381
372
|
|
|
382
373
|
/**
|
|
383
374
|
* Create a new Shelf.
|
|
384
375
|
*
|
|
385
376
|
* @private
|
|
386
|
-
* @class Shelf
|
|
387
|
-
* @param {number} y Top coordinate of the new shelf
|
|
388
|
-
* @param {number} w Width of the new shelf
|
|
389
|
-
* @param {number} h Height of the new shelf
|
|
390
377
|
* @example
|
|
391
378
|
* var shelf = new Shelf(64, 512, 24);
|
|
392
379
|
*/
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
380
|
+
class Shelf {
|
|
381
|
+
/**
|
|
382
|
+
* @param {number} y Top coordinate of the new shelf
|
|
383
|
+
* @param {number} w Width of the new shelf
|
|
384
|
+
* @param {number} h Height of the new shelf
|
|
385
|
+
*/
|
|
386
|
+
constructor(y, w, h) {
|
|
387
|
+
this.x = 0;
|
|
388
|
+
this.y = y;
|
|
389
|
+
this.w = this.free = w;
|
|
390
|
+
this.h = h;
|
|
391
|
+
}
|
|
399
392
|
|
|
400
393
|
|
|
401
|
-
/**
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Allocate a single bin into the shelf.
|
|
396
|
+
*
|
|
397
|
+
* @private
|
|
398
|
+
* @param {number} w Width of the bin to allocate
|
|
399
|
+
* @param {number} h Height of the bin to allocate
|
|
400
|
+
* @param {number|string} id Unique id of the bin to allocate
|
|
401
|
+
* @returns {Bin} Bin object with `id`, `x`, `y`, `w`, `h` properties, or `null` if allocation failed
|
|
402
|
+
* @example
|
|
403
|
+
* shelf.alloc(12, 16, 'a');
|
|
404
|
+
*/
|
|
405
|
+
alloc(w, h, id) {
|
|
406
|
+
if (w > this.free || h > this.h) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
var x = this.x;
|
|
410
|
+
this.x += w;
|
|
411
|
+
this.free -= w;
|
|
412
|
+
return new Bin(id, x, this.y, w, h, w, this.h);
|
|
415
413
|
}
|
|
416
|
-
var x = this.x;
|
|
417
|
-
this.x += w;
|
|
418
|
-
this.free -= w;
|
|
419
|
-
return new Bin(id, x, this.y, w, h, w, this.h);
|
|
420
|
-
};
|
|
421
414
|
|
|
422
415
|
|
|
423
|
-
/**
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
416
|
+
/**
|
|
417
|
+
* Resize the shelf.
|
|
418
|
+
*
|
|
419
|
+
* @private
|
|
420
|
+
* @param {number} w Requested new width of the shelf
|
|
421
|
+
* @returns {boolean} true
|
|
422
|
+
* @example
|
|
423
|
+
* shelf.resize(512);
|
|
424
|
+
*/
|
|
425
|
+
resize(w) {
|
|
426
|
+
this.free += (w - this.w);
|
|
427
|
+
this.w = w;
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
437
431
|
|
|
438
432
|
|
|
439
433
|
/**
|
|
440
434
|
* Create a new Bin object.
|
|
441
435
|
*
|
|
442
|
-
* @class Bin
|
|
443
|
-
* @param {number|string} id Unique id of the bin
|
|
444
|
-
* @param {number} x Left coordinate of the bin
|
|
445
|
-
* @param {number} y Top coordinate of the bin
|
|
446
|
-
* @param {number} w Width of the bin
|
|
447
|
-
* @param {number} h Height of the bin
|
|
448
|
-
* @param {number} [maxw] Max width of the bin (defaults to `w` if not provided)
|
|
449
|
-
* @param {number} [maxh] Max height of the bin (defaults to `h` if not provided)
|
|
450
436
|
* @example
|
|
451
437
|
* var bin = new Bin('a', 0, 0, 12, 16);
|
|
452
438
|
*/
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
439
|
+
export class Bin {
|
|
440
|
+
/**
|
|
441
|
+
* @param {number|string} id Unique id of the bin
|
|
442
|
+
* @param {number} x Left coordinate of the bin
|
|
443
|
+
* @param {number} y Top coordinate of the bin
|
|
444
|
+
* @param {number} w Width of the bin
|
|
445
|
+
* @param {number} h Height of the bin
|
|
446
|
+
* @param {number} [maxw] Max width of the bin (defaults to `w` if not provided)
|
|
447
|
+
* @param {number} [maxh] Max height of the bin (defaults to `h` if not provided)
|
|
448
|
+
*/
|
|
449
|
+
constructor(id, x, y, w, h, maxw, maxh) {
|
|
450
|
+
this.id = id;
|
|
451
|
+
this.x = x;
|
|
452
|
+
this.y = y;
|
|
453
|
+
this.w = w;
|
|
454
|
+
this.h = h;
|
|
455
|
+
this.maxw = maxw || w;
|
|
456
|
+
this.maxh = maxh || h;
|
|
457
|
+
this.refcount = 0;
|
|
458
|
+
}
|
|
462
459
|
}
|
|
463
|
-
|