2022-05-26 06:59:31 +02:00
#!/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
2023-01-02 00:32:42 +01:00
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2022-05-26 06:59:31 +02:00
# 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 *
2022-12-31 15:52:55 +01:00
2022-05-26 06:59:31 +02:00
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 ) :
return self . boxes . thickness
class CanStorage ( Boxes ) :
""" Storage box for round containers """
description = """
for AA batteries :
! [ CanStorage for AA batteries ] ( static / samples / CanStorageAA . jpg )
2023-01-02 00:32:42 +01:00
for canned tomatoes :
2022-05-26 06:59:31 +02:00
"""
ui_group = " Misc "
2023-01-08 19:41:02 +01:00
def __init__ ( self ) - > None :
2022-05-26 06:59:31 +02:00
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 (
2023-01-02 16:23:24 +01:00
" --canHeight " , action = " store " , type = float , default = 110 ,
2023-01-02 00:32:42 +01:00
help = " height of the cans to be stored (in mm) " )
2022-05-26 06:59:31 +02:00
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 = 0.25 * self . canDiameter
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
2023-01-02 16:23:24 +01:00
self . width = 0.01 * self . canHeight + self . canHeight + 0.01 * self . canHeight
2022-05-26 06:59:31 +02:00
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 \n and 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 \n side stacking foot for lateral stabilization. " , 3 , 0 , fontsize = 4 , color = Color . ANNOTATIONS )