Exemple #1
0
def _port_marker(port, is_subport):
    if is_subport:
        arrow_scale = 0.75
        rad = (port.orientation+45)*np.pi/180
        pm = +1
    else:
        arrow_scale = 1
        rad = (port.orientation-45)*np.pi/180
        pm = -1
    arrow_points = np.array([[0,0],[10,0],[6,pm*4],[6,pm*2],[0,pm*2]])/35*port.width*arrow_scale
    arrow_points += port.midpoint
    arrow_points = _rotate_points(arrow_points, angle = port.orientation, center = port.center)
    text_pos = np.array([np.cos(rad), np.sin(rad)])*port.width/3 + port.center
    return arrow_points, text_pos
Exemple #2
0
def _transform_port(
    point, orientation, origin=(0, 0), rotation=None, x_reflection=False
):
    new_point = np.array(point)
    new_orientation = orientation

    if x_reflection:
        new_point[1] = -new_point[1]
        new_orientation = -orientation
    if rotation is not None:
        new_point = _rotate_points(new_point, angle=rotation, center=[0, 0])
        new_orientation += rotation
    if origin is not None:
        new_point = new_point + np.array(origin)
    new_orientation = new_orientation % 360

    return new_point, new_orientation
Exemple #3
0
def _draw_port(ax, port, arrow_scale, color):
    # x,y = port.midpoint
    nv = port.normal
    n = (nv[1] - nv[0])
    dx, dy = n * port.width / 8 * arrow_scale
    dx += n[1] * port.width / 8 * arrow_scale
    dy += n[0] * port.width / 8 * arrow_scale
    # dx,dy = np.array(np.cos(port.orientation/180*np.pi), np.sin(port.orientation/180*np.pi))*port.width/10*arrow_scale + \
    #         np.array(np.cos((port.orientation+90)/180*np.pi), np.sin((port.orientation+90)/180*np.pi))*port.width/4*arrow_scale
    # print(port.midpoint)
    # print(port.width)
    # print(nv)
    xbound, ybound = np.column_stack(port.endpoints)
    #plt.plot(x, y, 'rp', markersize = 12) # Draw port midpoint
    arrow_points = np.array([[0, 0], [10, 0], [6, 4], [6, 2], [0, 2]
                             ]) / (40) * port.width * arrow_scale
    arrow_points += port.midpoint
    arrow_points = _rotate_points(arrow_points,
                                  angle=port.orientation,
                                  center=port.midpoint)
    xmin, ymin = np.min(np.vstack([arrow_points, port.endpoints]), axis=0)
    xmax, ymax = np.max(np.vstack([arrow_points, port.endpoints]), axis=0)
    ax.plot(xbound, ybound, alpha=0.5, linewidth=3,
            color=color)  # Draw port edge
    ax.plot(arrow_points[:, 0],
            arrow_points[:, 1],
            alpha=0.5,
            linewidth=1,
            color=color)  # Draw port edge
    # plt.arrow(x, y, dx, dy,length_includes_head=True, width = 0.1*arrow_scale,
    #           head_width=0.3*arrow_scale, alpha = 0.5, **kwargs)
    ax.text(port.midpoint[0] + dx,
            port.midpoint[1] + dy,
            port.name,
            horizontalalignment='center',
            verticalalignment='center',
            fontsize=14)
    bbox = [xmin, ymin, xmax, ymax]
    return bbox
