diff --git a/boxes/generators/smallpartstray.py b/boxes/generators/smallpartstray.py new file mode 100644 index 0000000..d625b03 --- /dev/null +++ b/boxes/generators/smallpartstray.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +# Copyright (C) 2013-2016 Florian Festi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes.edges import CompoundEdge + +class SmallPartsTray(Boxes): + """Tray with slants to easier get out game tokens or screws""" + + ui_group = "Tray" + ui_group = "Unstable" + + def __init__(self): + Boxes.__init__(self) + + self.addSettingsArgs(edges.FingerJointSettings) + # self.addSettingsArgs(edges.StackableSettings) + + self.buildArgParser(sx="50*3", y=100, h=30) + self.argparser.add_argument( + "--angle", action="store", type=float, default=45., + help="angle of the ramps") + self.argparser.add_argument( + "--rampheight", action="store", type=float, default=.5, + help="height of the ramps relative to to total height") + self.argparser.add_argument( + "--two_sided", action="store", type=boolarg, default=True, + help="have ramps on both sides") + self.argparser.add_argument( + "--front_panel", action="store", type=boolarg, default=True, + help="have a vertical wall at the ramp") + + + def innerWall(self, h, y, ramp_h, ramp_y, two_ramps, front=True, + edges="ffeff", move=None): + a = math.degrees(math.atan(ramp_h/ramp_y)) + l = (ramp_h**2 + ramp_y**2)**.5 + if two_ramps: + self.polygonWall( + [y-2*ramp_y, a, l, 90-a, h-ramp_h, 90, y, + 90, h-ramp_h, 90-a, l, a], + "fffeff" if front else "ffeeef", move=move) + else: + self.polygonWall( + [y-ramp_y, 90, h, 90, y, 90, h-ramp_h, 90-a, l, a], + "ffeff" if front else "ffeef", move=move) + + def outerWall(self, h, y, ramp_h, ramp_y, two_ramps, front=True, + edges="ffeff", move=None): + a = math.degrees(math.atan(ramp_h/ramp_y)) + l = (ramp_h**2 + ramp_y**2)**.5 + t = self.thickness + + def cb(): + with self.saved_context(): + self.moveTo(ramp_y, 0, 180-a) + self.fingerHolesAt(0, 0.5*t, l, 0) + if two_ramps: + self.moveTo(y-ramp_y, 0, a) + self.fingerHolesAt(0, -0.5*t, l, 0) + + if two_ramps: + self.rectangularWall( + y, h, + [CompoundEdge(self, "EFE", (ramp_y, y-2*ramp_y, ramp_y)), + CompoundEdge(self, "EF", (ramp_h, h-ramp_h)) if front else "e", + "e", + CompoundEdge(self, "FE", (h-ramp_h, ramp_h)) if front else "e"], + callback=[cb], move=move) + else: + self.rectangularWall( + y, h, [ + CompoundEdge(self, "EF", (ramp_y, y-ramp_y)) if front else "e", + "F", + "e", + CompoundEdge(self, "FE", (h-ramp_h, ramp_h))], + callback=[cb], move=move) + + + def holeCB(self, sections, height): + def CB(): + pos = -0.5 * self.thickness + for l in sections[:-1]: + pos += l + self.thickness + self.fingerHolesAt(pos, 0, height) + return CB + + def render_simple_tray_divider(self, width, height, move): + """ + Simple movable divider. A wall with small feet for a little more stability. + """ + + if self.move(width, height, move, True): + return + + t = self.thickness + self.moveTo(t) + self.polyline( + width - 2 * t, + 90, + t, + -90, + t, + 90, + height - t, + 90, + width, + 90, + height - t, + 90, + t, + -90, + t, + 90, + ) + + self.move(width, height, move) + + def render_simple_tray_divider_feet(self, move=None): + sqr2 = math.sqrt(2) + t = self.thickness + divider_foot_width = 2 * t + full_width = t + 2 * divider_foot_width + move_length = full_width / sqr2 + 2 * self.burn + move_width = full_width / sqr2 + 2 * self.burn + + if self.move(move_width, move_length, move, True): + return + + self.moveTo(self.burn) + self.ctx.save() + self.polyline( + sqr2 * divider_foot_width, + 135, + t, + -90, + t, + -90, + t, + 135, + sqr2 * divider_foot_width, + 135, + full_width, + 135, + ) + self.ctx.restore() + + self.moveTo(-self.burn / sqr2, self.burn * (1 + 1 / sqr2), 45) + self.moveTo(full_width) + + self.polyline( + 0, + 135, + sqr2 * divider_foot_width, + 135, + t, + -90, + t, + -90, + t, + 135, + sqr2 * divider_foot_width, + 135, + ) + + self.move(move_width, move_length, move) + + + def render(self): + # adjust to the variables you want in the local scope + sx, y, h = self.sx, self.y, self.h + t = self.thickness + a = self.angle + b = "e" + + x = sum(sx) + (len(sx)-1) * t + + ramp_h = h * self.rampheight + ramp_y = ramp_h / math.tan(math.radians(a)) + + if self.two_sided and (2*ramp_y + 3*t > y): + ramp_y = (y - 3*t) / 2 + ramp_h = ramp_y * math.tan(math.radians(a)) + elif ramp_y > y - t: + ramp_y = y - t + ramp_h = ramp_y * math.tan(math.radians(a)) + + ramp_l = (ramp_h**2 + ramp_y**2)**.5 + + with self.saved_context(): + self.outerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="up") + self.outerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="mirror up") + for i in range(len(sx)-1): + self.innerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="up") + self.innerWall(h, y, ramp_h, ramp_y, + self.two_sided, self.front_panel, move="right only") + + if self.front_panel: + self.rectangularWall( + x, h-ramp_h, "efef", + callback=[self.holeCB(sx, h-ramp_h)], move="up") + self.rectangularWall(x, ramp_l, "efef", + callback=[self.holeCB(sx, ramp_l)], move="up") + if self.two_sided: + self.rectangularWall( + x, y-2*ramp_y, "efef", + callback=[self.holeCB(sx, y-2*ramp_y)], move="up") + self.rectangularWall( + x, ramp_l, "efef", + callback=[self.holeCB(sx, ramp_l)], move="up") + if self.front_panel: + self.rectangularWall( + x, h-ramp_h, "efef", + callback=[self.holeCB(sx, h-ramp_h)], move="up") + else: + self.rectangularWall( + x, y-ramp_y, "efff", + callback=[self.holeCB(sx, y-ramp_y)], move="up") + self.rectangularWall( + x, h, "Ffef", + callback=[self.holeCB(sx, h)], move="up") + + + if self.two_sided: + for l in self.sx: + self.render_simple_tray_divider(l, h, move="right") + + self.partsMatrix(len(self.sx), 0, "right", self.render_simple_tray_divider_feet)