bin_packing 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/bin_packing.rb +19 -0
- data/lib/bin_packing/bin.rb +180 -0
- data/lib/bin_packing/box.rb +29 -0
- data/lib/bin_packing/error.rb +4 -0
- data/lib/bin_packing/export.rb +15 -0
- data/lib/bin_packing/export_binding.rb +18 -0
- data/lib/bin_packing/free_space_box.rb +12 -0
- data/lib/bin_packing/heuristics/base.rb +38 -0
- data/lib/bin_packing/heuristics/best_area_fit.rb +15 -0
- data/lib/bin_packing/heuristics/best_long_side_fit.rb +13 -0
- data/lib/bin_packing/heuristics/best_short_side_fit.rb +13 -0
- data/lib/bin_packing/heuristics/bottom_left.rb +12 -0
- data/lib/bin_packing/packer.rb +34 -0
- data/lib/bin_packing/resources/export.html.erb +51 -0
- data/lib/bin_packing/score.rb +43 -0
- data/lib/bin_packing/score_board.rb +64 -0
- data/lib/bin_packing/score_board_entry.rb +19 -0
- data/lib/bin_packing/version.rb +3 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 729c8a0a3ebeb449568d02a4ec00f7a4ecc4a574
|
4
|
+
data.tar.gz: 7c53d882f799e197c921245ac9135c2b3ccc7b9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5efb09d2832044bbd32fa250945eb7d7c0108df2c992a56c62ea85312b9dd3d76a262afc5c4570dc53dd5fe7fa47804da34aea47a1d8e69c8f33b4ba642e37c5
|
7
|
+
data.tar.gz: 3d708805897abef9f009edb147d383f6b71654d9ed59d24abe20caab6632cbf6ac17c26dcc559e3218d2a297eba2dc40f5294e421b3ae754432f7f51281f7807
|
data/lib/bin_packing.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bin_packing/error'
|
2
|
+
require 'bin_packing/box'
|
3
|
+
require 'bin_packing/free_space_box'
|
4
|
+
require 'bin_packing/bin'
|
5
|
+
require 'bin_packing/score'
|
6
|
+
require 'bin_packing/score_board_entry'
|
7
|
+
require 'bin_packing/score_board'
|
8
|
+
require 'bin_packing/heuristics/base'
|
9
|
+
require 'bin_packing/heuristics/best_area_fit'
|
10
|
+
require 'bin_packing/heuristics/best_long_side_fit'
|
11
|
+
require 'bin_packing/heuristics/best_short_side_fit'
|
12
|
+
require 'bin_packing/heuristics/bottom_left'
|
13
|
+
require 'bin_packing/packer'
|
14
|
+
require 'bin_packing/export_binding'
|
15
|
+
require 'bin_packing/export'
|
16
|
+
require 'bin_packing/version'
|
17
|
+
|
18
|
+
module BinPacking
|
19
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module BinPacking
|
2
|
+
class Bin
|
3
|
+
attr_reader :width, :height, :boxes, :heuristic
|
4
|
+
|
5
|
+
def initialize(width, height, heuristic = nil)
|
6
|
+
@width = width
|
7
|
+
@height = height
|
8
|
+
|
9
|
+
@boxes = []
|
10
|
+
box = BinPacking::FreeSpaceBox.new(width, height)
|
11
|
+
@free_rectangles = [box]
|
12
|
+
|
13
|
+
@heuristic = heuristic || BinPacking::Heuristics::BestShortSideFit.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def efficiency
|
17
|
+
boxes_area = 0
|
18
|
+
@boxes.each { |box| boxes_area += box.area }
|
19
|
+
boxes_area * 100 / area
|
20
|
+
end
|
21
|
+
|
22
|
+
def insert(box)
|
23
|
+
return false if box.packed?
|
24
|
+
|
25
|
+
@heuristic.find_position_for_new_node!(box, @free_rectangles)
|
26
|
+
return false unless box.packed?
|
27
|
+
|
28
|
+
num_rectangles_to_process = @free_rectangles.size
|
29
|
+
i = 0
|
30
|
+
while i < num_rectangles_to_process
|
31
|
+
if split_free_node(@free_rectangles[i], box)
|
32
|
+
@free_rectangles.delete_at(i)
|
33
|
+
num_rectangles_to_process -= 1
|
34
|
+
else
|
35
|
+
i += 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
prune_free_list
|
40
|
+
|
41
|
+
@boxes << box
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def insert!(box)
|
46
|
+
unless insert(box)
|
47
|
+
raise ArgumentError, "Could not insert box #{box.inspect} "\
|
48
|
+
"into bin #{inspect}."
|
49
|
+
end
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def score_for(box)
|
54
|
+
@heuristic.find_position_for_new_node!(box.clone, @free_rectangles)
|
55
|
+
end
|
56
|
+
|
57
|
+
def is_larger_than?(box)
|
58
|
+
(@width >= box.width && @height >= box.height) ||
|
59
|
+
(@height >= box.width && @width >= box.height)
|
60
|
+
end
|
61
|
+
|
62
|
+
def label
|
63
|
+
"#{@width}x#{@height} #{efficiency}%"
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def area
|
69
|
+
@width * @height
|
70
|
+
end
|
71
|
+
|
72
|
+
def place_rect(node)
|
73
|
+
num_rectangles_to_process = @free_rectangles.size
|
74
|
+
i = 0
|
75
|
+
while i < num_rectangles_to_process
|
76
|
+
if split_free_node(@free_rectangles[i], node)
|
77
|
+
@free_rectangles.delete_at(i)
|
78
|
+
num_rectangles_to_process -= 1
|
79
|
+
else
|
80
|
+
i += 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
prune_free_list
|
85
|
+
|
86
|
+
@boxes << node
|
87
|
+
end
|
88
|
+
|
89
|
+
def split_free_node(free_node, used_node)
|
90
|
+
# Test with SAT if the rectangles even intersect.
|
91
|
+
if used_node.x >= free_node.x + free_node.width ||
|
92
|
+
used_node.x + used_node.width <= free_node.x ||
|
93
|
+
used_node.y >= free_node.y + free_node.height ||
|
94
|
+
used_node.y + used_node.height <= free_node.y
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
|
98
|
+
try_split_free_node_vertically(free_node, used_node)
|
99
|
+
|
100
|
+
try_split_free_node_horizontally(free_node, used_node)
|
101
|
+
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
def try_split_free_node_vertically(free_node, used_node)
|
106
|
+
if used_node.x < free_node.x + free_node.width && used_node.x + used_node.width > free_node.x
|
107
|
+
try_leave_free_space_at_top(free_node, used_node)
|
108
|
+
try_leave_free_space_at_bottom(free_node, used_node)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def try_leave_free_space_at_top(free_node, used_node)
|
113
|
+
if used_node.y > free_node.y && used_node.y < free_node.y + free_node.height
|
114
|
+
new_node = free_node.clone
|
115
|
+
new_node.height = used_node.y - new_node.y
|
116
|
+
@free_rectangles << new_node
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def try_leave_free_space_at_bottom(free_node, used_node)
|
121
|
+
if used_node.y + used_node.height < free_node.y + free_node.height
|
122
|
+
new_node = free_node.clone
|
123
|
+
new_node.y = used_node.y + used_node.height
|
124
|
+
new_node.height = free_node.y + free_node.height - (used_node.y + used_node.height)
|
125
|
+
@free_rectangles << new_node
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def try_split_free_node_horizontally(free_node, used_node)
|
130
|
+
if used_node.y < free_node.y + free_node.height && used_node.y + used_node.height > free_node.y
|
131
|
+
try_leave_free_space_on_left(free_node, used_node)
|
132
|
+
try_leave_free_space_on_right(free_node, used_node)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def try_leave_free_space_on_left(free_node, used_node)
|
137
|
+
if used_node.x > free_node.x && used_node.x < free_node.x + free_node.width
|
138
|
+
new_node = free_node.clone
|
139
|
+
new_node.width = used_node.x - new_node.x
|
140
|
+
@free_rectangles << new_node
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def try_leave_free_space_on_right(free_node, used_node)
|
145
|
+
if used_node.x + used_node.width < free_node.x + free_node.width
|
146
|
+
new_node = free_node.clone
|
147
|
+
new_node.x = used_node.x + used_node.width
|
148
|
+
new_node.width = free_node.x + free_node.width - (used_node.x + used_node.width)
|
149
|
+
@free_rectangles << new_node
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Goes through the free rectangle list and removes any redundant entries.
|
154
|
+
def prune_free_list
|
155
|
+
i = 0
|
156
|
+
while i < @free_rectangles.size
|
157
|
+
j = i + 1
|
158
|
+
while j < @free_rectangles.size
|
159
|
+
if is_contained_in?(@free_rectangles[i], @free_rectangles[j])
|
160
|
+
@free_rectangles.delete_at(i)
|
161
|
+
i -= 1
|
162
|
+
break
|
163
|
+
end
|
164
|
+
if is_contained_in?(@free_rectangles[j], @free_rectangles[i])
|
165
|
+
@free_rectangles.delete_at(j)
|
166
|
+
else
|
167
|
+
j += 1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
i += 1
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def is_contained_in?(rect_a, rect_b)
|
175
|
+
return rect_a.x >= rect_b.x && rect_a.y >= rect_b.y &&
|
176
|
+
rect_a.x+rect_a.width <= rect_b.x+rect_b.width &&
|
177
|
+
rect_a.y+rect_a.height <= rect_b.y+rect_b.height
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module BinPacking
|
2
|
+
class Box
|
3
|
+
attr_accessor :width, :height, :x, :y, :packed
|
4
|
+
|
5
|
+
def initialize(width, height)
|
6
|
+
@width = width
|
7
|
+
@height = height
|
8
|
+
@x = 0
|
9
|
+
@y = 0
|
10
|
+
@packed = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def area
|
14
|
+
@area ||= @width * @height
|
15
|
+
end
|
16
|
+
|
17
|
+
def rotate
|
18
|
+
@width, @height = [@height, @width]
|
19
|
+
end
|
20
|
+
|
21
|
+
def packed?
|
22
|
+
@packed
|
23
|
+
end
|
24
|
+
|
25
|
+
def label
|
26
|
+
"#{@width}x#{@height} at [#{@x},#{@y}]"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BinPacking
|
2
|
+
class Export
|
3
|
+
def initialize(*bins)
|
4
|
+
@bins = Array(bins).flatten
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_html(options = {})
|
8
|
+
template_path = File.expand_path('../resources/export.html.erb', __FILE__)
|
9
|
+
template = File.read(template_path)
|
10
|
+
binding = ExportBinding.new(@bins, options[:zoom] || 1)
|
11
|
+
html = ERB.new(template).result(binding.get_binding)
|
12
|
+
html
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module BinPacking
|
2
|
+
module Heuristics
|
3
|
+
class Base
|
4
|
+
def find_position_for_new_node!(box, free_rectangles)
|
5
|
+
best_score = BinPacking::Score.new
|
6
|
+
width = box.width
|
7
|
+
height = box.height
|
8
|
+
|
9
|
+
free_rectangles.each do |free_rect|
|
10
|
+
try_place_rect_in(free_rect, box, width, height, best_score)
|
11
|
+
try_place_rect_in(free_rect, box, height, width, best_score)
|
12
|
+
end
|
13
|
+
|
14
|
+
best_score
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def try_place_rect_in(free_rect, box, rect_width, rect_height, best_score)
|
20
|
+
if free_rect.width >= rect_width && free_rect.height >= rect_height
|
21
|
+
score = calculate_score(free_rect, rect_width, rect_height)
|
22
|
+
if score > best_score
|
23
|
+
box.x = free_rect.x
|
24
|
+
box.y = free_rect.y
|
25
|
+
box.width = rect_width
|
26
|
+
box.height = rect_height
|
27
|
+
box.packed = true
|
28
|
+
best_score.assign(score)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def calculate_score(free_rect, rect_width, rect_height)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BinPacking
|
2
|
+
module Heuristics
|
3
|
+
class BestAreaFit < BinPacking::Heuristics::Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def calculate_score(free_rect, rect_width, rect_height)
|
7
|
+
area_fit = free_rect.width * free_rect.height - rect_width * rect_height
|
8
|
+
leftover_horiz = (free_rect.width - rect_width).abs
|
9
|
+
leftover_vert = (free_rect.height - rect_height).abs
|
10
|
+
short_side_fit = [leftover_horiz, leftover_vert].min
|
11
|
+
BinPacking::Score.new(area_fit, short_side_fit)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module BinPacking
|
2
|
+
module Heuristics
|
3
|
+
class BestLongSideFit < BinPacking::Heuristics::Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def calculate_score(free_rect, rect_width, rect_height)
|
7
|
+
leftover_horiz = (free_rect.width - rect_width).abs
|
8
|
+
leftover_vert = (free_rect.height - rect_height).abs
|
9
|
+
BinPacking::Score.new(*[leftover_horiz, leftover_vert].sort.reverse)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module BinPacking
|
2
|
+
module Heuristics
|
3
|
+
class BestShortSideFit < BinPacking::Heuristics::Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def calculate_score(free_rect, rect_width, rect_height)
|
7
|
+
leftover_horiz = (free_rect.width - rect_width).abs
|
8
|
+
leftover_vert = (free_rect.height - rect_height).abs
|
9
|
+
BinPacking::Score.new(*[leftover_horiz, leftover_vert].sort)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module BinPacking
|
2
|
+
module Heuristics
|
3
|
+
class BottomLeft < BinPacking::Heuristics::Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def calculate_score(free_rect, rect_width, rect_height)
|
7
|
+
top_side_y = free_rect.y + rect_height
|
8
|
+
BinPacking::Score.new(top_side_y, free_rect.x)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module BinPacking
|
2
|
+
class Packer
|
3
|
+
def initialize(bins)
|
4
|
+
@bins = bins
|
5
|
+
@unpacked_boxes = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def pack(boxes, options = {})
|
9
|
+
packed_boxes = []
|
10
|
+
boxes = boxes.reject(&:packed?)
|
11
|
+
return packed_boxes if boxes.none?
|
12
|
+
|
13
|
+
limit = options[:limit] || BinPacking::Score::MAX_INT
|
14
|
+
board = BinPacking::ScoreBoard.new(@bins, boxes)
|
15
|
+
while entry = board.best_fit
|
16
|
+
entry.bin.insert!(entry.box)
|
17
|
+
board.remove_box(entry.box)
|
18
|
+
board.recalculate_bin(entry.bin)
|
19
|
+
packed_boxes << entry.box
|
20
|
+
break if packed_boxes.size >= limit
|
21
|
+
end
|
22
|
+
|
23
|
+
packed_boxes
|
24
|
+
end
|
25
|
+
|
26
|
+
def pack!(boxes)
|
27
|
+
packed_boxes = pack(boxes)
|
28
|
+
if packed_boxes.size != boxes.size
|
29
|
+
raise ArgumentError, "#{boxes.size - packed_boxes.size} boxes not "\
|
30
|
+
"packed into #{@bins.size} bins!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<style>
|
5
|
+
body {
|
6
|
+
margin: 0;
|
7
|
+
}
|
8
|
+
|
9
|
+
.bin {
|
10
|
+
position: relative;
|
11
|
+
margin: 20px;
|
12
|
+
background: #eee;
|
13
|
+
}
|
14
|
+
|
15
|
+
.bin .label {
|
16
|
+
position: absolute;
|
17
|
+
bottom: 5px;
|
18
|
+
right: 5px;
|
19
|
+
}
|
20
|
+
|
21
|
+
.box {
|
22
|
+
position: absolute;
|
23
|
+
border: 1px solid white;
|
24
|
+
background: #0a0;
|
25
|
+
box-sizing: border-box;
|
26
|
+
}
|
27
|
+
|
28
|
+
.box .box-label {
|
29
|
+
position: absolute;
|
30
|
+
top: 5px;
|
31
|
+
left: 5px;
|
32
|
+
color: #fff;
|
33
|
+
}
|
34
|
+
</style>
|
35
|
+
</head>
|
36
|
+
<body>
|
37
|
+
<div>
|
38
|
+
<% bins.each do |bin| %>
|
39
|
+
<div class="bin" style="width: <%= zoom(bin.width) %>px; height: <%= zoom(bin.height) %>px;">
|
40
|
+
<div>
|
41
|
+
<% bin.boxes.each do |box| %>
|
42
|
+
<div class="box" style="width: <%= zoom(box.width) %>px; height: <%= zoom(box.height) %>px; left: <%= zoom(box.x) %>px; top: <%= zoom(box.y) %>px;">
|
43
|
+
<span class="box-label"><%= box.label %></span>
|
44
|
+
</div>
|
45
|
+
<% end %>
|
46
|
+
</div>
|
47
|
+
<span class="label"><%= bin.label %></span>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
</body>
|
51
|
+
</html>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module BinPacking
|
2
|
+
class Score
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
MAX_INT = (2**(0.size * 8 -2) -1)
|
6
|
+
|
7
|
+
attr_reader :score_1, :score_2
|
8
|
+
|
9
|
+
def self.new_blank
|
10
|
+
new
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(score_1 = nil, score_2 = nil)
|
14
|
+
@score_1 = score_1 || MAX_INT
|
15
|
+
@score_2 = score_2 || MAX_INT
|
16
|
+
end
|
17
|
+
|
18
|
+
# Smaller number is greater (used by original algorithm).
|
19
|
+
def <=>(other)
|
20
|
+
if self.score_1 > other.score_1 || (self.score_1 == other.score_1 && self.score_2 > other.score_2)
|
21
|
+
-1
|
22
|
+
elsif self.score_1 < other.score_1 || (self.score_1 == other.score_1 && self.score_2 < other.score_2)
|
23
|
+
1
|
24
|
+
else
|
25
|
+
0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def assign(other)
|
30
|
+
@score_1 = other.score_1
|
31
|
+
@score_2 = other.score_2
|
32
|
+
end
|
33
|
+
|
34
|
+
def is_blank?
|
35
|
+
@score_1 == MAX_INT
|
36
|
+
end
|
37
|
+
|
38
|
+
def decrease_by(delta)
|
39
|
+
@score_1 += delta
|
40
|
+
@score_2 += delta
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# box_1 box_2 box_3 ...
|
2
|
+
# bin_1 100 200 0
|
3
|
+
# bin_2 0 5 0
|
4
|
+
# bin_3 9 100 0
|
5
|
+
# ...
|
6
|
+
module BinPacking
|
7
|
+
class ScoreBoard
|
8
|
+
def initialize(bins, boxes)
|
9
|
+
@entries = []
|
10
|
+
bins.each do |bin|
|
11
|
+
add_bin_entries(bin, boxes)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def any?
|
16
|
+
@entries.any?
|
17
|
+
end
|
18
|
+
|
19
|
+
def largest_not_fiting_box
|
20
|
+
unfit = nil
|
21
|
+
fitting_boxes = Set.new(@entries.select(&:fit?).map(&:box))
|
22
|
+
@entries.each do |entry|
|
23
|
+
next if fitting_boxes.include?(entry.box)
|
24
|
+
unfit = entry if unfit.nil? || unfit.box.area < entry.box.area
|
25
|
+
end
|
26
|
+
unfit.try(:box)
|
27
|
+
end
|
28
|
+
|
29
|
+
def best_fit
|
30
|
+
best = nil
|
31
|
+
@entries.each do |entry|
|
32
|
+
next unless entry.fit?
|
33
|
+
best = entry if best.nil? || best.score < entry.score
|
34
|
+
end
|
35
|
+
best
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_box(box)
|
39
|
+
@entries.delete_if { |e| e.box == box }
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_bin(bin)
|
43
|
+
add_bin_entries(bin, current_boxes)
|
44
|
+
end
|
45
|
+
|
46
|
+
def recalculate_bin(bin)
|
47
|
+
@entries.select { |e| e.bin == bin }.each(&:calculate)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def add_bin_entries(bin, boxes)
|
53
|
+
boxes.each do |box|
|
54
|
+
entry = BinPacking::ScoreBoardEntry.new(bin, box)
|
55
|
+
entry.calculate
|
56
|
+
@entries << entry
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_boxes
|
61
|
+
@entries.map(&:box).uniq
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module BinPacking
|
2
|
+
class ScoreBoardEntry
|
3
|
+
attr_reader :bin, :box, :score
|
4
|
+
|
5
|
+
def initialize(bin, box)
|
6
|
+
@bin = bin
|
7
|
+
@box = box
|
8
|
+
@score = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def calculate
|
12
|
+
@score = @bin.score_for(@box)
|
13
|
+
end
|
14
|
+
|
15
|
+
def fit?
|
16
|
+
!@score.is_blank?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bin_packing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MAK IT
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.0
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
description: |
|
34
|
+
Provides algorithm for placing rectangles (box-es) in one or multiple rectangular areas (bins) with reasonable allocation efficiency.
|
35
|
+
email: info@makit.lv
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/bin_packing.rb
|
41
|
+
- lib/bin_packing/bin.rb
|
42
|
+
- lib/bin_packing/box.rb
|
43
|
+
- lib/bin_packing/error.rb
|
44
|
+
- lib/bin_packing/export.rb
|
45
|
+
- lib/bin_packing/export_binding.rb
|
46
|
+
- lib/bin_packing/free_space_box.rb
|
47
|
+
- lib/bin_packing/heuristics/base.rb
|
48
|
+
- lib/bin_packing/heuristics/best_area_fit.rb
|
49
|
+
- lib/bin_packing/heuristics/best_long_side_fit.rb
|
50
|
+
- lib/bin_packing/heuristics/best_short_side_fit.rb
|
51
|
+
- lib/bin_packing/heuristics/bottom_left.rb
|
52
|
+
- lib/bin_packing/packer.rb
|
53
|
+
- lib/bin_packing/resources/export.html.erb
|
54
|
+
- lib/bin_packing/score.rb
|
55
|
+
- lib/bin_packing/score_board.rb
|
56
|
+
- lib/bin_packing/score_board_entry.rb
|
57
|
+
- lib/bin_packing/version.rb
|
58
|
+
homepage: http://rubygems.org/gems/bin_packing
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.2.2
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: 2D bin packing algorithm
|
82
|
+
test_files: []
|