diff --git a/boxes/generators/atreus21.py b/boxes/generators/atreus21.py index 5dc604c..8d7ac1a 100644 --- a/boxes/generators/atreus21.py +++ b/boxes/generators/atreus21.py @@ -4,13 +4,14 @@ from copy import deepcopy from boxes import Boxes, Color, holeCol, restore, boolarg from boxes.edges import FingerJointSettings +from .keyboard import Keyboard -class Atreus21(Boxes): +class Atreus21(Boxes, Keyboard): """Generator for a split atreus keyboard.""" ui_group = 'Misc' btn_size = 15.6 - btn_outer = btn_size + 3.4 + half_btn = btn_size / 2 border = 6 row_offsets=[3, 6, 11, 5, 0, btn_size * .5] @@ -82,73 +83,36 @@ class Atreus21(Boxes): corner = [90, b] self.polyline(*([x, corner, y, corner] * 2)) self.moveTo(0, b) - - def half(self, cb=None, reverse=False): + + def half(self, hole_cb=None, reverse=False): row_offsets=self.row_offsets row_keys=self.row_keys scheme = list(zip(row_offsets, row_keys)) - if reverse: - scheme.reverse() - for offset, keys in scheme: - self.moveTo(0, offset) - self.key_row(keys, cb) - self.moveTo(self.btn_outer) - self.moveTo(0, -offset) - - total_moved_rows = len(row_offsets) * (self.btn_outer) - self.moveTo(total_moved_rows * -1, 0) - - def key_row(self, n, hole_cb=None): - """Callback for the key holes.""" if hole_cb == None: hole_cb = self.key - for _ in range(n): - hole_cb() - self.moveTo(0, -n * (self.btn_outer)) + self.moveTo(self.half_btn, self.half_btn) + self.apply_callback_on_columns( + hole_cb, + scheme, + self.STANDARD_KEY_SPACING, + reverse, + ) + self.moveTo(-self.half_btn, -self.half_btn) def support(self): - btn = [11.6, (-90, 2)] * 4 - self.set_source_color(Color.BLUE) - self.moveTo(0, 2, 90) - self.polyline(*btn) - self.moveTo(-2, 0, 270) - self.moveTo(0, self.btn_outer) - self.set_source_color(Color.BLACK) + self.outer_hole() def hotplug(self): - self.moveTo(7.8 , 7.8) - self.hole(0, 0, d=4) - self.hole(1.27 * -3, 1.27 * 2, d=2.9) - self.hole(1.27 * 2, 1.27 * 4, d=2.9) - - # pcb mounts - self.hole(1.27 * -4, 0, d=1.7) - self.hole(1.27 * 4, 0, d=1.7) - - self.moveTo(-7.8, -7.8) - self.moveTo(0, self.btn_outer) + self.pcb_holes() def key(self): - self.set_source_color(Color.BLUE) - - # 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 - - self.moveTo(0.81, 0.81, 90) - self.polyline(*btn) - self.moveTo(0, 0, 270) - self.moveTo(-0.81, -0.81) - self.moveTo(0, self.btn_outer) - - self.set_source_color(Color.BLACK) + self.castle_shaped_plate_cutout() # get case sizes def _case_x_y(self): - x = len(self.row_offsets) * self.btn_outer - 4 + x = len(self.row_offsets) * self.STANDARD_KEY_SPACING - 4 y = sum([ - max(self.row_keys) * self.btn_outer, # total button sizes + max(self.row_keys) * self.STANDARD_KEY_SPACING, # total button sizes max(self.row_offsets), # offset of highest row -4, ]) diff --git a/boxes/generators/keyboard.py b/boxes/generators/keyboard.py new file mode 100644 index 0000000..2b8e0e2 --- /dev/null +++ b/boxes/generators/keyboard.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (C) 2021 Guillaume Collic +# +# 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 . + + +class Keyboard: + """ + Code to manage Cherry MX compatible switches and Kailh hotswap socket. + """ + + STANDARD_KEY_SPACING = 19 + SWITCH_CASE_SIZE = 15.6 + + def __init__(self): + pass + + def pcb_holes(self): + grid_unit = 1.27 + main_hole_size = 4 + pcb_mount_size = 1.7 + led_hole_size = 1 + pin_hole_size = 2.9 + + def grid_hole(x, y, d): + self.hole(grid_unit * x, grid_unit * y, d=d) + + # main hole + grid_hole(0, 0, main_hole_size) + + # switch pins + grid_hole(-3, 2, pin_hole_size) + grid_hole(2, 4, pin_hole_size) + + grid_hole(-4, 0, pcb_mount_size) + grid_hole(4, 0, pcb_mount_size) + + def apply_callback_on_columns(self, cb, columns_definition, spacing, reverse=False): + if reverse: + columns_definition = list(reversed(columns_definition)) + + for offset, nb_keys in columns_definition: + self.moveTo(0, offset) + for _ in range(nb_keys): + cb() + self.moveTo(0, spacing) + self.moveTo(spacing, -nb_keys * spacing) + self.moveTo(0, -offset) + + total_width = len(columns_definition) * spacing + self.moveTo(-1 * total_width) + + def outer_hole(self, radius=2, centered=True): + """ + Draws a rounded square big enough to go around a whole switch (15.6mm) + """ + half_size = Keyboard.SWITCH_CASE_SIZE / 2 + if centered: + self.moveTo(-half_size, -half_size) + + # draw clock wise to work with burn correction + straight_edge = Keyboard.SWITCH_CASE_SIZE - 2 * radius + polyline = [straight_edge, (-90, radius)] * 4 + self.moveTo(0, radius, 90) + self.polyline(*polyline) + self.moveTo(0, 0, 270) + self.moveTo(0, -radius) + self.moveTo(0) + + if centered: + self.moveTo(half_size, half_size) + + def castle_shaped_plate_cutout(self, centered=True): + """ + This cutout shaped like a castle enables switch modding and rotation. + More information (type 4) on https://geekhack.org/index.php?topic=59837.0 + """ + half_size = Keyboard.SWITCH_CASE_SIZE / 2 + if centered: + self.moveTo(-half_size, -half_size) + + # 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 + + self.moveTo(0.81, 0.81, 90) + self.polyline(*btn) + self.moveTo(0, 0, 270) + self.moveTo(-0.81, -0.81) + + if centered: + self.moveTo(half_size, half_size) \ No newline at end of file diff --git a/boxes/generators/keypad.py b/boxes/generators/keypad.py index 372a07b..d16f890 100644 --- a/boxes/generators/keypad.py +++ b/boxes/generators/keypad.py @@ -4,13 +4,16 @@ from copy import deepcopy from boxes import Boxes, boolarg from boxes.edges import FingerJointSettings +from .keyboard import Keyboard -class Keypad(Boxes): +class Keypad(Boxes, Keyboard): """Generator for keypads with mechanical switches.""" ui_group = 'Box' btn_size = 15.6 - triangle = 25. + space_between_btn = 4 + box_padding = 10 + triangle = 25.0 def __init__(self): super().__init__() @@ -45,8 +48,8 @@ class Keypad(Boxes): 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 + x = self.btn_x * (self.btn_size) + (self.btn_x - 1) * self.space_between_btn + 2*self.box_padding + y = self.btn_y * (self.btn_size) + (self.btn_y - 1) * self.space_between_btn + 2*self.box_padding return x, y def render(self): @@ -69,10 +72,10 @@ class Keypad(Boxes): self.rectangularWall(x, h, "GFEF", callback=[self.wallx_cb], move="left up") # keypad lids - self.rectangularWall(x, y, "ffff", callback=[self.support_hole], move="right") - self.rectangularWall(x, y, "ffff", callback=[self.key_hole], move="up") + self.rectangularWall(x, y, "ffff", callback=self.to_grid_callback(self.support_hole), move="right") + self.rectangularWall(x, y, "ffff", callback=self.to_grid_callback(self.key_hole), move="up") if self.top2_enable: - self.rectangularWall(x, y, "ffff", callback=[self.hotplug]) + self.rectangularWall(x, y, "ffff", callback=self.to_grid_callback(self.hotplug)) # screwable tr = self.triangle @@ -87,64 +90,27 @@ class Keypad(Boxes): callback=[None, lambda: self.hole(trh, trh, d=d1)] ) + def to_grid_callback(self, inner_callback): + scheme = [(0, self.btn_y)]*self.btn_x + def callback(): + # move to first key center + key_margin = self.box_padding + self.btn_size / 2 + self.moveTo(key_margin, key_margin) + self.apply_callback_on_columns( + inner_callback, scheme, self.btn_size + self.space_between_btn + ) + + return [callback] def hotplug(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(7.8 , 7.8) - - self.hole(0, 0, d=4) - self.hole(1.27 * -3, 1.27 * 2, d=2.9) - self.hole(1.27 * 2, 1.27 * 4, d=2.9) - - # pcb mounts - self.hole(1.27 * -4, 0, d=1.7) - self.hole(1.27 * 4, 0, d=1.7) - - self.moveTo(-7.8, -7.8) - self.moveTo(s + 4) - - #self.moveTo(1.27 * -2, 1.17 * 3) - #self.moveTo(1.27 * 2, 1.17 * -3) - self.moveTo(self.btn_x * (s + 4) * -1, s + 4) + self.pcb_holes() 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) + self.outer_hole() 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) + self.castle_shaped_plate_cutout() # stolen form electronics-box def wallx_cb(self):