From 4d1d7b625707af6a637d6ed7ee1f2e88e7eb155d Mon Sep 17 00:00:00 2001 From: Luca Date: Sun, 18 Jul 2021 22:51:14 +0200 Subject: [PATCH] Add script to generate KiCad footprints from keyboard layouts --- .gitignore | 2 + scripts/layout2footprint.py | 132 ++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100755 scripts/layout2footprint.py diff --git a/.gitignore b/.gitignore index bd70969..36c466f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ fp-info-cache # Exported BOM files *.xml *.csv + +*.old diff --git a/scripts/layout2footprint.py b/scripts/layout2footprint.py new file mode 100755 index 0000000..c2ff282 --- /dev/null +++ b/scripts/layout2footprint.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +from json import loads +from os.path import dirname, join, realpath +from time import time + +SCRIPT_DIR = dirname(realpath(__file__)) + +def compute_dimensions(layout): + max_x = 0 + + x = 0 + y = 0 + shape_override = {} + for row in layout: + # ignore metadata + if isinstance(row, dict): + continue + + for key in row: + if isinstance(key, dict): + x += key.get('x', 0) + y += key.get('y', 0) + shape_override = key + continue + + x += shape_override.get('w', 1) + shape_override = {} + + max_x = max(x, max_x) + x = 0 + y += 1 + + return (max_x, y) + +def make_rect(x, y, w, h): + return [ + (x, y ), + (x+w, y ), + (x+w, y+h), + (x, y+h), + ] + +def write(f, points, r): + points = ' '.join(map(lambda p: f'(xy {p[0]} {p[1]})', points)) + f.write(f' (fp_poly (pts {points}) (layer F.Cu) (width {2*r}))\n') + f.write(f' (fp_poly (pts {points}) (layer F.Mask) (width {2*r-0.05}))\n') + +def plot_shape(f, shape, args, offset_x, offset_y): + p = args.padding + r = args.radius + s = args.scale + + coord = lambda c: (c+p)*s+r + dim = lambda d: (d-2*p)*s-2*r + + points = make_rect(coord(shape['x'])+offset_x, coord(shape['y'])+offset_y, dim(shape['w']), dim(shape['h'])) + write(f, points, r) + + if shape['x'] != shape['x2'] or shape['y'] != shape['y2'] or shape['w'] != shape['w2'] or shape['h'] != shape['h2']: + points = make_rect(coord(shape['x2'])+offset_x, coord(shape['y2'])+offset_y, dim(shape['w2']), dim(shape['h2'])) + write(f, points, r) + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument('-p', '--padding', default=0.1, type=float) + parser.add_argument('-r', '--radius', default=0.1, type=float) + parser.add_argument('-s', '--scale', default=1.905, type=float) + parser.add_argument('layout', help='path to layout file') + parser.add_argument('name', help='name of generated footprint') + args = parser.parse_args() + + with open(args.layout) as f: + layout = loads(f.read()) + + w, h = compute_dimensions(layout) + offset_x = -w*args.scale/2 + offset_y = -h*args.scale/2 + + name = args.name + out_path = join(SCRIPT_DIR, f'../kezboard-pcb.pretty/{name}.kicad_mod') + with open(out_path, 'w') as f: + f.write(f''' +(module {name} (layer F.Cu) (tedit {hex(int(time()))[2:].upper()}) + (descr "tiny kezboard is tiny") + (attr virtual) + (fp_text reference REF** (at 0 {offset_y-1}) (layer F.SilkS) hide + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_text value {name} (at 0 {-(offset_y-1)}) (layer F.Fab) hide + (effects (font (size 1 1) (thickness 0.15))) + ) +'''.lstrip()) + + x = 0 + y = 0 + shape_override = {} + for row in layout: + # ignore metadata + if isinstance(row, dict): + continue + + for key in row: + if isinstance(key, dict): + x += key.get('x', 0) + y += key.get('y', 0) + shape_override = key + continue + + w = shape_override.get('w', 1) + h = shape_override.get('h', 1) + shape = { + 'x': x, + 'y': y, + 'w': w, + 'h': h, + 'x2': x+shape_override.get('x2', 0), + 'y2': y+shape_override.get('y2', 0), + 'w2': shape_override.get('w2', w), + 'h2': shape_override.get('h2', h), + } + + plot_shape(f, shape, args, offset_x, offset_y) + + x += shape['w'] + shape_override = {} + + x = 0 + y += 1 + + f.write(')\n')