Source code for boxes.generators.compartmentbox

# Copyright (C) 2013-2014 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 <http://www.gnu.org/licenses/>.

from boxes import *
from boxes.generators.typetray import TypeTray


[docs] class CompartmentBox(TypeTray): """Type tray variation with sliding lid""" description = """Sliding lid rests on inner walls, so will not work if no inner walls are present. Suggested to place walls close to both sides for maximum stability. Margin helps to prevent the lid from getting stuck. Vertical margin increases the overall height. The lip holding the lid in place can be generated as two separate pieces or as a single piece that continues at the back. ![Closed](static/samples/CompartmentBox-closed.jpg) ![Half open](static/samples/CompartmentBox-lid.jpg) """ ui_group = "Tray" def __init__(self) -> None: Boxes.__init__(self) # avoid TypeTray.__init__ self.addSettingsArgs(edges.StackableSettings) self.buildArgParser("sx", "sy", "h", "outside", "bottom_edge") self.argparser.add_argument( "--handle", action="store", type=str, default="lip", choices={"none","lip","hole"}, help="how to grab the lid to remove") self.argparser.add_argument( "--radius", action="store", type=float, default=10, dest="radius", help="radius of the grip hole in mm") self.argparser.add_argument( "--holes", action="store", type=str, default="70", help="width of hole(s) in percentage of maximum hole width") self.argparser.add_argument( "--margin_t", action="store", type=float, default=0.1, dest="margin_vertical", help="vertical margin for sliding lid (multiples of thickness)") self.argparser.add_argument( "--margin_s", action="store", type=float, default=0.05, dest="margin_side", help="margin to add at both sides of sliding lid (multiples of thickness)") self.argparser.add_argument( "--split_lip", action="store", type=boolarg, default=True, help="create two strips to reduce waste material") def render(self): t = self.thickness k = self.burn b = self.bottom_edge stackable = b == "s" tside, tback = ["Š","S"] if stackable else ["F","E"] # top edges margin_side = self.margin_side margin_vertical = self.margin_vertical * t if (margin_vertical < 0): raise ValueError("vertical margin can not be negative") if (margin_side < 0): raise ValueError("side margin can not be negative") split_lip = self.split_lip if not split_lip: tback = tside if self.outside: self.sx = self.adjustSize(self.sx) self.sy = self.adjustSize(self.sy) self.h = self.adjustSize(self.h, b, tside) - 1 * t - margin_vertical self.hi = self.h x = sum(self.sx) + self.thickness * (len(self.sx) - 1) y = sum(self.sy) + self.thickness * (len(self.sy) - 1) h = self.h # x walls self.ctx.save() # outer walls - front/back hb = h + t + margin_vertical if stackable: hb += self.edges["S"].settings.holedistance + (t if split_lip else -t) self.rectangularWall(x, hb, [b, "F", tback, "F"], callback=[self.xHoles], ignore_widths=[1,2,5,6], move="up", label="back") self.rectangularWall(x, h, [b, "F", "e", "F"], callback=[self.mirrorX(self.xHoles, x)], ignore_widths=[1,6], move="up", label="front") # floor if b != "e": self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots], move="up", label="bottom") # Inner x walls be = "f" if b != "e" else "e" for i in range(len(self.sy) - 1): e = [edges.SlottedEdge(self, self.sx, be), "f", edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * h), "f"] self.rectangularWall(x, h, e, move="up", label=f"inner x {i+1}") # top / lid handle = self.handle x_compensated = x - 2*margin_side*t # margin at both sides (left, right) if handle == "lip": #compensate for the lid being a bit lower due to the margin, goal is to keep top at same height lip_height = (0 if stackable else t) + margin_vertical/2 if (stackable): #compensate for the stackable edge extra height lip_height += self.edges["S"].settings.holedistance #get this value from the settings # correct the stackable edge for the length lost at the ends s = copy.deepcopy(self.edges["S"].settings) # get value from settings and make change s.setValues(self.thickness, width = self.edges["S"].settings.width/self.thickness - margin_side) s.edgeObjects(self, chars="aA") # this seems to be correct self.rectangularWall(x_compensated, y, "feee", move="up", label="lid") self.rectangularWall(x_compensated, lip_height, "Fe" + ("A" if stackable else "e") + "e", move="up", label="lid lip") if handle == "hole": self.rectangularWall(x_compensated, y + t, move="up", label="lid", callback=[self.gripHole]) if handle == "none": self.rectangularWall(x_compensated, y + t, move="up", label="lid") self.ctx.restore() self.rectangularWall(x, h, "ffff", move="right only") # y walls # outer walls - left/right f = edges.CompoundEdge(self, "fE", [h+self.edges[b].startwidth(), t+margin_vertical]) self.rectangularWall(y, h+t+margin_vertical, [b, f, tside, "f"], callback=[self.yHoles, ], ignore_widths=[1,5,6], move="up", label="left side") self.rectangularWall(y, h+t+margin_vertical, [b, f, tside, "f"], callback=[self.yHoles, ], ignore_widths=[1,5,6], move="mirror up", label="right side") # inner y walls for i in range(len(self.sx) - 1): e = [edges.SlottedEdge(self, self.sy, be, slots=0.5 * h), "f", "e", "f"] self.rectangularWall(y, h, e, move="up", label=f"inner y {i+1}") # lip that holds the lid in place lip_front_edge = "e" if self.handle == "lip" else "E" if split_lip: self.rectangularWall(y, t, "eef" + lip_front_edge, move="up", label="Lip Left") self.rectangularWall(y, t, "eef" + lip_front_edge, move="mirror up", label="Lip Right") else: tx = y + self.edges.get('f').spacing() + self.edges.get(lip_front_edge).spacing() ty = x + 2 * self.edges.get('f').spacing() r=k # as sharp as possible without removing additional material from the part self.move(tx, ty, "up", before=True) self.moveTo(self.edges.get("f").margin(), self.edges.get("f").margin()) self.edges.get("f")(y) self.edgeCorner("f", lip_front_edge) self.edges.get(lip_front_edge)(t) self.edgeCorner(lip_front_edge, "e") self.edge(y-t-r) self.corner(-90, radius=r) self.edge(x-(t+r)*2) self.corner(-90, radius=r) self.edge(y-t-r) self.edgeCorner("e", lip_front_edge) self.edges.get(lip_front_edge)(t) self.edgeCorner(lip_front_edge, "f") self.edges.get('f')(y) self.corner(90) self.edges.get('f')(x) self.corner(90) self.move(tx, ty, "up", label="Lip") def gripHole(self): if not self.radius: return radius = self.radius t = self.thickness widths = argparseSections(self.holes) x = sum(self.sx) + self.thickness * (len(self.sx) - 1) if sum(widths) > 0: if sum(widths) < 100: slot_offset = ((1 - sum(widths) / 100) * (x - (len(widths) + 1) * self.thickness)) / (len(widths) * 2) else: slot_offset = 0 slot_height = 2* radius slot_x = self.thickness + slot_offset for w in widths: if sum(widths) > 100: slotwidth = w / sum(widths) * (x - (len(widths) + 1) * self.thickness) else: slotwidth = w / 100 * (x - (len(widths) + 1) * self.thickness) slot_x += slotwidth / 2 with self.saved_context(): self.rectangularHole(slot_x,radius+t,slotwidth,slot_height,radius,True,True) slot_x += slotwidth / 2 + slot_offset + self.thickness + slot_offset