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
示例#3
0
 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))
示例#8
0
    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))