Exemple #4
0
def euler(radius=3, angle=90, p=1.0, use_eff=False, num_pts=720):
    """ Create an Euler bend (also known as "racetrack" or "clothoid" curves)
    that adiabatically transitions from straight to curved.  By default,
    `radius` corresponds to the minimum radius of curvature of the bend.
    However, if `use_eff` is set to True, `radius` corresponds to the effective
    radius of curvature (making the curve a drop-in replacement for an arc). If
    p < 1.0, will create a "partial euler" curve as described in Vogelbacher et.
    al. https://dx.doi.org/10.1364/oe.27.031394

    Parameters
    ----------
    radius : int or float
        Minimum radius of curvature
    angle : int or float
        Total angle of curve
    p : float
        Proportion of curve that is an Euler curve
    use_eff : bool
        If False: `radius` corresponds to minimum radius of curvature of the bend
        If True: The curve will be scaled such that the endpoints match an arc
        with parameters `radius` and `angle`
    num_pts : int
        Number of points used per 360 degrees

    Returns
    -------
    Path
        A Path object with the specified Euler curve
    """
    if (p < 0) or (p > 1):
        raise ValueError(
            '[PHIDL] euler() requires argument `p` be between 0 and 1')
    if p == 0:
        P = arc(radius=radius, angle=angle, num_pts=num_pts)
        P.info['Reff'] = radius
        P.info['Rmin'] = radius
        return P

    if angle < 0:
        mirror = True
        angle = np.abs(angle)
    else:
        mirror = False

    R0 = 1
    alpha = np.radians(angle)
    Rp = R0 / (np.sqrt(p * alpha))
    sp = R0 * np.sqrt(p * alpha)
    s0 = 2 * sp + Rp * alpha * (1 - p)
    num_pts = abs(int(num_pts * angle / 360))
    num_pts_euler = int(np.round(sp / (s0 / 2) * num_pts))
    num_pts_arc = num_pts - num_pts_euler

    xbend1, ybend1 = _fresnel(R0, sp, num_pts_euler)
    xp, yp = xbend1[-1], ybend1[-1]

    dx = xp - Rp * np.sin(p * alpha / 2)
    dy = yp - Rp * (1 - np.cos(p * alpha / 2))

    s = np.linspace(sp, s0 / 2, num_pts_arc)
    xbend2 = Rp * np.sin((s - sp) / Rp + p * alpha / 2) + dx
    ybend2 = Rp * (1 - np.cos((s - sp) / Rp + p * alpha / 2)) + dy

    x = np.concatenate([xbend1, xbend2[1:]])
    y = np.concatenate([ybend1, ybend2[1:]])
    points1 = np.array([x, y]).T
    points2 = np.flipud(np.array([x, -y]).T)

    points2 = _rotate_points(points2, angle - 180)
    points2 += -points2[0, :] + points1[-1, :]

    points = np.concatenate([points1[:-1], points2])

    # Find y-axis intersection point to compute Reff
    start_angle = 180 * (angle < 0)
    end_angle = start_angle + angle
    dy = np.tan(np.radians(end_angle - 90)) * points[-1][0]
    Reff = points[-1][1] - dy
    Rmin = Rp

    # Fix degenerate condition at angle == 180
    if np.abs(180 - angle) < 1e-3:
        Reff = points[-1][1] / 2

    # Scale curve to either match Reff or Rmin
    if use_eff == True:
        scale = radius / Reff
    else:
        scale = radius / Rmin
    points *= scale

    P = Path()
    # Manually add points & adjust start and end angles
    P.points = points
    P.start_angle = start_angle
    P.end_angle = end_angle
    P.info['Reff'] = Reff * scale
    P.info['Rmin'] = Rmin * scale
    if mirror == True:
        P.mirror((1, 0))
    return P
