-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboard_utils.py
More file actions
96 lines (75 loc) · 3.41 KB
/
board_utils.py
File metadata and controls
96 lines (75 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""
Board Utilities - Functions for extracting PCB board information
"""
import re
import os
def get_board_dimensions(pcb_path: str) -> tuple:
"""
Extract board dimensions from a KiCad PCB file by parsing Edge.Cuts.
Optimized: only reads lines containing Edge.Cuts to minimize memory/time.
Returns:
tuple: (width_mm, height_mm) or (None, None) if unable to parse
"""
if not os.path.exists(pcb_path):
return None, None
try:
x_coords = []
y_coords = []
# Read file line by line looking for Edge.Cuts
# Much faster than loading entire 13MB file into memory
with open(pcb_path, 'r', encoding='utf-8') as f:
buffer = ""
in_edge_cuts_element = False
paren_depth = 0
for line in f:
# Quick check - does this line or buffer have Edge.Cuts potential?
if '"Edge.Cuts"' in line or in_edge_cuts_element or 'gr_line' in line or 'gr_arc' in line or 'gr_rect' in line:
buffer += line
# Track parenthesis to know when element ends
for char in line:
if char == '(':
paren_depth += 1
elif char == ')':
paren_depth -= 1
# Check if we have a complete gr_* element with Edge.Cuts
if paren_depth == 0 and buffer.strip():
if '"Edge.Cuts"' in buffer and re.search(r'\(gr_(line|arc|rect|poly)', buffer):
# Extract coordinates
for coord_match in re.finditer(r'\((?:start|end|mid|xy)\s+([\d.-]+)\s+([\d.-]+)\)', buffer):
x_coords.append(float(coord_match.group(1)))
y_coords.append(float(coord_match.group(2)))
buffer = ""
in_edge_cuts_element = False
elif paren_depth > 0:
in_edge_cuts_element = True
if not x_coords or not y_coords:
return None, None
# Calculate bounding box
width_mm = max(x_coords) - min(x_coords)
height_mm = max(y_coords) - min(y_coords)
if width_mm <= 0 or height_mm <= 0:
return None, None
return round(width_mm, 2), round(height_mm, 2)
except Exception as e:
print(f"Error parsing board dimensions: {e}")
return None, None
def get_board_aspect_ratio(pcb_path: str) -> float:
"""
Get the aspect ratio (width/height) of the board.
Returns:
float: aspect ratio, or 1.0 if unable to determine
"""
width, height = get_board_dimensions(pcb_path)
if width and height and height > 0:
return width / height
return 1.0
def calculate_dimensions_for_width(target_width: int, aspect_ratio: float) -> int:
"""Calculate height for a given width and aspect ratio."""
if aspect_ratio <= 0:
return target_width
return max(100, int(target_width / aspect_ratio))
def calculate_dimensions_for_height(target_height: int, aspect_ratio: float) -> int:
"""Calculate width for a given height and aspect ratio."""
if aspect_ratio <= 0:
return target_height
return max(100, int(target_height * aspect_ratio))