Beispiel #1
0
def point_path(points=[(0, 0), (4, 0), (4, 8)], width=1, layer=0):
    points = np.asarray(points)
    dxdy = points[1:] - points[:-1]
    angles = (np.arctan2(dxdy[:, 1], dxdy[:, 0])).tolist()
    angles = np.array([angles[0]] + angles + [angles[-1]])
    diff_angles = (angles[1:] - angles[:-1])
    mean_angles = (angles[1:] + angles[:-1]) / 2
    dx = width / 2 * np.cos((mean_angles - pi / 2)) / np.cos((diff_angles / 2))
    dy = width / 2 * np.sin((mean_angles - pi / 2)) / np.cos((diff_angles / 2))
    left_points = points.T - np.array([dx, dy])
    right_points = points.T + np.array([dx, dy])
    all_points = np.concatenate([left_points.T, right_points.T[::-1]])

    D = Device()
    D.add_polygon(all_points, layer=layer)
    D.add_port(name=1,
               midpoint=points[0],
               width=width,
               orientation=angles[0] * 180 / pi + 180)
    D.add_port(name=2,
               midpoint=points[-1],
               width=width,
               orientation=angles[-1] * 180 / pi)
    return D


# quickplot(point_path())
    def _import_gds(self):
        gdsii_lib = gdspy.GdsLibrary()
        gdsii_lib.read_gds(self.filename)
        top_level_cells = gdsii_lib.top_level()
        cellname = self.cellname
        if cellname is not None:
            if cellname not in gdsii_lib.cell_dict:
                raise ValueError(
                    'The requested cell (named %s) is not present in file %s' %
                    (cellname, self.filename))
            topcell = gdsii_lib.cell_dict[cellname]
        elif cellname is None and len(top_level_cells) == 1:
            topcell = top_level_cells[0]
        elif cellname is None and len(top_level_cells) > 1:
            areas = []
            for cell in top_level_cells:
                areas.append(cell.area())
            ind = areas.index(max(areas))
            topcell = top_level_cells[ind]

        D = Device('import_gds')
        polygons = topcell.get_polygons(by_spec=True)

        for layer_in_gds, polys in polygons.items():
            D.add_polygon(polys, layer=layer_in_gds)
        return D
Beispiel #3
0
def _arc(radius=10,
         width=0.5,
         theta=45,
         start_angle=0,
         angle_resolution=2.5,
         layer=0):
    """ Creates an arc of arclength ``theta`` starting at angle ``start_angle`` """
    inner_radius = radius - width / 2
    outer_radius = radius + width / 2
    angle1 = (start_angle) * pi / 180
    angle2 = (start_angle + theta) * pi / 180
    t = np.linspace(angle1, angle2, np.ceil(abs(theta) / angle_resolution))
    inner_points_x = (inner_radius * cos(t)).tolist()
    inner_points_y = (inner_radius * sin(t)).tolist()
    outer_points_x = (outer_radius * cos(t)).tolist()
    outer_points_y = (outer_radius * sin(t)).tolist()
    xpts = inner_points_x + outer_points_x[::-1]
    ypts = inner_points_y + outer_points_y[::-1]

    D = Device('arc')
    D.add_polygon(points=(xpts, ypts), layer=layer)
    D.add_port(name=1,
               midpoint=(radius * cos(angle1), radius * sin(angle1)),
               width=width,
               orientation=start_angle - 90 + 180 * (theta < 0))
    D.add_port(name=2,
               midpoint=(radius * cos(angle2), radius * sin(angle2)),
               width=width,
               orientation=start_angle + theta + 90 - 180 * (theta < 0))
    D.info['length'] = (abs(theta) * pi / 180) * radius
    return D
Beispiel #4
0
def _make_poly_connection(p1, p2, layer):

    d = Device()

    try:

        for l in layer:

            d.add_polygon([
                p1.endpoints[0], p1.endpoints[1], p2.endpoints[1],
                p2.endpoints[0]
            ],
                          layer=l)
            d.add_polygon([
                p1.endpoints[0], p1.endpoints[1], p2.endpoints[0],
                p2.endpoints[1]
            ],
                          layer=l)

    except:

        d.add_polygon([
            p1.endpoints[0], p1.endpoints[1], p2.endpoints[1], p2.endpoints[0]
        ],
                      layer=layer)
        d.add_polygon([
            p1.endpoints[0], p1.endpoints[1], p2.endpoints[1], p2.endpoints[0]
        ],
                      layer=l)

    return join(d)
