diff --git a/index.js b/index.js index 5d005c5..847094d 100644 --- a/index.js +++ b/index.js @@ -10,8 +10,15 @@ module.exports = function(items, options) { // Clone the items. var newItems = items.map(function(item) { return inPlace ? item : { width: item.width, height: item.height, item: item }; }); + // We need to know whether the widest item exceeds the maximum width. + // The optimal sorting strategy changes depending on this. + const widestWidth = + newItems.sort(function(a, b) { return b.width - a.width})[0].width + const widerThanMax = options.maxWidth && (widestWidth > options.maxWidth) + newItems = newItems.sort(function(a, b) { - if (options.maxWidth && !options.maxHeight) return b.width - a.width + if (options.maxWidth && !options.maxHeight && widerThanMax) return b.width - a.width + if (options.maxWidth && !options.maxHeight) return b.height - a.height if (options.maxHeight) return b.height - a.height // TODO: check that each actually HAS a width and a height. // Sort based on the size (area) of each block. diff --git a/package.json b/package.json index bdf22d9..202bc88 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bin-pack-with-constraints", - "version": "1.0.1", + "version": "1.0.2", "description": "A packing algorithm for 2D bin packing that allows setting a max width or height. Largely based on code and a blog post by Jake Gordon and Bryan Burgers.", "author": { "name": "Enoch Riese", diff --git a/packer.growing.js b/packer.growing.js index 01b9f62..8ed613e 100644 --- a/packer.growing.js +++ b/packer.growing.js @@ -33,7 +33,8 @@ Construction: { maxWidth = Infinity: set a max width to constrain the growth in that direction maxHeight = Infinity: set a max height to constrain the growth in that direction - TODO strictMax = false: reject blocks that are larger than the max + strictMax = false: require wider-than-max blocks to start at left boundary + PREVIOUS INTENDED TODO strictMax = false: reject blocks that are larger than the max } Inputs: @@ -76,7 +77,7 @@ Example: var GrowingPacker = function({maxWidth = Infinity, maxHeight = Infinity, strictMax = false}) { this.maxWidth = maxWidth this.maxHeight = maxHeight - // this.strictMax = strictMax + this.strictMax = strictMax this.ensureSquare = maxWidth === Infinity && maxHeight === Infinity }; @@ -87,7 +88,10 @@ GrowingPacker.prototype = { if (len === 0) { return } var n, node, block, fit; - var width = this.strictMax && (this.maxWidth < Infinity) ? this.maxWidth : blocks[0].width; + // Require wider-than-max blocks to start at the left boundary, so + // they encroach past the right boundary minimally. + var width = this.strictMax && (this.maxWidth < Infinity) ? Math.max(blocks[0].width, this.maxWidth) : blocks[0].width; + var height = this.strictMax && (this.maxHeight < Infinity) ? this.maxHeight : blocks[0].height; this.root = { x: 0, y: 0, width, height }; for (n = 0; n < len ; n++) { diff --git a/test/test.js b/test/test.js index 99e2805..4ca33d3 100644 --- a/test/test.js +++ b/test/test.js @@ -217,8 +217,48 @@ describe('bin-pack with maxWidth option', function() { assert.equal(result.items.length, bins.length, "Result has same amount of items as the source"); verifyResult(result, result.items); }) +}) + +describe('with strictMax: true', function() { + it('packs items that exceed the maximum width at the left edge', function() { + var bins = [ + { width: 100, height: 10 }, + { width: 110, height: 10 }, + ]; + + var result = pack(bins, {maxWidth: 90, strictMax: true}); + assert.ok('items' in result, "Result has items"); + assert.equal(result.items.length, bins.length, "Result has same amount of items as the source"); + assert.ok(result.width === 110, 'Width is that of the widest item') + verifyResult(result, result.items); + }) - describe('with strictMax: true', function() { - it('rejects bins that exceed the maximum') + it('does not unnecessarily pack items to exceed the maximum width', function() { + var bins = [ + { width: 10, height: 110 }, + { width: 10, height: 110 }, + { width: 10, height: 110 }, + { width: 40, height: 48 }, + { width: 40, height: 48 }, + { width: 40, height: 48 }, + { width: 50, height: 48 }, + { width: 50, height: 48 }, + { width: 50, height: 48 }, + { width: 60, height: 48 }, + { width: 60, height: 48 }, + { width: 70, height: 48 }, + { width: 70, height: 48 }, + { width: 30, height: 48 }, + { width: 30, height: 48 }, + { width: 30, height: 48 }, + ]; + + var result = pack(bins, {maxWidth: 60, strictMax: true}); + assert.ok('items' in result, "Result has items"); + assert.equal(result.items.length, bins.length, "Result has same amount of items as the source"); + assert.ok(result.width === 70, 'Width is that of the widest item') + verifyResult(result, result.items); }) + + it('rejects bins that exceed the maximum') })