2018-10-04 20:34:57 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2013-2014 Florian Festi
|
|
|
|
# Copyright (C) 2018 jens persson <jens@persson.cx>
|
2023-02-26 23:52:52 +01:00
|
|
|
# Copyright (C) 2023 Manuel Lohoff
|
2018-10-04 20:34:57 +02:00
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
from boxes import edges, Boxes, BoolArg
|
2018-10-04 20:34:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
class InsetEdgeSettings(edges.Settings):
|
|
|
|
"""Settings for InsetEdge"""
|
|
|
|
absolute_params = {
|
|
|
|
"thickness": 0,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class InsetEdge(edges.BaseEdge):
|
|
|
|
"""An edge with space to slide in a lid"""
|
|
|
|
def __call__(self, length, **kw):
|
|
|
|
t = self.settings.thickness
|
|
|
|
self.corner(90)
|
|
|
|
self.edge(t, tabs=2)
|
|
|
|
self.corner(-90)
|
|
|
|
self.edge(length, tabs=2)
|
|
|
|
self.corner(-90)
|
|
|
|
self.edge(t, tabs=2)
|
|
|
|
self.corner(90)
|
|
|
|
|
|
|
|
|
|
|
|
class FingerHoleEdgeSettings(edges.Settings):
|
|
|
|
"""Settings for FingerHoleEdge"""
|
|
|
|
absolute_params = {
|
|
|
|
"wallheight": 0,
|
2023-02-26 23:52:52 +01:00
|
|
|
"fingerholedepth": 0,
|
2018-10-04 20:34:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class FingerHoleEdge(edges.BaseEdge):
|
|
|
|
"""An edge with room to get your fingers around cards"""
|
|
|
|
def __call__(self, length, **kw):
|
2023-02-26 23:52:52 +01:00
|
|
|
depth = self.settings.fingerholedepth-10
|
2018-10-04 20:34:57 +02:00
|
|
|
self.edge(length/2-10, tabs=2)
|
|
|
|
self.corner(90)
|
|
|
|
self.edge(depth, tabs=2)
|
|
|
|
self.corner(-180, 10)
|
|
|
|
self.edge(depth, tabs=2)
|
|
|
|
self.corner(90)
|
|
|
|
self.edge(length/2-10, tabs=2)
|
|
|
|
|
|
|
|
|
|
|
|
class CardBox(Boxes):
|
2023-02-26 23:52:52 +01:00
|
|
|
"""Box for storage of playing cards, with versatile options"""
|
2018-10-04 20:34:57 +02:00
|
|
|
ui_group = "Box"
|
|
|
|
|
2020-05-26 18:05:45 +02:00
|
|
|
description = """
|
2023-02-26 23:52:52 +01:00
|
|
|
### Description
|
|
|
|
Versatile Box for Storage of playing cards. Multiple different styles of storage are supportet, e.g. a flat storage or a trading card deck box style storage. See images for ideas.
|
2020-05-26 18:05:45 +02:00
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
#### Building instructions
|
2020-05-27 17:27:23 +02:00
|
|
|
Place inner walls on floor first (if any). Then add the outer walls. Glue the two walls without finger joins to the inside of the side walls. Make sure there is no squeeze out on top, as this is going to form the rail for the lid.
|
2020-05-26 18:05:45 +02:00
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
Add the top of the rails to the sides (front open) or to the back and front (right side open) and the grip rail to the lid.
|
2022-01-05 21:34:15 +01:00
|
|
|
Details of the lid and rails
|
2020-05-26 18:05:45 +02:00
|
|
|
![Details](static/samples/CardBox-detail.jpg)
|
2022-01-05 21:34:15 +01:00
|
|
|
Whole box (early version still missing grip rail on the lid):
|
2020-05-26 18:05:45 +02:00
|
|
|
"""
|
|
|
|
|
2023-01-08 19:41:02 +01:00
|
|
|
def __init__(self) -> None:
|
2018-10-04 20:34:57 +02:00
|
|
|
Boxes.__init__(self)
|
2023-02-26 23:52:52 +01:00
|
|
|
|
2018-10-04 20:34:57 +02:00
|
|
|
self.addSettingsArgs(edges.FingerJointSettings)
|
2023-02-26 23:52:52 +01:00
|
|
|
self.buildArgParser(y=68, h=92, outside=False, sx="65*4")
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--openingdirection", action="store", type=str, default="front",
|
|
|
|
choices=['front', 'right'],
|
|
|
|
help="Direction in which the lid slides open. Lid length > Lid width recommended.")
|
2018-10-04 20:34:57 +02:00
|
|
|
self.argparser.add_argument(
|
2023-02-26 23:52:52 +01:00
|
|
|
"--fingerhole", action="store", type=str, default="regular",
|
|
|
|
choices=['regular', 'deep', 'custom'],
|
|
|
|
help="Depth of cutout to grab the cards")
|
2018-10-04 20:34:57 +02:00
|
|
|
self.argparser.add_argument(
|
2023-02-26 23:52:52 +01:00
|
|
|
"--fingerhole_depth", action="store", type=float, default=20,
|
|
|
|
help="Depth of cutout if fingerhole is set to 'custom'. Disabled otherwise.")
|
2018-10-10 23:06:57 +02:00
|
|
|
self.argparser.add_argument(
|
2023-02-26 23:52:52 +01:00
|
|
|
"--add_lidtopper", action="store", type=BoolArg(), default=False,
|
|
|
|
help="Add an additional lid topper for optical reasons and customisation"
|
|
|
|
)
|
2018-10-04 20:34:57 +02:00
|
|
|
|
|
|
|
@property
|
2023-02-26 23:52:52 +01:00
|
|
|
def fingerholedepth(self):
|
|
|
|
if self.fingerhole == 'custom':
|
|
|
|
return self.fingerhole_depth
|
|
|
|
elif self.fingerhole == 'regular':
|
|
|
|
a = self.h/4
|
|
|
|
if a < 35:
|
|
|
|
return a
|
|
|
|
else:
|
|
|
|
return 35
|
|
|
|
elif self.fingerhole == 'deep':
|
|
|
|
return self.h-self.thickness-10
|
|
|
|
|
|
|
|
#inner dimensions of surrounding box (disregarding inlays)
|
|
|
|
@property
|
|
|
|
def boxhight(self):
|
|
|
|
if self.outside:
|
|
|
|
return self.h - 3 * self.thickness
|
|
|
|
else:
|
|
|
|
return self.h
|
|
|
|
@property
|
2018-10-04 20:34:57 +02:00
|
|
|
def boxwidth(self):
|
2023-02-26 23:52:52 +01:00
|
|
|
return (len(self.sx) + 1) * self.thickness + sum(self.sx)
|
|
|
|
@property
|
|
|
|
def boxdepth(self):
|
|
|
|
if not self.outside:
|
|
|
|
if self.openingdirection == 'right':
|
|
|
|
return self.y + 2 * self.thickness
|
|
|
|
else:
|
|
|
|
return self.y
|
|
|
|
else:
|
|
|
|
return self.y - 2 * self.thickness
|
2018-10-04 20:34:57 +02:00
|
|
|
|
|
|
|
def divider_bottom(self):
|
2018-10-10 23:06:57 +02:00
|
|
|
t = self.thickness
|
2023-02-26 23:52:52 +01:00
|
|
|
sx = self.sx
|
|
|
|
y = self.boxdepth
|
2018-10-04 20:34:57 +02:00
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
pos = 0.5 * t
|
|
|
|
for i in sx[:-1]:
|
|
|
|
pos += i + t
|
|
|
|
self.fingerHolesAt(pos, 0, y, 90)
|
2018-10-04 20:34:57 +02:00
|
|
|
|
|
|
|
def divider_back_and_front(self):
|
2018-10-10 23:06:57 +02:00
|
|
|
t = self.thickness
|
2023-02-26 23:52:52 +01:00
|
|
|
sx = self.sx
|
|
|
|
y = self.boxhight
|
|
|
|
|
|
|
|
pos = 0.5 * t
|
|
|
|
for i in sx[:-1]:
|
|
|
|
pos += i + t
|
|
|
|
self.fingerHolesAt(pos, 0, y, 90)
|
2018-10-04 20:34:57 +02:00
|
|
|
|
|
|
|
def render(self):
|
|
|
|
t = self.thickness
|
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
h = self.boxhight
|
2018-10-04 20:34:57 +02:00
|
|
|
x = self.boxwidth
|
2023-02-26 23:52:52 +01:00
|
|
|
y = self.boxdepth
|
|
|
|
sx = self.sx
|
2018-10-04 20:34:57 +02:00
|
|
|
|
|
|
|
s = InsetEdgeSettings(thickness=t)
|
|
|
|
p = InsetEdge(self, s)
|
|
|
|
p.char = "a"
|
|
|
|
self.addPart(p)
|
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
s = FingerHoleEdgeSettings(thickness=t, wallheight=h, fingerholedepth=self.fingerholedepth)
|
2018-10-04 20:34:57 +02:00
|
|
|
p = FingerHoleEdge(self, s)
|
|
|
|
p.char = "A"
|
|
|
|
self.addPart(p)
|
|
|
|
|
2023-02-26 23:52:52 +01:00
|
|
|
if self.openingdirection == 'right':
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(x, y-t*.2, "eFee", move="right", label="Lid")
|
|
|
|
self.rectangularWall(x, y, "ffff", callback=[self.divider_bottom],
|
|
|
|
move="right", label="Bottom")
|
|
|
|
self.rectangularWall(x, y, "eEEE", move="up only")
|
|
|
|
self.rectangularWall(x, t, "feee", move="up", label="Lip Front")
|
|
|
|
self.rectangularWall(x, t, "eefe", move="up", label="Lip Back")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(x, h+t, "FfFf",
|
2019-01-12 23:16:11 +01:00
|
|
|
callback=[self.divider_back_and_front],
|
2021-06-27 12:53:56 +02:00
|
|
|
move="right",
|
|
|
|
label="Back")
|
2023-02-26 23:52:52 +01:00
|
|
|
self.rectangularWall(x, h+t, "FfFf",
|
2019-01-12 23:16:11 +01:00
|
|
|
callback=[self.divider_back_and_front],
|
2023-02-26 23:52:52 +01:00
|
|
|
move="right",
|
2021-06-27 12:53:56 +02:00
|
|
|
label="Front")
|
2023-02-26 23:52:52 +01:00
|
|
|
self.rectangularWall(x, h+t, "EEEE", move="up only")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(y, h+t, "FFEF", move="right", label="Outer Side Left")
|
|
|
|
self.rectangularWall(y, h+t, "FFaF", move="right", label="Outer Side Right")
|
|
|
|
self.rectangularWall(y, h+t, "fFfF", move="up only")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Left")
|
|
|
|
self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Right")
|
|
|
|
self.rectangularWall(y, h, "eAee", move="up only")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(y-t*.2, t, "fEeE", move="right", label="Lid Lip")
|
|
|
|
self.rectangularWall(y, t*2, "efee", move="up only")
|
|
|
|
|
|
|
|
for i in range(len(sx) - 1):
|
|
|
|
self.rectangularWall(h, y, "fAff", move="right", label="Divider")
|
|
|
|
|
|
|
|
for c in sx:
|
|
|
|
self.rectangularWall(c, h, "eeee", move="right", label="Front inlay")
|
|
|
|
self.rectangularWall(c, h, "eeee", move="right", label="Back inlay")
|
|
|
|
|
|
|
|
if self.add_lidtopper:
|
|
|
|
self.rectangularWall(x, y - 2*t, "eeee", move="right", label="Lid topper")
|
|
|
|
|
|
|
|
elif self.openingdirection == 'front':
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(x - t * .2, y, "eeFe", move="right", label="Lid")
|
|
|
|
self.rectangularWall(x, y, "ffff", callback=[self.divider_bottom],
|
|
|
|
move="right", label="Bottom")
|
|
|
|
self.rectangularWall(x, y, "eEEE", move="up only")
|
|
|
|
self.rectangularWall(x - t * .2, t, "fEeE", move="up", label="Lid Lip")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(x, h + t, "FFEF",
|
|
|
|
callback=[self.divider_back_and_front],
|
|
|
|
move="right",
|
|
|
|
label="Back")
|
|
|
|
self.rectangularWall(x, h + t, "FFaF",
|
|
|
|
callback=[self.divider_back_and_front],
|
|
|
|
move="right",
|
|
|
|
label="Front")
|
|
|
|
self.rectangularWall(x, h + t, "EEEE", move="up only")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(y, h + t, "FfFf", move="right", label="Outer Side Left")
|
|
|
|
self.rectangularWall(y, h + t, "FfFf", move="right", label="Outer Side Right")
|
|
|
|
self.rectangularWall(y, h + t, "fFfF", move="up only")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Left")
|
|
|
|
self.rectangularWall(y, h, "Aeee", move="right", label="Inner Side Right")
|
|
|
|
self.rectangularWall(y, h, "eAee", move="up only")
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.rectangularWall(y, t, "eefe", move="right", label="Lip Left")
|
|
|
|
self.rectangularWall(y, t, "feee", move="right", label="Lip Right")
|
|
|
|
self.rectangularWall(y, t * 2, "efee", move="up only")
|
|
|
|
|
|
|
|
for i in range(len(sx) - 1):
|
|
|
|
self.rectangularWall(h, y, "fAff", move="right", label="Divider")
|
|
|
|
|
|
|
|
if self.add_lidtopper:
|
|
|
|
self.rectangularWall(x, y - 2 * t, "eeee", move="right", label="Lid topper (optional)")
|