From 6a2ca3bb9c014e16fd9ef37fdc5fb634e2941c36 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Mon, 3 Jun 2024 12:22:51 +0200 Subject: [PATCH 01/10] Add ruff as test dependency --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 177c7a19..e6f28656 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ test = [ "flake8 ~= 4.0.1", "mypy == 1.10.0", "isort ~= 5.13.2", + "ruff ~= 0.12.2", ] [tool.setuptools.packages.find] From 2757b787978dcc952e684249a0a96eb442f943fb Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Mon, 3 Jun 2024 12:24:43 +0200 Subject: [PATCH 02/10] Add ruff configuration --- pyproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e6f28656..8a5ee213 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,5 +48,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" From 303bd3d3a0e79699e41a80046eb59b6fcfff4b15 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Mon, 24 Jun 2024 20:04:07 +0200 Subject: [PATCH 03/10] Mark places we don't want to format --- generate_mosfet_dual.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/generate_mosfet_dual.py b/generate_mosfet_dual.py index 382c6f47..412bc727 100644 --- a/generate_mosfet_dual.py +++ b/generate_mosfet_dual.py @@ -172,6 +172,7 @@ def generate_dev( if __name__ == '__main__': # Diodes Incorporated + # fmt: off generate_dev( library='Diodes_Incorporated.lplib', name='{name}', @@ -255,4 +256,5 @@ def generate_dev( 'https://www.diodes.com/assets/Datasheets/DMG6602SVTQ.pdf'), ], ) + # fmt: on save_cache(uuid_cache_file, uuid_cache) From 8f99cbac9c2f6d99634506353559d6a3c03359f6 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Fri, 11 Jul 2025 13:53:45 +0200 Subject: [PATCH 04/10] Limit line length to 100 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8a5ee213..470e77ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ sections = "FUTURE,STDLIB,TYPING,THIRDPARTY,FIRSTPARTY,LOCALFOLDER" [tool.ruff] target-version = "py38" -line-length = 120 +line-length = 100 [tool.ruff.format] quote-style = "single" From c022d6763a51d9406aa69fd3dc306ec69da10d55 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Fri, 11 Jul 2025 13:57:18 +0200 Subject: [PATCH 05/10] Format code with ruff --- cadquery_helpers.py | 6 +- common.py | 13 +- dfn_configs.py | 291 ++-- entities/attribute.py | 16 +- entities/common.py | 106 +- entities/component.py | 125 +- entities/device.py | 71 +- entities/package.py | 215 ++- entities/symbol.py | 93 +- generate_axial_tht.py | 719 ++++++---- generate_capacitor_radial_tht.py | 429 +++--- generate_chip.py | 898 +++++++----- generate_connectors.py | 409 ++++-- generate_dfn.py | 545 ++++--- generate_dip.py | 249 ++-- generate_do.py | 262 ++-- generate_idc.py | 603 +++++--- generate_jst_sh_connectors.py | 505 ++++--- generate_led.py | 431 ++++-- generate_mosfet_dual.py | 7 +- generate_mounting_holes.py | 264 ++-- generate_qfp.py | 574 +++++--- generate_screw_terminals.py | 624 ++++---- generate_so.py | 702 ++++----- generate_sod.py | 458 +++--- generate_sot.py | 58 +- generate_stm_mcu.py | 250 ++-- generate_tactile_switches.py | 2296 ++++++++++++++++++++---------- generate_to92.py | 63 +- test_attribute.py | 193 ++- test_common.py | 96 +- test_entities.py | 537 +++++-- test_generate_connectors.py | 88 +- test_generate_idc.py | 41 +- test_generate_qfn.py | 68 +- test_generate_stm_mcu.py | 41 +- 36 files changed, 7804 insertions(+), 4542 deletions(-) diff --git a/cadquery_helpers.py b/cadquery_helpers.py index 5d479cd1..19c124a6 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,9 @@ 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 005cf99b..255fa7de 100644 --- a/common.py +++ b/common.py @@ -1,6 +1,7 @@ """ Common functionality for generator scripts. """ + import collections import csv import re @@ -18,7 +19,7 @@ ('\r', '\\r'), ('\t', '\\t'), ('\v', '\\v'), - ('"', '\\"'), + ('"', '\\"'), ) @@ -113,10 +114,7 @@ def get_pad_uuids(base_lib_path: str, pkg_uuid: str) -> Dict[str, str]: """ with open(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: @@ -132,13 +130,16 @@ 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 return [_convert(x) for x in re.split(r'(\d+)', key) if x] -def serialize_common(serializable: Any, output_directory: str, uuid: str, long_type: str, short_type: str) -> None: +def serialize_common( + serializable: Any, output_directory: str, uuid: str, long_type: str, short_type: str +) -> None: """ Centralized serialize() implementation shared between Component, Symbol, Device, Package """ diff --git a/dfn_configs.py b/dfn_configs.py index af47aab8..8f7699f4 100644 --- a/dfn_configs.py +++ b/dfn_configs.py @@ -5,31 +5,36 @@ from typing import Any, Callable, Optional, Tuple -from entities.common import Angle, Circle, Diameter, Fill, GrabArea, Layer, Polygon, Position, Vertex, Width +from entities.common import ( + Angle, + Circle, + Diameter, + Fill, + GrabArea, + Layer, + Polygon, + Position, + Vertex, + Width, +) from entities.package import Footprint # 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, } # The real CadQuery types are not known statically, thus allowing any type. @@ -37,27 +42,30 @@ 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], Footprint], None]] = None, - step_modification_fn: Optional[StepModificationFn] = 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], Footprint], None] + ] = None, + step_modification_fn: Optional[StepModificationFn] = None, + ): self.length = length self.width = width self.pitch = pitch @@ -65,9 +73,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 @@ -80,12 +88,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 self.step_modification_fn = step_modification_fn @@ -97,11 +105,15 @@ 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'), @@ -119,22 +131,44 @@ 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'), @@ -143,8 +177,12 @@ 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'), @@ -164,16 +202,34 @@ 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'), @@ -221,8 +277,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, @@ -252,8 +312,12 @@ 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 @@ -269,70 +333,92 @@ 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], Footprint], None]: def _draw(config: DfnConfig, uuid: Callable[[str], str], footprint: Footprint) -> None: - footprint.add_circle(Circle( - uuid('hole-circle-doc'), - Layer('top_documentation'), - Width(0.1), - Fill(False), - GrabArea(False), - Diameter(diameter), - Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid('hole-circle-doc'), + Layer('top_documentation'), + Width(0.1), + Fill(False), + GrabArea(False), + Diameter(diameter), + Position(0, 0), + ) + ) + return _draw -def draw_rect(x: float, y: float, width: float, height: float) -> Callable[[DfnConfig, Callable[[str], str], Footprint], None]: +def draw_rect( + x: float, y: float, width: float, height: float +) -> Callable[[DfnConfig, Callable[[str], str], Footprint], None]: def _draw(config: DfnConfig, uuid: Callable[[str], str], footprint: Footprint) -> None: - footprint.add_polygon(Polygon( - uuid=uuid('hole-polygon-doc'), - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x - width / 2, y + height / 2), Angle(0)), - Vertex(Position(x + width / 2, y + height / 2), Angle(0)), - Vertex(Position(x + width / 2, y - height / 2), Angle(0)), - Vertex(Position(x - width / 2, y - height / 2), Angle(0)), - Vertex(Position(x - width / 2, y + height / 2), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid('hole-polygon-doc'), + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x - width / 2, y + height / 2), Angle(0)), + Vertex(Position(x + width / 2, y + height / 2), Angle(0)), + Vertex(Position(x + width / 2, y - height / 2), Angle(0)), + Vertex(Position(x - width / 2, y - height / 2), Angle(0)), + Vertex(Position(x - width / 2, y + height / 2), Angle(0)), + ], + ) + ) + return _draw def step_modification_sphere(diameter: float) -> StepModificationFn: def _fn(body: Any, dot: Any, workplane: Any) -> Tuple[Any, Any]: return body.cut(workplane.sphere(diameter / 2, centered=True)), dot + return _fn -def step_modification_cylinder(x: float, y: float, diameter: float, length: float) -> StepModificationFn: +def step_modification_cylinder( + x: float, y: float, diameter: float, length: float +) -> StepModificationFn: def _fn(body: Any, dot: Any, workplane: Any) -> Tuple[Any, Any]: - cutout = workplane.transformed(offset=(x, y, 0), rotate=(0, 90, 0)) \ - .cylinder(length, diameter / 2, centered=True) + cutout = workplane.transformed(offset=(x, y, 0), rotate=(0, 90, 0)).cylinder( + length, diameter / 2, centered=True + ) return body.cut(cutout), dot + return _fn def step_modification_sgp3x(body: Any, dot: Any, workplane: Any) -> Tuple[Any, Any]: - dot = workplane.cylinder(0.2, 0.6, centered=[True, True, False]) \ - .transformed(offset=(0.5, 0.5, 0), rotate=(0, 0, 45)) \ + dot = ( + workplane.cylinder(0.2, 0.6, centered=[True, True, False]) + .transformed(offset=(0.5, 0.5, 0), rotate=(0, 0, 45)) .box(0.3, 0.3, 0.3, centered=[True, True, False]) + ) return body, dot THIRD_CONFIGS = [ # length, width, pitch, pin_count, height_nominal, height_max, lead_length, exposed_width, exposed_length, keywords - # Sensirion DfnConfig( length=2.0, @@ -348,7 +434,7 @@ def step_modification_sgp3x(body: Any, dot: Any, workplane: Any) -> Tuple[Any, A 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), @@ -368,7 +454,7 @@ def step_modification_sgp3x(body: Any, dot: Any, workplane: Any) -> Tuple[Any, A 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, extended_doc_fn=draw_rect(x=0, y=-0.7, width=2.2, height=0.6), @@ -388,13 +474,12 @@ def step_modification_sgp3x(body: Any, dot: Any, workplane: Any) -> Tuple[Any, A keywords='sensirion,sgp,sgp30,sgpc3', name='SENSIRION_SGP30_SGPC3', # SGP4x needs different 3D model create_date='2019-12-27T19:39:48Z', - library="Sensirion.lplib", + library='Sensirion.lplib', no_exp=False, pin1_corner_dx_dy=0.3, extended_doc_fn=draw_circle(diameter=1.1), step_modification_fn=step_modification_sgp3x, ), - # Microchip DfnConfig( length=2.0, diff --git a/entities/attribute.py b/entities/attribute.py index e2cde486..e78b9e65 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,14 @@ 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 @@ -35,7 +41,9 @@ def __init__(self, name: str, value: Union[Value, str], attribute_type: Attribut self.attribute_type = attribute_type def __str__(self) -> str: - return '(attribute "{}" {} {} {})'.format(self.name, self.attribute_type, self.unit, self.value) + return '(attribute "{}" {} {} {})'.format( + self.name, self.attribute_type, self.unit, self.value + ) class CapacitanceUnit(AttributeUnit): diff --git a/entities/common.py b/entities/common.py index 96ae620d..9711e474 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 @@ -135,7 +141,9 @@ def zero() -> 'Position3D': return Position3D(0.0, 0.0, 0.0) def __str__(self) -> str: - return '(3d_position {} {} {})'.format(format_float(self.x), format_float(self.y), format_float(self.z)) + return '(3d_position {} {} {})'.format( + format_float(self.x), format_float(self.y), format_float(self.z) + ) class Rotation(FloatValue): @@ -143,7 +151,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 @@ -154,7 +162,9 @@ def zero() -> 'Rotation3D': return Rotation3D(0.0, 0.0, 0.0) def __str__(self) -> str: - return '(3d_rotation {} {} {})'.format(format_float(self.x), format_float(self.y), format_float(self.z)) + return '(3d_rotation {} {} {})'.format( + format_float(self.x), format_float(self.y), format_float(self.z) + ) class Length(FloatValue): @@ -187,7 +197,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 +206,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 +214,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 +235,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 +288,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 +309,9 @@ 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 +321,7 @@ def __init__(self, value: str): super().__init__('value', value) -class Align(): +class Align: def __init__(self, align: str): self.align = align @@ -302,8 +329,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,18 +349,24 @@ 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) + + ')' + ) -class Resource(): +class Resource: def __init__(self, name: str, mediatype: str, url: str): self.name = name self.mediatype = mediatype self.url = url def __str__(self) -> str: - return '(resource "{}" (mediatype "{}")\n'.format(escape_string(self.name), escape_string(self.mediatype)) +\ - ' (url "{}")\n'.format(escape_string(self.url)) +\ - ')' + return ( + '(resource "{}" (mediatype "{}")\n'.format( + escape_string(self.name), escape_string(self.mediatype) + ) + + ' (url "{}")\n'.format(escape_string(self.url)) + + ')' + ) diff --git a/entities/component.py b/entities/component.py index 449a9191..73785fd2 100644 --- a/entities/component.py +++ b/entities/component.py @@ -3,8 +3,21 @@ from common import serialize_common 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)) @@ -217,5 +262,5 @@ def serialize(self, output_directory: str) -> None: output_directory=output_directory, uuid=self.uuid, long_type='component', - short_type='cmp' + short_type='cmp', ) diff --git a/entities/device.py b/entities/device.py index b24ffa87..9e3185f0 100644 --- a/entities/device.py +++ b/entities/device.py @@ -4,8 +4,18 @@ from entities.attribute import Attribute from .common import ( - Author, Category, Created, Deprecated, Description, GeneratedBy, Keywords, Name, Resource, StringValue, UUIDValue, - Version + Author, + Category, + Created, + Deprecated, + Description, + GeneratedBy, + Keywords, + Name, + Resource, + StringValue, + UUIDValue, + Version, ) from .component import SignalUUID from .helper import indent_entities @@ -21,7 +31,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 @@ -35,8 +45,10 @@ def __init__(self, manufacturer: str): super().__init__('manufacturer', manufacturer) -class Part(): - def __init__(self, mpn: str, manufacturer: Manufacturer, attributes: Optional[List[Attribute]] = None): +class Part: + def __init__( + self, mpn: str, manufacturer: Manufacturer, attributes: Optional[List[Attribute]] = None + ): self.mpn = mpn self.manufacturer = manufacturer self.attributes = attributes or [] @@ -51,12 +63,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 @@ -87,19 +109,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]) + 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]) + ) ret += indent_entities(self.resources) - ret += ' {}\n'.format(self.component_uuid) +\ - ' {}\n'.format(self.package_uuid) + ret += ' {}\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)) @@ -112,5 +135,5 @@ def serialize(self, output_directory: str) -> None: output_directory=output_directory, uuid=self.uuid, long_type='device', - short_type='dev' + short_type='dev', ) diff --git a/entities/package.py b/entities/package.py index e81c3fa2..987d2557 100644 --- a/entities/package.py +++ b/entities/package.py @@ -3,17 +3,39 @@ from common import format_float, serialize_common 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 @@ -30,10 +52,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 @@ -47,14 +70,13 @@ def __lt__(self, other): # type: ignore return self.uuid < other.uuid -class AlternativeName(): +class AlternativeName: def __init__(self, name: str, reference: str): self.name = name self.reference = reference def __str__(self) -> str: - return '(alternative_name "{}" (reference "{}"))'.format( - self.name, self.reference) + return '(alternative_name "{}" (reference "{}"))'.format(self.name, self.reference) class AssemblyType(EnumValue): @@ -69,7 +91,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 @@ -107,12 +129,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 @@ -127,10 +159,14 @@ 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 @@ -156,7 +192,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 @@ -165,7 +201,7 @@ def __str__(self) -> str: return '(size {} {})'.format(format_float(self.width), format_float(self.height)) -class StopMaskConfig(): +class StopMaskConfig: AUTO = 'auto' OFF = 'off' @@ -174,8 +210,7 @@ def __init__(self, value: Union[str, float]): def __str__(self) -> str: return '(stop_mask {})'.format( - format_float(self.value) if type(self.value) is float - else self.value + format_float(self.value) if type(self.value) is float else self.value ) @@ -217,9 +252,10 @@ def __init__(self, diameter: float): super().__init__('diameter', diameter) -class Hole(): - def __init__(self, uuid: str, diameter: DrillDiameter, - vertices: List[Vertex], stop_mask: StopMaskConfig): +class Hole: + def __init__( + self, uuid: str, diameter: DrillDiameter, vertices: List[Vertex], stop_mask: StopMaskConfig + ): self.uuid = uuid self.diameter = diameter self.vertices = vertices @@ -233,9 +269,8 @@ def __str__(self) -> str: return ret -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 @@ -247,13 +282,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 @@ -269,19 +314,32 @@ 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 Zone(): - def __init__(self, uuid: str, top: bool, inner: bool, bottom: bool, - no_copper: bool, no_planes: bool, no_exposure: bool, - no_devices: bool, vertices: Optional[List[Vertex]] = None): +class Zone: + def __init__( + self, + uuid: str, + top: bool, + inner: bool, + bottom: bool, + no_copper: bool, + no_planes: bool, + no_exposure: bool, + no_devices: bool, + vertices: Optional[List[Vertex]] = None, + ): self.uuid = uuid self.top = top self.inner = inner @@ -313,9 +371,15 @@ def __str__(self) -> str: 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 @@ -351,10 +415,12 @@ def add_hole(self, hole: Hole) -> None: self.holes.append(hole) 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) @@ -367,11 +433,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 @@ -405,18 +480,20 @@ 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]) +\ - ''.join([' {}\n'.format(alt) for alt in self.alternative_names]) +\ - ' {}\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]) + + ''.join([' {}\n'.format(alt) for alt in self.alternative_names]) + + ' {}\n'.format(self.assembly_type) + ) ret += indent_entities(self.pads) ret += indent_entities(self.models_3d) ret += indent_entities(self.footprints) @@ -430,5 +507,5 @@ def serialize(self, output_directory: str) -> None: output_directory=output_directory, uuid=self.uuid, long_type='package', - short_type='pkg' + short_type='pkg', ) diff --git a/entities/symbol.py b/entities/symbol.py index 9c3f987e..75b8c0c8 100644 --- a/entities/symbol.py +++ b/entities/symbol.py @@ -3,13 +3,27 @@ from common import format_float, serialize_common 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 @@ -28,7 +42,7 @@ def __init__(self, height: float): super().__init__('name_height', height) -class NameAlign(): +class NameAlign: def __init__(self, align: str): self.align = align @@ -36,11 +50,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 @@ -52,18 +74,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 @@ -96,16 +129,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) @@ -120,5 +155,5 @@ def serialize(self, output_directory: str) -> None: output_directory=output_directory, uuid=self.uuid, long_type='symbol', - short_type='sym' + short_type='sym', ) diff --git a/generate_axial_tht.py b/generate_axial_tht.py index 324b9aab..13d18c7a 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) @@ -135,7 +183,9 @@ def _uuid(identifier: str) -> str: generated_3d_uuids = set() for variant in variants: uuid_ns = '{}{}-'.format('v' if variant.vertical else 'h', variant.pitch) - footprint_name = '{}, {}mm'.format('Vertical' if variant.vertical else 'Horizontal', variant.pitch) + footprint_name = '{}, {}mm'.format( + 'Vertical' if variant.vertical else 'Horizontal', variant.pitch + ) if variant.compact: uuid_ns += 'compact-' @@ -155,118 +205,139 @@ 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(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(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 = \ + 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 +349,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 +425,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,67 +524,88 @@ 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)) - name_3d = '{}, {} mm'.format('Vertical' if variant.vertical else 'Horizontal', variant.pitch) + name_3d = '{}, {} mm'.format( + 'Vertical' if variant.vertical else 'Horizontal', variant.pitch + ) # Note: Some 3D models are used by multiple footprints but they shall # be added to the package only once, thus we keep a list of which # 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 +641,112 @@ 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 +858,8 @@ 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 +888,8 @@ 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 +918,8 @@ 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 +947,8 @@ 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 +976,8 @@ 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 ce4dc7d6..d808d355 100644 --- a/generate_capacitor_radial_tht.py +++ b/generate_capacitor_radial_tht.py @@ -1,6 +1,7 @@ """ Generate THT polarized radial electrolytic capacitors (CAPPRD). """ + import sys from os import path from uuid import uuid4 @@ -9,15 +10,59 @@ 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, 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_capacitor_radial_tht.py)' @@ -77,21 +122,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) @@ -103,7 +152,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), @@ -138,119 +187,139 @@ 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(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - 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(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + 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(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - 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(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + 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', + ) + ) # package outline - footprint.add_circle(Circle( - uuid=_fpt_uuid('circle-outline'), - layer=Layer('top_package_outlines'), - width=Width(0.0), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(diameter), - position=Position(0.0, 0.0), - )) + footprint.add_circle( + Circle( + uuid=_fpt_uuid('circle-outline'), + layer=Layer('top_package_outlines'), + width=Width(0.0), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(diameter), + position=Position(0.0, 0.0), + ) + ) # 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 @@ -259,12 +328,12 @@ def _generate_fill_polygon(identifier: str, layer: str) -> Polygon: uuid=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), @@ -277,16 +346,17 @@ 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', + ) + ) # Generate 3D models uuid_3d = _pkg_uuid('3d') if generate_3d_models: - generate_3d(library, name, uuid_pkg, uuid_3d, diameter, height, - pitch, lead_width) + generate_3d(library, name, uuid_pkg, uuid_3d, diameter, height, pitch, lead_width) package.add_3d_model(Package3DModel(uuid_3d, Name(name))) for footprint in package.footprints: footprint.add_3d_model(Footprint3DModel(uuid_3d)) @@ -320,39 +390,48 @@ def generate_3d( core_radius = diameter * 0.35 core_depth = min(diameter * 0.02, 0.5) - body_ring_cutout = cq.Workplane('XZ', origin=(-body_ring_circle_radius, 0, body_ring_z)) \ - .circle(body_ring_radius) \ + body_ring_cutout = ( + cq.Workplane('XZ', origin=(-body_ring_circle_radius, 0, body_ring_z)) + .circle(body_ring_radius) .revolve(360, (body_ring_circle_radius, 0, 0), (body_ring_circle_radius, -1, 0)) + ) def _make_body(start_angle: float, angle: float) -> Any: - return cq.Workplane("XZ") \ - .transformed(rotate=(0, -start_angle, 0)) \ - .transformed(offset=(core_radius, 0, 0)) \ - .hLine((diameter / 2) - core_radius - body_fillet) \ - .ellipseArc(x_radius=body_fillet, y_radius=body_fillet, angle1=270, angle2=360, sense=1) \ - .vLine(height - (2 * body_fillet)) \ - .ellipseArc(x_radius=body_fillet, y_radius=body_fillet, angle1=360, angle2=90, sense=1) \ - .hLine(-(diameter / 2) + core_radius + body_fillet) \ - .close() \ - .revolve(angle, (-core_radius, 0, 0), (-core_radius, -1, 0)) \ + return ( + cq.Workplane('XZ') + .transformed(rotate=(0, -start_angle, 0)) + .transformed(offset=(core_radius, 0, 0)) + .hLine((diameter / 2) - core_radius - body_fillet) + .ellipseArc(x_radius=body_fillet, y_radius=body_fillet, angle1=270, angle2=360, sense=1) + .vLine(height - (2 * body_fillet)) + .ellipseArc(x_radius=body_fillet, y_radius=body_fillet, angle1=360, angle2=90, sense=1) + .hLine(-(diameter / 2) + core_radius + body_fillet) + .close() + .revolve(angle, (-core_radius, 0, 0), (-core_radius, -1, 0)) .cut(body_ring_cutout) + ) body = _make_body(marking_angle / 2, 360 - marking_angle) marking = _make_body(-marking_angle / 2, marking_angle) - core = cq.Workplane('XY', origin=(0, 0, core_depth)) \ - .cylinder(height - 2 * core_depth, core_radius, centered=(True, True, False)) - leg = cq.Workplane("XY").workplane(offset=(-core_depth - 1), invert=True) \ - .cylinder(StepConstants.THT_LEAD_SOLDER_LENGTH + core_depth + 1, lead_width / 2, - centered=(True, True, False)) + core = cq.Workplane('XY', origin=(0, 0, core_depth)).cylinder( + height - 2 * core_depth, core_radius, centered=(True, True, False) + ) + leg = ( + cq.Workplane('XY') + .workplane(offset=(-core_depth - 1), invert=True) + .cylinder( + StepConstants.THT_LEAD_SOLDER_LENGTH + core_depth + 1, + lead_width / 2, + centered=(True, True, False), + ) + ) assembly = StepAssembly(name) assembly.add_body(body, 'body', cq.Color('gray16')) assembly.add_body(marking, 'marking', cq.Color('gray60')) assembly.add_body(core, 'core', cq.Color('ghostwhite')) - assembly.add_body(leg, 'leg-1', StepColor.LEAD_THT, - location=cq.Location((-pitch / 2, 0, 0))) - assembly.add_body(leg, 'leg-2', StepColor.LEAD_THT, - location=cq.Location((pitch / 2, 0, 0))) + assembly.add_body(leg, 'leg-1', StepColor.LEAD_THT, location=cq.Location((-pitch / 2, 0, 0))) + assembly.add_body(leg, 'leg-2', StepColor.LEAD_THT, location=cq.Location((pitch / 2, 0, 0))) # Save with fusing since there are not many reused assembly parts. out_path = path.join('out', library, 'pkg', uuid_pkg, f'{uuid_3d}.step') @@ -379,12 +458,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), @@ -396,14 +475,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')) @@ -428,19 +511,19 @@ def _uuid(identifier: str) -> str: # 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 8986835d..a130399e 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,19 @@ 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) @@ -255,8 +301,12 @@ def _uuid(identifier: str) -> str: assembly_type=AssemblyType.SMT, ) - package.add_pad(PackagePad(uuid_pads[0], Name(polarization.name_marked if polarization else '1'))) - package.add_pad(PackagePad(uuid_pads[1], Name(polarization.name_unmarked if polarization else '2'))) + package.add_pad( + PackagePad(uuid_pads[0], Name(polarization.name_marked if polarization else '1')) + ) + package.add_pad( + PackagePad(uuid_pads[1], Name(polarization.name_unmarked if polarization else '2')) + ) def add_footprint_variant( key: str, @@ -264,7 +314,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 +368,37 @@ 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_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(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(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 +409,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 +554,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,48 +639,62 @@ 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) - add_footprint_variant('density~a', 'Density Level A (max protrusion)', 'A', gap=config.gap) + add_footprint_variant( + 'density~b', 'Density Level B (median protrusion)', 'B', gap=config.gap + ) + add_footprint_variant( + 'density~a', 'Density Level A (max protrusion)', 'A', gap=config.gap + ) elif config.footprints: a = config.footprints.get('A') b = config.footprints.get('B') c = config.footprints.get('C') if b: - add_footprint_variant('density~b', 'Density Level B (median protrusion)', 'B', dimensions=b) + add_footprint_variant( + 'density~b', 'Density Level B (median protrusion)', 'B', dimensions=b + ) if a: - add_footprint_variant('density~a', 'Density Level A (max protrusion)', 'A', dimensions=a) + add_footprint_variant( + 'density~a', 'Density Level A (max protrusion)', 'A', dimensions=a + ) if c: - add_footprint_variant('density~c', 'Density Level C (min protrusion)', 'C', dimensions=c) + add_footprint_variant( + 'density~c', 'Density Level C (min protrusion)', 'C', dimensions=c + ) else: raise ValueError('Either gap or footprints must be set') @@ -615,9 +710,9 @@ 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)) @@ -652,50 +747,76 @@ def generate_3d( edge_offset = length / 2 - edge if package_type != 'CAPPM': - 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)) + ) else: lead_tickness = 0.1 lead_length = config.body.lead_length lead_width = config.body.lead_width if lead_length is None or lead_width is None: - raise RuntimeError('Generating 3D models for CAPPM not supported for configs without lead') - - body_pts = [(0, 0), - (0, length / 2 - lead_length - lead_tickness), - (lead_tickness, length / 2 - lead_length - lead_tickness), - (lead_tickness, length / 2 - 2 * lead_tickness), - (height * .6, length / 2 - lead_tickness), - (height + lead_tickness, length / 2 - lead_tickness), - (height + lead_tickness, 0)] - lead_pts = [(0, 0), - (0, lead_length), - (height * .6 + lead_tickness, lead_length), - (height * .6 + lead_tickness, lead_length - lead_tickness), - (lead_tickness, lead_length - lead_tickness), - (lead_tickness, 0)] - - inner = cq.Workplane('ZX') \ - .polyline(body_pts).mirrorX().extrude(width / 2, both=True) \ - .edges("|Z").fillet(fillet) - left = cq.Workplane('ZX', origin=(length / 2 - lead_length, 0, 0)) \ - .polyline(lead_pts).close().extrude(lead_width / 2, both=True) \ - .edges('>X and |Y').fillet(lead_tickness / 1.1) - right = left.mirror(mirrorPlane="ZY") - marking = cq.Workplane('XY', origin=(-(length * .4 - lead_tickness - 0.01), 0, lead_tickness + height)) \ - .box(length * 0.2, width - 2 * fillet, 0.02) + raise RuntimeError( + 'Generating 3D models for CAPPM not supported for configs without lead' + ) + + body_pts = [ + (0, 0), + (0, length / 2 - lead_length - lead_tickness), + (lead_tickness, length / 2 - lead_length - lead_tickness), + (lead_tickness, length / 2 - 2 * lead_tickness), + (height * 0.6, length / 2 - lead_tickness), + (height + lead_tickness, length / 2 - lead_tickness), + (height + lead_tickness, 0), + ] + lead_pts = [ + (0, 0), + (0, lead_length), + (height * 0.6 + lead_tickness, lead_length), + (height * 0.6 + lead_tickness, lead_length - lead_tickness), + (lead_tickness, lead_length - lead_tickness), + (lead_tickness, 0), + ] + + inner = ( + cq.Workplane('ZX') + .polyline(body_pts) + .mirrorX() + .extrude(width / 2, both=True) + .edges('|Z') + .fillet(fillet) + ) + left = ( + cq.Workplane('ZX', origin=(length / 2 - lead_length, 0, 0)) + .polyline(lead_pts) + .close() + .extrude(lead_width / 2, both=True) + .edges('>X and |Y') + .fillet(lead_tickness / 1.1) + ) + right = left.mirror(mirrorPlane='ZY') + marking = cq.Workplane( + 'XY', origin=(-(length * 0.4 - lead_tickness - 0.01), 0, lead_tickness + height) + ).box(length * 0.2, width - 2 * fillet, 0.02) if package_type == 'RESC': inner_color = cq.Color('gray16') @@ -734,15 +855,14 @@ def generate_dev( pad_ids: Optional[Iterable[str]] = None, ) -> 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) @@ -750,8 +870,9 @@ def _uuid(identifier: str) -> str: # UUIDs uuid_dev = _uuid('dev') pkg = uuid('pkg', pkg_name, 'pkg', create=False) - pads = [uuid('pkg', pkg_name, 'pad-{}'.format(i), create=False) - for i in (pad_ids or ['1', '2'])] + pads = [ + uuid('pkg', pkg_name, 'pad-{}'.format(i), create=False) for i in (pad_ids or ['1', '2']) + ] print('Generating dev "{}": {}'.format(full_name, uuid_dev)) @@ -770,11 +891,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)) @@ -799,20 +920,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', @@ -827,7 +948,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), @@ -845,7 +966,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 @@ -889,9 +1010,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', @@ -899,61 +1020,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', @@ -968,7 +1144,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. @@ -1090,7 +1266,7 @@ def _uuid(identifier: str) -> str: ('7343-20', '', 'CAPPM730X430X200L130X240'), ('7343-31', '', 'CAPPM730X430X310L130X240'), ('7343-43', '', 'CAPPM730X430X430L130X240'), - ('7360-38', '', 'CAPPM730X600X380L130X410') + ('7360-38', '', 'CAPPM730X600X380L130X410'), ], cmp='c54375c5-7149-4ded-95c5-7462f7301ee7', cat='c011cc6b-b762-498e-8494-d1994f3043cf', diff --git a/generate_connectors.py b/generate_connectors.py index 5424c436..ac96a232 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(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(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,11 +384,15 @@ 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)) - print('{}x{:02d} {} ⌀{:.1f}mm: Wrote package {}'.format(rows, per_row, kind, drill, uuid_pkg)) + print( + '{}x{:02d} {} ⌀{:.1f}mm: Wrote package {}'.format( + rows, per_row, kind, drill, uuid_pkg + ) + ) def generate_silkscreen_female( @@ -430,24 +508,31 @@ 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)) \ - .box(lead_dimensions[0], lead_dimensions[1], total_lead_length, centered=(True, True, False)) + 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 +593,9 @@ 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\nGenerated with {}'.format(rows, per_row, name_lower, generator) + ), Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)), Author(author), Version(version), @@ -537,11 +623,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) + 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))) @@ -564,7 +646,7 @@ def _uuid(identifier: str) -> str: Layer('sym_outlines'), Width(line_width), Fill(True), - GrabArea(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))) @@ -584,7 +666,7 @@ def _uuid(identifier: str) -> str: Layer('sym_outlines'), Width(line_width * 0.75), Fill(False), - GrabArea(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 +679,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 +705,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 +715,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 +776,9 @@ 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\nGenerated with {}'.format(rows, per_row, name_lower, generator) + ), Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)), Author(author), Version(version), @@ -691,15 +792,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 +813,22 @@ 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)) + 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)) @@ -756,17 +863,23 @@ def _uuid(identifier: str) -> str: uuid_dev = _uuid('dev') uuid_cmp = uuid('cmp', kind, broad_variant, 'cmp') - uuid_signals = [uuid('cmp', kind, broad_variant, 'signal-{}'.format(p)) for p in range(i)] + uuid_signals = [ + uuid('cmp', kind, broad_variant, 'signal-{}'.format(p)) for p in range(i) + ] uuid_pkg = uuid('pkg', kind, variant, 'pkg') uuid_pads = [uuid('pkg', kind, variant, 'pad-{}'.format(p)) for p in range(i)] # 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(' (keywords "connector, {}x{}, d{:.1f}, {}")'.format(rows, per_row, drill, keywords)) + 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")') lines.append(' (created {})'.format(create_date or now())) @@ -777,9 +890,11 @@ def _uuid(identifier: str) -> str: lines.append(' (package {})'.format(uuid_pkg)) signalmappings = [] for p in range(1, i + 1): - signalmappings.append(' (pad {} (signal {}))'.format(uuid_pads[p - 1], uuid_signals[p - 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) @@ -791,7 +906,9 @@ def _uuid(identifier: str) -> str: f.write('\n'.join(lines)) f.write('\n') - print('{}x{} {} ⌀{:.1f}mm: Wrote device {}'.format(rows, per_row, kind, drill, uuid_dev)) + print( + '{}x{} {} ⌀{:.1f}mm: Wrote device {}'.format(rows, per_row, kind, drill, uuid_dev) + ) if __name__ == '__main__': diff --git a/generate_dfn.py b/generate_dfn.py index 219eb0fc..79e54400 100644 --- a/generate_dfn.py +++ b/generate_dfn.py @@ -2,6 +2,7 @@ Generate DFN packages """ + import sys from os import path from uuid import uuid4 @@ -12,14 +13,56 @@ from common import init_cache, now, save_cache from dfn_configs import JEDEC_CONFIGS, THIRD_CONFIGS, DfnConfig 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_NAME = 'librepcb-parts-generator (generate_dfn.py)' @@ -29,7 +72,7 @@ LABEL_OFFSET = 1.0 COURTYARD_EXCESS = 0.2 -MIN_CLEARANCE = 0.20 # For checking only --> warns if violated +MIN_CLEARANCE = 0.20 # For checking only --> warns if violated MIN_TRACE = 0.10 @@ -87,45 +130,50 @@ def generate_pkg( ) -> str: category = 'pkg' - 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 += "\n\nGenerated with {}".format(GENERATOR_NAME) + full_description += '\nPad length: {:.2f} mm'.format(config.lead_length) + full_description += '\n\nGenerated with {}'.format(GENERATOR_NAME) if config.keywords: - full_keywords = "dfn{},{},{}".format(config.pin_count, keywords, config.keywords.lower()) + full_keywords = 'dfn{},{},{}'.format(config.pin_count, keywords, config.keywords.lower()) else: - full_keywords = "dfn{},{}".format(config.pin_count, keywords) + full_keywords = 'dfn{},{}'.format(config.pin_count, keywords) def _uuid(identifier: str) -> str: return uuid(category, full_name, identifier) @@ -174,20 +222,29 @@ def _generate_footprint(key: str, name: str, pad_extension: float) -> None: pad_length = config.lead_length + config.toe_heel + pad_extension exposed_length = config.exposed_length - abs_pad_pos_x = (config.width / 2) - (config.lead_length / 2) + (config.toe_heel / 2) + (pad_extension / 2) + abs_pad_pos_x = ( + (config.width / 2) + - (config.lead_length / 2) + + (config.toe_heel / 2) + + (pad_extension / 2) + ) # Check clearance and make pads smaller if required 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) @@ -199,87 +256,124 @@ 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 - - footprint.add_pad(FootprintPad( - uuid=uuid_pads[pad_idx], - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(pad_pos_x, pad_pos_y), - rotation=Rotation(0), - size=Size(pad_length, config.lead_width), - radius=ShapeRadius(0), - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_pads[pad_idx]), - holes=[], - )) + pad_pos_y = -pad_pos_y + + footprint.add_pad( + FootprintPad( + uuid=uuid_pads[pad_idx], + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(pad_pos_x, pad_pos_y), + rotation=Rotation(0), + size=Size(pad_length, config.lead_width), + radius=ShapeRadius(0), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_pads[pad_idx]), + holes=[], + ) + ) # Make exposed pad, if required # TODO: Handle pin1_corner_dx_dy in config once custom pad shapes are possible if make_exposed: - footprint.add_pad(FootprintPad( - uuid=uuid_exp, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(0, 0), - rotation=Rotation(0), - size=Size(exposed_length, config.exposed_width), - radius=ShapeRadius(0), - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_exp), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_exp, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(0, 0), + rotation=Rotation(0), + size=Size(exposed_length, config.exposed_width), + radius=ShapeRadius(0), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_exp), + holes=[], + ) + ) # 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 if make_exposed: - silk_clearance = silk_top_line_height - (SILKSCREEN_LINE_WIDTH / 2) - (config.exposed_width / 2) + silk_clearance = ( + silk_top_line_height - (SILKSCREEN_LINE_WIDTH / 2) - (config.exposed_width / 2) + ) 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 + ) + ) # Silkscreen for idx, silkscreen_pos in enumerate([-1, 1]): uuid_silkscreen_poly = _uuid('polygon-silkscreen-{}-{}'.format(key, idx)) vertices = [ - Vertex(Position(-config.width / 2, silkscreen_pos * (silk_top_line_height - silk_down)), Angle(0)), + Vertex( + Position( + -config.width / 2, silkscreen_pos * (silk_top_line_height - silk_down) + ), + Angle(0), + ), ] # 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: - vertices.append(Vertex(Position(-config.width / 2, silkscreen_pos * silk_top_line_height), Angle(0))) - vertices.append(Vertex(Position(config.width / 2, silkscreen_pos * silk_top_line_height), Angle(0))) - vertices.append(Vertex(Position(config.width / 2, silkscreen_pos * (silk_top_line_height - silk_down)), Angle(0))) - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen_poly, - layer=Layer('top_legend'), - width=Width(SILKSCREEN_LINE_WIDTH), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=vertices, - )) + vertices.append( + Vertex( + Position(-config.width / 2, silkscreen_pos * silk_top_line_height), Angle(0) + ) + ) + vertices.append( + Vertex( + Position(config.width / 2, silkscreen_pos * silk_top_line_height), Angle(0) + ) + ) + vertices.append( + Vertex( + Position(config.width / 2, silkscreen_pos * (silk_top_line_height - silk_down)), + Angle(0), + ) + ) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen_poly, + layer=Layer('top_legend'), + width=Width(SILKSCREEN_LINE_WIDTH), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=vertices, + ) + ) # Create leads on docu uuid_leads = [_uuid('lead-{}'.format(p)) for p in range(1, config.pin_count + 1)] @@ -290,73 +384,81 @@ 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 - - footprint.add_polygon(Polygon( - uuid=lead_uuid, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x_min, y_max), Angle(0)), - Vertex(Position(x_max, y_max), Angle(0)), - Vertex(Position(x_max, y_min), Angle(0)), - Vertex(Position(x_min, y_min), Angle(0)), - Vertex(Position(x_min, y_max), Angle(0)), - ], - )) + x_min, x_max = -x_min, -x_max + + footprint.add_polygon( + Polygon( + uuid=lead_uuid, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x_min, y_max), Angle(0)), + Vertex(Position(x_max, y_max), Angle(0)), + Vertex(Position(x_max, y_min), Angle(0)), + Vertex(Position(x_min, y_min), Angle(0)), + Vertex(Position(x_min, y_max), Angle(0)), + ], + ) + ) # Create exposed pad on docu 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 - footprint.add_polygon(Polygon( - uuid=uuid_docu_exposed, - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x_min, y_max), Angle(0)), - Vertex(Position(x_max, y_max), Angle(0)), - Vertex(Position(x_max, y_min), Angle(0)), - Vertex(Position(x_min, y_min), Angle(0)), - Vertex(Position(x_min, y_max), Angle(0)), - ], - )) + x_min, x_max = -config.exposed_length / 2, config.exposed_length / 2 + y_min, y_max = -config.exposed_width / 2, config.exposed_width / 2 + footprint.add_polygon( + Polygon( + uuid=uuid_docu_exposed, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x_min, y_max), Angle(0)), + Vertex(Position(x_max, y_max), Angle(0)), + Vertex(Position(x_max, y_min), Angle(0)), + Vertex(Position(x_min, y_min), Angle(0)), + Vertex(Position(x_min, y_max), Angle(0)), + ], + ) + ) # Create body outline on docu uuid_body_outline = _uuid('body-outline') outline_line_width = 0.2 dx = config.width / 2 - outline_line_width / 2 dy = config.length / 2 - outline_line_width / 2 - footprint.add_polygon(Polygon( - uuid=uuid_body_outline, - layer=Layer('top_documentation'), - width=Width(outline_line_width), - 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_outline, + layer=Layer('top_documentation'), + width=Width(outline_line_width), + 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)), + ], + ) + ) if config.extended_doc_fn: + def _get_uuid(identifier: str) -> str: return _uuid(identifier + '-' + key) + config.extended_doc_fn(config, _get_uuid, footprint) # As discussed in https://github.com/LibrePCB-Libraries/LibrePCB_Base.lplib/pull/16 @@ -380,73 +482,83 @@ def _get_uuid(identifier: str) -> str: silk_circ_y = silk_circ_y - silk_down uuid_silkscreen_circ = _uuid('circle-silkscreen-{}'.format(key)) - footprint.add_circle(Circle( - uuid_silkscreen_circ, - Layer('top_legend'), - Width(0.0), - Fill(True), - GrabArea(False), - Diameter(silkscreen_circ_dia), - Position(silk_circ_x, silk_circ_y), - )) + footprint.add_circle( + Circle( + uuid_silkscreen_circ, + Layer('top_legend'), + Width(0.0), + Fill(True), + GrabArea(False), + Diameter(silkscreen_circ_dia), + Position(silk_circ_x, silk_circ_y), + ) + ) # Package Outline dx = config.width / 2 dy = config.length / 2 - footprint.add_polygon(Polygon( - uuid=_uuid('polygon-outline-{}'.format(key)), - 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('polygon-outline-{}'.format(key)), + 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 - footprint.add_polygon(generate_courtyard( - uuid=_uuid('polygon-courtyard-{}'.format(key)), - max_x=config.width / 2, - max_y=config.length / 2, - excess_x=COURTYARD_EXCESS, - excess_y=COURTYARD_EXCESS, - )) + footprint.add_polygon( + generate_courtyard( + uuid=_uuid('polygon-courtyard-{}'.format(key)), + max_x=config.width / 2, + max_y=config.length / 2, + excess_x=COURTYARD_EXCESS, + excess_y=COURTYARD_EXCESS, + ) + ) # Add name and value labels uuid_text_name = _uuid('text-name-{}'.format(key)) uuid_text_value = _uuid('text-value-{}'.format(key)) - footprint.add_text(StrokeText( - uuid=uuid_text_name, - layer=Layer('top_names'), - height=Height(1), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center bottom'), - position=Position(0, config.length / 2 + LABEL_OFFSET), - rotation=Rotation(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(1), - stroke_width=StrokeWidth(0.2), - letter_spacing=LetterSpacing.AUTO, - line_spacing=LineSpacing.AUTO, - align=Align('center top'), - position=Position(0.0, -config.length / 2 - LABEL_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(1), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center bottom'), + position=Position(0, config.length / 2 + LABEL_OFFSET), + rotation=Rotation(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(1), + stroke_width=StrokeWidth(0.2), + letter_spacing=LetterSpacing.AUTO, + line_spacing=LineSpacing.AUTO, + align=Align('center top'), + position=Position(0.0, -config.length / 2 - LABEL_OFFSET), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # Apply function to available footprints _generate_footprint('reflow', 'reflow', 0.0) @@ -486,16 +598,18 @@ def generate_3d( lead_standoff = 0.02 lead_height = 0.2 - body = cq.Workplane('XY', origin=(0, 0, lead_standoff + (config.height_nominal / 2))) \ - .box(config.width, config.length, config.height_nominal) + body = cq.Workplane('XY', origin=(0, 0, lead_standoff + (config.height_nominal / 2))).box( + config.width, config.length, config.height_nominal + ) surface = cq.Workplane('back', origin=(0, 0, lead_standoff + config.height_nominal + 0.05)) - dot = surface.cylinder(0.5, dot_diameter / 2, centered=(True, True, False)) \ - .translate((dot_x, dot_y, 0)) - lead = cq.Workplane("ZY") \ - .box(lead_height, config.lead_width, config.lead_length) + dot = surface.cylinder(0.5, dot_diameter / 2, centered=(True, True, False)).translate( + (dot_x, dot_y, 0) + ) + lead = cq.Workplane('ZY').box(lead_height, config.lead_width, config.lead_length) if make_exposed: - exposed_lead = cq.Workplane('XY', origin=(0, 0, (lead_height / 2))) \ - .box(config.exposed_length, config.exposed_width, lead_height) + exposed_lead = cq.Workplane('XY', origin=(0, 0, (lead_height / 2))).box( + config.exposed_length, config.exposed_width, lead_height + ) if config.step_modification_fn: body, dot = config.step_modification_fn(body, dot, surface) @@ -504,22 +618,25 @@ def generate_3d( assembly = StepAssembly(full_name) assembly.add_body(body, 'body', StepColor.IC_BODY) - assembly.add_body(dot, 'dot', StepColor.IC_PIN1_DOT, - location=cq.Location((0, 0, -0.05 - dot_depth))) + assembly.add_body( + dot, 'dot', StepColor.IC_PIN1_DOT, location=cq.Location((0, 0, -0.05 - dot_depth)) + ) pins_per_side = config.pin_count // 2 for i in range(0, config.pin_count): side = -1 if (i < pins_per_side) else 1 - y1 = get_y(1 if (i < pins_per_side) else pins_per_side, - pins_per_side, config.pitch, False) + y1 = get_y(1 if (i < pins_per_side) else pins_per_side, pins_per_side, config.pitch, False) y_index = i % pins_per_side assembly.add_body( lead, - 'lead-{}'.format(i + 1), StepColor.LEAD_SMT, - location=cq.Location(( - (((config.width - config.lead_length) / 2) + lead_standoff) * side, - y1 + y_index * config.pitch * side, - lead_height / 2, - )) + 'lead-{}'.format(i + 1), + StepColor.LEAD_SMT, + location=cq.Location( + ( + (((config.width - config.lead_length) / 2) + lead_standoff) * side, + y1 + y_index * config.pitch * side, + lead_height / 2, + ) + ), ) if make_exposed: assembly.add_body(exposed_lead, 'lead-exposed', StepColor.LEAD_SMT) @@ -559,11 +676,11 @@ def generate_3d( 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, @@ -574,7 +691,7 @@ def generate_3d( 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 @@ -591,10 +708,10 @@ def generate_3d( 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, @@ -605,6 +722,6 @@ def generate_3d( 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 b11413be..88797c2d 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,55 @@ 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(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(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(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(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 +445,25 @@ 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 \ + 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 +511,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 +538,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 +563,32 @@ 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 9662b9be..9fb29e6e 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 """ + import sys from os import path from uuid import uuid4 @@ -13,13 +14,53 @@ 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, 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_NAME = 'librepcb-parts-generator (generate_do.py)' @@ -52,7 +93,6 @@ def __init__( contact_length_max: float, contact_width_min: float, contact_width_max: float, - variant: str, common_name: str, ): @@ -150,21 +190,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(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(_pad_center(side), 0), + rotation=Rotation(0), + size=Size(_pad_length(), _pad_width()), + radius=ShapeRadius(0.0), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(pad_uuid), + holes=[], + ) + ) lead = Polygon( uuid=_uuid(uuid_ns + 'pad-lead'), layer=Layer('top_documentation'), @@ -172,11 +214,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( @@ -214,9 +258,13 @@ def _add_footprint( fill=Fill(False), grab_area=GrabArea(True), ) - _rect(body, - left_edge + line_offset, right_edge - line_offset, - bottom_edge + line_offset, top_edge - line_offset) + _rect( + body, + left_edge + line_offset, + right_edge - line_offset, + bottom_edge + line_offset, + top_edge - line_offset, + ) footprint.add_polygon(body) if polarity: @@ -229,9 +277,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) # @@ -313,44 +359,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-') @@ -390,31 +442,41 @@ def generate_3d( bar_height = 0.02 bar_x = -(config.body_length - bar_length) / 2 + (2 * body_chamfer_xy) - body = cq.Workplane('XY', origin=(0, 0, body_standoff + (config.body_height / 2))) \ - .box(config.body_length, config.body_width, config.body_height) \ - .edges('|Y').chamfer(body_chamfer_z, body_chamfer_xy) \ - .edges('|X').chamfer(body_chamfer_z, body_chamfer_xy) - bar = cq.Workplane('XY', origin=(bar_x, 0, body_standoff + config.body_height)) \ - .box(bar_length, bar_width, bar_height) - leg_path = cq.Workplane("XZ") \ - .hLine(-config.contact_length + (leg_height / 2) + bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, 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) \ + body = ( + cq.Workplane('XY', origin=(0, 0, body_standoff + (config.body_height / 2))) + .box(config.body_length, config.body_width, config.body_height) + .edges('|Y') + .chamfer(body_chamfer_z, body_chamfer_xy) + .edges('|X') + .chamfer(body_chamfer_z, body_chamfer_xy) + ) + bar = cq.Workplane('XY', origin=(bar_x, 0, body_standoff + config.body_height)).box( + bar_length, bar_width, bar_height + ) + leg_path = ( + cq.Workplane('XZ') + .hLine(-config.contact_length + (leg_height / 2) + bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, 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.contact_length) - leg = cq.Workplane("ZY") \ - .rect(leg_height, config.contact_width) \ - .sweep(leg_path) + ) + leg = cq.Workplane('ZY').rect(leg_height, config.contact_width).sweep(leg_path) assembly = StepAssembly(pkg_name) assembly.add_body(body, 'body', StepColor.IC_BODY) if polarity: assembly.add_body(bar, 'bar', StepColor.IC_PIN1_DOT) lead_x = (config.total_length / 2) - config.contact_length - assembly.add_body(leg, 'leg-1', StepColor.LEAD_SMT, - location=cq.Location((-lead_x, 0, leg_height / 2))) - assembly.add_body(leg, 'leg-2', StepColor.LEAD_SMT, - location=cq.Location((lead_x, 0, leg_height / 2), (0, 0, 1), 180)) + assembly.add_body( + leg, 'leg-1', StepColor.LEAD_SMT, location=cq.Location((-lead_x, 0, leg_height / 2)) + ) + assembly.add_body( + leg, + 'leg-2', + StepColor.LEAD_SMT, + location=cq.Location((lead_x, 0, leg_height / 2), (0, 0, 1), 180), + ) # Save without fusing for slightly better minification. out_path = path.join('out', library, 'pkg', uuid_pkg, f'{uuid_3d}.step') @@ -440,25 +502,21 @@ def generate_3d( # 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 e4de7198..ab9abe49 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)' @@ -76,7 +115,9 @@ def __init__(self, x: float, y: float, round_values: bool = True): self.y = y -def get_coords(pin_number: int, pin_count: int, row_count: int, pitch: float, row_spacing: float) -> Coord: +def get_coords( + pin_number: int, pin_count: int, row_count: int, pitch: float, row_spacing: float +) -> Coord: """ Return the x/y coordinates of the specified pin. @@ -152,8 +193,9 @@ 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 +261,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(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(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 +285,46 @@ 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,92 +341,123 @@ 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 + x_outline_extended = ( + abs(pin1.x) + config.pad_size[0] + config.pad_x_offset + polygon_offset + ) else: x_outline_extended = (config.lead_span / 2) + polygon_offset y_outline = body_bounds[1] + polygon_offset @@ -382,45 +483,48 @@ 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) + - ")" + '(approved missing_footprint_3d_model\n' + ' (footprint {})\n'.format(uuid_footprint) + ')' ) package.serialize(path.join('out', config.library, 'pkg')) @@ -458,15 +562,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 +579,129 @@ 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 4c8effe8..7408c2ad 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,17 +136,25 @@ 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 + return ( + self.support_pad_width + + self.support_pad_distance_x(circuits) + - self.header_width(circuits) + ) / 2 def header_x_center(self, circuits: int) -> float: return self.header_x(circuits) + (self.header_width(circuits) / 2) 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: @@ -117,19 +165,21 @@ def uuid(category: str, kind: str, variant: str, identifier: str) -> str: def connector_uuid(category: str, connector: Connector, identifier: str) -> str: - return uuid(category, connector.type, variant(connector.subtype, connector.circuits), identifier) + return uuid( + category, connector.type, variant(connector.subtype, connector.circuits), identifier + ) 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: @@ -145,9 +195,7 @@ def polygon_rect(polygon: Polygon, x: float, y: float, w: float, h: float) -> No def footprint_add_support_pads( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification + footprint: Footprint, connector: Connector, spec: FootprintSpecification ) -> None: for i in range(2): footprint.add_pad( @@ -155,7 +203,11 @@ def footprint_add_support_pads( 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,8 +215,8 @@ 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=[], ) ) @@ -173,7 +225,7 @@ def footprint_add_pads( footprint: Footprint, connector: Connector, spec: FootprintSpecification, - reverse_pad_order: bool + 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 +235,10 @@ 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 +247,49 @@ 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 + 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 + 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 +299,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 +319,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 @@ -279,37 +342,31 @@ def footprint_add_text( def footprint_add_leads( - footprint: Footprint, - connector: Connector, - spec: FootprintSpecification + 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 + footprint: Footprint, connector: Connector, spec: FootprintSpecification ) -> None: - outline_polygon = Polygon( uuid=footprint_uuid(connector, 'polygonoutline'), layer=Layer('top_package_outlines'), @@ -320,28 +377,47 @@ def footprint_add_outline( # left bottom vertex(spec.header_x(connector.circuits), spec.header_y), # right bottom - vertex(spec.header_x(connector.circuits) + spec.header_width(connector.circuits), spec.header_y), + 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_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, + ), + 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 + footprint: Footprint, connector: Connector, spec: FootprintSpecification ) -> None: courtyard_polygon = Polygon( uuid=footprint_uuid(connector, 'polygoncourtyard'), @@ -353,30 +429,55 @@ 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 + footprint: Footprint, connector: Connector, spec: FootprintSpecification ) -> None: - half_line_width = legend_line_width / 2 min_copper_clearance = 0.15 @@ -389,17 +490,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 +519,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,14 +556,19 @@ 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) @@ -454,15 +579,14 @@ def generate_footprint( spec: FootprintSpecification, description: str, reverse_pad_order: bool, - rotation: int + 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) @@ -484,8 +608,9 @@ 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 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 +630,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: @@ -528,7 +652,9 @@ def _rotate(p: Position) -> None: _rotate(circle.position) -def approve_footprint_warnings(package: Package, footprint: Footprint, connector: Connector) -> None: +def approve_footprint_warnings( + package: Package, footprint: Footprint, connector: Connector +) -> None: """ THIS FUNCTIONS APPROVES "missing_footprint_3d_model" AND "suspicious_pad_function" WARNINGS IN THE PACKAGE EDITOR TO MAKE THE CI PASS @@ -536,16 +662,13 @@ 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 +688,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,13 +701,17 @@ 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): - package.add_pad(PackagePad(pkg_uuid(connector, pad_uuid_name_pattern.format(i)), Name(str(i + 1)))) + package.add_pad( + PackagePad(pkg_uuid(connector, pad_uuid_name_pattern.format(i)), Name(str(i + 1))) + ) - footprint = generate_footprint(connector, footprint_spec, description, reverse_pad_order, rotation) + footprint = generate_footprint( + connector, footprint_spec, description, reverse_pad_order, rotation + ) approve_footprint_warnings(package, footprint, connector) @@ -606,12 +732,14 @@ 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)] + signal_uuids = [ + uuid_cache_connectors[f'{connector_uuid_stub}-signal-{i}'] + for i in range(connector.circuits) + ] dev = Device( uuid=dev_uuid(connector, 'dev'), @@ -631,13 +759,17 @@ def generate_dev( # only connect actual circuits (pins) to signals # the support pads are left unconnected (as per library conventions) 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_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 +792,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 +810,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 +823,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 +834,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 +871,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 +910,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 06aee38c..306d6587 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,29 @@ 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(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(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 +248,9 @@ 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 +296,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: @@ -287,7 +343,9 @@ def _add_flattened_circle( fill=Fill(False), grab_area=GrabArea(False), ) - polygon.add_vertex(Vertex(Position(inner_radius, y), Angle(angle if y > 0 else -angle))) + polygon.add_vertex( + Vertex(Position(inner_radius, y), Angle(angle if y > 0 else -angle)) + ) polygon.add_vertex(Vertex(Position(-inner_radius, y), Angle(0))) polygon.add_vertex(Vertex(Position(-inner_radius, y * 0.80), Angle(0))) footprint.add_polygon(polygon) @@ -334,34 +392,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, @@ -415,7 +477,10 @@ def _add_horizontal_footprint( fill=Fill(True), grab_area=GrabArea(False), ) - x0 = min((config.lead_spacing / 2 + lead_width / 2), config.top_diameter / 2) * factor + x0 = ( + min((config.lead_spacing / 2 + lead_width / 2), config.top_diameter / 2) + * factor + ) x1 = (2 * (config.lead_spacing / 2) - x0 * factor) * factor polygon.add_vertex(Vertex(Position(x0, body_offset), Angle(0))) polygon.add_vertex(Vertex(Position(x1, body_offset), Angle(0))) @@ -459,10 +524,14 @@ def _add_horizontal_footprint( if split_legend is False: polygon.add_vertex(Vertex(Position(-inner_radius, body_bottom_y), Angle(0))) elif body_bottom_silkscreen_x < inner_radius: - polygon.add_vertex(Vertex(Position(-body_bottom_silkscreen_x, body_bottom_y), Angle(0))) + polygon.add_vertex( + Vertex(Position(-body_bottom_silkscreen_x, body_bottom_y), Angle(0)) + ) polygon.add_vertex(Vertex(Position(-inner_radius, body_bottom_y), Angle(0))) else: - polygon.add_vertex(Vertex(Position(-inner_radius, body_bottom_silkscreen_y), Angle(0))) + polygon.add_vertex( + Vertex(Position(-inner_radius, body_bottom_silkscreen_y), Angle(0)) + ) polygon.add_vertex(Vertex(Position(-inner_radius, body_top_y), Angle(-180))) polygon.add_vertex(Vertex(Position(inner_radius, body_top_y), Angle(0))) polygon.add_vertex(Vertex(Position(inner_radius, body_middle_y), Angle(0))) @@ -472,9 +541,13 @@ def _add_horizontal_footprint( polygon.add_vertex(Vertex(Position(-inner_radius, body_bottom_y), Angle(0))) elif body_bottom_silkscreen_x < outer_radius: polygon.add_vertex(Vertex(Position(outer_radius, body_bottom_y), Angle(0))) - polygon.add_vertex(Vertex(Position(body_bottom_silkscreen_x, body_bottom_y), Angle(0))) + polygon.add_vertex( + Vertex(Position(body_bottom_silkscreen_x, body_bottom_y), Angle(0)) + ) else: - polygon.add_vertex(Vertex(Position(outer_radius, body_bottom_silkscreen_y), Angle(0))) + polygon.add_vertex( + Vertex(Position(outer_radius, body_bottom_silkscreen_y), Angle(0)) + ) footprint.add_polygon(polygon) # Package outline @@ -483,7 +556,9 @@ def _generate_outline(offset: float = 0, pad_offset: float = 0) -> List[Vertex]: r_outer = (config.bot_diameter / 2) + offset body_y_mid = body_bottom_y + 1.0 + (default_line_width / 2) + offset body_y_bot = body_offset - offset - leads_x = min(config.lead_spacing / 2 + lead_width / 2 + offset + pad_offset, r_inner) + leads_x = min( + config.lead_spacing / 2 + lead_width / 2 + offset + pad_offset, r_inner + ) leads_y = -lead_width / 2 - offset - pad_offset return [ Vertex(Position(-r_inner, body_y_bot), Angle(0)), @@ -498,55 +573,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 +699,83 @@ 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) \ - .box((config.bot_diameter - config.top_diameter - 0.1) / 2, 20, 20, centered=(False, True, True), combine='cut') + 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 +793,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 +815,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 412bc727..8d01021f 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 @@ -145,7 +146,9 @@ def generate_dev( print('Generating dev "{}": {}'.format(full_name, uuid_dev)) lines.append('(librepcb_device {}'.format(uuid_dev)) lines.append(' (name "{}")'.format(full_name)) - lines.append(' (description "{}\\n\\n{}Generated with {}")'.format(full_desc, datasheet, generator)) + lines.append( + ' (description "{}\\n\\n{}Generated with {}")'.format(full_desc, datasheet, generator) + ) lines.append(' (keywords "{}")'.format(keywords)) lines.append(' (author "{}")'.format(author)) lines.append(' (version "{}")'.format(version)) @@ -155,7 +158,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(')') diff --git a/generate_mounting_holes.py b/generate_mounting_holes.py index e9aec6ab..995397fd 100644 --- a/generate_mounting_holes.py +++ b/generate_mounting_holes.py @@ -6,6 +6,7 @@ - ISO14580: https://cdn.standards.iteh.ai/samples/56456/88025f720d57423b9e2c1ceb78304eec/ISO-14580-2011.pdf - DIN965: https://www.aramfix.com/content/files/d965cagll/datasheet%20din%20965.pdf """ + from os import path from uuid import uuid4 @@ -13,14 +14,49 @@ from common import init_cache, now, save_cache from entities.common import ( - Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, Keywords, - Layer, Name, Position, Position3D, Rotation, Rotation3D, Version, Vertex, Width + Angle, + Author, + Category, + Circle, + Created, + Deprecated, + Description, + Diameter, + Fill, + GeneratedBy, + GrabArea, + Keywords, + Layer, + Name, + Position, + Position3D, + Rotation, + Rotation3D, + Version, + Vertex, + Width, ) from entities.component import SignalUUID from entities.device import ComponentPad, ComponentUUID, Device, PackageUUID from entities.package import ( - AssemblyType, ComponentSide, CopperClearance, DrillDiameter, Footprint, FootprintPad, Hole, Package, PackagePad, - PackagePadUuid, PadFunction, PadHole, Shape, ShapeRadius, Size, SolderPasteConfig, StopMaskConfig, Zone + AssemblyType, + ComponentSide, + CopperClearance, + DrillDiameter, + Footprint, + FootprintPad, + Hole, + Package, + PackagePad, + PackagePadUuid, + PadFunction, + PadHole, + Shape, + ShapeRadius, + Size, + SolderPasteConfig, + StopMaskConfig, + Zone, ) generator = 'librepcb-parts-generator (generate_mounting_holes.py)' @@ -53,7 +89,7 @@ def generate_pkg( hole_diameter: float, pad_diameter: float, ) -> None: - full_name = f"MOUNTING_HOLE_{name}" + full_name = f'MOUNTING_HOLE_{name}' full_desc = f"""Generic mounting hole for {name} screws, compatible with ISO7380, ISO14580 and DIN965. Hole diameter: {hole_diameter:.2f} mm @@ -61,7 +97,7 @@ def generate_pkg( Generated with {generator} """ - keywords = f"mounting,hole,pad,drill,screw,{name},{hole_diameter}mm,{pad_diameter}mm" + keywords = f'mounting,hole,pad,drill,screw,{name},{hole_diameter}mm,{pad_diameter}mm' def _uuid(identifier: str) -> str: return uuid('pkg', name.lower(), identifier) @@ -101,126 +137,143 @@ def _add_footprint(name: str, pad: bool, cover: bool) -> None: # Pad or hole if pad: uuid_pad = _uuid(uuid_ns + 'pad') - footprint.add_pad(FootprintPad( - uuid=uuid_pad, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(0, 0), - rotation=Rotation(0), - size=Size(pad_diameter, pad_diameter), - radius=ShapeRadius(1), - stop_mask=StopMaskConfig(stopmask_excess), - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(copper_clearance), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_pkg_pad), - holes=[PadHole(uuid_pad, DrillDiameter(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(0, 0), + rotation=Rotation(0), + size=Size(pad_diameter, pad_diameter), + radius=ShapeRadius(1), + stop_mask=StopMaskConfig(stopmask_excess), + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(copper_clearance), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_pkg_pad), + holes=[ + PadHole( + uuid_pad, + DrillDiameter(hole_diameter), + [Vertex(Position(0.0, 0.0), Angle(0.0))], + ) + ], + ) + ) package.add_approval( - "(approved pad_with_copper_clearance\n" + - " (footprint {})\n".format(footprint.uuid) + - " (pad {})\n".format(uuid_pad) + - ")" + '(approved pad_with_copper_clearance\n' + + ' (footprint {})\n'.format(footprint.uuid) + + ' (pad {})\n'.format(uuid_pad) + + ')' ) else: - footprint.add_hole(Hole( - uuid=_uuid(uuid_ns + 'hole'), - diameter=DrillDiameter(hole_diameter), - vertices=[Vertex(Position(0.0, 0.0), Angle(0.0))], - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - )) + footprint.add_hole( + Hole( + uuid=_uuid(uuid_ns + 'hole'), + diameter=DrillDiameter(hole_diameter), + vertices=[Vertex(Position(0.0, 0.0), Angle(0.0))], + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + ) + ) zone_y = (pad_diameter / 2) + copper_clearance - footprint.add_zone(Zone( - uuid=_uuid(uuid_ns + 'zone'), - top=True, - inner=False, - bottom=True, - no_copper=True, - no_planes=True, - no_exposure=cover, - no_devices=False, - vertices=[ - Vertex(Position(0.0, zone_y), Angle(180.0)), - Vertex(Position(0.0, -zone_y), Angle(180.0)), - Vertex(Position(0.0, zone_y), Angle(0.0)), - ], - )) + footprint.add_zone( + Zone( + uuid=_uuid(uuid_ns + 'zone'), + top=True, + inner=False, + bottom=True, + no_copper=True, + no_planes=True, + no_exposure=cover, + no_devices=False, + vertices=[ + Vertex(Position(0.0, zone_y), Angle(180.0)), + Vertex(Position(0.0, -zone_y), Angle(180.0)), + Vertex(Position(0.0, zone_y), Angle(0.0)), + ], + ) + ) for side in ['top', 'bot']: # Stop mask if not pad and not cover: - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-stopmask-' + side), - layer=Layer(side + '_stop_mask'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - diameter=Diameter(pad_diameter + 2 * stopmask_excess), - position=Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-stopmask-' + side), + layer=Layer(side + '_stop_mask'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + diameter=Diameter(pad_diameter + 2 * stopmask_excess), + position=Position(0, 0), + ) + ) # Documentation - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-documentation-' + side), - layer=Layer(side + '_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(True), - diameter=Diameter(pad_diameter + line_width + 2 * legend_clearance), - position=Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-documentation-' + side), + layer=Layer(side + '_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(True), + diameter=Diameter(pad_diameter + line_width + 2 * legend_clearance), + position=Position(0, 0), + ) + ) # Legend - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-legend-' + side), - layer=Layer(side + '_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(True), - diameter=Diameter(pad_diameter + line_width + 2 * legend_clearance), - position=Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-legend-' + side), + layer=Layer(side + '_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(True), + diameter=Diameter(pad_diameter + line_width + 2 * legend_clearance), + position=Position(0, 0), + ) + ) # Package outline - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-outline-' + side), - layer=Layer(side + '_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(pad_diameter + 2 * legend_clearance), - position=Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-outline-' + side), + layer=Layer(side + '_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(pad_diameter + 2 * legend_clearance), + position=Position(0, 0), + ) + ) # Courtyard - footprint.add_circle(Circle( - uuid=_uuid(uuid_ns + 'circle-courtyard-' + side), - layer=Layer(side + '_courtyard'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(pad_diameter + 2 * courtyard_excess), - position=Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid(uuid_ns + 'circle-courtyard-' + side), + layer=Layer(side + '_courtyard'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(pad_diameter + 2 * courtyard_excess), + position=Position(0, 0), + ) + ) # Approvals package.add_approval( - "(approved missing_name_text\n" + - " (footprint {})\n".format(footprint.uuid) + - ")" + '(approved missing_name_text\n' + ' (footprint {})\n'.format(footprint.uuid) + ')' ) package.add_approval( - "(approved missing_value_text\n" + - " (footprint {})\n".format(footprint.uuid) + - ")" + '(approved missing_value_text\n' + ' (footprint {})\n'.format(footprint.uuid) + ')' ) _add_footprint('copper', pad=True, cover=False) _add_footprint('covered', pad=False, cover=True) _add_footprint('blank', pad=False, cover=False) - package.add_approval("(approved suspicious_assembly_type)") + package.add_approval('(approved suspicious_assembly_type)') package.serialize(path.join('out', library, 'pkg')) @@ -234,7 +287,7 @@ def generate_dev( hole_diameter: float, pad_diameter: float, ) -> None: - full_name = f"Mounting Hole {name}" + full_name = f'Mounting Hole {name}' full_desc = f"""Generic mounting hole for {name} screws, compatible with ISO7380, ISO14580 and DIN965. Hole diameter: {hole_diameter:.2f} mm @@ -242,7 +295,7 @@ def generate_dev( Generated with {generator} """ - keywords = f"mounting,hole,pad,drill,screw,{name},{hole_diameter}mm,{pad_diameter}mm" + keywords = f'mounting,hole,pad,drill,screw,{name},{hole_diameter}mm,{pad_diameter}mm' def _uuid(identifier: str) -> str: return uuid('dev', name.lower(), identifier) @@ -269,9 +322,12 @@ def _uuid(identifier: str) -> str: package_uuid=PackageUUID(uuid('pkg', name.lower(), 'pkg')), ) - device.add_pad(ComponentPad(uuid('pkg', name.lower(), 'pad'), - SignalUUID('c8721bab-6c90-43f6-8135-c32fce7aecc0'))) - device.add_approval("(approved no_parts)") + device.add_pad( + ComponentPad( + uuid('pkg', name.lower(), 'pad'), SignalUUID('c8721bab-6c90-43f6-8135-c32fce7aecc0') + ) + ) + device.add_approval('(approved no_parts)') device.serialize(path.join('out', library, 'dev')) diff --git a/generate_qfp.py b/generate_qfp.py index b2c6cf4c..11b2bd93 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,56 @@ 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', 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, 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', 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, 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'), ] @@ -378,7 +437,9 @@ def add_footprint_variant( ) -> None: # UUIDs uuid_footprint = _uuid('footprint-{}'.format(key)) - uuid_silkscreen = [_uuid('polygon-silkscreen-{}-{}'.format(quadrant, key)) for quadrant in [1, 2, 3, 4]] + uuid_silkscreen = [ + _uuid('polygon-silkscreen-{}-{}'.format(quadrant, key)) for quadrant in [1, 2, 3, 4] + ] uuid_body = _uuid('polygon-body-{}'.format(key)) uuid_pin1_dot = _uuid('pin1-dot-{}'.format(key)) uuid_outline = _uuid('polygon-outline-{}'.format(key)) @@ -390,11 +451,15 @@ 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) - pos_last = get_pad_coords(config.lead_count, config.lead_count, config.pitch, lead_contact_x_offset) + pos_last = get_pad_coords( + config.lead_count, config.lead_count, config.pitch, lead_contact_x_offset + ) footprint = Footprint( uuid=uuid_footprint, @@ -413,21 +478,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(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(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 +514,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,72 +542,92 @@ 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) for quadrant in [1, 2, 3, 4]: uuid = uuid_silkscreen[quadrant - 1] - x_min = abs(pos_last.x) + config.lead_width / 2 + excess.side + silkscreen_offset + line_width / 2 + x_min = ( + abs(pos_last.x) + + config.lead_width / 2 + + excess.side + + silkscreen_offset + + line_width / 2 + ) x_max = config.body_size_x / 2 + line_width / 2 - y_min = abs(pos_first.y) + config.lead_width / 2 + excess.side + silkscreen_offset + line_width / 2 + y_min = ( + abs(pos_first.y) + + config.lead_width / 2 + + excess.side + + silkscreen_offset + + line_width / 2 + ) y_max = config.body_size_y / 2 + line_width / 2 vertices = [(x_min, y_max), (x_max, y_max), (x_max, y_min)] # 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 @@ -556,7 +645,9 @@ def add_footprint_variant( ) footprint.add_circle(pin1_dot) - def _create_outline_vertices(offset: float = 0, around_pads: bool = False) -> List[Vertex]: + def _create_outline_vertices( + offset: float = 0, around_pads: bool = False + ) -> List[Vertex]: x_max = config.lead_span_x / 2 x_mid = config.body_size_x / 2 x_min = abs(pos_last.x) + config.lead_width / 2 @@ -570,13 +661,29 @@ 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), + (-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)) @@ -584,55 +691,63 @@ def _create_outline_vertices(offset: float = 0, around_pads: bool = False) -> Li ] # 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 +790,35 @@ 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 +828,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_screw_terminals.py b/generate_screw_terminals.py index 6d523490..17d38fb7 100644 --- a/generate_screw_terminals.py +++ b/generate_screw_terminals.py @@ -1,6 +1,7 @@ """ Generate screw terminal packages & devices """ + import math import sys from os import path @@ -11,16 +12,60 @@ from common import init_cache, now, save_cache from entities.attribute import Attribute, AttributeType from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Resource, 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, + Resource, + 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, 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_screw_terminals.py)' @@ -60,16 +105,32 @@ def __init__(self, x: float, width: float, height: float): class Family: - def __init__(self, manufacturer: str, pkg_name_prefix: str, - dev_name_prefix: str, pitch: float, drill: float, - pad_diameter: float, top: float, bottom: float, left: float, - right: float, height: float, lead_diameter: float, - lead_length: float, opening_width_bottom: float, - opening_width: float, opening_height: float, - screw_hole_diameter: float, conductor_cross_section: str, - walls_length: float, nipples_bottom: List[Nipple], - datasheet: Optional[str], keywords: List[str], - draw_body_sketch_fn: Callable[[Any], Any]) -> None: + def __init__( + self, + manufacturer: str, + pkg_name_prefix: str, + dev_name_prefix: str, + pitch: float, + drill: float, + pad_diameter: float, + top: float, + bottom: float, + left: float, + right: float, + height: float, + lead_diameter: float, + lead_length: float, + opening_width_bottom: float, + opening_width: float, + opening_height: float, + screw_hole_diameter: float, + conductor_cross_section: str, + walls_length: float, + nipples_bottom: List[Nipple], + datasheet: Optional[str], + keywords: List[str], + draw_body_sketch_fn: Callable[[Any], Any], + ) -> None: self.manufacturer = manufacturer self.pkg_name_prefix = pkg_name_prefix self.dev_name_prefix = dev_name_prefix @@ -96,16 +157,19 @@ def __init__(self, manufacturer: str, pkg_name_prefix: str, class Model: - def __init__(self, name: str, mpn: str, circuits: int, - datasheet: Optional[str] = None) -> None: + def __init__(self, name: str, mpn: str, circuits: int, datasheet: Optional[str] = None) -> None: self.name = name self.mpn = mpn self.circuits = circuits self.datasheet = datasheet def uuid_key(self, family: Family) -> str: - return '{}-{}'.format(family.pkg_name_prefix, model.name) \ - .lower().replace(' ', '').replace(',', 'p') + return ( + '{}-{}'.format(family.pkg_name_prefix, model.name) + .lower() + .replace(' ', '') + .replace(',', 'p') + ) def get_description(self, family: Family) -> str: return f"""Screw terminal from {family.manufacturer}: @@ -119,13 +183,16 @@ def get_description(self, family: Family) -> str: """ def get_keywords(self, family: Family) -> str: - return ','.join([ - 'screw', - 'terminal', - 'block', - '{:g}mm'.format(family.pitch), - '1x{}'.format(self.circuits), - ] + family.keywords) + return ','.join( + [ + 'screw', + 'terminal', + 'block', + '{:g}mm'.format(family.pitch), + '1x{}'.format(self.circuits), + ] + + family.keywords + ) def get_datasheet(self, family: Family) -> Optional[str]: ds = self.datasheet @@ -186,67 +253,82 @@ def _uuid(identifier: str) -> str: uuid_fpt_pad = _uuid('default-pad-{}'.format(pad_name)) package.add_pad(PackagePad(uuid=uuid_pkg_pad, name=Name(pad_name))) y = pad_1_y - (i * family.pitch) - footprint.add_pad(FootprintPad( - uuid=uuid_fpt_pad, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(0, y), - rotation=Rotation(0), - size=Size(family.pad_diameter, family.pad_diameter), - radius=ShapeRadius(0.0 if (i == 0) else 1.0), - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_pkg_pad), - holes=[PadHole(uuid_fpt_pad, DrillDiameter(family.drill), - [Vertex(Position(0.0, 0.0), Angle(0.0))])], - )) - footprint.add_circle(Circle( - uuid=_uuid('default-circle-{}'.format(pad_name)), - layer=Layer('top_documentation'), - width=Width(line_width * 0.75), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(family.screw_hole_diameter), - position=Position(0, y), - )) - footprint.add_polygon(Polygon( - uuid=_uuid('default-screw-upper-{}'.format(pad_name)), - layer=Layer('top_documentation'), - width=Width(line_width * 0.75), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=create_screw_diagonal(y, family.screw_hole_diameter, 1), - )) - footprint.add_polygon(Polygon( - uuid=_uuid('default-screw-lower-{}'.format(pad_name)), - layer=Layer('top_documentation'), - width=Width(line_width * 0.75), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=create_screw_diagonal(y, family.screw_hole_diameter, -1), - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_fpt_pad, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(0, y), + rotation=Rotation(0), + size=Size(family.pad_diameter, family.pad_diameter), + radius=ShapeRadius(0.0 if (i == 0) else 1.0), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_pkg_pad), + holes=[ + PadHole( + uuid_fpt_pad, + DrillDiameter(family.drill), + [Vertex(Position(0.0, 0.0), Angle(0.0))], + ) + ], + ) + ) + footprint.add_circle( + Circle( + uuid=_uuid('default-circle-{}'.format(pad_name)), + layer=Layer('top_documentation'), + width=Width(line_width * 0.75), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(family.screw_hole_diameter), + position=Position(0, y), + ) + ) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-screw-upper-{}'.format(pad_name)), + layer=Layer('top_documentation'), + width=Width(line_width * 0.75), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=create_screw_diagonal(y, family.screw_hole_diameter, 1), + ) + ) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-screw-lower-{}'.format(pad_name)), + layer=Layer('top_documentation'), + width=Width(line_width * 0.75), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=create_screw_diagonal(y, family.screw_hole_diameter, -1), + ) + ) # Documentation outline top = pad_1_y + family.top - (line_width / 2) bottom = -pad_1_y - family.bottom + (line_width / 2) left = -family.left + (line_width / 2) right = family.right - (line_width / 2) - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-documentation'), - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - Vertex(Position(left, top), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-documentation'), + layer=Layer('top_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + Vertex(Position(left, top), Angle(0)), + ], + ) + ) # Documentation walls if family.walls_length > 0: @@ -257,8 +339,32 @@ def _uuid(identifier: str) -> str: bottom = max(y - walls_dy / 2, -pad_1_y - family.bottom + (line_width / 2)) left = -family.left - family.walls_length right = -family.left - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-documentation-wall-{}'.format(i + 1)), + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-documentation-wall-{}'.format(i + 1)), + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + Vertex(Position(left, top), Angle(0)), + ], + ) + ) + + # Documentation nipples + for i, nipple in enumerate(family.nipples_bottom): + top = -pad_1_y - family.bottom + bottom = top - nipple.height + left = nipple.x - (nipple.width / 2) + right = nipple.x + (nipple.width / 2) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-documentation-nipple-bottom-{}'.format(i + 1)), layer=Layer('top_documentation'), width=Width(0), fill=Fill(True), @@ -270,28 +376,8 @@ def _uuid(identifier: str) -> str: Vertex(Position(left, bottom), Angle(0)), Vertex(Position(left, top), Angle(0)), ], - )) - - # Documentation nipples - for i, nipple in enumerate(family.nipples_bottom): - top = -pad_1_y - family.bottom - bottom = top - nipple.height - left = nipple.x - (nipple.width / 2) - right = nipple.x + (nipple.width / 2) - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-documentation-nipple-bottom-{}'.format(i + 1)), - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - Vertex(Position(left, top), Angle(0)), - ], - )) + ) + ) # Legend outline top = pad_1_y + family.top + (line_width / 2) @@ -318,64 +404,72 @@ def _uuid(identifier: str) -> str: Vertex(Position(left, bottom), Angle(0)), Vertex(Position(left, -dy), Angle(0)), ] - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-legend'), - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=legend_outline_vertices, - )) - for i in range(model.circuits - 1): - top = pad_1_y - (i * family.pitch) - (family.opening_width_bottom / 2) - bottom = top - (family.pitch - family.opening_width_bottom) - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-legend-{}'.format(i + 1)), + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-legend'), layer=Layer('top_legend'), width=Width(line_width), fill=Fill(False), grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top - (line_width / 2)), Angle(0)), - Vertex(Position(left, bottom + (line_width / 2)), Angle(0)), - ], - )) + vertices=legend_outline_vertices, + ) + ) + for i in range(model.circuits - 1): + top = pad_1_y - (i * family.pitch) - (family.opening_width_bottom / 2) + bottom = top - (family.pitch - family.opening_width_bottom) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-legend-{}'.format(i + 1)), + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top - (line_width / 2)), Angle(0)), + Vertex(Position(left, bottom + (line_width / 2)), Angle(0)), + ], + ) + ) # Pin-1 marking triangle_right = -family.screw_hole_diameter / 2 for layer in ['legend', 'documentation']: - footprint.add_polygon(Polygon( - uuid=_uuid('default-triangle-' + layer), - layer=Layer('top_' + layer), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(triangle_right - 0.7, pad_1_y + 0.6), Angle(0)), - Vertex(Position(triangle_right, pad_1_y), Angle(0)), - Vertex(Position(triangle_right - 0.7, pad_1_y - 0.6), Angle(0)), - Vertex(Position(triangle_right - 0.7, pad_1_y + 0.6), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-triangle-' + layer), + layer=Layer('top_' + layer), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(triangle_right - 0.7, pad_1_y + 0.6), Angle(0)), + Vertex(Position(triangle_right, pad_1_y), Angle(0)), + Vertex(Position(triangle_right - 0.7, pad_1_y - 0.6), Angle(0)), + Vertex(Position(triangle_right - 0.7, pad_1_y + 0.6), Angle(0)), + ], + ) + ) # Package outline top = pad_1_y + family.top bottom = -pad_1_y - family.bottom left = -family.left right = family.right - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-outline'), - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-outline'), + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + ], + ) + ) # Courtyard top = max(legend_outline_vertices, key=lambda v: v.position.y).position.y @@ -384,51 +478,57 @@ def _uuid(identifier: str) -> str: bottom -= courtyard_excess - (line_width / 2) left = -family.left - family.walls_length - courtyard_excess right = family.right + courtyard_excess - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-courtyard'), - layer=Layer('top_courtyard'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-courtyard'), + layer=Layer('top_courtyard'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + ], + ) + ) # Labels top = max(legend_outline_vertices, key=lambda v: v.position.y).position.y bottom = min(legend_outline_vertices, key=lambda v: v.position.y).position.y - footprint.add_text(StrokeText( - uuid=_uuid('default-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 + 0.4), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_uuid('default-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 - 0.4), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_uuid('default-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 + 0.4), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_uuid('default-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 - 0.4), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # Generate 3D model uuid_3d = _uuid('3d') @@ -458,45 +558,58 @@ def generate_3d_model( pad1_y = ((model.circuits - 1) * family.pitch) / 2 body_length = 2 * pad1_y + family.top + family.bottom - body = cq.Workplane('XZ') \ - .transformed(offset=(0, 0, -pad1_y - family.bottom)) \ - .tag('top') + body = cq.Workplane('XZ').transformed(offset=(0, 0, -pad1_y - family.bottom)).tag('top') body = family.draw_body_sketch_fn(body).close().extrude(body_length) for nipple in family.nipples_bottom: - body = body.workplaneFromTagged('top') \ - .workplane(offset=body_length) \ - .transformed(offset=(nipple.x, family.height - 1.5, 0)) \ + body = ( + body.workplaneFromTagged('top') + .workplane(offset=body_length) + .transformed(offset=(nipple.x, family.height - 1.5, 0)) .box(nipple.width, 3.0, 2 * nipple.height, centered=True) + ) for i in range(model.circuits): y = pad1_y - (i * family.pitch) - body = body.cut(cq.Workplane('XY', origin=(0.0, y, 0.0)) - .cylinder(100, family.screw_hole_diameter / 2)) - body = body.cut(cq.Workplane('YZ', origin=(-family.left, y, (family.opening_height / 2) + 0.5)) - .box(family.opening_width, family.opening_height - 1.0, 2 * family.left)) - body = body.cut(cq.Workplane('YZ', origin=(-family.left, y, 0.0)) - .box(family.opening_width_bottom, 2.2, 2 * family.left)) - - screw = cq.Workplane("XY").tag('bottom') \ - .cylinder(family.height - 0.2, family.screw_hole_diameter / 2, - centered=(True, True, False)) \ - .workplane(offset=(family.height / 2)) \ - .box(100, family.screw_hole_diameter * 0.2, 1.0, combine='cut') \ - .rotate((0.0, 0.0, 0.0), (0.0, 0.0, 1.0), 45.0) \ - .workplaneFromTagged('bottom') \ - .workplane(offset=(((family.opening_height - 1.5) / 2) + 1.0)) \ + body = body.cut( + cq.Workplane('XY', origin=(0.0, y, 0.0)).cylinder(100, family.screw_hole_diameter / 2) + ) + body = body.cut( + cq.Workplane('YZ', origin=(-family.left, y, (family.opening_height / 2) + 0.5)).box( + family.opening_width, family.opening_height - 1.0, 2 * family.left + ) + ) + body = body.cut( + cq.Workplane('YZ', origin=(-family.left, y, 0.0)).box( + family.opening_width_bottom, 2.2, 2 * family.left + ) + ) + + screw = ( + cq.Workplane('XY') + .tag('bottom') + .cylinder(family.height - 0.2, family.screw_hole_diameter / 2, centered=(True, True, False)) + .workplane(offset=(family.height / 2)) + .box(100, family.screw_hole_diameter * 0.2, 1.0, combine='cut') + .rotate((0.0, 0.0, 0.0), (0.0, 0.0, 1.0), 45.0) + .workplaneFromTagged('bottom') + .workplane(offset=(((family.opening_height - 1.5) / 2) + 1.0)) .box(100, family.opening_width - 0.8, family.opening_height - 4.0, combine='cut') - leg = cq.Workplane("XY").workplane(offset=(-1), invert=True) \ - .cylinder(family.lead_length + 1, family.lead_diameter / 2, - centered=(True, True, False)) + ) + leg = ( + cq.Workplane('XY') + .workplane(offset=(-1), invert=True) + .cylinder(family.lead_length + 1, family.lead_diameter / 2, centered=(True, True, False)) + ) assembly = StepAssembly(full_name) assembly.add_body(body, 'body', cq.Color('green3')) for i in range(model.circuits): y = pad1_y - (i * family.pitch) - assembly.add_body(screw, 'screw-{}'.format(i + 1), StepColor.LEAD_THT, - location=cq.Location((0, y, 0))) - assembly.add_body(leg, 'leg-{}'.format(i + 1), StepColor.LEAD_THT, - location=cq.Location((0, y, 0))) + assembly.add_body( + screw, 'screw-{}'.format(i + 1), StepColor.LEAD_THT, location=cq.Location((0, y, 0)) + ) + assembly.add_body( + leg, 'leg-{}'.format(i + 1), StepColor.LEAD_THT, location=cq.Location((0, y, 0)) + ) # Save without fusing for massively better minification! out_path = path.join('out', library, 'pkg', uuid_pkg, f'{uuid_3d}.step') @@ -511,7 +624,7 @@ def generate_dev( family: Family, model: Model, ) -> None: - full_name = f"{family.dev_name_prefix} {model.name}" + full_name = f'{family.dev_name_prefix} {model.name}' def _uuid(identifier: str) -> str: return uuid('dev', model.uuid_key(family), identifier) @@ -522,8 +635,9 @@ def _uuid(identifier: str) -> str: connector_uuid_stub = f'cmp-screwterminal-1x{model.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(model.circuits)] + signal_uuids = [ + uuid_cache_connectors[f'{connector_uuid_stub}-signal-{i}'] for i in range(model.circuits) + ] device = Device( uuid=uuid_dev, @@ -544,18 +658,28 @@ def _uuid(identifier: str) -> str: pad_uuid = uuid('pkg', model.uuid_key(family), 'pad-{}'.format(i + 1)) device.add_pad(ComponentPad(pad_uuid, SignalUUID(signal_uuids[i]))) - device.add_part(Part(model.mpn, Manufacturer(family.manufacturer), [ - Attribute('PITCH', f'{family.pitch:.2f} mm', AttributeType.STRING, None), - Attribute('CONDUCTOR', f'{family.conductor_cross_section}', AttributeType.STRING, None), - ])) + device.add_part( + Part( + model.mpn, + Manufacturer(family.manufacturer), + [ + Attribute('PITCH', f'{family.pitch:.2f} mm', AttributeType.STRING, None), + Attribute( + 'CONDUCTOR', f'{family.conductor_cross_section}', AttributeType.STRING, None + ), + ], + ) + ) datasheet = model.get_datasheet(family) if datasheet: - device.add_resource(Resource( - name='Datasheet {}'.format(model.name), - mediatype='application/pdf', - url=datasheet, - )) + device.add_resource( + Resource( + name='Datasheet {}'.format(model.name), + mediatype='application/pdf', + url=datasheet, + ) + ) device.serialize(path.join('out', library, 'dev')) @@ -600,18 +724,17 @@ def _uuid(identifier: str) -> str: ], datasheet='https://www.phoenixcontact.com/us/products/{mpn}/pdf', keywords=[], - draw_body_sketch_fn=lambda workplane: - workplane.moveTo(4.0, 0.0) - .lineTo(4.0, 3.5) - .lineTo(2.5, 11.4) - .lineTo(-2.5, 11.4) - .lineTo(-3.8, 6.5) - .lineTo(-4.5, 6.5) - .ellipseArc(x_radius=0.5, y_radius=0.5, angle1=90, angle2=180, sense=1) - .lineTo(-5.0, 4.0) - .ellipseArc(x_radius=0.5, y_radius=0.5, angle1=180, angle2=270, sense=1) - .lineTo(-4.3, 3.5) - .lineTo(-4.3, 0.0) + draw_body_sketch_fn=lambda workplane: workplane.moveTo(4.0, 0.0) + .lineTo(4.0, 3.5) + .lineTo(2.5, 11.4) + .lineTo(-2.5, 11.4) + .lineTo(-3.8, 6.5) + .lineTo(-4.5, 6.5) + .ellipseArc(x_radius=0.5, y_radius=0.5, angle1=90, angle2=180, sense=1) + .lineTo(-5.0, 4.0) + .ellipseArc(x_radius=0.5, y_radius=0.5, angle1=180, angle2=270, sense=1) + .lineTo(-4.3, 3.5) + .lineTo(-4.3, 0.0), ) models = [ Model(name='PT 1,5/2-5,0-H', mpn='1935161', circuits=2), @@ -675,14 +798,13 @@ def _uuid(identifier: str) -> str: ], datasheet='https://www.phoenixcontact.com/us/products/{mpn}/pdf', keywords=[], - draw_body_sketch_fn=lambda workplane: - workplane.moveTo(4.5, 0.0) - .lineTo(4.5, 13.5) - .lineTo(-2.5, 13.5) - .lineTo(-3.5, 6.5) - .lineTo(-4.0, 6.5) - .ellipseArc(x_radius=0.5, y_radius=0.5, angle1=90, angle2=180, sense=1) - .lineTo(-4.5, 0.0) + draw_body_sketch_fn=lambda workplane: workplane.moveTo(4.5, 0.0) + .lineTo(4.5, 13.5) + .lineTo(-2.5, 13.5) + .lineTo(-3.5, 6.5) + .lineTo(-4.0, 6.5) + .ellipseArc(x_radius=0.5, y_radius=0.5, angle1=90, angle2=180, sense=1) + .lineTo(-4.5, 0.0), ) models = [ Model(name='PT 2,5/2-5,0-H', mpn='1935776', circuits=2), diff --git a/generate_so.py b/generate_so.py index 7f75fea9..2fb9f3ee 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,26 +284,30 @@ 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(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(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) # Documentation: Leads - lead_contact_x_offset = total_width / 2 - lead_contact_length # this is the inner side of the contact area + lead_contact_x_offset = ( + total_width / 2 - lead_contact_length + ) # this is the inner side of the contact area for p in range(1, pin_count + 1): mid = pin_count // 2 if p <= mid: # left side @@ -278,35 +325,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 +366,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 +435,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 +506,9 @@ 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 +544,33 @@ 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) + .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) + ) + 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 +579,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 +622,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 +646,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 +670,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 +694,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 +713,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 +807,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 +894,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 +932,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_sod.py b/generate_sod.py index fd88f12d..132f0ca2 100644 --- a/generate_sod.py +++ b/generate_sod.py @@ -1,6 +1,7 @@ """ Generate SOD diode packages """ + import sys from os import path from uuid import uuid4 @@ -9,13 +10,55 @@ 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.package import ( - AlternativeName, AssemblyType, AutoRotate, ComponentSide, CopperClearance, Footprint, Footprint3DModel, - FootprintPad, LetterSpacing, LineSpacing, Mirror, Package, Package3DModel, PackagePad, PackagePadUuid, PadFunction, - Shape, ShapeRadius, Size, SolderPasteConfig, StopMaskConfig, StrokeText, StrokeWidth + AlternativeName, + 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_sod.py)' @@ -66,6 +109,7 @@ class FootprintConfig: L = Length, W = Width, G = Gap """ + def __init__( self, key: str, @@ -95,7 +139,7 @@ def __init__( lead_thickness: float, footprints: Iterable[FootprintConfig], flat: bool, - 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.name = name self.alternative_names = alternative_names @@ -120,7 +164,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: @@ -132,11 +176,17 @@ def generate_pkg( 'meta': config.meta, } full_name = config.name # Not generated due to non-standard rounding - full_desc = description.format(**fmt_params_desc) + \ - "\n\nGenerated with {}".format(generator) - full_keywords = ",".join(filter(None, [ - keywords.format(**fmt_params_desc).lower(), - ])) + full_desc = description.format(**fmt_params_desc) + '\n\nGenerated with {}'.format( + generator + ) + full_keywords = ','.join( + filter( + None, + [ + keywords.format(**fmt_params_desc).lower(), + ], + ) + ) def _uuid(identifier: str) -> str: return uuid(category, full_name, identifier) @@ -198,76 +248,84 @@ def add_footprint_variant(fpt_config: FootprintConfig) -> None: package.add_footprint(footprint) # Pads - pad_dx = (fpt_config.pad_gap / 2 + fpt_config.pad_length / 2) # x offset (delta-x) + pad_dx = fpt_config.pad_gap / 2 + fpt_config.pad_length / 2 # x offset (delta-x) for p in [(uuid_pad_c, -1), (uuid_pad_a, 1)]: - footprint.add_pad(FootprintPad( - uuid=p[0], - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(p[1] * pad_dx, 0), - rotation=Rotation(0), - size=Size(fpt_config.pad_length, fpt_config.pad_width), - radius=ShapeRadius(0), - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0.0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(p[0]), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=p[0], + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(p[1] * pad_dx, 0), + rotation=Rotation(0), + size=Size(fpt_config.pad_length, fpt_config.pad_width), + radius=ShapeRadius(0), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0.0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(p[0]), + holes=[], + ) + ) # Documentation dx = config.body_length / 2 - doc_lw / 2 dy = config.body_width / 2 - doc_lw / 2 - footprint.add_polygon(Polygon( - uuid=uuid_body, - 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, + 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)), + ], + ) + ) dx0 = config.lead_span / 2 dx1 = config.body_length / 2 dy = config.lead_width / 2 for p_uuid, sign in [(uuid_lead_left, -1), (uuid_lead_right, 1)]: - footprint.add_polygon(Polygon( - uuid=p_uuid, + footprint.add_polygon( + Polygon( + uuid=p_uuid, + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(dx0 * sign, dy), Angle(0)), + Vertex(Position(dx1 * sign, dy), Angle(0)), + Vertex(Position(dx1 * sign, -dy), Angle(0)), + Vertex(Position(dx0 * sign, -dy), Angle(0)), + Vertex(Position(dx0 * sign, dy), Angle(0)), + ], + ) + ) + dx_outer = ((config.body_length / 2) - doc_lw) * 0.75 + dx_inner = ((config.body_length / 2) - doc_lw) * 0.4 + 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(False), + grab_area=GrabArea(True), vertices=[ - Vertex(Position(dx0 * sign, dy), Angle(0)), - Vertex(Position(dx1 * sign, dy), Angle(0)), - Vertex(Position(dx1 * sign, -dy), Angle(0)), - Vertex(Position(dx0 * sign, -dy), Angle(0)), - Vertex(Position(dx0 * sign, dy), Angle(0)), + 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)), ], - )) - dx_outer = ((config.body_length / 2) - doc_lw) * 0.75 - dx_inner = ((config.body_length / 2) - doc_lw) * 0.4 - 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)), - ], - )) + ) + ) # Silkscreen x_left = -(pad_dx + fpt_config.pad_length / 2 + silk_lw / 2 + silkscreen_clearance) @@ -276,45 +334,51 @@ def add_footprint_variant(fpt_config: FootprintConfig) -> None: config.body_width / 2 + silk_lw / 2, # Based on body width fpt_config.pad_width / 2 + silk_lw / 2 + silkscreen_clearance, # Based on pad width ) - footprint.add_polygon(Polygon( - uuid=uuid_silkscreen, - layer=Layer('top_legend'), - width=Width(silk_lw), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(x_right, dy), Angle(0)), - Vertex(Position(x_left, dy), Angle(0)), - Vertex(Position(x_left, -dy), Angle(0)), - Vertex(Position(x_right, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=uuid_silkscreen, + layer=Layer('top_legend'), + width=Width(silk_lw), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(x_right, dy), Angle(0)), + Vertex(Position(x_left, dy), Angle(0)), + Vertex(Position(x_left, -dy), Angle(0)), + Vertex(Position(x_right, -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 - footprint.add_polygon(generate_courtyard( - uuid=uuid_courtyard, - max_x=config.lead_span / 2, - max_y=config.body_width / 2, - excess_x=courtyard_excess, - excess_y=courtyard_excess, - )) + footprint.add_polygon( + generate_courtyard( + uuid=uuid_courtyard, + max_x=config.lead_span / 2, + max_y=config.body_width / 2, + excess_x=courtyard_excess, + excess_y=courtyard_excess, + ) + ) # Labels if config.body_width < 2.0: @@ -322,34 +386,38 @@ def add_footprint_variant(fpt_config: FootprintConfig) -> None: 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}}'), + ) + ) for fpt in config.footprints: add_footprint_variant(fpt) @@ -386,29 +454,43 @@ def generate_3d( marking_width = config.body_length * 0.2 marking_pos = config.body_length * 0.25 - body = cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) \ - .box(config.body_length, config.body_width, body_height) \ - .edges().chamfer(body_chamfer) - marking = cq.Workplane('XY', origin=(-marking_pos, 0, body_standoff + body_height)) \ - .box(marking_width, config.body_width - 2 * body_chamfer, 0.05) + body = ( + cq.Workplane('XY', origin=(0, 0, body_standoff + (body_height / 2))) + .box(config.body_length, config.body_width, body_height) + .edges() + .chamfer(body_chamfer) + ) + marking = cq.Workplane('XY', origin=(-marking_pos, 0, body_standoff + body_height)).box( + marking_width, config.body_width - 2 * body_chamfer, 0.05 + ) if config.flat: - leg = cq.Workplane('XY', origin=(0, 0, config.lead_thickness / 2)) \ - .box(config.lead_span, config.lead_width, config.lead_thickness) + leg = cq.Workplane('XY', origin=(0, 0, config.lead_thickness / 2)).box( + config.lead_span, config.lead_width, config.lead_thickness + ) else: - leg_path = cq.Workplane("XZ") \ - .hLine(config.lead_contact_length - (config.lead_thickness / 2) - bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) \ - .vLine(leg_z_top - config.lead_thickness - (2 * bend_radius)) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .hLine(config.lead_span - (2 * bend_radius) - (2 * config.lead_contact_length) + config.lead_thickness) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ - .vLine(-(leg_z_top - config.lead_thickness - (2 * bend_radius))) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=1) \ + leg_path = ( + cq.Workplane('XZ') + .hLine(config.lead_contact_length - (config.lead_thickness / 2) - bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=270, angle2=360, sense=1) + .vLine(leg_z_top - config.lead_thickness - (2 * bend_radius)) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .hLine( + config.lead_span + - (2 * bend_radius) + - (2 * config.lead_contact_length) + + config.lead_thickness + ) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) + .vLine(-(leg_z_top - config.lead_thickness - (2 * bend_radius))) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=1) .hLine(config.lead_contact_length - (config.lead_thickness / 2) - bend_radius) - leg = cq.Workplane("ZY") \ - .rect(config.lead_thickness, config.lead_width) \ - .sweep(leg_path) \ + ) + leg = ( + cq.Workplane('ZY') + .rect(config.lead_thickness, config.lead_width) + .sweep(leg_path) .translate((-config.lead_span / 2, 0, 0)) + ) assembly = StepAssembly(full_name) assembly.add_body(body, 'body', StepColor.IC_BODY) @@ -436,35 +518,65 @@ def generate_3d( library='LibrePCB_Base.lplib', author='Danilo B., U. Bruhin', description='Small outline diode (JEDEC {meta[jedec]}).\n\n' - 'Lead span: {lead_span}\n' - 'Body length: {body_length}mm\n' - 'Body width: {body_width}mm\n' - 'Max height: {body_height}mm', + 'Lead span: {lead_span}\n' + 'Body length: {body_length}mm\n' + 'Body width: {body_width}mm\n' + 'Max height: {body_height}mm', configs=[ # https://www.diodes.com/assets/Package-Files/SOD123.pdf - Config('SOD3717X135', [AlternativeName('SOD-123', 'JEDEC')], - 2.65, 1.55, 1.05, - 3.65, 0.57, 0.3, 0.11, - [FootprintConfig('default', 'default', 0.9, 0.95, 2.25), - FootprintConfig('handsolder', 'Hand Soldering', 1.5, 0.95, 2.25)], - False, - meta={'jedec': 'SOD-123', 'keywords': 'SOD123'}), + Config( + 'SOD3717X135', + [AlternativeName('SOD-123', 'JEDEC')], + 2.65, + 1.55, + 1.05, + 3.65, + 0.57, + 0.3, + 0.11, + [ + FootprintConfig('default', 'default', 0.9, 0.95, 2.25), + FootprintConfig('handsolder', 'Hand Soldering', 1.5, 0.95, 2.25), + ], + False, + meta={'jedec': 'SOD-123', 'keywords': 'SOD123'}, + ), # https://www.diodes.com/assets/Package-Files/SOD323.pdf - Config('SOD2514X110', [AlternativeName('SOD-323', 'JEDEC')], - 1.7, 1.3, 1.05, - 2.5, 0.3, 0.3, 0.11, - [FootprintConfig('default', 'default', 0.6, 0.45, 1.51), - FootprintConfig('handsolder', 'Hand Soldering', 1.0, 0.45, 1.51)], - False, - meta={'jedec': 'SOD-323', 'keywords': 'SOD323'}), + Config( + 'SOD2514X110', + [AlternativeName('SOD-323', 'JEDEC')], + 1.7, + 1.3, + 1.05, + 2.5, + 0.3, + 0.3, + 0.11, + [ + FootprintConfig('default', 'default', 0.6, 0.45, 1.51), + FootprintConfig('handsolder', 'Hand Soldering', 1.0, 0.45, 1.51), + ], + False, + meta={'jedec': 'SOD-323', 'keywords': 'SOD323'}, + ), # https://www.diodes.com/assets/Package-Files/SOD523.pdf - Config('SOD1709X65', [AlternativeName('SOD-523', 'JEDEC')], - 1.2, 0.8, 0.6, - 1.65, 0.3, 0.3, 0.14, - [FootprintConfig('default', 'default', 0.6, 0.7, 0.8), - FootprintConfig('handsolder', 'Hand Soldering', 1.0, 0.7, 0.8)], - True, - meta={'jedec': 'SOD-523', 'keywords': 'SOD523'}), + Config( + 'SOD1709X65', + [AlternativeName('SOD-523', 'JEDEC')], + 1.2, + 0.8, + 0.6, + 1.65, + 0.3, + 0.3, + 0.14, + [ + FootprintConfig('default', 'default', 0.6, 0.7, 0.8), + FootprintConfig('handsolder', 'Hand Soldering', 1.0, 0.7, 0.8), + ], + True, + meta={'jedec': 'SOD-523', 'keywords': 'SOD523'}, + ), ], generate_3d_models=generate_3d_models, pkgcat='9b31c9b4-04b6-4f97-ad12-f095d196bd38', diff --git a/generate_sot.py b/generate_sot.py index c72e571a..4aea5b1c 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,59 @@ 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 059ad36d..17dde325 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,18 +438,16 @@ 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: """ Get a description of the symbol. """ - description = 'A {} MCU by ST Microelectronics with the following pins:\n\n'.format(self.family) + description = 'A {} MCU by ST Microelectronics with the following pins:\n\n'.format( + self.family + ) for pin_type in sorted(self.pin_types()): count = len(self.get_pin_names_by_type(pin_type)) description += '- {} {} pins\n'.format(count, pin_type) @@ -442,7 +479,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) @@ -451,7 +492,9 @@ def description(self) -> str: description += '\nGenerated with {}'.format(generator) return description - def generate_placement_data(self, debug: bool = False) -> Tuple[SymbolPinPlacement, Dict[str, str]]: + def generate_placement_data( + self, debug: bool = False + ) -> Tuple[SymbolPinPlacement, Dict[str, str]]: """ This method will generate placement data for the symbol. @@ -501,10 +544,7 @@ def __iter__(self) -> Iterator[PinName]: ] 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 +553,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 +608,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 +653,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 +676,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 +762,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 +787,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 @@ -757,7 +811,9 @@ def generate_cmp( print('Wrote cmp {}'.format(name)) -def generate_dev(mcu: MCU, symbol_map: Dict[str, str], base_lib_path: str, debug: bool = False) -> None: +def generate_dev( + mcu: MCU, symbol_map: Dict[str, str], base_lib_path: str, debug: bool = False +) -> None: """ A device will be generated for every MCU ref. """ @@ -767,17 +823,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 +859,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 +908,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_tactile_switches.py b/generate_tactile_switches.py index c9e171f8..0fbb424b 100644 --- a/generate_tactile_switches.py +++ b/generate_tactile_switches.py @@ -1,6 +1,7 @@ """ Generate various tactile switch packages & devices """ + import sys from os import path from uuid import uuid4 @@ -10,16 +11,60 @@ from common import init_cache, now, save_cache from entities.attribute import Attribute, AttributeType from entities.common import ( - Align, Angle, Author, Category, Circle, Created, Deprecated, Description, Diameter, Fill, GeneratedBy, GrabArea, - Height, Keywords, Layer, Name, Polygon, Position, Position3D, Resource, 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, + Resource, + 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, 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_tactile_switches.py)' @@ -40,10 +85,17 @@ def uuid(category: str, full_name: str, identifier: str) -> str: return uuid_cache[key] -class ThtLeadConfig(): - def __init__(self, pitch_x: float, pitch_y: float, drill: float, - pad_diameter: float, thickness: float, width: float, - length: float): +class ThtLeadConfig: + def __init__( + self, + pitch_x: float, + pitch_y: float, + drill: float, + pad_diameter: float, + thickness: float, + width: float, + length: float, + ): self.pitch_x = pitch_x self.pitch_y = pitch_y self.drill = drill @@ -53,10 +105,17 @@ def __init__(self, pitch_x: float, pitch_y: float, drill: float, self.length = length # From PCB surface to end of lead (Z) -class GullWingLeadConfig(): - def __init__(self, pitch_x: float, pitch_y: float, pad_size_x: float, - pad_size_y: float, thickness: float, width: float, - span: float): +class GullWingLeadConfig: + def __init__( + self, + pitch_x: float, + pitch_y: float, + pad_size_x: float, + pad_size_y: float, + thickness: float, + width: float, + span: float, + ): self.pitch_x = pitch_x self.pitch_y = pitch_y self.pad_size_x = pad_size_x @@ -66,9 +125,16 @@ def __init__(self, pitch_x: float, pitch_y: float, pad_size_x: float, self.span = span -class JLeadConfig(): - def __init__(self, pitch_x: float, pitch_y: float, pad_size_x: float, - pad_size_y: float, thickness: float, width: float): +class JLeadConfig: + def __init__( + self, + pitch_x: float, + pitch_y: float, + pad_size_x: float, + pad_size_y: float, + thickness: float, + width: float, + ): self.pitch_x = pitch_x self.pitch_y = pitch_y self.pad_size_x = pad_size_x @@ -78,14 +144,21 @@ def __init__(self, pitch_x: float, pitch_y: float, pad_size_x: float, class Family: - def __init__(self, manufacturer: str, pkg_name_prefix: str, - dev_name_prefix: str, body_size_x: float, body_size_y: float, - body_size_z: float, - actuator_size: Union[float, Tuple[float, float]], - actuator_color: str, - lead_config: Union[ThtLeadConfig, GullWingLeadConfig, JLeadConfig], - datasheet: Optional[str], datasheet_name: Optional[str], - keywords: List[str]) -> None: + def __init__( + self, + manufacturer: str, + pkg_name_prefix: str, + dev_name_prefix: str, + body_size_x: float, + body_size_y: float, + body_size_z: float, + actuator_size: Union[float, Tuple[float, float]], + actuator_color: str, + lead_config: Union[ThtLeadConfig, GullWingLeadConfig, JLeadConfig], + datasheet: Optional[str], + datasheet_name: Optional[str], + keywords: List[str], + ) -> None: self.manufacturer = manufacturer self.pkg_name_prefix = pkg_name_prefix self.dev_name_prefix = dev_name_prefix @@ -101,38 +174,50 @@ def __init__(self, manufacturer: str, pkg_name_prefix: str, class Model: - def __init__(self, name: str, actuator_height: float, parts: List[Part], - common_part_attributes: Optional[List[Attribute]] = None) -> None: + def __init__( + self, + name: str, + actuator_height: float, + parts: List[Part], + common_part_attributes: Optional[List[Attribute]] = None, + ) -> None: self.name = name self.actuator_height = actuator_height # From PCB surface to act. top self.parts = parts self.common_part_attributes = common_part_attributes or [] def uuid_key(self, family: Family) -> str: - return '{}-{}'.format(family.pkg_name_prefix, model.name) \ - .lower().replace(' ', '').replace(',', 'p') + return ( + '{}-{}'.format(family.pkg_name_prefix, model.name) + .lower() + .replace(' ', '') + .replace(',', 'p') + ) def get_description(self, family: Family) -> str: - s = f"Tactile switch from {family.manufacturer}." - s += f"\n\nBody Size: {family.body_size_x:.2f} x {family.body_size_y:.2f} mm" + s = f'Tactile switch from {family.manufacturer}.' + s += f'\n\nBody Size: {family.body_size_x:.2f} x {family.body_size_y:.2f} mm' if isinstance(family.lead_config, ThtLeadConfig): - s += f"\nPitch: {family.lead_config.pitch_x:.2f} x {family.lead_config.pitch_y:.2f} mm" + s += f'\nPitch: {family.lead_config.pitch_x:.2f} x {family.lead_config.pitch_y:.2f} mm' if isinstance(family.lead_config, GullWingLeadConfig): - s += f"\nLead Span: {family.lead_config.span:.2f} mm" + s += f'\nLead Span: {family.lead_config.span:.2f} mm' if not isinstance(family.lead_config, ThtLeadConfig): - s += f"\nLead Y-Pitch: {family.lead_config.pitch_y:.2f} mm" - s += f"\nActuator Height: {self.actuator_height:.2f} mm" - s += f"\n\nGenerated with {generator}" + s += f'\nLead Y-Pitch: {family.lead_config.pitch_y:.2f} mm' + s += f'\nActuator Height: {self.actuator_height:.2f} mm' + s += f'\n\nGenerated with {generator}' return s def get_keywords(self, family: Family) -> str: - return ','.join([ - 'push', - 'press', - 'button', - 'switch', - 'tactile', - ] + family.keywords) + return ','.join( + [ + 'push', + 'press', + 'button', + 'switch', + 'tactile', + ] + + family.keywords + ) def generate_pkg( @@ -167,7 +252,9 @@ def _uuid(identifier: str) -> str: Category('194951ec-03dd-412a-9828-70c40bbdd22d'), Category('c0f16db0-f0db-4121-ab12-b4570ff79738'), ], - assembly_type=AssemblyType.THT if isinstance(family.lead_config, ThtLeadConfig) else AssemblyType.SMT, + assembly_type=AssemblyType.THT + if isinstance(family.lead_config, ThtLeadConfig) + else AssemblyType.SMT, ) # Footprint @@ -188,216 +275,269 @@ def _uuid(identifier: str) -> str: x = (family.lead_config.pitch_x / 2) * (-1 if (i % 2 == 0) else 1) y = (family.lead_config.pitch_y / 2) * (1 if (i < 2) else -1) if isinstance(family.lead_config, ThtLeadConfig): - footprint.add_pad(FootprintPad( - uuid=uuid_fpt_pad, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(x, y), - rotation=Rotation(0), - size=Size(family.lead_config.pad_diameter, family.lead_config.pad_diameter), - radius=ShapeRadius(1.0), - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.OFF, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_pkg_pad), - holes=[PadHole(uuid_fpt_pad, DrillDiameter(family.lead_config.drill), - [Vertex(Position(0.0, 0.0), Angle(0.0))])], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_fpt_pad, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(x, y), + rotation=Rotation(0), + size=Size(family.lead_config.pad_diameter, family.lead_config.pad_diameter), + radius=ShapeRadius(1.0), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.OFF, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_pkg_pad), + holes=[ + PadHole( + uuid_fpt_pad, + DrillDiameter(family.lead_config.drill), + [Vertex(Position(0.0, 0.0), Angle(0.0))], + ) + ], + ) + ) else: - footprint.add_pad(FootprintPad( - uuid=uuid_fpt_pad, - side=ComponentSide.TOP, - shape=Shape.ROUNDED_RECT, - position=Position(x, y), - rotation=Rotation(0), - size=Size(family.lead_config.pad_size_x, family.lead_config.pad_size_y), - radius=ShapeRadius(0.5), - stop_mask=StopMaskConfig(StopMaskConfig.AUTO), - solder_paste=SolderPasteConfig.AUTO, - copper_clearance=CopperClearance(0), - function=PadFunction.STANDARD_PAD, - package_pad=PackagePadUuid(uuid_pkg_pad), - holes=[], - )) + footprint.add_pad( + FootprintPad( + uuid=uuid_fpt_pad, + side=ComponentSide.TOP, + shape=Shape.ROUNDED_RECT, + position=Position(x, y), + rotation=Rotation(0), + size=Size(family.lead_config.pad_size_x, family.lead_config.pad_size_y), + radius=ShapeRadius(0.5), + stop_mask=StopMaskConfig(StopMaskConfig.AUTO), + solder_paste=SolderPasteConfig.AUTO, + copper_clearance=CopperClearance(0), + function=PadFunction.STANDARD_PAD, + package_pad=PackagePadUuid(uuid_pkg_pad), + holes=[], + ) + ) if isinstance(family.lead_config, GullWingLeadConfig): left = (family.lead_config.span / 2) * (-1 if (i % 2 == 0) else 1) right = (family.body_size_x / 2) * (-1 if (i % 2 == 0) else 1) top = y + (family.lead_config.width / 2) bottom = y - (family.lead_config.width / 2) - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-documentation-{}'.format(i + 1)), - layer=Layer('top_documentation'), - width=Width(0), - fill=Fill(True), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - Vertex(Position(left, top), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-documentation-{}'.format(i + 1)), + layer=Layer('top_documentation'), + width=Width(0), + fill=Fill(True), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + Vertex(Position(left, top), Angle(0)), + ], + ) + ) # Documentation outline top = (family.body_size_y / 2) - (line_width / 2) bottom = -top left = -(family.body_size_x / 2) + (line_width / 2) right = -left - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-documentation'), - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - Vertex(Position(left, top), Angle(0)), - ], - )) - - # Documentation actuator - if isinstance(family.actuator_size, tuple): - dx = family.actuator_size[0] / 2 - dy = family.actuator_size[1] / 2 - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-documentation-actuator'), + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-documentation'), layer=Layer('top_documentation'), width=Width(line_width), 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)), + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + Vertex(Position(left, top), Angle(0)), ], - )) + ) + ) + + # Documentation actuator + if isinstance(family.actuator_size, tuple): + dx = family.actuator_size[0] / 2 + dy = family.actuator_size[1] / 2 + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-documentation-actuator'), + layer=Layer('top_documentation'), + width=Width(line_width), + 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)), + ], + ) + ) else: - footprint.add_circle(Circle( - uuid=_uuid('default-circle-documentation-actuator'), - layer=Layer('top_documentation'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - diameter=Diameter(family.actuator_size - line_width), - position=Position(0, 0), - )) + footprint.add_circle( + Circle( + uuid=_uuid('default-circle-documentation-actuator'), + layer=Layer('top_documentation'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + diameter=Diameter(family.actuator_size - line_width), + position=Position(0, 0), + ) + ) # Legend outline top & bottom dx = (family.body_size_x / 2) + (line_width / 2) dy = (family.body_size_y / 2) + (line_width / 2) if isinstance(family.lead_config, ThtLeadConfig): - dx = min(dx, (family.lead_config.pitch_x / 2) - (family.lead_config.pad_diameter / 2) - (line_width / 2) - 0.15) + dx = min( + dx, + (family.lead_config.pitch_x / 2) + - (family.lead_config.pad_diameter / 2) + - (line_width / 2) + - 0.15, + ) else: - dx = min(dx, (family.lead_config.pitch_x / 2) - (family.lead_config.pad_size_x / 2) - (line_width / 2) - 0.15) + dx = min( + dx, + (family.lead_config.pitch_x / 2) + - (family.lead_config.pad_size_x / 2) + - (line_width / 2) + - 0.15, + ) for sign, name in [(1, 'top'), (-1, 'bottom')]: - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-legend-{}'.format(name)), - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(-dx, dy * sign), Angle(0)), - Vertex(Position(dx, dy * sign), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-legend-{}'.format(name)), + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(-dx, dy * sign), Angle(0)), + Vertex(Position(dx, dy * sign), Angle(0)), + ], + ) + ) # Legend outline left & right dx = (family.body_size_x / 2) + (line_width / 2) dy = (family.body_size_y / 2) + (line_width / 2) if isinstance(family.lead_config, ThtLeadConfig): - dy = min(dy, (family.lead_config.pitch_y / 2) - (family.lead_config.pad_diameter / 2) - (line_width / 2) - 0.15) + dy = min( + dy, + (family.lead_config.pitch_y / 2) + - (family.lead_config.pad_diameter / 2) + - (line_width / 2) + - 0.15, + ) else: - dy = min(dy, (family.lead_config.pitch_y / 2) - (family.lead_config.pad_size_y / 2) - (line_width / 2) - 0.15) + dy = min( + dy, + (family.lead_config.pitch_y / 2) + - (family.lead_config.pad_size_y / 2) + - (line_width / 2) + - 0.15, + ) for sign, name in [(-1, 'left'), (1, 'right')]: - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-legend-{}'.format(name)), - layer=Layer('top_legend'), - width=Width(line_width), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(dx * sign, dy), Angle(0)), - Vertex(Position(dx * sign, -dy), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-legend-{}'.format(name)), + layer=Layer('top_legend'), + width=Width(line_width), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(dx * sign, dy), Angle(0)), + Vertex(Position(dx * sign, -dy), Angle(0)), + ], + ) + ) # Package outline top = family.body_size_y / 2 bottom = -top left = -(family.body_size_x / 2) right = -left - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-outline'), - layer=Layer('top_package_outlines'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-outline'), + layer=Layer('top_package_outlines'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + ], + ) + ) # Courtyard top = (family.body_size_y / 2) + courtyard_excess bottom = -top left = -(family.body_size_x / 2) - courtyard_excess right = -left - footprint.add_polygon(Polygon( - uuid=_uuid('default-polygon-courtyard'), - layer=Layer('top_courtyard'), - width=Width(0), - fill=Fill(False), - grab_area=GrabArea(False), - vertices=[ - Vertex(Position(left, top), Angle(0)), - Vertex(Position(right, top), Angle(0)), - Vertex(Position(right, bottom), Angle(0)), - Vertex(Position(left, bottom), Angle(0)), - ], - )) + footprint.add_polygon( + Polygon( + uuid=_uuid('default-polygon-courtyard'), + layer=Layer('top_courtyard'), + width=Width(0), + fill=Fill(False), + grab_area=GrabArea(False), + vertices=[ + Vertex(Position(left, top), Angle(0)), + Vertex(Position(right, top), Angle(0)), + Vertex(Position(right, bottom), Angle(0)), + Vertex(Position(left, bottom), Angle(0)), + ], + ) + ) # Labels top = (family.body_size_y / 2) + line_width bottom = -top - footprint.add_text(StrokeText( - uuid=_uuid('default-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 + 0.4), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{NAME}}'), - )) - footprint.add_text(StrokeText( - uuid=_uuid('default-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 - 0.4), - rotation=Rotation(0.0), - auto_rotate=AutoRotate(True), - mirror=Mirror(False), - value=Value('{{VALUE}}'), - )) + footprint.add_text( + StrokeText( + uuid=_uuid('default-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 + 0.4), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{NAME}}'), + ) + ) + footprint.add_text( + StrokeText( + uuid=_uuid('default-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 - 0.4), + rotation=Rotation(0.0), + auto_rotate=AutoRotate(True), + mirror=Mirror(False), + value=Value('{{VALUE}}'), + ) + ) # Generate 3D model uuid_3d = _uuid('3d') @@ -424,83 +564,130 @@ def generate_3d_model( print(f'Generating pkg 3D model "{full_name}": {uuid_3d}') - standoff = (family.lead_config.thickness) \ - if (type(family.lead_config) is JLeadConfig) else 0.2 + standoff = (family.lead_config.thickness) if (type(family.lead_config) is JLeadConfig) else 0.2 bend_radius = 0.3 - body = cq.Workplane('XY', origin=(0, 0, standoff)) \ - .box(family.body_size_x, family.body_size_y, family.body_size_z - standoff, - centered=(True, True, False)) \ - .edges().fillet(0.2) + body = ( + cq.Workplane('XY', origin=(0, 0, standoff)) + .box( + family.body_size_x, + family.body_size_y, + family.body_size_z - standoff, + centered=(True, True, False), + ) + .edges() + .fillet(0.2) + ) if isinstance(family.actuator_size, tuple): - actuator = cq.Workplane('XY', origin=(0, 0, 1.0)) \ - .box(family.actuator_size[0], family.actuator_size[1], - model.actuator_height - 1.0, centered=(True, True, False)) \ - .edges().fillet(0.2) + actuator = ( + cq.Workplane('XY', origin=(0, 0, 1.0)) + .box( + family.actuator_size[0], + family.actuator_size[1], + model.actuator_height - 1.0, + centered=(True, True, False), + ) + .edges() + .fillet(0.2) + ) else: - actuator = cq.Workplane('XY', origin=(0, 0, 1.0)) \ - .cylinder(model.actuator_height - 1.0, family.actuator_size / 2, - centered=(True, True, False)) \ - .edges().fillet(0.2) + actuator = ( + cq.Workplane('XY', origin=(0, 0, 1.0)) + .cylinder( + model.actuator_height - 1.0, family.actuator_size / 2, centered=(True, True, False) + ) + .edges() + .fillet(0.2) + ) if isinstance(family.lead_config, ThtLeadConfig): - lead_path = cq.Workplane("XZ") \ - .lineTo(0.0, 1.0) \ - .lineTo(-0.5, 2.0) \ - .lineTo(0.0, 3.0) \ - .lineTo(0.0, 4.0) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .lineTo(family.lead_config.pitch_x - bend_radius, 4.0 + bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ - .lineTo(family.lead_config.pitch_x, 3.0) \ - .lineTo(family.lead_config.pitch_x + 0.5, 2.0) \ - .lineTo(family.lead_config.pitch_x, 1.0) \ + lead_path = ( + cq.Workplane('XZ') + .lineTo(0.0, 1.0) + .lineTo(-0.5, 2.0) + .lineTo(0.0, 3.0) + .lineTo(0.0, 4.0) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .lineTo(family.lead_config.pitch_x - bend_radius, 4.0 + bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) + .lineTo(family.lead_config.pitch_x, 3.0) + .lineTo(family.lead_config.pitch_x + 0.5, 2.0) + .lineTo(family.lead_config.pitch_x, 1.0) .lineTo(family.lead_config.pitch_x, 0.0) - lead = cq.Workplane("XY") \ - .rect(family.lead_config.thickness, family.lead_config.width) \ + ) + lead = ( + cq.Workplane('XY') + .rect(family.lead_config.thickness, family.lead_config.width) .sweep(lead_path) + ) lead_xz = (-family.lead_config.pitch_x / 2, -family.lead_config.length) elif isinstance(family.lead_config, GullWingLeadConfig): - contact_length = ((family.lead_config.span - family.body_size_x) / 2) - bend_radius - family.lead_config.thickness - lead_path = cq.Workplane("XZ") \ - .lineTo(contact_length, 0.0) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=-90, angle2=360, sense=1) \ - .lineTo(contact_length + bend_radius, 0.2 + bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .lineTo(family.lead_config.span - contact_length - 2 * bend_radius, 0.2 + 2 * bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ - .lineTo(family.lead_config.span - contact_length - bend_radius, bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=-180, angle2=270, sense=1) \ + contact_length = ( + ((family.lead_config.span - family.body_size_x) / 2) + - bend_radius + - family.lead_config.thickness + ) + lead_path = ( + cq.Workplane('XZ') + .lineTo(contact_length, 0.0) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=-90, angle2=360, sense=1) + .lineTo(contact_length + bend_radius, 0.2 + bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .lineTo( + family.lead_config.span - contact_length - 2 * bend_radius, 0.2 + 2 * bend_radius + ) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) + .lineTo(family.lead_config.span - contact_length - bend_radius, bend_radius) + .ellipseArc( + x_radius=bend_radius, y_radius=bend_radius, angle1=-180, angle2=270, sense=1 + ) .lineTo(family.lead_config.span, 0.0) - lead = cq.Workplane("ZY") \ - .rect(family.lead_config.thickness, family.lead_config.width) \ + ) + lead = ( + cq.Workplane('ZY') + .rect(family.lead_config.thickness, family.lead_config.width) .sweep(lead_path) + ) lead_xz = (-family.lead_config.span / 2, family.lead_config.thickness / 2) elif isinstance(family.lead_config, JLeadConfig): width = family.body_size_x + family.lead_config.thickness height = 0.5 contact_length = 0.4 - lead_path = cq.Workplane("XZ") \ - .lineTo(-contact_length, 0.0) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=-1) \ - .lineTo(-contact_length - bend_radius, height + bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) \ - .lineTo(width - contact_length - 2 * bend_radius, height + 2 * bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) \ - .lineTo(width - contact_length - bend_radius, bend_radius) \ - .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=-90, angle2=0, sense=-1) \ + lead_path = ( + cq.Workplane('XZ') + .lineTo(-contact_length, 0.0) + .ellipseArc( + x_radius=bend_radius, y_radius=bend_radius, angle1=180, angle2=270, sense=-1 + ) + .lineTo(-contact_length - bend_radius, height + bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=90, angle2=180, sense=-1) + .lineTo(width - contact_length - 2 * bend_radius, height + 2 * bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=0, angle2=90, sense=-1) + .lineTo(width - contact_length - bend_radius, bend_radius) + .ellipseArc(x_radius=bend_radius, y_radius=bend_radius, angle1=-90, angle2=0, sense=-1) .lineTo(width - 2 * contact_length - 2 * bend_radius, 0.0) - lead = cq.Workplane("ZY") \ - .rect(family.lead_config.thickness, family.lead_config.width) \ + ) + lead = ( + cq.Workplane('ZY') + .rect(family.lead_config.thickness, family.lead_config.width) .sweep(lead_path) + ) lead_xz = (-(width / 2) + contact_length + bend_radius, family.lead_config.thickness / 2) assembly = StepAssembly(full_name) assembly.add_body(body, 'body', StepColor.IC_BODY) assembly.add_body(actuator, 'actuator', cq.Color(family.actuator_color)) - assembly.add_body(lead, 'lead-12', StepColor.LEAD_SMT, - location=cq.Location((lead_xz[0], family.lead_config.pitch_y / 2, lead_xz[1]))) - assembly.add_body(lead, 'lead-34', StepColor.LEAD_SMT, - location=cq.Location((lead_xz[0], -family.lead_config.pitch_y / 2, lead_xz[1]))) + assembly.add_body( + lead, + 'lead-12', + StepColor.LEAD_SMT, + location=cq.Location((lead_xz[0], family.lead_config.pitch_y / 2, lead_xz[1])), + ) + assembly.add_body( + lead, + 'lead-34', + StepColor.LEAD_SMT, + location=cq.Location((lead_xz[0], -family.lead_config.pitch_y / 2, lead_xz[1])), + ) # Save without fusing for massively better minification! out_path = path.join('out', library, 'pkg', uuid_pkg, f'{uuid_3d}.step') @@ -515,7 +702,7 @@ def generate_dev( family: Family, model: Model, ) -> None: - full_name = f"{family.dev_name_prefix} {model.name}" + full_name = f'{family.dev_name_prefix} {model.name}' def _uuid(identifier: str) -> str: return uuid('dev', model.uuid_key(family), identifier) @@ -553,11 +740,13 @@ def _uuid(identifier: str) -> str: device.add_part(part) if family.datasheet: - device.add_resource(Resource( - name='Datasheet {}'.format(family.datasheet_name), - mediatype='application/pdf', - url=family.datasheet, - )) + device.add_resource( + Resource( + name='Datasheet {}'.format(family.datasheet_name), + mediatype='application/pdf', + url=family.datasheet, + ) + ) device.serialize(path.join('out', library, 'dev')) @@ -600,109 +789,241 @@ def _uuid(identifier: str) -> str: ) models = [ # 4.3mm - Model(name='PTS645Sx432', actuator_height=4.3, parts=[ - Part('PTS645SK432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '4.3mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx432', + actuator_height=4.3, + parts=[ + Part( + 'PTS645SK432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '4.3mm', AttributeType.STRING, None), + ], + ), # 5.0mm - Model(name='PTS645Sx502', actuator_height=5.0, parts=[ - Part('PTS645SK502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR502LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '5.0mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx502', + actuator_height=5.0, + parts=[ + Part( + 'PTS645SK502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR502LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '5.0mm', AttributeType.STRING, None), + ], + ), # 7.0mm - Model(name='PTS645Sx702', actuator_height=7.0, parts=[ - Part('PTS645SK702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR702LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '7.0mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx702', + actuator_height=7.0, + parts=[ + Part( + 'PTS645SK702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR702LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '7.0mm', AttributeType.STRING, None), + ], + ), # 9.5mm - Model(name='PTS645Sx952', actuator_height=9.5, parts=[ - Part('PTS645SK952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR952LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '9.5mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx952', + actuator_height=9.5, + parts=[ + Part( + 'PTS645SK952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR952LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '9.5mm', AttributeType.STRING, None), + ], + ), ] for model in models: generate_pkg( @@ -747,31 +1068,64 @@ def _uuid(identifier: str) -> str: keywords=[], ) models = [ - Model(name='PTS645SJx732', actuator_height=7.3, parts=[ - Part('PTS645SJK432LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SJL732LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SJM732LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SJN732LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SJH732LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SJP732LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SJR732LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '7.3mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645SJx732', + actuator_height=7.3, + parts=[ + Part( + 'PTS645SJK432LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJL732LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJM732LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJN732LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJH732LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJP732LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJR732LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '7.3mm', AttributeType.STRING, None), + ], + ), ] for model in models: generate_pkg( @@ -817,161 +1171,359 @@ def _uuid(identifier: str) -> str: ) models = [ # 4.3mm - Model(name='PTS645Sx43SMTR92', actuator_height=4.3, parts=[ - Part('PTS645SK43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '4.3mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx43SMTR92', + actuator_height=4.3, + parts=[ + Part( + 'PTS645SK43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '4.3mm', AttributeType.STRING, None), + ], + ), # 5.0mm - Model(name='PTS645Sx50SMTR92', actuator_height=5.0, parts=[ - Part('PTS645SK50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR50SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '5.0mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx50SMTR92', + actuator_height=5.0, + parts=[ + Part( + 'PTS645SK50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR50SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '5.0mm', AttributeType.STRING, None), + ], + ), # 7.0mm - Model(name='PTS645Sx70SMTR92', actuator_height=7.0, parts=[ - Part('PTS645SK70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR70SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '7.0mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx70SMTR92', + actuator_height=7.0, + parts=[ + Part( + 'PTS645SK70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR70SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '7.0mm', AttributeType.STRING, None), + ], + ), # 9.5mm - Model(name='PTS645Sx95SMTR92', actuator_height=9.5, parts=[ - Part('PTS645SK95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR95SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '9.5mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx95SMTR92', + actuator_height=9.5, + parts=[ + Part( + 'PTS645SK95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR95SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '9.5mm', AttributeType.STRING, None), + ], + ), # 11mm - Model(name='PTS645Sx11SMTR92', actuator_height=11.0, parts=[ - Part('PTS645SK11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR11SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '11mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx11SMTR92', + actuator_height=11.0, + parts=[ + Part( + 'PTS645SK11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR11SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '11mm', AttributeType.STRING, None), + ], + ), # 13mm - Model(name='PTS645Sx13SMTR92', actuator_height=13.0, parts=[ - Part('PTS645SK13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR13SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '13mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx13SMTR92', + actuator_height=13.0, + parts=[ + Part( + 'PTS645SK13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR13SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '13mm', AttributeType.STRING, None), + ], + ), ] for model in models: generate_pkg( @@ -1016,31 +1568,64 @@ def _uuid(identifier: str) -> str: keywords=[], ) models = [ - Model(name='PTS645SJx73SMTR92', actuator_height=7.3, parts=[ - Part('PTS645SJK43SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SJL73SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SJM73SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SJN73SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SJH73SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SJP73SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SJR73SMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '7.3mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645SJx73SMTR92', + actuator_height=7.3, + parts=[ + Part( + 'PTS645SJK43SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJL73SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJM73SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJN73SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJH73SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJP73SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJR73SMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '7.3mm', AttributeType.STRING, None), + ], + ), ] for model in models: generate_pkg( @@ -1085,161 +1670,359 @@ def _uuid(identifier: str) -> str: ) models = [ # 4.3mm - Model(name='PTS645Sx43JSMTR92', actuator_height=4.3, parts=[ - Part('PTS645SK43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '4.3mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx43JSMTR92', + actuator_height=4.3, + parts=[ + Part( + 'PTS645SK43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '4.3mm', AttributeType.STRING, None), + ], + ), # 5.0mm - Model(name='PTS645Sx50JSMTR92', actuator_height=5.0, parts=[ - Part('PTS645SK50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR50JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '5.0mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx50JSMTR92', + actuator_height=5.0, + parts=[ + Part( + 'PTS645SK50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR50JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '5.0mm', AttributeType.STRING, None), + ], + ), # 7.0mm - Model(name='PTS645Sx70JSMTR92', actuator_height=7.0, parts=[ - Part('PTS645SK70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR70JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '7.0mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx70JSMTR92', + actuator_height=7.0, + parts=[ + Part( + 'PTS645SK70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR70JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '7.0mm', AttributeType.STRING, None), + ], + ), # 9.5mm - Model(name='PTS645Sx95JSMTR92', actuator_height=9.5, parts=[ - Part('PTS645SK95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR95JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '9.5mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx95JSMTR92', + actuator_height=9.5, + parts=[ + Part( + 'PTS645SK95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR95JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '9.5mm', AttributeType.STRING, None), + ], + ), # 11mm - Model(name='PTS645Sx11JSMTR92', actuator_height=11.0, parts=[ - Part('PTS645SK11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR11JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '11mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx11JSMTR92', + actuator_height=11.0, + parts=[ + Part( + 'PTS645SK11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR11JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '11mm', AttributeType.STRING, None), + ], + ), # 13mm - Model(name='PTS645Sx13JSMTR92', actuator_height=13.0, parts=[ - Part('PTS645SK13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SL13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SM13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SN13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SH13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SP13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SR13JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '13mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645Sx13JSMTR92', + actuator_height=13.0, + parts=[ + Part( + 'PTS645SK13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SL13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SM13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SN13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SH13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SP13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SR13JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '13mm', AttributeType.STRING, None), + ], + ), ] for model in models: generate_pkg( @@ -1283,31 +2066,64 @@ def _uuid(identifier: str) -> str: keywords=[], ) models = [ - Model(name='PTS645SJx73JSMTR92', actuator_height=7.3, parts=[ - Part('PTS645SJK43JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '260gf', AttributeType.STRING, None), - ]), - Part('PTS645SJL73JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '130gf', AttributeType.STRING, None), - ]), - Part('PTS645SJM73JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '160gf', AttributeType.STRING, None), - ]), - Part('PTS645SJN73JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '100gf', AttributeType.STRING, None), - ]), - Part('PTS645SJH73JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '200gf', AttributeType.STRING, None), - ]), - Part('PTS645SJP73JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '320gf', AttributeType.STRING, None), - ]), - Part('PTS645SJR73JSMTR92LFS', Manufacturer('C&K'), [ - Attribute('FORCE', '360gf', AttributeType.STRING, None), - ]), - ], common_part_attributes=[ - Attribute('HEIGHT', '7.3mm', AttributeType.STRING, None), - ]), + Model( + name='PTS645SJx73JSMTR92', + actuator_height=7.3, + parts=[ + Part( + 'PTS645SJK43JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '260gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJL73JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '130gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJM73JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '160gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJN73JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '100gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJH73JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '200gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJP73JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '320gf', AttributeType.STRING, None), + ], + ), + Part( + 'PTS645SJR73JSMTR92LFS', + Manufacturer('C&K'), + [ + Attribute('FORCE', '360gf', AttributeType.STRING, None), + ], + ), + ], + common_part_attributes=[ + Attribute('HEIGHT', '7.3mm', AttributeType.STRING, None), + ], + ), ] for model in models: generate_pkg( diff --git a/generate_to92.py b/generate_to92.py index 6f8620db..670d8fad 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,12 @@ 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)) \ + return ( + cq.Workplane(plane) + .vLine(length - straight_length - abs(delta)) + .line(-delta, abs(delta)) .vLine(straight_length) + ) def generate( @@ -31,48 +34,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/test_attribute.py b/test_attribute.py index 965e772b..4e618217 100644 --- a/test_attribute.py +++ b/test_attribute.py @@ -3,70 +3,175 @@ 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 49c966ed..e01e0983 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 42ff30a8..fdbce4bd 100644 --- a/test_entities.py +++ b/test_entities.py @@ -1,19 +1,80 @@ from pathlib import Path 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 @@ -21,12 +82,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")' @@ -46,23 +107,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: @@ -71,30 +137,50 @@ 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: @@ -110,28 +196,58 @@ 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") @@ -161,6 +277,7 @@ def test_symbol() -> None: (approval bar) (approval foo) )""" + ) def test_component_role() -> None: @@ -170,33 +287,84 @@ 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)) - variant = Variant('abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a', Norm.EMPTY, Name('default'), Description(''), gate) - assert str(variant) == """(variant abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a (norm "") + 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 "") (name "default") (description "") (gate c1e4b542-a1b1-44d5-bec3-070776143a29 @@ -205,6 +373,7 @@ def test_component_variant() -> None: (pin 0189aafc-f88a-4e65-8fb4-09a047a3e334 (signal 46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c) (text pin)) ) )""" + ) def test_component() -> None: @@ -223,17 +392,44 @@ 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)) - variant = Variant('abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a', Norm.EMPTY, Name('default'), Description(''), gate) + 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") @@ -261,6 +457,7 @@ def test_component() -> None: (approval bar) (approval foo) )""" + ) def test_package_pad() -> None: @@ -286,11 +483,13 @@ def test_footprint_pad() -> None: PadHole( '5c4d39d3-35cc-4836-a082-693143ee9135', DrillDiameter(1.0), - [Vertex(Position(0.0, 0.0), Angle(0.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) @@ -298,15 +497,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: @@ -318,63 +534,88 @@ 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(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(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(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(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) @@ -409,13 +650,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'), @@ -436,7 +680,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") @@ -488,11 +734,17 @@ 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: @@ -510,8 +762,18 @@ 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'))) @@ -519,7 +781,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") @@ -540,6 +804,7 @@ def test_device() -> None: (approval bar) (approval foo) )""" + ) def test_sort_package_3d_models() -> None: @@ -563,7 +828,7 @@ def check_all_file_newlines_in_dir_are_unix(dir_with_files: Path) -> bool: Helper function, not a test! """ for temp_file in dir_with_files.iterdir(): - with temp_file.open(mode="r", newline='') as file_under_test: + with temp_file.open(mode='r', newline='') as file_under_test: file_under_test.readlines() # read all lines to populate file_under_test.newlines if file_under_test.newlines is None: return False @@ -573,7 +838,6 @@ def check_all_file_newlines_in_dir_are_unix(dir_with_files: Path) -> bool: def test_serialized_component_line_endings(tmp_path: Path) -> None: - component_uuid = '00c36da8-e22b-43a1-9a87-c3a67e863f49' component = Component( @@ -598,7 +862,6 @@ def test_serialized_component_line_endings(tmp_path: Path) -> None: def test_serialized_symbol_line_endings(tmp_path: Path) -> None: - symbol_uuid = '01b03c10-7334-4bd5-b2bc-942c18325d2b' symbol = Symbol( @@ -620,7 +883,6 @@ def test_serialized_symbol_line_endings(tmp_path: Path) -> None: def test_serialized_device_line_endings(tmp_path: Path) -> None: - device_uuid = '00652f30-9f89-4027-91f5-7bd684eee751' device = Device( @@ -644,13 +906,14 @@ def test_serialized_device_line_endings(tmp_path: Path) -> None: def test_serialized_package_line_endings(tmp_path: Path) -> None: - package_uuid = '009e35ef-1f50-4bf3-ab58-11eb85bf5503' package = Package( package_uuid, 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'), diff --git a/test_generate_connectors.py b/test_generate_connectors.py index c823626d..0c90cfdc 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 c85db8c4..e68d9b74 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 52491c02..673d441f 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 d04868fb..cb2150a9 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 @@ -35,7 +38,10 @@ def test_mcu_ref_without_flash(mcu_ref, expected, flash_size): def test_mcu_ref_for_flash_variants_multiple(): mcu = MCU(ref='STM32F429IGHx', info=_make_empty_info(), pins=[]) - assert mcu.ref_for_flash_variants(['STM32F429IEHx', 'STM32F429IGHx', 'STM32F429IIHx']) == 'STM32F429I[EGI]Hx' + assert ( + mcu.ref_for_flash_variants(['STM32F429IEHx', 'STM32F429IGHx', 'STM32F429IIHx']) + == 'STM32F429I[EGI]Hx' + ) def test_mcu_ref_for_flash_variants_single(): @@ -43,16 +49,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 From 3b5e63417e0eb3d65b0e110e4eda5d458f77fb0a Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Fri, 11 Jul 2025 14:04:27 +0200 Subject: [PATCH 06/10] Replace isort with ruff --- .github/workflows/main.yml | 4 ++-- pyproject.toml | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ddd1371f..0fa6d6a5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,5 +24,5 @@ jobs: run: mypy . - name: Run flake8 run: flake8 - - name: Run isort - run: isort --check --diff . + - name: Run ruff format + run: ruff format --check diff --git a/pyproject.toml b/pyproject.toml index 470e77ff..01603c3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,6 @@ test = [ "pytest ~= 8.2.1", "flake8 ~= 4.0.1", "mypy == 1.10.0", - "isort ~= 5.13.2", "ruff ~= 0.12.2", ] @@ -41,13 +40,6 @@ module = [ ] disallow_untyped_defs = false -[tool.isort] -line_length = 120 -multi_line_output = 5 -balanced_wrapping = false -known_typing = "typing" -sections = "FUTURE,STDLIB,TYPING,THIRDPARTY,FIRSTPARTY,LOCALFOLDER" - [tool.ruff] target-version = "py38" line-length = 100 From 3fa9142a5b3c7b89bee2585ce985648903484552 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Fri, 11 Jul 2025 14:28:14 +0200 Subject: [PATCH 07/10] Replace flake8 with ruff as well --- .github/workflows/main.yml | 4 ++-- dfn_configs.py | 5 ++--- generate_stm_mcu.py | 3 ++- pyproject.toml | 13 ++++++++++++- test_entities.py | 12 +++++++----- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0fa6d6a5..8bbc9c90 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: run: pytest - name: Run mypy run: mypy . - - name: Run flake8 - run: flake8 + - name: Run ruff check + run: ruff check . - name: Run ruff format run: ruff format --check diff --git a/dfn_configs.py b/dfn_configs.py index 8f7699f4..00e82909 100644 --- a/dfn_configs.py +++ b/dfn_configs.py @@ -287,8 +287,8 @@ def __init__( 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, # so we may need to re-add them again later. - # DfnConfig(3.0, 2.0, 0.5, 8, 0.95, 1.00, 0.40, 2.20, 0.60, 'V3020D-4,VECD-4', no_exp=False), # no nominal exp_pad - # DfnConfig(3.0, 2.0, 0.5, 8, 0.75, 0.80, 0.40, 2.20, 0.60, 'W3020D-4,WECD-4', no_exp=False), # no nominal exp_pad + # DfnConfig(3.0, 2.0, 0.5, 8, 0.95, 1.00, 0.40, 2.20, 0.60, 'V3020D-4,VECD-4', no_exp=False), # no nominal exp_pad + # DfnConfig(3.0, 2.0, 0.5, 8, 0.75, 0.80, 0.40, 2.20, 0.60, 'W3020D-4,WECD-4', no_exp=False), # no nominal exp_pad DfnConfig(3.0, 2.0, 0.5, 10, 0.95, 1.00, 0.55, 2.20, 0.60, 'V3020D-2,VECD-2', print_pad=True), DfnConfig(3.0, 2.0, 0.5, 10, 0.75, 0.80, 0.55, 2.20, 0.60, 'W3020D-2,WECD-2'), DfnConfig(3.0, 2.0, 0.5, 10, 0.95, 1.00, 0.30, 2.20, 0.60, 'V3020D-3,VECD-3', print_pad=True), @@ -418,7 +418,6 @@ def step_modification_sgp3x(body: Any, dot: Any, workplane: Any) -> Tuple[Any, A THIRD_CONFIGS = [ - # length, width, pitch, pin_count, height_nominal, height_max, lead_length, exposed_width, exposed_length, keywords # Sensirion DfnConfig( length=2.0, diff --git a/generate_stm_mcu.py b/generate_stm_mcu.py index 17dde325..69e643d4 100644 --- a/generate_stm_mcu.py +++ b/generate_stm_mcu.py @@ -160,7 +160,8 @@ 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] + [ + + return [(l[0], Position(-dx, l[1] * grid), Rotation(0.0)) for l in self.left] + [ # noqa: E741 (r[0], Position(dx, r[1] * grid), Rotation(180.0)) for r in self.right ] diff --git a/pyproject.toml b/pyproject.toml index 01603c3d..09d25fef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ version = "0.1.0" [project.optional-dependencies] test = [ "pytest ~= 8.2.1", - "flake8 ~= 4.0.1", "mypy == 1.10.0", "ruff ~= 0.12.2", ] @@ -44,6 +43,18 @@ disallow_untyped_defs = false target-version = "py38" line-length = 100 +[tool.ruff.lint.pycodestyle] +# When the autoformatter fails to reduce the line length or it is disabled, +# allow for a bit more +max-line-length = 120 + + +[tool.ruff.lint] +select = [ + "E", + "F", +] + [tool.ruff.format] quote-style = "single" diff --git a/test_entities.py b/test_entities.py index fdbce4bd..05ca2601 100644 --- a/test_entities.py +++ b/test_entities.py @@ -658,7 +658,8 @@ def test_package() -> None: '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)' + 'A 1x19 soldered wire connector with 2.54mm pin spacing and 1.0mm drill holes.\n\n' + 'Generated with librepcb-parts-generator (generate_connectors.py)' ), Keywords('connector, 1x19, d1.0, connector, soldering, generic'), Author('Danilo B.'), @@ -681,10 +682,10 @@ def test_package() -> None: package.add_approval('(approval bar)') assert ( - str(package) - == """(librepcb_package 009e35ef-1f50-4bf3-ab58-11eb85bf5503 + 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)") + (description "A 1x19 soldered wire connector with 2.54mm pin spacing and 1.0mm drill holes.\\n\\n""" + """Generated with librepcb-parts-generator (generate_connectors.py)") (keywords "connector, 1x19, d1.0, connector, soldering, generic") (author "Danilo B.") (version "0.1") @@ -912,7 +913,8 @@ def test_serialized_package_line_endings(tmp_path: Path) -> None: package_uuid, 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)' + 'A 1x19 soldered wire connector with 2.54mm pin spacing and 1.0mm drill holes.\n\n' + 'Generated with librepcb-parts-generator (generate_connectors.py)' ), Keywords('connector, 1x19, d1.0, connector, soldering, generic'), Author('Danilo B.'), From 35b51255577a876f8c1a5e6c2a23ff8823d45338 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Fri, 11 Jul 2025 14:36:11 +0200 Subject: [PATCH 08/10] Apply ruff isort changes I tried to match the existing isort config as close as possible. --- generate_connectors.py | 3 +-- generate_stm_mcu.py | 3 +-- pyproject.toml | 6 ++++++ test_entities.py | 3 +-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/generate_connectors.py b/generate_connectors.py index ac96a232..9b37d568 100644 --- a/generate_connectors.py +++ b/generate_connectors.py @@ -97,9 +97,8 @@ StrokeText, StrokeWidth, ) -from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation +from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation, Symbol from entities.symbol import Pin as SymbolPin -from entities.symbol import Symbol generator = 'librepcb-parts-generator (generate_connectors.py)' diff --git a/generate_stm_mcu.py b/generate_stm_mcu.py index 69e643d4..9bc6f973 100644 --- a/generate_stm_mcu.py +++ b/generate_stm_mcu.py @@ -81,9 +81,8 @@ Variant, ) from entities.device import ComponentPad, ComponentUUID, Device, PackageUUID -from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation +from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation, Symbol from entities.symbol import Pin as SymbolPin -from entities.symbol import Symbol grid = 2.54 # Grid size in mm width_regular = 10 # Symbol width in grid units diff --git a/pyproject.toml b/pyproject.toml index 09d25fef..0ef89eb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,8 +53,14 @@ max-line-length = 120 select = [ "E", "F", + "I", ] +[tool.ruff.lint.isort] +force-wrap-aliases = false +sections = { typing = ["typing"] } +section-order = ["future", "standard-library", "typing", "third-party", "first-party", "local-folder"] + [tool.ruff.format] quote-style = "single" diff --git a/test_entities.py b/test_entities.py index 05ca2601..53c1354b 100644 --- a/test_entities.py +++ b/test_entities.py @@ -76,9 +76,8 @@ StrokeText, StrokeWidth, ) -from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation +from entities.symbol import NameAlign, NameHeight, NamePosition, NameRotation, Symbol from entities.symbol import Pin as SymbolPin -from entities.symbol import Symbol def test_name() -> None: From 3f9b187b2dce706fce5d90163f781344065b96a3 Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Fri, 11 Jul 2025 15:15:45 +0200 Subject: [PATCH 09/10] Also enable 'W' lints --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 0ef89eb4..38bfc128 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ max-line-length = 120 select = [ "E", "F", + "W", "I", ] From 2554cc405f10fd294acfb604710e51f9aa09cea4 Mon Sep 17 00:00:00 2001 From: Fred Turkington Date: Sun, 6 Jul 2025 11:59:36 -0500 Subject: [PATCH 10/10] Bump min Python to 3.9 --- .github/workflows/main.yml | 3 ++- .gitignore | 1 + README.md | 4 +--- pyproject.toml | 28 ++++++++++++++-------------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8bbc9c90..143aa585 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,10 +7,11 @@ jobs: strategy: matrix: python-version: - - "3.8" - "3.9" - "3.10" - "3.11" + - "3.12" + - "3.13" steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 diff --git a/.gitignore b/.gitignore index ed37c655..431bb744 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ out_3d/ *.pdf /librepcb_parts_generator.egg-info/ /build/ +.venv diff --git a/README.md b/README.md index 5e69b811..c4404b32 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,11 @@ This is a collection of Python 3 based scripts to generate parts for the [LibrePCB](https://librepcb.org) default library. - ## Requirements -- Python 3.8+ +- Python 3.9+ - Dependencies in `pyproject.toml` - ## Introduction / Concepts While it's easy to create a one-off script to generate LibrePCB library diff --git a/pyproject.toml b/pyproject.toml index 38bfc128..012080d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,16 @@ [project] +version = "0.1.0" name = "librepcb-parts-generator" authors = [ { name = "Danilo Bargen", email = "mail@dbrgn.ch" }, { name = "Raphael Nestler", email = "raphael.nestler@gmail.com" }, ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "cadquery == 2.3.1", - "numpy < 2", # Dependency of cadquery, but not working with v2.x + "numpy < 2", # Dependency of cadquery, but not working with v2.x] ] -version = "0.1.0" [project.optional-dependencies] test = [ @@ -25,9 +25,7 @@ exclude = ["out*"] [tool.mypy] warn_unused_configs = true strict = true -exclude = [ - 'build' -] +exclude = ['build'] [[tool.mypy.overrides]] module = [ @@ -40,7 +38,7 @@ module = [ disallow_untyped_defs = false [tool.ruff] -target-version = "py38" +target-version = "py39" line-length = 100 [tool.ruff.lint.pycodestyle] @@ -50,17 +48,19 @@ max-line-length = 120 [tool.ruff.lint] -select = [ - "E", - "F", - "W", - "I", -] +select = ["E", "F", "W", "I"] [tool.ruff.lint.isort] force-wrap-aliases = false sections = { typing = ["typing"] } -section-order = ["future", "standard-library", "typing", "third-party", "first-party", "local-folder"] +section-order = [ + "future", + "standard-library", + "typing", + "third-party", + "first-party", + "local-folder", +] [tool.ruff.format] quote-style = "single"