Beispiel #5
0
def from_phidl(component: Device, **kwargs) -> Component:
    """Returns gf.Component from a phidl Device or function"""
    device = call_if_func(component, **kwargs)
    component = Component(name=device.name)

    for ref in device.references:
        new_ref = ComponentReference(
            component=ref.parent,
            origin=ref.origin,
            rotation=ref.rotation,
            magnification=ref.magnification,
            x_reflection=ref.x_reflection,
        )
        new_ref.owner = component
        component.add(new_ref)
        for alias_name, alias_ref in device.aliases.items():
            if alias_ref == ref:
                component.aliases[alias_name] = new_ref

    for p in device.ports.values():
        component.add_port(
            port=Port(
                name=p.name,
                midpoint=p.midpoint,
                width=p.width,
                orientation=p.orientation,
                parent=p.parent,
            )
        )
    for poly in device.polygons:
        component.add_polygon(poly)
    for label in device.labels:
        component.add_label(
            text=label.text,
            position=label.position,
            layer=(label.layer, label.texttype),
        )
    return component
Beispiel #6
0
def route_basic(port1,
                port2,
                path_type='sine',
                width_type='straight',
                width1=None,
                width2=None,
                num_path_pts=99,
                layer=0):

    # Assuming they're both Ports for now
    point_a = np.array(port1.midpoint)
    if width1 is None: width1 = port1.width
    point_b = np.array(port2.midpoint)
    if width2 is None: width2 = port2.width
    if round(abs(mod(port1.orientation - port2.orientation, 360)), 3) != 180:
        raise ValueError(
            '[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)'
        )
    orientation = port1.orientation

    separation = point_b - point_a  # Vector drawn from A to B
    distance = norm(separation)  # Magnitude of vector from A to B
    rotation = np.arctan2(
        separation[1],
        separation[0]) * 180 / pi  # Rotation of vector from A to B
    angle = rotation - orientation  # If looking out along the normal of ``a``, the angle you would have to look to see ``b``
    forward_distance = distance * cos(angle * pi / 180)
    lateral_distance = distance * sin(angle * pi / 180)

    # Create a path assuming starting at the origin and setting orientation = 0
    # use the "connect" function later to move the path to the correct location
    xf = forward_distance
    yf = lateral_distance
    if path_type == 'straight':
        curve_fun = lambda t: [xf * t, yf * t]
        curve_deriv_fun = lambda t: [xf + t * 0, t * 0]
    if path_type == 'sine':
        curve_fun = lambda t: [xf * t, yf * (1 - cos(t * pi)) / 2]
        curve_deriv_fun = lambda t: [xf + t * 0, yf * (sin(t * pi) * pi) / 2]
    #if path_type == 'semicircle':
    #    def semicircle(t):
    #        t = np.array(t)
    #        x,y = np.zeros(t.shape), np.zeros(t.shape)
    #        ii = (0 <= t) & (t <= 0.5)
    #        jj = (0.5 < t) & (t <= 1)
    #        x[ii] = (cos(-pi/2 + t[ii]*pi/2))*xf
    #        y[ii] = (sin(-pi/2 + t[ii]*pi/2)+1)*yf*2
    #        x[jj] = (cos(pi*3/2 - t[jj]*pi)+2)*xf/2
    #        y[jj] = (sin(pi*3/2 - t[jj]*pi)+1)*yf/2
    #        return x,y
    #    curve_fun = semicircle
    #    curve_deriv_fun = None
    if width_type == 'straight':
        width_fun = lambda t: (width2 - width1) * t + width1
    if width_type == 'sine':
        width_fun = lambda t: (width2 - width1) * (1 - cos(t * pi)
                                                   ) / 2 + width1

    route_path = gdspy.Path(width=width1, initial_point=(0, 0))
    route_path.parametric(curve_fun,
                          curve_deriv_fun,
                          number_of_evaluations=num_path_pts,
                          max_points=199,
                          final_width=width_fun,
                          final_distance=None)
    route_path_polygons = route_path.polygons

    # Make the route path into a Device with ports, and use "connect" to move it
    # into the proper location
    D = Device()
    D.add_polygon(route_path_polygons, layer=layer)
    p1 = D.add_port(name=1, midpoint=(0, 0), width=width1, orientation=180)
    p2 = D.add_port(name=2,
                    midpoint=[forward_distance, lateral_distance],
                    width=width2,
                    orientation=0)
    D.info['length'] = route_path.length

    D.rotate(angle=180 + port1.orientation - p1.orientation,
             center=p1.midpoint)
    D.move(origin=p1, destination=port1)
    return D
