Skip to content

Instantly share code, notes, and snippets.

@buhii
Created October 3, 2015 19:30
Show Gist options
  • Save buhii/8d7f5cb058fe9b6c97f2 to your computer and use it in GitHub Desktop.
Save buhii/8d7f5cb058fe9b6c97f2 to your computer and use it in GitHub Desktop.
Simple 2D packing for PlotDevice
# -*- coding: utf-8 -*-
"""
Simple 2D packing for PlotDevice by Takahiro Kamatani
Based on https://pypi.python.org/pypi/ortoolpy by Saito Tsutomu
License: Python Software Foundation License
"""
class Cut(object):
V = True
H = False
class Plane(object):
def __init__(self, w, h, x=0, y=0):
assert w > 0 and h > 0
self.w = w
self.h = h
self.x = x
self.y = y
self.rgba = (random(), random(), random(), 0.5)
def __repr__(self):
return "<Plane %dx%d @%d,%d>" % (self.w, self.h, self.x, self.y)
def __eq__(self, other):
return self.w == other.w and self.h == other.h
def __lt__(self, other):
"Can I cover whole other plane?"
return self.w < other.w or self.h < other.h
def __le__(self, other):
return self < other or self == other
def __sub__(self, other):
if self >= other:
pass
else:
print "something wrong", self, other
assert self >= other
return self.w * self.h - other.w * other.h
def cut(self, depth, cut_dir):
if cut_dir == Cut.H:
if depth == self.h:
return [self]
elif depth < self.h:
return [
Plane(self.w, depth,
self.x, self.y),
Plane(self.w, self.h - depth,
self.x, self.y + depth)
]
elif cut_dir == Cut.V:
if depth == self.w:
return [self]
elif depth < self.w:
return [
Plane(depth, self.h,
self.x, self.y),
Plane(self.w - depth, self.h,
self.x + depth, self.y)
]
return []
def guillotine(self, other, first_cut_dir):
cut_order = [first_cut_dir, (not first_cut_dir)]
if first_cut_dir == Cut.V:
depth_order = [other.w, other.h]
else:
depth_order = [other.h, other.w]
planes = self.cut(depth_order[0], cut_order[0])
planes = planes[0].cut(depth_order[1], cut_order[1]) + [planes[1]]
return planes
def max_offset_guillotine(self, other):
if self < other:
return []
if self == other:
return [self]
offset_v = (self.w - other.w) * self.h
offset_w = (self.h - other.h) * self.w
if offset_w < offset_v:
return self.guillotine(other, Cut.V)
else:
return self.guillotine(other, Cut.H)
def __mod__(self, other):
return self.max_offset_guillotine(other)
def draw(self, filled=True):
nofill()
strokewidth(1)
stroke(0.3, 0.3, 0.3, 0.5)
rect(self.x, self.y, self.w - 1, self.h - 1)
if filled:
nostroke()
fill(*self.rgba)
rect(self.x, self.y, self.w, self.h)
class Container(object):
def __init__(self, w, h, x=0, y=0):
self.w = w
self.h = h
self.x = x
self.y = y
self.planes = [Plane(w, h, 0, 0)]
self.fixed = []
def __repr__(self):
return repr(self.planes)
def __gt__(self, other):
return any(filter(lambda p: p > other, self.planes))
def __imod__(self, other):
assert isinstance(other, Plane)
most_similar = self.find_most_similar_plane(other)
if not most_similar: # could not find most similar plane
return self
del self.planes[self.planes.index(most_similar)]
fragments = most_similar % other
for p in fragments:
if p == other:
self.fixed.append(p)
else:
self.planes.append(p)
return self
def rate(self):
fixed_area = sum(map(lambda p: p.w * p.h, self.planes))
return self.w * self.h / float(fixed_area)
def find_most_similar_plane(self, other):
min_diff = 1e100
result = None
for p in self.planes:
if p < other:
continue
if (p - other) < min_diff:
min_diff = p - other
result = p
return result
def draw(self):
translate(self.x, self.y)
for p in self.planes:
p.draw(filled=False)
for p in self.fixed:
p.draw()
translate(-self.x, -self.y)
class Solver(object):
def __init__(self, container, items):
self.new_container = lambda: Container(container.w, container.h)
self.items = items
self.container = self.new_container()
def solve(self):
container = self.new_container()
for item in shuffled(self.items):
container %= item
if self.container.rate() < container.rate():
self.container = container
def draw(self):
self.container.draw()
# --- main ---
size(300, 300)
speed(10)
NUM_ITEMS = 100
def setup(env):
container = Container(280, 280)
items = [Plane(random(50, 200), random(50, 200)) for _ in range(NUM_ITEMS)]
env.solver = Solver(
container=container,
items=items
)
def draw(env):
translate(10, 10)
env.solver.solve()
env.solver.draw()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment