diff --git a/src/faebryk/exporters/pcb/kicad/transformer.py b/src/faebryk/exporters/pcb/kicad/transformer.py index a0460256..5fff8682 100644 --- a/src/faebryk/exporters/pcb/kicad/transformer.py +++ b/src/faebryk/exporters/pcb/kicad/transformer.py @@ -9,7 +9,7 @@ from dataclasses import fields from enum import Enum, auto from itertools import pairwise -from typing import Any, Callable, Iterable, List, Optional, Sequence, TypeVar +from typing import Any, Callable, List, Optional, Sequence, TypeVar import numpy as np from shapely import Polygon @@ -317,7 +317,11 @@ def get_footprint_silkscreen_bbox( self, cmp: Node ) -> None | tuple[Point2D, Point2D]: fp = self.get_fp(cmp) - silk_outline = [geo for geo in get_all_geos(fp) if geo.layer == "F.SilkS"] + silk_outline = [ + geo + for geo in get_all_geos(fp) + if geo.layer == ("F.SilkS" if fp.layer.startswith("F") else "B.SilkS") + ] extremes = list[C_xy]() @@ -569,27 +573,35 @@ def _delete(self, obj: Any, prefix: str = ""): self._get_pcb_list_field(obj, prefix=prefix).remove(obj) def insert_via( - self, coord: tuple[float, float], net: str, size_drill: tuple[float, float] + self, coord: tuple[float, float], net: Net, size_drill: tuple[float, float] ): self.pcb.vias.append( Via( at=C_xy(*coord), - size=C_wh(size_drill[0], size_drill[0]), + size=size_drill[0], drill=size_drill[1], layers=["F.Cu", "B.Cu"], - net=net, + net=net.number, uuid=self.gen_uuid(mark=True), ) ) def insert_text( - self, text: str, at: C_xyr, font: Font, layer: str = "F.SilkS", alignment=None + self, + text: str, + at: C_xyr, + font: Font, + layer: str = "F.SilkS", + alignment=None, + knockout: bool = False, ): self.pcb.gr_texts.append( GR_Text( text=text, at=at, - layer=layer, + layer=C_text_layer(layer, C_text_layer.E_knockout.knockout) + if knockout + else C_text_layer(layer), effects=C_effects( font=font, justify=( @@ -696,6 +708,7 @@ def insert_zone( ): # check if exists zones = self.pcb.zones + # TODO: zones is always emtpy list # TODO check bbox if isinstance(layers, str): @@ -751,6 +764,7 @@ def insert_zone( connect_pads=Zone.C_connect_pads( mode=Zone.C_connect_pads.E_mode.thermal_reliefs, clearance=0.2 ), + # filled_polygon=zone.filled_polygon, ) ) @@ -878,7 +892,7 @@ def _flip(x): at=C_xyr(0, 0, rot_angle), effects=C_effects(self.font), uuid=self.gen_uuid(mark=True), - layer="User.5", + layer=C_text_layer(layer="User.5"), ) ) @@ -886,8 +900,8 @@ def _flip(x): # TODO: make generic def connect_line_pair_via_radius( self, - line1: C_line, - line2: C_line, + line1: Line, + line2: Line, radius: float, ) -> tuple[Line, Arc, Line]: # Assert if the endpoints of the lines are not connected @@ -965,29 +979,112 @@ def connect_line_pair_via_radius( return new_line1, arc, new_line2 + def round_corners2( + self, lines: Sequence[Line], corner_radius_mm: float + ) -> list[Geom]: + """ + Round the corners between the lines. + """ + geos = [] + prev_line = lines[-1] + for i, line2 in enumerate(list(lines) + [lines[0]]): + new_line1, arc, new_line2 = self.connect_line_pair_via_radius( + prev_line, + line2, + corner_radius_mm, + ) + if i < len(lines): # Skip adding new_line1 in the last iteration + geos.append(new_line1) + geos.append(arc) + prev_line = new_line2 + + return geos + + def round_cornersx( + self, lines: Sequence[Line], corner_radius_mm: float + ) -> list[Geom]: + """ + Round the corners between the lines. + """ + arcs = list[Arc]() + for line_pair in pairwise(list(lines) + [lines[-1]]): + new_line1, arc, new_line2 = self.connect_line_pair_via_radius( + line_pair[0], + line_pair[1], + corner_radius_mm, + ) + arcs.append(arc) + + # create lines between every arc + lines = list[Line]() + for arc1, arc2 in pairwise(arcs + [arcs[-1]]): + lines.append( + Line( + start=arc1.end, + end=arc2.start, + stroke=C_stroke(0.05, C_stroke.E_type.solid), + layer="Edge.Cuts", + uuid=self.gen_uuid(mark=True), + ) + ) + + return [*lines, *arcs] + def round_corners( - self, geometry: Sequence[Geom], corner_radius_mm: float + self, lines: Sequence[Line], corner_radius_mm: float ) -> list[Geom]: """ - Round the corners of a geometry by replacing line pairs with arcs. + Round the corners between the lines. """ + geos = [] + prev_line = lines[0] + for line2 in lines[1:]: + new_line1, arc, new_line2 = self.connect_line_pair_via_radius( + prev_line, + line2, + corner_radius_mm, + ) + geos.extend([new_line1, arc]) + prev_line = new_line2 + + # Add the last new_line2 to complete the outline + geos.append(prev_line) + + return geos - def _transform(geo1: Geom, geo2: Geom) -> Iterable[Geom]: - if not isinstance(geo1, Line) or not isinstance(geo2, Line): - return (geo1,) + def round_corners3( + self, lines: Sequence[Line], corner_radius_mm: float + ) -> list[Geom]: + """ + Round the corners between the lines, including the last-to-first connection. + """ + geos = [] + first_new_line1 = None + prev_line = lines[-1] # Start with the last line + for i, line2 in enumerate(lines): new_line1, arc, new_line2 = self.connect_line_pair_via_radius( - geo1, - geo2, + prev_line, + line2, corner_radius_mm, ) - return new_line1, new_line2, arc - return [ - t_geo - for pair in pairwise(list(geometry) + [geometry[0]]) - for t_geo in _transform(*pair) - ] + if i == 0: + first_new_line1 = new_line1 + else: + geos.append([new_line1, arc]) + prev_line = new_line2 + + # Connect the last line back to the first + assert first_new_line1, "First new line is None, can't round corners" + final_new_line1, final_arc, _ = self.connect_line_pair_via_radius( + prev_line, + first_new_line1, + corner_radius_mm, + ) + geos.append([final_new_line1, final_arc]) + + return geos def create_rectangular_edgecut( self, @@ -1061,19 +1158,38 @@ def plot_arc(start, mid, end): plt.plot([geo.start.x, geo.end.x], [geo.start.y, geo.end.y]) plt.show() - def set_pcb_outline_complex( + def insert_pcb_outline( self, - geometry: List[Geom], + outline_coordinates: list[Point2D], remove_existing_outline: bool = True, corner_radius_mm: float = 0.0, ): """ Create a board outline (edge cut) consisting out of - different geometries + line geometries and optional rounded corners """ - # TODO: remove - # self.plot_board_outline(geometry) + # create line objects from coordinates + outline = list[C_line]() + for coordinate in outline_coordinates: + outline.append( + C_line( + start=C_xy(coordinate[0], coordinate[1]), + end=C_xy( + outline_coordinates[ + (outline_coordinates.index(coordinate) + 1) + % len(outline_coordinates) + ][0], + outline_coordinates[ + (outline_coordinates.index(coordinate) + 1) + % len(outline_coordinates) + ][1], + ), + stroke=C_stroke(0.05, C_stroke.E_type.solid), + layer="Edge.Cuts", + uuid=self.gen_uuid(mark=True), + ) + ) # remove existing lines on Egde.cuts layer if remove_existing_outline: @@ -1086,10 +1202,10 @@ def set_pcb_outline_complex( # round corners between lines if corner_radius_mm > 0: - geometry = self.round_corners(geometry, corner_radius_mm) + outline = self.round_corners(outline, corner_radius_mm) # create Edge.Cuts geometries - for geo in geometry: + for geo in outline: assert geo.layer == "Edge.Cuts", f"Geometry {geo} is not on Edge.Cuts layer" self.insert_geo(geo) diff --git a/src/faebryk/exporters/pcb/routing/routing.py b/src/faebryk/exporters/pcb/routing/routing.py index 92cbb30c..39535e3f 100644 --- a/src/faebryk/exporters/pcb/routing/routing.py +++ b/src/faebryk/exporters/pcb/routing/routing.py @@ -220,7 +220,7 @@ def route_net(self, net: F.Net): if i > 0 and switch_point not in nodes: transformer.insert_via( coord=out_to_pcb(switch_point), - net=pcb_net.name, + net=pcb_net, size_drill=(0.45, 0.25), ) diff --git a/src/faebryk/exporters/pcb/routing/util.py b/src/faebryk/exporters/pcb/routing/util.py index 5bef9acb..e81bc8c4 100644 --- a/src/faebryk/exporters/pcb/routing/util.py +++ b/src/faebryk/exporters/pcb/routing/util.py @@ -149,7 +149,7 @@ def apply_route_in_pcb(route: Route, transformer: "PCB_Transformer"): coord = round(obj.pos[0], 2), round(obj.pos[1], 2) transformer.insert_via( - net=pcb_net.name, + net=pcb_net, coord=coord, size_drill=obj.size_drill, ) @@ -163,7 +163,7 @@ def apply_route_in_pcb(route: Route, transformer: "PCB_Transformer"): transformer.insert_zone( net=pcb_net, - layer=layer_name, + layers=layer_name, polygon=obj.polygon, ) diff --git a/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py b/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py index eeabab69..d02aecde 100644 --- a/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py +++ b/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py @@ -74,7 +74,12 @@ def __preinit__(self): ) # TODO: just set to a 1N4148 - self.reset_diode.forward_voltage.merge(715 * P.mV) - self.reset_diode.reverse_leakage_current.merge(1 * P.uA) - self.reset_diode.current.merge(300 * P.mA) - self.reset_diode.max_current.merge(1 * P.A) + # self.reset_diode.forward_voltage.merge(715 * P.mV) + # self.reset_diode.reverse_leakage_current.merge(1 * P.uA) + # self.reset_diode.current.merge(150 * P.mA) + # self.reset_diode.max_current.merge(300 * P.mA) + + F.has_descriptive_properties_defined.add_properties_to( + self.reset_diode, + {"LCSC": "C2128"}, + ) diff --git a/src/faebryk/library/LED.py b/src/faebryk/library/LED.py index 2d9c0749..d18b50a5 100644 --- a/src/faebryk/library/LED.py +++ b/src/faebryk/library/LED.py @@ -17,6 +17,9 @@ class Color(Enum): BLUE = auto() YELLOW = auto() WHITE = auto() + ORANGE = auto() + WARM_WHITE = auto() + COLD_WHITE = auto() brightness: F.TBD[Quantity] max_brightness: F.TBD[Quantity] diff --git a/src/faebryk/library/PoweredLED.py b/src/faebryk/library/PoweredLED.py index 95db8264..d9405bcd 100644 --- a/src/faebryk/library/PoweredLED.py +++ b/src/faebryk/library/PoweredLED.py @@ -11,12 +11,17 @@ class PoweredLED(Module): current_limiting_resistor: F.Resistor led: F.LED + def __init__(self, low_side_resistor: bool = True): + self.low_side_resistor = low_side_resistor + def __preinit__(self): - self.power.hv.connect(self.led.anode) + self.power.hv.connect( + self.led.anode + ) if self.low_side_resistor else self.power.lv.connect(self.led.cathode) self.led.connect_via_current_limiting_resistor_to_power( self.current_limiting_resistor, self.power, - low_side=True, + low_side=self.low_side_resistor, ) self.current_limiting_resistor.allow_removal_if_zero() diff --git a/src/faebryk/libs/kicad/fileformats.py b/src/faebryk/libs/kicad/fileformats.py index 4e492707..e19ceeb3 100644 --- a/src/faebryk/libs/kicad/fileformats.py +++ b/src/faebryk/libs/kicad/fileformats.py @@ -622,6 +622,7 @@ class C_pts: class C_footprint: class E_attr(SymEnum): smd = auto() + dnp = auto() board_only = auto() through_hole = auto() exclude_from_pos_files = auto() @@ -884,7 +885,7 @@ class C_net: @dataclass class C_via: at: C_xy - size: C_wh + size: float drill: float net: int uuid: UUID diff --git a/src/faebryk/libs/picker/jlcpcb/jlcpcb.py b/src/faebryk/libs/picker/jlcpcb/jlcpcb.py index 6120d2e6..e7e539dc 100644 --- a/src/faebryk/libs/picker/jlcpcb/jlcpcb.py +++ b/src/faebryk/libs/picker/jlcpcb/jlcpcb.py @@ -47,7 +47,7 @@ # TODO dont hardcode relative paths BUILD_FOLDER = Path("./build") -CACHE_FOLDER = BUILD_FOLDER / Path("cache") +CACHE_FOLDER = Path("../cache") class JLCPCB_Part(LCSC_Part):