Beispiel #7
0
def _get_glyph(font, letter):
    """
    Get a block reference to the given letter
    """
    if not isinstance(letter, six.string_types) and len(letter) == 1:
        raise TypeError("Letter must be a string of length 1. Got: (%s)." %
                        (letter))

    if not isinstance(font, freetype.Face):
        raise TypeError(
            ("font %r must be a freetype font face. "
             "Load a font using _get_font_by_name first.") % (font))

    if getattr(font, "gds_glyphs", None) is None:
        font.gds_glyphs = {}

    if letter in font.gds_glyphs:
        return font.gds_glyphs[letter]

    # Get the font name
    font_name = font.get_sfnt_name(
        freetype.TT_NAME_IDS['TT_NAME_ID_PS_NAME']).string.decode()
    if not font_name:
        # If there is no postscript name, use the family name
        font_name = font.family_name.replace(" ", "_")

    block_name = "*char_%s_0x%2X" % (font_name, ord(letter))

    # Load control points from font file
    font.load_char(letter, freetype.FT_LOAD_FLAGS['FT_LOAD_NO_BITMAP'])
    glyph = font.glyph
    outline = glyph.outline
    points = np.array(outline.points, dtype=float) / font.size.ascender
    tags = outline.tags

    # Add polylines
    start, end = 0, -1
    polylines = []
    for contour in outline.contours:
        start = end + 1
        end = contour

        # Build up the letter as a curve
        cpoint = start
        curve = gdspy.Curve(*points[cpoint], tolerance=0.001)
        while cpoint <= end:
            # Figure out what sort of point we are looking at
            if tags[cpoint] & 1:
                # We are at an on-curve control point. The next point may be
                # another on-curve point, in which case we create a straight
                # line interpolation, or it may be a quadratic or cubic
                # bezier curve. But first we check if we are at the end of the array
                if cpoint == end:
                    ntag = tags[start]
                    npoint = points[start]
                else:
                    ntag = tags[cpoint + 1]
                    npoint = points[cpoint + 1]

                # Then add the control points
                if ntag & 1:
                    curve.L(*npoint)
                    cpoint += 1
                elif ntag & 2:
                    # We are at a cubic bezier curve point
                    if cpoint + 3 <= end:
                        curve.C(*points[cpoint + 1:cpoint + 4].flatten())
                    elif cpoint + 2 <= end:
                        plist = list(points[cpoint + 1:cpoint + 3].flatten())
                        plist.extend(points[start])
                        curve.C(*plist)
                    else:
                        raise ValueError(
                            "Missing bezier control points. We require at least"
                            " two control points to get a cubic curve.")
                    cpoint += 3
                else:
                    # Otherwise we're at a quadratic bezier curve point
                    if cpoint + 2 > end:
                        cpoint_2 = start
                        end_tag = tags[start]
                    else:
                        cpoint_2 = cpoint + 2
                        end_tag = tags[cpoint_2]
                    p1 = points[cpoint + 1]
                    p2 = points[cpoint_2]

                    # Check if we are at a sequential control point. In that case,
                    # p2 is actually the midpoint of p1 and p2.
                    if end_tag & 1 == 0:
                        p2 = (p1 + p2) / 2

                    # Add the curve
                    curve.Q(p1[0], p1[1], p2[0], p2[1])
                    cpoint += 2
            else:
                # We are looking at a control point
                if not tags[cpoint] & 2:
                    # We are at a quadratic sequential control point.
                    # Check if we're at the end of the segment
                    if cpoint == end:
                        cpoint_1 = start
                        end_tag = tags[start]
                    else:
                        cpoint_1 = cpoint + 1
                        end_tag = tags[cpoint_1]

                    # If we are at the beginning, this is a special case,
                    # we need to reset the starting position
                    if cpoint == start:
                        p0 = points[end]
                        p1 = points[cpoint]
                        p2 = points[cpoint_1]
                        if tags[end] & 1 == 0:
                            # If the last point is also a control point, then the end is actually
                            # halfway between here and the last point
                            p0 = (p0 + p1) / 2
                        # And reset the starting position of the spline
                        curve = gdspy.Curve(*p0, tolerance=0.001)
                    else:
                        # The first control point is at the midpoint of this control point and the
                        # previous control point
                        p0 = points[cpoint - 1]
                        p1 = points[cpoint]
                        p2 = points[cpoint_1]
                        p0 = (p0 + p1) / 2

                    # Check if we are at a sequential control point again
                    if end_tag & 1 == 0:
                        p2 = (p1 + p2) / 2

                    # And add the segment
                    curve.Q(p1[0], p1[1], p2[0], p2[1])
                    cpoint += 1
                else:
                    raise ValueError(
                        "Sequential control points not valid for cubic splines."
                    )
        polylines.append(gdspy.Polygon(curve.get_points()))

    # Construct the device
    device = Device(block_name)
    if polylines:
        letter_polyline = polylines[0]
        for polyline in polylines[1:]:
            letter_polyline = gdspy.boolean(letter_polyline, polyline, "xor")
        device.add_polygon(letter_polyline)

    # Cache the return value and return it
    font.gds_glyphs[letter] = (device, glyph.advance.x / font.size.ascender)
    return font.gds_glyphs[letter]
Beispiel #8
0
def text(text='abcd', size=10, justify='left', layer=0, font="DEPLOF"):
    """ Creates geometries of text

    Parameters
    ----------
    text : str
        Text string to be written.
    size : int or float
        Size of the text
    justify : {'left', 'right', 'center'}
        Justification of the text.
    layer : int, array-like[2], or set
        Specific layer(s) to put polygon geometry on.
    font: str
        Font face to use. Default DEPLOF does not require additional libraries, otherwise
        freetype will be used to load fonts. Font can be given either by name (e.g. "Times New Roman"),
        or by file path. OTF or TTF fonts are supported.

    Returns
    -------
    t : Device
        A Device containing the text geometry.
    """
    t = Device('text')
    xoffset = 0
    yoffset = 0

    face = font
    if face == "DEPLOF":
        scaling = size / 1000

        for line in text.split('\n'):
            l = Device(name='textline')
            for c in line:
                ascii_val = ord(c)
                if c == ' ':
                    xoffset += 500 * scaling
                elif (33 <= ascii_val <= 126) or (ascii_val == 181):
                    for poly in _glyph[ascii_val]:
                        xpts = np.array(poly)[:, 0] * scaling
                        ypts = np.array(poly)[:, 1] * scaling
                        l.add_polygon([xpts + xoffset, ypts + yoffset],
                                      layer=layer)
                    xoffset += (_width[ascii_val] +
                                _indent[ascii_val]) * scaling
                else:
                    valid_chars = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~µ'
                    warnings.warn('[PHIDL] text(): Warning, some characters ignored, no geometry for character "%s" with ascii value %s. ' \
                                  'Valid characters: %s' % (chr(ascii_val), ascii_val, valid_chars))
            t.add_ref(l)
            yoffset -= 1500 * scaling
            xoffset = 0
    else:
        from .font import _get_font_by_name, _get_font_by_file, _get_glyph

        # Load the font
        # If we've passed a valid file, try to load that, otherwise search system fonts
        font = None
        if (face.endswith(".otf")
                or face.endswith(".ttf")) and os.path.exists(face):
            font = _get_font_by_file(face)
        else:
            try:
                font = _get_font_by_name(face)
            except ValueError:
                pass
        if font is None:
            raise ValueError((
                '[PHIDL] Failed to find font: "%s". ' +
                'Try specifying the exact (full) path to the .ttf or .otf file. '
                +
                'Otherwise, it might be resolved by rebuilding the matplotlib font cache'
            ) % (face))

        # Render each character
        for line in text.split('\n'):
            l = Device('textline')
            xoffset = 0
            for letter in line:
                letter_dev = Device("letter")
                letter_template, advance_x = _get_glyph(font, letter)
                for poly in letter_template.polygons:
                    letter_dev.add_polygon(poly.polygons, layer=layer)
                ref = l.add_ref(letter_dev)
                ref.move(destination=(xoffset, 0))
                ref.magnification = size
                xoffset += size * advance_x

            ref = t.add_ref(l)
            ref.move(destination=(0, yoffset))
            yoffset -= size

    justify = justify.lower()
    for l in t.references:
        if justify == 'left': pass
        if justify == 'right': l.xmax = 0
        if justify == 'center':
            l.move(origin=l.center, destination=(0, 0), axis='x')

    t.flatten()
    return t