kicad/fp_generator.py

163 lines
4.8 KiB
Python
Raw Permalink Normal View History

2022-09-23 01:14:27 -06:00
from datetime import datetime
from pathlib import Path
from typing import List, Literal, Optional, Tuple, Union
INCH = 25.4
class Footprint:
text_size = {
# layer: (size_x, size_y, stroke)
"F.SilkS": (0.030 * INCH, 0.030 * INCH, 0.007 * INCH),
"B.SilkS": (0.030 * INCH, 0.030 * INCH, 0.007 * INCH),
None: (0.025 * INCH, 0.025 * INCH, 0.005 * INCH),
}
line_size = {
"F_SilkS": 0.007,
"B_SilkS": 0.007,
None: 0.005,
2022-09-23 01:14:27 -06:00
}
def __init__(
self,
name: str,
lib: Union[Path, str],
2022-09-23 01:14:27 -06:00
description: Optional[str] = None,
keywords: Optional[List[str]] = None,
):
self.name = name
self.lib = Path(lib)
2022-09-23 01:14:27 -06:00
self.description = "" if description is None else description
self._pads = []
self._models = []
self._contents = (
"("
f'footprint "{self.name}"\n'
f' (version {datetime.now().strftime("%Y%m%d")})\n'
" (generator pcbnew)\n"
' (layer "F.Cu")\n'
" (attr smd)\n"
)
if description is not None:
self._contents += f' (descr "{description}")\n'
if keywords is not None:
self._contents += f' (tags "{" ".join(keywords)}")\n'
def add_pad(
self,
id: Union[int, str],
x: float,
y: float,
size: Union[float, Tuple[float]],
hole: Optional[float] = None,
shape: Optional[Literal["circle", "rect", "round_rect"]] = None,
):
try:
size = (float(size), float(size))
2022-09-23 19:17:09 -06:00
except TypeError:
2022-09-23 01:14:27 -06:00
size = tuple(size)
if shape not in [None, "circle", "rect", "round_rect"]:
raise ValueError
if hole is not None:
self._contents.replace("(attr smd)", "(attr through_hole)")
self._contents += (
2022-09-23 19:17:09 -06:00
f' (pad "{id}" {"thru_hole" if hole is not None else "smd"} {shape} '
+ f"(at {x} {y}) "
+ f"(size {size[0]} {size[1]}) "
+ (f"(drill {hole}) " if hole is not None else "")
+ ("(layers *.Cu *.Mask)" if hole is not None else '(layers "F.Cu" "F.Paste" "F.Mask")')
+ ")\n"
2022-09-23 01:14:27 -06:00
)
def add_model(
self,
path: Union[Path, str],
offset: Tuple[float] = (0, 0, 0),
rotate: Tuple[float] = (0, 0, 0),
scale: Tuple[float] = (1, 1, 1),
):
self._contents += (
f' (model "{str(Path(path).relative_to(self.lib))}" '
2022-09-23 01:14:27 -06:00
f"(offset (xyz {offset[0]} {offset[1]} {offset[2]})) "
f"(scale (xyz {scale[0]} {scale[1]} {scale[2]})) "
f"(rotate (xyz {rotate[0]} {rotate[1]} {rotate[2]})) "
")\n"
)
def add_text(
self,
name: str,
text: str,
x: float,
y: float,
layer: str,
rotation: float = 0,
size: Optional[Tuple[float]] = None,
hidden: bool = False,
):
if size is None:
if layer in self.text_size:
size = self.text_size[layer]
else:
size = self.text_size[None]
self._contents += (
f' (fp_text {name} "{text}" '
f"(at {x} {y} {rotation}) "
f'(layer "{layer}") '
f"{'hide' if hidden else ''}"
f"(effects (font (size {size[0]} {size[1]}) (thickness {size[2]}))) "
")\n"
)
def add_line(
self,
start: Tuple[float],
end: Tuple[float],
layer: str,
stroke: Optional[float] = None,
):
if stroke is None:
if layer in self.text_size:
stroke = self.text_size[layer][2]
else:
stroke = self.text_size[None][2]
stroke = float(stroke)
self._contents += (
f' (fp_line (start {start[0]} {start[1]}) (end {end[0]} {end[1]}) (layer "{layer}") (width {stroke}))\n'
)
def add_rect(
self,
start: Tuple[float],
end: Tuple[float],
layer: str,
stroke: Optional[float] = None,
fill: bool = False,
):
if stroke is None:
if layer in self.text_size:
stroke = self.text_size[layer][2]
else:
stroke = self.text_size[None][2]
stroke = float(stroke)
self._contents += (
" (fp_rect "
f"(start {start[0]} {start[1]}) "
f"(end {end[0]} {end[1]}) "
f"(stroke (width {stroke}) (type default)) "
f'(fill {"none" if not fill else "solidButIDontKnowWhatStringToUse"}) '
f'(layer "{layer}") '
")\n"
)
def write(self):
with open(self.lib / f"{self.name}.kicad_mod", "w") as f:
2022-09-23 01:14:27 -06:00
f.write(self._contents + ")")