diff --git a/boxes/__init__.py b/boxes/__init__.py index a760b12e4..c70c437c0 100755 --- a/boxes/__init__.py +++ b/boxes/__init__.py @@ -677,6 +677,7 @@ def _buildObjects(self): **self.edgesettings.get("FingerJoint", {})) s.edgeObjects(self) self.addPart(edges.FingerHoles(self, s), name="fingerHolesAt") + self.addPart(edges.TriFingerHoles(self, s), name="triFingerHolesAt") # Stackable edges.StackableSettings(self.thickness, True, **self.edgesettings.get("Stackable", {})).edgeObjects(self) @@ -2980,9 +2981,9 @@ def polygonWalls(self, borders, h, bottom="F", top="F", symmetrical=True): t = self.thickness # XXX edge.margin() leftsettings = copy.deepcopy(self.edges["f"].settings) - lf, lF, lh = leftsettings.edgeObjects(self, add=False) + lf, lF, _, _ = leftsettings.edgeObjects(self, add=False) rightsettings = copy.deepcopy(self.edges["f"].settings) - rf, rF, rh = rightsettings.edgeObjects(self, add=False) + rf, rF, _, _ = rightsettings.edgeObjects(self, add=False) length_correction = 0. angle = borders[-1] diff --git a/boxes/edges.py b/boxes/edges.py index 1037aae49..e02628882 100644 --- a/boxes/edges.py +++ b/boxes/edges.py @@ -894,20 +894,22 @@ def checkValues(self) -> None: if abs(self.space + self.finger) < 0.1: raise ValueError("FingerJointSettings: space + finger must not be close to zero") - def edgeObjects(self, boxes, chars: str = "fFh", add: bool = True): + def edgeObjects(self, boxes, chars: str = "fFhƒ", add: bool = True): edges = [FingerJointEdge(boxes, self), FingerJointEdgeCounterPart(boxes, self), FingerHoleEdge(boxes, self), + TriFingerJointEdge(boxes, self), ] return self._edgeObjects(edges, boxes, chars, add) class FingerJointBase(ABC): """Abstract base class for finger joint.""" + finger_factor = 1 def calcFingers(self, length: float, bedBolts) -> tuple[int, float]: space, finger = self.settings.space, self.settings.finger # type: ignore - fingers = int((length - (self.settings.surroundingspaces - 1) * space) // (space + finger)) # type: ignore + fingers = int((length - (self.settings.surroundingspaces - 1) * space) // (space + self.finger_factor * finger)) # type: ignore # shrink surrounding space up to half a thickness each side if fingers == 0 and length > finger + 1.0 * self.settings.thickness: # type: ignore fingers = 1 @@ -915,7 +917,7 @@ def calcFingers(self, length: float, bedBolts) -> tuple[int, float]: fingers = 0 if bedBolts: fingers = bedBolts.numFingers(fingers) - leftover = length - fingers * (space + finger) + space + leftover = length - fingers * (space + self.finger_factor * finger) + space if fingers <= 0: fingers = 0 @@ -983,6 +985,20 @@ def draw_finger(self, f, h, style, positive: bool = True, firsthalf: bool = True else: self.polyline(0, 90, h, -90, f, -90, h, 90) + def addTriFingerSpace(self, i, fingers, pattern) -> bool: + # no double finger - no space + if self.finger_factor == 1: + return False + # ensure symmetric pattern + j = min(i, fingers - i - 1) + # check if space is needed + if (j % 2) == 0 and pattern == 'A': + return True + if (j % 2) == 1 and pattern == 'B': + return True + # otherwise no space + return False + def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): positive = self.positive @@ -1025,8 +1041,12 @@ def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): self.bedBoltHole(s, bedBoltSettings) else: self.edge(s) + if self.addTriFingerSpace(i, fingers, 'B'): + self.edge(f) self.draw_finger(f, h, style, positive, i < fingers // 2) + if self.addTriFingerSpace(i, fingers, 'A'): + self.edge(f) self.edge(leftover / 2.0, tabs=1) @@ -1088,14 +1108,14 @@ def __call__(self, x, y, length, angle=90, bedBolts=None, bedBoltSettings=None): self.ctx.rectangle(b, -self.settings.width / 2 + b, length - 2 * b, self.settings.width - 2 * b) for i in range(fingers): - pos = leftover / 2.0 + i * (s + f) + pos = leftover / 2.0 + i * (s + self.finger_factor * f) if bedBolts and bedBolts.drawBolt(i): d = (bedBoltSettings or self.boxes.bedBoltSettings)[0] self.boxes.hole(pos - 0.5 * s, 0, d * 0.5) - self.boxes.rectangularHole(pos + 0.5 * f, 0, - f + p, self.settings.width + p) + self.boxes.rectangularHole(pos + 0.5 * self.finger_factor * f, 0, + self.finger_factor * f + p, self.settings.width + p) class FingerHoleEdge(BaseEdge): @@ -1156,6 +1176,29 @@ def startWidth(self) -> float: return self.outset +# The TriFingerJoint can be used to join two edges from oposing sides into a +# single wall. The holes in the wall have doubled length and the fingers have +# two spaces (one space sized and on finger sizes). For each hole the order of +# the fingers changes, to ensure a tight fit, if only one side is used. joint +# will like like the following: +# w = wall, l/r = fingers from left/right side +# lr w rl w lr w ... w rl w lr +# Note: the order of the pairs "lr" and "rl" is kepts symmetric. This causes a +# rectangularWall() with two opposing TriFingerJointEdges to cover the left and +# right sied, as the edges of an rectangularWall() are printed clockwise. +class TriFingerJointEdge(FingerJointEdge): + """Tri finger joint edge""" + char = 'ƒ' + description = "Tri Finger Joint" + finger_factor = 2 + + +class TriFingerHoles(FingerHoles): + """Hole matching a tri finger joint edge""" + description = "Edge (parallel Tri Finger Joint Holes)" + finger_factor = 2 + + ############################################################################# #### Stackable Joints ############################################################################# diff --git a/boxes/generators/hobbycase.py b/boxes/generators/hobbycase.py index 27e480a24..3d2adbf53 100644 --- a/boxes/generators/hobbycase.py +++ b/boxes/generators/hobbycase.py @@ -44,14 +44,23 @@ def __init__(self) -> None: self.argparser.add_argument("--add_rails", action="store", type=boolarg, default=True, help="Should rails be generated for slots unpopulated by shelves?") self.argparser.add_argument("--add_cover", action="store", type=boolarg, default=True, help="Should cover for the case be generated?") self.argparser.add_argument("--inset_shelves", action="store", type=boolarg, default=True, help="Should the inner dividers and shelves be inset from the front edge?") + self.argparser.add_argument("--double_vertical_wall", action="store", type=boolarg, default=True, help="Use double vertical walls") self.addSettingsArgs(boxes.edges.StackableSettings, angle=90, width=0.0, height=2.0) def prepare(self): + # derive double_vertical_wall parameters + if self.double_vertical_wall: + self.wall_factor = 2 + self.shelve_edge = "f" + else: + self.wall_factor = 1 + self.shelve_edge = "ƒ" + self.cols = len(self.unit_w) self.sum_w = sum(self.unit_w) - self.inside_w = self.sum_w + 2 * (self.cols - 1) * self.thickness + self.inside_w = self.sum_w + self.wall_factor * (self.cols - 1) * self.thickness self.outside_w = self.inside_w + 2 * self.thickness self.sum_h = self.rows * self.unit_h @@ -86,7 +95,7 @@ def vertical_walls(self, move="up"): self.verticalWall(self.outside_depth, self.inside_h, label="left") - for i in range(2 * (self.cols - 1)): + for i in range(self.wall_factor * (self.cols - 1)): self.verticalWall(self.inside_depth, self.inside_h, label="vertical wall") self.verticalWall(self.outside_depth, self.inside_h, move="up", label="right") @@ -98,7 +107,7 @@ def verticalWall(self, x, y, edges="feff", move="right", label=None): self.rectangularWall(x, y, edges, callback=[self.slotsHolesCallback], move=move, label=label) def slotsHolesCallback(self): - self.cut_shelve_holes_in_single_column(self.inside_depth, 0) + self.cut_shelve_side_holes_in_single_column(self.inside_depth, 0) # Cover def cover(self, move="up"): @@ -120,7 +129,7 @@ def shelves(self, move="up"): y = self.inside_depth self.partsMatrix(self.shelves_n[columnIndex], 0, move, self.rectangularWall, - x, y, "efff", label=f"shelf (column {columnIndex})\n({x}x{y})") + x, y, "e" + self.shelve_edge + "f" + self.shelve_edge, label=f"shelf (column {columnIndex})\n({x}x{y})") # Rails def rails(self, move="up"): @@ -130,9 +139,9 @@ def rails(self, move="up"): def railSet(self, sideLength, backLength, move=None): self.ctx.save() - self.rectangularWall( sideLength,0, "feSe", move="right") + self.rectangularWall( sideLength,0, self.shelve_edge + "eSe", move="right") self.rectangularWall( backLength - 8*self.thickness,0, "feSe", move="right") - self.rectangularWall( sideLength,0, "feSe", move="right") + self.rectangularWall( sideLength,0, self.shelve_edge + "eSe", move="right") self.move(2*sideLength+backLength, 3 * self.thickness, move) # Base plate @@ -143,8 +152,8 @@ def base_plate(self, move="up"): def baseplate_callback(self): for col in range(self.cols): - posx = sum(self.unit_w[:col]) + col * 2 * self.thickness - self.cut_shelve_holes_in_single_column(self.unit_w[col], posx) + posx = sum(self.unit_w[:col]) + col * self.wall_factor * self.thickness + self.cut_shelve_back_holes_in_single_column(self.unit_w[col], posx) self.cut_double_wall_holes(self.inside_h) # Render @@ -162,10 +171,21 @@ def render(self): # Helper functions def cut_double_wall_holes(self, length): for col in range(1, self.cols): - posx = self.thickness + sum(self.unit_w[:col]) + (col-1) * 2 * self.thickness - self.doubleFingerHolesAt(posx, 0, length, angle=90) + posx = 0.5 * self.wall_factor * self.thickness + sum(self.unit_w[:col]) + (col-1) * self.wall_factor * self.thickness + if self.double_vertical_wall: + self.doubleFingerHolesAt(posx, 0, length, angle=90) + else: + self.fingerHolesAt(posx, 0, length, angle=90) - def cut_shelve_holes_in_single_column(self, length, posx = 0): + def cut_shelve_back_holes_in_single_column(self, length, posx = 0): for row in range(1, self.rows): posy = 0.5 * self.thickness + row * self.unit_h + (row-1) * self.thickness self.fingerHolesAt(posx, posy, length, angle=0) + + def cut_shelve_side_holes_in_single_column(self, length, posx = 0): + for row in range(1, self.rows): + posy = 0.5 * self.thickness + row * self.unit_h + (row-1) * self.thickness + if self.double_vertical_wall: + self.fingerHolesAt(posx, posy, length, angle=0) + else: + self.triFingerHolesAt(posx, posy, length, angle=0) diff --git a/examples/AllEdges.svg b/examples/AllEdges.svg index 126314e98..df48f6517 100644 --- a/examples/AllEdges.svg +++ b/examples/AllEdges.svg @@ -1,5 +1,5 @@ - +