From ecd411bb47628e58c654678c3360d2428690f2b0 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 3 Sep 2025 13:11:23 +0100 Subject: [PATCH 01/11] Added an option for a latched lid to RoundedBox This adds an additional value for `--top` which is `lid_with_latches`. Using this will add an extra plate that sits just inside (i.e. underneath) the lid, with four latches on it. The latches may be moved in and out using four M3 screws. The lid must be glued together with two smaller plates, to make a single unit. Also, the holes for the screws need to be threaded before they will work - depending on the material it may be possible to self-tap. I've printed this in 3mm acrylic and it worked fairly well. --- boxes/generators/roundedbox.py | 107 ++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 78c9c8aa9..921caa678 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -14,6 +14,7 @@ # along with this program. If not, see . import boxes +from math import sqrt class RoundedBox(boxes.Boxes): @@ -50,16 +51,21 @@ 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", "closed", "lid_with_latches"], help="style of the top and lid") - def hole(self): + @property + def hole_dr(self): + dr = 2*self.thickness + if self.edge_style == "h": + dr = self.thickness + return dr + + def top_hole(self): t = self.thickness x, y, r = self.x, self.y, self.radius - dr = 2*t - if self.edge_style == "h": - dr = t + dr = self.hole_dr if r > dr: r -= dr @@ -75,6 +81,73 @@ def hole(self): self.edge(l) self.corner(90, r) + if self.top == "lid_with_latches": + # We end up where we started, so move up further to the + # centre of the radiused corner + self.moveTo(0, r) # Note r=self.radius-dr + self.screw_slots(lx, ly, 3.3/2) + + @boxes.holeCol + def screw_slots(self, lx, ly, r): + """Make four slots with rounded ends, at the four corners. + + These slots allow screws to slide along them, to move the latches + in and out. + """ + for l in (lx, ly, lx, ly): + self.moveTo(0, 0, 45) + self.moveTo(0, -r) + self.corner(180, r) + self.edge(2*self.thickness) + self.corner(180, r) + self.edge(2*self.thickness) + self.moveTo(0, r, -45) + self.moveTo(l, 0, 90) + + def screw_clearance_slots(self): + t = self.thickness + x, y, r = self.x, self.y, self.radius + + self.moveTo(0, r + t) + self.screw_slots(x - 2*r, y - 2*r, 5) + + def latches(self): + """Screw holes for tapping on latches""" + t = self.thickness + x, y, r = self.x - 4*t, self.y - 4*t, self.radius - 2*t + + # we end up at(r, 0), where the curve joins the edge + for l in [y-2*r, x-2*r, y-2*r, x-2*r]: + self.hole(0, r, d=2.5) + # This will cut a finger out of the corner + self.moveTo(0, 0, 45) + self.edge(40) + self.corner(180, r/sqrt(2)) + self.edge(40) + self.moveTo(0, 0, -135) + self.moveTo(l, 0, 180) + + def latch_screw_slots(self): + """Slots for the screws to slide in""" + t = self.thickness + # This will be on the "top" plate with the hole, so it's + # actually bigger than nominal by t in all directions. + x, y, r = self.x + 2*t, self.y + 2*t, self.radius + t + screw_r = 3.3/2 + + # we end up at(r, 0), where the curve joins the edge + for l in [y-2*r, x-2*r, y-2*r, x-2*r]: + # This will cut a slot, and move from one end of the + # curve to the other + self.moveTo(0, r, 45) + self.moveTo(-2*t, -screw_r) + self.edge(2*t) + self.corner(180, screw_r) + self.edge(2*t) + self.corner(180, screw_r) + self.moveTo(0, 0, -45) + self.moveTo(l, 0, 90) + def cb(self, nr): h = 0.5 * self.thickness @@ -112,23 +185,39 @@ def render(self): ec = True with self.saved_context(): + # This plate is the base of the box 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, extend_corners=False, move="right") + # This is the top plate (which has a hole in it, if requested) 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": + callback=[self.top_hole] if self.top != "closed" else None) + # An additional plate for the lid, if requested + if self.top 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, - extend_corners=False, move="right") - + extend_corners=False, move="right", + callback=[self.screw_clearance_slots] if self.top == "lid_with_latches" else None) + + # A plate with the latches. + if self.top == "lid_with_latches": + dr = self.hole_dr + if r < dr: + raise ValueError("Latches only work with radius > %f." % dr) + self.roundedPlate(x - 2*dr, y - 2*dr, r - dr, "e", wallpieces=self.wallpieces, + extend_corners=False, move="right", + callback=[self.latches]) + + # I don't know what this plate is for! 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) From 2828280dbdc25fe2864f2777384b4492500c69dd Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Fri, 5 Sep 2025 12:38:26 +0200 Subject: [PATCH 02/11] Alternative latch implementation without fasteners Based on the work of Richard Bowman Co-authored-by: Richard Bowman --- boxes/generators/roundedbox.py | 126 ++++++++++++++------------------- 1 file changed, 54 insertions(+), 72 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 921caa678..b7863c47f 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -54,6 +54,26 @@ def __init__(self) -> None: choices=["hole", "lid", "closed", "lid_with_latches"], help="style of the top and lid") + def latch(self, move=None): + t = self.thickness + p = .05 * t + if self.move(8*t, 3*t, move, True): + return + + self.polyline(8*t, 90, 1.5*t-p, 90, 3*t, -90, 2*t+p, 90, + 2*t, 90, 2*t+p, -90, + 2.75*t, 45, t/4*2**0.5, 45, 1.25*t-p) + + self.move(8*t, 3*t, move) + + 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 @@ -63,6 +83,7 @@ def hole_dr(self): def top_hole(self): t = self.thickness + p = 0.05*t x, y, r = self.x, self.y, self.radius dr = self.hole_dr @@ -82,71 +103,38 @@ def top_hole(self): self.corner(90, r) if self.top == "lid_with_latches": - # We end up where we started, so move up further to the - # centre of the radiused corner - self.moveTo(0, r) # Note r=self.radius-dr - self.screw_slots(lx, ly, 3.3/2) - - @boxes.holeCol - def screw_slots(self, lx, ly, r): - """Make four slots with rounded ends, at the four corners. - - These slots allow screws to slide along them, to move the latches - in and out. - """ - for l in (lx, ly, lx, ly): - self.moveTo(0, 0, 45) - self.moveTo(0, -r) - self.corner(180, r) - self.edge(2*self.thickness) - self.corner(180, r) - self.edge(2*self.thickness) - self.moveTo(0, r, -45) - self.moveTo(l, 0, 90) - - def screw_clearance_slots(self): + # 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, 1.5*t, + 1.1*t, 4*t, center_y=False)) + + 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) - self.moveTo(0, r + t) - self.screw_slots(x - 2*r, y - 2*r, 5) + if r > dr: + r -= dr + else: + r = 0 - def latches(self): - """Screw holes for tapping on latches""" - t = self.thickness - x, y, r = self.x - 4*t, self.y - 4*t, self.radius - 2*t - - # we end up at(r, 0), where the curve joins the edge - for l in [y-2*r, x-2*r, y-2*r, x-2*r]: - self.hole(0, r, d=2.5) - # This will cut a finger out of the corner - self.moveTo(0, 0, 45) - self.edge(40) - self.corner(180, r/sqrt(2)) - self.edge(40) - self.moveTo(0, 0, -135) - self.moveTo(l, 0, 180) - - def latch_screw_slots(self): - """Slots for the screws to slide in""" - t = self.thickness - # This will be on the "top" plate with the hole, so it's - # actually bigger than nominal by t in all directions. - x, y, r = self.x + 2*t, self.y + 2*t, self.radius + t - screw_r = 3.3/2 - - # we end up at(r, 0), where the curve joins the edge - for l in [y-2*r, x-2*r, y-2*r, x-2*r]: - # This will cut a slot, and move from one end of the - # curve to the other - self.moveTo(0, r, 45) - self.moveTo(-2*t, -screw_r) - self.edge(2*t) - self.corner(180, screw_r) - self.edge(2*t) - self.corner(180, screw_r) - self.moveTo(0, 0, -45) - self.moveTo(l, 0, 90) + lx = x - 2*r - 2*dr + ly = y - 2*r - 2*dr + + self.latch_positions( + lx, ly, r, + lambda: ( + self.rectangularHole(0, 1.5*t, t, 2*t, center_y=False), + self.rectangularHole(0, 0.5*t, + 7*t, 11*t, r=7*t, center_y=False), + self.hole(0, 7*t, d=5*t), + self.rectangularHole(0, 0.5*t, + 7*t, 13*t, r=7*t, center_y=False))) def cb(self, nr): h = 0.5 * self.thickness @@ -204,20 +192,14 @@ def render(self): r+r_extra, "e", wallpieces=self.wallpieces, extend_corners=False, move="right", - callback=[self.screw_clearance_slots] if self.top == "lid_with_latches" else None) - - # A plate with the latches. - if self.top == "lid_with_latches": - dr = self.hole_dr - if r < dr: - raise ValueError("Latches only work with radius > %f." % dr) - self.roundedPlate(x - 2*dr, y - 2*dr, r - dr, "e", wallpieces=self.wallpieces, - extend_corners=False, move="right", - callback=[self.latches]) + callback=[self.latches] if self.top == "lid_with_latches" else None) # I don't know what this plate is for! 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") + if self.top == "lid_with_latches": + for i in range(4): + self.latch(move="right") From bd119ad57eb6894d5ce36973e7dc3f4bffb07274 Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Fri, 5 Sep 2025 12:55:50 +0200 Subject: [PATCH 03/11] Hide the latch slot when closed Slot is still showing in the unlocked position but that's not quite as bad. Hiding it then, too, will require further lengthening to slider. --- boxes/generators/roundedbox.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index b7863c47f..9255c6299 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -131,10 +131,10 @@ def latches(self): lambda: ( self.rectangularHole(0, 1.5*t, t, 2*t, center_y=False), self.rectangularHole(0, 0.5*t, - 7*t, 11*t, r=7*t, center_y=False), - self.hole(0, 7*t, d=5*t), + 7*t, 12*t, r=7*t, center_y=False), + self.hole(0, 8.5*t, d=5*t), self.rectangularHole(0, 0.5*t, - 7*t, 13*t, r=7*t, center_y=False))) + 7*t, 14*t, r=7*t, center_y=False))) def cb(self, nr): h = 0.5 * self.thickness From fd21731218f248ccb7cd153f9331118c73232903 Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Fri, 5 Sep 2025 13:43:18 +0200 Subject: [PATCH 04/11] Fix pre-commit --- boxes/generators/roundedbox.py | 1 - 1 file changed, 1 deletion(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 9255c6299..90092dd3e 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -14,7 +14,6 @@ # along with this program. If not, see . import boxes -from math import sqrt class RoundedBox(boxes.Boxes): From 2dd87cc40654c320bfb00998da4b2de47a9d3fa4 Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Fri, 5 Sep 2025 20:46:04 +0200 Subject: [PATCH 05/11] Make latches snap --- boxes/generators/roundedbox.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 90092dd3e..ff8c953cc 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -56,14 +56,18 @@ def __init__(self) -> None: def latch(self, move=None): t = self.thickness p = .05 * t - if self.move(8*t, 3*t, move, True): + l = 9*t + if self.move(l, 3*t, move, True): return - self.polyline(8*t, 90, 1.5*t-p, 90, 3*t, -90, 2*t+p, 90, + 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, 2*t, 90, 2*t+p, -90, - 2.75*t, 45, t/4*2**0.5, 45, 1.25*t-p) + 2.75*t, 45, t/4*2**0.5, 45, 1.25*t-p, 90) - self.move(8*t, 3*t, move) + self.move(l, 3*t, move) def latch_positions(self, x, y, r, callback): d = (1-0.5*2**0.5) * r @@ -107,8 +111,10 @@ def top_hole(self): self.moveTo(-r) self.latch_positions( lx, ly, r, - lambda: self.rectangularHole(0, 1.5*t, - 1.1*t, 4*t, center_y=False)) + lambda: ( + self.rectangularHole(0, 1.5*t, 1.1*t, 4*t, center_y=False), + self.rectangularHole(0, 7*t, 1.1*t, 0.7*t), + self.rectangularHole(0, 9*t, 1.1*t, 0.7*t))) def latches(self): t = self.thickness From e0ef44ca3906c9e0273a2490f79d8406e3194bf3 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 8 Sep 2025 16:14:38 +0100 Subject: [PATCH 06/11] Add hooks to the latches to avoid relying only on glue When the latches are deployed, they'll now hook on to the inner plate. This ought to mean that a weak glue joint won't lead to the box coming un-stuck unexpectedly. --- boxes/generators/roundedbox.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index ff8c953cc..5f6576f7d 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -64,7 +64,7 @@ def latch(self, move=None): 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, - 2*t, 90, 2*t+p, -90, + 3*t, 90, t, 90, t, -90, t+p, -90, 2.75*t, 45, t/4*2**0.5, 45, 1.25*t-p, 90) self.move(l, 3*t, move) @@ -134,12 +134,12 @@ def latches(self): self.latch_positions( lx, ly, r, lambda: ( - self.rectangularHole(0, 1.5*t, t, 2*t, center_y=False), - self.rectangularHole(0, 0.5*t, - 7*t, 12*t, r=7*t, center_y=False), + self.rectangularHole(0, 0.5*t, t, 3*t, center_y=False), + self.rectangularHole(0, -0.5*t, + 7*t, 13*t, r=7*t, center_y=False), self.hole(0, 8.5*t, d=5*t), - self.rectangularHole(0, 0.5*t, - 7*t, 14*t, r=7*t, center_y=False))) + self.rectangularHole(0, -0.5*t, + 7*t, 15*t, r=7*t, center_y=False))) def cb(self, nr): h = 0.5 * self.thickness @@ -199,7 +199,7 @@ def render(self): extend_corners=False, move="right", callback=[self.latches] if self.top == "lid_with_latches" else None) - # I don't know what this plate is for! + # This doesn't make a new plate, it just puts the wall in the right place self.roundedPlate(x, y, r, es, wallpieces=self.wallpieces, move="up only") # This is the wall (i.e. the vertical part) From 8f534b89a6ad55faebd3b830916ee16d2ee104ef Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Wed, 10 Sep 2025 20:28:16 +0200 Subject: [PATCH 07/11] RoundedBox: Support same options for bottom than top Thanks to Richard Bowman for the idea! --- boxes/generators/roundedbox.py | 61 +++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 5f6576f7d..db33e1e5f 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -50,7 +50,11 @@ 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", "lid_with_latches"], + 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 latch(self, move=None): @@ -84,7 +88,11 @@ def hole_dr(self): dr = self.thickness return dr - def top_hole(self): + 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 @@ -105,7 +113,7 @@ def top_hole(self): self.edge(l) self.corner(90, r) - if self.top == "lid_with_latches": + if style == "lid_with_latches": # We end up where we started # go to "corner" of the hole self.moveTo(-r) @@ -151,6 +159,7 @@ def cb(self, nr): def render(self): + _ = self.translations.gettext x, y, sh, r = self.x, self.y, self.sh, self.radius if self.outside: @@ -178,33 +187,37 @@ def render(self): ec = True with self.saved_context(): - # This plate is the base of the box - 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") - # This is the top plate (which has a hole in it, if requested) - self.roundedPlate(x, y, r, es, wallpieces=self.wallpieces, - extend_corners=ec, move="right", - callback=[self.top_hole] if self.top != "closed" else None) - # An additional plate for the lid, if requested - if self.top 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, - extend_corners=False, move="right", - callback=[self.latches] if self.top == "lid_with_latches" else None) - - # This doesn't make a new plate, it just puts the wall in the right place + + 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, move="up") - if self.top == "lid_with_latches": - for i in range(4): - self.latch(move="right") + for style in (self.top, self.bottom): + if style == "lid_with_latches": + for i in range(4): + self.latch(move="right") From 312501ebfd1c70aa864c2e9e6ef5499113caf542 Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Wed, 10 Sep 2025 20:52:09 +0200 Subject: [PATCH 08/11] Move latches more to the middle --- boxes/generators/roundedbox.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index db33e1e5f..504230daf 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -60,7 +60,7 @@ def __init__(self) -> None: def latch(self, move=None): t = self.thickness p = .05 * t - l = 9*t + l = 10*t if self.move(l, 3*t, move, True): return @@ -69,7 +69,7 @@ def latch(self, move=None): 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, - 2.75*t, 45, t/4*2**0.5, 45, 1.25*t-p, 90) + 3.75*t, 45, t/4*2**0.5, 45, 1.25*t-p, 90) self.move(l, 3*t, move) @@ -120,9 +120,9 @@ def top_hole(self, style): self.latch_positions( lx, ly, r, lambda: ( - self.rectangularHole(0, 1.5*t, 1.1*t, 4*t, center_y=False), - self.rectangularHole(0, 7*t, 1.1*t, 0.7*t), - self.rectangularHole(0, 9*t, 1.1*t, 0.7*t))) + 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))) def latches(self): t = self.thickness @@ -142,11 +142,11 @@ def latches(self): self.latch_positions( lx, ly, r, lambda: ( - self.rectangularHole(0, 0.5*t, t, 3*t, center_y=False), - self.rectangularHole(0, -0.5*t, + self.rectangularHole(0, 1.5*t, t, 3*t, center_y=False), + self.rectangularHole(0, 0.5*t, 7*t, 13*t, r=7*t, center_y=False), - self.hole(0, 8.5*t, d=5*t), - self.rectangularHole(0, -0.5*t, + self.hole(0, 9.5*t, d=5*t), + self.rectangularHole(0, 0.5*t, 7*t, 15*t, r=7*t, center_y=False))) def cb(self, nr): From 9f1fcb92438622de70bd1a77609d916f1b523617 Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Wed, 10 Sep 2025 20:57:03 +0200 Subject: [PATCH 09/11] Add new labels to example file --- examples/RoundedBox.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From f59e96cc90eb73086d8c7c1989ede68b4026f6b3 Mon Sep 17 00:00:00 2001 From: Florian Festi Date: Tue, 16 Sep 2025 11:39:40 +0200 Subject: [PATCH 10/11] Make match slider slightly smaller Help with proper nesting. --- boxes/generators/roundedbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boxes/generators/roundedbox.py b/boxes/generators/roundedbox.py index 504230daf..082d64728 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -143,8 +143,8 @@ def latches(self): lx, ly, r, lambda: ( self.rectangularHole(0, 1.5*t, t, 3*t, center_y=False), - self.rectangularHole(0, 0.5*t, - 7*t, 13*t, r=7*t, center_y=False), + self.rectangularHole(0, 0.55*t, + 6.95*t, 12.95*t, r=7*t, center_y=False), self.hole(0, 9.5*t, d=5*t), self.rectangularHole(0, 0.5*t, 7*t, 15*t, r=7*t, center_y=False))) From 285ee8e57b7d0edf73124487aa32bc38eff0ffc8 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Tue, 23 Sep 2025 14:53:24 +0100 Subject: [PATCH 11/11] Add more colors for cutting order. I've added Color.CUT, which is an array with (currently) 5 colors in it. The first is Color.INNER_CUT and the last is Color.OUTER_CUT: the fade between the two is linear. I've then used this to cut the latches in the correct order, and I've added (bodged) in an annotation showing the cut order that's intended. --- boxes/Color.py | 3 +++ boxes/generators/roundedbox.py | 45 +++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) 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 082d64728..70e971978 100644 --- a/boxes/generators/roundedbox.py +++ b/boxes/generators/roundedbox.py @@ -124,6 +124,32 @@ def top_hole(self, style): 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 @@ -142,12 +168,9 @@ def latches(self): self.latch_positions( lx, ly, r, lambda: ( - self.rectangularHole(0, 1.5*t, t, 3*t, center_y=False), - self.rectangularHole(0, 0.55*t, - 6.95*t, 12.95*t, r=7*t, center_y=False), - self.hole(0, 9.5*t, d=5*t), - self.rectangularHole(0, 0.5*t, - 7*t, 15*t, r=7*t, center_y=False))) + self.latch_holes(), + self.latch_slider(), + self.latch_cutout())) def cb(self, nr): h = 0.5 * self.thickness @@ -157,7 +180,17 @@ 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