diff --git a/boxes/Color.py b/boxes/Color.py index 884c12bb4..f0364c703 100644 --- a/boxes/Color.py +++ b/boxes/Color.py @@ -14,3 +14,6 @@ class Color: ANNOTATIONS = RED ETCHING = GREEN ETCHING_DEEP = CYAN + CUT = [ # Even steps from INNER_CUT to OUTER_CUT + [0.0, 0.0, 1.0 - i/4.0] for i in range(5) + ] diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 78c9c8aa9..70e971978 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -50,16 +50,54 @@ def __init__(self) -> None: help="edge type for top and bottom edges") self.argparser.add_argument( "--top", action="store", type=str, default="hole", - choices=["hole", "lid", "closed",], + choices=["hole", "lid", "lid_with_latches", "closed"], + help="style of the top and lid") + self.argparser.add_argument( + "--bottom", action="store", type=str, default="closed", + choices=["closed", "hole", "lid", "lid_with_latches"], help="style of the top and lid") - def hole(self): + def latch(self, move=None): t = self.thickness - x, y, r = self.x, self.y, self.radius + p = .05 * t + l = 10*t + if self.move(l, 3*t, move, True): + return + + r = t / 2**0.5 + self.polyline(l, 90, 1.5*t, 45, 0, (90, r), 0, 45, + t, -90, 4*t, 180, 4*t, 90, + t-p, 90, 3*t, -90, 2*t+p, 90, + 3*t, 90, t, 90, t, -90, t+p, -90, + 3.75*t, 45, t/4*2**0.5, 45, 1.25*t-p, 90) + + self.move(l, 3*t, move) - dr = 2*t + def latch_positions(self, x, y, r, callback): + d = (1-0.5*2**0.5) * r + for l in (x, y, x, y): + with self.saved_context(): + self.moveTo(+d, d, -45) + callback() + self.moveTo(l+2*r, 0, 90) + + @property + def hole_dr(self): + dr = 2*self.thickness if self.edge_style == "h": - dr = t + dr = self.thickness + return dr + + def top_hole(self, style): + + if style == "closed": + return + + t = self.thickness + p = 0.05*t + x, y, r = self.x, self.y, self.radius + + dr = self.hole_dr if r > dr: r -= dr @@ -75,6 +113,65 @@ def hole(self): self.edge(l) self.corner(90, r) + if style == "lid_with_latches": + # We end up where we started + # go to "corner" of the hole + self.moveTo(-r) + self.latch_positions( + lx, ly, r, + lambda: ( + self.rectangularHole(0, 2.5*t, 1.1*t, 4*t, center_y=False), + self.rectangularHole(0, 8*t, 1.1*t, 0.7*t), + self.rectangularHole(0, 10*t, 1.1*t, 0.7*t))) + + @boxes.holeCol + def latch_holes(self): + """The holes cut out of one latch slider.""" + t = self.thickness + self.rectangularHole(0, 1.5*t, t, 3*t, center_y=False) + self.hole(0, 9.5*t, d=5*t), + + @boxes.holeCol + def latch_slider(self): + """The sliding piece that actuates the latch.""" + t = self.thickness + self.rectangularHole(0, 0.55*t, + 6.95*t, 12.95*t, + r=7*t, center_y=False, + color=boxes.Color.CUT[1]), + + @boxes.holeCol + def latch_cutout(self): + """The hole in which the latch_slider sits.""" + t = self.thickness + self.rectangularHole(0, 0.5*t, + 7*t, 15*t, + r=7*t, center_y=False, + color=boxes.Color.CUT[2]) + + + def latches(self): + t = self.thickness + x, y, r = self.x, self.y, self.radius + r_extra = self.edges[self.edge_style].spacing() + dr = self.hole_dr + self.moveTo(dr-r, dr+r_extra) + + if r > dr: + r -= dr + else: + r = 0 + + lx = x - 2*r - 2*dr + ly = y - 2*r - 2*dr + + self.latch_positions( + lx, ly, r, + lambda: ( + self.latch_holes(), + self.latch_slider(), + self.latch_cutout())) + def cb(self, nr): h = 0.5 * self.thickness @@ -83,8 +180,19 @@ def cb(self, nr): h += dh self.fingerHolesAt(0, h, l, 0) + def cut_order(self): + self.move(self.reference, 10, "down", before=True) + self.text("cut order:", self.reference + 25, 5, + fontsize=6, align="middle center", color=boxes.Color.ANNOTATIONS) + for i, col in enumerate(boxes.Color.CUT[:3] + [boxes.Color.OUTER_CUT]): + self.text(f"{i}", self.reference + 50 + 10 * i, 5, + fontsize=6, align="middle center", color=col) + self.move(self.reference, 10, "up", before=False) + def render(self): + self.cut_order() + _ = self.translations.gettext x, y, sh, r = self.x, self.y, self.sh, self.radius if self.outside: @@ -112,23 +220,37 @@ def render(self): ec = True with self.saved_context(): - self.roundedPlate(x, y, r, es, wallpieces=self.wallpieces, - extend_corners=ec, move="right") + # These are the inner shelves for dh in self.sh[:-1]: self.roundedPlate(x, y, r, "f", wallpieces=self.wallpieces, + label=_("inner shelf"), extend_corners=False, move="right") - self.roundedPlate(x, y, r, es, wallpieces=self.wallpieces, - extend_corners=ec, move="right", - callback=[self.hole] if self.top != "closed" else None) - if self.top == "lid": - r_extra = self.edges[self.edge_style].spacing() - self.roundedPlate(x+2*r_extra, - y+2*r_extra, - r+r_extra, - "e", wallpieces=self.wallpieces, - extend_corners=False, move="right") + for name, style in ((_("bottom"), self.bottom), + (_("top"), self.top)): + # This is the top/bottom plate (which has a hole in it, if requested) + self.roundedPlate( + x, y, r, es, wallpieces=self.wallpieces, + extend_corners=ec, move="right", + label=name, + callback=[lambda: self.top_hole(style)]) + # An additional plate for the lid, if requested + if style in ["lid", "lid_with_latches"]: + r_extra = self.edges[self.edge_style].spacing() + self.roundedPlate( + x+2*r_extra, y+2*r_extra, r+r_extra, + "e", wallpieces=self.wallpieces, + label=_("%s lid") % name, + extend_corners=False, move="right", + callback=[self.latches] if style == "lid_with_latches" else None) + + # Move up one row self.roundedPlate(x, y, r, es, wallpieces=self.wallpieces, move="up only") + # This is the wall (i.e. the vertical part) self.surroundingWall(x, y, r, h, pe, pe, pieces=self.wallpieces, - callback=self.cb) + callback=self.cb, move="up") + for style in (self.top, self.bottom): + if style == "lid_with_latches": + for i in range(4): + self.latch(move="right") diff --git a/examples/RoundedBox.svg b/examples/RoundedBox.svg index bffa5db24..398c7ff31 100644 --- a/examples/RoundedBox.svg +++ b/examples/RoundedBox.svg @@ -45,10 +45,10 @@ Command line short: boxes RoundedBox 100.0mm, burn:0.10mm - + bottom - + top