From e6feb1cd381b717bcf8c5f9c900936e8a18bb51b Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Tue, 22 Feb 2022 03:30:27 +0100 Subject: [PATCH] colorful PCB; better round rects --- mkbd65/key_mat.py | 79 +++++++++++++++++++++++++++------ mkbd65/lower_case.py | 101 +++++++++++++++++++++++++++++++++++++++++++ mkbd65/main.py | 24 ++++++++-- mkbd65/pcb.py | 88 +++++++++++++++++++++++++++++++++++++ mkbd65/phone.py | 10 +++-- mkbd65/util.py | 42 +++--------------- 6 files changed, 288 insertions(+), 56 deletions(-) create mode 100644 mkbd65/lower_case.py create mode 100644 mkbd65/pcb.py diff --git a/mkbd65/key_mat.py b/mkbd65/key_mat.py index e211d39..aba8cae 100644 --- a/mkbd65/key_mat.py +++ b/mkbd65/key_mat.py @@ -3,10 +3,9 @@ All sizes are in millimeters. """ from typing import Tuple -from solid import OpenSCADObject, cube, linear_extrude, text, translate, scale, rotate, cylinder, union, intersection, \ - color +from solid import OpenSCADObject, cube, linear_extrude, text, translate, scale, rotate, cylinder, union, color, square, offset -from util import round_rect, get_label_size, FILAMENT_LAYER, LAYOUTS, forward +from util import get_label_size, FILAMENT_LAYER, LAYOUTS, forward, right, backward def key( @@ -51,14 +50,23 @@ def key( ) ) * forward(v=2)( linear_extrude(height=dome_height / 2)( - round_rect(width=width, height=height, radius=corner_radius) + translate(v=(corner_radius, corner_radius, 0))( + offset(r=corner_radius)( + square(size=(width - 2 * corner_radius, height - 2 * corner_radius)) + ) + ) ) ), linear_extrude(height=2)( - round_rect(width=width, height=height, radius=corner_radius) + # round_rect(width=width, height=height, radius=corner_radius) + translate(v=(corner_radius, corner_radius, 0))( + offset(r=corner_radius)( + square(size=(width - 2 * corner_radius, height - 2 * corner_radius)) + ) + ) ) - ) - translate(v=(width / 2, height / 2, thickness + dome_height / 2 - dome_height * 2 * FILAMENT_LAYER))( - linear_extrude(height=dome_height * 2 * FILAMENT_LAYER)( + ) - translate(v=(width / 2, height / 2, thickness + dome_height / 2 - dome_height * 3 * FILAMENT_LAYER))( + linear_extrude(height=dome_height * 3 * FILAMENT_LAYER)( text(text=label, size=get_label_size(label), font=font, halign='center', valign='center') ) ) @@ -74,7 +82,9 @@ def key_mat( key_thickness: float, layout: str, variant: str, - font: str + font: str, + keys_h: int = 13, + keys_v: int = 5 ) -> OpenSCADObject: """ Generates elastic mat with fixed layout but variable sizes @@ -97,6 +107,8 @@ def key_mat( +---------------------------------------------+ \\ thickness + :param keys_v: + :param keys_h: :param variant: :param margin: left, right, top and bottom margin, can be single number to set all at once. :param padding: Horizontal and vertical padding, can be single number to set both at once. @@ -109,8 +121,6 @@ def key_mat( :param font: :return: """ - keys_h = 13 - keys_v = 5 spacebar_start = 4 # horizontal start key of space bar (0 index) spacebar_width = 3 # space bar length in keys @@ -128,6 +138,7 @@ def key_mat( thickness ]) + # add keys to mat for x in range(keys_h): for y in range(keys_v): if (x == keys_h - 1 and (y in [0, 2, 3] and layout.startswith("ansi") or y in [0, 2] and layout == "iso")) \ @@ -175,12 +186,52 @@ def key_mat( label=LAYOUTS[f"{layout}_{variant}"][y][x] )) + # add holes to keys for better "pressability" for x in range(keys_h): for y in range(keys_v): - mat -= translate(v=(padding_h + x * (key_width + padding_h) + 1, padding_v + (keys_v - y - 1) * (key_height + padding_v) + 1, 0))( - cube((key_width - 2, key_height - 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) - translate(v=(key_width / 2 - 1, key_height / 2 - 1, 0))( - cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + if (x == keys_h - 1 and (y in [0, 2, 3] and layout.startswith("ansi") or y in [0, 2] and layout == "iso")) \ + or (spacebar_start < x < spacebar_start + spacebar_width and y == keys_v - 1): + # skip for wide keys (backspace, enter, shift and space bar) + continue + elif x == keys_h - 1 and y == 1 and layout in ["ansi_big_enter", "iso"]: + # big enter + mat -= translate(v=(padding_h + x * (key_width + padding_h) + 1, padding_v + (keys_v - y - 2) * (key_height + padding_v) + 1, 0))( + cube((key_width - 2, 2 * key_height + padding_v - 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) - translate(v=(key_width / 2 - 1, key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) - translate(v=(key_width / 2 - 1, key_height + padding_v + key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) - translate(v=(key_width / 2 - 2, key_height / 2 - 1, 0))( + cube((2, padding_v + key_height, key_thickness + thickness - 2 * FILAMENT_LAYER)) + ) + ) + elif x == keys_h - 2 and (y == 0 or (y == 2 and layout == "ansi") or (y == 3 and layout != "iso")): + # wide key + mat -= translate(v=(padding_h + x * (key_width + padding_h) + 1, padding_v + (keys_v - y - 1) * (key_height + padding_v) + 1, 0))( + cube((2 * key_width + padding_h - 2, key_height - 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) - translate(v=(key_width / 2 - 1, key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) - translate(v=(key_width + padding_h + key_width / 2 - 1, key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) - translate(v=(key_width / 2 - 1, key_height / 2 - 2, 0))( + cube((padding_h + key_width, 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) + ) + ) + elif x == spacebar_start and y == keys_v - 1: + # space bar + mat -= translate(v=(padding_h + x * (key_width + padding_h) + 1, padding_v + (keys_v - y - 1) * (key_height + padding_v) + 1, 0))( + cube((spacebar_width * key_width + (spacebar_width - 1) * padding_h - 2, key_height - 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) - translate(v=(key_width / 2 - 1, key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) - translate(v=((spacebar_width - 1) * key_width + (spacebar_width - 1) * padding_h + key_width / 2 - 1, key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) - translate(v=(key_width / 2 - 1, key_height / 2 - 2, 0))( + cube(((spacebar_width - 1) * padding_h + (spacebar_width - 1) * key_width, 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) + ) + ) + else: + # normal key + mat -= translate(v=(padding_h + x * (key_width + padding_h) + 1, padding_v + (keys_v - y - 1) * (key_height + padding_v) + 1, 0))( + cube((key_width - 2, key_height - 2, key_thickness + thickness - 2 * FILAMENT_LAYER)) - translate(v=(key_width / 2 - 1, key_height / 2 - 1, 0))( + cylinder(d=2, h=key_thickness + thickness - 2 * FILAMENT_LAYER) + ) ) - ) return color("DarkSlateGray")(mat) diff --git a/mkbd65/lower_case.py b/mkbd65/lower_case.py new file mode 100644 index 0000000..993058e --- /dev/null +++ b/mkbd65/lower_case.py @@ -0,0 +1,101 @@ +from solid import OpenSCADObject, hull, translate, sphere, cube, scale, offset, square + +from util import PCB_WIDTH, PCB_HEIGHT, PCB_RADIUS + + +def lower_case(phone_width: float, phone_height: float, phone_radius: float) -> OpenSCADObject: + """ + Rounded bottom part of the case + + :param phone_width: + :param phone_height: + :param phone_radius: + :return: + """ + thickness = 2 + window_thickness = 0.5 + + return (scale(v=(1, 1, thickness / phone_radius))( + hull()( + translate(v=(phone_radius, phone_radius, 0))( + sphere(r=phone_radius), + translate(v=(0, phone_height - 2 * phone_radius, 0))( + sphere(r=phone_radius) + ), + translate(v=(phone_width - 2 * phone_radius, 0, 0))( + sphere(r=phone_radius) + ), + translate(v=(phone_width - 2 * phone_radius, phone_height - 2 * phone_radius, 0))( + sphere(r=phone_radius) + ) + ) + ) + ) * translate(v=(0, 0, -1 * thickness))( + cube([phone_width, phone_height, thickness]) + )) - translate(v=(2 * phone_radius, 2 * phone_radius, -1 * thickness / 2))( + scale(v=(1, 1, thickness))( + offset(r=phone_radius)( + square([phone_width - 4 * phone_radius, phone_height - 4 * phone_radius]) + ) + ) + ) - translate(v=(phone_width - PCB_WIDTH, phone_radius, -1 * thickness / 2 + window_thickness))( + scale(v=(1, 1, thickness))( + offset(r=PCB_RADIUS)( + square([PCB_WIDTH - 2 * PCB_RADIUS, PCB_HEIGHT - 2 * PCB_RADIUS]) + ) + ) + ) + + + +# width = 160.7; +# height = 74.1; +# radius = 10; +# thickness = 2; + +# pcb_radius = 5; +# pcb_width = 150; +# pcb_height = 64; +# window_thickness = 0.5; +# +# +# translate([0, 0, thickness]) { +# difference() { +# intersection() { +# scale([1, 1, thickness / radius]) { +# hull() { +# translate([radius, radius, 0]) { +# sphere(r=radius); +# translate([0, height-2*radius, 0]) { +# sphere(r=radius); +# } +# translate([width-2*radius, 0, 0]) { +# sphere(r=radius); +# } +# translate([width-2*radius, height-2*radius, 0]) { +# sphere(r=radius); +# } +# } +# } +# } +# translate([0, 0, -thickness]) { +# cube([width, height, thickness]); +# } +# } +# +# translate([2*radius, 2*radius, -thickness/2]) { +# scale([1, 1, thickness]) { +# offset(r=radius) { +# square([width - 4 * radius, height - 4 * radius]); +# } +# } +# } +# translate([width - pcb_width, radius, -1*thickness/2+window_thickness]) { +# scale([1,1,thickness]) { +# offset(r=pcb_radius) { +# square([pcb_width - 2 * pcb_radius, pcb_height - 2 * pcb_radius]); +# } +# } +# } +# } +# } \ No newline at end of file diff --git a/mkbd65/main.py b/mkbd65/main.py index 70b10d1..a0f6970 100755 --- a/mkbd65/main.py +++ b/mkbd65/main.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -""" -All sizes are in millimeters. -""" + import sys from subprocess import run from configparser import ConfigParser @@ -10,6 +8,7 @@ import click from solid import scad_render_to_file from key_mat import key_mat, key +from pcb import pcb from phone import phone @@ -56,6 +55,25 @@ def cli(layout, variant, stl, emf, amf, detail, config_file): layout=layout, variant=variant, font=config.get("key_mat", "font", fallback="DejaVu Sans:style=Bold") + )], + ["pcb", pcb( + key_width=config.getfloat("key_mat", "key_width", fallback=9), + key_height=config.getfloat("key_mat", "key_height", fallback=6), + key_padding=config.getfloat("key_mat", "padding", fallback=1) + )], + ["test_key_mat", key_mat( + margin=config.getfloat("key_mat", "margin", fallback=1), + padding=config.getfloat("key_mat", "padding", fallback=1), + thickness=config.getfloat("key_mat", "thickness", fallback=0.5), + key_width=config.getfloat("key_mat", "key_width", fallback=9), + key_height=config.getfloat("key_mat", "key_height", fallback=6), + key_radius=config.getfloat("key_mat", "key_corner_radius", fallback=1), + key_thickness=config.getfloat("key_mat", "key_thickness", fallback=2), + layout=layout, + variant=variant, + font=config.get("key_mat", "font", fallback="DejaVu Sans:style=Bold"), + keys_h=5, + keys_v=3 )] ] diff --git a/mkbd65/pcb.py b/mkbd65/pcb.py new file mode 100644 index 0000000..672ebd6 --- /dev/null +++ b/mkbd65/pcb.py @@ -0,0 +1,88 @@ +from solid import OpenSCADObject, color, linear_extrude, polygon, translate, scale, offset, square, union, cylinder + +from util import PCB_WIDTH, PCB_HEIGHT, PCB_RADIUS + + +def switch() -> OpenSCADObject: + """ + Alps SKTKABE010 + + 6 +----+ 5 + / \ + 7 + + 4 | 2mm + 8 + + 3 | + \ 4mm / + 1 +----+ 2 + + :return: + """ + thickness = 0.78 + + return color("Silver")( + linear_extrude(height=0.5)( + polygon(points=[(1, 0), (5, 0), (6, 1), (6, 3), (5, 4), (1, 4), (0, 3), (0, 1)]), + ) + ) + color("Black")( + translate(v=(3, 2, 0))( + cylinder(r=0.5, h=thickness) + ) + ) + + +def pcb(key_width: float, key_height: float, key_padding: float) -> OpenSCADObject: + """ + + :return: + """ + pcb_thickness = 0.5 + rows = 5 + columns = 12 + + board = color(c="Purple")( + translate(v=(PCB_RADIUS, PCB_RADIUS, pcb_thickness/2))( + scale(v=(1, 1, pcb_thickness))( + offset(r=PCB_RADIUS)( + square([PCB_WIDTH - 2 * PCB_RADIUS, PCB_HEIGHT - 2 * PCB_RADIUS]) + ) + ) + ) + ) + + switches = [] + margin_x = (PCB_WIDTH - (columns * key_width + (columns - 1) * key_padding)) / 2 + margin_y = 3 + + for x in range(columns): + for y in range(rows): + switches.append( + translate(v=(margin_x + x * (key_width + key_padding), margin_y + y * (key_height + key_padding), pcb_thickness))( + switch() + ) + ) + + return union()( + board, + *switches + ) + # TODO: Add ATMEGA and flat cable connector + + # rows = 5 + # columns = 12 + # hspacing = 4 + # vspacing = 3 + # width = 6 + # height = 4 + # + # board = color("Green")(cube([123, 49, 0.6])) + translate((13, 36, 0.6))( + # color("BlackPaint")(cube([7, 7, 1])) + # ) + translate((23, 35.75, 0.6))( + # color("BlackPaint")(cube([2.5, 2, 0.55])) + # ) + # + # for row in range(rows): + # for col in range(columns): + # board += translate((3.5 + (hspacing + width) * col, 2 + (vspacing + height) * row, 0.6))( + # switch() + # ) + # + # return board diff --git a/mkbd65/phone.py b/mkbd65/phone.py index 97782df..85d0b6d 100644 --- a/mkbd65/phone.py +++ b/mkbd65/phone.py @@ -2,9 +2,7 @@ All sizes are in millimeters. """ -from solid import OpenSCADObject, linear_extrude - -from util import round_rect +from solid import OpenSCADObject, linear_extrude, translate, offset, square def phone( @@ -24,6 +22,10 @@ def phone( :return: """ return linear_extrude(height=thickness)( - round_rect(width=width, height=height, radius=corner_radius) + translate(v=(corner_radius, corner_radius, 0))( + offset(r=corner_radius)( + square(size=(width - 2 * corner_radius, height - 2 * corner_radius)) + ) + ) ) diff --git a/mkbd65/util.py b/mkbd65/util.py index 80b52ac..474de80 100644 --- a/mkbd65/util.py +++ b/mkbd65/util.py @@ -1,8 +1,11 @@ from solid import polygon, circle, translate, OpenSCADObject - +PCB_RADIUS = 5 +PCB_WIDTH = 150 +PCB_HEIGHT = 64 FILAMENT_LAYER = 0.1 +# TODO: Add iso_fr (AZERTY) just for Slim :P LAYOUTS = { "ansi_en": [ ["Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "⌫", None], @@ -42,7 +45,9 @@ def get_label_size(label: str) -> float: :param label: :return: """ - if label in ["Esc", "Alt", "Fn", "Ä", "Ü", "Ö"]: + if label == "↵": + return 4 + elif label in ["Esc", "Alt", "Fn", "Ä", "Ü", "Ö"]: return 3 elif label == "Ctrl": return 2.5 @@ -50,39 +55,6 @@ def get_label_size(label: str) -> float: return 3.5 -def round_rect(width: float | int, height: float | int, radius: float | int) -> OpenSCADObject: - """ - Rectangle with rounded corners. - - Combines a polygon and four circles to form the round corners. - - O/-----\O - / \ - | | - \ / - O\_____/O - - :param width: Width of the rectangle - :param height: Height of the rectangle - :param radius: Radius of rectangle corners - :return: - """ - return polygon(points=[ - (0, radius), - (0, height - radius), - (radius, height), - (width - radius, height), - (width, height - radius), - (width, radius), - (width - radius, 0), - (radius, 0)] - ) \ - + translate(v=(radius, radius, 0))(circle(r=radius)) \ - + translate(v=(radius, height - radius, 0))(circle(r=radius)) \ - + translate(v=(width - radius, height - radius, 0))(circle(r=radius)) \ - + translate(v=(width - radius, radius, 0))(circle(r=radius)) - - def up(v: float) -> translate: return translate(v=(0, v, 0))