Source code for boxes.generators.can_storage

# 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 <http://www.gnu.org/licenses/>.

from boxes import *


class FrontEdge(edges.BaseEdge):
    char = "a"

    def __call__(self, length, **kw):
        x = math.ceil( ((self.canDiameter * 0.5 + 2 * self.thickness) * math.sin(math.radians(self.chuteAngle))) / self.thickness)
        if self.top_edge != "e":
            self.corner(90, self.thickness)
            self.edge(0.5 * self.canDiameter)
            self.corner(-90, 0.25 * self.canDiameter)
        else:
            self.moveTo(-self.burn, self.canDiameter + self.thickness, -90)
            self.corner(90, 0.25 * self.canDiameter)
            self.edge(self.thickness)

        self.edge(0.5 * self.canDiameter - self.thickness)
        self.corner(-90, 0.25 * self.canDiameter)
        self.edge(0.5 * self.canDiameter)
        self.corner(90, self.thickness)
        self.edge(x * self.thickness )
        self.corner(90, self.thickness)
        self.edge(0.5 * self.canDiameter)
        self.corner(-90, 0.25 * self.canDiameter)
        self.edge(0.5 * self.canDiameter - (1 + x) * self.thickness + self.top_chute_height + self.bottom_chute_height - self.barrier_height)
        self.corner(-90, 0.25 * self.canDiameter)
        self.edge(0.5 * self.canDiameter)
        self.corner(90, self.thickness)
        self.edge(self.barrier_height)
        self.edge(self.thickness)

class TopChuteEdge(edges.BaseEdge):
    char = "b"

    def __call__(self, length, **kw):
        self.edge(0.2 * length - self.thickness)
        self.corner(90, self.thickness)
        self.edge(1.5*self.canDiameter - 2 * self.thickness)
        self.corner(-90, self.thickness)
        self.edge(0.6 * length - 2 * self.thickness)
        self.corner(-90, self.thickness)
        self.edge(1.5*self.canDiameter - 2 * self.thickness)
        self.corner(90, self.thickness)
        self.edge(0.2 * length - self.thickness)

class BarrierEdge(edges.BaseEdge):
    char = "A"

    def __call__(self, length, **kw):
        self.edge(0.2*length)
        self.corner(90,self.thickness/2)
        self.corner(-90,self.thickness/2)
        self.edge(0.6*length-2*self.thickness)
        self.corner(-90,self.thickness/2)
        self.corner(90,self.thickness/2)
        self.edge(0.2*length)

    def startwidth(self) -> float:
        return self.boxes.thickness

