Skip to content

Commit

Permalink
Merge pull request #38 from selaux/add-bin-packing-layout-algorithm
Browse files Browse the repository at this point in the history
Add bin-packing layout algorithm.
  • Loading branch information
selaux committed Jun 5, 2015
2 parents 2ebb71e + 6ff85eb commit 9ceba8a
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ __pixelRatio__ (Type: `Integer` Default: `1`): Specifies the pixelRatio for reti
#### options.layout
Type: `String|Function`
Default value: `'vertical'`
The layout that is used to generate the sprite. The built-in layouts are `'vertical'`, `'horizontal'` and `'diagonal'`. You can also specify a function that generates a custom layout (see more at [extending node-sprite-generator](https://github.com/selaux/node-sprite-generator#extending-node-sprite-generator)).
The layout that is used to generate the sprite. The built-in layouts are `'packed'` (for bin-packing), `'vertical'`, `'horizontal'` and `'diagonal'`. You can also specify a function that generates a custom layout (see more at [extending node-sprite-generator](https://github.com/selaux/node-sprite-generator#extending-node-sprite-generator)).

#### options.layoutOptions
Type: `Object`
Expand Down
37 changes: 37 additions & 0 deletions lib/layout/packed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

var _ = require('underscore'),
binPack = require('bin-pack'),
defaultOptions = require('./utils/defaultOptions'),
scaleImages = require('./utils/scaleImages');

module.exports = function generateLayout(images, options, callback) {
var packed;

options = _.extend({}, defaultOptions, options);

images = scaleImages(images, options);
images = _.map(images, function (image) {
image.width += options.padding;
image.height += options.padding;
return image;
});

packed = binPack(images);
images = _.map(packed.items, function (image) {
var paddingOffset = options.padding / 2;

return _.extend({}, image.item, {
x: image.x + paddingOffset,
y: image.y + paddingOffset,
width: image.width - options.padding,
height: image.height - options.padding
});
});

callback(null, {
width: packed.width,
height: packed.height,
images: images
});
};
1 change: 1 addition & 0 deletions lib/nsg.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var async = require('async'),
providedLayouts = {
'diagonal': require('./layout/diagonal'),
'horizontal': require('./layout/horizontal'),
'packed': require('./layout/packed'),
'vertical': require('./layout/vertical')
},
providedCompositors = {},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "lib/nsg.js",
"dependencies": {
"async": "1.2.0",
"bin-pack": "1.0.1",
"glob": "5.0.10",
"underscore": "1.8.2"
},
Expand Down
74 changes: 74 additions & 0 deletions test/specs/layout/packed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use strict';

var expect = require('chai').expect,
_ = require('underscore'),
packed = require('../../../lib/layout/packed.js');

describe('Layout/Packed', function () {
var images = [
{ path: 'foo', width: 20, height: 20, data: 'image1' },
{ path: 'bar', width: 10, height: 10, data: 'image2' },
{ path: 'bla', width: 10, height: 10, data: 'image3' }
];

it('should generate the correct layout without any options', function (done) {
var options = {};

packed(images, options, function (err, layout) {
expect(err).not.to.be.ok;
expect(options).to.deep.equal({});
expect(layout).to.deep.equal({
width: 30,
height: 20,
images: [
_({ x: 0, y: 0 }).extend(images[0]),
_({ x: 20, y: 0 }).extend(images[1]),
_({ x: 20, y: 10 }).extend(images[2])
]
});
expect(layout.images[0]).not.to.equal(images[0]);
done();
});
});

it('should generate the correct layout when a padding is specified', function (done) {
var options = { padding: 10 };

packed(images, options, function (err, layout) {
expect(err).not.to.be.ok;
expect(options).to.deep.equal({ padding: 10 });
expect(layout).to.deep.equal({
width: 50,
height: 50,
images: [
_({ x: 5, y: 5 }).extend(images[0]),
_({ x: 35, y: 5 }).extend(images[1]),
_({ x: 5, y: 35 }).extend(images[2])
]
});
expect(layout.images[0]).not.to.equal(images[0]);
done();
});
});

it('should generate the correct layout when a scaling is specified', function (done) {
var options = { scaling: 0.5 };

packed(images, options, function (err, layout) {
expect(err).not.to.be.ok;
expect(options).to.deep.equal({ scaling: 0.5 });
expect(layout).to.deep.equal({
width: 15,
height: 10,
images: [
_.extend({}, images[0], { x: 0, y: 0, width: 10, height: 10 }),
_.extend({}, images[1], { x: 10, y: 0, width: 5, height: 5 }),
_.extend({}, images[2], { x: 10, y: 5, width: 5, height: 5 })
]
});
expect(layout.images[0]).not.to.equal(images[0]);
done();
});
});

});

0 comments on commit 9ceba8a

Please sign in to comment.