binpack 0.0.1

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in binpack.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,61 @@
1
+ # Introduction
2
+ This is a gem adapted from a solution for 2-dimensional bin packing by Ilmari Heikkinen. From the post:
3
+
4
+ > It's a greedy heuristic algorithm that tries to fit each box into the trunk, largest first.
5
+ > If no box fits, a new trunk is created.
6
+ > I'm representing trunks as 2D tables of squares (with one unit of hidden padding around), then fill it starting from top left.
7
+ > The fitterfirst finds a row with enough empty space to fit the box and then checks if the next box-height rows also contain the space.
8
+ > If they do, the box is drawn to the rows.
9
+ > Printing is easy because the trunk already is in printable format.
10
+
11
+ ## Usage
12
+ # Create an array of random items
13
+ items = []
14
+ 12.times do |i|
15
+ items << Binpack::Item.new("Associated object #{i+1}", (rand(10)+2)/2.0, (rand(10)+2)/2.0)
16
+ end
17
+
18
+ # Pack the array of items into a bin where the default bin size is 16x10 with a padding of 1
19
+ bins = Binpack::Bin.pack(items, [], Binpack::Bin.new(16, 10, 1))
20
+
21
+
22
+ Visual output example:
23
+
24
+ puts bins.join("\n\n")
25
+
26
+ 22222_0000_11_cc
27
+ 22222_0000_11_cc
28
+ 22222_0000_11_cc
29
+ ___________11_cc
30
+ 99_888_aaa____cc
31
+ 99_888__________
32
+ 99_____b_aaa____
33
+ 99_0_7_b________
34
+ 99_0_7_b_0______
35
+ ___0_7___0______
36
+
37
+ Array of items, their locations locations, and whether or not they had to be rotated 90º
38
+
39
+ puts bins[0].items.inspect
40
+
41
+ [
42
+ [#<Binpack::Item:0x007f84bb14c5c0 @obj="Associated object 12", @width=5.0, @height=3.0, @rotated=false>, 1, 1],
43
+ [#<Binpack::Item:0x007f84bb031438 @obj="Associated object 1", @width=4.5, @height=3.0, @rotated=false>, 7, 1],
44
+ [#<Binpack::Item:0x007f84bb14c6b0 @obj="Associated object 11", @width=2.5, @height=4.5, @rotated=false>, 12, 1],
45
+ [#<Binpack::Item:0x007f84bb031000 @obj="Associated object 3", @width=2.0, @height=5.0, @rotated=false>, 15, 1],
46
+ [#<Binpack::Item:0x007f84bb030dd0 @obj="Associated object 4", @width=2.0, @height=5.0, @rotated=false>, 1, 5],
47
+ [#<Binpack::Item:0x007f84bb14c890 @obj="Associated object 9", @width=3.5, @height=2.5, @rotated=false>, 4, 5],
48
+ [#<Binpack::Item:0x007f84bb14c980 @obj="Associated object 8", @width=3.5, @height=1.5, @rotated=false>, 8, 5],
49
+ [#<Binpack::Item:0x007f84bb030b00 @obj="Associated object 5", @width=1.5, @height=3.0, @rotated=false>, 4, 8],
50
+ [#<Binpack::Item:0x007f84bb0311e0 @obj="Associated object 2", @width=1.0, @height=4.0, @rotated=false>, 6, 8],
51
+ [#<Binpack::Item:0x007f84bb14c7a0 @obj="Associated object 10", @width=1.0, @height=3.5, @rotated=false>, 8, 7],
52
+ [#<Binpack::Item:0x007f84bb14cb60 @obj="Associated object 6", @width=3.5, @height=1.0, @rotated=false>, 10, 7],
53
+ [#<Binpack::Item:0x007f84bb02a070 @obj="Associated object 7", @width=1.0, @height=2.5, @rotated=true>, 10, 9]
54
+ ]
55
+
56
+ ## Notes
57
+
58
+ 1. The packing assumes a border of @padding. If you wish to put images on an 11"x17"
59
+ piece of paper with a 1" border, you will need to make the bin 16x10 with a padding of 1.
60
+ 2. When layout out your objects, be sure to note whether or not the item had to be rotated to fit.
61
+ 3. The algorithm uses string matching to determine placement so if higher precision than integral is required, you'll need to use a multiplier on everything.
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "binpack/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "binpack"
7
+ s.version = Binpack::VERSION
8
+ s.authors = ["Kelley Reynolds"]
9
+ s.email = ["kelley.reynolds@rubyscale.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{2 dimensional bin packing library}
12
+ s.description = %q{2 dimensional bin packing library adapted from a RubyQuiz solution by Ilmari Heikkinen}
13
+
14
+ s.rubyforge_project = "binpack"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,77 @@
1
+ #require "binpack/version"
2
+
3
+ module Binpack
4
+ class Item
5
+ attr_reader :width, :height, :rotated, :obj
6
+
7
+ def initialize(obj, width, height, rotated=false)
8
+ @obj, @width, @height, @rotated = obj, width, height, rotated
9
+ end
10
+
11
+ def rotate
12
+ self.class.new(@obj, @height, @width, !@rotated)
13
+ end
14
+ end
15
+
16
+ class Bin
17
+ attr_reader :width, :height, :padding, :items
18
+
19
+ def initialize(width, height, padding=1)
20
+ @width, @height, @padding = width, height, padding.to_i
21
+ @items = []
22
+ @rows = (@padding..@height + (@padding * 2)).map{ "_"*(@width + (@padding * 2)) }
23
+ end
24
+
25
+ def add(item)
26
+ try_adding(item) or try_adding(item.rotate)
27
+ end
28
+ alias_method "<<".to_sym, :add
29
+
30
+ def try_adding(item)
31
+ itemrow = "_" * (item.width + (@padding * 2))
32
+ @rows.each_with_index {|r,i|
33
+ break if i > @rows.size - (item.width + @padding * 2)
34
+ next unless r.include?(itemrow)
35
+ idxs = @rows[i + @padding, item.height + @padding].map { |s| s.index(itemrow) }
36
+ next unless idxs.all?
37
+ idx = idxs.max
38
+ next unless @rows[i, item.height + (@padding*2)].all? { |s| s[idx,itemrow.size] == itemrow }
39
+ g = rand(16).to_s(16)
40
+ @rows[i + @padding, item.height].each{ |s|
41
+ s[idx + @padding, item.width] = "#{g}" * item.width
42
+ }
43
+ @items.push([item, idx + @padding, i + @padding])
44
+ return item
45
+ }
46
+ nil
47
+ end
48
+
49
+ def empty?
50
+ @items.empty?
51
+ end
52
+
53
+ def to_s
54
+ @rows[@padding..(-1 - @padding)].map{ |r| r[@padding..(-1 - @padding)] }.join("\n")
55
+ end
56
+
57
+ def self.pack(items, bins=[], default_bin=nil)
58
+ default_bin ||= self.new(16, 10)
59
+ raise "Expected an array" if !bins.kind_of?(Array)
60
+
61
+ items = items.sort_by { |item| item.width*item.height }.reverse
62
+ bins << self.new(default_bin.width, default_bin.height, default_bin.padding) if bins.empty?
63
+ until items.empty?
64
+ fitting = items.find { |item| bins.last.add item }
65
+ if fitting
66
+ items.delete_at(items.index(fitting))
67
+ elsif bins.last.empty?
68
+ raise "Can't fit #{items.inspect} into the bins"
69
+ else
70
+ bins << self.new(default_bin.width, default_bin.height, default_bin.padding) unless items.empty?
71
+ end
72
+ end
73
+
74
+ bins
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,3 @@
1
+ module Binpack
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binpack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kelley Reynolds
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-24 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: 2 dimensional bin packing library adapted from a RubyQuiz solution by
15
+ Ilmari Heikkinen
16
+ email:
17
+ - kelley.reynolds@rubyscale.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - Rakefile
25
+ - Readme.md
26
+ - binpack.gemspec
27
+ - lib/binpack.rb
28
+ - lib/binpack/version.rb
29
+ homepage: ''
30
+ licenses: []
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project: binpack
49
+ rubygems_version: 1.8.15
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: 2 dimensional bin packing library
53
+ test_files: []