summaryrefslogtreecommitdiff
path: root/boxes
diff options
context:
space:
mode:
Diffstat (limited to 'boxes')
-rwxr-xr-xboxes/telescoping_box.py351
-rwxr-xr-xboxes/tuckbox.py230
2 files changed, 581 insertions, 0 deletions
diff --git a/boxes/telescoping_box.py b/boxes/telescoping_box.py
new file mode 100755
index 0000000..53a83bb
--- /dev/null
+++ b/boxes/telescoping_box.py
@@ -0,0 +1,351 @@
+#!/usr/bin/env python3
+
+import hazwaz
+
+import svgwrite
+
+try:
+ import argcomplete # type: ignore
+except ImportError:
+ argcomplete = False
+
+mm = 3.7795276
+
+LINE_STYLE = {
+ 'stroke': svgwrite.utils.rgb(127, 127, 127),
+ 'stroke_width': 0.5,
+ 'fill': 'none'
+ }
+
+
+class Structure(object):
+ def __init__(self, x, y, z, flap=15):
+ """
+ """
+ self.x = x * mm
+ self.y = y * mm
+ self.z = z * mm
+ self.f = flap * mm
+ self.t = self.y / 3 # tab lenght
+
+ self.full_width = self.f * 2 + self.z * 2 + self.x
+ self.full_height = self.f * 2 + self.z * 2 + self.y
+
+ def add_to(self, drw, canvas_x=210 * mm, canvas_y=297 * mm):
+ grp = drw.g()
+ grp.add(drw.rect(
+ (self.z + self.f, self.z + self.f),
+ (self.x, self.y),
+ id='base',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.f, (self.z + self.f)),
+ (self.z, self.y),
+ id='left',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ ((self.z + self.f + self.x), (self.z + self.f)),
+ (self.z, self.y),
+ id='right',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ ((self.z + self.f), self.f),
+ (self.x, self.z),
+ id='top',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ ((self.z + self.f), (self.f + self.z + self.y)),
+ (self.x, self.z),
+ id='bottom',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (0, (self.f + self.z)),
+ (self.f, self.y),
+ id='left_flap',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ ((self.f + self.z * 2 + self.x), (self.f + self.z)),
+ (self.f, self.y),
+ id='right_flap',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ ((self.f + self.z), 0),
+ (self.x, self.f),
+ id='top_flap',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ ((self.f + self.z), (self.f + self.z * 2 + self.y)),
+ (self.x, self.f),
+ id='bottom_flap',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.f + self.z, self.f),
+ (self.f + self.z, self.f + self.z),
+ (self.f + self.z - self.t, self.f + self.z - 2 * mm),
+ (self.f + self.z - self.t, self.f + 4 * mm)
+ ),
+ id='top_left_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.f + self.z + self.x, self.f),
+ (self.f + self.z + self.x + self.t, self.f + 4 * mm),
+ (self.f + self.z + self.x + self.t, self.f + self.z - 2 * mm),
+ (self.f + self.z + self.x, self.f + self.z),
+ ),
+ id='top_right_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.f + self.z, self.f + self.y + self.z),
+ (self.f + self.z, self.f + self.z + self.y + self.z),
+ (self.f + self.z - self.t, self.f + self.z - 4 * mm +
+ self.y + self.z),
+ (self.f + self.z - self.t, self.f + 2 * mm + self.y +
+ self.z)
+ ),
+ id='bottom_left_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.f + self.z + self.x, self.f + self.y + self.z),
+ (self.f + self.z + self.x + self.t, self.f + 4 * mm +
+ self.y + self.z),
+ (self.f + self.z + self.x + self.t, self.f + self.z - 2
+ * mm + self.y + self.z),
+ (self.f + self.z + self.x, self.f + self.z + self.y +
+ self.z),
+ ),
+ id='bottom_left_tab',
+ ** LINE_STYLE,
+ ))
+ grp.translate(
+ (canvas_x - self.full_width) / 2,
+ (canvas_y - self.full_height) / 2
+ )
+ drw.add(grp)
+
+
+class Cardboard(object):
+ def __init__(self, x, y, z, thickness=1):
+ """
+ """
+ self.x = x * mm
+ self.y = y * mm
+ self.z = z * mm
+ self.th = thickness * mm
+
+ @property
+ def full_width(self):
+ return max(self.x + self.z * 2, self.x * 2 + self.th * 4)
+
+ @property
+ def full_height(self):
+ return self.y + self.z
+
+ def _get_grp(self, drw):
+ grp = drw.g()
+ grp.add(drw.rect(
+ (self.z, 0),
+ (self.x, self.y),
+ id='base_base',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (0, 0),
+ (self.z, self.y),
+ id='base_left_side',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.z + self.x, 0),
+ (self.z, self.y),
+ id='base_right_side',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (0, self.y),
+ (self.x + self.th * 2, self.z),
+ id='base_top_side',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.x + self.th * 2, self.y),
+ (self.x + self.th * 2, self.z),
+ id='base_bottom_side',
+ ** LINE_STYLE,
+ ))
+ return grp
+
+ def add_to(self, drw, canvas_x=210 * mm, canvas_y=297 * mm):
+ grp = self._get_grp(drw)
+ grp.translate(
+ (canvas_x - self.full_width) / 2,
+ (canvas_y / 2 - self.full_height)
+ )
+ drw.add(grp)
+
+ self.x = self.x + self.th * 4
+ self.y = self.y + self.th * 4
+ self.z = self.z + self.th
+
+ grp = self._get_grp(drw)
+ grp.translate(
+ (canvas_x - self.full_width) / 2,
+ canvas_y / 2
+ )
+ drw.add(grp)
+
+
+class Base(Structure):
+ def __init__(self, x, y, z, thickness=1, flap=15):
+ """
+ """
+ super().__init__(
+ x + thickness * 2,
+ y + thickness * 2,
+ z + thickness,
+ flap=flap,
+ )
+
+
+class Lid(Structure):
+ def __init__(self, x, y, z, thickness=1, flap=15):
+ """
+ """
+ super().__init__(
+ x + thickness * 6,
+ y + thickness * 6,
+ z + thickness * 2,
+ flap=flap,
+ )
+
+
+class Command(hazwaz.MainCommand):
+ """
+ Generate SVGs templates to build a telescoping box.
+ """
+ def add_arguments(self, parser):
+ parser.add_argument(
+ "size_x",
+ help="inner width of the box",
+ type=int,
+ )
+ parser.add_argument(
+ "size_y",
+ help="inner height of the box",
+ type=int,
+ )
+ parser.add_argument(
+ "size_z",
+ help="inner depth of the box",
+ type=int,
+ )
+ parser.add_argument(
+ "-o", "--base_output",
+ help="base destination filename",
+ default="telescoping_box",
+ )
+ parser.add_argument(
+ "--paper_width",
+ help="paper width in millimiters",
+ type=int,
+ default=210,
+ )
+ parser.add_argument(
+ "--paper_height",
+ help="paper height in millimiters",
+ type=int,
+ default=297,
+ )
+ parser.add_argument(
+ "--flap",
+ help="height of the flap, in millimiters",
+ type=int,
+ default=15
+ )
+
+ def main(self):
+ size_spec = "{}×{}×{}".format(
+ str(self.args.size_x),
+ str(self.args.size_y),
+ str(self.args.size_z),
+ )
+ paper_size = (
+ "{}mm".format(self.args.paper_width),
+ "{}mm".format(self.args.paper_height),
+ )
+
+ # lid
+ dest = svgwrite.Drawing(
+ filename="{}-{}-lid.svg".format(
+ self.args.base_output,
+ size_spec,
+ ),
+ size=paper_size,
+ profile='full',
+ )
+ struc = Lid(
+ self.args.size_x, self.args.size_y, self.args.size_z,
+ flap=self.args.flap,
+ )
+ struc.add_to(
+ dest,
+ canvas_x=self.args.paper_width * mm,
+ canvas_y=self.args.paper_height * mm,
+ )
+ dest.save()
+
+ # base
+ dest = svgwrite.Drawing(
+ filename="{}-{}-base.svg".format(
+ self.args.base_output,
+ size_spec,
+ ),
+ size=paper_size,
+ profile='full',
+ )
+ struc = Base(
+ self.args.size_x, self.args.size_y, self.args.size_z,
+ flap=self.args.flap,
+ )
+ struc.add_to(
+ dest,
+ canvas_x=self.args.paper_width * mm,
+ canvas_y=self.args.paper_height * mm,
+ )
+ dest.save()
+
+ # cardboard
+ dest = svgwrite.Drawing(
+ filename="{}-{}-cardboard.svg".format(
+ self.args.base_output,
+ size_spec,
+ ),
+ size=paper_size,
+ profile='full',
+ )
+ struc = Cardboard(self.args.size_x, self.args.size_y, self.args.size_z)
+ struc.add_to(
+ dest,
+ canvas_x=self.args.paper_width * mm,
+ canvas_y=self.args.paper_height * mm,
+ )
+ dest.save()
+
+
+if __name__ == '__main__':
+ Command().run()
diff --git a/boxes/tuckbox.py b/boxes/tuckbox.py
new file mode 100755
index 0000000..73d4eaf
--- /dev/null
+++ b/boxes/tuckbox.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python3
+
+import argparse
+
+import svgwrite
+
+try:
+ from lesana.command import _get_first_docstring_line # type: ignore
+except ImportError:
+ def _get_first_docstring_line(obj):
+ return ""
+
+try:
+ import argcomplete # type: ignore
+except ImportError:
+ argcomplete = False
+
+
+# 1 mm in px
+mm = 3.7795276
+
+LINE_STYLE = {
+ 'stroke': svgwrite.utils.rgb(127, 127, 127),
+ 'stroke_width': 0.5,
+ 'fill': 'none'
+ }
+
+
+class Structure():
+ def __init__(self, x, y, z, flap=20, tab=16):
+ """
+ """
+ self.x = x * mm
+ self.y = y * mm
+ self.z = z * mm
+ self.f = flap * mm
+ self.t = tab * mm # tab lenght
+
+ self.full_width = self.t + self.x * 2 + self.z * 2
+ self.full_height = self.f + self.z * 2 + self.y
+
+ def add_to(self, drw, canvas_x=297 * mm, canvas_y=210 * mm):
+ grp = drw.g()
+ grp.add(drw.polygon(
+ (
+ (3 * mm, self.z - self.t),
+ (self.z - 3 * mm, self.z - self.t),
+ (self.z, self.z),
+ (0, self.z),
+ ),
+ id='right_side_bottom_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.z, 0),
+ (self.x, self.z),
+ id='base',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.z + self.x + 3 * mm, self.z - self.t),
+ (self.z * 2 + self.x - 3 * mm, self.z - self.t),
+ (self.z * 2 + self.x, self.z),
+ (self.z + self.x, self.z),
+ ),
+ id='left_side_bottom_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.z * 2 + self.x + 3 * mm, self.z - self.t),
+ (self.z * 2 + self.x * 2 - 3 * mm, self.z - self.t),
+ (self.z * 2 + self.x * 2, self.z),
+ (self.z * 2 + self.x, self.z),
+ ),
+ id='back_bottom_tab',
+ ** LINE_STYLE,
+ ))
+
+ grp.add(drw.rect(
+ (0, self.z),
+ (self.z, self.y),
+ id='right_side',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.z, self.z),
+ (self.x, self.y),
+ id='front',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.x + self.z, self.z),
+ (self.z, self.y),
+ id='left_side',
+ ** LINE_STYLE,
+ ))
+ # TODO: remove the semicircle from the rect
+ grp.add(drw.rect(
+ (self.x + self.z * 2, self.z),
+ (self.x, self.y),
+ id='back',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.ellipse(
+ (self.x + self.z * 2 + self.x / 2, self.z + self.y),
+ (8 * mm, 8 * mm),
+ id='back_cut',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.x * 2 + self.z * 2, self.z),
+ (self.x * 2 + self.z * 2 + self.t, self.z + 3 * mm),
+ (self.x * 2 + self.z * 2 + self.t, self.z + self.y - 3 * mm),
+ (self.x * 2 + self.z * 2, self.z + self.y),
+ ),
+ id='side_tab',
+ ** LINE_STYLE,
+ ))
+
+ grp.add(drw.polygon(
+ (
+ (0, self.z + self.y),
+ (self.z, self.z + self.y),
+ (self.z - 3 * mm, self.z + self.y + self.t),
+ (3 * mm, self.z + self.y + self.t),
+ ),
+ id='right_side_top_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.z, self.z + self.y),
+ (self.x, self.z),
+ id='top',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.polygon(
+ (
+ (self.z + self.x, self.z + self.y),
+ (self.z * 2 + self.x, self.z + self.y),
+ (self.z * 2 + self.x - 3 * mm, self.z + self.y + self.t),
+ (self.z + self.x + 3 * mm, self.z + self.y + self.t),
+ ),
+ id='left_side_top_tab',
+ ** LINE_STYLE,
+ ))
+
+ # TODO: make this an half-ellipse
+ grp.add(drw.ellipse(
+ (self.z + self.x / 2, self.z * 2 + self.y),
+ (self.x / 2, self.f),
+ id='top_tab',
+ ** LINE_STYLE,
+ ))
+ grp.add(drw.rect(
+ (self.z, self.z + self.y + self.z - self.f - 1),
+ (self.x, self.f + 1),
+ id='top_tab_excess',
+ ** LINE_STYLE,
+ ))
+
+ grp.rotate(
+ 180,
+ center=(canvas_x / 2, canvas_y / 2),
+ )
+ grp.translate(
+ (canvas_x - self.full_width) / 2,
+ (canvas_y - self.full_height) / 2
+ )
+ drw.add(grp)
+
+
+class Command():
+ """
+ Generate an SVG with the basic shape of a tuckbox for gaming cards.
+ """
+
+ def get_parser(self):
+ desc = _get_first_docstring_line(self)
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument(
+ "-x", "--size-x",
+ default=60,
+ type=int,
+ help="Internal width of the box.",
+ )
+ parser.add_argument(
+ "-y", "--size-y",
+ default=94,
+ type=int,
+ help="Internal height of the box.",
+ )
+ parser.add_argument(
+ "-z", "--size-z",
+ default=42,
+ type=int,
+ help="Internal thickness of the box.",
+ )
+ parser.add_argument(
+ "-o", "--output",
+ default="tuckbox",
+ help="Base output file name."
+ )
+ return parser
+
+ def main(self):
+ self.parser = self.get_parser()
+ if argcomplete:
+ argcomplete.autocomplete(self.parser)
+ self.args = self.parser.parse_args()
+
+ size_spec = "{}×{}×{}".format(
+ self.args.size_x,
+ self.args.size_y,
+ self.args.size_z,
+ )
+ dest = svgwrite.Drawing(
+ filename="{}-{}.svg".format(self.args.output, size_spec),
+ size=('297mm', '210mm'),
+ profile='full',
+ )
+ struc = Structure(self.args.size_x, self.args.size_y, self.args.size_z)
+ struc.add_to(dest)
+ dest.save()
+
+
+if __name__ == '__main__':
+ Command().main()