#!/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()