From 3a14e1796b02938471c94b8e99e7d3c44bc635b5 Mon Sep 17 00:00:00 2001 From: Stefan Schwarz Date: Tue, 3 Mar 2020 23:12:47 +0100 Subject: [PATCH] Generator for keypads with mechanical switches --- boxes/generators/keypad.py | 126 ++++++++++++++++++++++++++++++++ static/samples/Keypad-thumb.jpg | Bin 0 -> 18066 bytes 2 files changed, 126 insertions(+) create mode 100644 boxes/generators/keypad.py create mode 100644 static/samples/Keypad-thumb.jpg diff --git a/boxes/generators/keypad.py b/boxes/generators/keypad.py new file mode 100644 index 0000000..addf866 --- /dev/null +++ b/boxes/generators/keypad.py @@ -0,0 +1,126 @@ +"""Generator for keypads with mechanical switches.""" + +from copy import deepcopy + +from boxes import Boxes +from boxes.edges import FingerJointSettings + + +class Keypad(Boxes): + """Generator for keypads with mechanical switches.""" + ui_group = 'Box' + btn_size = 15.6 + triangle = 25. + + def __init__(self): + super().__init__() + self.argparser.add_argument( + '--h', action='store', type=int, default=30, + help='height of the box' + ) + self.argparser.add_argument( + '--btn_x', action='store', type=int, default=3, + help='number of buttons in x-row' + ) + self.argparser.add_argument( + '--btn_y', action='store', type=int, default=4, + help='number of buttons in x-row' + ) + self.argparser.add_argument( + '--top_thickness', action='store', type=float, default=1.5, + help='thickness of the top layer, cherry needs 1.5mm or smaller' + ) + self.addSettingsArgs(FingerJointSettings, surroundingspaces=1) + + def _get_x_y(self): + """Gets the keypad's size based on the number of buttons.""" + x = self.btn_x * (self.btn_size) + (self.btn_x - 1) * 4 + 20 + y = self.btn_y * (self.btn_size) + (self.btn_y - 1) * 4 + 20 + return x, y + + def render(self): + """Renders the keypad.""" + # deeper edge for top to add multiple layers + deep_edge = deepcopy(self.edges['f'].settings) + deep_edge.thickness = self.thickness + self.top_thickness + deep_edge.edgeObjects(self, 'gGH', True) + + d1, d2 = 2., 3. + x, y = self._get_x_y() + h = self.h + + # box sides + self.rectangularWall(x, h, "GFEF", callback=[self.wallx_cb], move="right") + self.rectangularWall(y, h, "GfEf", callback=[self.wally_cb], move="up") + self.rectangularWall(y, h, "GfEf", callback=[self.wally_cb]) + self.rectangularWall(x, h, "GFEF", callback=[self.wallx_cb], move="left up") + + # electronics box lid + self.rectangularWall(x, y, "FFFF", move="right") + + # keypad lids + self.rectangularWall(x, y, "ffff", callback=[self.support_hole], move="up") + self.rectangularWall(x, y, "ffff", callback=[self.key_hole]) + + # screwable + tr = self.triangle + trh = tr / 3 + self.rectangularWall( + x, y, + callback=[lambda: self.hole(trh, trh, d=d2)] * 4, + move='left up' + ) + self.rectangularTriangle( + tr, tr, "ffe", num=4, + callback=[None, lambda: self.hole(trh, trh, d=d1)] + ) + + + def support_hole(self): + """Callback for the key stabelizers.""" + # draw clock wise to work with burn correction + btn = [11.6, (-90, 2)] * 4 + + s = self.btn_size + self.moveTo(10, 10) + for _ in range(self.btn_y): + for _ in range(self.btn_x): + self.moveTo(0, 2, 90) + self.polyline(*btn) + self.moveTo(0, 0, 270) + self.moveTo(s + 4, -2) + self.moveTo(self.btn_x * (s + 4) * -1, s + 4) + + def key_hole(self): + """Callback for the key holes.""" + # draw clock wise to work with burn correction + btn_half_side = [0.98, 90, 0.81, -90, 3.5, -90, 0.81, 90, 2.505] + btn_full_side = [*btn_half_side, 0, *btn_half_side[::-1]] + btn = [*btn_full_side, -90] * 4 + + s = self.btn_size + self.moveTo(10, 10) + for _ in range(self.btn_y): + for _ in range(self.btn_x): + self.moveTo(0.81, 0.81, 90) + self.polyline(*btn) + self.moveTo(0, 0, 270) + self.moveTo(-0.81, -0.81) + self.moveTo(s + 4) + self.moveTo(self.btn_x * (s + 4) * -1, s + 4) + + # stolen form electronics-box + def wallx_cb(self): + """Callback for triangle holes on x-side.""" + x, _ = self._get_x_y() + t = self.thickness + self.fingerHolesAt(0, self.h - 1.5 * t, self.triangle, 0) + self.fingerHolesAt(x, self.h - 1.5 * t, self.triangle, 180) + + # stolen form electronics-box + def wally_cb(self): + """Callback for triangle holes on y-side.""" + _, y = self._get_x_y() + t = self.thickness + self.fingerHolesAt(0, self.h - 1.5 * t, self.triangle, 0) + self.fingerHolesAt(y, self.h - 1.5 * t, self.triangle, 180) diff --git a/static/samples/Keypad-thumb.jpg b/static/samples/Keypad-thumb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf0596a6e627513c4b6654e84e78db708877adfe GIT binary patch literal 18066 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 literal 0 HcmV?d00001