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

Core: KiCAD Sch Fileformat #60

Merged
merged 20 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/faebryk/core/graphinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from faebryk.core.link import Link, LinkDirect, LinkNamedParent
from faebryk.libs.util import (
NotNone,
exceptions_to_log,
try_avoid_endless_recursion,
)

Expand Down Expand Up @@ -123,7 +124,8 @@ def connect(self, other: Self, linkcls=None) -> Self:
self.G.add_edge(self, other, link=link)

if logger.isEnabledFor(logging.DEBUG):
logger.debug(f"GIF connection: {link}")
with exceptions_to_log():
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to add this because with logging at debug it failed to import this file

The stringification of some edge in the graph was hitting a NotNone assertion

This is the best solution IMO though because we shouldn't be killing the program with debug exceptions that might just be caused by import-time execution

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying!

logger.debug(f"GIF connection: {link}")

return self

Expand Down
124 changes: 12 additions & 112 deletions src/faebryk/libs/kicad/fileformats.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import logging
import uuid
from dataclasses import dataclass, field
from enum import IntEnum, StrEnum, auto
from pathlib import Path
from typing import Any, Optional

from dataclasses_json import CatchAll, Undefined, dataclass_json

from faebryk.libs.kicad.fileformats_common import (
UUID,
C_stroke,
C_wh,
C_xy,
C_xyr,
C_xyz,
C_effects,
gen_uuid,
C_pts,
)
from faebryk.libs.sexp.dataclass_sexp import JSON_File, SEXP_File, SymEnum, sexp_field

logger = logging.getLogger(__name__)

Check failure on line 22 in src/faebryk/libs/kicad/fileformats.py

View workflow job for this annotation

GitHub Actions / test

Ruff (I001)

src/faebryk/libs/kicad/fileformats.py:1:1: I001 Import block is un-sorted or un-formatted

# TODO find complete examples of the fileformats, maybe in the kicad repo

Expand Down Expand Up @@ -473,90 +483,6 @@
unknown: CatchAll = None


class UUID(str):
pass


def gen_uuid(mark: str = ""):
# format: d864cebe-263c-4d3f-bbd6-bb51c6d2a608
value = uuid.uuid4().hex

suffix = mark.encode().hex()
if suffix:
value = value[: -len(suffix)] + suffix

DASH_IDX = [8, 12, 16, 20]
formatted = value
for i, idx in enumerate(DASH_IDX):
formatted = formatted[: idx + i] + "-" + formatted[idx + i :]

return UUID(formatted)


@dataclass
class C_xy:
x: float = field(**sexp_field(positional=True))
y: float = field(**sexp_field(positional=True))

def __sub__(self, other: "C_xy") -> "C_xy":
return C_xy(x=self.x - other.x, y=self.y - other.y)

def __add__(self, other: "C_xy") -> "C_xy":
return C_xy(x=self.x + other.x, y=self.y + other.y)

def rotate(self, center: "C_xy", angle: float) -> "C_xy":
import math

angle = -angle # rotate kicad style counter-clockwise

# Translate point to origin
translated_x = self.x - center.x
translated_y = self.y - center.y

# Convert angle to radians
angle = math.radians(angle)

# Rotate
rotated_x = translated_x * math.cos(angle) - translated_y * math.sin(angle)
rotated_y = translated_x * math.sin(angle) + translated_y * math.cos(angle)

# Translate back
new_x = rotated_x + center.x
new_y = rotated_y + center.y

return C_xy(x=new_x, y=new_y)


@dataclass
class C_xyz:
x: float = field(**sexp_field(positional=True))
y: float = field(**sexp_field(positional=True))
z: float = field(**sexp_field(positional=True))


@dataclass
class C_xyr:
x: float = field(**sexp_field(positional=True))
y: float = field(**sexp_field(positional=True))
r: float = field(**sexp_field(positional=True), default=0)


@dataclass
class C_wh:
w: float = field(**sexp_field(positional=True))
h: Optional[float] = field(**sexp_field(positional=True), default=None)


@dataclass
class C_stroke:
class E_type(SymEnum):
solid = auto()
default = auto()

width: float
type: E_type


@dataclass
class C_text_layer:
class E_knockout(SymEnum):
Expand All @@ -566,28 +492,6 @@
knockout: Optional[E_knockout] = field(**sexp_field(positional=True), default=None)


@dataclass
class C_effects:
@dataclass
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()

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)


class E_fill(SymEnum):
none = auto()
solid = auto()
Expand Down Expand Up @@ -658,10 +562,6 @@

@dataclass
class C_polygon:
@dataclass
class C_pts:
xys: list[C_xy] = field(**sexp_field(multidict=True), default_factory=list)

pts: C_pts


Expand Down Expand Up @@ -1013,7 +913,7 @@
island: Optional[bool] = field(
**sexp_field(positional=True), default=None
)
pts: C_polygon.C_pts
pts: C_pts

net: int
net_name: str
Expand Down
122 changes: 122 additions & 0 deletions src/faebryk/libs/kicad/fileformats_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import logging
import uuid
from dataclasses import dataclass, field
from enum import auto
from typing import Optional

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

logger = logging.getLogger(__name__)

# TODO find complete examples of the fileformats, maybe in the kicad repo


class UUID(str):
pass


@dataclass
class C_xy:
x: float = field(**sexp_field(positional=True))
y: float = field(**sexp_field(positional=True))

def __sub__(self, other: "C_xy") -> "C_xy":
return C_xy(x=self.x - other.x, y=self.y - other.y)

def __add__(self, other: "C_xy") -> "C_xy":
return C_xy(x=self.x + other.x, y=self.y + other.y)

def rotate(self, center: "C_xy", angle: float) -> "C_xy":
import math

angle = -angle # rotate kicad style counter-clockwise

# Translate point to origin
translated_x = self.x - center.x
translated_y = self.y - center.y

# Convert angle to radians
angle = math.radians(angle)

# Rotate
rotated_x = translated_x * math.cos(angle) - translated_y * math.sin(angle)
rotated_y = translated_x * math.sin(angle) + translated_y * math.cos(angle)

# Translate back
new_x = rotated_x + center.x
new_y = rotated_y + center.y

return C_xy(x=new_x, y=new_y)


@dataclass
class C_xyz:
x: float = field(**sexp_field(positional=True))
y: float = field(**sexp_field(positional=True))
z: float = field(**sexp_field(positional=True))


@dataclass
class C_xyr:
x: float = field(**sexp_field(positional=True))
y: float = field(**sexp_field(positional=True))
r: float = field(**sexp_field(positional=True), default=0)


@dataclass
class C_wh:
w: float = field(**sexp_field(positional=True))
h: Optional[float] = field(**sexp_field(positional=True), default=None)


@dataclass
class C_stroke:
class E_type(SymEnum):
solid = auto()
default = auto()

width: float
type: E_type


@dataclass
class C_effects:
@dataclass
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()

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)


@dataclass
class C_pts:
xys: list[C_xy] = field(**sexp_field(multidict=True), default_factory=list)


def gen_uuid(mark: str = ""):
# format: d864cebe-263c-4d3f-bbd6-bb51c6d2a608
value = uuid.uuid4().hex

suffix = mark.encode().hex()
if suffix:
value = value[: -len(suffix)] + suffix

DASH_IDX = [8, 12, 16, 20]
formatted = value
for i, idx in enumerate(DASH_IDX):
formatted = formatted[: idx + i] + "-" + formatted[idx + i :]

return UUID(formatted)
Loading
Loading