Skip to content

Commit cfce35c

Browse files
committed
cng
1 parent 004651e commit cfce35c

4 files changed

Lines changed: 154 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ local_config.py
4747
*.tmp
4848
*.temp
4949
*.bak
50+
<<<<<<< Updated upstream
5051

5152
# Test artifacts
5253
test_*.stl
@@ -60,3 +61,6 @@ plate_generator.log
6061
# Debug/analysis scripts
6162
analyze_dxf.py
6263
draw_ascii.py
64+
=======
65+
.aider*
66+
>>>>>>> Stashed changes

extract_kle.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
2+
import sys
3+
import json
4+
from collections import defaultdict
5+
from pathlib import Path
6+
7+
sys.path.append('/home/mario/Documents/GitHub/KB_PLATE_VALIDATOR')
8+
from scripts.build_plate import find_kicad_switches, U1
9+
10+
def generate_kle_from_pcb(kicad_path):
11+
switches = find_kicad_switches(kicad_path)
12+
if not switches:
13+
print("No switches found!")
14+
return
15+
16+
# Sort switches by Y to group into rows
17+
switches.sort(key=lambda s: (round(s[1] / 5), s[0])) # Group Y within ~5mm
18+
19+
rows = []
20+
current_row = []
21+
last_y = None
22+
23+
for s in switches:
24+
x, y = s
25+
if last_y is None or abs(y - last_y) < 5.0:
26+
current_row.append((x, y))
27+
last_y = (last_y * len(current_row[:-1]) + y) / len(current_row) if last_y else y
28+
else:
29+
rows.append(current_row)
30+
current_row = [(x, y)]
31+
last_y = y
32+
if current_row:
33+
rows.append(current_row)
34+
35+
kle_rows = []
36+
for r in rows:
37+
r.sort(key=lambda s: s[0])
38+
row_keys = []
39+
last_x = r[0][0] - U1
40+
41+
for i, (x, y) in enumerate(r):
42+
# Calculate gap from last key center
43+
gap = (x - last_x) / U1
44+
45+
# This is a very rough heuristic for key width/spacing
46+
width = 1.0
47+
x_offset = 0
48+
49+
# If there's a significant gap, it's either an offset or a wide key
50+
# For the purpose of getting a functional KLE, we'll try to estimate width
51+
if i > 0:
52+
prev_x = r[i-1][0]
53+
dist = (x - prev_x) / U1
54+
55+
# If distance > 1.25, it might be a gap or a wide key
56+
if dist > 1.1:
57+
if dist >= 1.5:
58+
x_offset = dist - 1.0
59+
60+
# It's hard to guess exact widths from just centers, but let's try to emit basic keys
61+
# We'll rely on the user to adjust the exact modifiers, but this will give the structure
62+
if x_offset > 0.1:
63+
row_keys.append({"x": round(x_offset, 2)})
64+
65+
row_keys.append("") # Empty label
66+
last_x = x
67+
68+
kle_rows.append(row_keys)
69+
70+
print(json.dumps(kle_rows, separators=(',', ':')))
71+
72+
generate_kle_from_pcb('/home/mario/Documents/GitHub/TKL_VIDEO/TKL_Video/TKL_Video.kicad_pcb')

generate_custom_pcb.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
import json
3+
import re
4+
import math
5+
from pathlib import Path
6+
import sys
7+
8+
# Setup paths to use the project's internal parser
9+
PROJECT_ROOT = Path('/home/mario/Documents/GitHub/KB_PLATE_VALIDATOR')
10+
sys.path.append(str(PROJECT_ROOT))
11+
from scripts.build_plate import parse_kle, U1
12+
13+
# The KLE layout provided by the user
14+
kle_text = [
15+
["~\n`","!\n1","@\n2","#\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","+\n=",{"w":2},"Backspace"],
16+
[{"w":1.5},"Tab","Q","W","E","R","T","Y","U","I","O","P","{\n[","}\n]",{"w":1.5},"|\n\\"],
17+
[{"w":1.75},"Caps Lock","A","S","D","F","G","H","J","K","L",":\n;","\"\n'",{"w":2.25},"Enter"],
18+
[{"w":2},"Shift","Z","X","C","V","B","N","M","<\n,",">\n.","?\n/","Del","Up","Home"],
19+
[{"w":1.25},"Ctrl",{"w":1.25},"Win",{"w":1.25},"Alt",{"a":7,"w":6.25},"","Alt","Ctrl","Left","Down","Right"]
20+
]
21+
22+
keys, w_mm, h_mm = parse_kle(kle_text)
23+
24+
# KiCad v6+ S-expression template
25+
pcb_content = [
26+
'(kicad_pcb (version 20211014) (generator pcbnew)',
27+
' (general (thickness 1.6))',
28+
' (setup (stackup (layer "F.Cu" (type "copper") (thickness 0.035)) (layer "F.Paste" (type "solderpaste"))))',
29+
' (layers (0 "F.Cu" signal) (31 "B.Cu" signal) (44 "Edge.Cuts" user))',
30+
''
31+
]
32+
33+
# Add footprints
34+
for i, k in enumerate(keys):
35+
# KLE parser returns coordinates relative to top-left of the board
36+
# In KiCad, we'll place them exactly there.
37+
x, y = k['cx_u'] * U1, k['cy_u'] * U1
38+
39+
# We use the naming pattern 'SW_MX' which the validator regex looks for
40+
fp = f' (footprint "Button_Switch_Keyboard:SW_MX_1.00u" (layer "F.Cu") (at {x:.4f} {y:.4f})\n'
41+
fp += f' (tstamp {i:08x}-0000-0000-0000-000000000000)\n'
42+
fp += ' )'
43+
pcb_content.append(fp)
44+
45+
# Add a simple rectangle outline on Edge.Cuts
46+
pad = 2.0
47+
outline = [
48+
f' (gr_line (start {-pad} {-pad}) (end {w_mm+pad} {-pad}) (layer "Edge.Cuts") (width 0.1))',
49+
f' (gr_line (start {w_mm+pad} {-pad}) (end {w_mm+pad} {h_mm+pad}) (layer "Edge.Cuts") (width 0.1))',
50+
f' (gr_line (start {w_mm+pad} {h_mm+pad}) (end {-pad} {h_mm+pad}) (layer "Edge.Cuts") (width 0.1))',
51+
f' (gr_line (start {-pad} {h_mm+pad}) (end {-pad} {-pad}) (layer "Edge.Cuts") (width 0.1))'
52+
]
53+
pcb_content.extend(outline)
54+
pcb_content.append(')')
55+
56+
output_path = PROJECT_ROOT / 'custom_65.kicad_pcb'
57+
output_path.write_text('\n'.join(pcb_content))
58+
print(f"PCB generated at: {output_path}")
59+
print(f"Stats: {len(keys)} keys, Dimensions: {w_mm:.2f} x {h_mm:.2f} mm")

reproduce_issue.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
import json
3+
from scripts.build_plate import generate_plate
4+
from pathlib import Path
5+
6+
# Simple KLE with one 2u stabilized key
7+
kle_data = [
8+
["", {"w": 2}, ""]
9+
]
10+
11+
res = generate_plate(
12+
kle_text=json.dumps(kle_data),
13+
out_path="repro.dxf",
14+
gen_gerber=False,
15+
gen_stl=False
16+
)
17+
18+
print(f"Generated {res['out_path']}")
19+
print(f"Issues: {res['issues']}")

0 commit comments

Comments
 (0)