Exemple #5
0
def spiral(num_turns=5, gap=1, inner_gap=2, num_pts=10000):
    """ Creates a spiral geometry consisting of two oddly-symmetric
    semi-circular arcs in the centre and two Archimedean (involute) spiral arms
    extending outward from the ends of both arcs.

    Parameters
    ----------
    num_turns : int or float
        The number of turns in the spiral. Must be greater than 1. A full 
        spiral rotation counts as 1 turn, and the center arcs will together 
        always be 0.5 turn.
    gap : int or float
        The distance between any point on one arm of the spiral and a point 
        with the same angular coordinate on an adjacent arm.
    inner_gap : int or float
        The inner size of the spiral, equal to twice the chord length of the 
        centre arcs.
    num_pts: int
        The number of points in the entire spiral. The actual number of points 
        will be slightly different than the specified value, as they are 
        dynamically allocated using the path lengths of the spiral.

    Returns
    -------
    Path
        A Path object forming a spiral

    Notes
    -----
    ``num_turns`` usage (x is any whole number):
        - ``num_turns = x.0``: Output arm will be extended 0.5 turn to be on 
        the same side as the input.
        - ``num_turns < x.5``: Input arm will be extended by the fractional 
        amount.
        - ``num_turns = x.5``: Both arms will be the same length and the input 
        and output will be on opposite sides.
        - ``num_turns > x.5``: Output arm will be extended by the fractional 
        amount.
    """
    # Establishing number of turns in each arm
    if num_turns <= 1:
        raise ValueError('num_turns must be greater than 1')
    diff = num_turns - np.floor(num_turns)
    if diff < 0.5:
        num_turns1 = np.floor(num_turns) - 1 + 2 * diff
    else:
        num_turns1 = np.floor(num_turns)
    if diff > 0.5:
        num_turns2 = np.floor(num_turns) - 1 + 2 * diff
    else:
        num_turns2 = np.floor(num_turns)

    # Establishing relevant angles and spiral/centre arc parameters
    a1 = np.pi / 2
    a2 = np.array([np.pi * num_turns1 + a1, np.pi * num_turns2 + a1])
    a = inner_gap / 2 - gap / 2
    b = gap / np.pi
    Rc = inner_gap * np.sqrt(1 + (b / (a + b * a1))**2) / 4
    theta = np.degrees(2 * np.arcsin(inner_gap / 4 / Rc))

    # Establishing number of points in each arm
    s_centre = Rc * np.radians(theta)
    s_spiral = ((a + a2 * b)**2 + b**2)**(3 / 2) / (3 * (a * b + (a2 * b**2)))
    z = num_pts / (s_spiral[0] + s_spiral[1] + 2 * s_centre)
    num_pts0 = int(z * s_centre)
    num_pts1 = int(z * s_spiral[0])
    num_pts2 = int(z * s_spiral[1]) - num_pts1

    # Forming both spiral arms
    arm1 = np.linspace(a1, a2[0], num_pts1)
    arm2 = np.linspace(a2[0], a2[1], num_pts2)[1:]
    a_spiral = np.array([arm1, np.concatenate([arm1, arm2])])
    r_spiral = a + b * a_spiral
    x_spiral = np.array([np.zeros(num_pts1), np.zeros(len(a_spiral[1]))])
    y_spiral = np.array([np.zeros(num_pts1), np.zeros(len(a_spiral[1]))])
    for i in range(2):
        x_spiral[i] = r_spiral[i] * np.cos(a_spiral[i])
        y_spiral[i] = r_spiral[i] * np.sin(a_spiral[i])

    # Forming centre arcs
    pts = _rotate_points(
        arc(Rc, theta, 360 * num_pts0 / theta).points, -theta / 2 + 90)
    x_centre = pts[:, 0] + x_spiral[0][0] - pts[:, 0][-1]
    y_centre = pts[:, 1] + y_spiral[0][0] - pts[:, 1][-1]
    x_centre = np.concatenate([-np.flip(x_centre), x_centre])
    y_centre = np.concatenate([-np.flip(y_centre), y_centre])

    # Combining into final spiral
    x = np.concatenate([-np.flip(x_spiral[1]), x_centre, x_spiral[0]])
    y = np.concatenate([-np.flip(y_spiral[1]), y_centre, y_spiral[0]])
    points = np.array((x, y)).T

    P = Path()
    # Manually add points & adjust start and end angles
    P.points = points
    nx1, ny1 = points[1] - points[0]
    P.start_angle = np.arctan2(ny1, nx1) / np.pi * 180
    nx2, ny2 = points[-1] - points[-2]
    P.end_angle = np.arctan2(ny2, nx2) / np.pi * 180
    # print(P.start_angle)
    # print(P.end_angle)
    return P