New generator: SmallPartsTray

Tray for game tokens, small nuts and bolts or all other tiny items. It
has a slant on the bottom for the side wall so such pieces can be picked
up easily with one finger.
This commit is contained in:
Florian Festi 2023-01-09 16:49:25 +01:00
parent 3511046ae1
commit 14c2a820c0
1 changed files with 244 additions and 0 deletions

View File

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