[docs] class CanStorage(Boxes): """Storage box for round containers""" description = """ for AA batteries: ![CanStorage for AA batteries](static/samples/CanStorageAA.jpg) for canned tomatoes: """ ui_group = "Misc" def __init__(self) -> None: Boxes.__init__(self) self.addSettingsArgs(edges.FingerJointSettings, finger=2.0, space=2.0, surroundingspaces=0.0) self.addSettingsArgs(edges.StackableSettings) self.addSettingsArgs(fillHolesSettings) self.argparser.add_argument( "--top_edge", action="store", type=ArgparseEdgeType("efhŠ"), choices=list("efhŠ"), default="Š", help="edge type for top edge") self.argparser.add_argument( "--bottom_edge", action="store", type=ArgparseEdgeType("eEš"), choices=list("eEš"), default="š", help="edge type for bottom edge") # Add non default cli params if needed (see argparse std lib) self.argparser.add_argument( "--canDiameter", action="store", type=float, default=75, help="outer diameter of the cans to be stored (in mm)") self.argparser.add_argument( "--canHeight", action="store", type=float, default=110, help="height of the cans to be stored (in mm)") self.argparser.add_argument( "--canNum", action="store", type=int, default=12, help="number of cans to be stored") self.argparser.add_argument( "--chuteAngle", action="store", type=float, default=5.0, help="slope angle of the chutes") def DrawPusher(self, dbg = False): with self.saved_context(): if dbg == False: self.moveTo(0,self.thickness) self.edge(0.25*self.pusherA) self.corner(-90) self.edge(self.thickness) self.corner(90) self.edge(0.5*self.pusherA) self.corner(90) self.edge(self.thickness) self.corner(-90) self.edge(0.25*self.pusherA) self.corner(90-self.chuteAngle) self.edge(0.25*self.pusherB) self.corner(-90) self.edge(self.thickness) self.corner(90) self.edge(0.5*self.pusherB) self.corner(90) self.edge(self.thickness) self.corner(-90) self.edge(0.25*self.pusherB) self.corner(90+self.pusherAngle+self.chuteAngle) self.edge(self.pusherC) def cb_top_chute(self, nr): if nr == 0: # fill with holes border = [ (0, 0), (self.top_chute_depth, 0), (self.top_chute_depth, 0.2 * self.width - self.thickness), (self.top_chute_depth - self.thickness, 0.2 * self.width), (self.top_chute_depth - 1.5*self.canDiameter, 0.2 * self.width), (self.top_chute_depth - 1.5*self.canDiameter, 0.8 * self.width), (self.top_chute_depth - self.thickness, 0.8 * self.width), (self.top_chute_depth, 0.8 * self.width + self.thickness), (self.top_chute_depth, self.width), (0, self.width), ] if self.fillHoles_fill_pattern != "no fill": self.fillHoles( pattern="hbar", border=border, max_radius = min(2*self.thickness, self.fillHoles_hole_max_radius) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/30), hspace=min(2*self.thickness, self.fillHoles_space_between_holes) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bspace=min(2*self.thickness, self.fillHoles_space_to_border) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bar_length=self.fillHoles_bar_length, max_random=self.fillHoles_max_random, ) def cb_top(self, nr): if nr == 0: # fill with holes border = [ (0, 0), (self.depth, 0), (self.depth, self.width), (0, self.width), ] if self.fillHoles_fill_pattern != "no fill": self.fillHoles( pattern="hbar", border=border, max_radius = min(2*self.thickness, self.fillHoles_hole_max_radius) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/30), hspace=min(2*self.thickness, self.fillHoles_space_between_holes) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bspace=min(2*self.thickness, self.fillHoles_space_to_border) if self.fillHoles_fill_pattern in ["hbar", "vbar"] else min(2*self.thickness, self.width/20), bar_length=self.fillHoles_bar_length, max_random=self.fillHoles_max_random, ) def cb_bottom_chute(self, nr): if nr == 1: # holes for pusher self.rectangularHole(self.width*0.85-0.5*self.thickness, 0.25*self.pusherA, self.thickness, 0.5*self.pusherA, center_x=False, center_y=False) self.rectangularHole(self.width*0.5 -0.5*self.thickness, 0.25*self.pusherA, self.thickness, 0.5*self.pusherA, center_x=False, center_y=False) self.rectangularHole(self.width*0.15-0.5*self.thickness, 0.25*self.pusherA, self.thickness, 0.5*self.pusherA, center_x=False, center_y=False) def cb_back(self, nr): if nr == 1: # holes for pusher self.rectangularHole(self.width*0.85-0.5*self.thickness, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle)) + 0.25*self.pusherB, self.thickness, 0.5*self.pusherB + self.thickness, center_x=False, center_y=False) self.rectangularHole(self.width*0.5 -0.5*self.thickness, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle)) + 0.25*self.pusherB, self.thickness, 0.5*self.pusherB + self.thickness, center_x=False, center_y=False) self.rectangularHole(self.width*0.15-0.5*self.thickness, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle)) + 0.25*self.pusherB, self.thickness, 0.5*self.pusherB + self.thickness, center_x=False, center_y=False) def cb_sides(self, nr): if nr == 0: # for debugging only if self.debug: # draw orientation points self.hole(0, 0, 1, color=Color.ANNOTATIONS) self.hole(0, self.thickness, 1, color=Color.ANNOTATIONS) self.hole(0, self.thickness + self.canDiameter, 1, color=Color.ANNOTATIONS) self.hole(0, self.thickness + self.canDiameter + self.bottom_chute_height, 1, color=Color.ANNOTATIONS) self.hole(0, self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness, 1, color=Color.ANNOTATIONS) self.hole(0, self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness + self.canDiameter, 1, color=Color.ANNOTATIONS) self.hole(0, self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness + self.canDiameter + 1.0 * self.thickness, 1, color=Color.ANNOTATIONS) with self.saved_context(): # draw cans, bottom row self.moveTo(0, self.thickness, self.chuteAngle) self.rectangularHole(2*self.thickness, 0, math.ceil(self.canNum / 2) * self.canDiameter, self.canDiameter, center_x=False, center_y=False, color=Color.ANNOTATIONS) for i in range(math.ceil(self.canNum / 2)-1): self.hole(2*self.thickness+(0.5 + i) * self.canDiameter, self.canDiameter / 2, self.canDiameter / 2, color=Color.ANNOTATIONS) i+=1 self.hole(2*self.thickness+(0.5 + i) * self.canDiameter, self.canDiameter*0.8 , self.canDiameter / 2, color=Color.ANNOTATIONS) with self.saved_context(): # draw pusher self.moveTo(self.depth-self.pusherA, self.thickness + (self.depth-self.pusherA) * math.tan(math.radians(self.chuteAngle))) self.moveTo(0,0,self.chuteAngle) self.DrawPusher(True) with self.saved_context(): # draw cans, top row self.moveTo(0, self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + 0.5 * self.thickness, -self.chuteAngle) self.rectangularHole(0, 0.5 * self.thickness, math.ceil(self.canNum / 2) * self.canDiameter, self.canDiameter, center_x=False, center_y=False, color=Color.ANNOTATIONS) for i in range(math.ceil(self.canNum / 2)): self.hole((0.5 + i) * self.canDiameter, self.canDiameter / 2 + 0.5 * self.thickness, self.canDiameter / 2, color=Color.ANNOTATIONS) with self.saved_context(): # draw barrier self.moveTo(1.5 * self.thickness, 1.1 * self.thickness + self.burn + math.sin(math.radians(self.chuteAngle)) * 2 * self.thickness, 90) self.rectangularHole(0, 0, self.barrier_height, self.thickness, center_x=False, center_y=True, color=Color.ANNOTATIONS) # bottom chute with self.saved_context(): self.moveTo(0, 0.5 * self.thickness, self.chuteAngle) self.fingerHolesAt(0, 0, self.depth / math.cos(math.radians(self.chuteAngle)), 0) # top chute with self.saved_context(): self.moveTo(0, self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + 0.5 * self.thickness, -self.chuteAngle) self.fingerHolesAt(0, 0, self.top_chute_depth, 0) # front barrier with self.saved_context(): self.moveTo(1.5 * self.thickness, 1.1 * self.thickness + self.burn + math.sin(math.radians(self.chuteAngle)) * 2 * self.thickness, 90) self.fingerHolesAt(0, 0, self.barrier_height, 0) # fill with holes border = [ (2*self.thickness, 0.5*self.thickness + 2*self.thickness * math.tan(math.radians(self.chuteAngle)) + 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), (self.depth, self.thickness + self.depth * math.tan(math.radians(self.chuteAngle))), (self.depth, self.height), (self.thickness + 0.75 * self.canDiameter, self.height), (self.thickness + 0.75 * self.canDiameter, 0.5*self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness - (self.thickness + 0.75 * self.canDiameter) * math.tan(math.radians(self.chuteAngle)) + 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), (self.top_chute_depth * math.cos(math.radians(self.chuteAngle)), self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + self.thickness - (self.top_chute_depth) * math.sin(math.radians(self.chuteAngle))), (self.top_chute_depth * math.cos(math.radians(self.chuteAngle)), self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height - (self.top_chute_depth) * math.sin(math.radians(self.chuteAngle))), (self.thickness + 0.75 * self.canDiameter, 1.5*self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height - (self.thickness + 0.75 * self.canDiameter) * math.tan(math.radians(self.chuteAngle)) - 0.5*self.thickness/math.cos(math.radians(self.chuteAngle))), (self.thickness + 0.75 * self.canDiameter, 2*self.thickness + self.barrier_height ), (2*self.thickness, 2*self.thickness + self.barrier_height), ] self.fillHoles( pattern=self.fillHoles_fill_pattern, border=border, max_radius=self.fillHoles_hole_max_radius, hspace=self.fillHoles_space_between_holes, bspace=self.fillHoles_space_to_border, min_radius=self.fillHoles_hole_min_radius, style=self.fillHoles_hole_style, bar_length=self.fillHoles_bar_length, max_random=self.fillHoles_max_random, ) def render(self): self.chuteAngle = self.chuteAngle self.pusherAngle = 30 # angle of pusher self.pusherA = 0.75 * self.canDiameter # length of pusher self.pusherB = self.pusherA / math.sin(math.radians(180 - (90+self.chuteAngle) - self.pusherAngle)) * math.sin(math.radians(self.pusherAngle)) self.pusherC = self.pusherA / math.sin(math.radians(180 - (90+self.chuteAngle) - self.pusherAngle)) * math.sin(math.radians(90+self.chuteAngle)) self.addPart(FrontEdge(self, self)) self.addPart(TopChuteEdge(self, self)) self.addPart(BarrierEdge(self, self)) if self.canDiameter < 8 * self.thickness: self.edges["f"].settings.setValues(self.thickness, True, finger=1.0) self.edges["f"].settings.setValues(self.thickness, True, space=1.0) self.edges["f"].settings.setValues(self.thickness, True, surroundingspaces=0.0) if self.canDiameter < 4 * self.thickness: raise ValueError("Can diameter has to be at least 4 times the material thickness!") if self.canNum < 4: raise ValueError("4 cans is the minimum!") self.depth = self.canDiameter * (math.ceil(self.canNum / 2) + 0.1) + self.thickness self.top_chute_height = max(self.depth * math.sin(math.radians(self.chuteAngle)), 0.1 * self.canDiameter) self.top_chute_depth = (self.depth - 1.1 * self.canDiameter) / math.cos(math.radians(self.chuteAngle)) self.bottom_chute_height = max((self.depth - 1.1 * self.canDiameter) * math.sin(math.radians(self.chuteAngle)), 0.1 * self.canDiameter) self.bottom_chute_depth = self.depth / math.cos(math.radians(self.chuteAngle)) self.barrier_height = min( 0.25 * self.canDiameter, self.bottom_chute_height + self.top_chute_height - self.thickness) if (self.top_chute_depth + self.bottom_chute_height - self.thickness) < (self.barrier_height + self.canDiameter * 0.1): self.bottom_chute_height = self.barrier_height + self.canDiameter * 0.1 + self.thickness - self.top_chute_depth self.height = self.thickness + self.canDiameter + self.bottom_chute_height + self.top_chute_height + 0.5 * self.thickness + self.canDiameter + 1.5 * self.thickness # measurements from bottom to top self.width = 0.01 * self.canHeight + self.canHeight + 0.01 * self.canHeight edgs = self.bottom_edge + "h" + self.top_edge + "a" # render your parts here self.rectangularWall(self.depth, self.height, edges=edgs, callback=self.cb_sides, move="up", label="right") self.rectangularWall(self.depth, self.height, edges=edgs, callback=self.cb_sides, move="up mirror", label="left") self.rectangularWall(self.bottom_chute_depth, self.width, "fefe", callback=self.cb_bottom_chute, move="up", label="bottom chute") self.rectangularWall(self.top_chute_depth, self.width, "fbfe", callback=self.cb_top_chute, move="up", label="top chute") self.rectangularWall(self.barrier_height, self.width, "fAfe", move="right", label="barrier") self.rectangularWall(self.height, self.width, "fefe", callback=self.cb_back, move="up", label="back") self.rectangularWall(self.barrier_height, self.width, "fefe", move="left only", label="invisible") if self.top_edge != "e": self.rectangularWall(self.depth, self.width, "fefe", callback=self.cb_top, move="up", label="top") pusherH = self.pusherB * math.cos(math.radians(self.chuteAngle)) + self.thickness pusherV = self.pusherC * math.cos(math.radians(self.chuteAngle)) + self.thickness self.move(pusherV, pusherH, where ="right", before=True, label="Pusher") self.DrawPusher() self.move(pusherV, pusherH, where ="right", before=False, label="Pusher") self.move(pusherV, pusherH, where ="right", before=True, label="Pusher") self.DrawPusher() self.move(pusherV, pusherH, where ="right", before=False, label="Pusher") self.move(pusherV, pusherH, where ="up", before=True, label="Pusher") self.DrawPusher() self.text("Glue the Pusher pieces into slots on bottom\nand back plates to prevent stuck cans.", pusherV+3,0, fontsize=4, color=Color.ANNOTATIONS) self.move(pusherV, pusherH, where ="up", before=False, label="Pusher") self.move(pusherV, pusherH, where ="left only", before=True, label="Pusher") self.move(pusherV, pusherH, where ="left only", before=True, label="Pusher") if self.bottom_edge == "š": self.rectangularWall(self.edges["š"].settings.width+3*self.thickness, self.edges["š"].settings.height-4*self.burn, "eeee", move="right", label="Stabilizer 1") self.rectangularWall(self.edges["š"].settings.width+3*self.thickness, self.edges["š"].settings.height-4*self.burn, "eeee", move="right", label="Stabilizer 2") self.rectangularWall(self.edges["š"].settings.width+5*self.thickness, self.edges["š"].settings.height-4*self.burn, "eeee", move="right", label="Stabilizer 3") self.rectangularWall(self.edges["š"].settings.width+5*self.thickness, self.edges["š"].settings.height-4*self.burn, "eeee", move="right", label="Stabilizer 4") self.text("Glue a stabilizer on the inside of each bottom\nside stacking foot for lateral stabilization.",3 ,0 , fontsize=4, color=Color.ANNOTATIONS)