Created
October 3, 2015 19:30
-
-
Save buhii/8d7f5cb058fe9b6c97f2 to your computer and use it in GitHub Desktop.
Revisions
-
buhii created this gist
Oct 3, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,216 @@ # -*- 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()