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 create_footprint() -> Footprint: footprint = Footprint('17b9f232-2b15-4281-a07d-ad0db5213f92', Name('default'), Description('')) footprint.add_pad( FootprintPad('5c4d39d3-35cc-4836-a082-693143ee9135', Side.THT, Shape.RECT, Position(0.0, 22.86), Rotation(0.0), Size(2.54, 1.5875), Drill(1.0))) footprint.add_pad( FootprintPad('6100dd55-d3b3-4139-9085-d5a75e783c37', Side.THT, Shape.ROUND, Position(0.0, 20.32), Rotation(0.0), Size(2.54, 1.5875), Drill(1.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}}')) footprint.add_text(stroke_text) return footprint
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
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' +\ ')'
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)
def test_polygon() -> None: 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, -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' +\ ')'
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 _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
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))