def test_symbol() -> None: symbol = Symbol('01b03c10-7334-4bd5-b2bc-942c18325d2b', Name('Sym name'), Description(r'A multiline description.\n\nDescription'), Keywords('my, keywords'), Author('Test'), Version('0.2'), Created('2018-10-17T19:13:41Z'), 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))) 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))) assert str( symbol) == """(librepcb_symbol 01b03c10-7334-4bd5-b2bc-942c18325d2b
def test_component() -> None: component = Component( '00c36da8-e22b-43a1-9a87-c3a67e863f49', Name('Generic Connector 1x27'), Description(r'A 1x27 soldered wire connector.\n\nNext line'), Keywords('connector, 1x27'), Author('Test R.'), Version('0.2'), Created('2018-10-17T19:13:41Z'), Deprecated(False), Category('d0618c29-0436-42da-a388-fdadf7b23892'), SchematicOnly(False), DefaultValue(''), Prefix('J')) 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) component.add_variant(variant) assert str(component ) == """(librepcb_component 00c36da8-e22b-43a1-9a87-c3a67e863f49
def generate_dev( dirpath: str, diameter: float, height: float, pitch: float, lead_width: float, author: str, version: str, create_date: Optional[str], ) -> None: name = 'Capacitor Radial ⌀{}x{}/{}mm'.format(diameter, height, pitch) variant = get_variant(diameter, height, pitch, lead_width) def _uuid(identifier: str) -> str: return uuid('dev', variant, identifier) device = Device( 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)), keywords=Keywords( 'electrolytic,capacitor,polarized,radial,c,cap,cpol'), author=Author(author), version=Version(version), created=Created(create_date or now()), deprecated=Deprecated(False), category=Category('c011cc6b-b762-498e-8494-d1994f3043cf'), component_uuid=ComponentUUID('c54375c5-7149-4ded-95c5-7462f7301ee7'), package_uuid=PackageUUID(uuid('pkg', variant, 'pkg')), ) device.add_pad( ComponentPad( uuid=uuid('pkg', variant, 'pad-plus'), signal=SignalUUID('e010ecbb-6210-4da3-9270-ebd58656dbf0'), )) device.add_pad( ComponentPad( uuid=uuid('pkg', variant, 'pad-minus'), signal=SignalUUID('af3ffca8-0085-4edb-a775-fcb759f63411'), )) # write files pkg_dir_path = path.join(dirpath, device.uuid) if not (path.exists(pkg_dir_path) and path.isdir(pkg_dir_path)): makedirs(pkg_dir_path) with open(path.join(pkg_dir_path, '.librepcb-dev'), 'w') as f: f.write('0.1\n') with open(path.join(pkg_dir_path, 'device.lp'), 'w') as f: f.write(str(device)) f.write('\n') print('Wrote device {}'.format(name))
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. """ (placement, pin_mapping) = mcu.generate_placement_data(debug) name = mcu.ref 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 '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 'TSSOP14': 'fb8c2dc2-9812-4383-a810-b2fdbd525b4e', # TSSOP14P65_500X640X120L100X30 'TSSOP20': 'a040fccc-54e5-4f95-a5db-20044d8b37a5', # TSSOP20P65_650X640X120L100X30 } if mcu.package not in package_uuid_mapping: print('Skipping dev {} (missing package {})'.format(name, mcu.package)) return pad_uuid_mapping = common.get_pad_uuids(base_lib_path, package_uuid_mapping[mcu.package]) device = Device( uuid('dev', mcu.ref, 'dev'), Name(mcu.ref), Description(mcu.description), mcu.keywords, author, Version(dev_version), Created('2020-03-01T01:55:20Z'), Deprecated(False), cmpcat, ComponentUUID(uuid('cmp', mcu.component_identifier, 'cmp')), PackageUUID(package_uuid_mapping[mcu.package]), ) 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.serialize('out/stm_mcu/dev') print('Wrote dev {}'.format(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)' ), Keywords('connector, 1x19, d1.0, connector, soldering, generic'), Author('Danilo B.'), Version('0.1'), Created('2018-10-17T19:13:41Z'), Deprecated(False), Category('56a5773f-eeb4-4b39-8cb9-274f3da26f4f')) package.add_pad( PackagePad('5c4d39d3-35cc-4836-a082-693143ee9135', Name('1'))) package.add_pad( PackagePad('6100dd55-d3b3-4139-9085-d5a75e783c37', Name('2'))) package.add_footprint(create_footprint()) assert str( package) == """(librepcb_package 009e35ef-1f50-4bf3-ab58-11eb85bf5503
def test_device() -> None: device = Device( '00652f30-9f89-4027-91f5-7bd684eee751', Name('Foo'), Description('Bar'), Keywords('foo, bar'), Author('J. Rando'), Version('0.1'), Created('2018-10-17T19:13:41Z'), Deprecated(False), Category('ade6d8ff-3c4f-4dac-a939-cc540c87c280'), 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'))) assert str( device) == """(librepcb_device 00652f30-9f89-4027-91f5-7bd684eee751
def generate_cmp( name: str, mcus: List[MCU], symbol_map: Dict[str, str], debug: bool = False, ) -> None: """ When generating components, to reduce the number of components, they are merged as follows: - For every MCU, the "ref without flash" is calculated by replacing the 11th character in the ref name with an `x` and cutting off everything after the package character. - MCUs that share the same pinout will be merged if their ref names without flash are the same - The name of the component will be generated as follows: STM32F429IEHx + STM32F429IGHx + STM32F429IIHx = STM32F429I[EGI]Hx. - To achieve a stable UUID even if new MCUs are added, the "ref without flash" is combined with the SHA1 hash of the pins. Because renaming the pinout might result in a different UUID, when upgrading the stm-pinout database, changes (but not additions) must be analyzed manually. """ components = [] for mcu in mcus: (placement, pin_mapping) = mcu.generate_placement_data(debug) cmp_version = '0.1' component = Component( uuid('cmp', mcu.component_identifier, 'cmp'), Name(name), Description(mcu.component_description), mcu.keywords, author, Version(cmp_version), Created('2020-01-30T20:55:23Z'), Deprecated(False), cmpcat, SchematicOnly(False), DefaultValue('{{PARTNUMBER or DEVICE or COMPONENT}}'), Prefix('U'), ) # 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(''), )) # Add symbol variant gate = Gate( uuid('cmp', mcu.component_identifier, 'variant-single-gate1'), SymbolUUID(uuid('sym', mcu.symbol_identifier, 'sym')), Position(0, 0), Rotation(0.0), Required(True), 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, )) components.append(component) # Make sure all grouped components are identical assert len(set([str(c) for c in components])) == 1 components[0].serialize('out/stm_mcu/cmp') print('Wrote cmp {}'.format(name))
def generate_sym(mcus: List[MCU], symbol_map: Dict[str, str], debug: bool = False) -> None: # Determine symbol width pin_mappings = [mcu.generate_placement_data()[1].values() for mcu in mcus] max_pin_name_len = max([max(map(len, pin_map)) for pin_map in pin_mappings]) width = width_wide if max_pin_name_len >= width_wide_threshold else width_regular symbols = [] for mcu in mcus: assert mcu.symbol_identifier not in symbol_map sym_version = '0.1' # Generate pin placement data (placement, pin_mapping) = mcu.generate_placement_data(debug) if debug: print(pin_mapping) uuid_sym = uuid('sym', mcu.symbol_identifier, 'sym') symbol = Symbol( uuid_sym, Name(mcu.symbol_name), Description(mcu.symbol_description), mcu.keywords, author, Version(sym_version), Created('2020-01-30T20:55:23Z'), cmpcat, ) 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), )) polygon = Polygon( uuid('sym', mcu.symbol_identifier, 'polygon'), Layer('sym_outlines'), Width(line_width), Fill(False), GrabArea(True), ) (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, min_y), Angle(0.0))) polygon.add_vertex(Vertex(Position(-dx, max_y), Angle(0.0))) symbol.add_polygon(polygon) text_name = Text( uuid('sym', mcu.symbol_identifier, 'text-name'), Layer('sym_names'), Value('{{NAME}}'), Align('left bottom'), Height(text_height), Position(-dx, max_y), Rotation(0.0), ) text_value = Text( uuid('sym', mcu.symbol_identifier, 'text-value'), Layer('sym_values'), Value('{{VALUE}}'), Align('left top'), Height(text_height), Position(-dx, min_y), Rotation(0.0), ) symbol.add_text(text_name) symbol.add_text(text_value) symbols.append(symbol) # Make sure all grouped symbols are identical assert len(set([str(s) for s in symbols])) == 1 dirpath = 'out/stm_mcu/sym' sym_dir_path = path.join(dirpath, symbols[0].uuid) if not (path.exists(sym_dir_path) and path.isdir(sym_dir_path)): makedirs(sym_dir_path) with open(path.join(sym_dir_path, '.librepcb-sym'), 'w') as f: f.write('0.1\n') with open(path.join(sym_dir_path, 'symbol.lp'), 'w') as f: f.write(str(symbols[0])) f.write('\n') symbol_map[mcus[0].symbol_identifier] = symbols[0].uuid print('Wrote sym {}'.format(symbols[0].name))
def generate_pkg( dirpath: str, diameter: float, height: float, pitch: float, lead_width: float, author: str, version: str, create_date: Optional[str], ) -> None: # 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)) 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 pad_diameter = drill + (2 * restring) # outer diameter of pad courtyard_diameter = diameter + (1.0 if diameter >= 10.0 else 0.8) def _generate_fill_polygon(identifier: str, layer: str) -> Polygon: polygon = Polygon( uuid=_fpt_uuid(identifier), layer=Layer(layer), width=Width(0.0), fill=Fill(True), grab_area=GrabArea(False), ) if ((pitch - pad_diameter) < 0.6): # not enough space, use a simplified polygon vertices = [ (0.0, (diameter / 2) - 0.2, 0.0), (0.0, (pad_diameter / 2) + 0.2, 0.0), (pitch / 2, (pad_diameter / 2) + 0.2, -180.0), (pitch / 2, -(pad_diameter / 2) - 0.2, 0.0), (0.0, -(pad_diameter / 2) - 0.2, 0.0), (0.0, -(diameter / 2) + 0.2, 180.0), (0.0, (diameter / 2) - 0.2, 0.0), ] else: vertices = [ (0.0, (diameter / 2) - 0.2, 0.0), (0.0, 0.0, 0.0), ((pitch / 2) - (pad_diameter / 2) - 0.2, 0.0, -180.0), ((pitch / 2) + (pad_diameter / 2) + 0.2, 0.0, -180.0), ((pitch / 2) - (pad_diameter / 2) - 0.2, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, -(diameter / 2) + 0.2, 180.0), (0.0, (diameter / 2) - 0.2, 0.0), ] for vertex in vertices: polygon.add_vertex( Vertex(Position(vertex[0], vertex[1]), Angle(vertex[2]))) return polygon footprint = Footprint( uuid=_fpt_uuid('footprint'), name=Name(name), description=Description(''), ) footprint.add_pad( FootprintPad( uuid=_pkg_uuid('pad-plus'), side=Side.THT, shape=Shape.RECT, position=Position(-pitch / 2, 0), rotation=Rotation(0), size=Size(pad_diameter, pad_diameter), drill=Drill(drill), )) footprint.add_pad( FootprintPad( uuid=_pkg_uuid('pad-minus'), side=Side.THT, shape=Shape.ROUND, position=Position(pitch / 2, 0), rotation=Rotation(0), size=Size(pad_diameter, pad_diameter), drill=Drill(drill), )) # placement footprint.add_circle( Circle( uuid=_fpt_uuid('circle-placement'), layer=Layer('top_placement'), 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_placement', )) # documentation footprint.add_circle( Circle( uuid=_fpt_uuid('circle-documentation'), layer=Layer('top_documentation'), width=Width(0.2), fill=Fill(False), grab_area=GrabArea(False), diameter=Diameter(diameter - 0.2), position=Position(0.0, 0.0), )) footprint.add_polygon( _generate_fill_polygon( identifier='polygon-documentation-fill', layer='top_documentation', )) # courtyard footprint.add_circle( Circle( uuid=_fpt_uuid('circle-courtyard'), layer=Layer('top_courtyard'), width=Width(0.2), 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}}'), )) return footprint # package package = Package( uuid=_pkg_uuid('pkg'), name=Name(name), description=Description( 'Polarized radial electrolytic capacitor.\\n\\n' + 'Diameter: {} mm\\n'.format(diameter) + 'Height: {} mm\\n'.format(height) + 'Lead Spacing: {} mm\\n'.format(pitch) + 'Max. Lead Diameter: {} mm\\n\\n'.format(lead_width) + 'Generated with {}'.format(generator)), keywords=Keywords( 'electrolytic,capacitor,polarized,radial,c,cap,cpol'), author=Author(author), version=Version(version), created=Created(create_date or now()), deprecated=Deprecated(False), category=Category('ee75e31d-f231-41d9-8a3b-bea5114f41e3'), ) 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', )) # write files pkg_dir_path = path.join(dirpath, package.uuid) if not (path.exists(pkg_dir_path) and path.isdir(pkg_dir_path)): makedirs(pkg_dir_path) with open(path.join(pkg_dir_path, '.librepcb-pkg'), 'w') as f: f.write('0.1\n') with open(path.join(pkg_dir_path, 'package.lp'), 'w') as f: f.write(str(package)) f.write('\n') print('Wrote package {}'.format(name))
def generate_cmp( dirpath: str, author: str, name: str, name_lower: str, kind: str, cmpcat: str, keywords: str, default_value: str, rows: int, min_pads: int, max_pads: int, version: str, create_date: Optional[str], ) -> None: category = 'cmp' assert rows in [1, 2] for i in range(min_pads, max_pads + 1, rows): per_row = i // rows variant = '{}x{}'.format(rows, per_row) def _uuid(identifier: str) -> str: return uuid(category, kind, variant, identifier) uuid_cmp = _uuid('cmp') uuid_pins = [ uuid('sym', kind, variant, 'pin-{}'.format(p)) for p in range(i) ] uuid_signals = [_uuid('signal-{}'.format(p)) for p in range(i)] uuid_variant = _uuid('variant-default') uuid_gate = _uuid('gate-default') uuid_symbol = uuid('sym', kind, variant, 'sym') # General info 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)), Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)), Author(author), Version(version), Created(create_date or now()), Deprecated(False), Category(cmpcat), SchematicOnly(False), DefaultValue(default_value), Prefix('J'), ) 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(''), )) gate = Gate( uuid_gate, SymbolUUID(uuid_symbol), Position(0.0, 0.0), Rotation(0.0), Required(True), 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, )) component.add_variant( Variant(uuid_variant, Norm.EMPTY, Name('default'), Description(''), gate)) component.serialize(dirpath) print('{}x{} {}: Wrote component {}'.format(rows, per_row, kind, uuid_cmp))
def generate_sym( dirpath: str, author: str, name: str, name_lower: str, kind: str, cmpcat: str, keywords: str, rows: int, min_pads: int, max_pads: int, version: str, create_date: Optional[str], ) -> None: category = 'sym' assert rows in [1, 2] for i in range(min_pads, max_pads + 1, rows): per_row = i // rows w = width * rows # Make double-row symbols wider! variant = '{}x{}'.format(rows, per_row) def _uuid(identifier: str) -> str: return uuid(category, kind, variant, identifier) uuid_sym = _uuid('sym') uuid_pins = [_uuid('pin-{}'.format(p)) for p in range(i)] uuid_polygon = _uuid('polygon-contour') uuid_decoration = _uuid('polygon-decoration') uuid_text_name = _uuid('text-name') uuid_text_value = _uuid('text-value') # General info 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)), Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)), Author(author), Version(version), Created(create_date or now()), Category(cmpcat), ) for p in range(1, i + 1): x_sign = 1 if (p % rows == 0) else -1 pin = SymbolPin( uuid_pins[p - 1], Name(str(p)), Position((w + 2.54) * x_sign, get_y(p, i, rows, spacing, True)), Rotation(180.0 if p % rows == 0 else 0), Length(3.81)) symbol.add_pin(pin) # Polygons y_max, y_min = get_rectangle_bounds(i, rows, spacing, spacing, True) polygon = Polygon(uuid_polygon, Layer('sym_outlines'), Width(line_width), Fill(False), GrabArea(True)) polygon.add_vertex(Vertex(Position(-w, y_max), Angle(0.0))) polygon.add_vertex(Vertex(Position(w, y_max), Angle(0.0))) polygon.add_vertex(Vertex(Position(w, y_min), Angle(0.0))) polygon.add_vertex(Vertex(Position(-w, y_min), Angle(0.0))) polygon.add_vertex(Vertex(Position(-w, y_max), Angle(0.0))) symbol.add_polygon(polygon) # Decorations if kind == KIND_HEADER: # Headers: Small rectangle for p in range(1, i + 1): x_sign = 1 if (p % rows == 0) else -1 y = get_y(p, i, rows, spacing, True) dx = spacing / 8 * 1.5 * x_sign dy = spacing / 8 / 1.5 x_offset = x_sign * (w - 1.27) polygon = Polygon(uuid_decoration, Layer('sym_outlines'), Width(line_width), Fill(True), GrabArea(True)) polygon.add_vertex( Vertex(Position(x_offset - dx, y + dy), Angle(0.0))) polygon.add_vertex( Vertex(Position(x_offset + dx, y + dy), Angle(0.0))) polygon.add_vertex( Vertex(Position(x_offset + dx, y - dy), Angle(0.0))) 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))) symbol.add_polygon(polygon) elif kind == KIND_SOCKET: # Sockets: Small semicircle for p in range(1, i + 1): x_sign = 1 if (p % rows == 0) else -1 y = get_y(p, i, rows, spacing, True) dy = spacing / 4 * 0.75 x_offset = x_sign * (w - 1.27 - dy * 0.75) polygon = Polygon(uuid_decoration, Layer('sym_outlines'), Width(line_width * 0.75), Fill(False), GrabArea(False)) 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))) symbol.add_polygon(polygon) # 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)) 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)) symbol.add_text(text) sym_dir_path = path.join(dirpath, uuid_sym) if not (path.exists(sym_dir_path) and path.isdir(sym_dir_path)): makedirs(sym_dir_path) with open(path.join(sym_dir_path, '.librepcb-sym'), 'w') as f: f.write('0.1\n') with open(path.join(sym_dir_path, 'symbol.lp'), 'w') as f: f.write(str(symbol)) f.write('\n') print('{}x{} {}: Wrote symbol {}'.format(rows, per_row, kind, uuid_sym))