diff --git a/.gitignore b/.gitignore index 96b0c38e..70caafc6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ venv/ .vscode/ tracker.log *.isorted +.DS_Store diff --git a/boxes/generators/brick_sorter.py b/boxes/generators/brick_sorter.py new file mode 100644 index 00000000..a4c0c985 --- /dev/null +++ b/boxes/generators/brick_sorter.py @@ -0,0 +1,213 @@ +# Copyright (C) 2023 fidoriel +# +# 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 collections import OrderedDict +from typing import Tuple + +from boxes import * + + +class BrickSorter(Boxes): + """Stackable nestable sorting sieve for bricks""" + + description = """## Stackable nestable sorting sieve for bricks +A stackable sorting sieve for bricks, nestable for storage. +You will need to export all 5 levels, to get a full sieve. +If you feel you do not need the upper levels, just do not export them. +x,y,h are the dimensions for the largest sieve, +they will be the outer dimensions of the box, +the smaller sieves will be nested inside, therefor smaller. +Of course 256mm or 384mm (base plate size) are recommended values for x and y, +but you can use any value you like. + +Full set of all 5 levels: +![Full Set](static/samples/BrickSorter-2.jpg) + +![Full Set](static/samples/BrickSorter-3.jpg) + +Stacked for Usage: +![Stacked](static/samples/BrickSorter-4.jpg) + +Nested for Storage: +![Full Set](static/samples/BrickSorter-5.jpg) + +In Use: +![Full Set](static/samples/BrickSorter-6.jpg) +""" + + ui_group = "Box" + + # level name, size of the holes in mm, and the thickness of the grid + sieve_sizes = OrderedDict( + ( + ("large_sieve", (30, 5)), + ("medium_sieve", (20, 5)), + ("small_sieve", (15, 4)), + ("tiny_sieve", (10, 3)), + ) + ) + + bottom_edge: str = "h" + level: str + radius: int + wiggle: float + edge_width: int = 3 + + def __init__(self) -> None: + Boxes.__init__(self) + self.addSettingsArgs(edges.FingerJointSettings, edge_width=self.edge_width) + self.buildArgParser(x=256, y=256, h=120) + self.level_desc = list(self.sieve_sizes.keys()) + ["bottom"] + self.argparser.add_argument( + "--level", + action="store", + type=str, + default="large_sieve", + choices=self.level_desc, + help="Level of the nestable sieve", + ) + + self.argparser.add_argument( + "--radius", + action="store", + type=int, + default=3, + help="Radius of the corners of the sieve pattern in mm. Enter 30 for circular holes.", + ) + + self.argparser.add_argument( + "--wiggle", + action="store", + type=float, + default=4, + help="Wiggle room, that the layers can slide in each other." + ) + for action in self.argparser._actions: + if action.dest in ["x", "y"]: + action.help = "outer width of the most outer layer" + + @property + def _sieve_grid_thickness(self) -> int: + return self.sieve_sizes[self.level][1] + + @property + def _sieve_level_index(self) -> int: + """Return the index of the current sieve level, where 0 is the most outer sieve""" + return self.level_desc.index(self.level) + + @property + def _outer_height_after_nesting(self) -> float: + return self.h - (((self.edge_width + 1) * self.thickness) * self._sieve_level_index) - (self._sieve_level_index * 2) + + def _xy_after_nesting(self, a: float) -> float: + return a - ((2 * self.thickness + self.wiggle) * self._sieve_level_index) + + @property + def _outer_x_after_nesting(self) -> float: + return self._xy_after_nesting(self.x) + + @property + def _outer_y_after_nesting(self) -> float: + return self._xy_after_nesting(self.y) + + @property + def _level_hole_size(self) -> float: + return self.sieve_sizes[self.level][0] + + def _calc_hole_count(self, inner_mm_after_nesting: float) -> int: + return int( + (inner_mm_after_nesting - self._sieve_grid_thickness) + / (self._level_hole_size + self._sieve_grid_thickness) + ) + + def _calc_grid_size_width_offset( + self, inner_mm_after_nesting: float + ) -> Tuple[int, float]: + """Return the size of the grid and the offset from the outer top right corner""" + hole_count = self._calc_hole_count(inner_mm_after_nesting) + grid_size = ( + self._level_hole_size + self._sieve_grid_thickness + ) * hole_count + self._sieve_grid_thickness + offset = (inner_mm_after_nesting - grid_size) / 2 + return hole_count, offset + + def _draw_sieve(self, x: float, y: float) -> None: + if self.level == "bottom": + raise Exception("Cannot draw sieve pattern on bottom level") + + x_count, x_offset = self._calc_grid_size_width_offset(x) + y_count, y_offset = self._calc_grid_size_width_offset(y) + size = self._level_hole_size + + for relx in range(x_count): + for rely in range(y_count): + x_pos = ( + x + - x_offset + - size + - relx * (size + self._sieve_grid_thickness) + - self._sieve_grid_thickness + ) + y_pos = ( + y + - y_offset + - size + - rely * (size + self._sieve_grid_thickness) + - self._sieve_grid_thickness + ) + self.rectangularHole( + x=x_pos, + y=y_pos, + dx=size, + dy=size, + r=self.radius, + center_x=False, + center_y=False, + ) + + def render(self): + # this is directly adapted from ABox.render + x, y, h = ( + self._outer_x_after_nesting, + self._outer_y_after_nesting, + self._outer_height_after_nesting, + ) + + t1, t2, t3, t4 = "eeee" + b = self.edges.get(self.bottom_edge, self.edges["F"]) + sideedge = "F" + + self.x = x = self.adjustSize(x, sideedge, sideedge) + self.y = y = self.adjustSize(y) + self.h = h = self.adjustSize(h, b, t1) + + with self.saved_context(): + self.rectangularWall( + x, h, [b, sideedge, t1, sideedge], ignore_widths=[1, 6], move="up" + ) + self.rectangularWall( + x, h, [b, sideedge, t3, sideedge], ignore_widths=[1, 6], move="up" + ) + + if self.level == "bottom": + callback = None + else: + callback = [lambda: self._draw_sieve(x, y)] + self.rectangularWall(x, y, "ffff", move="up", callback=callback) + + self.rectangularWall( + x, h, [b, sideedge, t3, sideedge], ignore_widths=[1, 6], move="right only" + ) + self.rectangularWall(y, h, [b, "f", t2, "f"], ignore_widths=[1, 6], move="up") + self.rectangularWall(y, h, [b, "f", t4, "f"], ignore_widths=[1, 6], move="up") diff --git a/examples/BrickSorter.svg b/examples/BrickSorter.svg new file mode 100644 index 00000000..149216a0 --- /dev/null +++ b/examples/BrickSorter.svg @@ -0,0 +1,221 @@ + + + +BrickSorter + + +Box - BrickSorter +boxes BrickSorter +Stackable nestable sorting sieve for bricks + +## Stackable nestable sorting sieve for bricks +A stackable sorting sieve for bricks, nestable for storage. +You will need to export all 5 levels, to get a full sieve. +If you feel you do not need the upper levels, just do not export them. +x,y,h are the dimensions for the largest sieve, +they will be the outer dimensions of the box, +the smaller sieves will be nested inside, therefor smaller. +Of course 256mm or 384mm (base plate size) are recommended values for x and y, +but you can use any value you like. + +Full set of all 5 levels: +![Full Set](static/samples/BrickSorter-2.jpg) + +![Full Set](static/samples/BrickSorter-3.jpg) + +Stacked for Usage: +![Stacked](static/samples/BrickSorter-4.jpg) + +Nested for Storage: +![Full Set](static/samples/BrickSorter-5.jpg) + +In Use: +![Full Set](static/samples/BrickSorter-6.jpg) + + +Created with Boxes.py (https://festi.info/boxes.py) +Command line: boxes BrickSorter +Command line short: boxes BrickSorter + + + + + 100.0mm, burn:0.10mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/README.rst b/examples/README.txt similarity index 100% rename from examples/README.rst rename to examples/README.txt diff --git a/static/samples/BrickSorter-2-thumb.jpg b/static/samples/BrickSorter-2-thumb.jpg new file mode 100644 index 00000000..face87ab Binary files /dev/null and b/static/samples/BrickSorter-2-thumb.jpg differ diff --git a/static/samples/BrickSorter-2.jpg b/static/samples/BrickSorter-2.jpg new file mode 100644 index 00000000..c76cf1a2 Binary files /dev/null and b/static/samples/BrickSorter-2.jpg differ diff --git a/static/samples/BrickSorter-3-thumb.jpg b/static/samples/BrickSorter-3-thumb.jpg new file mode 100644 index 00000000..74e59473 Binary files /dev/null and b/static/samples/BrickSorter-3-thumb.jpg differ diff --git a/static/samples/BrickSorter-3.jpg b/static/samples/BrickSorter-3.jpg new file mode 100644 index 00000000..24aac100 Binary files /dev/null and b/static/samples/BrickSorter-3.jpg differ diff --git a/static/samples/BrickSorter-4-thumb.jpg b/static/samples/BrickSorter-4-thumb.jpg new file mode 100644 index 00000000..c349a28f Binary files /dev/null and b/static/samples/BrickSorter-4-thumb.jpg differ diff --git a/static/samples/BrickSorter-4.jpg b/static/samples/BrickSorter-4.jpg new file mode 100644 index 00000000..aea317f2 Binary files /dev/null and b/static/samples/BrickSorter-4.jpg differ diff --git a/static/samples/BrickSorter-5-thumb.jpg b/static/samples/BrickSorter-5-thumb.jpg new file mode 100644 index 00000000..6be02314 Binary files /dev/null and b/static/samples/BrickSorter-5-thumb.jpg differ diff --git a/static/samples/BrickSorter-5.jpg b/static/samples/BrickSorter-5.jpg new file mode 100644 index 00000000..db3e188a Binary files /dev/null and b/static/samples/BrickSorter-5.jpg differ diff --git a/static/samples/BrickSorter-6-thumb.jpg b/static/samples/BrickSorter-6-thumb.jpg new file mode 100644 index 00000000..a72fe5a2 Binary files /dev/null and b/static/samples/BrickSorter-6-thumb.jpg differ diff --git a/static/samples/BrickSorter-6.jpg b/static/samples/BrickSorter-6.jpg new file mode 100644 index 00000000..413e4f6c Binary files /dev/null and b/static/samples/BrickSorter-6.jpg differ diff --git a/static/samples/BrickSorter-thumb.jpg b/static/samples/BrickSorter-thumb.jpg new file mode 100644 index 00000000..9b99a95f Binary files /dev/null and b/static/samples/BrickSorter-thumb.jpg differ diff --git a/static/samples/BrickSorter.jpg b/static/samples/BrickSorter.jpg new file mode 100644 index 00000000..f7af52c4 Binary files /dev/null and b/static/samples/BrickSorter.jpg differ diff --git a/static/samples/samples.sha256 b/static/samples/samples.sha256 index 4e5c4f45..45441c26 100644 --- a/static/samples/samples.sha256 +++ b/static/samples/samples.sha256 @@ -165,3 +165,12 @@ f625a31c8f1f08341f8e4c0ba5d34524f92e258ca2ae3027774c399a200ddfc9 ../static/samp 94e37a41d8b873f39bd3e8c74465b012c8f861031c93f64fd6eda89af02015c0 ../static/samples/FlexBook-2.jpg 4d8b4d5467a88431ba24e893157a6e09997d74654cd7e27878d2a54b1ee751c6 ../static/samples/CoinBankSafe-closed.jpg bace3582c13ee543f09fd45752d4403e237d01541aaa4ea266e61e64fd12156a ../static/samples/FlexBook.jpg +0518ad5dfec317949f4a02b8bb4b60bcf781c82561e3f121dca1f2e2d0c5468c ../static/samples/BrickSorter-5.jpg +53ce98807aabf8fdd14e6fa9f0a3e405dadd8f4d7c936f8abc31f1572657763d ../static/samples/BrickSorter.jpg +f94c22f55d7067875d65c157a6dc221a09d383ace9234fee4b81544ab9cc4341 ../static/samples/Shadowbox-backlit.jpg +cf5315266705af168fcadf691dc58053e5623c3955bc782ca67087b0feff672e ../static/samples/BrickSorter-3.jpg +f51e3cb0e74e380beda4c0966ee770142ff4fa38c266ca026177f9f4978190a4 ../static/samples/BrickSorter-6.jpg +86b78170bfaada5aacf7f8246c5f190aa113bed1acb24e18e0a2f8395f3814b3 ../static/samples/BrickSorter-4.jpg +192047afde6a53a10715473e7a3efcf4e11e6d610f5b3fc658c2dfe329304856 ../static/samples/Shadowbox.jpg +a0865738425d5d9966dc6975d7e73559bac3c307c9614e8b48bae4abdf3efb5b ../static/samples/BrickSorter-2.jpg +2dcb314cdfa8b136b59288d2f4f7e501b4290ff68560216b6bedd779a32095ad ../static/samples/Shadowbox-diagram.jpg