Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into mawildoer/kicad-schematic-exporter-2
Browse files Browse the repository at this point in the history
  • Loading branch information
iopapamanoglou committed Sep 13, 2024
2 parents f5a9e6b + 024094a commit b51cb4c
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 50 deletions.
57 changes: 24 additions & 33 deletions src/faebryk/exporters/pcb/kicad/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
Point = Geometry.Point
Point2D = Geometry.Point2D

Justify = C_effects.C_justify.E_justify
Alignment = tuple[Justify, Justify, Justify]
Alignment_Default = (Justify.center_horizontal, Justify.center_vertical, Justify.normal)


def gen_uuid(mark: str = "") -> UUID:
return _gen_uuid(mark)
Expand Down Expand Up @@ -580,9 +584,19 @@ def insert_text(
at: C_xyr,
font: Font,
layer: str = "F.SilkS",
alignment=None,
alignment: Alignment | None = None,
knockout: bool = False,
):
if not alignment:
if layer.startswith("F."):
alignment = Alignment_Default
else:
alignment = (
Justify.center_horizontal,
Justify.center_vertical,
Justify.mirror,
)

self.pcb.gr_texts.append(
GR_Text(
text=text,
Expand All @@ -592,21 +606,7 @@ def insert_text(
else C_text_layer(layer),
effects=C_effects(
font=font,
justify=(
(
C_effects.E_justify.center,
C_effects.E_justify.center,
C_effects.E_justify.mirror,
)
if not layer.startswith("F.")
else (
C_effects.E_justify.center,
C_effects.E_justify.center,
C_effects.E_justify.normal,
)
)
if alignment is None
else alignment,
justify=alignment,
),
uuid=self.gen_uuid(mark=True),
)
Expand Down Expand Up @@ -1116,9 +1116,7 @@ def set_designator_position(
layer: Optional[C_text_layer] = None,
font: Optional[Font] = None,
knockout: Optional[C_text_layer.E_knockout] = None,
justify: Optional[
tuple[C_effects.E_justify, C_effects.E_justify, C_effects.E_justify]
] = None,
justify: Alignment | None = None,
):
for mod, fp in self.get_all_footprints():
reference = fp.propertys["Reference"]
Expand All @@ -1129,14 +1127,13 @@ def set_designator_position(
layer="F.SilkS" if fp.layer.startswith("F") else "B.SilkS"
)
)
reference.layer.knockout = (
knockout if knockout else reference.layer.knockout
)
reference.effects.font = font if font else reference.effects.font
if knockout:
reference.layer.knockout = knockout
if font:
reference.effects.font = font
if justify:
reference.effects.justifys = [C_effects.C_justify(justify)]

reference.effects.justify = (
justify if justify else reference.effects.justify
)
rot = rotation if rotation else reference.at.r

footprint_bbox = self.get_footprint_silkscreen_bbox(mod)
Expand Down Expand Up @@ -1168,13 +1165,7 @@ def add_git_version(
layer: str = "F.SilkS",
font: Font = Font(size=C_wh(1, 1), thickness=0.2),
knockout: bool = True,
alignment: tuple[
C_effects.E_justify, C_effects.E_justify, C_effects.E_justify
] = (
C_effects.E_justify.center,
C_effects.E_justify.center,
C_effects.E_justify.normal,
),
alignment: Alignment = Alignment_Default,
):
# check if gitcli is available
try:
Expand Down
51 changes: 40 additions & 11 deletions src/faebryk/libs/kicad/fileformats_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Optional

from faebryk.libs.sexp.dataclass_sexp import SymEnum, sexp_field
from faebryk.libs.util import KeyErrorAmbiguous

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -86,19 +87,47 @@ class C_font:
size: C_wh
thickness: Optional[float] = None

class E_justify(SymEnum):
center = ""
left = auto()
right = auto()
bottom = auto()
top = auto()
normal = ""
mirror = auto()
@dataclass
class C_justify:
class E_justify(SymEnum):
center_horizontal = ""
left = auto()
right = auto()
center_vertical = ""
bottom = auto()
top = auto()
normal = ""
mirror = auto()

justifys: list[E_justify] = field(
**sexp_field(positional=True), default_factory=list
)

font: C_font
justify: Optional[tuple[E_justify, E_justify, E_justify]] = None
# TODO: this should be a Union as it's actually a tuple with 3 positional
# and optional enums: (E_justify_horizontal, E_justify_vertical, E_mirrored)

# Legal:
# (justify mirror right)
# (justify bottom)
justifys: list[C_justify] = field(
**sexp_field(multidict=True), default_factory=list
)

def get_justifys(self) -> list[C_justify.E_justify]:
return [j_ for j in self.justifys for j_ in j.justifys]

def __post_init__(self):
justifys = set(self.get_justifys())

J = C_effects.C_justify.E_justify

def _only_one_of(lst: list[J]):
dups = [j for j in justifys if j in lst]
if len(dups) > 1:
raise KeyErrorAmbiguous(dups)

_only_one_of([J.mirror, J.normal])
_only_one_of([J.left, J.right, J.center_horizontal])
_only_one_of([J.top, J.bottom, J.center_vertical])


@dataclass
Expand Down
23 changes: 18 additions & 5 deletions src/faebryk/libs/sexp/dataclass_sexp.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,20 @@ def _decode[T](
raise ValueError(f"Unexpected symbol {v}")
continue

value_dict[f.name] = _convert(v, f.type, stack, f.name)
# positional list = var args
origin = get_origin(f.type)
if origin is list:
vs = []
next_val = v
# consume all values
while next_val is not None:
vs.append(next_val)
next_val = it.next(1, None)
out = _convert(vs, f.type, stack, f.name)
else:
out = _convert(v, f.type, stack, f.name)

value_dict[f.name] = out

# Check assertions ----------------------------------------------------
for f in fs:
Expand Down Expand Up @@ -356,11 +369,11 @@ def _append(_val):
val = getattr(t, name)

if sp.positional:
converted = _convert2(val)
# TODO only if Optional?
if converted is None:
if isinstance(val, list):
for v in val:
_append(_convert2(v))
continue
_append(converted)
_append(_convert2(val))
continue

def _append_kv(name, v):
Expand Down
4 changes: 3 additions & 1 deletion test/common/resources/test.kicad_pcb
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,15 @@
(property "Footprint" "logos:faebryk_logo"
(at 0 0 0)
(layer "F.Fab")
(hide yes)
(hide no)
(uuid "17374849-fed2-4a32-a7bf-154b6ae74f94")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
(justify mirror right)
(justify bottom)
)
)
(property "Datasheet" ""
Expand Down
57 changes: 57 additions & 0 deletions test/libs/kicad/test_fileformats.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pathlib import Path

from faebryk.libs.kicad.fileformats import (
C_effects,
C_footprint,
C_kicad_footprint_file,
C_kicad_fp_lib_table_file,
Expand Down Expand Up @@ -39,6 +40,9 @@
DUMP = ConfigFlag("DUMP", "dump load->save into /tmp")


DUMP = ConfigFlag("DUMP", descr="dump load->save into /tmp")


class TestFileFormats(unittest.TestCase):
def test_parser(self):
pcb = C_kicad_pcb_file.loads(PCBFILE)
Expand Down Expand Up @@ -83,6 +87,35 @@ def test_parser(self):
],
)

# Var args parser
effects = (
find(pcb.kicad_pcb.footprints, lambda f: f.name == "logos:faebryk_logo")
.propertys["Footprint"]
.effects
)
self.assertEqual(
effects.justifys[0].justifys,
[
C_effects.C_justify.E_justify.mirror,
C_effects.C_justify.E_justify.right,
],
)
self.assertEqual(
effects.justifys[1].justifys,
[
C_effects.C_justify.E_justify.bottom,
],
)

self.assertEqual(
effects.get_justifys(),
[
C_effects.C_justify.E_justify.mirror,
C_effects.C_justify.E_justify.right,
C_effects.C_justify.E_justify.bottom,
],
)

self.assertEqual(pro.pcbnew.last_paths.netlist, "../../faebryk/faebryk.net")

self.assertEqual(
Expand Down Expand Up @@ -126,13 +159,37 @@ def _b1_p1(pcb: C_kicad_pcb_file):
_b1_p1(pcb).drill = C_footprint.C_pad.C_drill(
C_footprint.C_pad.C_drill.E_shape.stadium, 0.5, 0.4
)

def _effects(pcb: C_kicad_pcb_file):
return (
find(pcb.kicad_pcb.footprints, lambda f: f.name == "logos:faebryk_logo")
.propertys["Datasheet"]
.effects
)

_effects(pcb).justifys.append(
C_effects.C_justify([C_effects.C_justify.E_justify.center_horizontal])
)
_effects(pcb).justifys.append(
C_effects.C_justify([C_effects.C_justify.E_justify.top])
)

pcb_reload = C_kicad_pcb_file.loads(pcb.dumps())

self.assertEqual(
NotNone(_b1_p1(pcb_reload).drill).shape,
C_footprint.C_pad.C_drill.E_shape.stadium,
)

# empty center string ignored
self.assertEqual(
_effects(pcb).get_justifys(),
[
C_effects.C_justify.E_justify.center_horizontal,
C_effects.C_justify.E_justify.top,
],
)

def test_dump_load_equality(self):
def test_reload(path: Path, parser: type[SEXP_File | JSON_File]):
loaded = parser.loads(path)
Expand Down

0 comments on commit b51cb4c

Please sign in to comment.