diff --git a/.gitignore b/.gitignore index 70caafc6..51b88216 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *~ *.plt *.svg +!examples/*.svg *.dxf *.pyc *.pwj diff --git a/boxes/generators/wallstackablebin.py b/boxes/generators/wallstackablebin.py new file mode 100644 index 00000000..56758cff --- /dev/null +++ b/boxes/generators/wallstackablebin.py @@ -0,0 +1,169 @@ +# Copyright (C) 2024 Alex Roberts +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from boxes import * +from boxes.walledges import _WallMountedBox + +class StackableBinEdge(edges.BaseEdge): + # Character used to identify this edge type in wall specifications + char = "j" + + def __call__(self, length, **kw): + # ASCII art showing edge profile geometry: + # _ + # | / + # | / b <- Angle between vertical and hyp2 + # | / + # | hyp2 / <- Upper hypotenuse length + # | / + # | / + # _ | / + # | | | + # | | | + # l h | <- Total height + # | | | + # _ _ | | ---- w ---- <- Width of slope projection + # | | \ + # | | \ + # f | hyp1 \ <- Lower hypotenuse length + # | | \ + # | | \ a <- Base angle from settings + # _ _ \ + + # Get configuration from settings + a = self.settings.angle # Base angle for slope + f = self.settings.front # Fraction of height for front slope + l = self.settings.label_height # Fraction of height for label + label = self.settings.label # Whether to include label area + h = self.settings.h # Total height of bin + + # If label is disabled, set label height to 0 + if not label: + l = 0 + + # Calculate key dimensions: + w = (h * f) * math.tan(math.radians(a)) # Width of slope projection + hyp1 = (h * f) / math.cos(math.radians(a)) # Length of lower slope + hyp2 = math.sqrt(w**2 + ((1-(l+f))*h)**2) # Length of upper slope + b = math.degrees(math.atan(w/((1-l-f)*h))) # Angle between vertical and hyp2 + + # Draw the edge profile sequence: + self.corner(-b) # Rotate to start upper diagonal + self.edges["e"](hyp2) # Draw upper diagonal section + self.corner(b) # Return to vertical + self.edges["f"](l*h) # Draw label section if enabled + self.corner(a) # Rotate to lower diagonal + self.edges["f"](hyp1) # Draw lower diagonal section + self.corner(-a) # Return to horizontal + + def margin(self) -> float: + # Calculate the margin required for this edge type + # Same calculation as width of slope projection + # Add material thickness if a label area is included + t = self.settings.thickness if self.settings.label else 0 + return (self.settings.h * self.settings.front) * math.tan(math.radians(self.settings.angle)) + t + +class WallStackableBin(_WallMountedBox): + """A wall-mounted bin that can stack or hang from a wall.""" + description = ''' +####Features: +- Configurable dimensions (width, height, depth) +- Adjustable front slope angle for easy access +- Optional label area on front face +- Stackable design with interlocking geometry +- Multiple wall mounting options + +####Stacking Compatibility: +The stacking mechanism requires careful consideration of wall mounting types. +Bins using "plain" wall mounts can stack on bins with other mount types. +Note: Using the "outside" dimension option is generally not compatible with stacking. + +####Assembly Notes: +1. The generator produces three pieces with angled finger joints. +Bottom panel, sloped front panel and label panel (if enabled). +2. Joint lengths vary to accommodate the slope angles +3. Orient pieces as shown in the generated layout to assemble correctly. +''' + def __init__(self) -> None: + super().__init__() + + self.addSettingsArgs(edges.StackableSettings, bottom_stabilizers=2.4) + + self.buildArgParser("outside") + self.buildArgParser(x=90, h=130, y=150) + + self.argparser.add_argument( + "--front", action="store", type=float, default=0.3, + help="fraction of bin height covered with slope") + self.argparser.add_argument( + "--angle", action="store", type=float, default=30, + help="angle of the bottom slope") + + self.argparser.add_argument( + "--label", action="store", type=boolarg, default=True, + help="include a label area on the front") + self.argparser.add_argument( + "--label_height", action="store", type=float, default=0.2, + help="fraction of bin height covered with label (if label is enabled)") + + def render(self): + # Generate standard wall mounting edges (A-D and vertical separator |) + self.generateWallEdges() + + # Add custom stackable bin edge for the front profile + self.addPart(StackableBinEdge(self, self)) + + # Configure angled finger joints for the sloped sections + # First set: For bottom-to-slope connection + angledsettings = copy.deepcopy(self.edges["f"].settings) + angledsettings.setValues(self.thickness, True, angle=90-self.angle) + angledsettings.edgeObjects(self, chars="gG") + + # Second set: For slope-to-label connection + angledsettings = copy.deepcopy(self.edges["f"].settings) + angledsettings.setValues(self.thickness, True, angle=self.angle) + angledsettings.edgeObjects(self, chars="kK") + + # Adjust dimensions if outside measurements specified + if self.outside: + self.x = self.adjustSize(self.x) + self.h = self.adjustSize(self.h, "s", "S") + self.y = self.adjustSize(self.y, "h", "b") + + with self.saved_context(): + # Bottom panel with finger joints + self.rectangularWall(self.x, self.y, "ffGf", label="bottom", move="up") + + if self.label: + # Sloped front with label area + self.rectangularWall(self.x, self.h*self.front/math.cos(math.radians(self.angle)), + "gFkF", label="slope", move="up") + # Label panel + self.rectangularWall(self.x, self.h*self.label_height, + "KFeF", label="label", move="up") + else: + # Sloped front without label + self.rectangularWall(self.x, self.h*self.front/math.cos(math.radians(self.angle)), + "gFeF", label="slope", move="up") + + # Back panel with wall mount edges + self.rectangularWall(self.x, self.h, "hCec", label="back", move="up") + + # Non drawn spacer to move wall pieces to the right + self.rectangularWall(self.x, 3, "DDDD", label="movement", move="right only") + + # Side panels with wall mount and stackable edges + self.rectangularWall(self.y, self.h, "sBSj", label="left side", move="up") + self.rectangularWall(self.y, self.h, "sBSj", label="right side", move="mirror up") diff --git a/examples/WallStackableBin.svg b/examples/WallStackableBin.svg new file mode 100644 index 00000000..05cb316e --- /dev/null +++ b/examples/WallStackableBin.svg @@ -0,0 +1,123 @@ + + + +WallStackableBin + + +WallMounted - WallStackableBin +boxes WallStackableBin +A wall-mounted bin that can stack or hang from a wall. + + +####Features: +- Configurable dimensions (width, height, depth) +- Adjustable front slope angle for easy access +- Optional label area on front face +- Stackable design with interlocking geometry +- Multiple wall mounting options + +####Stacking Compatibility: +The stacking mechanism requires careful consideration of wall mounting types. +Bins using "plain" wall mounts can stack on bins with other mount types. +Note: Using the "outside" dimension option is generally not compatible with stacking. + +####Assembly Notes: +1. The generator produces three pieces with angled finger joints. +Bottom panel, sloped front panel and label panel (if enabled). +2. Joint lengths vary to accommodate the slope angles +3. Orient pieces as shown in the generated layout to assemble correctly. + + +Created with Boxes.py (https://boxes.hackerspace-bamberg.de/) +Command line: boxes WallStackableBin +Command line short: boxes WallStackableBin + + + + + 100.0mm, burn:0.10mm + + + bottom + + + slope + + + label + + + + + + + + + back + + + + + + + + + + + + + + + + + + + left side + + + + + + + + + + + + + + + + + + + right side + + \ No newline at end of file diff --git a/static/samples/WallStackableBin-thumb.jpg b/static/samples/WallStackableBin-thumb.jpg new file mode 100644 index 00000000..a10d1808 Binary files /dev/null and b/static/samples/WallStackableBin-thumb.jpg differ diff --git a/static/samples/WallStackableBin.jpg b/static/samples/WallStackableBin.jpg new file mode 100644 index 00000000..e25d51e6 Binary files /dev/null and b/static/samples/WallStackableBin.jpg differ diff --git a/static/samples/samples.sha256 b/static/samples/samples.sha256 index 0e60f0af..9e229057 100644 --- a/static/samples/samples.sha256 +++ b/static/samples/samples.sha256 @@ -189,3 +189,4 @@ d7f7fd6c1b5a51c4fdfc21a03d55597f04d78383fef2138bc6ade4ee95676bc9 ../static/samp fec237bd76c18f1cad18a888f57299e5ff5033ab8032e7a26ea0b1259a42d150 ../static/samples/CompartmentBox-lid.jpg 5c2127a79948504f629ed792de539022411e428eb46dc8ad9c5286fb397cd603 ../static/samples/CompartmentBox.jpg ca53e3c8b9ba8d46ca2d6fdff24865514dde27380b409fef82f0b87ac0bada2d ../static/samples/HobbyCase.jpg +478769d7f422d0e47d50c7c1476b72a269f9acd03042d2d16dc0c00fa80941f6 ../static/samples/WallStackableBin.jpg