diff --git a/cadquery_helpers.py b/cadquery_helpers.py index 5d479cd..6438822 100644 --- a/cadquery_helpers.py +++ b/cadquery_helpers.py @@ -21,6 +21,7 @@ class StepAssembly: """ A STEP assembly. """ + def __init__(self, name: str): self.assembly = cq.Assembly(name=name) @@ -28,8 +29,7 @@ def __init__(self, name: str): for printer in Message.DefaultMessenger_s().Printers(): printer.SetTraceLevel(Message_Gravity.Message_Fail) - def add_body(self, body: cq.Workplane, name: str, color: cq.Color, - location: Optional[cq.Location] = None) -> None: + def add_body(self, body: cq.Workplane, name: str, color: cq.Color, location: Optional[cq.Location] = None) -> None: """ Add a body to the assembly. diff --git a/common.py b/common.py index 25e81f6..150d15a 100644 --- a/common.py +++ b/common.py @@ -1,6 +1,7 @@ """ Common functionality for generator scripts. """ + import collections import csv import os.path @@ -18,7 +19,7 @@ ('\r', '\\r'), ('\t', '\\t'), ('\v', '\\v'), - ('"', '\\"'), + ('"', '\\"'), ) @@ -150,10 +151,7 @@ def get_pad_uuids(base_lib_path: str, pkg_uuid: str) -> Dict[str, str]: """ with open(os.path.join(base_lib_path, 'pkg', pkg_uuid, 'package.lp'), 'r') as f: lines = f.readlines() - opt_matches = [ - re.match(r' \(pad ([^\s]*) \(name "([^"]*)"\)\)$', line) - for line in lines - ] + opt_matches = [re.match(r' \(pad ([^\s]*) \(name "([^"]*)"\)\)$', line) for line in lines] matches = list(filter(None, opt_matches)) mapping = {} for match in matches: @@ -169,6 +167,7 @@ def human_sort_key(key: str) -> List[Any]: Function that can be used for natural sorting, where "PB2" comes before "PB10" and after "PA3". """ + def _convert(text: str) -> Union[int, str]: return int(text) if text.isdigit() else text diff --git a/dfn_configs.py b/dfn_configs.py index 5e5a9d0..907530a 100644 --- a/dfn_configs.py +++ b/dfn_configs.py @@ -10,49 +10,44 @@ # Maximal lead width as a function of pitch, Table 4 in the JEDEC # standard MO-229F, available (with registration!) from # https://www.jedec.org/system/files/docs/MO-229F.pdf -LEAD_WIDTH = { - 0.95: 0.45, - 0.8: 0.35, - 0.65: 0.35, - 0.5: 0.30, - 0.4: 0.25 -} +LEAD_WIDTH = {0.95: 0.45, 0.8: 0.35, 0.65: 0.35, 0.5: 0.30, 0.4: 0.25} # Toe and heel length as a function of pitch # According to IPC-7351C, see slide 26 of # http://ocipcdc.org/archive/What_is_New_in_IPC-7351C_03_11_2015.pdf LEAD_TOE_HEEL = { 1.00: 0.35, - 0.95: 0.35, # not specified in standard + 0.95: 0.35, # not specified in standard 0.8: 0.33, 0.65: 0.31, 0.50: 0.29, 0.40: 0.27, - 0.35: 0.25 + 0.35: 0.25, } class DfnConfig: - def __init__(self, - length: float, - width: float, - pitch: float, - pin_count: int, - height_nominal: float, - height_max: float, - lead_length: float, - exposed_width: float, - exposed_length: float, - keywords: str, - no_exp: bool = True, # By default we create variants w/o exp - print_pad: bool = False, # By default, the pad length is not in the full name - lead_width: Optional[float] = None, - name: Optional[str] = None, - create_date: Optional[str] = None, - library: Optional[str] = None, - pin1_corner_dx_dy: Optional[float] = None, # Some parts have a triangular pin1 marking - extended_doc_fn: Optional[Callable[['DfnConfig', Callable[[str], str], List[str]], None]] = None, - ): + def __init__( + self, + length: float, + width: float, + pitch: float, + pin_count: int, + height_nominal: float, + height_max: float, + lead_length: float, + exposed_width: float, + exposed_length: float, + keywords: str, + no_exp: bool = True, # By default we create variants w/o exp + print_pad: bool = False, # By default, the pad length is not in the full name + lead_width: Optional[float] = None, + name: Optional[str] = None, + create_date: Optional[str] = None, + library: Optional[str] = None, + pin1_corner_dx_dy: Optional[float] = None, # Some parts have a triangular pin1 marking + extended_doc_fn: Optional[Callable[['DfnConfig', Callable[[str], str], List[str]], None]] = None, + ): self.length = length self.width = width self.pitch = pitch @@ -60,9 +55,9 @@ def __init__(self, self.height = height_max self.height_nominal = height_nominal - self.exposed_width = exposed_width # E2 - self.exposed_length = exposed_length # D2 - self.no_exp = no_exp # Option with noexp + self.exposed_width = exposed_width # E2 + self.exposed_length = exposed_length # D2 + self.no_exp = no_exp # Option with noexp self.lead_length = lead_length self.print_pad = print_pad @@ -75,12 +70,12 @@ def __init__(self, try: self.toe_heel = LEAD_TOE_HEEL[pitch] except KeyError: - raise NotImplementedError("No toe/heel length for pitch {:s}".format(pitch)) + raise NotImplementedError('No toe/heel length for pitch {:s}'.format(pitch)) self.keywords = keywords self.name = name self.create_date = create_date - self.library = library or "LibrePCB_Base.lplib" + self.library = library or 'LibrePCB_Base.lplib' self.extended_doc_fn = extended_doc_fn @@ -91,11 +86,11 @@ def __init__(self, DfnConfig(1.5, 1.5, 0.5, 4, 0.95, 1.00, 0.55, 0.70, 0.10, 'V1515D,VBBD'), DfnConfig(1.5, 1.5, 0.5, 4, 0.75, 0.80, 0.55, 0.70, 0.10, 'W1515D,WBBD'), # Square, 2.0 x 2.0 - DfnConfig(2.0, 2.0, 0.65, 6, 0.95, 1.00, 0.30, 1.58, 0.65, 'V2020C,VCCC'), # no nominal exp_pad + DfnConfig(2.0, 2.0, 0.65, 6, 0.95, 1.00, 0.30, 1.58, 0.65, 'V2020C,VCCC'), # no nominal exp_pad DfnConfig(2.0, 2.0, 0.5, 4, 0.95, 1.00, 0.55, 1.20, 0.60, 'V2020D-1,VCCD-1'), DfnConfig(2.0, 2.0, 0.5, 4, 0.75, 0.80, 0.55, 1.20, 0.60, 'W2020D-1,WCCD-1'), - DfnConfig(2.0, 2.0, 0.5, 6, 0.95, 1.00, 0.40, 1.75, 0.80, 'V2020D-4,VCCD-4', no_exp=False), # no nominal exp_pad - DfnConfig(2.0, 2.0, 0.5, 6, 0.75, 0.80, 0.40, 1.75, 0.80, 'W2020D-4,WCCD-4', no_exp=False), # no nominal exp_pad + DfnConfig(2.0, 2.0, 0.5, 6, 0.95, 1.00, 0.40, 1.75, 0.80, 'V2020D-4,VCCD-4', no_exp=False), # no nominal exp_pad + DfnConfig(2.0, 2.0, 0.5, 6, 0.75, 0.80, 0.40, 1.75, 0.80, 'W2020D-4,WCCD-4', no_exp=False), # no nominal exp_pad DfnConfig(2.0, 2.0, 0.5, 6, 0.95, 1.00, 0.55, 1.20, 0.60, 'V2020D-2,VCCD-2'), DfnConfig(2.0, 2.0, 0.5, 6, 0.75, 0.80, 0.55, 1.20, 0.60, 'W2020D-2,WCCD-2'), DfnConfig(2.0, 2.0, 0.5, 8, 0.95, 1.00, 0.30, 1.20, 0.60, 'V2020D-3,VCCD-3'), @@ -113,22 +108,26 @@ def __init__(self, DfnConfig(3.0, 3.0, 0.95, 6, 0.75, 0.80, 0.55, 1.50, 1.20, 'W3030A-2,WEEA-2'), DfnConfig(3.0, 3.0, 0.8, 6, 0.95, 1.00, 0.50, 2.20, 1.30, 'V3030B,VEEB'), DfnConfig(3.0, 3.0, 0.8, 6, 0.75, 0.80, 0.55, 2.20, 1.30, 'W3030B,WEEB'), - DfnConfig(3.0, 3.0, 0.65, 8, 0.95, 1.00, 0.30, 2.25, 1.30, 'V3030C-1,VEEC-1'), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.65, 8, 0.95, 1.00, 0.40, 2.50, 1.75, 'V3030C-2,VEEC-2', no_exp=False), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.65, 8, 0.75, 0.80, 0.40, 2.50, 1.75, 'W3030C-2,WEEC-2', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.65, 8, 0.95, 1.00, 0.30, 2.25, 1.30, 'V3030C-1,VEEC-1'), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.65, 8, 0.95, 1.00, 0.40, 2.50, 1.75, 'V3030C-2,VEEC-2', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.65, 8, 0.75, 0.80, 0.40, 2.50, 1.75, 'W3030C-2,WEEC-2', no_exp=False), # no nominal exp_pad DfnConfig(3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.55, 2.20, 1.60, 'V3030D-1,VEED-1'), DfnConfig(3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.55, 2.00, 1.20, 'W3030D-1,WEED-1'), - DfnConfig(3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.40, 2.70, 1.75, 'V3030D-4,VEED-4', no_exp=False), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.40, 2.70, 1.75, 'W3030D-4,WEED-4', no_exp=False), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.55, 2.50, 1.50, 'V3030D-6,VEED-6', no_exp=False), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.55, 2.50, 1.50, 'W3030D-6,WEED-6', no_exp=False), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.45, 1.60, 1.60, 'V3030D-7,VEED-7', no_exp=False), # no nominal pad length and exp_pad - DfnConfig(3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.45, 1.60, 1.60, 'W3030D-7,WEED-7', no_exp=False), # no nominal pad length and exp_pad + DfnConfig(3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.40, 2.70, 1.75, 'V3030D-4,VEED-4', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.40, 2.70, 1.75, 'W3030D-4,WEED-4', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.55, 2.50, 1.50, 'V3030D-6,VEED-6', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.55, 2.50, 1.50, 'W3030D-6,WEED-6', no_exp=False), # no nominal exp_pad + DfnConfig( + 3.0, 3.0, 0.5, 8, 0.95, 1.00, 0.45, 1.60, 1.60, 'V3030D-7,VEED-7', no_exp=False + ), # no nominal pad length and exp_pad + DfnConfig( + 3.0, 3.0, 0.5, 8, 0.75, 0.80, 0.45, 1.60, 1.60, 'W3030D-7,WEED-7', no_exp=False + ), # no nominal pad length and exp_pad DfnConfig(3.0, 3.0, 0.5, 10, 0.95, 1.00, 0.55, 2.20, 1.60, 'V3030D-2,VEED-2', print_pad=True), DfnConfig(3.0, 3.0, 0.5, 10, 0.75, 0.80, 0.55, 2.00, 1.20, 'W3030D-2,WEED-2'), DfnConfig(3.0, 3.0, 0.5, 10, 0.95, 1.00, 0.30, 2.20, 1.60, 'V3030D-3,VEED-3', print_pad=True), - DfnConfig(3.0, 3.0, 0.5, 10, 0.95, 1.00, 0.40, 2.70, 1.75, 'V3030D-5,VEED-5', no_exp=False), # no nominal exp_pad - DfnConfig(3.0, 3.0, 0.5, 10, 0.75, 0.80, 0.40, 2.70, 1.75, 'W3030D-5,WEED-5', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.5, 10, 0.95, 1.00, 0.40, 2.70, 1.75, 'V3030D-5,VEED-5', no_exp=False), # no nominal exp_pad + DfnConfig(3.0, 3.0, 0.5, 10, 0.75, 0.80, 0.40, 2.70, 1.75, 'W3030D-5,WEED-5', no_exp=False), # no nominal exp_pad # Square, 3.5 x 3.5 DfnConfig(3.5, 3.5, 0.5, 10, 0.95, 1.00, 0.55, 2.70, 2.10, 'V3535D-1,VFFD-1'), DfnConfig(3.5, 3.5, 0.5, 10, 0.75, 0.80, 0.55, 2.70, 2.10, 'W3535D-1,WFFD-1'), @@ -137,8 +136,8 @@ def __init__(self, # Square, 4.0 x 4.0 DfnConfig(4.0, 4.0, 0.8, 8, 0.95, 1.00, 0.55, 3.00, 2.20, 'V4040B,VGGB'), DfnConfig(4.0, 4.0, 0.8, 8, 0.75, 0.80, 0.55, 3.00, 2.20, 'W4040B,WGGB'), - DfnConfig(4.0, 4.0, 0.65, 10, 0.95, 1.00, 0.40, 3.50, 2.80, 'V4040C,VGGC', no_exp=False), # no nominal exp_pad - DfnConfig(4.0, 4.0, 0.65, 10, 0.75, 0.80, 0.40, 3.50, 2.80, 'W4040C,WGGC', no_exp=False), # no nominal exp_pad + DfnConfig(4.0, 4.0, 0.65, 10, 0.95, 1.00, 0.40, 3.50, 2.80, 'V4040C,VGGC', no_exp=False), # no nominal exp_pad + DfnConfig(4.0, 4.0, 0.65, 10, 0.75, 0.80, 0.40, 3.50, 2.80, 'W4040C,WGGC', no_exp=False), # no nominal exp_pad DfnConfig(4.0, 4.0, 0.5, 10, 0.95, 1.00, 0.55, 3.20, 2.60, 'V4040D-1,VGGD-1'), DfnConfig(4.0, 4.0, 0.5, 10, 0.75, 0.80, 0.55, 3.00, 2.20, 'W4040D-1,WGGD-1'), DfnConfig(4.0, 4.0, 0.5, 12, 0.95, 1.00, 0.55, 3.20, 2.60, 'V4040D-2,VGGD-2'), @@ -158,16 +157,22 @@ def __init__(self, DfnConfig(2.0, 2.5, 0.8, 4, 0.75, 0.80, 0.55, 1.00, 0.70, 'W2025B,WCDB'), DfnConfig(2.0, 2.5, 0.5, 6, 0.95, 1.00, 0.55, 1.00, 0.70, 'V2025D-1,VCDD-1'), DfnConfig(2.0, 2.5, 0.5, 6, 0.75, 0.80, 0.55, 1.00, 0.70, 'W2025D-1,WCDD-1'), - DfnConfig(2.0, 2.5, 0.5, 8, 0.95, 1.00, 0.55, 1.10, 0.80, 'V2025D-2,VCDD-2'), # no nominal exp_pad - DfnConfig(2.0, 2.5, 0.5, 8, 0.75, 0.80, 0.55, 1.10, 0.80, 'W2025D-2,WCDD-2'), # no nominal exp_pad + DfnConfig(2.0, 2.5, 0.5, 8, 0.95, 1.00, 0.55, 1.10, 0.80, 'V2025D-2,VCDD-2'), # no nominal exp_pad + DfnConfig(2.0, 2.5, 0.5, 8, 0.75, 0.80, 0.55, 1.10, 0.80, 'W2025D-2,WCDD-2'), # no nominal exp_pad # Rectangular, Type 1, 2.0 x 3.0 - DfnConfig(2.0, 3.0, 0.5, 6, 0.95, 1.00, 0.40, 1.00, 1.20, 'V2030D-1,VCED-1', no_exp=False), # no nominal exp_pad - DfnConfig(2.0, 3.0, 0.5, 6, 0.75, 0.80, 0.40, 1.00, 1.20, 'W2030D-1,WCED-1', no_exp=False), # no nominal exp_pad - DfnConfig(2.0, 3.0, 0.5, 8, 0.95, 1.00, 0.40, 1.75, 1.90, 'V2030D-2,VCED-2', no_exp=False), # no nominal exp_pad - DfnConfig(2.0, 3.0, 0.5, 8, 0.75, 0.80, 0.40, 1.75, 1.90, 'W2030D-2,WCED-2', no_exp=False), # no nominal exp_pad - DfnConfig(2.0, 3.0, 0.5, 8, 0.95, 1.00, 0.45, 1.60, 1.60, 'V2030D-3,VCED-3', no_exp=False), # no nominal pad length and exp_pad - DfnConfig(2.0, 3.0, 0.5, 8, 0.75, 0.80, 0.45, 1.60, 1.60, 'W2030D-3,WCED-3', no_exp=False), # no nominal pad length and exp_pad - DfnConfig(2.0, 3.0, 0.5, 8, 0.55, 0.65, 0.45, 1.60, 1.60, 'U2030D', no_exp=False), # no nominal pad length and exp_pad + DfnConfig(2.0, 3.0, 0.5, 6, 0.95, 1.00, 0.40, 1.00, 1.20, 'V2030D-1,VCED-1', no_exp=False), # no nominal exp_pad + DfnConfig(2.0, 3.0, 0.5, 6, 0.75, 0.80, 0.40, 1.00, 1.20, 'W2030D-1,WCED-1', no_exp=False), # no nominal exp_pad + DfnConfig(2.0, 3.0, 0.5, 8, 0.95, 1.00, 0.40, 1.75, 1.90, 'V2030D-2,VCED-2', no_exp=False), # no nominal exp_pad + DfnConfig(2.0, 3.0, 0.5, 8, 0.75, 0.80, 0.40, 1.75, 1.90, 'W2030D-2,WCED-2', no_exp=False), # no nominal exp_pad + DfnConfig( + 2.0, 3.0, 0.5, 8, 0.95, 1.00, 0.45, 1.60, 1.60, 'V2030D-3,VCED-3', no_exp=False + ), # no nominal pad length and exp_pad + DfnConfig( + 2.0, 3.0, 0.5, 8, 0.75, 0.80, 0.45, 1.60, 1.60, 'W2030D-3,WCED-3', no_exp=False + ), # no nominal pad length and exp_pad + DfnConfig( + 2.0, 3.0, 0.5, 8, 0.55, 0.65, 0.45, 1.60, 1.60, 'U2030D', no_exp=False + ), # no nominal pad length and exp_pad # Rectangular, Type 1, 2.5 x 3.0 DfnConfig(2.5, 3.0, 0.8, 6, 0.95, 1.00, 0.55, 1.50, 1.20, 'V2530B,VDEB'), DfnConfig(2.5, 3.0, 0.8, 6, 0.75, 0.80, 0.55, 1.50, 1.20, 'W2530B,WDEB'), @@ -215,8 +220,12 @@ def __init__(self, DfnConfig(3.0, 1.5, 0.5, 10, 0.95, 1.00, 0.55, 2.20, 0.10, 'W3015D-2,VEBD-2'), DfnConfig(3.0, 1.5, 0.5, 10, 0.75, 0.80, 0.55, 2.20, 0.10, 'W3015D-2,WEBD-2'), # Rectangular, Type 2, 3.0 x 2.0 - DfnConfig(3.0, 2.0, 0.95, 6, 0.95, 1.00, 0.30, 2.20, 0.60, 'V3020A,VECA'), # no nominal exp_pad, using manual values - DfnConfig(3.0, 2.0, 0.65, 8, 0.95, 1.00, 0.30, 2.20, 0.60, 'V3020C,VECC'), # no nominal exp_pad, using manual values + DfnConfig( + 3.0, 2.0, 0.95, 6, 0.95, 1.00, 0.30, 2.20, 0.60, 'V3020A,VECA' + ), # no nominal exp_pad, using manual values + DfnConfig( + 3.0, 2.0, 0.65, 8, 0.95, 1.00, 0.30, 2.20, 0.60, 'V3020C,VECC' + ), # no nominal exp_pad, using manual values DfnConfig(3.0, 2.0, 0.5, 8, 0.95, 1.00, 0.55, 2.20, 0.60, 'V3020D-1,V3020D-4,VECD-1,VECD-4'), DfnConfig(3.0, 2.0, 0.5, 8, 0.75, 0.80, 0.55, 2.20, 0.60, 'W3020D-1,W3020D-4,WECD-1,WECD-4'), # Commented out as they coincide with the V3020D-1, only the tolerances are different, @@ -246,8 +255,8 @@ def __init__(self, DfnConfig(4.0, 3.0, 0.5, 10, 0.75, 0.80, 0.55, 3.20, 1.60, 'W4030D-1,WGED-1'), DfnConfig(4.0, 3.0, 0.5, 12, 0.95, 1.00, 0.55, 3.20, 1.60, 'V4030D-2,VGED-2'), DfnConfig(4.0, 3.0, 0.5, 12, 0.75, 0.80, 0.55, 3.20, 1.60, 'W4030D-2,WGED-2'), - DfnConfig(4.0, 3.0, 0.5, 12, 0.95, 1.00, 0.40, 3.70, 1.80, 'V4030D-4,VGED-4', no_exp=False), # no nominal exp_pad - DfnConfig(4.0, 3.0, 0.5, 12, 0.75, 0.80, 0.40, 3.70, 1.80, 'W4030D-4,WGED-4', no_exp=False), # no nominal exp_pad + DfnConfig(4.0, 3.0, 0.5, 12, 0.95, 1.00, 0.40, 3.70, 1.80, 'V4030D-4,VGED-4', no_exp=False), # no nominal exp_pad + DfnConfig(4.0, 3.0, 0.5, 12, 0.75, 0.80, 0.40, 3.70, 1.80, 'W4030D-4,WGED-4', no_exp=False), # no nominal exp_pad DfnConfig(4.0, 3.0, 0.5, 14, 0.95, 1.00, 0.55, 3.20, 1.60, 'V4030D-3,VGED-3'), DfnConfig(4.0, 3.0, 0.5, 14, 0.75, 0.80, 0.55, 3.20, 1.60, 'W4030D-3,WGED-3'), # Rectangular, Type 2, 5.0 x 3.0 @@ -263,24 +272,26 @@ def __init__(self, DfnConfig(5.0, 4.0, 0.5, 18, 0.95, 1.00, 0.55, 4.20, 2.60, 'V5040D-3,VJGD-3'), DfnConfig(5.0, 4.0, 0.5, 18, 0.75, 0.80, 0.55, 4.20, 2.60, 'W5040D-3,WJGD-3'), # Rectangular, Type 2, 6.0 x 5.0 - DfnConfig(6.0, 5.0, 0.5, 16, 0.95, 1.00, 0.55, 4.70, 3.40, 'V6050D-1,VLJD-1', no_exp=False), # no nominal exp_pad - DfnConfig(6.0, 5.0, 0.5, 16, 0.75, 0.80, 0.55, 4.70, 3.40, 'W6050D-1,WLJD-1', no_exp=False), # no nominal exp_pad - DfnConfig(6.0, 5.0, 0.5, 18, 0.95, 1.00, 0.55, 4.70, 3.40, 'V6050D-2,VLJD-2', no_exp=False), # no nominal exp_pad - DfnConfig(6.0, 5.0, 0.5, 18, 0.75, 0.80, 0.55, 4.70, 3.40, 'W6050D-2,WLJD-2', no_exp=False), # no nominal exp_pad + DfnConfig(6.0, 5.0, 0.5, 16, 0.95, 1.00, 0.55, 4.70, 3.40, 'V6050D-1,VLJD-1', no_exp=False), # no nominal exp_pad + DfnConfig(6.0, 5.0, 0.5, 16, 0.75, 0.80, 0.55, 4.70, 3.40, 'W6050D-1,WLJD-1', no_exp=False), # no nominal exp_pad + DfnConfig(6.0, 5.0, 0.5, 18, 0.95, 1.00, 0.55, 4.70, 3.40, 'V6050D-2,VLJD-2', no_exp=False), # no nominal exp_pad + DfnConfig(6.0, 5.0, 0.5, 18, 0.75, 0.80, 0.55, 4.70, 3.40, 'W6050D-2,WLJD-2', no_exp=False), # no nominal exp_pad ] def draw_circle(diameter: float) -> Callable[[DfnConfig, Callable[[str], str], List[str]], None]: def _draw(config: DfnConfig, uuid: Callable[[str], str], lines: List[str]) -> None: lines.append(' (circle {} (layer top_documentation)'.format(uuid('hole-circle-doc'))) - lines.append(' (width 0.1) (fill false) (grab_area false) (diameter {}) (position 0.0 0.0)'.format(ff(diameter))) + lines.append( + ' (width 0.1) (fill false) (grab_area false) (diameter {}) (position 0.0 0.0)'.format(ff(diameter)) + ) lines.append(' )') + return _draw THIRD_CONFIGS = [ # length, width, pitch, pin_count, height_nominal, height_max, lead_length, exposed_width, exposed_length, keywords - # Sensirion DfnConfig( length=2.0, @@ -296,7 +307,7 @@ def _draw(config: DfnConfig, uuid: Callable[[str], str], lines: List[str]) -> No keywords='sensirion,sht,shtcx,shtc1,shtc3', name='SENSIRION_SHTCx', create_date='2019-01-24T21:50:44Z', - library="Sensirion.lplib", + library='Sensirion.lplib', no_exp=False, pin1_corner_dx_dy=0.2, extended_doc_fn=draw_circle(diameter=0.9), @@ -315,7 +326,7 @@ def _draw(config: DfnConfig, uuid: Callable[[str], str], lines: List[str]) -> No keywords='sensirion,sht,sht2x,sht20,sht21,sht25', name='SENSIRION_SHT2x', create_date='2019-01-24T22:13:46Z', - library="Sensirion.lplib", + library='Sensirion.lplib', no_exp=False, pin1_corner_dx_dy=0.2, ), @@ -332,12 +343,11 @@ def _draw(config: DfnConfig, uuid: Callable[[str], str], lines: List[str]) -> No exposed_length=1.25, keywords='sensirion,sgp,sgp30,sgpc3', name='SENSIRION_SGPxx', - library="Sensirion.lplib", + library='Sensirion.lplib', no_exp=False, pin1_corner_dx_dy=0.3, extended_doc_fn=draw_circle(diameter=1.1), ), - # Microchip DfnConfig( length=2.0, diff --git a/entities/attribute.py b/entities/attribute.py index e2cde48..d913d0b 100644 --- a/entities/attribute.py +++ b/entities/attribute.py @@ -9,7 +9,7 @@ def get_name(self) -> str: class UnitlessUnit(AttributeUnit): - NONE = "none" + NONE = 'none' class AttributeType(EnumValue): @@ -26,8 +26,10 @@ def get_name(self) -> str: return 'type' -class Attribute(): - def __init__(self, name: str, value: Union[Value, str], attribute_type: AttributeType, unit: Optional[AttributeUnit]) -> None: +class Attribute: + def __init__( + self, name: str, value: Union[Value, str], attribute_type: AttributeType, unit: Optional[AttributeUnit] + ) -> None: self.name = name self.value = Value(value) if isinstance(value, str) else value diff --git a/entities/common.py b/entities/common.py index b2c9911..bf9b0ed 100644 --- a/entities/common.py +++ b/entities/common.py @@ -13,6 +13,7 @@ class EnumValue(Enum): """Helper class to represent enumeration like values""" + def get_name(self) -> str: raise NotImplementedError('Override get_name in subclass') @@ -20,8 +21,9 @@ def __str__(self) -> str: return '({} {})'.format(self.get_name(), self.value) -class DateValue(): +class DateValue: """Helper class to represent a single named date value""" + def __init__(self, name: str, date: str): self.name = name self.date = date @@ -30,8 +32,9 @@ def __str__(self) -> str: return '({} {})'.format(self.name, self.date) -class UUIDValue(): +class UUIDValue: """Helper class to represent a single named UUID value""" + def __init__(self, name: str, uuid: str): self.name = name self.uuid = uuid @@ -40,8 +43,9 @@ def __str__(self) -> str: return '({} {})'.format(self.name, self.uuid) -class BoolValue(): +class BoolValue: """Helper class to represent a single named boolean value""" + def __init__(self, name: str, value: bool): self.name = name self.value = str(value).lower() @@ -50,8 +54,9 @@ def __str__(self) -> str: return '({} {})'.format(self.name, self.value) -class StringValue(): +class StringValue: """Helper class to represent a single named string value""" + def __init__(self, name: str, value: str): self.name = name self.value = value @@ -60,8 +65,9 @@ def __str__(self) -> str: return '({} "{}")'.format(self.name, escape_string(self.value)) -class FloatValue(): +class FloatValue: """Helper class to represent a single named float value""" + def __init__(self, name: str, value: float): self.name = name self.value = value @@ -115,7 +121,7 @@ def __init__(self, category: str): super().__init__('category', category) -class Position(): +class Position: def __init__(self, x: float, y: float): self.x = x self.y = y @@ -124,7 +130,7 @@ def __str__(self) -> str: return '(position {} {})'.format(format_float(self.x), format_float(self.y)) -class Position3D(): +class Position3D: def __init__(self, x: float, y: float, z: float): self.x = x self.y = y @@ -143,7 +149,7 @@ def __init__(self, rotation: float): super().__init__('rotation', rotation) -class Rotation3D(): +class Rotation3D: def __init__(self, x: float, y: float, z: float): self.x = x self.y = y @@ -187,7 +193,7 @@ def __init__(self, grab_area: bool): super().__init__('grab_area', grab_area) -class Vertex(): +class Vertex: def __init__(self, position: Position, angle: Angle): self.position = position self.angle = angle @@ -196,7 +202,7 @@ def __str__(self) -> str: return '(vertex {} {})'.format(self.position, self.angle) -class Layer(): +class Layer: def __init__(self, layer: str): self.layer = layer @@ -204,9 +210,16 @@ def __str__(self) -> str: return '(layer {})'.format(self.layer) -class Polygon(): - def __init__(self, uuid: str, layer: Layer, width: Width, fill: Fill, - grab_area: GrabArea, vertices: Optional[List[Vertex]] = None): +class Polygon: + def __init__( + self, + uuid: str, + layer: Layer, + width: Width, + fill: Fill, + grab_area: GrabArea, + vertices: Optional[List[Vertex]] = None, + ): self.uuid = uuid self.layer = layer self.width = width @@ -218,8 +231,9 @@ def add_vertex(self, vertex: Vertex) -> None: self.vertices.append(vertex) def __str__(self) -> str: - ret = '(polygon {} {}\n'.format(self.uuid, self.layer) +\ - ' {} {} {}\n'.format(self.width, self.fill, self.grab_area) + ret = '(polygon {} {}\n'.format(self.uuid, self.layer) + ' {} {} {}\n'.format( + self.width, self.fill, self.grab_area + ) ret += indent_entities(self.vertices) ret += ')' return ret @@ -270,9 +284,17 @@ def __init__(self, diameter: float): super().__init__('diameter', diameter) -class Circle(): - def __init__(self, uuid: str, layer: Layer, width: Width, fill: Fill, - grab_area: GrabArea, diameter: Diameter, position: Position): +class Circle: + def __init__( + self, + uuid: str, + layer: Layer, + width: Width, + fill: Fill, + grab_area: GrabArea, + diameter: Diameter, + position: Position, + ): self.uuid = uuid self.layer = layer self.width = width @@ -283,8 +305,7 @@ def __init__(self, uuid: str, layer: Layer, width: Width, fill: Fill, def __str__(self) -> str: ret = '(circle {} {}\n'.format(self.uuid, self.layer) - ret += ' {} {} {} {} {}\n'.format(self.width, self.fill, self.grab_area, - self.diameter, self.position) + ret += ' {} {} {} {} {}\n'.format(self.width, self.fill, self.grab_area, self.diameter, self.position) ret += ')' return ret @@ -294,7 +315,7 @@ def __init__(self, value: str): super().__init__('value', value) -class Align(): +class Align: def __init__(self, align: str): self.align = align @@ -302,8 +323,17 @@ def __str__(self) -> str: return '(align {})'.format(self.align) -class Text(): - def __init__(self, uuid: str, layer: Layer, value: Value, align: Align, height: Height, position: Position, rotation: Rotation): +class Text: + def __init__( + self, + uuid: str, + layer: Layer, + value: Value, + align: Align, + height: Height, + position: Position, + rotation: Rotation, + ): self.uuid = uuid self.layer = layer self.value = value @@ -313,6 +343,8 @@ def __init__(self, uuid: str, layer: Layer, value: Value, align: Align, height: self.rotation = rotation def __str__(self) -> str: - return '(text {} {} {}\n'.format(self.uuid, self.layer, self.value) +\ - ' {} {} {} {}\n'.format(self.align, self.height, self.position, self.rotation) +\ - ')' + return ( + '(text {} {} {}\n'.format(self.uuid, self.layer, self.value) + + ' {} {} {} {}\n'.format(self.align, self.height, self.position, self.rotation) + + ')' + ) diff --git a/entities/component.py b/entities/component.py index 847625c..4383619 100644 --- a/entities/component.py +++ b/entities/component.py @@ -3,8 +3,21 @@ from typing import Iterable, List from .common import ( - Author, BoolValue, Category, Created, Deprecated, Description, EnumValue, GeneratedBy, Keywords, Name, Position, - Rotation, StringValue, UUIDValue, Version + Author, + BoolValue, + Category, + Created, + Deprecated, + Description, + EnumValue, + GeneratedBy, + Keywords, + Name, + Position, + Rotation, + StringValue, + UUIDValue, + Version, ) from .helper import indent_entities @@ -51,9 +64,17 @@ def __init__(self, forced_net: str): super().__init__('forced_net', forced_net) -class Signal(): - def __init__(self, uuid: str, name: Name, role: Role, required: Required, - negated: Negated, clock: Clock, forced_net: ForcedNet): +class Signal: + def __init__( + self, + uuid: str, + name: Name, + role: Role, + required: Required, + negated: Negated, + clock: Clock, + forced_net: ForcedNet, + ): self.uuid = uuid self.name = name self.role = role @@ -63,9 +84,11 @@ def __init__(self, uuid: str, name: Name, role: Role, required: Required, self.forced_net = forced_net def __str__(self) -> str: - ret = '(signal {} {} {}\n'.format(self.uuid, self.name, self.role) +\ - ' {} {} {} {}\n'.format(self.required, self.negated, self.clock, self.forced_net) +\ - ')' + ret = ( + '(signal {} {} {}\n'.format(self.uuid, self.name, self.role) + + ' {} {} {} {}\n'.format(self.required, self.negated, self.clock, self.forced_net) + + ')' + ) return ret @@ -87,9 +110,8 @@ def get_name(self) -> str: return 'text' -class PinSignalMap(): - def __init__(self, pin_uuid: str, signal_uuid: SignalUUID, - text_designator: TextDesignator): +class PinSignalMap: + def __init__(self, pin_uuid: str, signal_uuid: SignalUUID, text_designator: TextDesignator): self.pin_uuid = pin_uuid self.signal_uuid = signal_uuid self.text_designator = text_designator @@ -103,9 +125,16 @@ def __init__(self, suffix: str): super().__init__('suffix', suffix) -class Gate(): - def __init__(self, uuid: str, symbol_uuid: SymbolUUID, position: Position, - rotation: Rotation, required: Required, suffix: Suffix): +class Gate: + def __init__( + self, + uuid: str, + symbol_uuid: SymbolUUID, + position: Position, + rotation: Rotation, + required: Required, + suffix: Suffix, + ): self.uuid = uuid self.symbol_uuid = symbol_uuid self.position = position @@ -118,9 +147,11 @@ def add_pin_signal_map(self, pin_signal_map: PinSignalMap) -> None: self.pins.append(pin_signal_map) def __str__(self) -> str: - ret = '(gate {}\n'.format(self.uuid) +\ - ' {}\n'.format(self.symbol_uuid) +\ - ' {} {} {} {}\n'.format(self.position, self.rotation, self.required, self.suffix) + ret = ( + '(gate {}\n'.format(self.uuid) + + ' {}\n'.format(self.symbol_uuid) + + ' {} {} {} {}\n'.format(self.position, self.rotation, self.required, self.suffix) + ) pin_lines = [] for pin in self.pins: pin_lines.append(' {}'.format(pin)) @@ -150,21 +181,33 @@ def add_gate(self, gate_map: Gate) -> None: self.gates.append(gate_map) def __str__(self) -> str: - ret = '(variant {} {}\n'.format(self.uuid, self.norm) +\ - ' {}\n'.format(self.name) +\ - ' {}\n'.format(self.description) + ret = ( + '(variant {} {}\n'.format(self.uuid, self.norm) + + ' {}\n'.format(self.name) + + ' {}\n'.format(self.description) + ) ret += indent_entities(sorted(self.gates, key=lambda x: str(x.uuid))) ret += ')' return ret class Component: - def __init__(self, uuid: str, name: Name, description: Description, - keywords: Keywords, author: Author, version: Version, - created: Created, deprecated: Deprecated, - generated_by: GeneratedBy, categories: Iterable[Category], - schematic_only: SchematicOnly, - default_value: DefaultValue, prefix: Prefix): + def __init__( + self, + uuid: str, + name: Name, + description: Description, + keywords: Keywords, + author: Author, + version: Version, + created: Created, + deprecated: Deprecated, + generated_by: GeneratedBy, + categories: Iterable[Category], + schematic_only: SchematicOnly, + default_value: DefaultValue, + prefix: Prefix, + ): self.uuid = uuid self.name = name self.description = description @@ -186,19 +229,21 @@ def add_approval(self, approval: str) -> None: self.approvals.append(approval) def __str__(self) -> str: - ret = '(librepcb_component {}\n'.format(self.uuid) +\ - ' {}\n'.format(self.name) +\ - ' {}\n'.format(self.description) +\ - ' {}\n'.format(self.keywords) +\ - ' {}\n'.format(self.author) +\ - ' {}\n'.format(self.version) +\ - ' {}\n'.format(self.created) +\ - ' {}\n'.format(self.deprecated) +\ - ' {}\n'.format(self.generated_by) +\ - ''.join([' {}\n'.format(cat) for cat in self.categories]) +\ - ' {}\n'.format(self.schematic_only) +\ - ' {}\n'.format(self.default_value) +\ - ' {}\n'.format(self.prefix) + ret = ( + '(librepcb_component {}\n'.format(self.uuid) + + ' {}\n'.format(self.name) + + ' {}\n'.format(self.description) + + ' {}\n'.format(self.keywords) + + ' {}\n'.format(self.author) + + ' {}\n'.format(self.version) + + ' {}\n'.format(self.created) + + ' {}\n'.format(self.deprecated) + + ' {}\n'.format(self.generated_by) + + ''.join([' {}\n'.format(cat) for cat in self.categories]) + + ' {}\n'.format(self.schematic_only) + + ' {}\n'.format(self.default_value) + + ' {}\n'.format(self.prefix) + ) ret += indent_entities(self.signals) ret += indent_entities(self.variants) ret += indent_entities(sorted(self.approvals)) diff --git a/entities/device.py b/entities/device.py index 3508ff0..f99e537 100644 --- a/entities/device.py +++ b/entities/device.py @@ -6,7 +6,17 @@ from entities.attribute import Attribute from .common import ( - Author, Category, Created, Deprecated, Description, GeneratedBy, Keywords, Name, StringValue, UUIDValue, Version + Author, + Category, + Created, + Deprecated, + Description, + GeneratedBy, + Keywords, + Name, + StringValue, + UUIDValue, + Version, ) from .component import SignalUUID from .helper import indent_entities @@ -22,7 +32,7 @@ def __init__(self, package_uuid: str): super().__init__('package', package_uuid) -class ComponentPad(): +class ComponentPad: def __init__(self, pad_uuid: str, signal: SignalUUID): self.pad_uuid = pad_uuid self.signal = signal @@ -36,7 +46,7 @@ def __init__(self, manufacturer: str): super().__init__('manufacturer', manufacturer) -class Part(): +class Part: def __init__(self, mpn: str, manufacturer: Manufacturer, attributes: Optional[List[Attribute]] = None): self.mpn = mpn self.manufacturer = manufacturer @@ -52,12 +62,22 @@ def add_attribute(self, attr: Attribute) -> None: self.attributes.append(attr) -class Device(): - def __init__(self, uuid: str, name: Name, description: Description, - keywords: Keywords, author: Author, version: Version, - created: Created, deprecated: Deprecated, - generated_by: GeneratedBy, categories: Iterable[Category], - component_uuid: ComponentUUID, package_uuid: PackageUUID): +class Device: + def __init__( + self, + uuid: str, + name: Name, + description: Description, + keywords: Keywords, + author: Author, + version: Version, + created: Created, + deprecated: Deprecated, + generated_by: GeneratedBy, + categories: Iterable[Category], + component_uuid: ComponentUUID, + package_uuid: PackageUUID, + ): self.uuid = uuid self.name = name self.description = description @@ -84,18 +104,20 @@ def add_approval(self, approval: str) -> None: self.approvals.append(approval) def __str__(self) -> str: - ret = '(librepcb_device {}\n'.format(self.uuid) +\ - ' {}\n'.format(self.name) +\ - ' {}\n'.format(self.description) +\ - ' {}\n'.format(self.keywords) +\ - ' {}\n'.format(self.author) +\ - ' {}\n'.format(self.version) +\ - ' {}\n'.format(self.created) +\ - ' {}\n'.format(self.deprecated) +\ - ' {}\n'.format(self.generated_by) +\ - ''.join([' {}\n'.format(cat) for cat in self.categories]) +\ - ' {}\n'.format(self.component_uuid) +\ - ' {}\n'.format(self.package_uuid) + ret = ( + '(librepcb_device {}\n'.format(self.uuid) + + ' {}\n'.format(self.name) + + ' {}\n'.format(self.description) + + ' {}\n'.format(self.keywords) + + ' {}\n'.format(self.author) + + ' {}\n'.format(self.version) + + ' {}\n'.format(self.created) + + ' {}\n'.format(self.deprecated) + + ' {}\n'.format(self.generated_by) + + ''.join([' {}\n'.format(cat) for cat in self.categories]) + + ' {}\n'.format(self.component_uuid) + + ' {}\n'.format(self.package_uuid) + ) ret += indent_entities(sorted(self.pads, key=lambda x: str(x.pad_uuid))) ret += indent_entities(self.parts) ret += indent_entities(sorted(self.approvals)) diff --git a/entities/package.py b/entities/package.py index 3301f2f..ae893d5 100644 --- a/entities/package.py +++ b/entities/package.py @@ -5,17 +5,39 @@ from common import format_float from .common import ( - Align, Author, BoolValue, Category, Circle, Created, Deprecated, Description, EnumValue, FloatValue, GeneratedBy, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, UUIDValue, Value, Version, - Vertex + Align, + Author, + BoolValue, + Category, + Circle, + Created, + Deprecated, + Description, + EnumValue, + FloatValue, + GeneratedBy, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + UUIDValue, + Value, + Version, + Vertex, ) from .helper import indent_entities -class Package3DModel(): +class Package3DModel: """ A 3D model in a package. """ + def __init__(self, uuid: str, name: Name): self.uuid = uuid self.name = name @@ -32,10 +54,11 @@ def __lt__(self, other): # type: ignore return self.uuid < other.uuid -class Footprint3DModel(): +class Footprint3DModel: """ A 3D model reference in a footprint. """ + def __init__(self, uuid: str): self.uuid = uuid @@ -61,7 +84,7 @@ def get_name(self) -> str: return 'assembly_type' -class PackagePad(): +class PackagePad: def __init__(self, uuid: str, name: Name): self.uuid = uuid self.name = name @@ -99,12 +122,22 @@ def __init__(self, mirror: bool): super().__init__('mirror', mirror) -class StrokeText(): - def __init__(self, uuid: str, layer: Layer, height: Height, - stroke_width: StrokeWidth, letter_spacing: LetterSpacing, - line_spacing: LineSpacing, align: Align, position: Position, - rotation: Rotation, auto_rotate: AutoRotate, mirror: Mirror, - value: Value): +class StrokeText: + def __init__( + self, + uuid: str, + layer: Layer, + height: Height, + stroke_width: StrokeWidth, + letter_spacing: LetterSpacing, + line_spacing: LineSpacing, + align: Align, + position: Position, + rotation: Rotation, + auto_rotate: AutoRotate, + mirror: Mirror, + value: Value, + ): self.uuid = uuid self.layer = layer self.height = height @@ -119,10 +152,12 @@ def __init__(self, uuid: str, layer: Layer, height: Height, self.value = value def __str__(self) -> str: - ret = '(stroke_text {} {}\n'.format(self.uuid, self.layer) +\ - ' {} {} {} {}\n'.format(self.height, self.stroke_width, self.letter_spacing, self.line_spacing) +\ - ' {} {} {}\n'.format(self.align, self.position, self.rotation) +\ - ' {} {} {}\n)'.format(self.auto_rotate, self.mirror, self.value) + ret = ( + '(stroke_text {} {}\n'.format(self.uuid, self.layer) + + ' {} {} {} {}\n'.format(self.height, self.stroke_width, self.letter_spacing, self.line_spacing) + + ' {} {} {}\n'.format(self.align, self.position, self.rotation) + + ' {} {} {}\n)'.format(self.auto_rotate, self.mirror, self.value) + ) return ret @@ -148,7 +183,7 @@ def __init__(self, radius_normalized: float): super().__init__('radius', radius_normalized) -class Size(): +class Size: def __init__(self, width: float, height: float): self.width = width self.height = height @@ -203,9 +238,8 @@ def __init__(self, diameter: float): super().__init__('diameter', diameter) -class PadHole(): - def __init__(self, uuid: str, diameter: DrillDiameter, - vertices: List[Vertex]): +class PadHole: + def __init__(self, uuid: str, diameter: DrillDiameter, vertices: List[Vertex]): self.uuid = uuid self.diameter = diameter self.vertices = vertices @@ -217,13 +251,23 @@ def __str__(self) -> str: return ret -class FootprintPad(): - def __init__(self, uuid: str, side: ComponentSide, shape: Shape, - position: Position, rotation: Rotation, size: Size, - radius: ShapeRadius, stop_mask: StopMaskConfig, - solder_paste: SolderPasteConfig, - copper_clearance: CopperClearance, function: PadFunction, - package_pad: PackagePadUuid, holes: List[PadHole]): +class FootprintPad: + def __init__( + self, + uuid: str, + side: ComponentSide, + shape: Shape, + position: Position, + rotation: Rotation, + size: Size, + radius: ShapeRadius, + stop_mask: StopMaskConfig, + solder_paste: SolderPasteConfig, + copper_clearance: CopperClearance, + function: PadFunction, + package_pad: PackagePadUuid, + holes: List[PadHole], + ): self.uuid = uuid self.side = side self.shape = shape @@ -239,18 +283,21 @@ def __init__(self, uuid: str, side: ComponentSide, shape: Shape, self.holes = holes def __str__(self) -> str: - ret = '(pad {} {} {}\n'.format(self.uuid, self.side, self.shape) +\ - ' {} {} {} {}\n'.format(self.position, self.rotation, self.size, self.radius) +\ - ' {} {} {} {}\n'.format(self.stop_mask, self.solder_paste, self.copper_clearance, self.function) +\ - ' {}\n'.format(self.package_pad) + ret = ( + '(pad {} {} {}\n'.format(self.uuid, self.side, self.shape) + + ' {} {} {} {}\n'.format(self.position, self.rotation, self.size, self.radius) + + ' {} {} {} {}\n'.format(self.stop_mask, self.solder_paste, self.copper_clearance, self.function) + + ' {}\n'.format(self.package_pad) + ) ret += indent_entities(self.holes) ret += ')' return ret -class Footprint(): - def __init__(self, uuid: str, name: Name, description: Description, - position_3d: Position3D, rotation_3d: Rotation3D): +class Footprint: + def __init__( + self, uuid: str, name: Name, description: Description, position_3d: Position3D, rotation_3d: Rotation3D + ): self.uuid = uuid self.name = name self.description = description @@ -278,10 +325,12 @@ def add_text(self, text: StrokeText) -> None: self.texts.append(text) def __str__(self) -> str: - ret = '(footprint {}\n'.format(self.uuid) +\ - ' {}\n'.format(self.name) +\ - ' {}\n'.format(self.description) +\ - ' {} {}\n'.format(self.position_3d, self.rotation_3d) + ret = ( + '(footprint {}\n'.format(self.uuid) + + ' {}\n'.format(self.name) + + ' {}\n'.format(self.description) + + ' {} {}\n'.format(self.position_3d, self.rotation_3d) + ) ret += indent_entities(sorted(self.models_3d)) ret += indent_entities(self.pads) ret += indent_entities(self.polygons) @@ -292,11 +341,20 @@ def __str__(self) -> str: class Package: - def __init__(self, uuid: str, name: Name, description: Description, - keywords: Keywords, author: Author, version: Version, - created: Created, deprecated: Deprecated, - generated_by: GeneratedBy, categories: Iterable[Category], - assembly_type: AssemblyType): + def __init__( + self, + uuid: str, + name: Name, + description: Description, + keywords: Keywords, + author: Author, + version: Version, + created: Created, + deprecated: Deprecated, + generated_by: GeneratedBy, + categories: Iterable[Category], + assembly_type: AssemblyType, + ): self.uuid = uuid self.name = name self.description = description @@ -326,17 +384,19 @@ def add_approval(self, approval: str) -> None: self.approvals.append(approval) def __str__(self) -> str: - ret = '(librepcb_package {}\n'.format(self.uuid) +\ - ' {}\n'.format(self.name) +\ - ' {}\n'.format(self.description) +\ - ' {}\n'.format(self.keywords) +\ - ' {}\n'.format(self.author) +\ - ' {}\n'.format(self.version) +\ - ' {}\n'.format(self.created) +\ - ' {}\n'.format(self.deprecated) +\ - ' {}\n'.format(self.generated_by) +\ - ''.join([' {}\n'.format(cat) for cat in self.categories]) +\ - ' {}\n'.format(self.assembly_type) + ret = ( + '(librepcb_package {}\n'.format(self.uuid) + + ' {}\n'.format(self.name) + + ' {}\n'.format(self.description) + + ' {}\n'.format(self.keywords) + + ' {}\n'.format(self.author) + + ' {}\n'.format(self.version) + + ' {}\n'.format(self.created) + + ' {}\n'.format(self.deprecated) + + ' {}\n'.format(self.generated_by) + + ''.join([' {}\n'.format(cat) for cat in self.categories]) + + ' {}\n'.format(self.assembly_type) + ) ret += indent_entities(self.pads) ret += indent_entities(self.models_3d) ret += indent_entities(self.footprints) diff --git a/entities/symbol.py b/entities/symbol.py index 180a888..8a2d3db 100644 --- a/entities/symbol.py +++ b/entities/symbol.py @@ -5,13 +5,27 @@ from common import format_float from .common import ( - Author, Category, Circle, Created, Deprecated, Description, FloatValue, GeneratedBy, Keywords, Length, Name, - Polygon, Position, Rotation, Text, Version + Author, + Category, + Circle, + Created, + Deprecated, + Description, + FloatValue, + GeneratedBy, + Keywords, + Length, + Name, + Polygon, + Position, + Rotation, + Text, + Version, ) from .helper import indent_entities -class NamePosition(): +class NamePosition: def __init__(self, x: float, y: float): self.x = x self.y = y @@ -30,7 +44,7 @@ def __init__(self, height: float): super().__init__('name_height', height) -class NameAlign(): +class NameAlign: def __init__(self, align: str): self.align = align @@ -38,11 +52,19 @@ def __str__(self) -> str: return '(name_align {})'.format(self.align) -class Pin(): - def __init__(self, uuid: str, name: Name, position: Position, - rotation: Rotation, length: Length, - name_position: NamePosition, name_rotation: NameRotation, - name_height: NameHeight, name_align: NameAlign): +class Pin: + def __init__( + self, + uuid: str, + name: Name, + position: Position, + rotation: Rotation, + length: Length, + name_position: NamePosition, + name_rotation: NameRotation, + name_height: NameHeight, + name_align: NameAlign, + ): self.uuid = uuid self.name = name self.position = position @@ -54,18 +76,29 @@ def __init__(self, uuid: str, name: Name, position: Position, self.name_align = name_align def __str__(self) -> str: - return '(pin {} {}\n'.format(self.uuid, self.name) +\ - ' {} {} {}\n'.format(self.position, self.rotation, self.length) +\ - ' {} {} {}\n'.format(self.name_position, self.name_rotation, self.name_height) +\ - ' {}\n'.format(self.name_align) +\ - ')' + return ( + '(pin {} {}\n'.format(self.uuid, self.name) + + ' {} {} {}\n'.format(self.position, self.rotation, self.length) + + ' {} {} {}\n'.format(self.name_position, self.name_rotation, self.name_height) + + ' {}\n'.format(self.name_align) + + ')' + ) class Symbol: - def __init__(self, uuid: str, name: Name, description: Description, - keywords: Keywords, author: Author, version: Version, - created: Created, deprecated: Deprecated, - generated_by: GeneratedBy, categories: Iterable[Category]): + def __init__( + self, + uuid: str, + name: Name, + description: Description, + keywords: Keywords, + author: Author, + version: Version, + created: Created, + deprecated: Deprecated, + generated_by: GeneratedBy, + categories: Iterable[Category], + ): self.uuid = uuid self.name = name self.description = description @@ -98,16 +131,18 @@ def add_approval(self, approval: str) -> None: self.approvals.append(approval) def __str__(self) -> str: - ret = '(librepcb_symbol {}\n'.format(self.uuid) +\ - ' {}\n'.format(self.name) +\ - ' {}\n'.format(self.description) +\ - ' {}\n'.format(self.keywords) +\ - ' {}\n'.format(self.author) +\ - ' {}\n'.format(self.version) +\ - ' {}\n'.format(self.created) +\ - ' {}\n'.format(self.deprecated) +\ - ' {}\n'.format(self.generated_by) +\ - ''.join([' {}\n'.format(cat) for cat in self.categories]) + ret = ( + '(librepcb_symbol {}\n'.format(self.uuid) + + ' {}\n'.format(self.name) + + ' {}\n'.format(self.description) + + ' {}\n'.format(self.keywords) + + ' {}\n'.format(self.author) + + ' {}\n'.format(self.version) + + ' {}\n'.format(self.created) + + ' {}\n'.format(self.deprecated) + + ' {}\n'.format(self.generated_by) + + ''.join([' {}\n'.format(cat) for cat in self.categories]) + ) ret += indent_entities(self.pins) ret += indent_entities(self.polygons) ret += indent_entities(self.circles) diff --git a/generate_axial_tht.py b/generate_axial_tht.py index 02c6127..d69584d 100644 --- a/generate_axial_tht.py +++ b/generate_axial_tht.py @@ -4,6 +4,7 @@ - JEDEC DO-204 https://www.jedec.org/system/files/docs/DO-204B-D.PDF """ + import sys from math import acos, asin, pi, sqrt from os import path @@ -13,13 +14,57 @@ from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, DrillDiameter, Footprint, Footprint3DModel, FootprintPad, - LetterSpacing, LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, PadHole, - Shape, ShapeRadius, Size, SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_axial_tht.py)' @@ -99,7 +144,9 @@ def generate_pkg( create_date: Optional[str], generate_3d_models: bool, ) -> None: - full_desc = description + f""" + full_desc = ( + description + + f""" Body diameter: {body_diameter_nom:.2f} mm Body length: {body_length_nom:.2f} mm @@ -107,6 +154,7 @@ def generate_pkg( Generated with {generator} """ + ) def _uuid(identifier: str) -> str: return uuid('pkg', pkg_identifier, identifier) @@ -155,118 +203,131 @@ def _uuid(identifier: str) -> str: pad_size = (pad_size[1], pad_size[0]) for i, sign in enumerate([-1, 1]): uuid_pad = _uuid(uuid_ns + 'pad-{}'.format(i + 1)) - footprint.add_pad(FootprintPad( - uuid=uuid_pad, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(sign * variant.pitch / 2, 0), - rotation=Rotation(0), - size=Size(*pad_size), - radius=ShapeRadius(0 if (i == 0) else 1), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(_uuid('pad-' + str(i + 1))), - holes=[PadHole(uuid_pad, DrillDiameter(pad_hole_diameter), - [Vertex(Position(0.0, 0.0), Angle(0.0))])], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_pad, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(sign * variant.pitch / 2, 0), + rotation=Rotation(0), + size=Size(*pad_size), + radius=ShapeRadius(0 if (i == 0) else 1), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(_uuid('pad-' + str(i + 1))), + holes=[ + PadHole(uuid_pad, DrillDiameter(pad_hole_diameter), [Vertex(Position(0.0, 0.0), Angle(0.0))]) + ], + ) + ) # Documentation if variant.vertical: - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-body'), - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(True), - diameter=Diameter(body_diameter_nom - line_width), - position=Position(-variant.pitch / 2, 0), - )) - dx = (variant.pitch / 2) - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-leg'), - layer=Layer('top_documentation'), - width=Width(leg_diameter_nom), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, 0), Angle(0)), - Vertex(Position(dx, 0), Angle(0)), - ], - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-body'), + layer=Layer('top_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(True), + diameter=Diameter(body_diameter_nom - line_width), + position=Position(-variant.pitch / 2, 0), + ) + ) + dx = variant.pitch / 2 + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-leg'), + layer=Layer('top_documentation'), + width=Width(leg_diameter_nom), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, 0), Angle(0)), + Vertex(Position(dx, 0), Angle(0)), + ], + ) + ) else: dx = (body_length_nom / 2) - (line_width / 2) dy = (body_diameter_nom / 2) - (line_width / 2) - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-body'), - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(True), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), # NW - Vertex(Position(dx, dy), Angle(0)), # NE - Vertex(Position(dx, -dy), Angle(0)), # SE - Vertex(Position(-dx, -dy), Angle(0)), # SW - Vertex(Position(-dx, dy), Angle(0)), # NW - ], - )) - for i, sign in enumerate([-1, 1]): - x0 = sign * (variant.pitch / 2) - x1 = sign * (body_length_nom / 2) - dy = leg_diameter_nom / 2 - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-leg{}'.format(i + 1)), + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-body'), layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), + width=Width(line_width), + fill=Fill(False), grab_area=GrabArea(True), vertices=[ - Vertex(Position(x0, dy), Angle(0)), - Vertex(Position(x1, dy), Angle(0)), - Vertex(Position(x1, -dy), Angle(0)), - Vertex(Position(x0, -dy), Angle(sign * 180)), - Vertex(Position(x0, dy), Angle(0)), + Vertex(Position(-dx, dy), Angle(0)), # NW + Vertex(Position(dx, dy), Angle(0)), # NE + Vertex(Position(dx, -dy), Angle(0)), # SE + Vertex(Position(-dx, -dy), Angle(0)), # SW + Vertex(Position(-dx, dy), Angle(0)), # NW ], - )) + ) + ) + for i, sign in enumerate([-1, 1]): + x0 = sign * (variant.pitch / 2) + x1 = sign * (body_length_nom / 2) + dy = leg_diameter_nom / 2 + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-leg{}'.format(i + 1)), + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(True), + vertices=[ + Vertex(Position(x0, dy), Angle(0)), + Vertex(Position(x1, dy), Angle(0)), + Vertex(Position(x1, -dy), Angle(0)), + Vertex(Position(x0, -dy), Angle(sign * 180)), + Vertex(Position(x0, dy), Angle(0)), + ], + ) + ) # Silkscreen if variant.vertical: - silk_pad_clearance_left = (body_diameter_nom / 2) - \ - sqrt(pad_size[0] ** 2 + pad_size[1] ** 2) / 2 - silk_pad_clearance_right = \ - variant.pitch - (body_diameter_nom / 2) - (pad_size[0] / 2) - line_width + silk_pad_clearance_left = (body_diameter_nom / 2) - sqrt(pad_size[0] ** 2 + pad_size[1] ** 2) / 2 + silk_pad_clearance_right = variant.pitch - (body_diameter_nom / 2) - (pad_size[0] / 2) - line_width silk_pad_clearance = min(silk_pad_clearance_left, silk_pad_clearance_right) simple_silkscreen = silk_pad_clearance < 0.15 if not simple_silkscreen: - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-legend'), - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(True), - diameter=Diameter(body_diameter_nom + line_width), - position=Position(-variant.pitch / 2, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-legend'), + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(True), + diameter=Diameter(body_diameter_nom + line_width), + position=Position(-variant.pitch / 2, 0), + ) + ) x0 = (-variant.pitch / 2) + (body_diameter_nom / 2) + (line_width / 2) x1 = (variant.pitch / 2) - (pad_size[0] / 2) - 0.15 dy = leg_diameter_nom / 2 if x1 - x0 >= 0.1: - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-leg'), - layer=Layer('top_legend'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x0, dy), Angle(0)), - Vertex(Position(x1, dy), Angle(0)), - Vertex(Position(x1, -dy), Angle(0)), - Vertex(Position(x0, -dy), Angle(0)), - Vertex(Position(x0, dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-leg'), + layer=Layer('top_legend'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x0, dy), Angle(0)), + Vertex(Position(x1, dy), Angle(0)), + Vertex(Position(x1, -dy), Angle(0)), + Vertex(Position(x0, -dy), Angle(0)), + Vertex(Position(x0, dy), Angle(0)), + ], + ) + ) else: silk_pad_clearance = (variant.pitch - pad_size[0] - body_length_nom) / 2 if silk_pad_clearance < 0.25: # 0.1mm line plus 0.15mm clearance @@ -278,63 +339,71 @@ def _uuid(identifier: str) -> str: dx = (body_length_nom / 2) + (silkscreen_width / 2) dy = (body_diameter_nom / 2) + (silkscreen_width / 2) if split_silkscreen: - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-top'), - layer=Layer('top_legend'), - width=Width(silkscreen_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), - Vertex(Position(dx, dy), Angle(0)), - ], - )) - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-bottom'), - layer=Layer('top_legend'), - width=Width(silkscreen_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, -dy), Angle(0)), - Vertex(Position(dx, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-top'), + layer=Layer('top_legend'), + width=Width(silkscreen_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), + Vertex(Position(dx, dy), Angle(0)), + ], + ) + ) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-bottom'), + layer=Layer('top_legend'), + width=Width(silkscreen_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, -dy), Angle(0)), + Vertex(Position(dx, -dy), Angle(0)), + ], + ) + ) else: - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-body'), - layer=Layer('top_legend'), - width=Width(silkscreen_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), # NW - Vertex(Position(dx, dy), Angle(0)), # NE - Vertex(Position(dx, -dy), Angle(0)), # SE - Vertex(Position(-dx, -dy), Angle(0)), # SW - Vertex(Position(-dx, dy), Angle(0)), # NW - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-body'), + layer=Layer('top_legend'), + width=Width(silkscreen_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), # NW + Vertex(Position(dx, dy), Angle(0)), # NE + Vertex(Position(dx, -dy), Angle(0)), # SE + Vertex(Position(-dx, -dy), Angle(0)), # SW + Vertex(Position(-dx, dy), Angle(0)), # NW + ], + ) + ) for i, sign in enumerate([-1, 1]): x0 = sign * ((variant.pitch / 2) - (pad_size[0] / 2) - 0.2) x1 = sign * ((body_length_nom / 2) + silkscreen_width) if abs(x0) - abs(x1) < 0.1: continue # No space left for this polygon dy = leg_diameter_nom / 2 - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-leg{}'.format(i + 1)), - layer=Layer('top_legend'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x0, dy), Angle(0)), - Vertex(Position(x1, dy), Angle(0)), - Vertex(Position(x1, -dy), Angle(0)), - Vertex(Position(x0, -dy), Angle(0)), - Vertex(Position(x0, dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-leg{}'.format(i + 1)), + layer=Layer('top_legend'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x0, dy), Angle(0)), + Vertex(Position(x1, dy), Angle(0)), + Vertex(Position(x1, -dy), Angle(0)), + Vertex(Position(x0, -dy), Angle(0)), + Vertex(Position(x0, dy), Angle(0)), + ], + ) + ) # Pin-1 markings bar_width = 0.0 @@ -346,61 +415,67 @@ def _uuid(identifier: str) -> str: r = (body_diameter_nom / 2) + (line_width if simple_silkscreen else 0.01) h = r - ((pad_size[0] / 2) + 0.15) x = -(variant.pitch / 2) - r + h - dy = sqrt(2 * r * h - h ** 2) + dy = sqrt(2 * r * h - h**2) angle = 360 * acos(1 - (h / r)) / pi - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-bar'), - layer=Layer('top_legend'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x, dy), Angle(0)), - Vertex(Position(x, -dy), Angle(-angle)), - Vertex(Position(x, dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-bar'), + layer=Layer('top_legend'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x, dy), Angle(0)), + Vertex(Position(x, -dy), Angle(-angle)), + Vertex(Position(x, dy), Angle(0)), + ], + ) + ) else: x = (-body_length_nom / 2) + bar_position * body_length_nom x1 = x - (bar_width / 2) x2 = x + (bar_width / 2) dy = (body_diameter_nom / 2) - line_width - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-bar'), - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x1, dy), Angle(0)), # NW - Vertex(Position(x2, dy), Angle(0)), # NE - Vertex(Position(x2, -dy), Angle(0)), # SE - Vertex(Position(x1, -dy), Angle(0)), # SW - Vertex(Position(x1, dy), Angle(0)), # NW - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-bar'), + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x1, dy), Angle(0)), # NW + Vertex(Position(x2, dy), Angle(0)), # NE + Vertex(Position(x2, -dy), Angle(0)), # SE + Vertex(Position(x1, -dy), Angle(0)), # SW + Vertex(Position(x1, dy), Angle(0)), # NW + ], + ) + ) dy = body_diameter_nom / 2 - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-legend-bar'), - layer=Layer('top_legend'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x1, dy), Angle(0)), # NW - Vertex(Position(x2, dy), Angle(0)), # NE - Vertex(Position(x2, -dy), Angle(0)), # SE - Vertex(Position(x1, -dy), Angle(0)), # SW - Vertex(Position(x1, dy), Angle(0)), # NW - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-legend-bar'), + layer=Layer('top_legend'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x1, dy), Angle(0)), # NW + Vertex(Position(x2, dy), Angle(0)), # NE + Vertex(Position(x2, -dy), Angle(0)), # SE + Vertex(Position(x1, -dy), Angle(0)), # SW + Vertex(Position(x1, dy), Angle(0)), # NW + ], + ) + ) def _create_outline_vertices(offset: float = 0, around_pads: bool = False) -> List[Vertex]: if variant.vertical: dy_body = (body_diameter_nom / 2) + offset dy_leg = (leg_diameter_nom / 2) + offset x0 = -variant.pitch / 2 - h = dy_body - 0.5 * sqrt(4 * dy_body ** 2 - (2 * dy_leg) ** 2) + h = dy_body - 0.5 * sqrt(4 * dy_body**2 - (2 * dy_leg) ** 2) x1 = x0 + dy_body - h x2 = variant.pitch / 2 angle = 180 * asin(dy_leg / dy_body) / pi @@ -439,55 +514,63 @@ def _create_outline_vertices(offset: float = 0, around_pads: bool = False) -> Li ] # Package outline - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-outline'), - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=_create_outline_vertices(), - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-outline'), + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=_create_outline_vertices(), + ) + ) # Courtyard - footprint.add_polygon(Polygon( - uuid=_uuid(uuid_ns + 'polygon-courtyard'), - layer=Layer('top_courtyard'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=_create_outline_vertices(offset=courtyard_excess, around_pads=True), - )) + footprint.add_polygon( + Polygon( + uuid=_uuid(uuid_ns + 'polygon-courtyard'), + layer=Layer('top_courtyard'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=_create_outline_vertices(offset=courtyard_excess, around_pads=True), + ) + ) # Text x = (-variant.pitch / 2) if variant.vertical else 0 - footprint.add_text(StrokeText( - uuid=_uuid(uuid_ns + 'text-name'), - layer=Layer('top_names'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(x, (body_diameter_nom / 2) + line_width + 0.5), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(False), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_uuid(uuid_ns + 'text-value'), - layer=Layer('top_values'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(x, (-body_diameter_nom / 2) - (line_width + 0.5)), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(False), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_uuid(uuid_ns + 'text-name'), + layer=Layer('top_names'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(x, (body_diameter_nom / 2) + line_width + 0.5), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(False), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_uuid(uuid_ns + 'text-value'), + layer=Layer('top_values'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(x, (-body_diameter_nom / 2) - (line_width + 0.5)), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(False), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # 3D model uuid_3d = _uuid('{}{}-3d'.format('v' if variant.vertical else 'h', variant.pitch)) @@ -497,9 +580,20 @@ def _create_outline_vertices(offset: float = 0, around_pads: bool = False) -> Li # models were already added. if uuid_3d not in generated_3d_uuids: if generate_3d_models: - generate_3d(library, pkg_type, name_3d, uuid_pkg, uuid_3d, - body_diameter_nom, body_length_nom, leg_diameter_nom, - variant.pitch, variant.vertical, bar_position, bar_width) + generate_3d( + library, + pkg_type, + name_3d, + uuid_pkg, + uuid_3d, + body_diameter_nom, + body_length_nom, + leg_diameter_nom, + variant.pitch, + variant.vertical, + bar_position, + bar_width, + ) package.add_3d_model(Package3DModel(uuid_3d, Name(name_3d))) generated_3d_uuids.add(uuid_3d) footprint.add_3d_model(Footprint3DModel(uuid_3d)) @@ -535,65 +629,91 @@ def generate_3d( body_color = cq.Color('bisque3') outer_length = body_length * 0.25 if vertical: - body_inner = cq.Workplane("XY") \ - .cylinder(body_length - outer_length, body_diameter * 0.42, centered=(True, True, False)) \ + body_inner = ( + cq.Workplane('XY') + .cylinder(body_length - outer_length, body_diameter * 0.42, centered=(True, True, False)) .translate((-pitch / 2, 0, vertical_standoff + outer_length / 2)) - body_outer = cq.Workplane("XY") \ - .cylinder(outer_length, body_diameter / 2, centered=(True, True, False)) \ + ) + body_outer = ( + cq.Workplane('XY') + .cylinder(outer_length, body_diameter / 2, centered=(True, True, False)) .fillet(body_diameter / 6) - assembly.add_body(body_outer, 'body_outer_1', body_color, - location=cq.Location((-pitch / 2, 0, vertical_standoff))) - assembly.add_body(body_outer, 'body_outer_2', body_color, - location=cq.Location((-pitch / 2, 0, vertical_standoff + body_length - outer_length))) + ) + assembly.add_body( + body_outer, 'body_outer_1', body_color, location=cq.Location((-pitch / 2, 0, vertical_standoff)) + ) + assembly.add_body( + body_outer, + 'body_outer_2', + body_color, + location=cq.Location((-pitch / 2, 0, vertical_standoff + body_length - outer_length)), + ) else: - body_inner = cq.Workplane("YZ") \ - .cylinder(body_length - outer_length, body_diameter * 0.42, centered=(True, True, True)) \ + body_inner = ( + cq.Workplane('YZ') + .cylinder(body_length - outer_length, body_diameter * 0.42, centered=(True, True, True)) .translate((0, 0, body_diameter / 2)) - body_outer = cq.Workplane("YZ") \ - .cylinder(outer_length, body_diameter / 2, centered=(True, False, True)) \ + ) + body_outer = ( + cq.Workplane('YZ') + .cylinder(outer_length, body_diameter / 2, centered=(True, False, True)) .fillet(body_diameter / 6) - assembly.add_body(body_outer, 'body_outer_1', body_color, - location=cq.Location((-(body_length - outer_length) / 2, 0, 0))) - assembly.add_body(body_outer, 'body_outer_2', body_color, - location=cq.Location(((body_length - outer_length) / 2, 0, 0))) + ) + assembly.add_body( + body_outer, 'body_outer_1', body_color, location=cq.Location((-(body_length - outer_length) / 2, 0, 0)) + ) + assembly.add_body( + body_outer, 'body_outer_2', body_color, location=cq.Location(((body_length - outer_length) / 2, 0, 0)) + ) assembly.add_body(body_inner, 'body_inner', body_color) elif pkg_type == 'DO': if vertical: - body = cq.Workplane("XY") \ - .cylinder(body_length, body_diameter / 2, centered=(True, True, False)) \ + body = ( + cq.Workplane('XY') + .cylinder(body_length, body_diameter / 2, centered=(True, True, False)) .translate((-pitch / 2, 0, vertical_standoff)) + ) else: - body = cq.Workplane("YZ") \ - .cylinder(body_length, body_diameter / 2, centered=(True, False, True)) + body = cq.Workplane('YZ').cylinder(body_length, body_diameter / 2, centered=(True, False, True)) assembly.add_body(body, 'body', cq.Color('gray16')) marking_diameter = body_diameter + 0.05 marking_offset = body_length * (0.5 - marking_position) if vertical: - marking = cq.Workplane("XY") \ - .cylinder(marking_width, marking_diameter / 2, centered=(True, True, False)) \ + marking = ( + cq.Workplane('XY') + .cylinder(marking_width, marking_diameter / 2, centered=(True, True, False)) .translate((-pitch / 2, 0, vertical_standoff + (body_length / 2) - marking_offset)) + ) else: - marking = cq.Workplane("YZ") \ - .cylinder(marking_width, marking_diameter / 2, centered=(True, False, True)) \ + marking = ( + cq.Workplane('YZ') + .cylinder(marking_width, marking_diameter / 2, centered=(True, False, True)) .translate((-marking_offset, 0, 0)) + ) assembly.add_body(marking, 'marking', cq.Color('gray80')) else: raise RuntimeError(f'Unsupported 3D package type: {pkg_type}') - leg_length = StepConstants.THT_LEAD_SOLDER_LENGTH - bend_radius + \ - ((body_length + 2 * vertical_standoff + (leg_diameter / 2)) if vertical - else (body_diameter / 2)) - leg_path = cq.Workplane("XZ") \ - .vLine(leg_length) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .hLine(pitch - 2 * bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ + leg_length = ( + StepConstants.THT_LEAD_SOLDER_LENGTH + - bend_radius + + ((body_length + 2 * vertical_standoff + (leg_diameter / 2)) if vertical else (body_diameter / 2)) + ) + leg_path = ( + cq.Workplane('XZ') + .vLine(leg_length) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .hLine(pitch - 2 * bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) .vLine(-leg_length) - leg = cq.Workplane("XY") \ - .circle(leg_diameter / 2) \ - .sweep(leg_path) \ + ) + leg = ( + cq.Workplane('XY') + .circle(leg_diameter / 2) + .sweep(leg_path) .translate((-pitch / 2, 0, -StepConstants.THT_LEAD_SOLDER_LENGTH)) + ) assembly.add_body(leg, 'leg', StepColor.LEAD_THT) out_path = path.join('out', library, 'pkg', uuid_pkg, f'{uuid_3d}.step') @@ -705,8 +825,7 @@ def generate_3d( pkg_type='DO', pkg_identifier='do204aa', name='DO-204AA', - description='Diode outline package as specified by JEDEC DO-204AA. ' + - 'Also known as DO-7.', + description='Diode outline package as specified by JEDEC DO-204AA. ' + 'Also known as DO-7.', keywords='do204aa,do7,do-7', leg_diameter_nom=(0.46 + 0.55) / 2, # b body_diameter_nom=(2.16 + 2.71) / 2, # D @@ -735,8 +854,7 @@ def generate_3d( pkg_type='DO', pkg_identifier='do204ac', name='DO-204AC', - description='Diode outline package as specified by JEDEC DO-204AC. ' + - 'Also known as DO-15.', + description='Diode outline package as specified by JEDEC DO-204AC. ' + 'Also known as DO-15.', keywords='do204ac,do15,do-15', leg_diameter_nom=(0.69 + 0.88) / 2, # b body_diameter_nom=(2.65 + 3.55) / 2, # D @@ -765,8 +883,7 @@ def generate_3d( pkg_type='DO', pkg_identifier='do204ag', name='DO-204AG', - description='Diode outline package as specified by JEDEC DO-204AG. ' + - 'Also known as DO-34.', + description='Diode outline package as specified by JEDEC DO-204AG. ' + 'Also known as DO-34.', keywords='do204ag,do43,do-34', leg_diameter_nom=(0.46 + 0.55) / 2, # b body_diameter_nom=(1.27 + 1.9) / 2, # D @@ -794,8 +911,7 @@ def generate_3d( pkg_type='DO', pkg_identifier='do204ah', name='DO-204AH', - description='Diode outline package as specified by JEDEC DO-204AH. ' + - 'Also known as DO-35.', + description='Diode outline package as specified by JEDEC DO-204AH. ' + 'Also known as DO-35.', keywords='do204ah,do35,do-35', leg_diameter_nom=(0.46 + 0.55) / 2, # b body_diameter_nom=(1.53 + 2.28) / 2, # D @@ -823,8 +939,7 @@ def generate_3d( pkg_type='DO', pkg_identifier='do204al', name='DO-204AL', - description='Diode outline package as specified by JEDEC DO-204AL. ' + - 'Also known as DO-41.', + description='Diode outline package as specified by JEDEC DO-204AL. ' + 'Also known as DO-41.', keywords='do204al,do41,do-41', leg_diameter_nom=(0.72 + 0.86) / 2, # b body_diameter_nom=(2.04 + 2.71) / 2, # D diff --git a/generate_capacitor_radial_tht.py b/generate_capacitor_radial_tht.py index d5f64f3..55d0968 100644 --- a/generate_capacitor_radial_tht.py +++ b/generate_capacitor_radial_tht.py @@ -1,6 +1,7 @@ """ Generate THT polarized radial electrolytic capacitors (CAPPRD). """ + from os import path from uuid import uuid4 @@ -8,15 +9,57 @@ from common import format_ipc_dimension, init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.component import SignalUUID from entities.device import ComponentPad, ComponentUUID, Device, PackageUUID from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, DrillDiameter, Footprint, FootprintPad, LetterSpacing, - LineSpacing, Mirror, Package, PackagePad, PackagePadUuid, PadFunction, PadHole, Shape, ShapeRadius, Size, - SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_capacitor_radial_tht.py)' @@ -75,21 +118,25 @@ def generate_pkg( # Name according IPC-7351 "Capacitor, Polarized Radial Diameter": # CAPPRD + Lead Spacing + W Lead Width + D Body Diameter + H Body Height name = 'CAPPRD{}W{}D{}H{}'.format( - format_ipc_dimension(pitch), format_ipc_dimension(lead_width), - format_ipc_dimension(diameter), format_ipc_dimension(height)) + format_ipc_dimension(pitch), + format_ipc_dimension(lead_width), + format_ipc_dimension(diameter), + format_ipc_dimension(height), + ) variant = get_variant(diameter, height, pitch, lead_width) def _pkg_uuid(identifier: str) -> str: return uuid('pkg', variant, identifier) def _create_footprint(footprint_identifier: str, name: str) -> Footprint: - def _fpt_uuid(identifier: str) -> str: return _pkg_uuid(footprint_identifier + '-' + identifier) drill = LEAD_WIDTH_TO_DRILL[lead_width] - restring = min((0.4 if diameter >= 6.0 else 0.3), # preferred restring - (pitch - drill - 0.25) / 2) # minimum required restring + restring = min( + (0.4 if diameter >= 6.0 else 0.3), # preferred restring + (pitch - drill - 0.25) / 2, + ) # minimum required restring pad_diameter = drill + (2 * restring) # outer diameter of pad courtyard_diameter = diameter + (1.0 if diameter >= 10.0 else 0.8) @@ -101,7 +148,7 @@ def _generate_fill_polygon(identifier: str, layer: str) -> Polygon: fill=Fill(True), grab_area=GrabArea(False), ) - if ((pitch - pad_diameter) < 0.6): + if (pitch - pad_diameter) < 0.6: # not enough space, use a simplified polygon vertices = [ (0.0, (diameter / 2) - 0.2, 0.0), @@ -136,108 +183,126 @@ def _generate_fill_polygon(identifier: str, layer: str) -> Polygon: ) pad_hole_path = [Vertex(Position(0.0, 0.0), Angle(0.0))] uuid_plus = _pkg_uuid('pad-plus') - footprint.add_pad(FootprintPad( - uuid=uuid_plus, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(-pitch / 2, 0), - rotation=Rotation(0), - size=Size(pad_diameter, pad_diameter), - radius=ShapeRadius(0.0), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.UNSPECIFIED, - package_pad=PackagePadUuid(uuid_plus), - holes=[PadHole(uuid_plus, DrillDiameter(drill), pad_hole_path)], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_plus, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(-pitch / 2, 0), + rotation=Rotation(0), + size=Size(pad_diameter, pad_diameter), + radius=ShapeRadius(0.0), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.UNSPECIFIED, + package_pad=PackagePadUuid(uuid_plus), + holes=[PadHole(uuid_plus, DrillDiameter(drill), pad_hole_path)], + ) + ) uuid_minus = _pkg_uuid('pad-minus') - footprint.add_pad(FootprintPad( - uuid=uuid_minus, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(pitch / 2, 0), - rotation=Rotation(0), - size=Size(pad_diameter, pad_diameter), - radius=ShapeRadius(1.0), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.UNSPECIFIED, - package_pad=PackagePadUuid(uuid_minus), - holes=[PadHole(uuid_minus, DrillDiameter(drill), pad_hole_path)], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_minus, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(pitch / 2, 0), + rotation=Rotation(0), + size=Size(pad_diameter, pad_diameter), + radius=ShapeRadius(1.0), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.UNSPECIFIED, + package_pad=PackagePadUuid(uuid_minus), + holes=[PadHole(uuid_minus, DrillDiameter(drill), pad_hole_path)], + ) + ) # placement - footprint.add_circle(Circle( - uuid=_fpt_uuid('circle-placement'), - layer=Layer('top_legend'), - width=Width(0.2), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(diameter + 0.2), - position=Position(0.0, 0.0), - )) - footprint.add_polygon(_generate_fill_polygon( - identifier='polygon-placement-fill', - layer='top_legend', - )) + footprint.add_circle( + Circle( + uuid=_fpt_uuid('circle-placement'), + layer=Layer('top_legend'), + width=Width(0.2), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(diameter + 0.2), + position=Position(0.0, 0.0), + ) + ) + footprint.add_polygon( + _generate_fill_polygon( + identifier='polygon-placement-fill', + layer='top_legend', + ) + ) # documentation - footprint.add_circle(Circle( - uuid=_fpt_uuid('circle-documentation'), - layer=Layer('top_documentation'), - width=Width(0.2), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(diameter - 0.2), - position=Position(0.0, 0.0), - )) - footprint.add_polygon(_generate_fill_polygon( - identifier='polygon-documentation-fill', - layer='top_documentation', - )) + footprint.add_circle( + Circle( + uuid=_fpt_uuid('circle-documentation'), + layer=Layer('top_documentation'), + width=Width(0.2), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(diameter - 0.2), + position=Position(0.0, 0.0), + ) + ) + footprint.add_polygon( + _generate_fill_polygon( + identifier='polygon-documentation-fill', + layer='top_documentation', + ) + ) # courtyard - footprint.add_circle(Circle( - uuid=_fpt_uuid('circle-courtyard'), - layer=Layer('top_courtyard'), - width=Width(0.0), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(courtyard_diameter), - position=Position(0.0, 0.0), - )) + footprint.add_circle( + Circle( + uuid=_fpt_uuid('circle-courtyard'), + layer=Layer('top_courtyard'), + width=Width(0.0), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(courtyard_diameter), + position=Position(0.0, 0.0), + ) + ) # texts - footprint.add_text(StrokeText( - uuid=_fpt_uuid('text-name'), - layer=Layer('top_names'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, (diameter / 2) + 0.8), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_fpt_uuid('text-value'), - layer=Layer('top_values'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -(diameter / 2) - 0.8), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_fpt_uuid('text-name'), + layer=Layer('top_names'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, (diameter / 2) + 0.8), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_fpt_uuid('text-value'), + layer=Layer('top_values'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -(diameter / 2) - 0.8), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) return footprint # package @@ -245,12 +310,12 @@ def _generate_fill_polygon(identifier: str, layer: str) -> Polygon: uuid=_pkg_uuid('pkg'), name=Name(name), description=Description( - 'Polarized radial electrolytic capacitor.\n\n' + - 'Diameter: {} mm\n'.format(diameter) + - 'Height: {} mm\n'.format(height) + - 'Lead Spacing: {} mm\n'.format(pitch) + - 'Max. Lead Diameter: {} mm\n\n'.format(lead_width) + - 'Generated with {}'.format(generator) + 'Polarized radial electrolytic capacitor.\n\n' + + 'Diameter: {} mm\n'.format(diameter) + + 'Height: {} mm\n'.format(height) + + 'Lead Spacing: {} mm\n'.format(pitch) + + 'Max. Lead Diameter: {} mm\n\n'.format(lead_width) + + 'Generated with {}'.format(generator) ), keywords=Keywords('electrolytic,capacitor,polarized,radial,c,cap,cpol'), author=Author(author), @@ -263,10 +328,12 @@ def _generate_fill_polygon(identifier: str, layer: str) -> Polygon: ) package.add_pad(PackagePad(uuid=_pkg_uuid('pad-plus'), name=Name('+'))) package.add_pad(PackagePad(uuid=_pkg_uuid('pad-minus'), name=Name('-'))) - package.add_footprint(_create_footprint( - footprint_identifier='default', - name='default', - )) + package.add_footprint( + _create_footprint( + footprint_identifier='default', + name='default', + ) + ) # write files package.serialize(path.join('out', library, 'pkg')) @@ -293,12 +360,12 @@ def _uuid(identifier: str) -> str: uuid=_uuid('dev'), name=Name(name), description=Description( - 'Generic polarized radial electrolytic capacitor.\n\n' + - 'Diameter: {} mm\n'.format(diameter) + - 'Height: {} mm\n'.format(height) + - 'Lead Spacing: {} mm\n'.format(pitch) + - 'Max. Lead Diameter: {} mm\n\n'.format(lead_width) + - 'Generated with {}'.format(generator) + 'Generic polarized radial electrolytic capacitor.\n\n' + + 'Diameter: {} mm\n'.format(diameter) + + 'Height: {} mm\n'.format(height) + + 'Lead Spacing: {} mm\n'.format(pitch) + + 'Max. Lead Diameter: {} mm\n\n'.format(lead_width) + + 'Generated with {}'.format(generator) ), keywords=Keywords('electrolytic,capacitor,polarized,radial,c,cap,cpol'), author=Author(author), @@ -310,14 +377,18 @@ def _uuid(identifier: str) -> str: component_uuid=ComponentUUID('c54375c5-7149-4ded-95c5-7462f7301ee7'), package_uuid=PackageUUID(uuid('pkg', variant, 'pkg')), ) - device.add_pad(ComponentPad( - pad_uuid=uuid('pkg', variant, 'pad-plus'), - signal=SignalUUID('e010ecbb-6210-4da3-9270-ebd58656dbf0'), - )) - device.add_pad(ComponentPad( - pad_uuid=uuid('pkg', variant, 'pad-minus'), - signal=SignalUUID('af3ffca8-0085-4edb-a775-fcb759f63411'), - )) + device.add_pad( + ComponentPad( + pad_uuid=uuid('pkg', variant, 'pad-plus'), + signal=SignalUUID('e010ecbb-6210-4da3-9270-ebd58656dbf0'), + ) + ) + device.add_pad( + ComponentPad( + pad_uuid=uuid('pkg', variant, 'pad-minus'), + signal=SignalUUID('af3ffca8-0085-4edb-a775-fcb759f63411'), + ) + ) # write files device.serialize(path.join('out', library, 'dev')) @@ -325,25 +396,24 @@ def _uuid(identifier: str) -> str: if __name__ == '__main__': - CONFIGS = [ # Some typical, frequently used configurations. The lead width depends # from package to package, thus choosing the highest value to ensure # compatibility with all variants (models with thinner leads can # still be mount). - {'diameter': 3.0, 'height': 5.0, 'pitch': 1.0, 'lead_width': 0.4}, - {'diameter': 4.0, 'height': 5.0, 'pitch': 1.5, 'lead_width': 0.45}, - {'diameter': 4.0, 'height': 7.0, 'pitch': 1.5, 'lead_width': 0.45}, - {'diameter': 4.0, 'height': 11.0, 'pitch': 1.5, 'lead_width': 0.45}, - {'diameter': 5.0, 'height': 5.0, 'pitch': 2.0, 'lead_width': 0.5}, - {'diameter': 5.0, 'height': 7.0, 'pitch': 2.0, 'lead_width': 0.5}, - {'diameter': 5.0, 'height': 11.0, 'pitch': 2.0, 'lead_width': 0.5}, - {'diameter': 6.3, 'height': 5.0, 'pitch': 2.5, 'lead_width': 0.5}, - {'diameter': 6.3, 'height': 7.0, 'pitch': 2.5, 'lead_width': 0.5}, - {'diameter': 6.3, 'height': 11.0, 'pitch': 2.5, 'lead_width': 0.5}, - {'diameter': 8.0, 'height': 5.0, 'pitch': 2.5, 'lead_width': 0.6}, - {'diameter': 8.0, 'height': 7.0, 'pitch': 3.5, 'lead_width': 0.6}, - {'diameter': 8.0, 'height': 11.5, 'pitch': 3.5, 'lead_width': 0.6}, + {'diameter': 3.0, 'height': 5.0, 'pitch': 1.0, 'lead_width': 0.4}, + {'diameter': 4.0, 'height': 5.0, 'pitch': 1.5, 'lead_width': 0.45}, + {'diameter': 4.0, 'height': 7.0, 'pitch': 1.5, 'lead_width': 0.45}, + {'diameter': 4.0, 'height': 11.0, 'pitch': 1.5, 'lead_width': 0.45}, + {'diameter': 5.0, 'height': 5.0, 'pitch': 2.0, 'lead_width': 0.5}, + {'diameter': 5.0, 'height': 7.0, 'pitch': 2.0, 'lead_width': 0.5}, + {'diameter': 5.0, 'height': 11.0, 'pitch': 2.0, 'lead_width': 0.5}, + {'diameter': 6.3, 'height': 5.0, 'pitch': 2.5, 'lead_width': 0.5}, + {'diameter': 6.3, 'height': 7.0, 'pitch': 2.5, 'lead_width': 0.5}, + {'diameter': 6.3, 'height': 11.0, 'pitch': 2.5, 'lead_width': 0.5}, + {'diameter': 8.0, 'height': 5.0, 'pitch': 2.5, 'lead_width': 0.6}, + {'diameter': 8.0, 'height': 7.0, 'pitch': 3.5, 'lead_width': 0.6}, + {'diameter': 8.0, 'height': 11.5, 'pitch': 3.5, 'lead_width': 0.6}, {'diameter': 10.0, 'height': 12.5, 'pitch': 5.0, 'lead_width': 0.6}, {'diameter': 10.0, 'height': 16.0, 'pitch': 5.0, 'lead_width': 0.6}, {'diameter': 10.0, 'height': 20.0, 'pitch': 5.0, 'lead_width': 0.6}, diff --git a/generate_chip.py b/generate_chip.py index 780ff1c..d49025c 100644 --- a/generate_chip.py +++ b/generate_chip.py @@ -5,6 +5,7 @@ - Chip capacitors SMT """ + import sys from os import path from uuid import uuid4 @@ -14,15 +15,56 @@ from common import format_ipc_dimension as fd from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Created, Deprecated, Description, Fill, GeneratedBy, GrabArea, Height, Keywords, - Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width, generate_courtyard + Align, + Angle, + Author, + Category, + Created, + Deprecated, + Description, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, + generate_courtyard, ) from entities.component import SignalUUID from entities.device import ComponentPad, ComponentUUID, Device, PackageUUID from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, Footprint3DModel, FootprintPad, LetterSpacing, - LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, Shape, ShapeRadius, Size, - SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_chip.py)' @@ -90,6 +132,7 @@ class BodyDimensions: """ Dimensions of the physical body. """ + def __init__( self, length: float, @@ -107,7 +150,7 @@ def __init__( @property def gap(self) -> Optional[float]: if self.lead_length: - return (self.length - 2 * self.lead_length) + return self.length - 2 * self.lead_length return None @@ -123,6 +166,7 @@ class FootprintDimensions: L = Length, W = Width, G = Gap """ + def __init__(self, pad_length: float, pad_width: float, pad_gap: float): self.pad_length = pad_length self.pad_width = pad_width @@ -136,6 +180,7 @@ class ChipConfig: Note: Specify either footprints or gap, but not both. """ + def __init__( self, size_imperial: str, # String, e.g. "1206" @@ -143,7 +188,7 @@ def __init__( *, footprints: Optional[Dict[str, FootprintDimensions]] = None, gap: Optional[float] = None, - meta: Optional[Dict[str, str]] = None # Metadata that can be used in description + meta: Optional[Dict[str, str]] = None, # Metadata that can be used in description ): self._size_imperial = size_imperial self.body = body @@ -160,22 +205,16 @@ def __init__( raise ValueError('Invalid density level: {}'.format(density_level)) def size_metric(self) -> str: - return str(int(self.body.length * 10)).rjust(2, '0') + \ - str(int(self.body.width * 10 if self.body.width < 10 else self.body.width)).rjust(2, '0') + return str(int(self.body.length * 10)).rjust(2, '0') + str( + int(self.body.width * 10 if self.body.width < 10 else self.body.width) + ).rjust(2, '0') def size_imperial(self) -> str: return self._size_imperial class PolarizationConfig: - def __init__( - self, - *, - name_marked: str, - id_marked: str, - name_unmarked: str, - id_unmarked: str - ): + def __init__(self, *, name_marked: str, id_marked: str, name_unmarked: str, id_unmarked: str): self.name_marked = name_marked self.id_marked = id_marked self.name_unmarked = name_unmarked @@ -194,7 +233,7 @@ def generate_pkg( pkgcat: str, keywords: str, version: str, - create_date: Optional[str] + create_date: Optional[str], ) -> None: category = 'pkg' for config in configs: @@ -219,12 +258,17 @@ def generate_pkg( 'meta': config.meta, } full_name = name.format(**fmt_params_name) - full_desc = description.format(**fmt_params_desc) + \ - "\n\nGenerated with {}".format(generator) - full_keywords = ",".join(filter(None, [ - config.size_metric(), config.size_imperial(), - keywords.format(**fmt_params_desc).lower(), - ])) + full_desc = description.format(**fmt_params_desc) + '\n\nGenerated with {}'.format(generator) + full_keywords = ','.join( + filter( + None, + [ + config.size_metric(), + config.size_imperial(), + keywords.format(**fmt_params_desc).lower(), + ], + ) + ) def _uuid(identifier: str) -> str: return uuid(category, full_name, identifier) @@ -264,7 +308,7 @@ def add_footprint_variant( density_level: str, *, gap: Optional[float] = None, - dimensions: Optional[FootprintDimensions] = None + dimensions: Optional[FootprintDimensions] = None, ) -> None: """ Generate a footprint variant. @@ -318,33 +362,35 @@ def add_footprint_variant( pad_width = dimensions.pad_width pad_length = dimensions.pad_length pad_gap = dimensions.pad_gap - pad_dx = (pad_gap / 2 + pad_length / 2) # x offset (delta-x) + pad_dx = pad_gap / 2 + pad_length / 2 # x offset (delta-x) elif gap is not None: pad_gap = gap pad_width = config.body.width + get_by_density(config.body.length, density_level, 'side') pad_toe = get_by_density(config.body.length, density_level, 'toe') pad_length = (config.body.length - gap) / 2 + pad_toe - pad_dx = (gap / 2 + pad_length / 2) # x offset (delta-x) + pad_dx = gap / 2 + pad_length / 2 # x offset (delta-x) else: raise ValueError('Either dimensions or gap must be set') for p in [0, 1]: pad_uuid = uuid_pads[p - 1] sign = -1 if p == 1 else 1 - footprint.add_pad(FootprintPad( - uuid=pad_uuid, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(sign * pad_dx, 0), - rotation=Rotation(0), - size=Size(pad_length, pad_width), - radius=ShapeRadius(0), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(pad_uuid), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=pad_uuid, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(sign * pad_dx, 0), + rotation=Rotation(0), + size=Size(pad_length, pad_width), + radius=ShapeRadius(0), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(pad_uuid), + holes=[], + ) + ) max_x = max(max_x, pad_length / 2 + sign * pad_dx) max_y = max(max_y, config.body.width / 2) max_y = max(max_y, pad_width / 2) @@ -355,125 +401,141 @@ def add_footprint_variant( # We assume that leads are across the entire width of the part (e.g. MLCC) dx = config.body.length / 2 dy = config.body.width / 2 - footprint.add_polygon(Polygon( - uuid=uuid_body_left, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), # NW - Vertex(Position(-half_gap, dy), Angle(0)), # NE - Vertex(Position(-half_gap, -dy), Angle(0)), # SE - Vertex(Position(-dx, -dy), Angle(0)), # SW - Vertex(Position(-dx, dy), Angle(0)), # NW - ], - )) - footprint.add_polygon(Polygon( - uuid=uuid_body_right, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(dx, dy), Angle(0)), # NE - Vertex(Position(half_gap, dy), Angle(0)), # NW - Vertex(Position(half_gap, -dy), Angle(0)), # SW - Vertex(Position(dx, -dy), Angle(0)), # SE - Vertex(Position(dx, dy), Angle(0)), # NE - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_body_left, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), # NW + Vertex(Position(-half_gap, dy), Angle(0)), # NE + Vertex(Position(-half_gap, -dy), Angle(0)), # SE + Vertex(Position(-dx, -dy), Angle(0)), # SW + Vertex(Position(-dx, dy), Angle(0)), # NW + ], + ) + ) + footprint.add_polygon( + Polygon( + uuid=uuid_body_right, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(dx, dy), Angle(0)), # NE + Vertex(Position(half_gap, dy), Angle(0)), # NW + Vertex(Position(half_gap, -dy), Angle(0)), # SW + Vertex(Position(dx, -dy), Angle(0)), # SE + Vertex(Position(dx, dy), Angle(0)), # NE + ], + ) + ) dy = config.body.width / 2 - doc_lw / 2 - footprint.add_polygon(Polygon( - uuid=uuid_body_top, - layer=Layer('top_documentation'), - width=Width(doc_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-half_gap, dy), Angle(0)), - Vertex(Position(half_gap, dy), Angle(0)), - ], - )) - footprint.add_polygon(Polygon( - uuid=uuid_body_bot, - layer=Layer('top_documentation'), - width=Width(doc_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-half_gap, -dy), Angle(0)), - Vertex(Position(half_gap, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_body_top, + layer=Layer('top_documentation'), + width=Width(doc_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-half_gap, dy), Angle(0)), + Vertex(Position(half_gap, dy), Angle(0)), + ], + ) + ) + footprint.add_polygon( + Polygon( + uuid=uuid_body_bot, + layer=Layer('top_documentation'), + width=Width(doc_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-half_gap, -dy), Angle(0)), + Vertex(Position(half_gap, -dy), Angle(0)), + ], + ) + ) else: # We have more precise information about the lead (e.g. molded # packages where leads are not the full width of the package). dx = config.body.length / 2 - doc_lw / 2 dy = config.body.width / 2 - doc_lw / 2 - footprint.add_polygon(Polygon( - uuid=uuid_body_around, - layer=Layer('top_documentation'), - width=Width(doc_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), - Vertex(Position(dx, dy), Angle(0)), - Vertex(Position(dx, -dy), Angle(0)), - Vertex(Position(-dx, -dy), Angle(0)), - Vertex(Position(-dx, dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_body_around, + layer=Layer('top_documentation'), + width=Width(doc_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), + Vertex(Position(dx, dy), Angle(0)), + Vertex(Position(dx, -dy), Angle(0)), + Vertex(Position(-dx, -dy), Angle(0)), + Vertex(Position(-dx, dy), Angle(0)), + ], + ) + ) dx = config.body.length / 2 dy = (config.body.lead_width or dimensions.pad_width) / 2 - footprint.add_polygon(Polygon( - uuid=uuid_body_left, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), - Vertex(Position(-half_gap, dy), Angle(0)), - Vertex(Position(-half_gap, -dy), Angle(0)), - Vertex(Position(-dx, -dy), Angle(0)), - Vertex(Position(-dx, dy), Angle(0)), - ], - )) - footprint.add_polygon(Polygon( - uuid=uuid_body_right, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(dx, dy), Angle(0)), - Vertex(Position(half_gap, dy), Angle(0)), - Vertex(Position(half_gap, -dy), Angle(0)), - Vertex(Position(dx, -dy), Angle(0)), - Vertex(Position(dx, dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_body_left, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), + Vertex(Position(-half_gap, dy), Angle(0)), + Vertex(Position(-half_gap, -dy), Angle(0)), + Vertex(Position(-dx, -dy), Angle(0)), + Vertex(Position(-dx, dy), Angle(0)), + ], + ) + ) + footprint.add_polygon( + Polygon( + uuid=uuid_body_right, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(dx, dy), Angle(0)), + Vertex(Position(half_gap, dy), Angle(0)), + Vertex(Position(half_gap, -dy), Angle(0)), + Vertex(Position(dx, -dy), Angle(0)), + Vertex(Position(dx, dy), Angle(0)), + ], + ) + ) if polarization: polarization_mark_width = config.body.width / 8 dx_outer = half_gap - polarization_mark_width / 2 dx_inner = half_gap - polarization_mark_width * 1.5 dy = config.body.width / 2 - doc_lw - footprint.add_polygon(Polygon( - uuid=uuid_polarization_mark, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(True), - vertices=[ - Vertex(Position(-dx_outer, dy), Angle(0)), - Vertex(Position(-dx_inner, dy), Angle(0)), - Vertex(Position(-dx_inner, -dy), Angle(0)), - Vertex(Position(-dx_outer, -dy), Angle(0)), - Vertex(Position(-dx_outer, dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_polarization_mark, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(True), + vertices=[ + Vertex(Position(-dx_outer, dy), Angle(0)), + Vertex(Position(-dx_inner, dy), Angle(0)), + Vertex(Position(-dx_inner, -dy), Angle(0)), + Vertex(Position(-dx_outer, -dy), Angle(0)), + Vertex(Position(-dx_outer, dy), Angle(0)), + ], + ) + ) # Silkscreen if config.body.length > 1.0: @@ -484,73 +546,84 @@ def add_footprint_variant( config.body.width / 2 + silk_lw / 2, # Based on body width pad_width / 2 + silk_lw / 2 + silkscreen_clearance, # Based on pad width ) - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen_top, - layer=Layer('top_legend'), - width=Width(silk_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(dx_unmarked, dy), Angle(0)), - Vertex(Position(-dx_marked, dy), Angle(0)), - Vertex(Position(-dx_marked, -dy), Angle(0)), - Vertex(Position(dx_unmarked, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen_top, + layer=Layer('top_legend'), + width=Width(silk_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(dx_unmarked, dy), Angle(0)), + Vertex(Position(-dx_marked, dy), Angle(0)), + Vertex(Position(-dx_marked, -dy), Angle(0)), + Vertex(Position(dx_unmarked, -dy), Angle(0)), + ], + ) + ) else: - assert gap is not None, \ - "Support for non-polarized packages with irregular pads not yet fully implemented" + assert ( + gap is not None + ), 'Support for non-polarized packages with irregular pads not yet fully implemented' dx = gap / 2 - silk_lw / 2 - silkscreen_clearance dy = config.body.width / 2 + silk_lw / 2 - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen_top, - layer=Layer('top_legend'), - width=Width(silk_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), - Vertex(Position(dx, dy), Angle(0)), - ], - )) - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen_bot, - layer=Layer('top_legend'), - width=Width(silk_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, -dy), Angle(0)), - Vertex(Position(dx, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen_top, + layer=Layer('top_legend'), + width=Width(silk_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), + Vertex(Position(dx, dy), Angle(0)), + ], + ) + ) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen_bot, + layer=Layer('top_legend'), + width=Width(silk_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, -dy), Angle(0)), + Vertex(Position(dx, -dy), Angle(0)), + ], + ) + ) # Package outlines dx = config.body.length / 2 dy = config.body.width / 2 - footprint.add_polygon(Polygon( - uuid=uuid_outline, - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), # NW - Vertex(Position(dx, dy), Angle(0)), # NE - Vertex(Position(dx, -dy), Angle(0)), # SE - Vertex(Position(-dx, -dy), Angle(0)), # SW - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_outline, + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), # NW + Vertex(Position(dx, dy), Angle(0)), # NE + Vertex(Position(dx, -dy), Angle(0)), # SE + Vertex(Position(-dx, -dy), Angle(0)), # SW + ], + ) + ) # Courtyard courtyard_excess = get_by_density(config.body.length, density_level, 'courtyard') - footprint.add_polygon(generate_courtyard( - uuid=uuid_courtyard, - max_x=max_x, - max_y=max_y, - excess_x=courtyard_excess, - excess_y=courtyard_excess, - )) + footprint.add_polygon( + generate_courtyard( + uuid=uuid_courtyard, + max_x=max_x, + max_y=max_y, + excess_x=courtyard_excess, + excess_y=courtyard_excess, + ) + ) # Labels if config.body.width < 2.0: @@ -558,34 +631,38 @@ def add_footprint_variant( else: offset = label_offset dy = config.body.width / 2 + offset # y offset (delta-y) - footprint.add_text(StrokeText( - uuid=uuid_text_name, - layer=Layer('top_names'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, dy), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=uuid_text_value, - layer=Layer('top_values'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -dy), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=uuid_text_name, + layer=Layer('top_names'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, dy), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=uuid_text_value, + layer=Layer('top_values'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -dy), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) if config.gap: add_footprint_variant('density~b', 'Density Level B (median protrusion)', 'B', gap=config.gap) @@ -615,9 +692,7 @@ def add_footprint_variant( # For unsupported package types, approve the warnings for footprint in package.footprints: package.add_approval( - "(approved missing_footprint_3d_model\n" + - " (footprint {})\n".format(footprint.uuid) + - ")" + '(approved missing_footprint_3d_model\n' + ' (footprint {})\n'.format(footprint.uuid) + ')' ) package.serialize(path.join('out', library, category)) @@ -651,20 +726,23 @@ def generate_3d( translation = (0, 0, height / 2) edge_offset = length / 2 - edge - inner = cq.Workplane("XY") \ - .box(length - 2 * edge, width, height) \ - .edges('+X').fillet(fillet) \ + inner = cq.Workplane('XY').box(length - 2 * edge, width, height).edges('+X').fillet(fillet).translate(translation) + left = ( + cq.Workplane('XY') + .box(edge, width, height) + .edges('+X or X').fillet(fillet) \ - .translate(translation) \ + ) + right = ( + cq.Workplane('XY') + .box(edge, width, height) + .edges('+X or >X') + .fillet(fillet) + .translate(translation) .translate((edge_offset + edge / 2, 0, 0)) + ) if package_type == 'RESC': inner_color = cq.Color('gray16') @@ -695,18 +773,17 @@ def generate_dev( signals: Iterable[str], keywords: str, version: str, - create_date: Optional[str] + create_date: Optional[str], ) -> None: category = 'dev' - for (size_metric, size_imperial, pkg_name) in packages: + for size_metric, size_imperial, pkg_name in packages: fmt_params: Dict[str, str] = { 'size_metric': size_metric, 'size_imperial': size_imperial, } full_name = name.format(**fmt_params) - full_desc = description.format(**fmt_params) + \ - "\n\nGenerated with {}".format(generator) - full_keywords = "{},{},{}".format(size_metric, size_imperial, keywords) + full_desc = description.format(**fmt_params) + '\n\nGenerated with {}'.format(generator) + full_keywords = '{},{},{}'.format(size_metric, size_imperial, keywords) def _uuid(identifier: str) -> str: return uuid(category, full_name, identifier) @@ -733,11 +810,11 @@ def _uuid(identifier: str) -> str: package_uuid=PackageUUID(pkg), ) - for (pad, signal) in sorted(zip(pads, signals)): + for pad, signal in sorted(zip(pads, signals)): device.add_pad(ComponentPad(pad_uuid=pad, signal=SignalUUID(signal))) # Approve "no parts" warning because it's a generic device - device.add_approval("(approved no_parts)") + device.add_approval('(approved no_parts)') device.serialize(path.join('out', library, category)) @@ -762,20 +839,20 @@ def _uuid(identifier: str) -> str: package_type='RESC', name='{package_type}{size_metric} ({size_imperial})', description='Generic chip resistor {size_metric} (imperial {size_imperial}).\n\n' - 'Length: {length}mm\nWidth: {width}mm', + 'Length: {length}mm\nWidth: {width}mm', polarization=None, configs=[ # Configuration: Values taken from Samsung specs. - ChipConfig('01005', BodyDimensions(.4, .2, 0.15), gap=0.2), # noqa - ChipConfig('0201', BodyDimensions(.6, .3, 0.26), gap=0.28), # noqa - ChipConfig('0402', BodyDimensions(1.0, .5, 0.35), gap=0.5), # noqa - ChipConfig('0603', BodyDimensions(1.6, .8, 0.55), gap=0.8), # noqa - ChipConfig('0805', BodyDimensions(2.0, 1.25, 0.70), gap=1.2), # noqa - ChipConfig('1206', BodyDimensions(3.2, 1.6, 0.70), gap=1.8), # noqa - ChipConfig('1210', BodyDimensions(3.2, 2.55, 0.70), gap=1.8), # noqa - ChipConfig('1218', BodyDimensions(3.2, 4.6, 0.70), gap=1.8), # noqa - ChipConfig('2010', BodyDimensions(5.0, 2.5, 0.70), gap=3.3), # noqa - ChipConfig('2512', BodyDimensions(6.4, 3.2, 0.70), gap=4.6), # noqa + ChipConfig('01005', BodyDimensions(0.4, 0.2, 0.15), gap=0.2), # noqa + ChipConfig('0201', BodyDimensions(0.6, 0.3, 0.26), gap=0.28), # noqa + ChipConfig('0402', BodyDimensions(1.0, 0.5, 0.35), gap=0.5), # noqa + ChipConfig('0603', BodyDimensions(1.6, 0.8, 0.55), gap=0.8), # noqa + ChipConfig('0805', BodyDimensions(2.0, 1.25, 0.70), gap=1.2), # noqa + ChipConfig('1206', BodyDimensions(3.2, 1.6, 0.70), gap=1.8), # noqa + ChipConfig('1210', BodyDimensions(3.2, 2.55, 0.70), gap=1.8), # noqa + ChipConfig('1218', BodyDimensions(3.2, 4.6, 0.70), gap=1.8), # noqa + ChipConfig('2010', BodyDimensions(5.0, 2.5, 0.70), gap=3.3), # noqa + ChipConfig('2512', BodyDimensions(6.4, 3.2, 0.70), gap=4.6), # noqa ], generate_3d_models=generate_3d_models, pkgcat='a20f0330-06d3-4bc2-a1fa-f8577deb6770', @@ -790,7 +867,7 @@ def _uuid(identifier: str) -> str: package_type='RESJ', name='{package_type}{size_metric} ({size_imperial})', description='Generic J-lead resistor {size_metric} (imperial {size_imperial}).\n\n' - 'Length: {length}mm\nWidth: {width}mm', + 'Length: {length}mm\nWidth: {width}mm', polarization=None, configs=[ ChipConfig('4527', BodyDimensions(11.56, 6.98, 5.84), gap=5.2), @@ -808,7 +885,7 @@ def _uuid(identifier: str) -> str: package_type='CAPC', name='{package_type}{size_metric} ({size_imperial})', description='Generic chip capacitor {size_metric} (imperial {size_imperial}).\n\n' - 'Length: {length}mm\nWidth: {width}mm', + 'Length: {length}mm\nWidth: {width}mm', polarization=None, configs=[ # C0402 @@ -852,9 +929,9 @@ def _uuid(identifier: str) -> str: package_type='CAPPM', name='{package_type}{length}X{width}X{height}L{lead_length}X{lead_width}', description='Generic polarized molded inward-L capacitor (EIA {meta[eia]}).\n\n' - 'Length: {length}mm\nWidth: {width}mm\nMax height: {height}mm\n\n' - 'EIA Size Code: {meta[eia]}\n' - 'KEMET Case Code: {meta[kemet]}\nAVX Case Code: {meta[avx]}', + 'Length: {length}mm\nWidth: {width}mm\nMax height: {height}mm\n\n' + 'EIA Size Code: {meta[eia]}\n' + 'KEMET Case Code: {meta[kemet]}\nAVX Case Code: {meta[avx]}', polarization=PolarizationConfig( name_marked='+', id_marked='p', @@ -862,61 +939,116 @@ def _uuid(identifier: str) -> str: id_unmarked='n', ), configs=[ - ChipConfig('', BodyDimensions(3.2, 1.6, 1.0, 0.8, 1.2), footprints={ - 'A': FootprintDimensions(2.20, 1.35, 0.62), - 'B': FootprintDimensions(1.80, 1.23, 0.82), - 'C': FootprintDimensions(1.42, 1.13, 0.98), - }, meta={'eia': '3216-10', 'kemet': 'I', 'avx': 'K'}), - ChipConfig('', BodyDimensions(3.2, 1.6, 1.2, 0.8, 1.2), footprints={ - 'A': FootprintDimensions(2.20, 1.35, 0.62), - 'B': FootprintDimensions(1.80, 1.23, 0.82), - 'C': FootprintDimensions(1.42, 1.13, 0.98), - }, meta={'eia': '3216-12', 'kemet': 'S', 'avx': 'S'}), - ChipConfig('', BodyDimensions(3.2, 1.6, 1.8, 0.8, 1.2), footprints={ - 'A': FootprintDimensions(2.20, 1.35, 0.62), - 'B': FootprintDimensions(1.80, 1.23, 0.82), - 'C': FootprintDimensions(1.42, 1.13, 0.98), - }, meta={'eia': '3216-18', 'kemet': 'A', 'avx': 'A'}), - ChipConfig('', BodyDimensions(3.5, 2.8, 1.2, 0.8, 2.2), footprints={ - 'A': FootprintDimensions(2.20, 2.35, 0.92), - 'B': FootprintDimensions(1.80, 2.23, 1.12), - 'C': FootprintDimensions(1.42, 2.13, 1.28), - }, meta={'eia': '3528-12', 'kemet': 'T', 'avx': 'T'}), - ChipConfig('', BodyDimensions(3.5, 2.8, 2.1, 0.8, 2.2), footprints={ - 'A': FootprintDimensions(2.21, 2.35, 0.92), - 'B': FootprintDimensions(1.80, 2.23, 1.12), - 'C': FootprintDimensions(1.42, 2.13, 1.28), - }, meta={'eia': '3528-21', 'kemet': 'B', 'avx': 'B'}), - ChipConfig('', BodyDimensions(6.0, 3.2, 1.5, 1.3, 2.2), footprints={ - 'A': FootprintDimensions(2.77, 2.35, 2.37), - 'B': FootprintDimensions(2.37, 2.23, 2.57), - 'C': FootprintDimensions(1.99, 2.13, 2.73), - }, meta={'eia': '6032-15', 'kemet': 'U', 'avx': 'W'}), - ChipConfig('', BodyDimensions(6.0, 3.2, 2.8, 1.3, 2.2), footprints={ - 'A': FootprintDimensions(2.77, 2.35, 2.37), - 'B': FootprintDimensions(2.37, 2.23, 2.57), - 'C': FootprintDimensions(1.99, 2.13, 2.73), - }, meta={'eia': '6032-28', 'kemet': 'C', 'avx': 'C'}), - ChipConfig('', BodyDimensions(7.3, 6.0, 3.8, 1.3, 4.1), footprints={ - 'A': FootprintDimensions(2.77, 4.25, 3.68), - 'B': FootprintDimensions(2.37, 4.13, 3.87), - 'C': FootprintDimensions(1.99, 4.03, 4.03), - }, meta={'eia': '7360-38', 'kemet': 'E', 'avx': 'V'}), - ChipConfig('', BodyDimensions(7.3, 4.3, 2.0, 1.3, 2.4), footprints={ - 'A': FootprintDimensions(2.77, 2.55, 3.67), - 'B': FootprintDimensions(2.37, 2.43, 3.87), - 'C': FootprintDimensions(1.99, 2.33, 4.03), - }, meta={'eia': '7343-20', 'kemet': 'V', 'avx': 'Y'}), - ChipConfig('', BodyDimensions(7.3, 4.3, 3.1, 1.3, 2.4), footprints={ - 'A': FootprintDimensions(2.77, 2.55, 3.67), - 'B': FootprintDimensions(2.37, 2.43, 3.87), - 'C': FootprintDimensions(1.99, 2.33, 4.03), - }, meta={'eia': '7343-31', 'kemet': 'D', 'avx': 'D'}), - ChipConfig('', BodyDimensions(7.3, 4.3, 4.3, 1.3, 2.4), footprints={ - 'A': FootprintDimensions(2.77, 2.55, 3.67), - 'B': FootprintDimensions(2.37, 2.43, 3.87), - 'C': FootprintDimensions(1.99, 2.33, 4.03), - }, meta={'eia': '7343-43', 'kemet': 'X', 'avx': 'E'}), + ChipConfig( + '', + BodyDimensions(3.2, 1.6, 1.0, 0.8, 1.2), + footprints={ + 'A': FootprintDimensions(2.20, 1.35, 0.62), + 'B': FootprintDimensions(1.80, 1.23, 0.82), + 'C': FootprintDimensions(1.42, 1.13, 0.98), + }, + meta={'eia': '3216-10', 'kemet': 'I', 'avx': 'K'}, + ), + ChipConfig( + '', + BodyDimensions(3.2, 1.6, 1.2, 0.8, 1.2), + footprints={ + 'A': FootprintDimensions(2.20, 1.35, 0.62), + 'B': FootprintDimensions(1.80, 1.23, 0.82), + 'C': FootprintDimensions(1.42, 1.13, 0.98), + }, + meta={'eia': '3216-12', 'kemet': 'S', 'avx': 'S'}, + ), + ChipConfig( + '', + BodyDimensions(3.2, 1.6, 1.8, 0.8, 1.2), + footprints={ + 'A': FootprintDimensions(2.20, 1.35, 0.62), + 'B': FootprintDimensions(1.80, 1.23, 0.82), + 'C': FootprintDimensions(1.42, 1.13, 0.98), + }, + meta={'eia': '3216-18', 'kemet': 'A', 'avx': 'A'}, + ), + ChipConfig( + '', + BodyDimensions(3.5, 2.8, 1.2, 0.8, 2.2), + footprints={ + 'A': FootprintDimensions(2.20, 2.35, 0.92), + 'B': FootprintDimensions(1.80, 2.23, 1.12), + 'C': FootprintDimensions(1.42, 2.13, 1.28), + }, + meta={'eia': '3528-12', 'kemet': 'T', 'avx': 'T'}, + ), + ChipConfig( + '', + BodyDimensions(3.5, 2.8, 2.1, 0.8, 2.2), + footprints={ + 'A': FootprintDimensions(2.21, 2.35, 0.92), + 'B': FootprintDimensions(1.80, 2.23, 1.12), + 'C': FootprintDimensions(1.42, 2.13, 1.28), + }, + meta={'eia': '3528-21', 'kemet': 'B', 'avx': 'B'}, + ), + ChipConfig( + '', + BodyDimensions(6.0, 3.2, 1.5, 1.3, 2.2), + footprints={ + 'A': FootprintDimensions(2.77, 2.35, 2.37), + 'B': FootprintDimensions(2.37, 2.23, 2.57), + 'C': FootprintDimensions(1.99, 2.13, 2.73), + }, + meta={'eia': '6032-15', 'kemet': 'U', 'avx': 'W'}, + ), + ChipConfig( + '', + BodyDimensions(6.0, 3.2, 2.8, 1.3, 2.2), + footprints={ + 'A': FootprintDimensions(2.77, 2.35, 2.37), + 'B': FootprintDimensions(2.37, 2.23, 2.57), + 'C': FootprintDimensions(1.99, 2.13, 2.73), + }, + meta={'eia': '6032-28', 'kemet': 'C', 'avx': 'C'}, + ), + ChipConfig( + '', + BodyDimensions(7.3, 6.0, 3.8, 1.3, 4.1), + footprints={ + 'A': FootprintDimensions(2.77, 4.25, 3.68), + 'B': FootprintDimensions(2.37, 4.13, 3.87), + 'C': FootprintDimensions(1.99, 4.03, 4.03), + }, + meta={'eia': '7360-38', 'kemet': 'E', 'avx': 'V'}, + ), + ChipConfig( + '', + BodyDimensions(7.3, 4.3, 2.0, 1.3, 2.4), + footprints={ + 'A': FootprintDimensions(2.77, 2.55, 3.67), + 'B': FootprintDimensions(2.37, 2.43, 3.87), + 'C': FootprintDimensions(1.99, 2.33, 4.03), + }, + meta={'eia': '7343-20', 'kemet': 'V', 'avx': 'Y'}, + ), + ChipConfig( + '', + BodyDimensions(7.3, 4.3, 3.1, 1.3, 2.4), + footprints={ + 'A': FootprintDimensions(2.77, 2.55, 3.67), + 'B': FootprintDimensions(2.37, 2.43, 3.87), + 'C': FootprintDimensions(1.99, 2.33, 4.03), + }, + meta={'eia': '7343-31', 'kemet': 'D', 'avx': 'D'}, + ), + ChipConfig( + '', + BodyDimensions(7.3, 4.3, 4.3, 1.3, 2.4), + footprints={ + 'A': FootprintDimensions(2.77, 2.55, 3.67), + 'B': FootprintDimensions(2.37, 2.43, 3.87), + 'C': FootprintDimensions(1.99, 2.33, 4.03), + }, + meta={'eia': '7343-43', 'kemet': 'X', 'avx': 'E'}, + ), ], generate_3d_models=generate_3d_models, pkgcat='414f873f-4099-47fd-8526-bdd8419de581', @@ -931,7 +1063,7 @@ def _uuid(identifier: str) -> str: package_type='INDC', name='{package_type}{size_metric} ({size_imperial})', description='Generic chip inductor {size_metric} (imperial {size_imperial}).\n\n' - 'Length: {length}mm\nWidth: {width}mm', + 'Length: {length}mm\nWidth: {width}mm', polarization=None, configs=[ # Configuration: Values taken from Taiyo Yuden, TDK and Murata specs. diff --git a/generate_connectors.py b/generate_connectors.py index 898aa06..15fe9c5 100644 --- a/generate_connectors.py +++ b/generate_connectors.py @@ -12,6 +12,7 @@ +---+ """ + import math import sys from functools import partial @@ -22,18 +23,79 @@ from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Length, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Text, Value, Version, - Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Length, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Text, + Value, + Version, + Vertex, + Width, ) from entities.component import ( - Clock, Component, DefaultValue, ForcedNet, Gate, Negated, Norm, PinSignalMap, Prefix, Required, Role, SchematicOnly, - Signal, SignalUUID, Suffix, SymbolUUID, TextDesignator, Variant + Clock, + Component, + DefaultValue, + ForcedNet, + Gate, + Negated, + Norm, + PinSignalMap, + Prefix, + Required, + Role, + SchematicOnly, + Signal, + SignalUUID, + Suffix, + SymbolUUID, + TextDesignator, + Variant, ) from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, DrillDiameter, Footprint, Footprint3DModel, FootprintPad, - LetterSpacing, LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, PadHole, - Shape, ShapeRadius, Size, SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation from entities.symbol import Pin as SymbolPin @@ -164,9 +226,11 @@ def _uuid(identifier: str) -> str: uuid_text_value = _uuid('text-value') full_name = f'{name} {rows}x{per_row:02d} ⌀{drill:.1f}mm' - full_description = f'A generic {rows}x{per_row} {name_lower} ' + \ - f'with {spacing}mm pin spacing and {drill:.1f}mm drill holes.' \ - f'\n\nGenerated with {generator}' + full_description = ( + f'A generic {rows}x{per_row} {name_lower} ' + + f'with {spacing}mm pin spacing and {drill:.1f}mm drill holes.' + f'\n\nGenerated with {generator}' + ) # Define package package = Package( @@ -206,27 +270,29 @@ def _uuid(identifier: str) -> str: x = spacing / 2 if (p % rows == 0) else -spacing / 2 y = get_y(p, i, rows, spacing, False) corner_radius = 0.0 if p == 1 else 1.0 - footprint.add_pad(FootprintPad( - uuid=pad_uuid, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(x, y), - rotation=Rotation(0), - size=Size(pad_size[0], pad_size[1]), - radius=ShapeRadius(corner_radius), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(pad_uuid), - holes=[ - PadHole( - pad_uuid, - DrillDiameter(drill), - [Vertex(Position(0.0, 0.0), Angle(0.0))], - ) - ], - )) + footprint.add_pad( + FootprintPad( + uuid=pad_uuid, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(x, y), + rotation=Rotation(0), + size=Size(pad_size[0], pad_size[1]), + radius=ShapeRadius(corner_radius), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(pad_uuid), + holes=[ + PadHole( + pad_uuid, + DrillDiameter(drill), + [Vertex(Position(0.0, 0.0), Angle(0.0))], + ) + ], + ) + ) # Add silkscreen to footprint silkscreen = generate_silkscreen(category, kind, variant, i, rows) @@ -235,67 +301,75 @@ def _uuid(identifier: str) -> str: # Package outline dx = (width + (rows - 1) * spacing) / 2 dy = (width + (per_row - 1) * spacing) / 2 - footprint.add_polygon(Polygon( - uuid=uuid_outline, - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), - Vertex(Position(dx, dy), Angle(0)), - Vertex(Position(dx, -dy), Angle(0)), - Vertex(Position(-dx, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_outline, + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), + Vertex(Position(dx, dy), Angle(0)), + Vertex(Position(dx, -dy), Angle(0)), + Vertex(Position(-dx, -dy), Angle(0)), + ], + ) + ) # Courtyard dx += courtyard_offset dy += courtyard_offset - footprint.add_polygon(Polygon( - uuid=uuid_courtyard, - layer=Layer('top_courtyard'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), - Vertex(Position(dx, dy), Angle(0)), - Vertex(Position(dx, -dy), Angle(0)), - Vertex(Position(-dx, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_courtyard, + layer=Layer('top_courtyard'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), + Vertex(Position(dx, dy), Angle(0)), + Vertex(Position(dx, -dy), Angle(0)), + Vertex(Position(-dx, -dy), Angle(0)), + ], + ) + ) # Labels y_max, y_min = get_rectangle_bounds(i, rows, spacing, top_offset + 1.27, False) - footprint.add_text(StrokeText( - uuid=uuid_text_name, - layer=Layer('top_names'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, y_max), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=uuid_text_value, - layer=Layer('top_values'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, y_min), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=uuid_text_name, + layer=Layer('top_names'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, y_max), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=uuid_text_value, + layer=Layer('top_values'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, y_min), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # Generate 3D models (for some packages) if generate_3d_model is not None: @@ -310,7 +384,7 @@ def _uuid(identifier: str) -> str: if assembly_type == AssemblyType.NONE: # Assembly type is reported as suspicious because there are # some pads, but this is intended for soldered wire connectors. - package.add_approval("(approved suspicious_assembly_type)") + package.add_approval('(approved suspicious_assembly_type)') package.serialize(path.join('out', library, category)) @@ -430,24 +504,27 @@ def generate_3d_model_generic( # Insulator if model_type == 'female': hole_offset = 1.0 - insulator = cq.Workplane('XY') \ - .box(spacing, spacing + a_little, insulator_height, centered=(True, True, False)) \ - .transformed(offset=(0, 0, hole_offset)) \ - .rect(spacing / 1.5, spacing / 1.5) \ - .offset2D(spacing / 20) \ + insulator = ( + cq.Workplane('XY') + .box(spacing, spacing + a_little, insulator_height, centered=(True, True, False)) + .transformed(offset=(0, 0, hole_offset)) + .rect(spacing / 1.5, spacing / 1.5) + .offset2D(spacing / 20) .cutBlind(insulator_height - hole_offset) + ) else: - insulator = cq.Workplane('XY') \ - .box(spacing, spacing + a_little, standoff_height, centered=(True, True, False)) + insulator = cq.Workplane('XY').box(spacing, spacing + a_little, standoff_height, centered=(True, True, False)) # Lead if model_type == 'female': total_lead_length = lead_length_bottom else: total_lead_length = lead_length_bottom + standoff_height + lead_length_top_exposed - lead = cq.Workplane('XY') \ - .transformed(offset=(0, 0, -lead_length_bottom)) \ + lead = ( + cq.Workplane('XY') + .transformed(offset=(0, 0, -lead_length_bottom)) .box(lead_dimensions[0], lead_dimensions[1], total_lead_length, centered=(True, True, False)) + ) # Combine into assembly assembly = StepAssembly(full_name) @@ -508,8 +585,7 @@ def _uuid(identifier: str) -> str: symbol = Symbol( uuid_sym, Name('{} {}x{:02d}'.format(name, rows, per_row)), - Description('A {}x{} {}.\n\n' - 'Generated with {}'.format(rows, per_row, name_lower, generator)), + Description('A {}x{} {}.\n\n' 'Generated with {}'.format(rows, per_row, name_lower, generator)), Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)), Author(author), Version(version), @@ -536,13 +612,7 @@ def _uuid(identifier: str) -> str: # Polygons y_max, y_min = get_rectangle_bounds(i, rows, spacing, spacing, True) - polygon = Polygon( - uuid_polygon, - Layer('sym_outlines'), - Width(line_width), - Fill(False), - GrabArea(True) - ) + polygon = Polygon(uuid_polygon, Layer('sym_outlines'), Width(line_width), Fill(False), GrabArea(True)) polygon.add_vertex(Vertex(Position(-w, y_max), Angle(0.0))) polygon.add_vertex(Vertex(Position(w, y_max), Angle(0.0))) polygon.add_vertex(Vertex(Position(w, y_min), Angle(0.0))) @@ -559,13 +629,7 @@ def _uuid(identifier: str) -> str: dx = spacing / 8 * 1.5 * x_sign dy = spacing / 8 / 1.5 x_offset = x_sign * (w - 1.27) - polygon = Polygon( - uuid_decoration, - Layer('sym_outlines'), - Width(line_width), - Fill(True), - GrabArea(True) - ) + polygon = Polygon(uuid_decoration, Layer('sym_outlines'), Width(line_width), Fill(True), GrabArea(True)) polygon.add_vertex(Vertex(Position(x_offset - dx, y + dy), Angle(0.0))) polygon.add_vertex(Vertex(Position(x_offset + dx, y + dy), Angle(0.0))) polygon.add_vertex(Vertex(Position(x_offset + dx, y - dy), Angle(0.0))) @@ -580,11 +644,7 @@ def _uuid(identifier: str) -> str: dy = spacing / 4 * 0.75 x_offset = x_sign * (w - 1.27 - dy * 0.75) polygon = Polygon( - uuid_decoration, - Layer('sym_outlines'), - Width(line_width * 0.75), - Fill(False), - GrabArea(False) + uuid_decoration, Layer('sym_outlines'), Width(line_width * 0.75), Fill(False), GrabArea(False) ) polygon.add_vertex(Vertex(Position(x_offset, y - dy), Angle(x_sign * 135.0))) polygon.add_vertex(Vertex(Position(x_offset, y + dy), Angle(0.0))) @@ -597,21 +657,23 @@ def _uuid(identifier: str) -> str: diam = 1.6 x_offset = w - (diam / 2) - pin_length_inside pos = Position(x_offset, y) - symbol.add_circle(Circle( - uuid_decoration, - Layer('sym_outlines'), - Width(line_width * 0.75), - Fill(False), - GrabArea(False), - Diameter(diam), - pos, - )) + symbol.add_circle( + Circle( + uuid_decoration, + Layer('sym_outlines'), + Width(line_width * 0.75), + Fill(False), + GrabArea(False), + Diameter(diam), + pos, + ) + ) line_dx = (diam / 2) * math.cos(math.pi / 4 - math.pi / 16) line_dy = (diam / 2) * math.sin(math.pi / 4 - math.pi / 16) line1 = Polygon( uuid_decoration_2, Layer('sym_outlines'), - Width(line_width * .5), + Width(line_width * 0.5), Fill(False), GrabArea(False), ) @@ -621,7 +683,7 @@ def _uuid(identifier: str) -> str: line2 = Polygon( uuid_decoration_3, Layer('sym_outlines'), - Width(line_width * .5), + Width(line_width * 0.5), Fill(False), GrabArea(False), ) @@ -631,10 +693,26 @@ def _uuid(identifier: str) -> str: # Text y_max, y_min = get_rectangle_bounds(i, rows, spacing, spacing, True) - text = Text(uuid_text_name, Layer('sym_names'), Value('{{NAME}}'), Align('center bottom'), Height(sym_text_height), Position(0.0, y_max), Rotation(0.0)) + text = Text( + uuid_text_name, + Layer('sym_names'), + Value('{{NAME}}'), + Align('center bottom'), + Height(sym_text_height), + Position(0.0, y_max), + Rotation(0.0), + ) symbol.add_text(text) - text = Text(uuid_text_value, Layer('sym_values'), Value('{{VALUE}}'), Align('center top'), Height(sym_text_height), Position(0.0, y_min), Rotation(0.0)) + text = Text( + uuid_text_value, + Layer('sym_values'), + Value('{{VALUE}}'), + Align('center top'), + Height(sym_text_height), + Position(0.0, y_min), + Rotation(0.0), + ) symbol.add_text(text) symbol.serialize(path.join('out', library, category)) @@ -676,8 +754,7 @@ def _uuid(identifier: str) -> str: component = Component( uuid_cmp, Name('{} {}x{:02d}'.format(name, rows, per_row)), - Description('A {}x{} {}.\n\n' - 'Generated with {}'.format(rows, per_row, name_lower, generator)), + Description('A {}x{} {}.\n\n' 'Generated with {}'.format(rows, per_row, name_lower, generator)), Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)), Author(author), Version(version), @@ -691,15 +768,17 @@ def _uuid(identifier: str) -> str: ) for p in range(1, i + 1): - component.add_signal(Signal( - uuid_signals[p - 1], - Name(str(p)), - Role.PASSIVE, - Required(False), - Negated(False), - Clock(False), - ForcedNet(''), - )) + component.add_signal( + Signal( + uuid_signals[p - 1], + Name(str(p)), + Role.PASSIVE, + Required(False), + Negated(False), + Clock(False), + ForcedNet(''), + ) + ) gate = Gate( uuid_gate, @@ -710,18 +789,20 @@ def _uuid(identifier: str) -> str: Suffix(''), ) for p in range(1, i + 1): - gate.add_pin_signal_map(PinSignalMap( - uuid_pins[p - 1], - SignalUUID(uuid_signals[p - 1]), - TextDesignator.SYMBOL_PIN_NAME, - )) + gate.add_pin_signal_map( + PinSignalMap( + uuid_pins[p - 1], + SignalUUID(uuid_signals[p - 1]), + TextDesignator.SYMBOL_PIN_NAME, + ) + ) component.add_variant(Variant(uuid_variant, Norm.EMPTY, Name('default'), Description(''), gate)) # Message approvals if len(default_value) == 0: # Approve the "no default value set" message. - component.add_approval("(approved empty_default_value)") + component.add_approval('(approved empty_default_value)') component.serialize(path.join('out', library, category)) print('{}x{} {}: Wrote component {}'.format(rows, per_row, kind, uuid_cmp)) @@ -763,9 +844,11 @@ def _uuid(identifier: str) -> str: # General info lines.append('(librepcb_device {}'.format(uuid_dev)) lines.append(' (name "{} {}x{:02d} ⌀{:.1f}mm")'.format(name, rows, per_row, drill)) - lines.append(' (description "A {}x{} {} with {}mm pin spacing ' - 'and {:.1f}mm drill holes.\\n\\n' - 'Generated with {}")'.format(rows, per_row, name_lower, spacing, drill, generator)) + lines.append( + ' (description "A {}x{} {} with {}mm pin spacing ' + 'and {:.1f}mm drill holes.\\n\\n' + 'Generated with {}")'.format(rows, per_row, name_lower, spacing, drill, generator) + ) lines.append(' (keywords "connector, {}x{}, d{:.1f}, {}")'.format(rows, per_row, drill, keywords)) lines.append(' (author "{}")'.format(author)) lines.append(' (version "0.1.1")') @@ -779,7 +862,7 @@ def _uuid(identifier: str) -> str: for p in range(1, i + 1): signalmappings.append(' (pad {} (signal {}))'.format(uuid_pads[p - 1], uuid_signals[p - 1])) lines.extend(sorted(signalmappings)) - lines.append(" (approved no_parts)") + lines.append(' (approved no_parts)') lines.append(')') dev_dir_path = path.join('out', library, category, uuid_dev) diff --git a/generate_dfn.py b/generate_dfn.py index 0e3ec0c..bd439be 100644 --- a/generate_dfn.py +++ b/generate_dfn.py @@ -2,6 +2,7 @@ Generate DFN packages """ + from os import makedirs, path from uuid import uuid4 @@ -17,9 +18,9 @@ SILKSCREEN_OFFSET = 0.15 SILKSCREEN_LINE_WIDTH = 0.254 LABEL_OFFSET = 1.0 -TEXT_ATTRS = "(height 1.0) (stroke_width 0.2) (letter_spacing auto) (line_spacing auto)" +TEXT_ATTRS = '(height 1.0) (stroke_width 0.2) (letter_spacing auto) (line_spacing auto)' -MIN_CLEARANCE = 0.20 # For checking only --> warns if violated +MIN_CLEARANCE = 0.20 # For checking only --> warns if violated MIN_TRACE = 0.10 @@ -77,40 +78,43 @@ def generate_pkg( category = 'pkg' lines = [] - full_name = name.format(length=fd(config.length), - width=fd(config.width), - height=fd(config.height_nominal), - pin_count=config.pin_count, - pitch=fd(config.pitch)) + full_name = name.format( + length=fd(config.length), + width=fd(config.width), + height=fd(config.height_nominal), + pin_count=config.pin_count, + pitch=fd(config.pitch), + ) # Add pad length for otherwise identical names/packages if config.print_pad: - full_name += "P{:s}".format(fd(config.lead_length)) + full_name += 'P{:s}'.format(fd(config.lead_length)) if make_exposed: # According to: http://www.ocipcdc.org/archive/What_is_New_in_IPC-7351C_03_11_2015.pdf exp_width = fd(config.exposed_width) exp_length = fd(config.exposed_length) if exp_width == exp_length: - full_name += "T{}".format(exp_width) + full_name += 'T{}'.format(exp_width) else: - full_name += "T{}X{}".format(exp_width, exp_length) + full_name += 'T{}X{}'.format(exp_width, exp_length) # Override name if specified if config.name: full_name = config.name - full_description = description.format(height=config.height_nominal, - pin_count=config.pin_count, - pitch=config.pitch, - width=config.width, - length=config.length) + full_description = description.format( + height=config.height_nominal, + pin_count=config.pin_count, + pitch=config.pitch, + width=config.width, + length=config.length, + ) if make_exposed: - full_description += "\\nExposed Pad: {:.2f} x {:.2f} mm".format( - config.exposed_width, config.exposed_length) + full_description += '\\nExposed Pad: {:.2f} x {:.2f} mm'.format(config.exposed_width, config.exposed_length) if config.print_pad: - full_description += "\\nPad length: {:.2f} mm".format(config.lead_length) + full_description += '\\nPad length: {:.2f} mm'.format(config.lead_length) def _uuid(identifier: str) -> str: return uuid(category, full_name, identifier) @@ -126,8 +130,7 @@ def _uuid(identifier: str) -> str: # General info lines.append('(librepcb_package {}'.format(uuid_pkg)) lines.append(' (name "{}")'.format(full_name)) - lines.append(' (description "{}\\n\\nGenerated with {}")'.format(full_description, - GENERATOR_NAME)) + lines.append(' (description "{}\\n\\nGenerated with {}")'.format(full_description, GENERATOR_NAME)) if config.keywords: lines.append(' (keywords "dfn{},{},{}")'.format(config.pin_count, keywords, config.keywords.lower())) else: @@ -160,14 +163,14 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: if make_exposed: clearance = (config.width / 2) - config.lead_length - (exposed_length / 2) if clearance < MIN_CLEARANCE: - print("Increasing clearance from {:.2f} to {:.2f}".format(clearance, MIN_CLEARANCE)) + print('Increasing clearance from {:.2f} to {:.2f}'.format(clearance, MIN_CLEARANCE)) d_clearance = (MIN_CLEARANCE - clearance) / 2 pad_length = pad_length - d_clearance exposed_length = exposed_length - 2 * d_clearance abs_pad_pos_x = abs_pad_pos_x + (d_clearance / 2) if exposed_length < MIN_TRACE: - print("Increasing exposed path width from {:.2f} to {:.2f}".format(exposed_length, MIN_TRACE)) + print('Increasing exposed path width from {:.2f} to {:.2f}'.format(exposed_length, MIN_TRACE)) d_exp = MIN_TRACE - exposed_length exposed_length = exposed_length + d_exp pad_length = pad_length - (d_exp / 2) @@ -179,35 +182,47 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: pad_pos_y = get_y(pad_idx % half_n_pads + 1, half_n_pads, config.pitch, False) if pad_idx < (config.pin_count / 2): - pad_pos_x = - abs_pad_pos_x + pad_pos_x = -abs_pad_pos_x else: pad_pos_x = abs_pad_pos_x - pad_pos_y = - pad_pos_y + pad_pos_y = -pad_pos_y lines.append(' (pad {} (side top) (shape rect)'.format(uuid_pads[pad_idx])) - lines.append(' (position {} {}) (rotation 0.0) (size {} {}) (drill 0.0)'.format( - ff(pad_pos_x), ff(pad_pos_y), - ff(pad_length), ff(config.lead_width))) + lines.append( + ' (position {} {}) (rotation 0.0) (size {} {}) (drill 0.0)'.format( + ff(pad_pos_x), ff(pad_pos_y), ff(pad_length), ff(config.lead_width) + ) + ) lines.append(' )') # Make exposed pad, if required # TODO: Handle pin1_corner_dx_dy in config once custom pad shapes are possible if make_exposed: lines.append(' (pad {} (side top) (shape rect)'.format(uuid_exp)) - lines.append(' (position 0.0 0.0) (rotation 0.0) (size {} {}) (drill 0.0)'.format( - ff(exposed_length), ff(config.exposed_width))) + lines.append( + ' (position 0.0 0.0) (rotation 0.0) (size {} {}) (drill 0.0)'.format( + ff(exposed_length), ff(config.exposed_width) + ) + ) lines.append(' )') # Measure clearance pad-exposed pad clearance = abs(pad_pos_x) - (pad_length / 2) - (exposed_length / 2) if round(clearance, ndigits=2) < MIN_CLEARANCE: - print("Warning: minimal clearance violated in {}: {:.4f} < {:.2f}".format(full_name, clearance, MIN_CLEARANCE)) + print( + 'Warning: minimal clearance violated in {}: {:.4f} < {:.2f}'.format( + full_name, clearance, MIN_CLEARANCE + ) + ) # Create Silk Screen (lines and dot only) - silk_down = (config.length / 2 - SILKSCREEN_OFFSET - - get_y(1, half_n_pads, config.pitch, False) - - config.lead_width / 2 - - SILKSCREEN_LINE_WIDTH / 2) # required for round ending of line + silk_down = ( + config.length / 2 + - SILKSCREEN_OFFSET + - get_y(1, half_n_pads, config.pitch, False) + - config.lead_width / 2 + - SILKSCREEN_LINE_WIDTH / 2 + ) # required for round ending of line # Measure clearance silkscreen to exposed pad silk_top_line_height = config.length / 2 @@ -216,29 +231,36 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: if round(silk_clearance, ndigits=2) < SILKSCREEN_OFFSET: silk_top_line_height = silk_top_line_height + (SILKSCREEN_OFFSET - silk_clearance) silk_down = silk_down + (SILKSCREEN_OFFSET - silk_clearance) - print("Increasing exp-silk clearance from {:.4f} to {:.2f}".format(silk_clearance, SILKSCREEN_OFFSET)) + print('Increasing exp-silk clearance from {:.4f} to {:.2f}'.format(silk_clearance, SILKSCREEN_OFFSET)) for idx, silkscreen_pos in enumerate([-1, 1]): uuid_silkscreen_poly = _uuid('polygon-silkscreen-{}-{}'.format(key, idx)) lines.append(' (polygon {} (layer top_placement)'.format(uuid_silkscreen_poly)) - lines.append(' (width {}) (fill false) (grab_area false)'.format( - SILKSCREEN_LINE_WIDTH)) - lines.append(' (vertex (position {} {}) (angle 0.0))'.format( - ff(-config.width / 2), - ff(silkscreen_pos * (silk_top_line_height - silk_down)))) + lines.append(' (width {}) (fill false) (grab_area false)'.format(SILKSCREEN_LINE_WIDTH)) + lines.append( + ' (vertex (position {} {}) (angle 0.0))'.format( + ff(-config.width / 2), ff(silkscreen_pos * (silk_top_line_height - silk_down)) + ) + ) # If this is negative, the silkscreen line has to be moved away from # the real position, in order to keep the required distance to the # pad. We then only draw a single line, so we can omit the parts below. if silk_down > 0: - lines.append(' (vertex (position {} {}) (angle 0.0))'.format( - ff(-config.width / 2), - ff(silkscreen_pos * silk_top_line_height))) - lines.append(' (vertex (position {} {}) (angle 0.0))'.format( - ff(config.width / 2), - ff(silkscreen_pos * silk_top_line_height))) - lines.append(' (vertex (position {} {}) (angle 0.0))'.format( - ff(config.width / 2), - ff(silkscreen_pos * (silk_top_line_height - silk_down)))) + lines.append( + ' (vertex (position {} {}) (angle 0.0))'.format( + ff(-config.width / 2), ff(silkscreen_pos * silk_top_line_height) + ) + ) + lines.append( + ' (vertex (position {} {}) (angle 0.0))'.format( + ff(config.width / 2), ff(silkscreen_pos * silk_top_line_height) + ) + ) + lines.append( + ' (vertex (position {} {}) (angle 0.0))'.format( + ff(config.width / 2), ff(silkscreen_pos * (silk_top_line_height - silk_down)) + ) + ) lines.append(' )') @@ -251,14 +273,14 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: half_n_pads = config.pin_count // 2 pad_pos_y = get_y(pad_idx % half_n_pads + 1, half_n_pads, config.pitch, False) if pad_idx >= (config.pin_count / 2): - pad_pos_y = - pad_pos_y + pad_pos_y = -pad_pos_y y_min = pad_pos_y - config.lead_width / 2 y_max = pad_pos_y + config.lead_width / 2 x_max = config.width / 2 x_min = x_max - config.lead_length if pad_idx < (config.pin_count / 2): - x_min, x_max = - x_min, - x_max + x_min, x_max = -x_min, -x_max # Convert numbers to librepcb format x_min_str, x_max_str = ff(x_min), ff(x_max) @@ -277,8 +299,8 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: if make_exposed: uuid_docu_exposed = _uuid('lead-exposed') - x_min, x_max = - config.exposed_length / 2, config.exposed_length / 2 - y_min, y_max = - config.exposed_width / 2, config.exposed_width / 2 + x_min, x_max = -config.exposed_length / 2, config.exposed_length / 2 + y_min, y_max = -config.exposed_width / 2, config.exposed_width / 2 lines.append(' (polygon {} (layer top_documentation)'.format(uuid_docu_exposed)) lines.append(' (width 0.0) (fill true) (grab_area false)') @@ -328,12 +350,11 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: uuid_silkscreen_circ = _uuid('circle-silkscreen-{}'.format(key)) lines.append(' (circle {} (layer top_placement)'.format(uuid_silkscreen_circ)) - lines.append(' (width 0.0) (fill true) (grab_area false) ' - '(diameter {}) (position {} {})'.format( - ff(silkscreen_circ_dia), - ff(silk_circ_x), - ff(silk_circ_y) - )) + lines.append( + ' (width 0.0) (fill true) (grab_area false) ' '(diameter {}) (position {} {})'.format( + ff(silkscreen_circ_dia), ff(silk_circ_x), ff(silk_circ_y) + ) + ) lines.append(' )') # Add name and value labels @@ -342,14 +363,14 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: lines.append(' (stroke_text {} (layer top_names)'.format(uuid_text_name)) lines.append(' {}'.format(TEXT_ATTRS)) - lines.append(' (align center bottom) (position 0.0 {}) (rotation 0.0)'.format( - config.length / 2 + LABEL_OFFSET)) + lines.append( + ' (align center bottom) (position 0.0 {}) (rotation 0.0)'.format(config.length / 2 + LABEL_OFFSET) + ) lines.append(' (auto_rotate true) (mirror false) (value "{{NAME}}")') lines.append(' )') lines.append(' (stroke_text {} (layer top_values)'.format(uuid_text_value)) lines.append(' {}'.format(TEXT_ATTRS)) - lines.append(' (align center top) (position 0.0 {}) (rotation 0.0)'.format( - -config.length / 2 - LABEL_OFFSET)) + lines.append(' (align center top) (position 0.0 {}) (rotation 0.0)'.format(-config.length / 2 - LABEL_OFFSET)) lines.append(' (auto_rotate true) (mirror false) (value "{{VALUE}}")') lines.append(' )') @@ -394,11 +415,11 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: author='Hannes Badertscher', name='DFN{pitch}P{length}X{width}X{height}-{pin_count}', description='{pin_count}-pin Dual Flat No-Lead package (DFN), ' - 'standardized by JEDEC MO-229F.\\n\\n' - 'Pitch: {pitch:.2f} mm\\n' - 'Nominal width: {width:.2f} mm\\n' - 'Nominal length: {length:.2f} mm\\n' - 'Height: {height:.2f}mm', + 'standardized by JEDEC MO-229F.\\n\\n' + 'Pitch: {pitch:.2f} mm\\n' + 'Nominal width: {width:.2f} mm\\n' + 'Nominal length: {length:.2f} mm\\n' + 'Height: {height:.2f}mm', pkgcat='88cbb15c-2b69-4612-8764-c5d323f88f13', keywords='dfn,dual flat no-leads,mo-229f', config=config, @@ -408,7 +429,7 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: if name not in generated_packages: generated_packages.append(name) else: - print("Duplicate name found: {}".format(name)) + print('Duplicate name found: {}'.format(name)) for config in THIRD_CONFIGS: # Find out which configs to create @@ -425,10 +446,10 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: author='Hannes Badertscher', name='DFN{pitch}P{length}X{width}X{height}-{pin_count}', description='{pin_count}-pin Dual Flat No-Lead package (DFN), ' - 'Pitch: {pitch:.2f} mm\\n' - 'Nominal width: {width:.2f} mm\\n' - 'Nominal length: {length:.2f} mm\\n' - 'Height: {height:.2f}mm', + 'Pitch: {pitch:.2f} mm\\n' + 'Nominal width: {width:.2f} mm\\n' + 'Nominal length: {length:.2f} mm\\n' + 'Height: {height:.2f}mm', pkgcat='88cbb15c-2b69-4612-8764-c5d323f88f13', keywords='dfn,dual flat no-leads', config=config, @@ -438,6 +459,6 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: if name not in generated_packages: generated_packages.append(name) else: - print("Duplicate name found: {}".format(name)) + print('Duplicate name found: {}'.format(name)) save_cache(uuid_cache_file, uuid_cache) diff --git a/generate_dip.py b/generate_dip.py index e510cae..201e26c 100644 --- a/generate_dip.py +++ b/generate_dip.py @@ -157,6 +157,7 @@ +----+-------------+-----------+------------+------------------+ """ + from os import path from uuid import uuid4 @@ -165,13 +166,55 @@ from common import format_ipc_dimension as ipc from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, DrillDiameter, Footprint, FootprintPad, LetterSpacing, - LineSpacing, Mirror, Package, PackagePad, PackagePadUuid, PadFunction, PadHole, Shape, ShapeRadius, Size, - SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_dip.py)' @@ -277,25 +320,25 @@ def _uuid(identifier: str) -> str: L = ipc(config.body_length) H = ipc(config.height) Q = pin_count - ipc_name = "DIP{}W{}P{:.0f}L{}H{}Q{}".format(DIP, W, P, L, H, Q) + ipc_name = 'DIP{}W{}P{:.0f}L{}H{}Q{}'.format(DIP, W, P, L, H, Q) # Description - description = "{}-lead DIP (Dual In-Line) package".format(pin_count) + description = '{}-lead DIP (Dual In-Line) package'.format(pin_count) if config.standard: - description += " ({})".format(config.standard) - description += "\n\n" - description += "Pitch: {:.2f}mm\n".format(pitch) - description += "Lead span: {:.2f}mm\n".format(config.lead_span) - description += "Body length: {:.2f}mm\n".format(config.body_length) - description += "Lead width: {:.2f}mm\n".format(lead_width) - description += "Max height: {:.2f}mm\n".format(config.height) - description += "\nGenerated with {}".format(generator) + description += ' ({})'.format(config.standard) + description += '\n\n' + description += 'Pitch: {:.2f}mm\n'.format(pitch) + description += 'Lead span: {:.2f}mm\n'.format(config.lead_span) + description += 'Body length: {:.2f}mm\n'.format(config.body_length) + description += 'Lead width: {:.2f}mm\n'.format(lead_width) + description += 'Max height: {:.2f}mm\n'.format(config.height) + description += '\nGenerated with {}'.format(generator) package = Package( uuid=uuid_pkg, name=Name(ipc_name), description=Description(description), - keywords=Keywords("dip{},pdip{},{}".format(pin_count, pin_count, keywords)), + keywords=Keywords('dip{},pdip{},{}'.format(pin_count, pin_count, keywords)), author=Author(author), version=Version(version), created=Created(create_date or now()), @@ -333,41 +376,49 @@ def add_footprint_variant(key: str, name: str, pad_size: Tuple[float, float]) -> for p in range(1, pin_count // 2 + 1): # Down on the left y = get_y(p, pin_count // 2, pitch, False) - footprint.add_pad(FootprintPad( - uuid_pads[p - 1], - ComponentSide.TOP, - Shape.ROUNDED_RECT, - Position(-pad_x_offset, y), - Rotation(0.0), - Size(pad_size[0], pad_size[1]), - ShapeRadius(0 if p == 1 else 1), - StopMaskConfig.AUTO, - SolderPasteConfig.OFF, - CopperClearance(0), - PadFunction.STANDARD_PAD, - PackagePadUuid(uuid_pads[p - 1]), - [PadHole(uuid_pads[p - 1], DrillDiameter(drill_diameter), - [Vertex(Position(0, 0), Angle(0))])], - )) + footprint.add_pad( + FootprintPad( + uuid_pads[p - 1], + ComponentSide.TOP, + Shape.ROUNDED_RECT, + Position(-pad_x_offset, y), + Rotation(0.0), + Size(pad_size[0], pad_size[1]), + ShapeRadius(0 if p == 1 else 1), + StopMaskConfig.AUTO, + SolderPasteConfig.OFF, + CopperClearance(0), + PadFunction.STANDARD_PAD, + PackagePadUuid(uuid_pads[p - 1]), + [PadHole(uuid_pads[p - 1], DrillDiameter(drill_diameter), [Vertex(Position(0, 0), Angle(0))])], + ) + ) for p in range(1, pin_count // 2 + 1): # Up on the right y = -get_y(p, pin_count // 2, pitch, False) - footprint.add_pad(FootprintPad( - uuid_pads[p + pin_count // 2 - 1], - ComponentSide.TOP, - Shape.ROUNDED_RECT, - Position(pad_x_offset, y), - Rotation(0.0), - Size(pad_size[0], pad_size[1]), - ShapeRadius(1), - StopMaskConfig.AUTO, - SolderPasteConfig.OFF, - CopperClearance(0), - PadFunction.STANDARD_PAD, - PackagePadUuid(uuid_pads[p + pin_count // 2 - 1]), - [PadHole(uuid_pads[p + pin_count // 2 - 1], DrillDiameter(drill_diameter), - [Vertex(Position(0, 0), Angle(0))])], - )) + footprint.add_pad( + FootprintPad( + uuid_pads[p + pin_count // 2 - 1], + ComponentSide.TOP, + Shape.ROUNDED_RECT, + Position(pad_x_offset, y), + Rotation(0.0), + Size(pad_size[0], pad_size[1]), + ShapeRadius(1), + StopMaskConfig.AUTO, + SolderPasteConfig.OFF, + CopperClearance(0), + PadFunction.STANDARD_PAD, + PackagePadUuid(uuid_pads[p + pin_count // 2 - 1]), + [ + PadHole( + uuid_pads[p + pin_count // 2 - 1], + DrillDiameter(drill_diameter), + [Vertex(Position(0, 0), Angle(0))], + ) + ], + ) + ) # Silkscreen silkscreen_top = Polygon( @@ -388,23 +439,20 @@ def add_footprint_variant(key: str, name: str, pad_size: Tuple[float, float]) -> dx = body_width / 2 + line_width / 2 dx_pin1 = config.lead_span / 2 + pad_size[0] / 2 - line_width / 2 notch_dx = dx / 4 - dy1 = get_y(1, pin_count // 2, pitch, False) \ - + pad_size[1] / 2 \ - + line_width / 2 \ - + silkscreen_offset + dy1 = get_y(1, pin_count // 2, pitch, False) + pad_size[1] / 2 + line_width / 2 + silkscreen_offset dy2 = config.body_length / 2 + line_width / 2 silkscreen_top.add_vertex(Vertex(Position(-dx_pin1, dy1), Angle(0.0))) silkscreen_top.add_vertex(Vertex(Position(-dx, dy1), Angle(0.0))) silkscreen_top.add_vertex(Vertex(Position(-dx, dy2), Angle(0.0))) silkscreen_top.add_vertex(Vertex(Position(-notch_dx, dy2), Angle(180.0))) - silkscreen_top.add_vertex(Vertex(Position( notch_dx, dy2), Angle(0.0))) - silkscreen_top.add_vertex(Vertex(Position( dx, dy2), Angle(0.0))) - silkscreen_top.add_vertex(Vertex(Position( dx, dy1), Angle(0.0))) + silkscreen_top.add_vertex(Vertex(Position(notch_dx, dy2), Angle(0.0))) + silkscreen_top.add_vertex(Vertex(Position(dx, dy2), Angle(0.0))) + silkscreen_top.add_vertex(Vertex(Position(dx, dy1), Angle(0.0))) footprint.add_polygon(silkscreen_top) silkscreen_bot.add_vertex(Vertex(Position(-dx, -dy1), Angle(0.0))) silkscreen_bot.add_vertex(Vertex(Position(-dx, -dy2), Angle(0.0))) - silkscreen_bot.add_vertex(Vertex(Position( dx, -dy2), Angle(0.0))) - silkscreen_bot.add_vertex(Vertex(Position( dx, -dy1), Angle(0.0))) + silkscreen_bot.add_vertex(Vertex(Position(dx, -dy2), Angle(0.0))) + silkscreen_bot.add_vertex(Vertex(Position(dx, -dy1), Angle(0.0))) footprint.add_polygon(silkscreen_bot) # Documentation @@ -452,18 +500,18 @@ def add_footprint_variant(key: str, name: str, pad_size: Tuple[float, float]) -> dx_outer = config.lead_span / 2 + outline_hole_offset dy_inner = get_y(1, pin_count // 2, pitch, False) + pad_size[1] / 2 dy_outer = config.body_length / 2 - outline.add_vertex(Vertex(Position(-dx_inner, dy_outer), Angle(0.0))) # Top left - outline.add_vertex(Vertex(Position( dx_inner, dy_outer), Angle(0.0))) # CW - outline.add_vertex(Vertex(Position( dx_inner, dy_inner), Angle(0.0))) - outline.add_vertex(Vertex(Position( dx_outer, dy_inner), Angle(0.0))) - outline.add_vertex(Vertex(Position( dx_outer, -dy_inner), Angle(0.0))) - outline.add_vertex(Vertex(Position( dx_inner, -dy_inner), Angle(0.0))) - outline.add_vertex(Vertex(Position( dx_inner, -dy_outer), Angle(0.0))) + outline.add_vertex(Vertex(Position(-dx_inner, dy_outer), Angle(0.0))) # Top left + outline.add_vertex(Vertex(Position(dx_inner, dy_outer), Angle(0.0))) # CW + outline.add_vertex(Vertex(Position(dx_inner, dy_inner), Angle(0.0))) + outline.add_vertex(Vertex(Position(dx_outer, dy_inner), Angle(0.0))) + outline.add_vertex(Vertex(Position(dx_outer, -dy_inner), Angle(0.0))) + outline.add_vertex(Vertex(Position(dx_inner, -dy_inner), Angle(0.0))) + outline.add_vertex(Vertex(Position(dx_inner, -dy_outer), Angle(0.0))) outline.add_vertex(Vertex(Position(-dx_inner, -dy_outer), Angle(0.0))) outline.add_vertex(Vertex(Position(-dx_inner, -dy_inner), Angle(0.0))) outline.add_vertex(Vertex(Position(-dx_outer, -dy_inner), Angle(0.0))) - outline.add_vertex(Vertex(Position(-dx_outer, dy_inner), Angle(0.0))) - outline.add_vertex(Vertex(Position(-dx_inner, dy_inner), Angle(0.0))) + outline.add_vertex(Vertex(Position(-dx_outer, dy_inner), Angle(0.0))) + outline.add_vertex(Vertex(Position(-dx_inner, dy_inner), Angle(0.0))) footprint.add_polygon(outline) # Courtyard @@ -479,18 +527,18 @@ def add_footprint_variant(key: str, name: str, pad_size: Tuple[float, float]) -> dx_outer = pad_x_offset + pad_size[0] / 2 + offset dy_inner = get_y(1, pin_count // 2, pitch, False) + pad_size[1] / 2 + offset dy_outer = config.body_length / 2 + offset - courtyard.add_vertex(Vertex(Position(-dx_inner, dy_outer), Angle(0.0))) # Top left - courtyard.add_vertex(Vertex(Position( dx_inner, dy_outer), Angle(0.0))) # CW - courtyard.add_vertex(Vertex(Position( dx_inner, dy_inner), Angle(0.0))) - courtyard.add_vertex(Vertex(Position( dx_outer, dy_inner), Angle(0.0))) - courtyard.add_vertex(Vertex(Position( dx_outer, -dy_inner), Angle(0.0))) - courtyard.add_vertex(Vertex(Position( dx_inner, -dy_inner), Angle(0.0))) - courtyard.add_vertex(Vertex(Position( dx_inner, -dy_outer), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(-dx_inner, dy_outer), Angle(0.0))) # Top left + courtyard.add_vertex(Vertex(Position(dx_inner, dy_outer), Angle(0.0))) # CW + courtyard.add_vertex(Vertex(Position(dx_inner, dy_inner), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(dx_outer, dy_inner), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(dx_outer, -dy_inner), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(dx_inner, -dy_inner), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(dx_inner, -dy_outer), Angle(0.0))) courtyard.add_vertex(Vertex(Position(-dx_inner, -dy_outer), Angle(0.0))) courtyard.add_vertex(Vertex(Position(-dx_inner, -dy_inner), Angle(0.0))) courtyard.add_vertex(Vertex(Position(-dx_outer, -dy_inner), Angle(0.0))) - courtyard.add_vertex(Vertex(Position(-dx_outer, dy_inner), Angle(0.0))) - courtyard.add_vertex(Vertex(Position(-dx_inner, dy_inner), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(-dx_outer, dy_inner), Angle(0.0))) + courtyard.add_vertex(Vertex(Position(-dx_inner, dy_inner), Angle(0.0))) footprint.add_polygon(courtyard) # Labels @@ -504,28 +552,30 @@ def add_footprint_variant(key: str, name: str, pad_size: Tuple[float, float]) -> 'auto_rotate': AutoRotate(True), 'mirror': Mirror(False), } - footprint.add_text(StrokeText( - uuid_text_name, - Layer('top_names'), - align=Align('center bottom'), - position=Position(0.0, dy), - value=Value('{{NAME}}'), - **text_attrs, # type: ignore # (mypy cannot deal with kwargs) - )) - footprint.add_text(StrokeText( - uuid_text_value, - Layer('top_values'), - align=Align('center top'), - position=Position(0.0, -dy), - value=Value('{{VALUE}}'), - **text_attrs, # type: ignore # (mypy cannot deal with kwargs) - )) + footprint.add_text( + StrokeText( + uuid_text_name, + Layer('top_names'), + align=Align('center bottom'), + position=Position(0.0, dy), + value=Value('{{NAME}}'), + **text_attrs, # type: ignore # (mypy cannot deal with kwargs) + ) + ) + footprint.add_text( + StrokeText( + uuid_text_value, + Layer('top_values'), + align=Align('center top'), + position=Position(0.0, -dy), + value=Value('{{VALUE}}'), + **text_attrs, # type: ignore # (mypy cannot deal with kwargs) + ) + ) # Approvals package.add_approval( - "(approved missing_footprint_3d_model\n" + - " (footprint {})\n".format(uuid_footprint) + - ")" + '(approved missing_footprint_3d_model\n' + ' (footprint {})\n'.format(uuid_footprint) + ')' ) add_footprint_variant('handsoldering', 'hand soldering', (2.54, 1.27)) diff --git a/generate_do.py b/generate_do.py index d80f7c7..ec4144a 100644 --- a/generate_do.py +++ b/generate_do.py @@ -4,6 +4,7 @@ - JEDEC DO-214 https://www.jedec.org/system/files/docs/DO-214D.PDF """ + from os import path from uuid import uuid4 @@ -12,13 +13,51 @@ from common import format_ipc_dimension as fd from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Created, Deprecated, Description, Fill, GeneratedBy, GrabArea, Height, Keywords, - Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Created, + Deprecated, + Description, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, FootprintPad, LetterSpacing, LineSpacing, - Mirror, Package, PackagePad, PackagePadUuid, PadFunction, Shape, ShapeRadius, Size, SolderPasteConfig, - StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + Footprint, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + PackagePad, + PackagePadUuid, + PadFunction, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) GENERATOR_NAME = 'librepcb-parts-generator (generate_do.py)' @@ -51,7 +90,6 @@ def __init__( contact_length_max: float, contact_width_min: float, contact_width_max: float, - variant: str, common_name: str, ): @@ -148,21 +186,23 @@ def _add_pads( ) -> None: for pad, name, side in pads: pad_uuid = _uuid(f'pad-{pad}') - footprint.add_pad(FootprintPad( - uuid=pad_uuid, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(_pad_center(side), 0), - rotation=Rotation(0), - size=Size(_pad_length(), _pad_width()), - radius=ShapeRadius(0.0), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.UNSPECIFIED, - package_pad=PackagePadUuid(pad_uuid), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=pad_uuid, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(_pad_center(side), 0), + rotation=Rotation(0), + size=Size(_pad_length(), _pad_width()), + radius=ShapeRadius(0.0), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.UNSPECIFIED, + package_pad=PackagePadUuid(pad_uuid), + holes=[], + ) + ) lead = Polygon( uuid=_uuid(uuid_ns + 'pad-lead'), layer=Layer('top_documentation'), @@ -170,11 +210,13 @@ def _add_pads( fill=Fill(True), grab_area=GrabArea(False), ) - _rect(lead, - config.total_length / 2 * side, - config.total_length / 2 * side - config.contact_length * side, - -config.contact_width / 2, - config.contact_width / 2) + _rect( + lead, + config.total_length / 2 * side, + config.total_length / 2 * side - config.contact_length * side, + -config.contact_width / 2, + config.contact_width / 2, + ) footprint.add_polygon(lead) def _add_footprint( @@ -212,9 +254,13 @@ def _add_footprint( fill=Fill(False), grab_area=GrabArea(True), ) - _rect(outline, - left_edge + line_offset, right_edge - line_offset, - bottom_edge + line_offset, top_edge - line_offset) + _rect( + outline, + left_edge + line_offset, + right_edge - line_offset, + bottom_edge + line_offset, + top_edge - line_offset, + ) footprint.add_polygon(outline) if polarity: @@ -227,9 +273,7 @@ def _add_footprint( ) x0 = _pad_center(-1) + _pad_length() / 2 + 0.2 x1 = x0 + 0.3 - _rect(band, - x0, x1, - bottom_edge + line_width, top_edge - line_width) + _rect(band, x0, x1, bottom_edge + line_width, top_edge - line_width) footprint.add_polygon(band) # @@ -282,44 +326,50 @@ def _add_silkscreen( fill=Fill(False), grab_area=GrabArea(False), ) - _rect(courtyard, - _pad_center(-1) - (_pad_length() / 2 + 0.4 + line_offset), - _pad_center(1) + (_pad_length() / 2 + 0.4 + line_offset), - bottom_edge - line_width, - top_edge + line_width) + _rect( + courtyard, + _pad_center(-1) - (_pad_length() / 2 + 0.4 + line_offset), + _pad_center(1) + (_pad_length() / 2 + 0.4 + line_offset), + bottom_edge - line_width, + top_edge + line_width, + ) footprint.add_polygon(courtyard) # # Text # - footprint.add_text(StrokeText( - uuid=_uuid(uuid_ns + 'text-name'), - layer=Layer('top_names'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, top_edge + line_width + 0.5), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(False), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_uuid(uuid_ns + 'text-value'), - layer=Layer('top_values'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, bottom_edge - (line_width + 0.5)), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(False), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_uuid(uuid_ns + 'text-name'), + layer=Layer('top_names'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, top_edge + line_width + 0.5), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(False), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_uuid(uuid_ns + 'text-value'), + layer=Layer('top_values'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, bottom_edge - (line_width + 0.5)), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(False), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) _add_footprint(package, Name('default'), 'default-') @@ -333,25 +383,13 @@ def _add_silkscreen( # total_length_nom (E); total_height_nom (A) # contact_length_min, contact_length_nom, contact_length_max (L); contact_width_min, contact_width_max (b) # variant; common_name - configs.append(DoConfig(4.30, 3.60, 2.15, - 5.40, 2.30, - 0.75, 1.15, 1.60, 1.95, 2.20, - 'AA', 'SMB')) - - configs.append(DoConfig(6.85, 5.90, 2.15, - 7.95, 2.30, - 0.75, 1.15, 1.60, 2.90, 3.20, - 'AB', 'SMC')) - - configs.append(DoConfig(4.30, 2.60, 2.30, - 5.20, 2.40, - 0.75, 1.15, 1.60, 1.25, 1.65, - 'AC', 'SMA')) - - configs.append(DoConfig(4.45, 2.60, 2.80, - 5.25, 2.95, - 0.75, 1.15, 1.60, 1.00, 1.70, - 'BA', 'GF1')) + configs.append(DoConfig(4.30, 3.60, 2.15, 5.40, 2.30, 0.75, 1.15, 1.60, 1.95, 2.20, 'AA', 'SMB')) + + configs.append(DoConfig(6.85, 5.90, 2.15, 7.95, 2.30, 0.75, 1.15, 1.60, 2.90, 3.20, 'AB', 'SMC')) + + configs.append(DoConfig(4.30, 2.60, 2.30, 5.20, 2.40, 0.75, 1.15, 1.60, 1.25, 1.65, 'AC', 'SMA')) + + configs.append(DoConfig(4.45, 2.60, 2.80, 5.25, 2.95, 0.75, 1.15, 1.60, 1.00, 1.70, 'BA', 'GF1')) for config in configs: generate_pkg( diff --git a/generate_idc.py b/generate_idc.py index 3e7be05..2b555c7 100644 --- a/generate_idc.py +++ b/generate_idc.py @@ -8,6 +8,7 @@ - CNC Tech 3220-xx-0300-00 (1.27 mm pitch) """ + from math import sqrt from os import path from uuid import uuid4 @@ -16,15 +17,53 @@ from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Created, Deprecated, Description, Fill, GeneratedBy, GrabArea, Height, Keywords, - Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Created, + Deprecated, + Description, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.component import SignalUUID from entities.device import ComponentPad, ComponentUUID, Device, Manufacturer, PackageUUID, Part from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, FootprintPad, LetterSpacing, LineSpacing, - Mirror, Package, PackagePad, PackagePadUuid, PadFunction, Shape, ShapeRadius, Size, SolderPasteConfig, - StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + Footprint, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + PackagePad, + PackagePadUuid, + PadFunction, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_idc.py)' @@ -152,8 +191,7 @@ def __init__( self.dev_author = dev_author self.dev_version = dev_version self.dev_create_date = dev_create_date - self.description = description.format(pin_count=pin_count) + \ - "\n\nGenerated with {}".format(generator) + self.description = description.format(pin_count=pin_count) + '\n\nGenerated with {}'.format(generator) self.keywords = keywords self.pitch = pitch self.row_spacing = row_spacing @@ -219,21 +257,23 @@ def _uuid(identifier: str) -> str: x_offset_abs = config.pad_size[0] / 2 + config.pad_x_offset x_offset = -x_offset_abs if i % 2 == 1 else x_offset_abs uuid_pad = uuid_pads[i - 1] - footprint.add_pad(FootprintPad( - uuid=uuid_pad, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(coords.x + x_offset, coords.y), - rotation=Rotation(0), - size=Size(*config.pad_size), - radius=ShapeRadius(0.5), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_pad), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_pad, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(coords.x + x_offset, coords.y), + rotation=Rotation(0), + size=Size(*config.pad_size), + radius=ShapeRadius(0.5), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_pad), + holes=[], + ) + ) # Legs on documentation layer for i in range(1, config.pin_count + 1): @@ -241,20 +281,28 @@ def _uuid(identifier: str) -> str: x_offset_abs = config.pad_size[0] / 2 + config.pad_x_offset x_offset = -x_offset_abs if i % 2 == 1 else x_offset_abs sign = 1 if coords.x > 0 else -1 - footprint.add_polygon(Polygon( - uuid=uuid_leads[i - 1], - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(coords.x - config.lead_width / 2 * sign, coords.y + config.lead_width / 2), Angle(0)), - Vertex(Position(coords.x - config.lead_width / 2 * sign, coords.y - config.lead_width / 2), Angle(0)), - Vertex(Position(config.lead_span / 2 * sign, coords.y - config.lead_width / 2), Angle(0)), - Vertex(Position(config.lead_span / 2 * sign, coords.y + config.lead_width / 2), Angle(0)), - Vertex(Position(coords.x - config.lead_width / 2 * sign, coords.y + config.lead_width / 2), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_leads[i - 1], + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex( + Position(coords.x - config.lead_width / 2 * sign, coords.y + config.lead_width / 2), Angle(0) + ), + Vertex( + Position(coords.x - config.lead_width / 2 * sign, coords.y - config.lead_width / 2), Angle(0) + ), + Vertex(Position(config.lead_span / 2 * sign, coords.y - config.lead_width / 2), Angle(0)), + Vertex(Position(config.lead_span / 2 * sign, coords.y + config.lead_width / 2), Angle(0)), + Vertex( + Position(coords.x - config.lead_width / 2 * sign, coords.y + config.lead_width / 2), Angle(0) + ), + ], + ) + ) # Body bounds pin1 = get_coords(1, config.pin_count, 2, config.pitch, config.row_spacing) @@ -271,89 +319,107 @@ def _uuid(identifier: str) -> str: x_mark_pin1 = abs(pin1.x) + config.pad_size[0] + config.pad_x_offset - line_width / 2 y_above_pin1 = pin1.y + config.pad_size[1] / 2 + silkscreen_offset + line_width / 2 # North part contains extended line to mark pin 1 - footprint.add_polygon(Polygon( - uuid=uuid_legend_north, - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-x_mark_pin1, y_above_pin1), Angle(0)), - Vertex(Position(-x_outside_body, y_above_pin1), Angle(0)), - Vertex(Position(-x_outside_body, y_outside_body), Angle(0)), - Vertex(Position(x_outside_body, y_outside_body), Angle(0)), - Vertex(Position(x_outside_body, y_above_pin1), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_legend_north, + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-x_mark_pin1, y_above_pin1), Angle(0)), + Vertex(Position(-x_outside_body, y_above_pin1), Angle(0)), + Vertex(Position(-x_outside_body, y_outside_body), Angle(0)), + Vertex(Position(x_outside_body, y_outside_body), Angle(0)), + Vertex(Position(x_outside_body, y_above_pin1), Angle(0)), + ], + ) + ) # South part doesn't contain any pin markings - footprint.add_polygon(Polygon( - uuid=uuid_legend_south, - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x_outside_body, -y_above_pin1), Angle(0)), - Vertex(Position(x_outside_body, -y_outside_body), Angle(0)), - Vertex(Position(-x_outside_body, -y_outside_body), Angle(0)), - Vertex(Position(-x_outside_body, -y_above_pin1), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_legend_south, + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x_outside_body, -y_above_pin1), Angle(0)), + Vertex(Position(x_outside_body, -y_outside_body), Angle(0)), + Vertex(Position(-x_outside_body, -y_outside_body), Angle(0)), + Vertex(Position(-x_outside_body, -y_above_pin1), Angle(0)), + ], + ) + ) # Documentation layer - footprint.add_polygon(Polygon( - uuid=uuid_doc_contour, - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-x_inside_body, config.body_gap / 2), Angle(0)), - Vertex(Position(-x_inside_body, y_inside_body), Angle(0)), - Vertex(Position(x_inside_body, y_inside_body), Angle(0)), - Vertex(Position(x_inside_body, -y_inside_body), Angle(0)), - Vertex(Position(-x_inside_body, -y_inside_body), Angle(0)), - Vertex(Position(-x_inside_body, -config.body_gap / 2), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_doc_contour, + layer=Layer('top_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-x_inside_body, config.body_gap / 2), Angle(0)), + Vertex(Position(-x_inside_body, y_inside_body), Angle(0)), + Vertex(Position(x_inside_body, y_inside_body), Angle(0)), + Vertex(Position(x_inside_body, -y_inside_body), Angle(0)), + Vertex(Position(-x_inside_body, -y_inside_body), Angle(0)), + Vertex(Position(-x_inside_body, -config.body_gap / 2), Angle(0)), + ], + ) + ) # Triangle on doc layer triangle_size = 1.0 triangle_width = sqrt(3) / 2.0 * triangle_size * 0.8 triangle_offset = triangle_size / 2 # Offset from doc layer - footprint.add_polygon(Polygon( - uuid=uuid_doc_triangle, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-x_inside_body + triangle_offset, y_inside_body - triangle_offset), Angle(0)), - Vertex(Position(-x_inside_body + triangle_offset, y_inside_body - triangle_offset - triangle_size), Angle(0)), - Vertex(Position(-x_inside_body + triangle_offset + triangle_width, y_inside_body - triangle_offset - triangle_size / 2), Angle(0)), - Vertex(Position(-x_inside_body + triangle_offset, y_inside_body - triangle_offset), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_doc_triangle, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-x_inside_body + triangle_offset, y_inside_body - triangle_offset), Angle(0)), + Vertex( + Position(-x_inside_body + triangle_offset, y_inside_body - triangle_offset - triangle_size), + Angle(0), + ), + Vertex( + Position( + -x_inside_body + triangle_offset + triangle_width, + y_inside_body - triangle_offset - triangle_size / 2, + ), + Angle(0), + ), + Vertex(Position(-x_inside_body + triangle_offset, y_inside_body - triangle_offset), Angle(0)), + ], + ) + ) # Grab area - footprint.add_polygon(Polygon( - uuid=uuid_grab_area, - layer=Layer('top_hidden_grab_areas'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(True), - vertices=[ - Vertex(Position(-body_bounds[0], body_bounds[1]), Angle(0)), - Vertex(Position(body_bounds[0], body_bounds[1]), Angle(0)), - Vertex(Position(body_bounds[0], -body_bounds[1]), Angle(0)), - Vertex(Position(-body_bounds[0], -body_bounds[1]), Angle(0)), - Vertex(Position(-body_bounds[0], body_bounds[1]), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_grab_area, + layer=Layer('top_hidden_grab_areas'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(True), + vertices=[ + Vertex(Position(-body_bounds[0], body_bounds[1]), Angle(0)), + Vertex(Position(body_bounds[0], body_bounds[1]), Angle(0)), + Vertex(Position(body_bounds[0], -body_bounds[1]), Angle(0)), + Vertex(Position(-body_bounds[0], -body_bounds[1]), Angle(0)), + Vertex(Position(-body_bounds[0], body_bounds[1]), Angle(0)), + ], + ) + ) # Package outline and courtyard - def _create_outline(polygon_uuid: str, polygon_layer: str, - polygon_offset: float, around_pads: bool) -> Polygon: + def _create_outline(polygon_uuid: str, polygon_layer: str, polygon_offset: float, around_pads: bool) -> Polygon: x_outline = body_bounds[0] + polygon_offset if around_pads: x_outline_extended = abs(pin1.x) + config.pad_size[0] + config.pad_x_offset + polygon_offset @@ -382,46 +448,47 @@ def _create_outline(polygon_uuid: str, polygon_layer: str, Vertex(Position(-x_outline_extended, -y_outline_extended), Angle(0)), ], ) + footprint.add_polygon(_create_outline(uuid_outline, 'top_package_outlines', 0, False)) footprint.add_polygon(_create_outline(uuid_courtyard, 'top_courtyard', courtyard_offset, True)) # Labels body_y_max = (config.pin_count / 2 - 1) * config.pitch / 2 + config.body_offset_y - footprint.add_text(StrokeText( - uuid=uuid_text_name, - layer=Layer('top_names'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, body_y_max + 1), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=uuid_text_value, - layer=Layer('top_values'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -body_y_max - 1), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=uuid_text_name, + layer=Layer('top_names'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, body_y_max + 1), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=uuid_text_value, + layer=Layer('top_values'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -body_y_max - 1), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # Approvals - package.add_approval( - "(approved missing_footprint_3d_model\n" + - " (footprint {})\n".format(uuid_footprint) + - ")" - ) + package.add_approval('(approved missing_footprint_3d_model\n' + ' (footprint {})\n'.format(uuid_footprint) + ')') package.serialize(path.join('out', config.library, 'pkg')) print('Wrote package {}: {}'.format(uuid_pkg, config.pkg_name)) @@ -458,15 +525,15 @@ def _uuid_cmp(identifier: str) -> str: ) for i in range(1, config.pin_count + 1): - device.add_pad(ComponentPad( - pad_uuid=uuid_pads[i - 1], - signal=SignalUUID(uuid_signals[i - 1]), - )) + device.add_pad( + ComponentPad( + pad_uuid=uuid_pads[i - 1], + signal=SignalUUID(uuid_signals[i - 1]), + ) + ) for mpn in config.parts_mpn: - device.add_part(Part( - mpn=mpn, manufacturer=Manufacturer(config.parts_manufacturer or '') - )) + device.add_part(Part(mpn=mpn, manufacturer=Manufacturer(config.parts_manufacturer or ''))) # write files device.serialize(path.join('out', config.library, 'dev')) @@ -475,88 +542,98 @@ def _uuid_cmp(identifier: str) -> str: if __name__ == '__main__': # CNC Tech - configs = \ - [Config( - library='CNC_Tech.lplib', - identifier='cnctech-3220-{pin_count}-0300', - pkg_name='CNCTECH_3220-{pin_count:02}-0300-XX', - pkg_author='Danilo Bargen', - pkg_version='0.2', - pkg_create_date='2019-07-09T21:31:21Z', - pkg_categories=['92186130-e1a4-4a82-8ce9-88f4aa854195', 'e4d3a6bf-af32-48a2-b427-5e794bed949a'], - dev_name='CNC Tech 3220-{pin_count:02}-0300', - dev_author='U. Bruhin', - dev_version='0.2', - dev_create_date='2019-10-19T10:11:49Z', - description='{pin_count}-pin 1.27mm pitch SMD IDC box header by CNC Tech.', - keywords='cnc tech,idc,header,male,box header,smd,3220,1.27mm', - pitch=1.27, - row_spacing=1.27, - pad_size=(2.4, 0.76), - pad_x_offset=0.115, - body_offset_x=1.915, - body_offset_y=3.785, - body_gap=2.35, - lead_width=0.4, - lead_span=5.5, - pin_count=pc, - parts_manufacturer='CNC Tech', - parts_mpn=['3220-{pin_count:02}-0300-00', '3220-{pin_count:02}-0300-00-TR'], - ) for pc in [10, 14, 16, 20, 26, 30, 34, 40, 50, 60]] + \ - [Config( - library='CNC_Tech.lplib', - identifier='cnctech-3120-{pin_count}-0300', - pkg_name='CNCTECH_3120-{pin_count:02}-0300-XX', - pkg_author='Danilo Bargen', - pkg_version='0.2', - pkg_create_date='2019-07-09T21:31:21Z', - pkg_categories=['92186130-e1a4-4a82-8ce9-88f4aa854195', 'e4d3a6bf-af32-48a2-b427-5e794bed949a'], - dev_name='CNC Tech 3120-{pin_count:02}-0300', - dev_author='U. Bruhin', - dev_version='0.2', - dev_create_date='2023-08-29T17:06:05Z', - description='{pin_count}-pin 2.00mm pitch SMD IDC box header by CNC Tech.', - keywords='cnc tech,idc,header,male,box header,smd,3120,2.00mm', - pitch=2.0, - row_spacing=2.0, - pad_size=(3.45, 0.9), - pad_x_offset=-0.2, - body_offset_x=1.75, - body_offset_y=4.65, - body_gap=3.7, - lead_width=0.5, - lead_span=7.5, - pin_count=pc, - parts_manufacturer='CNC Tech', - parts_mpn=['3120-{pin_count:02}-0300-00'], # No '-TR' variant(?) - ) for pc in [6, 8, 10, 12, 14, 16, 18, 20, 24, 26, 30, 34, 40, 44, 50, 60, 64]] + \ - [Config( - library='CNC_Tech.lplib', - identifier='cnctech-3020-{pin_count}-0300', - pkg_name='CNCTECH_3020-{pin_count:02}-0300-XX', - pkg_author='Danilo Bargen', - pkg_version='0.2', - pkg_create_date='2019-07-09T21:31:21Z', - pkg_categories=['92186130-e1a4-4a82-8ce9-88f4aa854195', 'e4d3a6bf-af32-48a2-b427-5e794bed949a'], - dev_name='CNC Tech 3020-{pin_count:02}-0300', - dev_author='U. Bruhin', - dev_version='0.2', - dev_create_date='2023-08-29T17:06:05Z', - description='{pin_count}-pin 2.54mm pitch SMD IDC box header by CNC Tech.', - keywords='cnc tech,idc,header,male,box header,smd,3020,2.54mm', - pitch=2.54, - row_spacing=2.54, - pad_size=(4.8, 0.9), - pad_x_offset=-0.42, - body_offset_x=3.13, - body_offset_y=5.08, - body_gap=5.08, - lead_width=0.64, - lead_span=10.2, - pin_count=pc, - parts_manufacturer='CNC Tech', - parts_mpn=['3020-{pin_count:02}-0300-00', '3020-{pin_count:02}-0300-00-TR'], - ) for pc in [6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 50, 60, 64]] + configs = ( + [ + Config( + library='CNC_Tech.lplib', + identifier='cnctech-3220-{pin_count}-0300', + pkg_name='CNCTECH_3220-{pin_count:02}-0300-XX', + pkg_author='Danilo Bargen', + pkg_version='0.2', + pkg_create_date='2019-07-09T21:31:21Z', + pkg_categories=['92186130-e1a4-4a82-8ce9-88f4aa854195', 'e4d3a6bf-af32-48a2-b427-5e794bed949a'], + dev_name='CNC Tech 3220-{pin_count:02}-0300', + dev_author='U. Bruhin', + dev_version='0.2', + dev_create_date='2019-10-19T10:11:49Z', + description='{pin_count}-pin 1.27mm pitch SMD IDC box header by CNC Tech.', + keywords='cnc tech,idc,header,male,box header,smd,3220,1.27mm', + pitch=1.27, + row_spacing=1.27, + pad_size=(2.4, 0.76), + pad_x_offset=0.115, + body_offset_x=1.915, + body_offset_y=3.785, + body_gap=2.35, + lead_width=0.4, + lead_span=5.5, + pin_count=pc, + parts_manufacturer='CNC Tech', + parts_mpn=['3220-{pin_count:02}-0300-00', '3220-{pin_count:02}-0300-00-TR'], + ) + for pc in [10, 14, 16, 20, 26, 30, 34, 40, 50, 60] + ] + + [ + Config( + library='CNC_Tech.lplib', + identifier='cnctech-3120-{pin_count}-0300', + pkg_name='CNCTECH_3120-{pin_count:02}-0300-XX', + pkg_author='Danilo Bargen', + pkg_version='0.2', + pkg_create_date='2019-07-09T21:31:21Z', + pkg_categories=['92186130-e1a4-4a82-8ce9-88f4aa854195', 'e4d3a6bf-af32-48a2-b427-5e794bed949a'], + dev_name='CNC Tech 3120-{pin_count:02}-0300', + dev_author='U. Bruhin', + dev_version='0.2', + dev_create_date='2023-08-29T17:06:05Z', + description='{pin_count}-pin 2.00mm pitch SMD IDC box header by CNC Tech.', + keywords='cnc tech,idc,header,male,box header,smd,3120,2.00mm', + pitch=2.0, + row_spacing=2.0, + pad_size=(3.45, 0.9), + pad_x_offset=-0.2, + body_offset_x=1.75, + body_offset_y=4.65, + body_gap=3.7, + lead_width=0.5, + lead_span=7.5, + pin_count=pc, + parts_manufacturer='CNC Tech', + parts_mpn=['3120-{pin_count:02}-0300-00'], # No '-TR' variant(?) + ) + for pc in [6, 8, 10, 12, 14, 16, 18, 20, 24, 26, 30, 34, 40, 44, 50, 60, 64] + ] + + [ + Config( + library='CNC_Tech.lplib', + identifier='cnctech-3020-{pin_count}-0300', + pkg_name='CNCTECH_3020-{pin_count:02}-0300-XX', + pkg_author='Danilo Bargen', + pkg_version='0.2', + pkg_create_date='2019-07-09T21:31:21Z', + pkg_categories=['92186130-e1a4-4a82-8ce9-88f4aa854195', 'e4d3a6bf-af32-48a2-b427-5e794bed949a'], + dev_name='CNC Tech 3020-{pin_count:02}-0300', + dev_author='U. Bruhin', + dev_version='0.2', + dev_create_date='2023-08-29T17:06:05Z', + description='{pin_count}-pin 2.54mm pitch SMD IDC box header by CNC Tech.', + keywords='cnc tech,idc,header,male,box header,smd,3020,2.54mm', + pitch=2.54, + row_spacing=2.54, + pad_size=(4.8, 0.9), + pad_x_offset=-0.42, + body_offset_x=3.13, + body_offset_y=5.08, + body_gap=5.08, + lead_width=0.64, + lead_span=10.2, + pin_count=pc, + parts_manufacturer='CNC Tech', + parts_mpn=['3020-{pin_count:02}-0300-00', '3020-{pin_count:02}-0300-00-TR'], + ) + for pc in [6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 50, 60, 64] + ] + ) for config in configs: generate_pkg(config=config) generate_dev(config=config) diff --git a/generate_jst_sh_connectors.py b/generate_jst_sh_connectors.py index 622e4b7..1d2c225 100644 --- a/generate_jst_sh_connectors.py +++ b/generate_jst_sh_connectors.py @@ -1,10 +1,11 @@ """ - Generate JST SH wire-to-board female connectors +Generate JST SH wire-to-board female connectors - see - https://en.wikipedia.org/wiki/JST_connector, https://jst.de/product-family/show/65/sh +see + https://en.wikipedia.org/wiki/JST_connector, https://jst.de/product-family/show/65/sh """ + import math from os import path from uuid import uuid4 @@ -14,15 +15,53 @@ from common import init_cache, now, save_cache from entities.attribute import StringAttribute from entities.common import ( - Align, Angle, Author, Category, Created, Deprecated, Description, Fill, GeneratedBy, GrabArea, Height, Keywords, - Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Created, + Deprecated, + Description, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.component import SignalUUID from entities.device import ComponentPad, ComponentUUID, Device, Manufacturer, PackageUUID, Part from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, FootprintPad, LetterSpacing, LineSpacing, - Mirror, Package, PackagePad, PackagePadUuid, PadFunction, Shape, ShapeRadius, Size, SolderPasteConfig, - StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + Footprint, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + PackagePad, + PackagePadUuid, + PadFunction, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_jst_sh_connectors.py)' @@ -55,21 +94,22 @@ def __init__(self, type: str, subtype: str, circuits: int) -> None: class FootprintSpecification: - def __init__(self, - pad_width: float, - pad_height: float, - lead_width: float, - lead_height: float, - support_pad_width: float, - support_pad_height: float, - smallest_header_width: float, - header_width_increase_per_pin: float, - header_height: float, - pad_distance_mid_to_mid_x: float, - pad_first_x_center: float, - pad_first_y_center: float, - header_y: float - ): + def __init__( + self, + pad_width: float, + pad_height: float, + lead_width: float, + lead_height: float, + support_pad_width: float, + support_pad_height: float, + smallest_header_width: float, + header_width_increase_per_pin: float, + header_height: float, + pad_distance_mid_to_mid_x: float, + pad_first_x_center: float, + pad_first_y_center: float, + header_y: float, + ): self.pad_width = pad_width self.pad_height = pad_height self.lead_width = lead_width @@ -96,7 +136,11 @@ def header_width(self, circuits: int) -> float: return self.smallest_header_width + (circuits - 2) * self.header_width_increase_per_pin def support_pad_distance_x(self, circuits: int) -> float: - return self.pad_first_x_center + (circuits - 1) * self.pad_distance_mid_to_mid_x + self.support_pad_edge_to_pad_edge + return ( + self.pad_first_x_center + + (circuits - 1) * self.pad_distance_mid_to_mid_x + + self.support_pad_edge_to_pad_edge + ) def header_x(self, circuits: int) -> float: return (self.support_pad_width + self.support_pad_distance_x(circuits) - self.header_width(circuits)) / 2 @@ -106,7 +150,7 @@ def header_x_center(self, circuits: int) -> float: def variant(mounting_variant: str, circuits: int) -> str: - return f"{mounting_variant}{circuits}" + return f'{mounting_variant}{circuits}' def uuid(category: str, kind: str, variant: str, identifier: str) -> str: @@ -121,15 +165,15 @@ def connector_uuid(category: str, connector: Connector, identifier: str) -> str: def pkg_uuid(connector: Connector, identifier: str) -> str: - return connector_uuid("pkg", connector, identifier) + return connector_uuid('pkg', connector, identifier) def footprint_uuid(connector: Connector, identifier: str) -> str: - return connector_uuid("footprint", connector, identifier) + return connector_uuid('footprint', connector, identifier) def dev_uuid(connector: Connector, identifier: str) -> str: - return connector_uuid("dev", connector, identifier) + return connector_uuid('dev', connector, identifier) def vertex(x: float, y: float) -> Vertex: @@ -144,18 +188,17 @@ def polygon_rect(polygon: Polygon, x: float, y: float, w: float, h: float) -> No polygon.add_vertex(Vertex(Position(x, y), Angle(0))) -def footprint_add_support_pads( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification -) -> None: +def footprint_add_support_pads(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: for i in range(2): footprint.add_pad( FootprintPad( uuid=footprint_uuid(connector, support_pad_uuid_pattern.format(i)), side=ComponentSide.TOP, shape=Shape.ROUNDED_RECT, - position=Position(spec.support_pad_first_x_center + i * spec.support_pad_distance_x(connector.circuits), spec.support_pad_first_y_center), + position=Position( + spec.support_pad_first_x_center + i * spec.support_pad_distance_x(connector.circuits), + spec.support_pad_first_y_center, + ), rotation=Rotation(0), size=Size(spec.support_pad_width, spec.support_pad_height), radius=ShapeRadius(0.5), # 0.5 is LibrePCB default @@ -163,17 +206,14 @@ def footprint_add_support_pads( solder_paste=SolderPasteConfig.AUTO, copper_clearance=CopperClearance(0), function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid("none"), # not connected, mechanical pad - holes=[] + package_pad=PackagePadUuid('none'), # not connected, mechanical pad + holes=[], ) ) def footprint_add_pads( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification, - reverse_pad_order: bool + footprint: Footprint, connector: Connector, spec: FootprintSpecification, reverse_pad_order: bool ) -> None: for i in range(connector.circuits): pad_number = i if not reverse_pad_order else (connector.circuits - 1) - i @@ -183,7 +223,9 @@ def footprint_add_pads( uuid=footprint_uuid(connector, pad_uuid_identifier), side=ComponentSide.TOP, shape=Shape.ROUNDED_RECT, - position=Position(spec.pad_first_x_center + i * spec.pad_distance_mid_to_mid_x, spec.pad_first_y_center), + position=Position( + spec.pad_first_x_center + i * spec.pad_distance_mid_to_mid_x, spec.pad_first_y_center + ), rotation=Rotation(0), size=Size(spec.pad_width, spec.pad_height), radius=ShapeRadius(0.5), # 0.5 is LibrePCB default @@ -192,53 +234,45 @@ def footprint_add_pads( copper_clearance=CopperClearance(0), function=PadFunction.STANDARD_PAD, package_pad=PackagePadUuid(pkg_uuid(connector, pad_uuid_identifier)), - holes=[] + holes=[], ) ) -def footprint_add_header( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification -) -> None: +def footprint_add_header(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: header_half_line_width = header_line_width / 2 header_polygon = Polygon( uuid=footprint_uuid(connector, 'polygonheader'), - layer=Layer("top_documentation"), + layer=Layer('top_documentation'), width=Width(header_line_width), fill=Fill(False), - grab_area=GrabArea(True) + grab_area=GrabArea(True), ) # NOTE: lines in librePCB expand equally on each # side if drawn with a width of > 0 - polygon_rect(header_polygon, - spec.header_x(connector.circuits) + header_half_line_width, - spec.header_y + header_half_line_width, - spec.header_width(connector.circuits) - header_half_line_width * 2, - spec.header_height - header_half_line_width * 2) + polygon_rect( + header_polygon, + spec.header_x(connector.circuits) + header_half_line_width, + spec.header_y + header_half_line_width, + spec.header_width(connector.circuits) - header_half_line_width * 2, + spec.header_height - header_half_line_width * 2, + ) footprint.add_polygon(header_polygon) -def footprint_add_text( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification, - rotation: int -) -> None: - +def footprint_add_text(footprint: Footprint, connector: Connector, spec: FootprintSpecification, rotation: int) -> None: # TODO: use match expression when python 3.10 is min required version for librePCB if rotation == 0: - value_align, name_align = "left center", "right center" + value_align, name_align = 'left center', 'right center' elif rotation == 90: - value_align, name_align = "center top", "center bottom" + value_align, name_align = 'center top', 'center bottom' elif rotation == 180: - value_align, name_align = "right center", "left center" + value_align, name_align = 'right center', 'left center' else: # rotation == 270 and fallback - value_align, name_align = "center bottom", "center top" + value_align, name_align = 'center bottom', 'center top' name_text = StrokeText( uuid=footprint_uuid(connector, 'textname'), @@ -248,11 +282,16 @@ def footprint_add_text( letter_spacing=LetterSpacing.AUTO, line_spacing=LineSpacing.AUTO, align=Align(name_align), - position=Position(spec.header_x_center(connector.circuits) - (spec.header_width(connector.circuits) / 2) - text_header_spacing, spec.header_y_center), + position=Position( + spec.header_x_center(connector.circuits) + - (spec.header_width(connector.circuits) / 2) + - text_header_spacing, + spec.header_y_center, + ), rotation=Rotation(0), auto_rotate=AutoRotate(True), mirror=Mirror(False), - value=Value('{{NAME}}') + value=Value('{{NAME}}'), ) value_text = StrokeText( @@ -263,14 +302,21 @@ def footprint_add_text( letter_spacing=LetterSpacing.AUTO, line_spacing=LineSpacing.AUTO, align=Align(value_align), - position=Position(spec.header_x_center(connector.circuits) + (spec.header_width(connector.circuits) / 2) + text_header_spacing, spec.header_y_center), + position=Position( + spec.header_x_center(connector.circuits) + + (spec.header_width(connector.circuits) / 2) + + text_header_spacing, + spec.header_y_center, + ), rotation=Rotation(0), auto_rotate=AutoRotate(True), mirror=Mirror(False), - value=Value('{{VALUE}}') + value=Value('{{VALUE}}'), ) - if rotation > 90: # swap positions and alignemnts to always keep the name either left or top and the value always right or down + if ( + rotation > 90 + ): # swap positions and alignemnts to always keep the name either left or top and the value always right or down name_text.position, value_text.position = value_text.position, name_text.position name_text.align, value_text.align = value_text.align, name_text.align @@ -278,38 +324,28 @@ def footprint_add_text( footprint.add_text(value_text) -def footprint_add_leads( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification -) -> None: - +def footprint_add_leads(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: for i in range(connector.circuits): - lead_polygon = Polygon( - uuid=footprint_uuid(connector, f"lead{i}"), + uuid=footprint_uuid(connector, f'lead{i}'), layer=Layer('top_documentation'), width=Width(0), fill=Fill(True), - grab_area=GrabArea(False) + grab_area=GrabArea(False), ) - polygon_rect(lead_polygon, - spec.pad_first_x_center - (spec.lead_width / 2) + i * spec.pad_distance_mid_to_mid_x, - spec.header_y + spec.header_height, - spec.lead_width, - spec.lead_height - ) + polygon_rect( + lead_polygon, + spec.pad_first_x_center - (spec.lead_width / 2) + i * spec.pad_distance_mid_to_mid_x, + spec.header_y + spec.header_height, + spec.lead_width, + spec.lead_height, + ) footprint.add_polygon(lead_polygon) -def footprint_add_outline( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification -) -> None: - +def footprint_add_outline(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: outline_polygon = Polygon( uuid=footprint_uuid(connector, 'polygonoutline'), layer=Layer('top_package_outlines'), @@ -322,27 +358,38 @@ def footprint_add_outline( # right bottom vertex(spec.header_x(connector.circuits) + spec.header_width(connector.circuits), spec.header_y), # right top - vertex(spec.header_x(connector.circuits) + spec.header_width(connector.circuits), spec.header_y + spec.header_height), + vertex( + spec.header_x(connector.circuits) + spec.header_width(connector.circuits), + spec.header_y + spec.header_height, + ), # leads - vertex(spec.pad_first_x_center + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + (spec.pad_width / 2), spec.header_y + spec.header_height), - vertex(spec.pad_first_x_center + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + (spec.pad_width / 2), spec.header_y + spec.header_height + spec.lead_height), - vertex(spec.pad_first_x_center - (spec.pad_width / 2), spec.header_y + spec.lead_height + spec.header_height), + vertex( + spec.pad_first_x_center + + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + + (spec.pad_width / 2), + spec.header_y + spec.header_height, + ), + vertex( + spec.pad_first_x_center + + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + + (spec.pad_width / 2), + spec.header_y + spec.header_height + spec.lead_height, + ), + vertex( + spec.pad_first_x_center - (spec.pad_width / 2), spec.header_y + spec.lead_height + spec.header_height + ), vertex(spec.pad_first_x_center - (spec.pad_width / 2), spec.header_y + spec.header_height), # left top vertex(spec.header_x(connector.circuits), spec.header_y + spec.header_height), # left bottom - vertex(spec.header_x(connector.circuits), spec.header_y) - ] + vertex(spec.header_x(connector.circuits), spec.header_y), + ], ) footprint.add_polygon(outline_polygon) -def footprint_add_courtyard( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification -) -> None: +def footprint_add_courtyard(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: courtyard_polygon = Polygon( uuid=footprint_uuid(connector, 'polygoncourtyard'), layer=Layer('top_courtyard'), @@ -353,30 +400,49 @@ def footprint_add_courtyard( # left bottom vertex(0 - courtyard_excess, 0 - courtyard_excess), # right bottom - vertex(spec.support_pad_distance_x(connector.circuits) + spec.support_pad_width + courtyard_excess, 0 - courtyard_excess), + vertex( + spec.support_pad_distance_x(connector.circuits) + spec.support_pad_width + courtyard_excess, + 0 - courtyard_excess, + ), # right top - vertex(spec.support_pad_distance_x(connector.circuits) + spec.support_pad_width + courtyard_excess, spec.header_y + spec.header_height + courtyard_excess), + vertex( + spec.support_pad_distance_x(connector.circuits) + spec.support_pad_width + courtyard_excess, + spec.header_y + spec.header_height + courtyard_excess, + ), # leads - vertex(spec.pad_first_x_center + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + (spec.pad_width / 2) + courtyard_excess, spec.header_y + spec.header_height + courtyard_excess), - vertex(spec.pad_first_x_center + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + (spec.pad_width / 2) + courtyard_excess, spec.pad_first_y_center + (spec.pad_height / 2) + courtyard_excess), - vertex(spec.pad_first_x_center - (spec.pad_width / 2) - courtyard_excess, spec.pad_first_y_center + (spec.pad_height / 2) + courtyard_excess), - vertex(spec.pad_first_x_center - (spec.pad_width / 2) - courtyard_excess, spec.header_y + spec.header_height + courtyard_excess), + vertex( + spec.pad_first_x_center + + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + + (spec.pad_width / 2) + + courtyard_excess, + spec.header_y + spec.header_height + courtyard_excess, + ), + vertex( + spec.pad_first_x_center + + (spec.pad_distance_mid_to_mid_x * (connector.circuits - 1)) + + (spec.pad_width / 2) + + courtyard_excess, + spec.pad_first_y_center + (spec.pad_height / 2) + courtyard_excess, + ), + vertex( + spec.pad_first_x_center - (spec.pad_width / 2) - courtyard_excess, + spec.pad_first_y_center + (spec.pad_height / 2) + courtyard_excess, + ), + vertex( + spec.pad_first_x_center - (spec.pad_width / 2) - courtyard_excess, + spec.header_y + spec.header_height + courtyard_excess, + ), # left top vertex(0 - courtyard_excess, spec.header_y + spec.header_height + courtyard_excess), # left bottom - vertex(0 - courtyard_excess, 0 - courtyard_excess) - ] + vertex(0 - courtyard_excess, 0 - courtyard_excess), + ], ) footprint.add_polygon(courtyard_polygon) -def footprint_add_legend( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification -) -> None: - +def footprint_add_legend(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: half_line_width = legend_line_width / 2 min_copper_clearance = 0.15 @@ -389,17 +455,23 @@ def footprint_add_legend( vertices=[ vertex( spec.header_x(connector.circuits) - legend_header_spacing - half_line_width, - spec.support_pad_first_y_center + (spec.support_pad_height / 2) + half_line_width + max(min_copper_clearance, legend_line_width) + spec.support_pad_first_y_center + + (spec.support_pad_height / 2) + + half_line_width + + max(min_copper_clearance, legend_line_width), ), vertex( spec.header_x(connector.circuits) - legend_header_spacing - half_line_width, - spec.header_y + spec.header_height + legend_header_spacing + half_line_width + spec.header_y + spec.header_height + legend_header_spacing + half_line_width, ), vertex( - spec.pad_first_x_center - (spec.pad_width / 2) - half_line_width - max(min_copper_clearance, legend_line_width), - spec.header_y + spec.header_height + legend_header_spacing + half_line_width - ) - ] + spec.pad_first_x_center + - (spec.pad_width / 2) + - half_line_width + - max(min_copper_clearance, legend_line_width), + spec.header_y + spec.header_height + legend_header_spacing + half_line_width, + ), + ], ) footprint.add_polygon(top_left_legend_polygon) @@ -412,18 +484,31 @@ def footprint_add_legend( grab_area=GrabArea(False), vertices=[ vertex( - spec.header_x(connector.circuits) + spec.header_width(connector.circuits) + legend_header_spacing + half_line_width, - spec.support_pad_first_y_center + (spec.support_pad_height / 2) + half_line_width + max(min_copper_clearance, legend_line_width) + spec.header_x(connector.circuits) + + spec.header_width(connector.circuits) + + legend_header_spacing + + half_line_width, + spec.support_pad_first_y_center + + (spec.support_pad_height / 2) + + half_line_width + + max(min_copper_clearance, legend_line_width), ), vertex( - spec.header_x(connector.circuits) + spec.header_width(connector.circuits) + legend_header_spacing + half_line_width, - spec.header_y + spec.header_height + legend_header_spacing + half_line_width + spec.header_x(connector.circuits) + + spec.header_width(connector.circuits) + + legend_header_spacing + + half_line_width, + spec.header_y + spec.header_height + legend_header_spacing + half_line_width, ), vertex( - spec.pad_first_x_center + spec.pad_distance_mid_to_mid_x * (connector.circuits - 1) + (spec.pad_width / 2) + half_line_width + max(min_copper_clearance, legend_line_width), - spec.header_y + spec.header_height + legend_header_spacing + half_line_width - ) - ] + spec.pad_first_x_center + + spec.pad_distance_mid_to_mid_x * (connector.circuits - 1) + + (spec.pad_width / 2) + + half_line_width + + max(min_copper_clearance, legend_line_width), + spec.header_y + spec.header_height + legend_header_spacing + half_line_width, + ), + ], ) footprint.add_polygon(top_right_legend_polygon) @@ -436,33 +521,33 @@ def footprint_add_legend( grab_area=GrabArea(False), vertices=[ vertex( - spec.support_pad_first_x_center + (spec.support_pad_width / 2) + half_line_width + max(min_copper_clearance, legend_line_width), - spec.header_y - half_line_width - legend_header_spacing + spec.support_pad_first_x_center + + (spec.support_pad_width / 2) + + half_line_width + + max(min_copper_clearance, legend_line_width), + spec.header_y - half_line_width - legend_header_spacing, ), vertex( - spec.support_pad_distance_x(connector.circuits) - half_line_width - max(min_copper_clearance, legend_line_width), - spec.header_y - half_line_width - legend_header_spacing - ) - ] + spec.support_pad_distance_x(connector.circuits) + - half_line_width + - max(min_copper_clearance, legend_line_width), + spec.header_y - half_line_width - legend_header_spacing, + ), + ], ) footprint.add_polygon(bottom_center_legend_polygon) def generate_footprint( - connector: Connector, - spec: FootprintSpecification, - description: str, - reverse_pad_order: bool, - rotation: int + connector: Connector, spec: FootprintSpecification, description: str, reverse_pad_order: bool, rotation: int ) -> Footprint: - footprint = Footprint( uuid=footprint_uuid(connector, 'footprint'), - name=Name("default"), + name=Name('default'), description=Description(description), position_3d=Position3D.zero(), - rotation_3d=Rotation3D.zero() + rotation_3d=Rotation3D.zero(), ) footprint_add_support_pads(footprint, connector, spec) @@ -485,7 +570,6 @@ def generate_footprint( # shift all members inside a Footprint with a position to the center # ASSUMES that the current origin/center is in the first quadrant def footprint_shift_to_center(footprint: Footprint, connector: Connector, spec: FootprintSpecification) -> None: - def _center(p: Position) -> None: p.x -= spec.header_x_center(connector.circuits) p.y -= spec.header_y_center @@ -505,7 +589,6 @@ def _center(p: Position) -> None: def footprint_rotate_around_center(footprint: Footprint, angle_deg: int) -> None: - angle_rad = math.radians(angle_deg) def _rotate(p: Position) -> None: @@ -536,16 +619,11 @@ def approve_footprint_warnings(package: Package, footprint: Footprint, connector TODO: Remove the 3D models approval once we have 3D models TODO: Use "Approval" entity once it gets added """ - footprint_str = f" (footprint {footprint.uuid})\n" + footprint_str = f' (footprint {footprint.uuid})\n' - approval_missing_3d_model = "(approved missing_footprint_3d_model\n" +\ - footprint_str +\ - ")" + approval_missing_3d_model = '(approved missing_footprint_3d_model\n' + footprint_str + ')' - approval_suspicious_pad = "(approved suspicious_pad_function\n" +\ - footprint_str +\ - " (pad {})\n" +\ - ")" + approval_suspicious_pad = '(approved suspicious_pad_function\n' + footprint_str + ' (pad {})\n' + ')' for i in range(2): pad_uuid = footprint_uuid(connector, support_pad_uuid_pattern.format(i)) @@ -565,12 +643,11 @@ def generate_pkg( generated_by: str, footprint_spec: FootprintSpecification, reverse_pad_order: bool, - rotation: int + rotation: int, ) -> Package: - package = Package( uuid=pkg_uuid(connector, 'pkg'), - name=Name(f"JST_{connector.type}-{connector.subtype}-{connector.circuits:02d}"), + name=Name(f'JST_{connector.type}-{connector.subtype}-{connector.circuits:02d}'), description=Description(description), keywords=Keywords(keywords), author=Author(author), @@ -579,7 +656,7 @@ def generate_pkg( deprecated=Deprecated(False), generated_by=GeneratedBy(generated_by), categories=[Category(pkgcat) for pkgcat in sorted(pkgcats)], - assembly_type=AssemblyType.SMT + assembly_type=AssemblyType.SMT, ) for i in range(connector.circuits): @@ -606,9 +683,8 @@ def generate_dev( create_date: Optional[str], generated_by: str, dev_name: str, - suction_cap_variant_available: bool + suction_cap_variant_available: bool, ) -> Device: - connector_uuid_stub = f'cmp-pinheader-1x{connector.circuits}' component_uuid = uuid_cache_connectors[f'{connector_uuid_stub}-cmp'] signal_uuids = [uuid_cache_connectors[f'{connector_uuid_stub}-signal-{i}'] for i in range(connector.circuits)] @@ -633,11 +709,11 @@ def generate_dev( for i in range(connector.circuits): dev.add_pad(ComponentPad(pkg_uuid(connector, pad_uuid_name_pattern.format(i)), SignalUUID(signal_uuids[i]))) - dev.add_part(Part(dev_name, Manufacturer("JST"))) + dev.add_part(Part(dev_name, Manufacturer('JST'))) if suction_cap_variant_available: - part_with_suction_cap = Part(dev_name + 'T', Manufacturer("JST")) - part_with_suction_cap.add_attribute(StringAttribute("FEATURES", "Suction Cap")) + part_with_suction_cap = Part(dev_name + 'T', Manufacturer('JST')) + part_with_suction_cap.add_attribute(StringAttribute('FEATURES', 'Suction Cap')) dev.add_part(part_with_suction_cap) return dev @@ -660,13 +736,11 @@ def generate_jst( suction_cap_variant_available: bool, device_naming_pattern: str, reverse_pad_order: bool, - rotation: int + rotation: int, ) -> None: - - assert (rotation >= 0 and rotation <= 270 and rotation % 90 == 0) + assert rotation >= 0 and rotation <= 270 and rotation % 90 == 0 for circuits in available_circuits: - conn = Connector(pkg_type, pkg_subtype, circuits) pkg = generate_pkg( @@ -680,7 +754,7 @@ def generate_jst( generated_by=generated_by, footprint_spec=footprint_spec, reverse_pad_order=reverse_pad_order, - rotation=rotation + rotation=rotation, ) dev = generate_dev( @@ -693,8 +767,8 @@ def generate_jst( version=version, create_date=create_date, generated_by=generated_by, - dev_name=device_naming_pattern.format(f"{circuits:02d}").upper(), - suction_cap_variant_available=suction_cap_variant_available + dev_name=device_naming_pattern.format(f'{circuits:02d}').upper(), + suction_cap_variant_available=suction_cap_variant_available, ) pkg.serialize(path.join('out', library, 'pkg')) @@ -704,22 +778,24 @@ def generate_jst( print(f'wrote device {dev.name.value}: {dev.uuid}') -if __name__ == "__main__": - +if __name__ == '__main__': create_date = '2024-05-03T17:19:09Z' # units in mm generate_jst( - library="JST.lplib", - pkg_type="SH", - pkg_subtype="SM", - description="Header SR 1.0 SMT side entry, 1mm pitch", - keywords="connector,jst", # taken from https://jst.de/product-family/show/65/sh - author="nbes4", - generated_by="", # leave empty, not used yet - pkgcats=["e4d3a6bf-af32-48a2-b427-5e794bed949a", "3f0f5992-67fd-4ce9-a510-7679870d6271"], # Pin Headers (male), JST - devcat="4a4e3c72-94fb-45f9-a6d8-122d2af16fb1", # Pin Headers (male) - version="0.2", + library='JST.lplib', + pkg_type='SH', + pkg_subtype='SM', + description='Header SR 1.0 SMT side entry, 1mm pitch', + keywords='connector,jst', # taken from https://jst.de/product-family/show/65/sh + author='nbes4', + generated_by='', # leave empty, not used yet + pkgcats=[ + 'e4d3a6bf-af32-48a2-b427-5e794bed949a', + '3f0f5992-67fd-4ce9-a510-7679870d6271', + ], # Pin Headers (male), JST + devcat='4a4e3c72-94fb-45f9-a6d8-122d2af16fb1', # Pin Headers (male) + version='0.2', footprint_spec=FootprintSpecification( pad_width=0.6, pad_height=1.55, @@ -739,23 +815,26 @@ def generate_jst( ), available_circuits=[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20], suction_cap_variant_available=False, - device_naming_pattern="SM{}B-SRSS-TB", + device_naming_pattern='SM{}B-SRSS-TB', create_date=create_date, reverse_pad_order=True, - rotation=270 + rotation=270, ) generate_jst( - library="JST.lplib", - pkg_type="SH", - pkg_subtype="BM", - description="Header SR 1.0 SMT top entry, 1mm pitch", - keywords="connector,jst", - author="nbes4", - generated_by="", # leave empty, not used yet - pkgcats=["e4d3a6bf-af32-48a2-b427-5e794bed949a", "3f0f5992-67fd-4ce9-a510-7679870d6271"], # Pin Headers (male), JST - devcat="4a4e3c72-94fb-45f9-a6d8-122d2af16fb1", # Pin Headers (male) - version="0.2", + library='JST.lplib', + pkg_type='SH', + pkg_subtype='BM', + description='Header SR 1.0 SMT top entry, 1mm pitch', + keywords='connector,jst', + author='nbes4', + generated_by='', # leave empty, not used yet + pkgcats=[ + 'e4d3a6bf-af32-48a2-b427-5e794bed949a', + '3f0f5992-67fd-4ce9-a510-7679870d6271', + ], # Pin Headers (male), JST + devcat='4a4e3c72-94fb-45f9-a6d8-122d2af16fb1', # Pin Headers (male) + version='0.2', footprint_spec=FootprintSpecification( pad_width=0.6, pad_height=1.55, @@ -775,10 +854,10 @@ def generate_jst( ), available_circuits=[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], suction_cap_variant_available=True, # see https://github.com/LibrePCB/librepcb-parts-generator/pull/127#issuecomment-2079003507 - device_naming_pattern="BM{}B-SRSS-TB", + device_naming_pattern='BM{}B-SRSS-TB', create_date=create_date, reverse_pad_order=False, - rotation=90 + rotation=90, ) save_cache(uuid_cache_jst_file, uuid_cache_jst) diff --git a/generate_led.py b/generate_led.py index 0d51d92..aae27c5 100644 --- a/generate_led.py +++ b/generate_led.py @@ -1,6 +1,7 @@ """ Generate THT LED packages. """ + import sys from math import acos, asin, degrees, sqrt from os import path @@ -11,15 +12,59 @@ from common import format_ipc_dimension as fd from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.component import SignalUUID from entities.device import ComponentPad, ComponentUUID, Device, PackageUUID from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, DrillDiameter, Footprint, Footprint3DModel, FootprintPad, - LetterSpacing, LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, PadHole, - Shape, ShapeRadius, Size, SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) GENERATOR_NAME = 'librepcb-parts-generator (generate_led.py)' @@ -81,13 +126,13 @@ def __init__( standoff_option=('S' + fd(standoff)) if standoff_in_name else '', body_color=body_color.upper(), ) - self.pkg_description = \ - 'Generic through-hole LED with {top_diameter:.2f} mm' \ - ' body diameter.\n\n' \ - 'Body height: {body_height:.2f} mm\n' \ - 'Lead spacing: {lead_spacing:.2f} mm\n' \ - 'Standoff: {standoff:.2f} mm\n' \ - 'Body color: {body_color}' \ + self.pkg_description = ( + 'Generic through-hole LED with {top_diameter:.2f} mm' + ' body diameter.\n\n' + 'Body height: {body_height:.2f} mm\n' + 'Lead spacing: {lead_spacing:.2f} mm\n' + 'Standoff: {standoff:.2f} mm\n' + 'Body color: {body_color}' '\n\nGenerated with {generator}'.format( top_diameter=top_diameter, body_height=body_height, @@ -96,6 +141,7 @@ def __init__( body_color=body_color, generator=GENERATOR_NAME, ) + ) self.dev_name = 'LED ⌀{top_diameter}x{body_height}{standoff_option}/{lead_spacing}mm {body_color}'.format( top_diameter=top_diameter, @@ -170,22 +216,23 @@ def _add_footprint( # Footprint pads for pad, factor in [('a', 1), ('c', -1)]: pad_uuid = _uuid('pad-{}'.format(pad)) - footprint.add_pad(FootprintPad( - uuid=pad_uuid, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(config.lead_spacing / 2 * factor, 0), - rotation=Rotation(90), - size=pad_size, - radius=ShapeRadius(0.0 if pad == 'c' else 1.0), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(pad_uuid), - holes=[PadHole(pad_uuid, DrillDiameter(pad_drill), - [Vertex(Position(0.0, 0.0), Angle(0.0))])], - )) + footprint.add_pad( + FootprintPad( + uuid=pad_uuid, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(config.lead_spacing / 2 * factor, 0), + rotation=Rotation(90), + size=pad_size, + radius=ShapeRadius(0.0 if pad == 'c' else 1.0), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(pad_uuid), + holes=[PadHole(pad_uuid, DrillDiameter(pad_drill), [Vertex(Position(0.0, 0.0), Angle(0.0))])], + ) + ) # 3D model uuid_3d = _uuid(identifier_3d + '-3d') @@ -195,8 +242,7 @@ def _add_footprint( # models were already added. if uuid_3d not in generated_3d_uuids: if generate_3d_models: - generate_3d(library, name_3d, uuid_pkg, uuid_3d, config, - vertical, horizontal_offset) + generate_3d(library, name_3d, uuid_pkg, uuid_3d, config, vertical, horizontal_offset) package.add_3d_model(Package3DModel(uuid_3d, Name(name_3d))) generated_3d_uuids.add(uuid_3d) footprint.add_3d_model(Footprint3DModel(uuid_3d)) @@ -242,19 +288,21 @@ def _add_flattened_circle( """ # Special case: If outer_radius == inner_radius, return a full circle. if outer_radius == inner_radius: - footprint.add_circle(Circle( - uuid=_uuid(identifier), - layer=Layer(layer), - width=Width(line_width), - position=Position(0, 0), - diameter=Diameter(outer_radius * 2), - fill=Fill(False), - grab_area=GrabArea(False), - )) + footprint.add_circle( + Circle( + uuid=_uuid(identifier), + layer=Layer(layer), + width=Width(line_width), + position=Position(0, 0), + diameter=Diameter(outer_radius * 2), + fill=Fill(False), + grab_area=GrabArea(False), + ) + ) return # To calculate the y offset of the flat side, use Pythagoras - y = sqrt(outer_radius ** 2 - inner_radius ** 2) + y = sqrt(outer_radius**2 - inner_radius**2) # Now we can calculate the angle of the circle segment if reduced: @@ -334,34 +382,38 @@ def _add_flattened_circle( ) # Text - footprint.add_text(StrokeText( - uuid=_uuid('text-name' + identifier_suffix), - layer=Layer('top_names'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, (config.bot_diameter / 2) + 0.8), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_uuid('text-value' + identifier_suffix), - layer=Layer('top_values'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -(config.bot_diameter / 2) - 0.8), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_uuid('text-name' + identifier_suffix), + layer=Layer('top_names'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, (config.bot_diameter / 2) + 0.8), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_uuid('text-value' + identifier_suffix), + layer=Layer('top_values'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -(config.bot_diameter / 2) - 0.8), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) def _add_horizontal_footprint( package: Package, @@ -498,55 +550,63 @@ def _generate_outline(offset: float = 0, pad_offset: float = 0) -> List[Vertex]: Vertex(Position(-leads_x, body_y_bot), Angle(0)), ] - footprint.add_polygon(Polygon( - uuid=_uuid('polygon-outline' + identifier_suffix), - layer=Layer('top_package_outlines'), - width=Width(0.0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=_generate_outline(), - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('polygon-outline' + identifier_suffix), + layer=Layer('top_package_outlines'), + width=Width(0.0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=_generate_outline(), + ) + ) # Courtyard courtyard_offset = 0.5 if config.bot_diameter >= 10.0 else 0.4 - footprint.add_polygon(Polygon( - uuid=_uuid('polygon-courtyard' + identifier_suffix), - layer=Layer('top_courtyard'), - width=Width(0.0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=_generate_outline(courtyard_offset, 0.1), - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('polygon-courtyard' + identifier_suffix), + layer=Layer('top_courtyard'), + width=Width(0.0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=_generate_outline(courtyard_offset, 0.1), + ) + ) # Text - footprint.add_text(StrokeText( - uuid=_uuid('text-name' + identifier_suffix), - layer=Layer('top_names'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -1.27), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_uuid('text-value' + identifier_suffix), - layer=Layer('top_values'), - height=Height(1.0), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -3.0), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_uuid('text-name' + identifier_suffix), + layer=Layer('top_names'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -1.27), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_uuid('text-value' + identifier_suffix), + layer=Layer('top_values'), + height=Height(1.0), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -3.0), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # Add footprints _add_vertical_footprint( @@ -616,52 +676,72 @@ def generate_3d( standoff_height = min(config.standoff - standoff_clearance, 1.0) standoff_width = lead_width + 0.3 - body = cq.Workplane('XY') \ - .cylinder(ring_height, config.bot_diameter / 2, centered=(True, True, False)) \ - .faces('>Z') \ - .cylinder(cylinder_height, config.top_diameter / 2, centered=(True, True, False)) \ - .faces('>Z') \ - .sphere(config.top_diameter / 2) \ - .center(-config.bot_diameter / 2, 0) \ + body = ( + cq.Workplane('XY') + .cylinder(ring_height, config.bot_diameter / 2, centered=(True, True, False)) + .faces('>Z') + .cylinder(cylinder_height, config.top_diameter / 2, centered=(True, True, False)) + .faces('>Z') + .sphere(config.top_diameter / 2) + .center(-config.bot_diameter / 2, 0) .box((config.bot_diameter - config.top_diameter - 0.1) / 2, 20, 20, centered=(False, True, True), combine='cut') + ) if vertical: body = body.translate((0, 0, config.standoff)) - leg = cq.Workplane('XY') \ - .box(lead_width, lead_width, StepConstants.THT_LEAD_SOLDER_LENGTH + config.standoff + 0.1, centered=(True, True, False)) \ - .faces(' 0: - leg = leg.faces('Z') \ - .workplane(offset=-lead_width / 2) \ - .center(0, horizontal_length + bend_radius - config.standoff) \ + leg = ( + leg.faces('>Z') + .workplane(offset=-lead_width / 2) + .center(0, horizontal_length + bend_radius - config.standoff) .box(standoff_width, standoff_height, lead_width, centered=(True, False, True)) + ) assembly = StepAssembly(name) assembly.add_body(body, 'body', cq.Color(*config.body_color_rgba)) - assembly.add_body(leg, 'leg-1', StepColor.LEAD_THT, location=cq.Location( - (-config.lead_spacing / 2, 0, -StepConstants.THT_LEAD_SOLDER_LENGTH)) + assembly.add_body( + leg, + 'leg-1', + StepColor.LEAD_THT, + location=cq.Location((-config.lead_spacing / 2, 0, -StepConstants.THT_LEAD_SOLDER_LENGTH)), ) - assembly.add_body(leg, 'leg-2', StepColor.LEAD_THT, location=cq.Location( - (config.lead_spacing / 2, 0, -StepConstants.THT_LEAD_SOLDER_LENGTH)) + assembly.add_body( + leg, + 'leg-2', + StepColor.LEAD_THT, + location=cq.Location((config.lead_spacing / 2, 0, -StepConstants.THT_LEAD_SOLDER_LENGTH)), ) out_path = path.join('out', library, 'pkg', uuid_pkg, f'{uuid_3d}.step') @@ -679,6 +759,7 @@ def generate_dev( ) -> None: category = 'dev' for config in configs: + def _uuid(identifier: str) -> str: return uuid(category, config.dev_name, identifier) @@ -700,14 +781,18 @@ def _uuid(identifier: str) -> str: component_uuid=ComponentUUID('2b24b18d-bd95-4fb4-8fe6-bce1d020ead4'), package_uuid=PackageUUID(uuid('pkg', config.pkg_name, 'pkg')), ) - device.add_pad(ComponentPad( - pad_uuid=uuid('pkg', config.pkg_name, 'pad-a'), - signal=SignalUUID('f1467b5c-cc7d-44b4-8076-d729f35b3a6a'), - )) - device.add_pad(ComponentPad( - pad_uuid=uuid('pkg', config.pkg_name, 'pad-c'), - signal=SignalUUID('7b023430-b68f-403a-80b8-c7deb12e7a0c'), - )) + device.add_pad( + ComponentPad( + pad_uuid=uuid('pkg', config.pkg_name, 'pad-a'), + signal=SignalUUID('f1467b5c-cc7d-44b4-8076-d729f35b3a6a'), + ) + ) + device.add_pad( + ComponentPad( + pad_uuid=uuid('pkg', config.pkg_name, 'pad-c'), + signal=SignalUUID('7b023430-b68f-403a-80b8-c7deb12e7a0c'), + ) + ) device.serialize(path.join('out', library, category)) diff --git a/generate_mosfet_dual.py b/generate_mosfet_dual.py index 382c6f4..28cb49e 100644 --- a/generate_mosfet_dual.py +++ b/generate_mosfet_dual.py @@ -1,6 +1,7 @@ """ Generate dual mosfet devices. """ + from os import makedirs, path from uuid import uuid4 @@ -155,7 +156,7 @@ def generate_dev( lines.append(' (component {})'.format(uuid_cmp)) lines.append(' (package {})'.format(uuid_pkg)) pad_signal_mappings = [] - for (pad, signal) in zip(uuid_pads, uuid_signals): + for pad, signal in zip(uuid_pads, uuid_signals): pad_signal_mappings.append(' (pad {} (signal {}))'.format(pad, signal)) lines.extend(sorted(pad_signal_mappings)) lines.append(')') @@ -184,75 +185,273 @@ def generate_dev( uuid_cmp='9d043413-9574-4727-af3a-21c5623cffae', configs=[ # SOIC127P600X175-8 - FetConfig('DMC2020USD', 20, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DMC2020USD.pdf'), - FetConfig('DMC3016LSD', 30, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DMC3016LSD.pdf'), - FetConfig('DMC3021LSD[Q]', 30, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], [ - 'https://www.diodes.com/assets/Datasheets/ds32152.pdf', - 'https://www.diodes.com/assets/Datasheets/DMC3021LSDQ.pdf', - ]), - FetConfig('DMC3025LSD[Q]', 30, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], [ - 'https://www.diodes.com/assets/Datasheets/DMC3025LSD.pdf', - 'https://www.diodes.com/assets/Datasheets/DMC3025LSDQ.pdf', - ]), - FetConfig('DMC3028LSD[Q[X]]', 30, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], [ - 'https://www.diodes.com/assets/Datasheets/DMC3028LSD.pdf', - 'https://www.diodes.com/assets/Datasheets/DMC3028LSDX.pdf', - 'https://www.diodes.com/assets/Datasheets/DMC3028LSDXQ.pdf', - ]), - FetConfig('DMC3032LSD', 30, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/ds32153.pdf'), - FetConfig('DMC4015SSD', 40, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DMC4015SSD.pdf'), - FetConfig('DMC4028SSD', 40, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DMC4028SSD.pdf'), - FetConfig('DMC4029SSD', 40, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DMC4029SSD.pdf'), - FetConfig('DMC4040SSD[Q]', 40, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], [ - 'https://www.diodes.com/assets/Datasheets/ds32120.pdf', - 'https://www.diodes.com/assets/Datasheets/DMC4040SSDQ.pdf', - ]), - FetConfig('DMC4047LSD', 40, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DMC4047LSD.pdf'), - FetConfig('DMC4050SSD[Q]', 40, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], 'https://www.diodes.com/assets/Datasheets/DS33310.pdf'), - FetConfig('DMC6040SSD[Q]', 60, 'SOIC127P600X175-8', [ - 'sn', 'gn', 'sp', 'gp', 'dp', 'dp', 'dn', 'dn', - ], [ - 'https://www.diodes.com/assets/Datasheets/DMC6040SSD.pdf', - 'https://www.diodes.com/assets/Datasheets/DMC6040SSDQ.pdf', - ]), - + FetConfig( + 'DMC2020USD', + 20, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DMC2020USD.pdf', + ), + FetConfig( + 'DMC3016LSD', + 30, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DMC3016LSD.pdf', + ), + FetConfig( + 'DMC3021LSD[Q]', + 30, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + [ + 'https://www.diodes.com/assets/Datasheets/ds32152.pdf', + 'https://www.diodes.com/assets/Datasheets/DMC3021LSDQ.pdf', + ], + ), + FetConfig( + 'DMC3025LSD[Q]', + 30, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + [ + 'https://www.diodes.com/assets/Datasheets/DMC3025LSD.pdf', + 'https://www.diodes.com/assets/Datasheets/DMC3025LSDQ.pdf', + ], + ), + FetConfig( + 'DMC3028LSD[Q[X]]', + 30, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + [ + 'https://www.diodes.com/assets/Datasheets/DMC3028LSD.pdf', + 'https://www.diodes.com/assets/Datasheets/DMC3028LSDX.pdf', + 'https://www.diodes.com/assets/Datasheets/DMC3028LSDXQ.pdf', + ], + ), + FetConfig( + 'DMC3032LSD', + 30, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/ds32153.pdf', + ), + FetConfig( + 'DMC4015SSD', + 40, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DMC4015SSD.pdf', + ), + FetConfig( + 'DMC4028SSD', + 40, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DMC4028SSD.pdf', + ), + FetConfig( + 'DMC4029SSD', + 40, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DMC4029SSD.pdf', + ), + FetConfig( + 'DMC4040SSD[Q]', + 40, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + [ + 'https://www.diodes.com/assets/Datasheets/ds32120.pdf', + 'https://www.diodes.com/assets/Datasheets/DMC4040SSDQ.pdf', + ], + ), + FetConfig( + 'DMC4047LSD', + 40, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DMC4047LSD.pdf', + ), + FetConfig( + 'DMC4050SSD[Q]', + 40, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + 'https://www.diodes.com/assets/Datasheets/DS33310.pdf', + ), + FetConfig( + 'DMC6040SSD[Q]', + 60, + 'SOIC127P600X175-8', + [ + 'sn', + 'gn', + 'sp', + 'gp', + 'dp', + 'dp', + 'dn', + 'dn', + ], + [ + 'https://www.diodes.com/assets/Datasheets/DMC6040SSD.pdf', + 'https://www.diodes.com/assets/Datasheets/DMC6040SSDQ.pdf', + ], + ), # SOT95P280X145-6 - FetConfig('DMC2053UVT', 20, 'SOT95P280X145-6', ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], - 'https://www.diodes.com/assets/Datasheets/DMC2053UVT.pdf'), - FetConfig('DMC2057UVT', 20, 'SOT95P280X145-6', ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], - 'https://www.diodes.com/assets/Datasheets/DMC2057UVT2.pdf'), - FetConfig('DMC3071LVT', 30, 'SOT95P280X145-6', ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], - 'https://www.diodes.com/assets/Datasheets/DMC3071LVT.pdf'), - FetConfig('DMC3730UVT', 25, 'SOT95P280X145-6', ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], - 'https://www.diodes.com/assets/Datasheets/DMC3730UVT.pdf'), - FetConfig('DMG6601LVT', 30, 'SOT95P280X145-6', ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], - 'https://www.diodes.com/assets/Datasheets/DMG6601LVT.pdf'), - FetConfig('DMG6602SVTQ', 30, 'SOT95P280X145-6', ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], - 'https://www.diodes.com/assets/Datasheets/DMG6602SVTQ.pdf'), + FetConfig( + 'DMC2053UVT', + 20, + 'SOT95P280X145-6', + ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], + 'https://www.diodes.com/assets/Datasheets/DMC2053UVT.pdf', + ), + FetConfig( + 'DMC2057UVT', + 20, + 'SOT95P280X145-6', + ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], + 'https://www.diodes.com/assets/Datasheets/DMC2057UVT2.pdf', + ), + FetConfig( + 'DMC3071LVT', + 30, + 'SOT95P280X145-6', + ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], + 'https://www.diodes.com/assets/Datasheets/DMC3071LVT.pdf', + ), + FetConfig( + 'DMC3730UVT', + 25, + 'SOT95P280X145-6', + ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], + 'https://www.diodes.com/assets/Datasheets/DMC3730UVT.pdf', + ), + FetConfig( + 'DMG6601LVT', + 30, + 'SOT95P280X145-6', + ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], + 'https://www.diodes.com/assets/Datasheets/DMG6601LVT.pdf', + ), + FetConfig( + 'DMG6602SVTQ', + 30, + 'SOT95P280X145-6', + ['gn', 'sp', 'gp', 'dp', 'sn', 'dn'], + 'https://www.diodes.com/assets/Datasheets/DMG6602SVTQ.pdf', + ), ], ) save_cache(uuid_cache_file, uuid_cache) diff --git a/generate_qfp.py b/generate_qfp.py index c514ce8..06c2a73 100644 --- a/generate_qfp.py +++ b/generate_qfp.py @@ -8,6 +8,7 @@ - JEDEC MS-026 https://www.jedec.org/system/files/docs/MS-026D.pdf """ + import sys from collections import namedtuple from copy import deepcopy @@ -20,13 +21,55 @@ from common import format_ipc_dimension as fd from common import init_cache, now, save_cache, sign from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, ) from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, Footprint3DModel, FootprintPad, LetterSpacing, - LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, Shape, ShapeRadius, Size, - SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_qfp.py)' @@ -45,23 +88,23 @@ # Excess as a function of pitch according to IPC-7351C. Excess = namedtuple('Excess', 'toe heel side courtyard') DENSITY_LEVEL_A = { # Most - 1.00: Excess(0.35, 0.45, 0.06, 0.40), - 0.80: Excess(0.30, 0.40, 0.05, 0.40), - 0.65: Excess(0.25, 0.35, 0.03, 0.40), - 0.50: Excess(0.20, 0.30, 0.00, 0.40), - 0.40: Excess(0.20, 0.30, -0.01, 0.40) + 1.00: Excess(0.35, 0.45, 0.06, 0.40), + 0.80: Excess(0.30, 0.40, 0.05, 0.40), + 0.65: Excess(0.25, 0.35, 0.03, 0.40), + 0.50: Excess(0.20, 0.30, 0.00, 0.40), + 0.40: Excess(0.20, 0.30, -0.01, 0.40), } DENSITY_LEVEL_B = { # Nominal - 1.00: Excess(0.30, 0.40, 0.05, 0.20), - 0.80: Excess(0.25, 0.35, 0.04, 0.20), - 0.65: Excess(0.20, 0.30, 0.02, 0.20), + 1.00: Excess(0.30, 0.40, 0.05, 0.20), + 0.80: Excess(0.25, 0.35, 0.04, 0.20), + 0.65: Excess(0.20, 0.30, 0.02, 0.20), 0.50: Excess(0.15, 0.25, -0.01, 0.20), - 0.40: Excess(0.15, 0.25, -0.02, 0.20) + 0.40: Excess(0.15, 0.25, -0.02, 0.20), } DENSITY_LEVEL_C = { # Least - 1.00: Excess(0.25, 0.35, 0.04, 0.10), - 0.80: Excess(0.20, 0.30, 0.03, 0.10), - 0.65: Excess(0.15, 0.25, 0.01, 0.10), + 1.00: Excess(0.25, 0.35, 0.04, 0.10), + 0.80: Excess(0.20, 0.30, 0.03, 0.10), + 0.65: Excess(0.15, 0.25, 0.01, 0.10), 0.50: Excess(0.10, 0.20, -0.02, 0.10), 0.40: Excess(0.10, 0.20, -0.03, 0.10), } @@ -128,13 +171,22 @@ def description(self) -> str: full_name = 'Quad Flat Package (QFP)' else: raise ValueError('Invalid name: {}'.format(self.name)) - return '{}-pin {}, standardized by JEDEC in MS-026.\n\n' \ - 'Pitch: {} mm\nBody size: {}x{} mm\nLead span: {}x{} mm\n' \ - 'Nominal height: {} mm\nMax height: {} mm\n\nGenerated with {}'.format( - self.lead_count, full_name, self.pitch, self.body_size_x, - self.body_size_y, self.lead_span_x, self.lead_span_y, - self.height_nom, self.height_max, generator, - ) + return ( + '{}-pin {}, standardized by JEDEC in MS-026.\n\n' + 'Pitch: {} mm\nBody size: {}x{} mm\nLead span: {}x{} mm\n' + 'Nominal height: {} mm\nMax height: {} mm\n\nGenerated with {}'.format( + self.lead_count, + full_name, + self.pitch, + self.body_size_x, + self.body_size_y, + self.lead_span_x, + self.lead_span_y, + self.height_nom, + self.height_max, + generator, + ) + ) def excess_by_density(self, density: str) -> Excess: """ @@ -164,6 +216,7 @@ class LTQfpConfig: """ Generate the different L/T height variants for a certain base config. """ + def __init__( self, base_config: QfpConfig, # The base config. Height will be overwritten. @@ -176,7 +229,7 @@ def __init__( def get_configs(self) -> List[QfpConfig]: configs = [] - for (variation, height_nom, height_max, prefix) in [ + for variation, height_nom, height_max, prefix in [ (self.variation_t, 1.00, 1.20, 'T'), (self.variation_l, 1.40, 1.60, 'L'), ]: @@ -194,50 +247,40 @@ def get_configs(self) -> List[QfpConfig]: JEDEC_CONFIGS = [ # May contain any type that has a `get_configs(self) -> List[QfpConfig]` method # Datasheet designators D1 E1 A e D E b # Description body-x,y ptch pin span-x,y - - LTQfpConfig(QfpConfig('QFP', 4.0, 4.0, -1, -1, 0.65, 20, 6.0, 6.0, 0.32, ''), 'AKA', 'BKA'), - LTQfpConfig(QfpConfig('QFP', 4.0, 4.0, -1, -1, 0.50, 24, 6.0, 6.0, 0.22, ''), 'AKB', 'BKB'), - LTQfpConfig(QfpConfig('QFP', 4.0, 4.0, -1, -1, 0.40, 32, 6.0, 6.0, 0.18, ''), 'AKC', 'BKC'), - - LTQfpConfig(QfpConfig('QFP', 5.0, 5.0, -1, -1, 0.50, 32, 7.0, 7.0, 0.22, ''), 'AAA', 'BAA'), - LTQfpConfig(QfpConfig('QFP', 5.0, 5.0, -1, -1, 0.40, 40, 7.0, 7.0, 0.18, ''), 'AAB', 'BAB'), - - LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.80, 32, 9.0, 9.0, 0.37, ''), 'ABA', 'BBA'), - LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.65, 40, 9.0, 9.0, 0.32, ''), 'ABB', 'BBB'), - LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.50, 48, 9.0, 9.0, 0.22, ''), 'ABC', 'BBC'), - LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.40, 64, 9.0, 9.0, 0.18, ''), 'ABD', 'BBD'), - - LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 1.00, 36, 12.0, 12.0, 0.42, ''), 'ACA', 'BCA'), - LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.80, 44, 12.0, 12.0, 0.37, ''), 'ACB', 'BCB'), - LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.65, 52, 12.0, 12.0, 0.32, ''), 'ACC', 'BCC'), - LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.50, 64, 12.0, 12.0, 0.22, ''), 'ACD', 'BCD'), - LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.40, 80, 12.0, 12.0, 0.18, ''), 'ACE', 'BCE'), - - LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 1.00, 44, 14.0, 14.0, 0.42, ''), 'ADA', 'BDA'), - LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.80, 52, 14.0, 14.0, 0.37, ''), 'ADB', 'BDB'), - LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.65, 64, 14.0, 14.0, 0.32, ''), 'ADC', 'BDC'), - LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.50, 80, 14.0, 14.0, 0.22, ''), 'ADD', 'BDD'), + LTQfpConfig(QfpConfig('QFP', 4.0, 4.0, -1, -1, 0.65, 20, 6.0, 6.0, 0.32, ''), 'AKA', 'BKA'), + LTQfpConfig(QfpConfig('QFP', 4.0, 4.0, -1, -1, 0.50, 24, 6.0, 6.0, 0.22, ''), 'AKB', 'BKB'), + LTQfpConfig(QfpConfig('QFP', 4.0, 4.0, -1, -1, 0.40, 32, 6.0, 6.0, 0.18, ''), 'AKC', 'BKC'), + LTQfpConfig(QfpConfig('QFP', 5.0, 5.0, -1, -1, 0.50, 32, 7.0, 7.0, 0.22, ''), 'AAA', 'BAA'), + LTQfpConfig(QfpConfig('QFP', 5.0, 5.0, -1, -1, 0.40, 40, 7.0, 7.0, 0.18, ''), 'AAB', 'BAB'), + LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.80, 32, 9.0, 9.0, 0.37, ''), 'ABA', 'BBA'), + LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.65, 40, 9.0, 9.0, 0.32, ''), 'ABB', 'BBB'), + LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.50, 48, 9.0, 9.0, 0.22, ''), 'ABC', 'BBC'), + LTQfpConfig(QfpConfig('QFP', 7.0, 7.0, -1, -1, 0.40, 64, 9.0, 9.0, 0.18, ''), 'ABD', 'BBD'), + LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 1.00, 36, 12.0, 12.0, 0.42, ''), 'ACA', 'BCA'), + LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.80, 44, 12.0, 12.0, 0.37, ''), 'ACB', 'BCB'), + LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.65, 52, 12.0, 12.0, 0.32, ''), 'ACC', 'BCC'), + LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.50, 64, 12.0, 12.0, 0.22, ''), 'ACD', 'BCD'), + LTQfpConfig(QfpConfig('QFP', 10.0, 10.0, -1, -1, 0.40, 80, 12.0, 12.0, 0.18, ''), 'ACE', 'BCE'), + LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 1.00, 44, 14.0, 14.0, 0.42, ''), 'ADA', 'BDA'), + LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.80, 52, 14.0, 14.0, 0.37, ''), 'ADB', 'BDB'), + LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.65, 64, 14.0, 14.0, 0.32, ''), 'ADC', 'BDC'), + LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.50, 80, 14.0, 14.0, 0.22, ''), 'ADD', 'BDD'), LTQfpConfig(QfpConfig('QFP', 12.0, 12.0, -1, -1, 0.40, 100, 14.0, 14.0, 0.18, ''), 'ADE', 'BDE'), - - LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 1.00, 52, 16.0, 16.0, 0.42, ''), 'AEA', 'BEA'), - LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 0.80, 64, 16.0, 16.0, 0.37, ''), 'AEB', 'BEB'), - LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 0.65, 80, 16.0, 16.0, 0.32, ''), 'AEC', 'BEC'), + LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 1.00, 52, 16.0, 16.0, 0.42, ''), 'AEA', 'BEA'), + LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 0.80, 64, 16.0, 16.0, 0.37, ''), 'AEB', 'BEB'), + LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 0.65, 80, 16.0, 16.0, 0.32, ''), 'AEC', 'BEC'), LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 0.50, 100, 16.0, 16.0, 0.22, ''), 'AED', 'BED'), LTQfpConfig(QfpConfig('QFP', 14.0, 14.0, -1, -1, 0.40, 120, 16.0, 16.0, 0.18, ''), 'AEE', 'BEE'), - LTQfpConfig(QfpConfig('QFP', 20.0, 20.0, -1, -1, 0.65, 112, 22.0, 22.0, 0.32, ''), 'AFA', 'BFA'), LTQfpConfig(QfpConfig('QFP', 20.0, 20.0, -1, -1, 0.50, 144, 22.0, 22.0, 0.22, ''), 'AFB', 'BFB'), LTQfpConfig(QfpConfig('QFP', 20.0, 20.0, -1, -1, 0.40, 176, 22.0, 22.0, 0.18, ''), 'AFC', 'BFC'), - LTQfpConfig(QfpConfig('QFP', 24.0, 24.0, -1, -1, 0.50, 176, 26.0, 26.0, 0.22, ''), 'AGA', 'BGA'), LTQfpConfig(QfpConfig('QFP', 24.0, 24.0, -1, -1, 0.40, 216, 26.0, 26.0, 0.18, ''), 'AGB', 'BGB'), - # LTQfpConfig(QfpConfig('QFP', 20.0, 14.0, -1, -1, 0.65, 100, 22.0, 16.0, 0.32, ''), 'AHA', 'BHA'), # LTQfpConfig(QfpConfig('QFP', 20.0, 14.0, -1, -1, 0.50, 128, 22.0, 16.0, 0.22, ''), 'AHB', 'BHB'), - - LTQfpConfig(QfpConfig('QFP', 28.0, 28.0, -1, -1, 0.65, 160, 30.0, 30.0, 0.32, ''), None, 'BJA'), - LTQfpConfig(QfpConfig('QFP', 28.0, 28.0, -1, -1, 0.50, 208, 30.0, 30.0, 0.22, ''), None, 'BJB'), - LTQfpConfig(QfpConfig('QFP', 28.0, 28.0, -1, -1, 0.40, 256, 30.0, 30.0, 0.18, ''), None, 'BJC'), + LTQfpConfig(QfpConfig('QFP', 28.0, 28.0, -1, -1, 0.65, 160, 30.0, 30.0, 0.32, ''), None, 'BJA'), + LTQfpConfig(QfpConfig('QFP', 28.0, 28.0, -1, -1, 0.50, 208, 30.0, 30.0, 0.22, ''), None, 'BJB'), + LTQfpConfig(QfpConfig('QFP', 28.0, 28.0, -1, -1, 0.40, 256, 30.0, 30.0, 0.18, ''), None, 'BJC'), ] @@ -390,7 +433,9 @@ def add_footprint_variant( excess = config.excess_by_density(density_level) # Lead contact offsets - lead_contact_x_offset = config.lead_span_x / 2 - config.lead_contact_length # this is the inner side of the contact area + lead_contact_x_offset = ( + config.lead_span_x / 2 - config.lead_contact_length + ) # this is the inner side of the contact area # Position of the first and last pad pos_first = get_pad_coords(1, config.lead_count, config.pitch, lead_contact_x_offset) @@ -413,21 +458,23 @@ def add_footprint_variant( pad_center_offset_x = config.lead_span_x / 2 - pad_length / 2 + excess.toe pos = get_pad_coords(p, config.lead_count, config.pitch, pad_center_offset_x) pad_rotation = 90.0 if pos.orientation == 'horizontal' else 0.0 - footprint.add_pad(FootprintPad( - uuid=pad_uuid, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(pos.x, pos.y), - rotation=Rotation(pad_rotation), - size=Size(pad_width, pad_length), - radius=ShapeRadius(0.5), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(pad_uuid), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=pad_uuid, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(pos.x, pos.y), + rotation=Rotation(pad_rotation), + size=Size(pad_width, pad_length), + radius=ShapeRadius(0.5), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(pad_uuid), + holes=[], + ) + ) # Documentation: Leads for p in range(1, config.lead_count + 1): @@ -447,20 +494,22 @@ def add_footprint_variant( x2 = pos.x + config.lead_width / 2 y1 = pos.y y2 = pos.y + sign(pos.y) * config.lead_contact_length - footprint.add_polygon(Polygon( - uuid=lead_uuid_ctct, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x1, y1), Angle(0)), - Vertex(Position(x2, y1), Angle(0)), - Vertex(Position(x2, y2), Angle(0)), - Vertex(Position(x1, y2), Angle(0)), - Vertex(Position(x1, y1), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=lead_uuid_ctct, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x1, y1), Angle(0)), + Vertex(Position(x2, y1), Angle(0)), + Vertex(Position(x2, y2), Angle(0)), + Vertex(Position(x1, y2), Angle(0)), + Vertex(Position(x1, y1), Angle(0)), + ], + ) + ) # Vertical projection, between contact area and body if pos.orientation == 'horizontal': x1 = sign(pos.x) * config.body_size_x / 2 @@ -473,20 +522,22 @@ def add_footprint_variant( x2 = pos.x + config.lead_width / 2 y1 = sign(pos.y) * config.body_size_y / 2 y2 = pos.y - footprint.add_polygon(Polygon( - uuid=lead_uuid_proj, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x1, y1), Angle(0)), - Vertex(Position(x2, y1), Angle(0)), - Vertex(Position(x2, y2), Angle(0)), - Vertex(Position(x1, y2), Angle(0)), - Vertex(Position(x1, y1), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=lead_uuid_proj, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x1, y1), Angle(0)), + Vertex(Position(x2, y1), Angle(0)), + Vertex(Position(x2, y2), Angle(0)), + Vertex(Position(x1, y2), Angle(0)), + Vertex(Position(x1, y1), Angle(0)), + ], + ) + ) # Silkscreen: 1 per quadrant # (Quadrant 1 is at the top right, the rest follows CCW) @@ -501,44 +552,47 @@ def add_footprint_variant( # Pin 1 marking line if quadrant == 2: - vertices.append(( - config.lead_span_x / 2 + excess.toe - line_width / 2, - y_min, - )) + vertices.append( + ( + config.lead_span_x / 2 + excess.toe - line_width / 2, + y_min, + ) + ) sign_x = 1 if quadrant in [1, 4] else -1 sign_y = 1 if quadrant in [1, 2] else -1 - footprint.add_polygon(Polygon( - uuid=uuid, - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(sign_x * x, sign_y * y), Angle(0)) - for (x, y) in vertices - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid, + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[Vertex(Position(sign_x * x, sign_y * y), Angle(0)) for (x, y) in vertices], + ) + ) # Documentation outline (fully inside body) outline_x_offset = config.body_size_x / 2 - line_width / 2 outline_y_offset = config.body_size_y / 2 - line_width / 2 oxo = outline_x_offset # Used for shorter code lines below :) oyo = outline_y_offset # Used for shorter code lines below :) - footprint.add_polygon(Polygon( - uuid=uuid_body, - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(oxo, oyo), Angle(0)), # NE - Vertex(Position(oxo, -oyo), Angle(0)), # SE - Vertex(Position(-oxo, -oyo), Angle(0)), # SW - Vertex(Position(-oxo, oyo), Angle(0)), # NW - Vertex(Position(oxo, oyo), Angle(0)), # NE - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_body, + layer=Layer('top_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(oxo, oyo), Angle(0)), # NE + Vertex(Position(oxo, -oyo), Angle(0)), # SE + Vertex(Position(-oxo, -oyo), Angle(0)), # SW + Vertex(Position(-oxo, oyo), Angle(0)), # NW + Vertex(Position(oxo, oyo), Angle(0)), # NE + ], + ) + ) # Documentation: Pin 1 dot pin1_dot_diameter = 0.5 @@ -570,69 +624,90 @@ def _create_outline_vertices(offset: float = 0, around_pads: bool = False) -> Li y_min += excess.side vertices = [ # Starting at top left # Top - (-x_min, y_max), ( x_min, y_max), ( x_min, y_mid), ( x_mid, y_mid), ( x_mid, y_min), + (-x_min, y_max), + (x_min, y_max), + (x_min, y_mid), + (x_mid, y_mid), + (x_mid, y_min), # Right - ( x_max, y_min), ( x_max, -y_min), ( x_mid, -y_min), ( x_mid, -y_mid), ( x_min, -y_mid), + (x_max, y_min), + (x_max, -y_min), + (x_mid, -y_min), + (x_mid, -y_mid), + (x_min, -y_mid), # Bottom - ( x_min, -y_max), (-x_min, -y_max), (-x_min, -y_mid), (-x_mid, -y_mid), (-x_mid, -y_min), + (x_min, -y_max), + (-x_min, -y_max), + (-x_min, -y_mid), + (-x_mid, -y_mid), + (-x_mid, -y_min), # Left - (-x_max, -y_min), (-x_max, y_min), (-x_mid, y_min), (-x_mid, y_mid), (-x_min, y_mid), - ] - return [ - Vertex(Position(x + sign(x) * offset, y + sign(y) * offset), Angle(0)) - for (x, y) in vertices + (-x_max, -y_min), + (-x_max, y_min), + (-x_mid, y_min), + (-x_mid, y_mid), + (-x_min, y_mid), ] + return [Vertex(Position(x + sign(x) * offset, y + sign(y) * offset), Angle(0)) for (x, y) in vertices] # Package Outline - footprint.add_polygon(Polygon( - uuid=uuid_outline, - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=_create_outline_vertices(), - )) + footprint.add_polygon( + Polygon( + uuid=uuid_outline, + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=_create_outline_vertices(), + ) + ) # Courtyard - footprint.add_polygon(Polygon( - uuid=uuid_courtyard, - layer=Layer('top_courtyard'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=_create_outline_vertices(offset=excess.courtyard, around_pads=True), - )) + footprint.add_polygon( + Polygon( + uuid=uuid_courtyard, + layer=Layer('top_courtyard'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=_create_outline_vertices(offset=excess.courtyard, around_pads=True), + ) + ) # Labels y_offset = config.lead_span_y / 2 + text_y_offset - footprint.add_text(StrokeText( - uuid=uuid_text_name, - layer=Layer('top_names'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, y_offset), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=uuid_text_value, - layer=Layer('top_values'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -y_offset), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=uuid_text_name, + layer=Layer('top_names'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, y_offset), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=uuid_text_value, + layer=Layer('top_values'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -y_offset), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) add_footprint_variant('density~b', 'Density Level B (median protrusion)', 'B') add_footprint_variant('density~a', 'Density Level A (max protrusion)', 'A') @@ -675,29 +750,31 @@ def generate_3d( dot_center = ( -(config.body_size_x / 2) + dot_position, (config.body_size_y / 2) - dot_position, - body_standoff + body_height - dot_depth + body_standoff + body_height - dot_depth, ) - body = cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) \ - .box(config.body_size_x, config.body_size_y, body_height) \ - .edges().chamfer(body_chamfer) \ - .workplane(origin=(dot_center[0], dot_center[1]), offset=(body_height / 2) - dot_depth) \ + body = ( + cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) + .box(config.body_size_x, config.body_size_y, body_height) + .edges() + .chamfer(body_chamfer) + .workplane(origin=(dot_center[0], dot_center[1]), offset=(body_height / 2) - dot_depth) .cylinder(5, dot_diameter / 2, centered=(True, True, False), combine='cut') - dot = cq.Workplane('XY', origin=dot_center) \ - .cylinder(0.05, dot_diameter / 2, centered=(True, True, False)) - leg_path = cq.Workplane("XZ") \ - .hLine(config.lead_contact_length - (leg_height / 2) - bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) \ - .vLine(leg_z_top - leg_height - (2 * bend_radius)) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .hLine(config.lead_span_x - (2 * bend_radius) - (2 * config.lead_contact_length) + leg_height) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ - .vLine(-(leg_z_top - leg_height - (2 * bend_radius))) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=1) \ + ) + dot = cq.Workplane('XY', origin=dot_center).cylinder(0.05, dot_diameter / 2, centered=(True, True, False)) + leg_path = ( + cq.Workplane('XZ') + .hLine(config.lead_contact_length - (leg_height / 2) - bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) + .vLine(leg_z_top - leg_height - (2 * bend_radius)) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .hLine(config.lead_span_x - (2 * bend_radius) - (2 * config.lead_contact_length) + leg_height) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) + .vLine(-(leg_z_top - leg_height - (2 * bend_radius))) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=1) .hLine(config.lead_contact_length - (leg_height / 2) - bend_radius) - leg = cq.Workplane("ZY") \ - .rect(leg_height, config.lead_width) \ - .sweep(leg_path) + ) + leg = cq.Workplane('ZY').rect(leg_height, config.lead_width).sweep(leg_path) assert config.lead_span_x == config.lead_span_y # Only one leg object! assembly = StepAssembly(full_name) @@ -707,21 +784,28 @@ def generate_3d( for i in range(0, config.lead_count // 2): if i < config.lead_count // 4: # Horizontal leads - location = cq.Location(( - -config.lead_span_x / 2, - lead_offset - i * config.pitch, - leg_height / 2, - )) + location = cq.Location( + ( + -config.lead_span_x / 2, + lead_offset - i * config.pitch, + leg_height / 2, + ) + ) else: # Vertical leads - location = cq.Location(( - -lead_offset + (i - config.lead_count // 4) * config.pitch, - -config.lead_span_y / 2, - leg_height / 2, - ), (0, 0, 1), 90) + location = cq.Location( + ( + -lead_offset + (i - config.lead_count // 4) * config.pitch, + -config.lead_span_y / 2, + leg_height / 2, + ), + (0, 0, 1), + 90, + ) assembly.add_body( leg, - 'leg-{}'.format(i + 1), StepColor.LEAD_SMT, + 'leg-{}'.format(i + 1), + StepColor.LEAD_SMT, location=location, ) diff --git a/generate_so.py b/generate_so.py index 34350d5..e0dab98 100644 --- a/generate_so.py +++ b/generate_so.py @@ -7,6 +7,7 @@ - TSOP (JEDEC MS-024) """ + import sys from os import path from uuid import uuid4 @@ -16,14 +17,56 @@ from common import format_ipc_dimension as fd from common import init_cache, now, save_cache from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Value, Version, Vertex, Width, - generate_courtyard + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Value, + Version, + Vertex, + Width, + generate_courtyard, ) from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, Footprint3DModel, FootprintPad, LetterSpacing, - LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, Shape, ShapeRadius, Size, - SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) generator = 'librepcb-parts-generator (generate_so.py)' @@ -163,7 +206,7 @@ def generate_pkg( lead_width=lead_width, lead_length=lead_length, variation=config.variation, - ) + "\n\nGenerated with {}".format(generator) + ) + '\n\nGenerated with {}'.format(generator) def _uuid(identifier: str) -> str: return uuid(category, full_name, identifier) @@ -179,7 +222,7 @@ def _uuid(identifier: str) -> str: uuid=uuid_pkg, name=Name(full_name), description=Description(full_description), - keywords=Keywords("soic{},so{},{}".format(pin_count, pin_count, keywords)), + keywords=Keywords('soic{},so{},{}'.format(pin_count, pin_count, keywords)), author=Author(author), version=Version(version), created=Created(create_date or now()), @@ -241,21 +284,23 @@ def add_footprint_variant( y = -get_y(p - mid, pin_count // 2, pitch, False) pxo = pad_x_offset pad_uuid = uuid_pads[p - 1] - footprint.add_pad(FootprintPad( - uuid=pad_uuid, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(pxo, y), - rotation=Rotation(0), - size=Size(pad_length, pad_width), - radius=ShapeRadius(0.5), - stop_mask=StopMaskConfig.AUTO, - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(pad_uuid), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=pad_uuid, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(pxo, y), + rotation=Rotation(0), + size=Size(pad_length, pad_width), + radius=ShapeRadius(0.5), + stop_mask=StopMaskConfig.AUTO, + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(pad_uuid), + holes=[], + ) + ) max_y_copper = max(max_y_copper, y + pad_width / 2) max_x = max(max_x, total_width / 2 + pad_toe) @@ -278,35 +323,39 @@ def add_footprint_variant( lead_uuid_ctct = uuid_leads1[p - 1] # Contact area lead_uuid_proj = uuid_leads2[p - 1] # Vertical projection # Contact area - footprint.add_polygon(Polygon( - uuid=lead_uuid_ctct, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(lcxo_min, y_max), Angle(0)), - Vertex(Position(lcxo_max, y_max), Angle(0)), - Vertex(Position(lcxo_max, y_min), Angle(0)), - Vertex(Position(lcxo_min, y_min), Angle(0)), - Vertex(Position(lcxo_min, y_max), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=lead_uuid_ctct, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(lcxo_min, y_max), Angle(0)), + Vertex(Position(lcxo_max, y_max), Angle(0)), + Vertex(Position(lcxo_max, y_min), Angle(0)), + Vertex(Position(lcxo_min, y_min), Angle(0)), + Vertex(Position(lcxo_min, y_max), Angle(0)), + ], + ) + ) # Vertical projection, between contact area and body - footprint.add_polygon(Polygon( - uuid=lead_uuid_proj, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(body_side, y_max), Angle(0)), - Vertex(Position(lcxo_min, y_max), Angle(0)), - Vertex(Position(lcxo_min, y_min), Angle(0)), - Vertex(Position(body_side, y_min), Angle(0)), - Vertex(Position(body_side, y_max), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=lead_uuid_proj, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(body_side, y_max), Angle(0)), + Vertex(Position(lcxo_min, y_max), Angle(0)), + Vertex(Position(lcxo_min, y_min), Angle(0)), + Vertex(Position(body_side, y_min), Angle(0)), + Vertex(Position(body_side, y_max), Angle(0)), + ], + ) + ) # Silkscreen (fully outside body) # Ensure minimum clearance between copper and silkscreen @@ -315,48 +364,54 @@ def add_footprint_variant( y_min = -body_length / 2 - line_width / 2 - y_offset short_x_offset = body_width / 2 - line_width / 2 long_x_offset = total_width / 2 - line_width / 2 + pad_toe # Pin1 marking - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen_top, - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-long_x_offset, y_max), Angle(0)), - Vertex(Position(short_x_offset, y_max), Angle(0)), - ], - )) - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen_bot, - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-short_x_offset, y_min), Angle(0)), - Vertex(Position(short_x_offset, y_min), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen_top, + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-long_x_offset, y_max), Angle(0)), + Vertex(Position(short_x_offset, y_max), Angle(0)), + ], + ) + ) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen_bot, + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-short_x_offset, y_min), Angle(0)), + Vertex(Position(short_x_offset, y_min), Angle(0)), + ], + ) + ) # Documentation body body_x_offset = body_width / 2 - line_width / 2 y_max = body_length / 2 - line_width / 2 y_min = -body_length / 2 + line_width / 2 oxo = body_x_offset # Used for shorter code lines below :) - footprint.add_polygon(Polygon( - uuid=uuid_body, - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(True), - vertices=[ - Vertex(Position(-oxo, y_max), Angle(0)), - Vertex(Position(oxo, y_max), Angle(0)), - Vertex(Position(oxo, y_min), Angle(0)), - Vertex(Position(-oxo, y_min), Angle(0)), - Vertex(Position(-oxo, y_max), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_body, + layer=Layer('top_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(True), + vertices=[ + Vertex(Position(-oxo, y_max), Angle(0)), + Vertex(Position(oxo, y_max), Angle(0)), + Vertex(Position(oxo, y_min), Angle(0)), + Vertex(Position(-oxo, y_min), Angle(0)), + Vertex(Position(-oxo, y_max), Angle(0)), + ], + ) + ) max_y = max(max_y, body_length / 2) # Body contour # Documentation: Pin 1 dot @@ -378,61 +433,69 @@ def add_footprint_variant( # Package Outline dx = config.total_width / 2 dy = config.body_length / 2 - footprint.add_polygon(Polygon( - uuid=uuid_outline, - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy), Angle(0)), # NW - Vertex(Position(dx, dy), Angle(0)), # NE - Vertex(Position(dx, -dy), Angle(0)), # SE - Vertex(Position(-dx, -dy), Angle(0)), # SW - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_outline, + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy), Angle(0)), # NW + Vertex(Position(dx, dy), Angle(0)), # NE + Vertex(Position(dx, -dy), Angle(0)), # SE + Vertex(Position(-dx, -dy), Angle(0)), # SW + ], + ) + ) # Courtyard courtyard_excess = get_by_density(pitch, density_level, 'courtyard') - footprint.add_polygon(generate_courtyard( - uuid=uuid_courtyard, - max_x=max_x, - max_y=max_y, - excess_x=courtyard_excess, - excess_y=courtyard_excess, - )) + footprint.add_polygon( + generate_courtyard( + uuid=uuid_courtyard, + max_x=max_x, + max_y=max_y, + excess_x=courtyard_excess, + excess_y=courtyard_excess, + ) + ) # Labels y_max = body_length / 2 + 1.27 y_min = -body_length / 2 - 1.27 - footprint.add_text(StrokeText( - uuid=uuid_text_name, - layer=Layer('top_names'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0.0, y_max), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=uuid_text_value, - layer=Layer('top_values'), - height=Height(pkg_text_height), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, y_min), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=uuid_text_name, + layer=Layer('top_names'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0.0, y_max), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=uuid_text_value, + layer=Layer('top_values'), + height=Height(pkg_text_height), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, y_min), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) add_footprint_variant('density~b', 'Density Level B (median protrusion)', 'B') add_footprint_variant('density~a', 'Density Level A (max protrusion)', 'A') @@ -441,8 +504,7 @@ def add_footprint_variant( # Generate 3D models uuid_3d = uuid('pkg', full_name, '3d') if generate_3d_models: - generate_3d(library, full_name, uuid_pkg, uuid_3d, config, - lead_width, lead_contact_length) + generate_3d(library, full_name, uuid_pkg, uuid_3d, config, lead_width, lead_contact_length) package.add_3d_model(Package3DModel(uuid_3d, Name(full_name))) for footprint in package.footprints: footprint.add_3d_model(Footprint3DModel(uuid_3d)) @@ -478,29 +540,31 @@ def generate_3d( dot_center = ( -(config.body_width / 2) + dot_position, (config.body_length / 2) - dot_position, - body_standoff + body_height - dot_depth + body_standoff + body_height - dot_depth, ) - body = cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) \ - .box(config.body_width, config.body_length, body_height) \ - .edges().chamfer(body_chamfer) \ - .workplane(origin=(dot_center[0], dot_center[1]), offset=(body_height / 2) - dot_depth) \ + body = ( + cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) + .box(config.body_width, config.body_length, body_height) + .edges() + .chamfer(body_chamfer) + .workplane(origin=(dot_center[0], dot_center[1]), offset=(body_height / 2) - dot_depth) .cylinder(5, dot_diameter / 2, centered=(True, True, False), combine='cut') - dot = cq.Workplane('XY', origin=dot_center) \ - .cylinder(0.05, dot_diameter / 2, centered=(True, True, False)) - leg_path = cq.Workplane("XZ") \ - .hLine(lead_contact_length - (leg_height / 2) - bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) \ - .vLine(leg_z_top - leg_height - (2 * bend_radius)) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .hLine(config.total_width - (2 * bend_radius) - (2 * lead_contact_length) + leg_height) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ - .vLine(-(leg_z_top - leg_height - (2 * bend_radius))) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=1) \ + ) + dot = cq.Workplane('XY', origin=dot_center).cylinder(0.05, dot_diameter / 2, centered=(True, True, False)) + leg_path = ( + cq.Workplane('XZ') .hLine(lead_contact_length - (leg_height / 2) - bend_radius) - leg = cq.Workplane("ZY") \ - .rect(leg_height, lead_width) \ - .sweep(leg_path) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) + .vLine(leg_z_top - leg_height - (2 * bend_radius)) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .hLine(config.total_width - (2 * bend_radius) - (2 * lead_contact_length) + leg_height) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) + .vLine(-(leg_z_top - leg_height - (2 * bend_radius))) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=1) + .hLine(lead_contact_length - (leg_height / 2) - bend_radius) + ) + leg = cq.Workplane('ZY').rect(leg_height, lead_width).sweep(leg_path) assembly = StepAssembly(full_name) assembly.add_body(body, 'body', StepColor.IC_BODY) @@ -509,12 +573,15 @@ def generate_3d( for i in range(0, config.pin_count // 2): assembly.add_body( leg, - 'leg-{}'.format(i + 1), StepColor.LEAD_SMT, - location=cq.Location(( - -config.total_width / 2, - y1 - i * config.pitch, - leg_height / 2, - )) + 'leg-{}'.format(i + 1), + StepColor.LEAD_SMT, + location=cq.Location( + ( + -config.total_width / 2, + y1 - i * config.pitch, + leg_height / 2, + ) + ), ) # Save without fusing for massively better minification! @@ -549,8 +616,8 @@ def generate_3d( author='Danilo B.', name='SOIC{pitch}P762X{height}-{pin_count}', description='{pin_count}-pin Small Outline Integrated Circuit (SOIC), ' - 'standardized by EIAJ.\n\n' - 'Pitch: {pitch:.2f} mm\nNominal width: 7.62mm\nHeight: {height:.2f}mm', + 'standardized by EIAJ.\n\n' + 'Pitch: {pitch:.2f} mm\nNominal width: 7.62mm\nHeight: {height:.2f}mm', configs=configs, lead_width_lookup={1.27: 0.4}, lead_contact_length=0.8, @@ -573,8 +640,8 @@ def generate_3d( author='Danilo B.', name='SOIC{pitch}P1524X{height}-{pin_count}', description='{pin_count}-pin Small Outline Integrated Circuit (SOIC), ' - 'standardized by EIAJ.\n\n' - 'Pitch: {pitch:.2f} mm\nNominal width: 15.24mm\nHeight: {height:.2f}mm', + 'standardized by EIAJ.\n\n' + 'Pitch: {pitch:.2f} mm\nNominal width: 15.24mm\nHeight: {height:.2f}mm', configs=configs, lead_width_lookup={1.27: 0.4}, lead_contact_length=0.8, @@ -597,8 +664,8 @@ def generate_3d( author='Danilo B.', name='SOIC{pitch}P600X{height}-{pin_count}', description='{pin_count}-pin Small Outline Integrated Circuit (SOIC), ' - 'standardized by JEDEC (MS-012G).\n\n' - 'Pitch: {pitch:.2f} mm\nNominal width: 6.00mm\nHeight: {height:.2f}mm', + 'standardized by JEDEC (MS-012G).\n\n' + 'Pitch: {pitch:.2f} mm\nNominal width: 6.00mm\nHeight: {height:.2f}mm', configs=configs, lead_width_lookup={1.27: 0.45}, lead_contact_length=0.835, @@ -621,8 +688,8 @@ def generate_3d( author='U. Bruhin', name='SOIC{pitch}P1030X{height}-{pin_count}', description='{pin_count}-pin Small Outline Integrated Circuit (SOIC), ' - 'standardized by JEDEC (MS-013F).\n\n' - 'Pitch: {pitch:.2f} mm\nNominal width: 10.30mm\nHeight: {height:.2f}mm', + 'standardized by JEDEC (MS-013F).\n\n' + 'Pitch: {pitch:.2f} mm\nNominal width: 10.30mm\nHeight: {height:.2f}mm', configs=configs, lead_width_lookup={1.27: 0.45}, lead_contact_length=0.835, @@ -640,83 +707,79 @@ def generate_3d( # Name according to IPC7351C name='TSSOP{pin_count}P{pitch}_{body_length}X{lead_span}X{height}L{lead_length}X{lead_width}', description='{pin_count}-pin Thin-Shrink Small Outline Package (TSSOP), ' - 'standardized by JEDEC (MO-153), variation {variation}.\n\n' - 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' - 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' - 'Height: {height:.2f} mm\n' - 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', + 'standardized by JEDEC (MO-153), variation {variation}.\n\n' + 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' + 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' + 'Height: {height:.2f} mm\n' + 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', configs=[ # pin count, pitch, body length, body width, total width, height - # Symbols based on JEDEC MO-153: # N e D E1 E A - # 4.40mm body width # 0.65mm pitch - SoConfig( 8, 0.65, 3.0, 4.4, 6.4, 1.2, 'AA'), - SoConfig(14, 0.65, 5.0, 4.4, 6.4, 1.2, 'AB-1'), - SoConfig(16, 0.65, 5.0, 4.4, 6.4, 1.2, 'AB'), - SoConfig(20, 0.65, 6.5, 4.4, 6.4, 1.2, 'AC'), - SoConfig(24, 0.65, 7.8, 4.4, 6.4, 1.2, 'AD'), - SoConfig(28, 0.65, 9.7, 4.4, 6.4, 1.2, 'AE'), + SoConfig(8, 0.65, 3.0, 4.4, 6.4, 1.2, 'AA'), + SoConfig(14, 0.65, 5.0, 4.4, 6.4, 1.2, 'AB-1'), + SoConfig(16, 0.65, 5.0, 4.4, 6.4, 1.2, 'AB'), + SoConfig(20, 0.65, 6.5, 4.4, 6.4, 1.2, 'AC'), + SoConfig(24, 0.65, 7.8, 4.4, 6.4, 1.2, 'AD'), + SoConfig(28, 0.65, 9.7, 4.4, 6.4, 1.2, 'AE'), # 0.5mm pitch - SoConfig(20, 0.50, 5.0, 4.4, 6.4, 1.2, 'BA'), - SoConfig(24, 0.50, 6.5, 4.4, 6.4, 1.2, 'BB'), - SoConfig(28, 0.50, 7.8, 4.4, 6.4, 1.2, 'BC'), - SoConfig(30, 0.50, 7.8, 4.4, 6.4, 1.2, 'BC-1'), - SoConfig(36, 0.50, 9.7, 4.4, 6.4, 1.2, 'BD'), - SoConfig(38, 0.50, 9.7, 4.4, 6.4, 1.2, 'BD-1'), - SoConfig(44, 0.50, 11.0, 4.4, 6.4, 1.2, 'BE'), - SoConfig(50, 0.50, 12.5, 4.4, 6.4, 1.2, 'BF'), + SoConfig(20, 0.50, 5.0, 4.4, 6.4, 1.2, 'BA'), + SoConfig(24, 0.50, 6.5, 4.4, 6.4, 1.2, 'BB'), + SoConfig(28, 0.50, 7.8, 4.4, 6.4, 1.2, 'BC'), + SoConfig(30, 0.50, 7.8, 4.4, 6.4, 1.2, 'BC-1'), + SoConfig(36, 0.50, 9.7, 4.4, 6.4, 1.2, 'BD'), + SoConfig(38, 0.50, 9.7, 4.4, 6.4, 1.2, 'BD-1'), + SoConfig(44, 0.50, 11.0, 4.4, 6.4, 1.2, 'BE'), + SoConfig(50, 0.50, 12.5, 4.4, 6.4, 1.2, 'BF'), # 0.4mm pitch - SoConfig(24, 0.40, 5.0, 4.4, 6.4, 1.2, 'CA'), - SoConfig(32, 0.40, 6.5, 4.4, 6.4, 1.2, 'CB'), - SoConfig(36, 0.40, 7.8, 4.4, 6.4, 1.2, 'CC'), - SoConfig(48, 0.40, 9.7, 4.4, 6.4, 1.2, 'CD'), - + SoConfig(24, 0.40, 5.0, 4.4, 6.4, 1.2, 'CA'), + SoConfig(32, 0.40, 6.5, 4.4, 6.4, 1.2, 'CB'), + SoConfig(36, 0.40, 7.8, 4.4, 6.4, 1.2, 'CC'), + SoConfig(48, 0.40, 9.7, 4.4, 6.4, 1.2, 'CD'), # 6.10mm body width # 0.65mm pitch - SoConfig(24, 0.65, 7.8, 6.1, 8.1, 1.2, 'DA'), - SoConfig(28, 0.65, 9.7, 6.1, 8.1, 1.2, 'DB'), - SoConfig(30, 0.65, 9.7, 6.1, 8.1, 1.2, 'DB-1'), - SoConfig(32, 0.65, 11.0, 6.1, 8.1, 1.2, 'DC'), - SoConfig(36, 0.65, 12.5, 6.1, 8.1, 1.2, 'DD'), - SoConfig(38, 0.65, 12.5, 6.1, 8.1, 1.2, 'DD-1'), - SoConfig(40, 0.65, 14.0, 6.1, 8.1, 1.2, 'DE'), + SoConfig(24, 0.65, 7.8, 6.1, 8.1, 1.2, 'DA'), + SoConfig(28, 0.65, 9.7, 6.1, 8.1, 1.2, 'DB'), + SoConfig(30, 0.65, 9.7, 6.1, 8.1, 1.2, 'DB-1'), + SoConfig(32, 0.65, 11.0, 6.1, 8.1, 1.2, 'DC'), + SoConfig(36, 0.65, 12.5, 6.1, 8.1, 1.2, 'DD'), + SoConfig(38, 0.65, 12.5, 6.1, 8.1, 1.2, 'DD-1'), + SoConfig(40, 0.65, 14.0, 6.1, 8.1, 1.2, 'DE'), # 0.5mm pitch - SoConfig(28, 0.50, 7.8, 6.1, 8.1, 1.2, 'EA'), - SoConfig(36, 0.50, 9.7, 6.1, 8.1, 1.2, 'EB'), - SoConfig(40, 0.50, 11.0, 6.1, 8.1, 1.2, 'EC'), - SoConfig(44, 0.50, 11.0, 6.1, 8.1, 1.2, 'EC-1'), - SoConfig(48, 0.50, 12.5, 6.1, 8.1, 1.2, 'ED'), - SoConfig(56, 0.50, 14.0, 6.1, 8.1, 1.2, 'EE'), - SoConfig(64, 0.50, 17.0, 6.1, 8.1, 1.2, 'EF'), + SoConfig(28, 0.50, 7.8, 6.1, 8.1, 1.2, 'EA'), + SoConfig(36, 0.50, 9.7, 6.1, 8.1, 1.2, 'EB'), + SoConfig(40, 0.50, 11.0, 6.1, 8.1, 1.2, 'EC'), + SoConfig(44, 0.50, 11.0, 6.1, 8.1, 1.2, 'EC-1'), + SoConfig(48, 0.50, 12.5, 6.1, 8.1, 1.2, 'ED'), + SoConfig(56, 0.50, 14.0, 6.1, 8.1, 1.2, 'EE'), + SoConfig(64, 0.50, 17.0, 6.1, 8.1, 1.2, 'EF'), # 0.4mm pitch - SoConfig(36, 0.40, 7.8, 6.1, 8.1, 1.2, 'FA'), - SoConfig(48, 0.40, 9.7, 6.1, 8.1, 1.2, 'FB'), - SoConfig(52, 0.40, 11.0, 6.1, 8.1, 1.2, 'FC'), - SoConfig(56, 0.40, 12.5, 6.1, 8.1, 1.2, 'FD'), - SoConfig(64, 0.40, 14.0, 6.1, 8.1, 1.2, 'FE'), - SoConfig(80, 0.40, 17.0, 6.1, 8.1, 1.2, 'FF'), - + SoConfig(36, 0.40, 7.8, 6.1, 8.1, 1.2, 'FA'), + SoConfig(48, 0.40, 9.7, 6.1, 8.1, 1.2, 'FB'), + SoConfig(52, 0.40, 11.0, 6.1, 8.1, 1.2, 'FC'), + SoConfig(56, 0.40, 12.5, 6.1, 8.1, 1.2, 'FD'), + SoConfig(64, 0.40, 14.0, 6.1, 8.1, 1.2, 'FE'), + SoConfig(80, 0.40, 17.0, 6.1, 8.1, 1.2, 'FF'), # 8.00mm body width # 0.65mm pitch - SoConfig(28, 0.65, 9.7, 8.0, 10.0, 1.2, 'GA'), - SoConfig(32, 0.65, 11.0, 8.0, 10.0, 1.2, 'GB'), - SoConfig(36, 0.65, 12.5, 8.0, 10.0, 1.2, 'GC'), - SoConfig(40, 0.65, 14.0, 8.0, 10.0, 1.2, 'GD'), + SoConfig(28, 0.65, 9.7, 8.0, 10.0, 1.2, 'GA'), + SoConfig(32, 0.65, 11.0, 8.0, 10.0, 1.2, 'GB'), + SoConfig(36, 0.65, 12.5, 8.0, 10.0, 1.2, 'GC'), + SoConfig(40, 0.65, 14.0, 8.0, 10.0, 1.2, 'GD'), # 0.5mm pitch - SoConfig(36, 0.50, 9.7, 8.0, 10.0, 1.2, 'HA'), - SoConfig(40, 0.50, 11.0, 8.0, 10.0, 1.2, 'HB'), - SoConfig(48, 0.50, 12.5, 8.0, 10.0, 1.2, 'HC'), - SoConfig(56, 0.50, 14.0, 8.0, 10.0, 1.2, 'HD'), + SoConfig(36, 0.50, 9.7, 8.0, 10.0, 1.2, 'HA'), + SoConfig(40, 0.50, 11.0, 8.0, 10.0, 1.2, 'HB'), + SoConfig(48, 0.50, 12.5, 8.0, 10.0, 1.2, 'HC'), + SoConfig(56, 0.50, 14.0, 8.0, 10.0, 1.2, 'HD'), # 0.4mm pitch - SoConfig(48, 0.40, 9.7, 8.0, 10.0, 1.2, 'JA'), - SoConfig(52, 0.40, 11.0, 8.0, 10.0, 1.2, 'JB'), - SoConfig(56, 0.40, 12.5, 8.0, 10.0, 1.2, 'JC'), - SoConfig(60, 0.40, 12.5, 8.0, 10.0, 1.2, 'JC-1'), - SoConfig(64, 0.40, 14.0, 8.0, 10.0, 1.2, 'JD'), - SoConfig(68, 0.40, 14.0, 8.0, 10.0, 1.2, 'JD-1'), + SoConfig(48, 0.40, 9.7, 8.0, 10.0, 1.2, 'JA'), + SoConfig(52, 0.40, 11.0, 8.0, 10.0, 1.2, 'JB'), + SoConfig(56, 0.40, 12.5, 8.0, 10.0, 1.2, 'JC'), + SoConfig(60, 0.40, 12.5, 8.0, 10.0, 1.2, 'JC-1'), + SoConfig(64, 0.40, 14.0, 8.0, 10.0, 1.2, 'JD'), + SoConfig(68, 0.40, 14.0, 8.0, 10.0, 1.2, 'JD-1'), ], lead_width_lookup={ 0.65: 0.3, @@ -738,78 +801,74 @@ def generate_3d( # Name according to IPC7351C name='SSOP{pin_count}P{pitch}_{body_length}X{lead_span}X{height}L{lead_length}X{lead_width}', description='{pin_count}-pin Plastic Shrink Small Outline Package (SSOP), ' - 'standardized by JEDEC (MO-152), variation {variation}.\n\n' - 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' - 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' - 'Height: {height:.2f} mm\n' - 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', + 'standardized by JEDEC (MO-152), variation {variation}.\n\n' + 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' + 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' + 'Height: {height:.2f} mm\n' + 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', configs=[ # pin count, pitch, body length, body width, total width, height - # Symbols based on JEDEC MO-152: # N e D E1 E A - # 4.40mm body width # 0.65mm pitch - SoConfig( 8, 0.65, 3.0, 4.4, 6.4, 2.0, 'AA'), - SoConfig(14, 0.65, 5.0, 4.4, 6.4, 2.0, 'AB-1'), - SoConfig(16, 0.65, 5.0, 4.4, 6.4, 2.0, 'AB'), - SoConfig(20, 0.65, 6.5, 4.4, 6.4, 2.0, 'AC'), - SoConfig(24, 0.65, 7.8, 4.4, 6.4, 2.0, 'AD'), - SoConfig(28, 0.65, 9.7, 4.4, 6.4, 2.0, 'AE'), + SoConfig(8, 0.65, 3.0, 4.4, 6.4, 2.0, 'AA'), + SoConfig(14, 0.65, 5.0, 4.4, 6.4, 2.0, 'AB-1'), + SoConfig(16, 0.65, 5.0, 4.4, 6.4, 2.0, 'AB'), + SoConfig(20, 0.65, 6.5, 4.4, 6.4, 2.0, 'AC'), + SoConfig(24, 0.65, 7.8, 4.4, 6.4, 2.0, 'AD'), + SoConfig(28, 0.65, 9.7, 4.4, 6.4, 2.0, 'AE'), # 0.5mm pitch - SoConfig(20, 0.50, 5.0, 4.4, 6.4, 2.0, 'BA'), - SoConfig(24, 0.50, 6.5, 4.4, 6.4, 2.0, 'BB'), - SoConfig(28, 0.50, 7.8, 4.4, 6.4, 2.0, 'BC'), - SoConfig(36, 0.50, 9.7, 4.4, 6.4, 2.0, 'BD'), + SoConfig(20, 0.50, 5.0, 4.4, 6.4, 2.0, 'BA'), + SoConfig(24, 0.50, 6.5, 4.4, 6.4, 2.0, 'BB'), + SoConfig(28, 0.50, 7.8, 4.4, 6.4, 2.0, 'BC'), + SoConfig(36, 0.50, 9.7, 4.4, 6.4, 2.0, 'BD'), # 0.4mm pitch - SoConfig(24, 0.40, 5.0, 4.4, 6.4, 2.0, 'CA'), - SoConfig(32, 0.40, 6.5, 4.4, 6.4, 2.0, 'CB'), - SoConfig(36, 0.40, 7.8, 4.4, 6.4, 2.0, 'CC'), - SoConfig(48, 0.40, 9.7, 4.4, 6.4, 2.0, 'CD'), - + SoConfig(24, 0.40, 5.0, 4.4, 6.4, 2.0, 'CA'), + SoConfig(32, 0.40, 6.5, 4.4, 6.4, 2.0, 'CB'), + SoConfig(36, 0.40, 7.8, 4.4, 6.4, 2.0, 'CC'), + SoConfig(48, 0.40, 9.7, 4.4, 6.4, 2.0, 'CD'), # 6.10mm body width # 0.65mm pitch - SoConfig(24, 0.65, 7.8, 6.1, 8.1, 2.0, 'DA'), - SoConfig(28, 0.65, 9.7, 6.1, 8.1, 2.0, 'DB'), - SoConfig(30, 0.65, 9.7, 6.1, 8.1, 2.0, 'DB-1'), - SoConfig(32, 0.65, 11.0, 6.1, 8.1, 2.0, 'DC'), - SoConfig(36, 0.65, 12.5, 6.1, 8.1, 2.0, 'DD'), - SoConfig(40, 0.65, 14.0, 6.1, 8.1, 2.0, 'DE'), + SoConfig(24, 0.65, 7.8, 6.1, 8.1, 2.0, 'DA'), + SoConfig(28, 0.65, 9.7, 6.1, 8.1, 2.0, 'DB'), + SoConfig(30, 0.65, 9.7, 6.1, 8.1, 2.0, 'DB-1'), + SoConfig(32, 0.65, 11.0, 6.1, 8.1, 2.0, 'DC'), + SoConfig(36, 0.65, 12.5, 6.1, 8.1, 2.0, 'DD'), + SoConfig(40, 0.65, 14.0, 6.1, 8.1, 2.0, 'DE'), # 0.5mm pitch - SoConfig(28, 0.50, 7.8, 6.1, 8.1, 2.0, 'EA'), - SoConfig(36, 0.50, 9.7, 6.1, 8.1, 2.0, 'EB'), - SoConfig(40, 0.50, 11.0, 6.1, 8.1, 2.0, 'EC'), - SoConfig(44, 0.50, 11.0, 6.1, 8.1, 2.0, 'EC-1'), - SoConfig(48, 0.50, 12.5, 6.1, 8.1, 2.0, 'ED'), - SoConfig(56, 0.50, 14.0, 6.1, 8.1, 2.0, 'EE'), - SoConfig(64, 0.50, 17.0, 6.1, 8.1, 2.0, 'EF'), + SoConfig(28, 0.50, 7.8, 6.1, 8.1, 2.0, 'EA'), + SoConfig(36, 0.50, 9.7, 6.1, 8.1, 2.0, 'EB'), + SoConfig(40, 0.50, 11.0, 6.1, 8.1, 2.0, 'EC'), + SoConfig(44, 0.50, 11.0, 6.1, 8.1, 2.0, 'EC-1'), + SoConfig(48, 0.50, 12.5, 6.1, 8.1, 2.0, 'ED'), + SoConfig(56, 0.50, 14.0, 6.1, 8.1, 2.0, 'EE'), + SoConfig(64, 0.50, 17.0, 6.1, 8.1, 2.0, 'EF'), # 0.4mm pitch - SoConfig(36, 0.40, 7.8, 6.1, 8.1, 2.0, 'FA'), - SoConfig(48, 0.40, 9.7, 6.1, 8.1, 2.0, 'FB'), - SoConfig(52, 0.40, 11.0, 6.1, 8.1, 2.0, 'FC'), - SoConfig(56, 0.40, 12.5, 6.1, 8.1, 2.0, 'FD'), - SoConfig(64, 0.40, 14.0, 6.1, 8.1, 2.0, 'FE'), - SoConfig(80, 0.40, 17.0, 6.1, 8.1, 2.0, 'FF'), - + SoConfig(36, 0.40, 7.8, 6.1, 8.1, 2.0, 'FA'), + SoConfig(48, 0.40, 9.7, 6.1, 8.1, 2.0, 'FB'), + SoConfig(52, 0.40, 11.0, 6.1, 8.1, 2.0, 'FC'), + SoConfig(56, 0.40, 12.5, 6.1, 8.1, 2.0, 'FD'), + SoConfig(64, 0.40, 14.0, 6.1, 8.1, 2.0, 'FE'), + SoConfig(80, 0.40, 17.0, 6.1, 8.1, 2.0, 'FF'), # 8.00mm body width # 0.65mm pitch - SoConfig(28, 0.65, 9.7, 8.0, 10.0, 2.0, 'GA'), - SoConfig(32, 0.65, 11.0, 8.0, 10.0, 2.0, 'GB'), - SoConfig(36, 0.65, 12.5, 8.0, 10.0, 2.0, 'GC'), - SoConfig(40, 0.65, 14.0, 8.0, 10.0, 2.0, 'GD'), + SoConfig(28, 0.65, 9.7, 8.0, 10.0, 2.0, 'GA'), + SoConfig(32, 0.65, 11.0, 8.0, 10.0, 2.0, 'GB'), + SoConfig(36, 0.65, 12.5, 8.0, 10.0, 2.0, 'GC'), + SoConfig(40, 0.65, 14.0, 8.0, 10.0, 2.0, 'GD'), # 0.5mm pitch - SoConfig(36, 0.50, 9.7, 8.0, 10.0, 2.0, 'HA'), - SoConfig(40, 0.50, 11.0, 8.0, 10.0, 2.0, 'HB'), - SoConfig(48, 0.50, 12.5, 8.0, 10.0, 2.0, 'HC'), - SoConfig(56, 0.50, 14.0, 8.0, 10.0, 2.0, 'HD'), + SoConfig(36, 0.50, 9.7, 8.0, 10.0, 2.0, 'HA'), + SoConfig(40, 0.50, 11.0, 8.0, 10.0, 2.0, 'HB'), + SoConfig(48, 0.50, 12.5, 8.0, 10.0, 2.0, 'HC'), + SoConfig(56, 0.50, 14.0, 8.0, 10.0, 2.0, 'HD'), # 0.4mm pitch - SoConfig(48, 0.40, 9.7, 8.0, 10.0, 2.0, 'JA'), - SoConfig(52, 0.40, 11.0, 8.0, 10.0, 2.0, 'JB'), - SoConfig(56, 0.40, 12.5, 8.0, 10.0, 2.0, 'JC'), - SoConfig(60, 0.40, 12.5, 8.0, 10.0, 2.0, 'JC-1'), - SoConfig(64, 0.40, 14.0, 8.0, 10.0, 2.0, 'JD'), - SoConfig(68, 0.40, 14.0, 8.0, 10.0, 2.0, 'JD-1'), + SoConfig(48, 0.40, 9.7, 8.0, 10.0, 2.0, 'JA'), + SoConfig(52, 0.40, 11.0, 8.0, 10.0, 2.0, 'JB'), + SoConfig(56, 0.40, 12.5, 8.0, 10.0, 2.0, 'JC'), + SoConfig(60, 0.40, 12.5, 8.0, 10.0, 2.0, 'JC-1'), + SoConfig(64, 0.40, 14.0, 8.0, 10.0, 2.0, 'JD'), + SoConfig(68, 0.40, 14.0, 8.0, 10.0, 2.0, 'JD-1'), ], lead_width_lookup={ 0.65: 0.30, @@ -829,24 +888,22 @@ def generate_3d( # Name according to IPC7351C name='SSOP{pin_count}P{pitch}_{body_length}X{lead_span}X{height}L{lead_length}X{lead_width}', description='{pin_count}-pin Plastic Shrink Small Outline Package (SSOP), ' - 'standardized by JEDEC (MO-150), variation {variation}.\n\n' - 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' - 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' - 'Height: {height:.2f} mm\n' - 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', + 'standardized by JEDEC (MO-150), variation {variation}.\n\n' + 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' + 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' + 'Height: {height:.2f} mm\n' + 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', configs=[ # pin count, pitch, body length, body width, total width, height - # Symbols based on JEDEC MO-150: # N e D E1 E A - - SoConfig( 8, 0.65, 3.0, 5.3, 7.8, 2.0, 'AA'), - SoConfig(14, 0.65, 6.2, 5.3, 7.8, 2.0, 'AB'), - SoConfig(16, 0.65, 6.2, 5.3, 7.8, 2.0, 'AC'), - SoConfig(18, 0.65, 7.2, 5.3, 7.8, 2.0, 'AD'), - SoConfig(20, 0.65, 7.2, 5.3, 7.8, 2.0, 'AE'), - SoConfig(22, 0.65, 8.2, 5.3, 7.8, 2.0, 'AF'), - SoConfig(24, 0.65, 8.2, 5.3, 7.8, 2.0, 'AG'), + SoConfig(8, 0.65, 3.0, 5.3, 7.8, 2.0, 'AA'), + SoConfig(14, 0.65, 6.2, 5.3, 7.8, 2.0, 'AB'), + SoConfig(16, 0.65, 6.2, 5.3, 7.8, 2.0, 'AC'), + SoConfig(18, 0.65, 7.2, 5.3, 7.8, 2.0, 'AD'), + SoConfig(20, 0.65, 7.2, 5.3, 7.8, 2.0, 'AE'), + SoConfig(22, 0.65, 8.2, 5.3, 7.8, 2.0, 'AF'), + SoConfig(24, 0.65, 8.2, 5.3, 7.8, 2.0, 'AG'), SoConfig(28, 0.65, 10.2, 5.3, 7.8, 2.0, 'AH'), SoConfig(30, 0.65, 10.2, 5.3, 7.8, 2.0, 'AJ'), SoConfig(38, 0.65, 12.6, 5.3, 7.8, 2.0, 'AK'), @@ -869,28 +926,27 @@ def generate_3d( # Name extrapolated from IPC7351C name='TSOP{pin_count}P{pitch}_{body_length}X{lead_span}X{height}L{lead_length}X{lead_width}', description='{pin_count}-pin Thin Small Outline Package (TSOP), ' - 'standardized by JEDEC (MS-024), Type II (pins on longer side), variation {variation}.\n\n' - 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' - 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' - 'Height: {height:.2f} mm\n' - 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', + 'standardized by JEDEC (MS-024), Type II (pins on longer side), variation {variation}.\n\n' + 'Pitch: {pitch:.2f} mm\nBody length: {body_length:.2f} mm\n' + 'Body width: {body_width:.2f} mm\nLead span: {lead_span:.2f} mm\n' + 'Height: {height:.2f} mm\n' + 'Lead length: {lead_length:.2f} mm\nLead width: {lead_width:.2f} mm', configs=[ # pin count, pitch, body length, body width, total width, height - # Symbols based on JEDEC MS-024: # N e D E1 E A - SoConfig(28, 1.27, 18.41, 10.16, 11.76, 1.2, 'AA'), - SoConfig(32, 1.27, 20.95, 10.16, 11.76, 1.2, 'BA'), - SoConfig(50, 0.80, 20.95, 10.16, 11.76, 1.2, 'BC'), - SoConfig(80, 0.50, 20.95, 10.16, 11.76, 1.2, 'BD'), - SoConfig(36, 1.27, 23.49, 10.16, 11.76, 1.2, 'CA'), - SoConfig(70, 0.65, 23.49, 10.16, 11.76, 1.2, 'CB'), - SoConfig(40, 1.27, 26.03, 10.16, 11.76, 1.2, 'DA'), - SoConfig(70, 0.80, 28.57, 10.16, 11.76, 1.2, 'EA'), - SoConfig(54, 0.80, 22.22, 10.16, 11.76, 1.2, 'FA'), - SoConfig(86, 0.50, 22.22, 10.16, 11.76, 1.2, 'FB'), - SoConfig(66, 0.65, 22.22, 10.16, 11.76, 1.2, 'FC'), - SoConfig(54, 0.40, 11.20, 10.16, 11.76, 1.2, 'GA'), + SoConfig(28, 1.27, 18.41, 10.16, 11.76, 1.2, 'AA'), + SoConfig(32, 1.27, 20.95, 10.16, 11.76, 1.2, 'BA'), + SoConfig(50, 0.80, 20.95, 10.16, 11.76, 1.2, 'BC'), + SoConfig(80, 0.50, 20.95, 10.16, 11.76, 1.2, 'BD'), + SoConfig(36, 1.27, 23.49, 10.16, 11.76, 1.2, 'CA'), + SoConfig(70, 0.65, 23.49, 10.16, 11.76, 1.2, 'CB'), + SoConfig(40, 1.27, 26.03, 10.16, 11.76, 1.2, 'DA'), + SoConfig(70, 0.80, 28.57, 10.16, 11.76, 1.2, 'EA'), + SoConfig(54, 0.80, 22.22, 10.16, 11.76, 1.2, 'FA'), + SoConfig(86, 0.50, 22.22, 10.16, 11.76, 1.2, 'FB'), + SoConfig(66, 0.65, 22.22, 10.16, 11.76, 1.2, 'FC'), + SoConfig(54, 0.40, 11.20, 10.16, 11.76, 1.2, 'GA'), ], lead_width_lookup={ 0.40: 0.18, diff --git a/generate_sot.py b/generate_sot.py index c72e571..e09caef 100644 --- a/generate_sot.py +++ b/generate_sot.py @@ -1,6 +1,7 @@ """ Generate only the 3D models for SOT packages """ + from os import path from typing import Iterable, Tuple @@ -38,44 +39,51 @@ def generate( dot_center = ( -(body_width / 2) + dot_position, (body_length / 2) - dot_position, - body_standoff + body_height - dot_depth + body_standoff + body_height - dot_depth, ) - body = cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) \ - .box(body_width, body_length, body_height) \ - .edges().chamfer(body_chamfer) + body = ( + cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) + .box(body_width, body_length, body_height) + .edges() + .chamfer(body_chamfer) + ) if pin1_indicator: - body = body.workplane(origin=(dot_center[0], dot_center[1]), offset=(body_height / 2) - dot_depth) \ - .cylinder(5, dot_diameter / 2, centered=(True, True, False), combine='cut') + body = body.workplane(origin=(dot_center[0], dot_center[1]), offset=(body_height / 2) - dot_depth).cylinder( + 5, dot_diameter / 2, centered=(True, True, False), combine='cut' + ) assembly.add_body(body, 'body', StepColor.IC_BODY) if pin1_indicator: - dot = cq.Workplane('XY', origin=dot_center) \ - .cylinder(0.05, dot_diameter / 2, centered=(True, True, False)) + dot = cq.Workplane('XY', origin=dot_center).cylinder(0.05, dot_diameter / 2, centered=(True, True, False)) assembly.add_body(dot, 'dot', StepColor.IC_PIN1_DOT) leads_by_width = dict() for x_sgn, rotation, leads in [(-1, 0, leads_left), (1, 180, leads_right)]: for lead_name, lead_y, lead_width in leads: if lead_width not in leads_by_width: - lead_path = cq.Workplane("XZ") \ - .hLine(lead_contact_length - (lead_height / 2) - bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) \ - .vLine(lead_z_top - lead_height - (2 * bend_radius)) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ + lead_path = ( + cq.Workplane('XZ') + .hLine(lead_contact_length - (lead_height / 2) - bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) + .vLine(lead_z_top - lead_height - (2 * bend_radius)) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) .hLine((lead_span / 2) - bend_radius - lead_contact_length + (lead_height / 2)) - leads_by_width[lead_width] = cq.Workplane('ZY') \ - .rect(lead_height, lead_width) \ - .sweep(lead_path) + ) + leads_by_width[lead_width] = cq.Workplane('ZY').rect(lead_height, lead_width).sweep(lead_path) assembly.add_body( leads_by_width[lead_width], 'lead-{}'.format(lead_name), StepColor.LEAD_SMT, - location=cq.Location(( - x_sgn * lead_span / 2, - lead_y, - lead_height / 2, - ), (0, 0, 1), rotation) + location=cq.Location( + ( + x_sgn * lead_span / 2, + lead_y, + lead_height / 2, + ), + (0, 0, 1), + rotation, + ), ) # Save without fusing for massively better minification! diff --git a/generate_stm_mcu.py b/generate_stm_mcu.py index 059ad36..78e990c 100644 --- a/generate_stm_mcu.py +++ b/generate_stm_mcu.py @@ -22,6 +22,7 @@ +--------------------------------------+------------------+---------------+ """ + import argparse import json import math @@ -35,12 +36,49 @@ import common from common import human_sort_key, init_cache, save_cache from entities.common import ( - Align, Angle, Author, Category, Created, Deprecated, Description, Fill, GeneratedBy, GrabArea, Height, Keywords, - Layer, Length, Name, Polygon, Position, Rotation, Text, Value, Version, Vertex, Width + Align, + Angle, + Author, + Category, + Created, + Deprecated, + Description, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Length, + Name, + Polygon, + Position, + Rotation, + Text, + Value, + Version, + Vertex, + Width, ) from entities.component import ( - Clock, Component, DefaultValue, ForcedNet, Gate, Negated, Norm, PinSignalMap, Prefix, Required, Role, SchematicOnly, - Signal, SignalUUID, Suffix, SymbolUUID, TextDesignator, Variant + Clock, + Component, + DefaultValue, + ForcedNet, + Gate, + Negated, + Norm, + PinSignalMap, + Prefix, + Required, + Role, + SchematicOnly, + Signal, + SignalUUID, + Suffix, + SymbolUUID, + TextDesignator, + Variant, ) from entities.device import ComponentPad, ComponentUUID, Device, PackageUUID from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation @@ -122,8 +160,9 @@ def pins(self, width: int, grid: float) -> List[Tuple[str, Position, Rotation]]: Return all pins spaced with the specified grid size. """ dx = (width + 2) * grid / 2 - return [(l[0], Position(-dx, l[1] * grid), Rotation(0.0)) for l in self.left] + \ - [(r[0], Position(dx, r[1] * grid), Rotation(180.0)) for r in self.right] + return [(l[0], Position(-dx, l[1] * grid), Rotation(0.0)) for l in self.left] + [ + (r[0], Position(dx, r[1] * grid), Rotation(180.0)) for r in self.right + ] def maxmin_y(self, grid: float) -> Tuple[float, float]: """ @@ -220,8 +259,7 @@ def _cleanup_pin_name(pin_name: str) -> str: # Validate according to LibrePCB signal name rules # (libs/librepcb/common/circuitidentifier.h) - assert re.match(r'^[-a-zA-Z0-9_+/!?@#$]*$', val), \ - 'Invalid signal name: {}'.format(val) + assert re.match(r'^[-a-zA-Z0-9_+/!?@#$]*$', val), 'Invalid signal name: {}'.format(val) return val @@ -286,10 +324,12 @@ def get_pin_names_by_type(self, pin_type: str) -> List[PinName]: for pin in pins: if pin.name in known_names: continue - result.append(PinName( - '{}{}'.format(pin_type, i), - pin.name, - )) + result.append( + PinName( + '{}{}'.format(pin_type, i), + pin.name, + ) + ) known_names.add(pin.name) i += 1 return result @@ -343,11 +383,10 @@ def ref_without_flash(self) -> str: 'H': 1536, 'I': 2048, } - assert size in flash_sizes, \ - "{}: Flash size {} doesn't look valid".format(self.ref, size) + assert size in flash_sizes, "{}: Flash size {} doesn't look valid".format(self.ref, size) # Replace flash size character with 'x' - return self.ref[:offset] + 'x' + self.ref[offset + 1:] + return self.ref[:offset] + 'x' + self.ref[offset + 1 :] def ref_for_flash_variants(self, variants: List[str]) -> str: """ @@ -370,7 +409,7 @@ def ref_for_flash_variants(self, variants: List[str]) -> str: # Ensure the offset is correct for variant in variants: assert variant[:offset] == self.ref[:offset] - assert variant[(offset + 1):] == self.ref[(offset + 1):] + assert variant[(offset + 1) :] == self.ref[(offset + 1) :] # Return merged name flash_variants = sorted([ref[offset] for ref in variants]) @@ -378,7 +417,7 @@ def ref_for_flash_variants(self, variants: List[str]) -> str: return '{}[{}]{}'.format( self.ref[:offset], ''.join(flash_variants), - self.ref[(offset + 1):], + self.ref[(offset + 1) :], ) else: return self.ref @@ -399,11 +438,7 @@ def symbol_identifier(self) -> str: """ Get the symbol identifier, used as a key for the UUID lookup. """ - return self.symbol_name \ - .lower() \ - .replace(' ', '_') \ - .replace('-', '~') \ - .replace('/', '') + return self.symbol_name.lower().replace(' ', '_').replace('-', '~').replace('/', '') @property def symbol_description(self) -> str: @@ -442,7 +477,11 @@ def component_description(self) -> str: def description(self) -> str: description = 'A {} MCU by ST Microelectronics.\n\n'.format(self.name) description += 'Package: {}\nFlash: {}\nRAM: {}\nI/Os: {}\nFrequency: {}\n'.format( - self.package, self.flash, self.ram, self.io_count, self.frequency, + self.package, + self.flash, + self.ram, + self.io_count, + self.frequency, ) if self.voltage: description += 'Voltage: {}\n'.format(self.voltage) @@ -495,16 +534,10 @@ def __iter__(self) -> Iterator[PinName]: return iter(self.pins) # Determine number of pins on both sides - left_pins = [ - PinGroup(t, self.get_pin_names_by_type(t)) - for t in ['Reset', 'Power', 'MonoIO', 'Boot', 'NC'] - ] + left_pins = [PinGroup(t, self.get_pin_names_by_type(t)) for t in ['Reset', 'Power', 'MonoIO', 'Boot', 'NC']] left_pins = [group for group in left_pins if len(group) > 0] left_count = sum(len(group) for group in left_pins) - right_pins = [ - PinGroup(t, self.get_pin_names_by_type(t)) - for t in ['IO'] - ] + right_pins = [PinGroup(t, self.get_pin_names_by_type(t)) for t in ['IO']] right_pins = [group for group in right_pins if len(group) > 0] right_count = sum(len(group) for group in right_pins) @@ -513,21 +546,27 @@ def __iter__(self) -> Iterator[PinName]: # the groups. Finally, add some height for double spacing of power # pins. Do this calculation for both sides, and use the highest side. power_pin_spacing = max(len(self.get_pin_names_by_type('Power')) - 1, 0) - height = max([ - left_count + len(left_pins) - 1 + power_pin_spacing, - right_count + len(right_pins) - 1, - ]) + height = max( + [ + left_count + len(left_pins) - 1 + power_pin_spacing, + right_count + len(right_pins) - 1, + ] + ) max_y = math.ceil(height / 2) if debug: print('Placement info:') - print(' Left {} pins {} steps'.format( - left_count, - left_count + len(left_pins) - 1, - )) - print(' Right {} pins {} steps'.format( - right_count, - right_count + len(right_pins) - 1, - )) + print( + ' Left {} pins {} steps'.format( + left_count, + left_count + len(left_pins) - 1, + ) + ) + print( + ' Right {} pins {} steps'.format( + right_count, + right_count + len(right_pins) - 1, + ) + ) print(' Height: {} steps, max_y: {} steps'.format(height, max_y)) # Generate placement info @@ -562,10 +601,10 @@ def __iter__(self) -> Iterator[PinName]: if debug: print('Placement:') print(' Left:') - for (pin_name_str, y) in placement.left: + for pin_name_str, y in placement.left: print(' {} {}'.format(y, pin_name_str)) print(' Right:') - for (pin_name_str, y) in placement.right: + for pin_name_str, y in placement.right: print(' {} {}'.format(y, pin_name_str)) return (placement, name_mapping) @@ -607,17 +646,19 @@ def generate_sym(mcus: List[MCU], symbol_map: Dict[str, str], debug: bool = Fals placement_pins = placement.pins(width, grid) placement_pins.sort(key=lambda x: (x[1].x, x[1].y)) for pin_name, position, rotation in placement.pins(width, grid): - symbol.add_pin(SymbolPin( - uuid('sym', mcu.symbol_identifier, 'pin-{}'.format(pin_name)), - Name(pin_name), - position, - rotation, - Length(grid), - NamePosition(3.81, 0.0), - NameRotation(0.0), - NameHeight(2.5), - NameAlign('left center') - )) + symbol.add_pin( + SymbolPin( + uuid('sym', mcu.symbol_identifier, 'pin-{}'.format(pin_name)), + Name(pin_name), + position, + rotation, + Length(grid), + NamePosition(3.81, 0.0), + NameRotation(0.0), + NameHeight(2.5), + NameAlign('left center'), + ) + ) polygon = Polygon( uuid('sym', mcu.symbol_identifier, 'polygon'), Layer('sym_outlines'), @@ -628,8 +669,8 @@ def generate_sym(mcus: List[MCU], symbol_map: Dict[str, str], debug: bool = Fals (max_y, min_y) = placement.maxmin_y(grid) dx = width * grid / 2 polygon.add_vertex(Vertex(Position(-dx, max_y), Angle(0.0))) - polygon.add_vertex(Vertex(Position( dx, max_y), Angle(0.0))) - polygon.add_vertex(Vertex(Position( dx, min_y), Angle(0.0))) + polygon.add_vertex(Vertex(Position(dx, max_y), Angle(0.0))) + polygon.add_vertex(Vertex(Position(dx, min_y), Angle(0.0))) polygon.add_vertex(Vertex(Position(-dx, min_y), Angle(0.0))) polygon.add_vertex(Vertex(Position(-dx, max_y), Angle(0.0))) symbol.add_polygon(polygon) @@ -714,18 +755,20 @@ def generate_cmp( # Add signals signals = sorted({pin.name for pin in mcu.pins}, key=human_sort_key) for signal in signals: - component.add_signal(Signal( - # Use original signal name, so that changing the cleanup function - # does not influence the identifier. - uuid('cmp', mcu.component_identifier, 'signal-{}'.format(signal)), - # Use cleaned up signal name for name - Name(signal), - Role.PASSIVE, - Required(False), - Negated(False), - Clock(False), - ForcedNet(''), - )) + component.add_signal( + Signal( + # Use original signal name, so that changing the cleanup function + # does not influence the identifier. + uuid('cmp', mcu.component_identifier, 'signal-{}'.format(signal)), + # Use cleaned up signal name for name + Name(signal), + Role.PASSIVE, + Required(False), + Negated(False), + Clock(False), + ForcedNet(''), + ) + ) # Add symbol variant gate = Gate( @@ -737,18 +780,22 @@ def generate_cmp( Suffix(''), ) for generic, concrete in pin_mapping.items(): - gate.add_pin_signal_map(PinSignalMap( - uuid('sym', mcu.symbol_identifier, 'pin-{}'.format(generic)), - SignalUUID(uuid('cmp', mcu.component_identifier, 'signal-{}'.format(concrete))), - TextDesignator.SIGNAL_NAME, - )) - component.add_variant(Variant( - uuid('cmp', mcu.component_identifier, 'variant-single'), - Norm.EMPTY, - Name('single'), - Description('Symbol with all MCU pins'), - gate, - )) + gate.add_pin_signal_map( + PinSignalMap( + uuid('sym', mcu.symbol_identifier, 'pin-{}'.format(generic)), + SignalUUID(uuid('cmp', mcu.component_identifier, 'signal-{}'.format(concrete))), + TextDesignator.SIGNAL_NAME, + ) + ) + component.add_variant( + Variant( + uuid('cmp', mcu.component_identifier, 'variant-single'), + Norm.EMPTY, + Name('single'), + Description('Symbol with all MCU pins'), + gate, + ) + ) components.append(component) # Make sure all grouped components are identical @@ -767,17 +814,17 @@ def generate_dev(mcu: MCU, symbol_map: Dict[str, str], base_lib_path: str, debug dev_version = '0.1' package_uuid_mapping = { - 'LQFP32': 'd1944164-969d-421f-8b46-1e79fc368195', # LQFP80P900X900X140-32 - 'LQFP44': 'b373f788-8d26-4e3d-9256-89851d962373', # LQFP80P1200X1200X140-44 - 'LQFP48': '584b7c26-5a8e-4a2b-807a-977edd1df991', # LQFP50P900X900X140-48 - 'LQFP64': '54cc857c-3af1-4af3-82b0-fba7a121bcb1', # LQFP50P1200X1200X140-64 - 'LQFP80': 'fde7e4d0-0548-4c0a-aa3e-6f8ce25e751c', # LQFP65P1600X1600X140-80 + 'LQFP32': 'd1944164-969d-421f-8b46-1e79fc368195', # LQFP80P900X900X140-32 + 'LQFP44': 'b373f788-8d26-4e3d-9256-89851d962373', # LQFP80P1200X1200X140-44 + 'LQFP48': '584b7c26-5a8e-4a2b-807a-977edd1df991', # LQFP50P900X900X140-48 + 'LQFP64': '54cc857c-3af1-4af3-82b0-fba7a121bcb1', # LQFP50P1200X1200X140-64 + 'LQFP80': 'fde7e4d0-0548-4c0a-aa3e-6f8ce25e751c', # LQFP65P1600X1600X140-80 'LQFP100': 'f74cdcb2-833d-4877-876f-56d4c15b5cb8', # LQFP50P1600X1600X140-100 'LQFP144': '2fc34b46-a86d-40e3-9dd1-def143ac3318', # LQFP50P2200X2200X140-144 'LQFP176': '43ab9eca-7912-433f-afaa-61d3ec6c84b2', # LQFP50P2600X2600X140-176 'LQFP208': '422600f0-a868-49b6-92f7-22c1874258bb', # LQFP50P3000X3000X140-208 - 'SO8': 'ffbf2bed-9155-45a9-b154-2f766c7f9019', # SOIC127P600X175-8 - 'SO8N': 'ffbf2bed-9155-45a9-b154-2f766c7f9019', # SOIC127P600X175-8 + 'SO8': 'ffbf2bed-9155-45a9-b154-2f766c7f9019', # SOIC127P600X175-8 + 'SO8N': 'ffbf2bed-9155-45a9-b154-2f766c7f9019', # SOIC127P600X175-8 'TSSOP14': 'fb8c2dc2-9812-4383-a810-b2fdbd525b4e', # TSSOP14P65_500X640X120L100X30 'TSSOP20': 'a040fccc-54e5-4f95-a5db-20044d8b37a5', # TSSOP20P65_650X640X120L100X30 } @@ -803,10 +850,12 @@ def generate_dev(mcu: MCU, symbol_map: Dict[str, str], base_lib_path: str, debug ) for pin in mcu.pins: pad_uuid = pad_uuid_mapping[pin.number] - device.add_pad(ComponentPad( - pad_uuid, - SignalUUID(uuid('cmp', mcu.component_identifier, 'signal-{}'.format(pin.name))), - )) + device.add_pad( + ComponentPad( + pad_uuid, + SignalUUID(uuid('cmp', mcu.component_identifier, 'signal-{}'.format(pin.name))), + ) + ) device.serialize(path.join(outdir, 'dev')) print('Wrote dev {}'.format(name)) @@ -850,11 +899,15 @@ def generate(data: Dict[str, MCU], base_lib_path: str, debug: bool = False) -> N if __name__ == '__main__': parser = argparse.ArgumentParser(description='Generate STM MCU library elements') parser.add_argument( - '--data-dir', metavar='path-to-data-dir', required=True, + '--data-dir', + metavar='path-to-data-dir', + required=True, help='path to the data dir from https://github.com/LibrePCB/stm-pinout', ) parser.add_argument( - '--base-lib', metavar='path-to-base-lib', required=True, + '--base-lib', + metavar='path-to-base-lib', + required=True, help='path to the LibrePCB-Base.lplib library', ) parser.add_argument( diff --git a/generate_to92.py b/generate_to92.py index 6f8620d..c9e1eb9 100644 --- a/generate_to92.py +++ b/generate_to92.py @@ -1,6 +1,7 @@ """ Generate only the 3D models for TO-92 """ + from os import path import cadquery as cq @@ -10,10 +11,9 @@ def generate_leg_path(plane: str, length: float, delta: float) -> cq.Workplane: straight_length = 1 - return cq.Workplane(plane) \ - .vLine(length - straight_length - abs(delta)) \ - .line(-delta, abs(delta)) \ - .vLine(straight_length) + return ( + cq.Workplane(plane).vLine(length - straight_length - abs(delta)).line(-delta, abs(delta)).vLine(straight_length) + ) def generate( @@ -31,48 +31,48 @@ def generate( leg_z = -StepConstants.THT_LEAD_SOLDER_LENGTH leg_length = StepConstants.THT_LEAD_SOLDER_LENGTH + body_standoff + 0.1 - body = cq.Workplane('XY', origin=(0, 0, body_standoff)) \ - .cylinder(body_height, body_diameter / 2, centered=(True, True, False)) \ - .fillet(0.3) \ - .moveTo(0, body_edge - 10) \ + body = ( + cq.Workplane('XY', origin=(0, 0, body_standoff)) + .cylinder(body_height, body_diameter / 2, centered=(True, True, False)) + .fillet(0.3) + .moveTo(0, body_edge - 10) .box(20, 10, 50, centered=(True, False, False), combine='cut') + ) - leg_straight = cq.Workplane('XY', origin=(0, 0, leg_z)) \ - .box(leg_width, leg_width, leg_length, centered=(True, True, False)) + leg_straight = cq.Workplane('XY', origin=(0, 0, leg_z)).box( + leg_width, leg_width, leg_length, centered=(True, True, False) + ) if pitch_x == 1.27: leg_1 = leg_straight leg_3 = leg_straight else: - leg_1 = cq.Workplane('XY') \ - .rect(leg_width, leg_width) \ - .sweep(generate_leg_path('XZ', leg_length, -pitch_x + 1.27)) \ + leg_1 = ( + cq.Workplane('XY') + .rect(leg_width, leg_width) + .sweep(generate_leg_path('XZ', leg_length, -pitch_x + 1.27)) .translate((-pitch_x + 1.27, 0, leg_z)) - leg_3 = cq.Workplane('XY') \ - .rect(leg_width, leg_width) \ - .sweep(generate_leg_path('XZ', leg_length, pitch_x - 1.27)) \ + ) + leg_3 = ( + cq.Workplane('XY') + .rect(leg_width, leg_width) + .sweep(generate_leg_path('XZ', leg_length, pitch_x - 1.27)) .translate((pitch_x - 1.27, 0, leg_z)) + ) if pitch_y == 0: leg_2 = leg_straight else: - leg_2 = cq.Workplane('XY') \ - .rect(leg_width, leg_width) \ - .sweep(generate_leg_path('YZ', leg_length, pitch_y)) \ + leg_2 = ( + cq.Workplane('XY') + .rect(leg_width, leg_width) + .sweep(generate_leg_path('YZ', leg_length, pitch_y)) .translate((0, pitch_y, leg_z)) + ) assembly = StepAssembly(name) assembly.add_body(body, 'body', StepColor.IC_BODY) - assembly.add_body( - leg_1, 'leg-1', StepColor.LEAD_SMT, - location=cq.Location((-1.27, 0, 0)) - ) - assembly.add_body( - leg_2, 'leg-2', StepColor.LEAD_SMT, - location=cq.Location((0, 0, 0)) - ) - assembly.add_body( - leg_3, 'leg-3', StepColor.LEAD_SMT, - location=cq.Location((1.27, 0, 0)) - ) + assembly.add_body(leg_1, 'leg-1', StepColor.LEAD_SMT, location=cq.Location((-1.27, 0, 0))) + assembly.add_body(leg_2, 'leg-2', StepColor.LEAD_SMT, location=cq.Location((0, 0, 0))) + assembly.add_body(leg_3, 'leg-3', StepColor.LEAD_SMT, location=cq.Location((1.27, 0, 0))) out_path = path.join('out_3d', 'to92', f'{name}.step') assembly.save(out_path, fused=True) diff --git a/pyproject.toml b/pyproject.toml index 1da4339..5c697ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ test = [ "flake8 ~= 4.0.1", "mypy == 1.10.0", "isort ~= 5.13.2", + "ruff ~= 0.4.7", ] [tool.setuptools.packages.find] @@ -46,5 +47,12 @@ balanced_wrapping = false known_typing = "typing" sections = "FUTURE,STDLIB,TYPING,THIRDPARTY,FIRSTPARTY,LOCALFOLDER" +[tool.ruff] +target-version = "py38" +line-length = 120 + +[tool.ruff.format] +quote-style = "single" + [tool.pytest.ini_options] addopts = "--doctest-modules" diff --git a/test_attribute.py b/test_attribute.py index 965e772..378d7c2 100644 --- a/test_attribute.py +++ b/test_attribute.py @@ -3,70 +3,157 @@ import pytest from entities.attribute import ( - Attribute, AttributeType, AttributeUnit, CapacitanceAttribute, CapacitanceUnit, CurrentAttribute, CurrentUnit, - FrequencyAttribute, FrequencyUnit, InductanceAttribute, InductanceUnit, PowerAttribute, PowerUnit, - ResistanceAttribute, ResistanceUnit, StringAttribute, UnitlessUnit, VoltageAttribute, VoltageUnit + Attribute, + AttributeType, + AttributeUnit, + CapacitanceAttribute, + CapacitanceUnit, + CurrentAttribute, + CurrentUnit, + FrequencyAttribute, + FrequencyUnit, + InductanceAttribute, + InductanceUnit, + PowerAttribute, + PowerUnit, + ResistanceAttribute, + ResistanceUnit, + StringAttribute, + UnitlessUnit, + VoltageAttribute, + VoltageUnit, ) -@pytest.mark.parametrize(['name', 'value', 'attribute_type', 'unit', 'output'], [ - ("n", "vvv", AttributeType.STRING, None, '(attribute "n" (type string) (unit none) (value "vvv"))'), - ("n", "vvv", AttributeType.STRING, UnitlessUnit.NONE, '(attribute "n" (type string) (unit none) (value "vvv"))'), - ("n", "0.1", AttributeType.CURRENT, CurrentUnit.MILLIAMPERE, '(attribute "n" (type current) (unit milliampere) (value "0.1"))'), - ("n", "0.1", AttributeType.CAPACITANCE, CapacitanceUnit.MILLIFARAD, '(attribute "n" (type capacitance) (unit millifarad) (value "0.1"))'), - ("n", "0.1", AttributeType.FREQUENCY, FrequencyUnit.KILOHERTZ, '(attribute "n" (type frequency) (unit kilohertz) (value "0.1"))'), - ("n", "0.1", AttributeType.INDUCTANCE, InductanceUnit.MICROHENRY, '(attribute "n" (type inductance) (unit microhenry) (value "0.1"))'), - ("n", "0.1", AttributeType.POWER, PowerUnit.WATT, '(attribute "n" (type power) (unit watt) (value "0.1"))'), - ("n", "0.1", AttributeType.RESISTANCE, ResistanceUnit.MEGAOHM, '(attribute "n" (type resistance) (unit megaohm) (value "0.1"))'), - ("n", "0.1", AttributeType.VOLTAGE, VoltageUnit.KILOVOLT, '(attribute "n" (type voltage) (unit kilovolt) (value "0.1"))'), - # add more for new types -]) -def test_attribute(name: str, value: str, attribute_type: AttributeType, unit: Optional[AttributeUnit], output: str) -> None: +@pytest.mark.parametrize( + ['name', 'value', 'attribute_type', 'unit', 'output'], + [ + ('n', 'vvv', AttributeType.STRING, None, '(attribute "n" (type string) (unit none) (value "vvv"))'), + ( + 'n', + 'vvv', + AttributeType.STRING, + UnitlessUnit.NONE, + '(attribute "n" (type string) (unit none) (value "vvv"))', + ), + ( + 'n', + '0.1', + AttributeType.CURRENT, + CurrentUnit.MILLIAMPERE, + '(attribute "n" (type current) (unit milliampere) (value "0.1"))', + ), + ( + 'n', + '0.1', + AttributeType.CAPACITANCE, + CapacitanceUnit.MILLIFARAD, + '(attribute "n" (type capacitance) (unit millifarad) (value "0.1"))', + ), + ( + 'n', + '0.1', + AttributeType.FREQUENCY, + FrequencyUnit.KILOHERTZ, + '(attribute "n" (type frequency) (unit kilohertz) (value "0.1"))', + ), + ( + 'n', + '0.1', + AttributeType.INDUCTANCE, + InductanceUnit.MICROHENRY, + '(attribute "n" (type inductance) (unit microhenry) (value "0.1"))', + ), + ('n', '0.1', AttributeType.POWER, PowerUnit.WATT, '(attribute "n" (type power) (unit watt) (value "0.1"))'), + ( + 'n', + '0.1', + AttributeType.RESISTANCE, + ResistanceUnit.MEGAOHM, + '(attribute "n" (type resistance) (unit megaohm) (value "0.1"))', + ), + ( + 'n', + '0.1', + AttributeType.VOLTAGE, + VoltageUnit.KILOVOLT, + '(attribute "n" (type voltage) (unit kilovolt) (value "0.1"))', + ), + # add more for new types + ], +) +def test_attribute( + name: str, value: str, attribute_type: AttributeType, unit: Optional[AttributeUnit], output: str +) -> None: attribute_s_exp = str(Attribute(name, value, attribute_type, unit)) assert attribute_s_exp == output -@pytest.mark.parametrize(['attr', 'attribute_type'], [ - (StringAttribute("s", "a"), AttributeType.STRING), - (CurrentAttribute("c", "1", CurrentUnit.AMPERE), AttributeType.CURRENT), - (CapacitanceAttribute("c", "1", CapacitanceUnit.FARAD), AttributeType.CAPACITANCE), - (FrequencyAttribute("f", "1", FrequencyUnit.HERTZ), AttributeType.FREQUENCY), - (InductanceAttribute("i", "1", InductanceUnit.HENRY), AttributeType.INDUCTANCE), - (PowerAttribute("p", "1", PowerUnit.WATT), AttributeType.POWER), - (ResistanceAttribute("r", "1", ResistanceUnit.OHM), AttributeType.RESISTANCE), - (VoltageAttribute("v", "1", VoltageUnit.VOLT), AttributeType.VOLTAGE), - # add more for new types -]) +@pytest.mark.parametrize( + ['attr', 'attribute_type'], + [ + (StringAttribute('s', 'a'), AttributeType.STRING), + (CurrentAttribute('c', '1', CurrentUnit.AMPERE), AttributeType.CURRENT), + (CapacitanceAttribute('c', '1', CapacitanceUnit.FARAD), AttributeType.CAPACITANCE), + (FrequencyAttribute('f', '1', FrequencyUnit.HERTZ), AttributeType.FREQUENCY), + (InductanceAttribute('i', '1', InductanceUnit.HENRY), AttributeType.INDUCTANCE), + (PowerAttribute('p', '1', PowerUnit.WATT), AttributeType.POWER), + (ResistanceAttribute('r', '1', ResistanceUnit.OHM), AttributeType.RESISTANCE), + (VoltageAttribute('v', '1', VoltageUnit.VOLT), AttributeType.VOLTAGE), + # add more for new types + ], +) def test_typed_attribute_has_correct_type(attr: Attribute, attribute_type: AttributeType) -> None: assert attr.attribute_type == attribute_type -@pytest.mark.parametrize('attr', [ - StringAttribute("s", "a"), - # add more for new unitless types -]) +@pytest.mark.parametrize( + 'attr', + [ + StringAttribute('s', 'a'), + # add more for new unitless types + ], +) def test_unitless_typed_types_have_unit_none(attr: Attribute) -> None: assert attr.unit == UnitlessUnit.NONE def test_none_unit_evaluates_to_unitless_none() -> None: # we pass None as unit - a = Attribute("n", "v", AttributeType.STRING, None) + a = Attribute('n', 'v', AttributeType.STRING, None) # check if it gets set to UnitlessUnit.NONE internally assert a.unit == UnitlessUnit.NONE -@pytest.mark.parametrize(['typed', 'general'], [ - (StringAttribute("s", "a"), Attribute("s", "a", AttributeType.STRING, None)), - (StringAttribute("s", "a"), Attribute("s", "a", AttributeType.STRING, UnitlessUnit.NONE)), - (CurrentAttribute("c", "1", CurrentUnit.AMPERE), Attribute("c", "1", AttributeType.CURRENT, CurrentUnit.AMPERE)), - (CapacitanceAttribute("c", "1", CapacitanceUnit.FARAD), Attribute("c", "1", AttributeType.CAPACITANCE, CapacitanceUnit.FARAD)), - (FrequencyAttribute("f", "1", FrequencyUnit.HERTZ), Attribute("f", "1", AttributeType.FREQUENCY, FrequencyUnit.HERTZ)), - (InductanceAttribute("i", "1", InductanceUnit.HENRY), Attribute("i", "1", AttributeType.INDUCTANCE, InductanceUnit.HENRY)), - (PowerAttribute("p", "1", PowerUnit.WATT), Attribute("p", "1", AttributeType.POWER, PowerUnit.WATT)), - (ResistanceAttribute("r", "1", ResistanceUnit.OHM), Attribute("r", "1", AttributeType.RESISTANCE, ResistanceUnit.OHM)), - (VoltageAttribute("v", "1", VoltageUnit.VOLT), Attribute("v", "1", AttributeType.VOLTAGE, VoltageUnit.VOLT)), - # add more for new types -]) +@pytest.mark.parametrize( + ['typed', 'general'], + [ + (StringAttribute('s', 'a'), Attribute('s', 'a', AttributeType.STRING, None)), + (StringAttribute('s', 'a'), Attribute('s', 'a', AttributeType.STRING, UnitlessUnit.NONE)), + ( + CurrentAttribute('c', '1', CurrentUnit.AMPERE), + Attribute('c', '1', AttributeType.CURRENT, CurrentUnit.AMPERE), + ), + ( + CapacitanceAttribute('c', '1', CapacitanceUnit.FARAD), + Attribute('c', '1', AttributeType.CAPACITANCE, CapacitanceUnit.FARAD), + ), + ( + FrequencyAttribute('f', '1', FrequencyUnit.HERTZ), + Attribute('f', '1', AttributeType.FREQUENCY, FrequencyUnit.HERTZ), + ), + ( + InductanceAttribute('i', '1', InductanceUnit.HENRY), + Attribute('i', '1', AttributeType.INDUCTANCE, InductanceUnit.HENRY), + ), + (PowerAttribute('p', '1', PowerUnit.WATT), Attribute('p', '1', AttributeType.POWER, PowerUnit.WATT)), + ( + ResistanceAttribute('r', '1', ResistanceUnit.OHM), + Attribute('r', '1', AttributeType.RESISTANCE, ResistanceUnit.OHM), + ), + (VoltageAttribute('v', '1', VoltageUnit.VOLT), Attribute('v', '1', AttributeType.VOLTAGE, VoltageUnit.VOLT)), + # add more for new types + ], +) def test_typed_vs_general_attribute_equivalence(typed: Attribute, general: Attribute) -> None: assert str(typed) == str(general) diff --git a/test_common.py b/test_common.py index 49c966e..e01e098 100644 --- a/test_common.py +++ b/test_common.py @@ -3,64 +3,82 @@ from common import escape_string, format_float, format_ipc_dimension, human_sort_key, sign -@pytest.mark.parametrize(['inval', 'outval'], [ - ('', ''), - ('"', '\\"'), - ('\n', '\\n'), - ('\\', '\\\\'), -]) +@pytest.mark.parametrize( + ['inval', 'outval'], + [ + ('', ''), + ('"', '\\"'), + ('\n', '\\n'), + ('\\', '\\\\'), + ], +) def test_escape_string(inval: str, outval: str) -> None: assert escape_string(inval) == outval -@pytest.mark.parametrize(['inval', 'outval'], [ - (3.14456, '3.145'), - (-7.0, '-7.0'), - (0.4, '0.4'), - (-0.0, '0.0'), - (-0.0001, '0.0'), # Unsigned zero, due to rounding to 3 decimals -]) +@pytest.mark.parametrize( + ['inval', 'outval'], + [ + (3.14456, '3.145'), + (-7.0, '-7.0'), + (0.4, '0.4'), + (-0.0, '0.0'), + (-0.0001, '0.0'), # Unsigned zero, due to rounding to 3 decimals + ], +) def test_format_float(inval: float, outval: str) -> None: assert format_float(inval) == outval -@pytest.mark.parametrize(['inval', 'decimals', 'outval'], [ - (3.14456, 1, '31'), - (3.14456, 2, '314'), - (75.0, 2, '7500'), - (0.4, 2, '40'), - (0.75, 2, '75'), - (30.0, 2, '3000'), - (0.7999999999, 2, '80'), - (0.809, 2, '80'), -]) +@pytest.mark.parametrize( + ['inval', 'decimals', 'outval'], + [ + (3.14456, 1, '31'), + (3.14456, 2, '314'), + (75.0, 2, '7500'), + (0.4, 2, '40'), + (0.75, 2, '75'), + (30.0, 2, '3000'), + (0.7999999999, 2, '80'), + (0.809, 2, '80'), + ], +) def test_format_ipc_dimension(inval: float, decimals: int, outval: str) -> None: assert format_ipc_dimension(inval, decimals) == outval -@pytest.mark.parametrize(['inval', 'outval'], [ - (3.14456, 1), - (75.0, 1), - (0, 1), - (0.0, 1), - (-0.0, 1), - (-1, -1), - (-0.001, -1), -]) +@pytest.mark.parametrize( + ['inval', 'outval'], + [ + (3.14456, 1), + (75.0, 1), + (0, 1), + (0.0, 1), + (-0.0, 1), + (-1, -1), + (-0.001, -1), + ], +) def test_sign(inval: float, outval: int) -> None: assert sign(inval) == outval -@pytest.mark.parametrize(['inval', 'outval'], [ - ('123', [123]), - ('PA10-PB1', ['PA', 10, '-PB', 1]), -]) +@pytest.mark.parametrize( + ['inval', 'outval'], + [ + ('123', [123]), + ('PA10-PB1', ['PA', 10, '-PB', 1]), + ], +) def test_human_sort_key(inval, outval): assert human_sort_key(inval) == outval -@pytest.mark.parametrize(['inlist', 'sortedlist'], [ - (['PA5', 'PA10', 'PA4', 'PB12'], ['PA4', 'PA5', 'PA10', 'PB12']), -]) +@pytest.mark.parametrize( + ['inlist', 'sortedlist'], + [ + (['PA5', 'PA10', 'PA4', 'PB12'], ['PA4', 'PA5', 'PA10', 'PB12']), + ], +) def test_human_sort_key_list(inlist, sortedlist): assert sorted(inlist, key=human_sort_key) == sortedlist diff --git a/test_entities.py b/test_entities.py index 0743111..b7bbb14 100644 --- a/test_entities.py +++ b/test_entities.py @@ -1,17 +1,78 @@ from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Length, Name, Polygon, Position, Position3D, Rotation, Rotation3D, Text, Value, Version, - Vertex, Width + Align, + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Height, + Keywords, + Layer, + Length, + Name, + Polygon, + Position, + Position3D, + Rotation, + Rotation3D, + Text, + Value, + Version, + Vertex, + Width, ) from entities.component import ( - Clock, Component, DefaultValue, ForcedNet, Gate, Negated, Norm, PinSignalMap, Prefix, Required, Role, SchematicOnly, - Signal, SignalUUID, Suffix, SymbolUUID, TextDesignator, Variant + Clock, + Component, + DefaultValue, + ForcedNet, + Gate, + Negated, + Norm, + PinSignalMap, + Prefix, + Required, + Role, + SchematicOnly, + Signal, + SignalUUID, + Suffix, + SymbolUUID, + TextDesignator, + Variant, ) from entities.device import ComponentPad, ComponentUUID, Device, Manufacturer, PackageUUID, Part from entities.package import ( - AssemblyType, AutoRotate, ComponentSide, CopperClearance, DrillDiameter, Footprint, Footprint3DModel, FootprintPad, - LetterSpacing, LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, PadHole, - Shape, ShapeRadius, Size, SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AssemblyType, + AutoRotate, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + Footprint3DModel, + FootprintPad, + LetterSpacing, + LineSpacing, + Mirror, + Package, + Package3DModel, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + StrokeText, + StrokeWidth, ) from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation from entities.symbol import Pin as SymbolPin @@ -19,12 +80,12 @@ def test_name() -> None: - name_s_exp = str(Name("bar")) + name_s_exp = str(Name('bar')) assert name_s_exp == '(name "bar")' def test_description() -> None: - description = str(Description("My Description\nWith two \" lines")) + description = str(Description('My Description\nWith two " lines')) assert description == '(description "My Description\\nWith two \\" lines")' @@ -44,23 +105,28 @@ def test_length() -> None: def test_symbol_pin() -> None: - symbol_pin_s_exp = str(SymbolPin( - 'my_uuid', - Name('foo'), - Position(1.0, 2.0), - Rotation(180.0), - Length(3.81), - NamePosition(3.0, 4.0), - NameRotation(270.0), - NameHeight(2.5), - NameAlign('left center'), - )) - - assert symbol_pin_s_exp == '(pin my_uuid (name "foo")\n' + \ - ' (position 1.0 2.0) (rotation 180.0) (length 3.81)\n' + \ - ' (name_position 3.0 4.0) (name_rotation 270.0) (name_height 2.5)\n' + \ - ' (name_align left center)\n' + \ - ')' + symbol_pin_s_exp = str( + SymbolPin( + 'my_uuid', + Name('foo'), + Position(1.0, 2.0), + Rotation(180.0), + Length(3.81), + NamePosition(3.0, 4.0), + NameRotation(270.0), + NameHeight(2.5), + NameAlign('left center'), + ) + ) + + assert ( + symbol_pin_s_exp + == '(pin my_uuid (name "foo")\n' + + ' (position 1.0 2.0) (rotation 180.0) (length 3.81)\n' + + ' (name_position 3.0 4.0) (name_rotation 270.0) (name_height 2.5)\n' + + ' (name_align left center)\n' + + ')' + ) def test_vertex() -> None: @@ -69,30 +135,46 @@ def test_vertex() -> None: def test_polygon() -> None: - polygon = Polygon('743dbf3d-98e8-46f0-9a32-00e00d0e811f', Layer('sym_outlines'), Width(0.25), Fill(False), GrabArea(True)) + polygon = Polygon( + '743dbf3d-98e8-46f0-9a32-00e00d0e811f', Layer('sym_outlines'), Width(0.25), Fill(False), GrabArea(True) + ) polygon.add_vertex(Vertex(Position(-2.54, 22.86), Angle(0.0))) - polygon.add_vertex(Vertex(Position( 2.54, 22.86), Angle(0.0))) - polygon.add_vertex(Vertex(Position( 2.54, -25.4), Angle(0.0))) + polygon.add_vertex(Vertex(Position(2.54, 22.86), Angle(0.0))) + polygon.add_vertex(Vertex(Position(2.54, -25.4), Angle(0.0))) polygon.add_vertex(Vertex(Position(-2.54, -25.4), Angle(0.0))) polygon.add_vertex(Vertex(Position(-2.54, 22.86), Angle(0.0))) - assert str(polygon) == '(polygon 743dbf3d-98e8-46f0-9a32-00e00d0e811f (layer sym_outlines)\n' +\ - ' (width 0.25) (fill false) (grab_area true)\n' +\ - ' (vertex (position -2.54 22.86) (angle 0.0))\n' +\ - ' (vertex (position 2.54 22.86) (angle 0.0))\n' +\ - ' (vertex (position 2.54 -25.4) (angle 0.0))\n' +\ - ' (vertex (position -2.54 -25.4) (angle 0.0))\n' +\ - ' (vertex (position -2.54 22.86) (angle 0.0))\n' +\ - ')' + assert ( + str(polygon) + == '(polygon 743dbf3d-98e8-46f0-9a32-00e00d0e811f (layer sym_outlines)\n' + + ' (width 0.25) (fill false) (grab_area true)\n' + + ' (vertex (position -2.54 22.86) (angle 0.0))\n' + + ' (vertex (position 2.54 22.86) (angle 0.0))\n' + + ' (vertex (position 2.54 -25.4) (angle 0.0))\n' + + ' (vertex (position -2.54 -25.4) (angle 0.0))\n' + + ' (vertex (position -2.54 22.86) (angle 0.0))\n' + + ')' + ) def test_text() -> None: - text = str(Text('b9c4aa19-0a46-400c-9c96-e8c3dfb8f83e', Layer('sym_names'), - Value('{{NAME}}'), Align('center bottom'), Height(2.54), - Position(0.0, 22.86), Rotation(0.0))) - assert text == '(text b9c4aa19-0a46-400c-9c96-e8c3dfb8f83e (layer sym_names) (value "{{NAME}}")\n' +\ - ' (align center bottom) (height 2.54) (position 0.0 22.86) (rotation 0.0)\n' +\ - ')' + text = str( + Text( + 'b9c4aa19-0a46-400c-9c96-e8c3dfb8f83e', + Layer('sym_names'), + Value('{{NAME}}'), + Align('center bottom'), + Height(2.54), + Position(0.0, 22.86), + Rotation(0.0), + ) + ) + assert ( + text + == '(text b9c4aa19-0a46-400c-9c96-e8c3dfb8f83e (layer sym_names) (value "{{NAME}}")\n' + + ' (align center bottom) (height 2.54) (position 0.0 22.86) (rotation 0.0)\n' + + ')' + ) def test_symbol() -> None: @@ -108,28 +190,54 @@ def test_symbol() -> None: GeneratedBy('black magic'), [Category('d0618c29-0436-42da-a388-fdadf7b23892')], ) - symbol.add_pin(SymbolPin( - '6da06b2b-7806-4e68-bd0c-e9f18eb2f9d8', - Name('1'), - Position(5.08, 20.32), - Rotation(180.0), - Length(3.81), - NamePosition(1.0, 2.0), - NameRotation(270.0), - NameHeight(2.5), - NameAlign('left center'), - )) - polygon = Polygon('743dbf3d-98e8-46f0-9a32-00e00d0e811f', Layer('sym_outlines'), Width(0.25), Fill(False), GrabArea(True)) + symbol.add_pin( + SymbolPin( + '6da06b2b-7806-4e68-bd0c-e9f18eb2f9d8', + Name('1'), + Position(5.08, 20.32), + Rotation(180.0), + Length(3.81), + NamePosition(1.0, 2.0), + NameRotation(270.0), + NameHeight(2.5), + NameAlign('left center'), + ) + ) + polygon = Polygon( + '743dbf3d-98e8-46f0-9a32-00e00d0e811f', Layer('sym_outlines'), Width(0.25), Fill(False), GrabArea(True) + ) polygon.add_vertex(Vertex(Position(-2.54, 22.86), Angle(0.0))) polygon.add_vertex(Vertex(Position(-2.54, -25.4), Angle(0.0))) polygon.add_vertex(Vertex(Position(-2.54, 22.86), Angle(0.0))) symbol.add_polygon(polygon) - symbol.add_circle(Circle('b5599e68-ff6a-464b-9a40-c6ba8ef8daf5', Layer('sym_outlines'), Width(0.254), Fill(False), GrabArea(False), Diameter(1.27), Position(5.715, 0.0))) - symbol.add_text(Text('b9c4aa19-0a46-400c-9c96-e8c3dfb8f83e', Layer('sym_names'), Value('{{NAME}}'), Align('center bottom'), Height(2.54), Position(0.0, 22.86), Rotation(0.0))) + symbol.add_circle( + Circle( + 'b5599e68-ff6a-464b-9a40-c6ba8ef8daf5', + Layer('sym_outlines'), + Width(0.254), + Fill(False), + GrabArea(False), + Diameter(1.27), + Position(5.715, 0.0), + ) + ) + symbol.add_text( + Text( + 'b9c4aa19-0a46-400c-9c96-e8c3dfb8f83e', + Layer('sym_names'), + Value('{{NAME}}'), + Align('center bottom'), + Height(2.54), + Position(0.0, 22.86), + Rotation(0.0), + ) + ) symbol.add_approval('(approval foo)') symbol.add_approval('(approval bar)') - assert str(symbol) == """(librepcb_symbol 01b03c10-7334-4bd5-b2bc-942c18325d2b + assert ( + str(symbol) + == """(librepcb_symbol 01b03c10-7334-4bd5-b2bc-942c18325d2b (name "Sym name") (description "A multiline description.\\n\\nDescription") (keywords "my, keywords") @@ -159,6 +267,7 @@ def test_symbol() -> None: (approval bar) (approval foo) )""" + ) def test_component_role() -> None: @@ -168,33 +277,82 @@ def test_component_role() -> None: def test_component_signal() -> None: - signal = Signal('f46a4643-fc68-4593-a889-3d987bfe3544', Name('1'), Role.PASSIVE, Required(False), Negated(False), Clock(False), ForcedNet('')) - assert str(signal) == """(signal f46a4643-fc68-4593-a889-3d987bfe3544 (name "1") (role passive) + signal = Signal( + 'f46a4643-fc68-4593-a889-3d987bfe3544', + Name('1'), + Role.PASSIVE, + Required(False), + Negated(False), + Clock(False), + ForcedNet(''), + ) + assert ( + str(signal) + == """(signal f46a4643-fc68-4593-a889-3d987bfe3544 (name "1") (role passive) (required false) (negated false) (clock false) (forced_net "") )""" + ) def test_component_pin_signal_map() -> None: - pin_signal_map = PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334', SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), TextDesignator.SYMBOL_PIN_NAME) + pin_signal_map = PinSignalMap( + '0189aafc-f88a-4e65-8fb4-09a047a3e334', + SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), + TextDesignator.SYMBOL_PIN_NAME, + ) - assert str(pin_signal_map) == '(pin 0189aafc-f88a-4e65-8fb4-09a047a3e334 (signal 46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c) (text pin))' + assert ( + str(pin_signal_map) + == '(pin 0189aafc-f88a-4e65-8fb4-09a047a3e334 (signal 46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c) (text pin))' + ) def test_component_gate() -> None: - gate = Gate('c1e4b542-a1b1-44d5-bec3-070776143a29', SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'), Position(0.0, 0.0), Rotation(0.0), Required(True), Suffix('')) - gate.add_pin_signal_map(PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334', SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), TextDesignator.SYMBOL_PIN_NAME)) - assert str(gate) == """(gate c1e4b542-a1b1-44d5-bec3-070776143a29 + gate = Gate( + 'c1e4b542-a1b1-44d5-bec3-070776143a29', + SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'), + Position(0.0, 0.0), + Rotation(0.0), + Required(True), + Suffix(''), + ) + gate.add_pin_signal_map( + PinSignalMap( + '0189aafc-f88a-4e65-8fb4-09a047a3e334', + SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), + TextDesignator.SYMBOL_PIN_NAME, + ) + ) + assert ( + str(gate) + == """(gate c1e4b542-a1b1-44d5-bec3-070776143a29 (symbol 8f1a97f2-4cdf-43da-b38d-b3787c47b5ad) (position 0.0 0.0) (rotation 0.0) (required true) (suffix "") (pin 0189aafc-f88a-4e65-8fb4-09a047a3e334 (signal 46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c) (text pin)) )""" + ) def test_component_variant() -> None: - gate = Gate('c1e4b542-a1b1-44d5-bec3-070776143a29', SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'), Position(0.0, 0.0), Rotation(0.0), Required(True), Suffix('')) - gate.add_pin_signal_map(PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334', SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), TextDesignator.SYMBOL_PIN_NAME)) + gate = Gate( + 'c1e4b542-a1b1-44d5-bec3-070776143a29', + SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'), + Position(0.0, 0.0), + Rotation(0.0), + Required(True), + Suffix(''), + ) + gate.add_pin_signal_map( + PinSignalMap( + '0189aafc-f88a-4e65-8fb4-09a047a3e334', + SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), + TextDesignator.SYMBOL_PIN_NAME, + ) + ) variant = Variant('abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a', Norm.EMPTY, Name('default'), Description(''), gate) - assert str(variant) == """(variant abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a (norm "") + assert ( + str(variant) + == """(variant abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a (norm "") (name "default") (description "") (gate c1e4b542-a1b1-44d5-bec3-070776143a29 @@ -203,6 +361,7 @@ def test_component_variant() -> None: (pin 0189aafc-f88a-4e65-8fb4-09a047a3e334 (signal 46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c) (text pin)) ) )""" + ) def test_component() -> None: @@ -221,17 +380,42 @@ def test_component() -> None: DefaultValue(''), Prefix('J'), ) - component.add_signal(Signal('f46a4643-fc68-4593-a889-3d987bfe3544', Name('1'), Role.PASSIVE, Required(False), Negated(False), Clock(False), ForcedNet(''))) + component.add_signal( + Signal( + 'f46a4643-fc68-4593-a889-3d987bfe3544', + Name('1'), + Role.PASSIVE, + Required(False), + Negated(False), + Clock(False), + ForcedNet(''), + ) + ) - gate = Gate('c1e4b542-a1b1-44d5-bec3-070776143a29', SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'), Position(0.0, 0.0), Rotation(0.0), Required(True), Suffix('')) - gate.add_pin_signal_map(PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334', SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), TextDesignator.SYMBOL_PIN_NAME)) + gate = Gate( + 'c1e4b542-a1b1-44d5-bec3-070776143a29', + SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'), + Position(0.0, 0.0), + Rotation(0.0), + Required(True), + Suffix(''), + ) + gate.add_pin_signal_map( + PinSignalMap( + '0189aafc-f88a-4e65-8fb4-09a047a3e334', + SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'), + TextDesignator.SYMBOL_PIN_NAME, + ) + ) variant = Variant('abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a', Norm.EMPTY, Name('default'), Description(''), gate) component.add_variant(variant) component.add_approval('(approval foo)') component.add_approval('(approval bar)') - assert str(component) == """(librepcb_component 00c36da8-e22b-43a1-9a87-c3a67e863f49 + assert ( + str(component) + == """(librepcb_component 00c36da8-e22b-43a1-9a87-c3a67e863f49 (name "Generic Connector 1x27") (description "A 1x27 soldered wire connector.\\n\\nNext line") (keywords "connector, 1x27") @@ -259,6 +443,7 @@ def test_component() -> None: (approval bar) (approval foo) )""" + ) def test_package_pad() -> None: @@ -282,13 +467,13 @@ def test_footprint_pad() -> None: PackagePadUuid('5c4d39d3-35cc-4836-a082-693143ee9135'), [ PadHole( - '5c4d39d3-35cc-4836-a082-693143ee9135', - DrillDiameter(1.0), - [Vertex(Position(0.0, 0.0), Angle(0.0))] + '5c4d39d3-35cc-4836-a082-693143ee9135', DrillDiameter(1.0), [Vertex(Position(0.0, 0.0), Angle(0.0))] ), ], ) - assert str(footprint_pad) == """(pad 5c4d39d3-35cc-4836-a082-693143ee9135 (side top) (shape roundrect) + assert ( + str(footprint_pad) + == """(pad 5c4d39d3-35cc-4836-a082-693143ee9135 (side top) (shape roundrect) (position 0.0 22.86) (rotation 0.0) (size 2.54 1.587) (radius 0.5) (stop_mask auto) (solder_paste off) (clearance 0.1) (function unspecified) (package_pad 5c4d39d3-35cc-4836-a082-693143ee9135) @@ -296,15 +481,32 @@ def test_footprint_pad() -> None: (vertex (position 0.0 0.0) (angle 0.0)) ) )""" + ) def test_stroke_text() -> None: - stroke_text = StrokeText('f16d1604-8a82-4688-bc58-be1c1375873f', Layer('top_names'), Height(1.0), StrokeWidth(0.2), LetterSpacing.AUTO, LineSpacing.AUTO, Align('center bottom'), Position(0.0, 25.63), Rotation(0.0), AutoRotate(True), Mirror(False), Value('{{NAME}}')) - assert str(stroke_text) == """(stroke_text f16d1604-8a82-4688-bc58-be1c1375873f (layer top_names) + stroke_text = StrokeText( + 'f16d1604-8a82-4688-bc58-be1c1375873f', + Layer('top_names'), + Height(1.0), + StrokeWidth(0.2), + LetterSpacing.AUTO, + LineSpacing.AUTO, + Align('center bottom'), + Position(0.0, 25.63), + Rotation(0.0), + AutoRotate(True), + Mirror(False), + Value('{{NAME}}'), + ) + assert ( + str(stroke_text) + == """(stroke_text f16d1604-8a82-4688-bc58-be1c1375873f (layer top_names) (height 1.0) (stroke_width 0.2) (letter_spacing auto) (line_spacing auto) (align center bottom) (position 0.0 25.63) (rotation 0.0) (auto_rotate true) (mirror false) (value "{{NAME}}") )""" + ) def create_footprint() -> Footprint: @@ -316,63 +518,80 @@ def create_footprint() -> Footprint: Rotation3D(10.0, 20.0, 30.0), ) footprint.add_3d_model(Footprint3DModel('ea459880-68df-4929-b796-b5c8686a1862')) - footprint.add_pad(FootprintPad( - '5c4d39d3-35cc-4836-a082-693143ee9135', - ComponentSide.TOP, - Shape.ROUNDED_RECT, - Position(0.0, 22.86), - Rotation(0.0), - Size(2.54, 1.5875), - ShapeRadius(0.5), - StopMaskConfig.AUTO, - SolderPasteConfig.OFF, - CopperClearance(0.1), - PadFunction.UNSPECIFIED, - PackagePadUuid('5c4d39d3-35cc-4836-a082-693143ee9135'), - [ - PadHole( - '5c4d39d3-35cc-4836-a082-693143ee9135', - DrillDiameter(1.0), - [Vertex(Position(0.0, 0.0), Angle(0.0))] - ), - ], - )) - footprint.add_pad(FootprintPad( - '6100dd55-d3b3-4139-9085-d5a75e783c37', - ComponentSide.TOP, - Shape.ROUNDED_RECT, - Position(0.0, 20.32), - Rotation(0.0), - Size(2.54, 1.5875), - ShapeRadius(0.5), - StopMaskConfig.AUTO, - SolderPasteConfig.OFF, - CopperClearance(0.1), - PadFunction.UNSPECIFIED, - PackagePadUuid('6100dd55-d3b3-4139-9085-d5a75e783c37'), - [ - PadHole( - '6100dd55-d3b3-4139-9085-d5a75e783c37', - DrillDiameter(1.0), - [Vertex(Position(0.0, 0.0), Angle(0.0))] - ), - ], - )) - polygon = Polygon('5e18e4ea-5667-42b3-b60f-fcc91b0461d3', Layer('top_placement'), Width(0.25), Fill(False), GrabArea(True)) + footprint.add_pad( + FootprintPad( + '5c4d39d3-35cc-4836-a082-693143ee9135', + ComponentSide.TOP, + Shape.ROUNDED_RECT, + Position(0.0, 22.86), + Rotation(0.0), + Size(2.54, 1.5875), + ShapeRadius(0.5), + StopMaskConfig.AUTO, + SolderPasteConfig.OFF, + CopperClearance(0.1), + PadFunction.UNSPECIFIED, + PackagePadUuid('5c4d39d3-35cc-4836-a082-693143ee9135'), + [ + PadHole( + '5c4d39d3-35cc-4836-a082-693143ee9135', DrillDiameter(1.0), [Vertex(Position(0.0, 0.0), Angle(0.0))] + ), + ], + ) + ) + footprint.add_pad( + FootprintPad( + '6100dd55-d3b3-4139-9085-d5a75e783c37', + ComponentSide.TOP, + Shape.ROUNDED_RECT, + Position(0.0, 20.32), + Rotation(0.0), + Size(2.54, 1.5875), + ShapeRadius(0.5), + StopMaskConfig.AUTO, + SolderPasteConfig.OFF, + CopperClearance(0.1), + PadFunction.UNSPECIFIED, + PackagePadUuid('6100dd55-d3b3-4139-9085-d5a75e783c37'), + [ + PadHole( + '6100dd55-d3b3-4139-9085-d5a75e783c37', DrillDiameter(1.0), [Vertex(Position(0.0, 0.0), Angle(0.0))] + ), + ], + ) + ) + polygon = Polygon( + '5e18e4ea-5667-42b3-b60f-fcc91b0461d3', Layer('top_placement'), Width(0.25), Fill(False), GrabArea(True) + ) polygon.add_vertex(Vertex(Position(-1.27, +24.36), Angle(0.0))) polygon.add_vertex(Vertex(Position(+1.27, +24.36), Angle(0.0))) polygon.add_vertex(Vertex(Position(+1.27, -24.36), Angle(0.0))) polygon.add_vertex(Vertex(Position(-1.27, -24.36), Angle(0.0))) polygon.add_vertex(Vertex(Position(-1.27, +24.36), Angle(0.0))) footprint.add_polygon(polygon) - stroke_text = StrokeText('f16d1604-8a82-4688-bc58-be1c1375873f', Layer('top_names'), Height(1.0), StrokeWidth(0.2), LetterSpacing.AUTO, LineSpacing.AUTO, Align('center bottom'), Position(0.0, 25.63), Rotation(0.0), AutoRotate(True), Mirror(False), Value('{{NAME}}')) + stroke_text = StrokeText( + 'f16d1604-8a82-4688-bc58-be1c1375873f', + Layer('top_names'), + Height(1.0), + StrokeWidth(0.2), + LetterSpacing.AUTO, + LineSpacing.AUTO, + Align('center bottom'), + Position(0.0, 25.63), + Rotation(0.0), + AutoRotate(True), + Mirror(False), + Value('{{NAME}}'), + ) footprint.add_text(stroke_text) return footprint def test_footprint() -> None: footprint = create_footprint() - assert str(footprint) == """(footprint 17b9f232-2b15-4281-a07d-ad0db5213f92 + assert ( + str(footprint) + == """(footprint 17b9f232-2b15-4281-a07d-ad0db5213f92 (name "default") (description "") (3d_position 1.0 2.0 3.0) (3d_rotation 10.0 20.0 30.0) @@ -407,13 +626,16 @@ def test_footprint() -> None: (auto_rotate true) (mirror false) (value "{{NAME}}") ) )""" + ) def test_package() -> None: package = Package( '009e35ef-1f50-4bf3-ab58-11eb85bf5503', Name('Soldered Wire Connector 1x19 ⌀1.0mm'), - Description('A 1x19 soldered wire connector with 2.54mm pin spacing and 1.0mm drill holes.\n\nGenerated with librepcb-parts-generator (generate_connectors.py)'), + Description( + 'A 1x19 soldered wire connector with 2.54mm pin spacing and 1.0mm drill holes.\n\nGenerated with librepcb-parts-generator (generate_connectors.py)' + ), Keywords('connector, 1x19, d1.0, connector, soldering, generic'), Author('Danilo B.'), Version('0.1'), @@ -434,7 +656,9 @@ def test_package() -> None: package.add_approval('(approval foo)') package.add_approval('(approval bar)') - assert str(package) == """(librepcb_package 009e35ef-1f50-4bf3-ab58-11eb85bf5503 + assert ( + str(package) + == """(librepcb_package 009e35ef-1f50-4bf3-ab58-11eb85bf5503 (name "Soldered Wire Connector 1x19 ⌀1.0mm") (description "A 1x19 soldered wire connector with 2.54mm pin spacing and 1.0mm drill holes.\\n\\nGenerated with librepcb-parts-generator (generate_connectors.py)") (keywords "connector, 1x19, d1.0, connector, soldering, generic") @@ -486,11 +710,16 @@ def test_package() -> None: (approval bar) (approval foo) )""" + ) def test_component_pad() -> None: - component_pad = ComponentPad('67a7b034-b30b-4644-b8d3-d7a99606efdc', SignalUUID('9bccea5e-e23f-4b88-9de1-4be00dc0c12a')) - assert str(component_pad) == '(pad 67a7b034-b30b-4644-b8d3-d7a99606efdc (signal 9bccea5e-e23f-4b88-9de1-4be00dc0c12a))' + component_pad = ComponentPad( + '67a7b034-b30b-4644-b8d3-d7a99606efdc', SignalUUID('9bccea5e-e23f-4b88-9de1-4be00dc0c12a') + ) + assert ( + str(component_pad) == '(pad 67a7b034-b30b-4644-b8d3-d7a99606efdc (signal 9bccea5e-e23f-4b88-9de1-4be00dc0c12a))' + ) def test_device() -> None: @@ -508,8 +737,12 @@ def test_device() -> None: ComponentUUID('bc911fcc-8b5c-4728-b596-d644797c55da'), PackageUUID('b4e92c64-18c4-44a6-aa39-d1be3e8c29bd'), ) - device.add_pad(ComponentPad('aec3f475-28c4-4508-ab4f-e1b618a0d77d', SignalUUID('726fd1ce-a01b-4287-bb61-e3ff165a0644'))) - device.add_pad(ComponentPad('67a7b034-b30b-4644-b8d3-d7a99606efdc', SignalUUID('9bccea5e-e23f-4b88-9de1-4be00dc0c12a'))) + device.add_pad( + ComponentPad('aec3f475-28c4-4508-ab4f-e1b618a0d77d', SignalUUID('726fd1ce-a01b-4287-bb61-e3ff165a0644')) + ) + device.add_pad( + ComponentPad('67a7b034-b30b-4644-b8d3-d7a99606efdc', SignalUUID('9bccea5e-e23f-4b88-9de1-4be00dc0c12a')) + ) device.add_part(Part(mpn='mpn1', manufacturer=Manufacturer('man1'))) device.add_part(Part(mpn='mpn2', manufacturer=Manufacturer('man2'))) @@ -517,7 +750,9 @@ def test_device() -> None: device.add_approval('(approval foo)') device.add_approval('(approval bar)') - assert str(device) == """(librepcb_device 00652f30-9f89-4027-91f5-7bd684eee751 + assert ( + str(device) + == """(librepcb_device 00652f30-9f89-4027-91f5-7bd684eee751 (name "Foo") (description "Bar") (keywords "foo, bar") @@ -538,6 +773,7 @@ def test_device() -> None: (approval bar) (approval foo) )""" + ) def test_sort_package_3d_models() -> None: diff --git a/test_generate_connectors.py b/test_generate_connectors.py index c823626..0c90cfd 100644 --- a/test_generate_connectors.py +++ b/test_generate_connectors.py @@ -3,56 +3,54 @@ import generate_connectors -@pytest.mark.parametrize(['pin_number', 'pin_count', 'rows', 'spacing', 'y'], [ - # Special case: 1 - (1, 1, 1, 2.54, 0), - - # Odd number of pins - (1, 5, 1, 2.54, 5.08), - (2, 5, 1, 2.54, 2.54), - (3, 5, 1, 2.54, 0), - (4, 5, 1, 2.54, -2.54), - (5, 5, 1, 2.54, -5.08), - - # Even number of pins, grid align - (1, 4, 1, 1.6, 1.6), - (2, 4, 1, 1.6, 0), - (3, 4, 1, 1.6, -1.6), - (4, 4, 1, 1.6, -3.2), - - # Two rows, odd number of cols - (1, 6, 2, 5.0, 5.0), - (2, 6, 2, 5.0, 5.0), - (3, 6, 2, 5.0, 0), - (4, 6, 2, 5.0, 0), - (5, 6, 2, 5.0, -5.0), - (6, 6, 2, 5.0, -5.0), - - # Two rows, even number of cols, grid align - (1, 4, 2, 1.0, 0.0), - (2, 4, 2, 1.0, 0.0), - (3, 4, 2, 1.0, -1.0), - (4, 4, 2, 1.0, -1.0), - -]) +@pytest.mark.parametrize( + ['pin_number', 'pin_count', 'rows', 'spacing', 'y'], + [ + # Special case: 1 + (1, 1, 1, 2.54, 0), + # Odd number of pins + (1, 5, 1, 2.54, 5.08), + (2, 5, 1, 2.54, 2.54), + (3, 5, 1, 2.54, 0), + (4, 5, 1, 2.54, -2.54), + (5, 5, 1, 2.54, -5.08), + # Even number of pins, grid align + (1, 4, 1, 1.6, 1.6), + (2, 4, 1, 1.6, 0), + (3, 4, 1, 1.6, -1.6), + (4, 4, 1, 1.6, -3.2), + # Two rows, odd number of cols + (1, 6, 2, 5.0, 5.0), + (2, 6, 2, 5.0, 5.0), + (3, 6, 2, 5.0, 0), + (4, 6, 2, 5.0, 0), + (5, 6, 2, 5.0, -5.0), + (6, 6, 2, 5.0, -5.0), + # Two rows, even number of cols, grid align + (1, 4, 2, 1.0, 0.0), + (2, 4, 2, 1.0, 0.0), + (3, 4, 2, 1.0, -1.0), + (4, 4, 2, 1.0, -1.0), + ], +) def test_get_y_grid_align(pin_number, pin_count, rows, spacing, y): result = generate_connectors.get_y(pin_number, pin_count, rows, spacing, True) assert result == y -@pytest.mark.parametrize(['pin_count', 'rows', 'spacing', 'top', 'grid', 'expected'], [ - # Special case: 1 - (1, 1, 1.6, 2, True, (2, -2)), - - # Odd number of pins - (5, 1, 2.54, 1.5, True, (5.08 + 1.5, -5.08 - 1.5)), - - # Even number of pins - (6, 1, 2.54, 1.5, True, (2.54 * 2 + 1.5, -(2.54 * 3) - 1.5)), - - # Two rows, odd number of cols - (6, 2, 1.0, 0.3, True, (1.3, -1.3)), -]) +@pytest.mark.parametrize( + ['pin_count', 'rows', 'spacing', 'top', 'grid', 'expected'], + [ + # Special case: 1 + (1, 1, 1.6, 2, True, (2, -2)), + # Odd number of pins + (5, 1, 2.54, 1.5, True, (5.08 + 1.5, -5.08 - 1.5)), + # Even number of pins + (6, 1, 2.54, 1.5, True, (2.54 * 2 + 1.5, -(2.54 * 3) - 1.5)), + # Two rows, odd number of cols + (6, 2, 1.0, 0.3, True, (1.3, -1.3)), + ], +) def test_get_rectangle_bounds(pin_count, rows, spacing, top, grid, expected): result = generate_connectors.get_rectangle_bounds(pin_count, rows, spacing, top, grid) assert result == pytest.approx(expected) diff --git a/test_generate_idc.py b/test_generate_idc.py index c85db8c..e68d9b7 100644 --- a/test_generate_idc.py +++ b/test_generate_idc.py @@ -3,25 +3,28 @@ import generate_idc -@pytest.mark.parametrize([ - 'pin_number', - 'pin_count', - 'row_count', - 'pitch', - 'row_spacing', - 'x', - 'y', -], [ - # 2x4 - (1, 8, 2, 2.0, 3.0, -1.5, +3.0), - (2, 8, 2, 2.0, 3.0, +1.5, +3.0), - (3, 8, 2, 2.0, 3.0, -1.5, +1.0), - (4, 8, 2, 2.0, 3.0, +1.5, +1.0), - (5, 8, 2, 2.0, 3.0, -1.5, -1.0), - (6, 8, 2, 2.0, 3.0, +1.5, -1.0), - (7, 8, 2, 2.0, 3.0, -1.5, -3.0), - (8, 8, 2, 2.0, 3.0, +1.5, -3.0), -]) +@pytest.mark.parametrize( + [ + 'pin_number', + 'pin_count', + 'row_count', + 'pitch', + 'row_spacing', + 'x', + 'y', + ], + [ + # 2x4 + (1, 8, 2, 2.0, 3.0, -1.5, +3.0), + (2, 8, 2, 2.0, 3.0, +1.5, +3.0), + (3, 8, 2, 2.0, 3.0, -1.5, +1.0), + (4, 8, 2, 2.0, 3.0, +1.5, +1.0), + (5, 8, 2, 2.0, 3.0, -1.5, -1.0), + (6, 8, 2, 2.0, 3.0, +1.5, -1.0), + (7, 8, 2, 2.0, 3.0, -1.5, -3.0), + (8, 8, 2, 2.0, 3.0, +1.5, -3.0), + ], +) def test_get_coords(pin_number, pin_count, row_count, pitch, row_spacing, x, y): coord = generate_idc.get_coords(pin_number, pin_count, row_count, pitch, row_spacing) assert coord.x == x diff --git a/test_generate_qfn.py b/test_generate_qfn.py index 52491c0..673d441 100644 --- a/test_generate_qfn.py +++ b/test_generate_qfn.py @@ -3,39 +3,41 @@ import generate_qfp -@pytest.mark.parametrize(['pad_number', 'pad_count', 'pitch', 'offset', 'x', 'y'], [ - # Square, odd number of pads per side - (1, 20, 2.54, 8.0, -8.0, 5.08), - (2, 20, 2.54, 8.0, -8.0, 2.54), - (3, 20, 2.54, 8.0, -8.0, 0.0), - (4, 20, 2.54, 8.0, -8.0, -2.54), - (5, 20, 2.54, 8.0, -8.0, -5.08), - (6, 20, 2.54, 8.0, -5.08, -8.0), - (7, 20, 2.54, 8.0, -2.54, -8.0), - (8, 20, 2.54, 8.0, 0.0, -8.0), - (9, 20, 2.54, 8.0, 2.54, -8.0), - (10, 20, 2.54, 8.0, 5.08, -8.0), - (11, 20, 2.54, 8.0, 8.0, -5.08), - (12, 20, 2.54, 8.0, 8.0, -2.54), - (13, 20, 2.54, 8.0, 8.0, 0.0), - (14, 20, 2.54, 8.0, 8.0, 2.54), - (15, 20, 2.54, 8.0, 8.0, 5.08), - (16, 20, 2.54, 8.0, 5.08, 8.0), - (17, 20, 2.54, 8.0, 2.54, 8.0), - (18, 20, 2.54, 8.0, 0.0, 8.0), - (19, 20, 2.54, 8.0, -2.54, 8.0), - (20, 20, 2.54, 8.0, -5.08, 8.0), - - # Square, even number of pads per side - (1, 16, 4.0, 12.0, -12.0, 6.0), - (2, 16, 4.0, 12.0, -12.0, 2.0), - (3, 16, 4.0, 12.0, -12.0, -2.0), - (4, 16, 4.0, 12.0, -12.0, -6.0), - (5, 16, 4.0, 12.0, -6.0, -12.0), - (6, 16, 4.0, 12.0, -2.0, -12.0), - (7, 16, 4.0, 12.0, 2.0, -12.0), - (8, 16, 4.0, 12.0, 6.0, -12.0), -]) +@pytest.mark.parametrize( + ['pad_number', 'pad_count', 'pitch', 'offset', 'x', 'y'], + [ + # Square, odd number of pads per side + (1, 20, 2.54, 8.0, -8.0, 5.08), + (2, 20, 2.54, 8.0, -8.0, 2.54), + (3, 20, 2.54, 8.0, -8.0, 0.0), + (4, 20, 2.54, 8.0, -8.0, -2.54), + (5, 20, 2.54, 8.0, -8.0, -5.08), + (6, 20, 2.54, 8.0, -5.08, -8.0), + (7, 20, 2.54, 8.0, -2.54, -8.0), + (8, 20, 2.54, 8.0, 0.0, -8.0), + (9, 20, 2.54, 8.0, 2.54, -8.0), + (10, 20, 2.54, 8.0, 5.08, -8.0), + (11, 20, 2.54, 8.0, 8.0, -5.08), + (12, 20, 2.54, 8.0, 8.0, -2.54), + (13, 20, 2.54, 8.0, 8.0, 0.0), + (14, 20, 2.54, 8.0, 8.0, 2.54), + (15, 20, 2.54, 8.0, 8.0, 5.08), + (16, 20, 2.54, 8.0, 5.08, 8.0), + (17, 20, 2.54, 8.0, 2.54, 8.0), + (18, 20, 2.54, 8.0, 0.0, 8.0), + (19, 20, 2.54, 8.0, -2.54, 8.0), + (20, 20, 2.54, 8.0, -5.08, 8.0), + # Square, even number of pads per side + (1, 16, 4.0, 12.0, -12.0, 6.0), + (2, 16, 4.0, 12.0, -12.0, 2.0), + (3, 16, 4.0, 12.0, -12.0, -2.0), + (4, 16, 4.0, 12.0, -12.0, -6.0), + (5, 16, 4.0, 12.0, -6.0, -12.0), + (6, 16, 4.0, 12.0, -2.0, -12.0), + (7, 16, 4.0, 12.0, 2.0, -12.0), + (8, 16, 4.0, 12.0, 6.0, -12.0), + ], +) def test_get_pad_coords( pad_number: int, pad_count: int, diff --git a/test_generate_stm_mcu.py b/test_generate_stm_mcu.py index d04868f..601d47a 100644 --- a/test_generate_stm_mcu.py +++ b/test_generate_stm_mcu.py @@ -23,11 +23,14 @@ def _make_empty_info(flash_size: int = 0) -> Dict[str, Any]: } -@pytest.mark.parametrize(['mcu_ref', 'expected', 'flash_size'], [ - ('STM32F429NEHx', 'STM32F429NxHx', 512), - ('STM32L552CETxP', 'STM32L552CxTxP', 512), - ('STM32MP153CADx', 'STM32MP153CADx', 0), -]) +@pytest.mark.parametrize( + ['mcu_ref', 'expected', 'flash_size'], + [ + ('STM32F429NEHx', 'STM32F429NxHx', 512), + ('STM32L552CETxP', 'STM32L552CxTxP', 512), + ('STM32MP153CADx', 'STM32MP153CADx', 0), + ], +) def test_mcu_ref_without_flash(mcu_ref, expected, flash_size): mcu = MCU(ref=mcu_ref, info=_make_empty_info(flash_size), pins=[]) assert mcu.ref_without_flash == expected @@ -43,16 +46,19 @@ def test_mcu_ref_for_flash_variants_single(): assert mcu.ref_for_flash_variants(['STM32F429IGHx']) == 'STM32F429IGHx' -@pytest.mark.parametrize(['pin_name', 'expected'], [ - # Oscillator normalization - ('PC14OSC32_IN', 'PC14-OSC32_IN'), - ('PC3 / OSC', 'PC3-OSC'), - ('PC3/ OSC', 'PC3-OSC'), - # Everything after a space is stripped out - ('PH3-BOOT0 (BOOT0)', 'PH3-BOOT0'), - ('PC15-OSC32_OUT (OSC32_OUT)', 'PC15-OSC32_OUT'), - ('PA13 (JTMS/SWDIO)', 'PA13'), -]) +@pytest.mark.parametrize( + ['pin_name', 'expected'], + [ + # Oscillator normalization + ('PC14OSC32_IN', 'PC14-OSC32_IN'), + ('PC3 / OSC', 'PC3-OSC'), + ('PC3/ OSC', 'PC3-OSC'), + # Everything after a space is stripped out + ('PH3-BOOT0 (BOOT0)', 'PH3-BOOT0'), + ('PC15-OSC32_OUT (OSC32_OUT)', 'PC15-OSC32_OUT'), + ('PA13 (JTMS/SWDIO)', 'PA13'), + ], +) def test_cleanup_pin_name(pin_name, expected): mcu = MCU(ref='STM32L071KBTx', info=_make_empty_info(), pins=[]) assert mcu._cleanup_pin_name(pin_name) == expected