예제 #1
0
def intersect(A, eA, B, eB):
    """Computes intersection between lines defined by points A/B and vectors eA/eB"""

    # from http://mathforum.org/library/drmath/view/62814.html

    assert abs(cross_prod(eA, eB)) > 0, "Vectors must not be parallel"

    a = cross_prod(B - A, eB) / cross_prod(eA, eB)
    return A + a * eA
예제 #2
0
    def smooth_append(point_list, point):
        if len(point_list) < 1:
            point_list.append(point)
            return point_list
        elif len(point_list) < 2:
            curr_edge = point - point_list[-1]
            if norm(curr_edge) > 0:
                point_list.append(point)
                return point_list

        curr_edge = point - point_list[-1]
        if norm(curr_edge) > 0:
            prev_edge = point_list[-1] - point_list[-2]

            # Only add new point if the area of the triangle built with
            # current edge and previous edge is greater than dbu^2/2
            if abs(cross_prod(prev_edge, curr_edge)) > dbu**2 / 2:
                if smooth:
                    # avoid corners when smoothing
                    if cos_angle(curr_edge, prev_edge) > cos(130 / 180 * pi):
                        point_list.append(point)
                    else:
                        # edge case when there is prev_edge is small and
                        # needs to be deleted to get rid of the corner
                        if norm(curr_edge) > norm(prev_edge):
                            point_list[-1] = point
                else:
                    point_list.append(point)
            # avoid unnecessary points
            else:
                point_list[-1] = point
        return point_list
예제 #3
0
def solve_V(A, B, C, radius):
    XB = bisect(A - B, C - B)

    isCCW = cross_prod(C - B, A - B) > 0

    Aprime = project(A, B, XB + B)
    Cprime = project(C, B, XB + B)

    rA = (A - Aprime).norm()
    rC = (C - Cprime).norm()

    if rA > rC:
        Csec = project(Cprime, A, B)
        return [_Line(A, Csec), _Arc(Csec, Cprime, C, isCCW)], []
    else:
        Asec = project(Aprime, B, C)
        return [_Arc(A, Aprime, Asec, isCCW)], [Asec, C]
예제 #4
0
def rectangle(center, width, height, ex, ey):
    """
    returns the polygon of a rectangle centered at center,
    aligned with ex, with width and height in microns

    Args:
        center: pya.DPoint (um units)
        width (x axis): float (um units)
        height (y axis): float (um unit)
        ex: orientation of x axis
        ey: orientation of y axis
    """

    if cross_prod(ex, ey) == 0:
        raise RuntimeError("ex={} and ey={} are not orthogonal.".format(
            repr(ex), repr(ey)))

    point1 = center - width / 2 * ex - height / 2 * ey
    point3 = center + width / 2 * ex + height / 2 * ey

    return box(point1, point3, ex=ex, ey=ey)
예제 #5
0
def solve_U(A, B, C, D, radius):
    # TODO: known bug. This assumes that there is enough space between
    # A and B / C and D to perform the turn. Suggestion: if there isn't,
    # abort or move Eprime and Gprime accordingly.
    XB = bisect(A - B, C - B)
    XC = bisect(B - C, D - C)

    orientation = cross_prod(XB, XC) > 0  # positive if CCW waveguide turn

    X = intersect(B, XB, C, XC)

    XB, XC = B - X, C - X

    Fprime = project(X, B, C)

    h = (Fprime - X).norm()

    # if h is too close to R, we will have extra unnecessary arcs
    # use two solve_3 with h as a radius instead
    if h >= radius - 0.001:
        solution1, rest_points = solve_3(A, B, C, h)
        solution2, rest_points = solve_3(rest_points[0], C, D, h)
        return solution1 + solution2, rest_points

    # F = X + (Fprime - X) * radius / h

    # Bprime = X + XB * radius / h
    # Cprime = X + XC * radius / h

    eAB = B - A
    eAB /= eAB.norm()
    eDC = C - D
    eDC /= eDC.norm()

    Eprime = project(X, A, B)
    Gprime = project(X, D, C)

    E = X + (Eprime - X) * radius / h
    G = X + (Gprime - X) * radius / h

    def compute_A_prime(E, Eprime, eAB):
        from math import sqrt

        D = (E - Eprime).norm()
        L = sqrt(D * (4 * radius - D))
        Aprime = Eprime - eAB * L
        return Aprime

    Aprime = compute_A_prime(E, Eprime, eAB)
    Dprime = compute_A_prime(G, Gprime, eDC)

    Asec = Aprime + (E - X)
    Dsec = Dprime + (G - X)

    H = 0.5 * (Asec + X)
    II = 0.5 * (Dsec + X)

    return (
        [
            _Line(A, Aprime),
            _Arc(Aprime, Asec, H, not orientation),
            _Arc(H, X, II, orientation),
            _Arc(II, Dsec, Dprime, not orientation),
        ],
        [Dprime, D],
    )
예제 #6
0
 def sin_angle(point1, point2):
     return cross_prod(point1, point2) / norm(point1) / norm(point2)
예제 #7
0
def waveguide_dpolygon(points_list, width, dbu, smooth=True):
    """Returns a polygon outlining a waveguide.

    This was updated over many iterations of failure. It can be used for both
    smooth optical waveguides or DC metal traces with corners. It is better
    than klayout's Path because it can have varying width.

    Args:
        points_list: list of pya.DPoint (at least 2 points)
        width (microns): constant or list. If list, then it has to have the same length as points
        dbu: dbu: typically 0.001, only used for accuracy calculations.
        smooth: tries to smooth final polygons to avoid very sharp edges (greater than 130 deg)
    Returns:
        polygon DPoints

    """
    if len(points_list) < 2:
        raise NotImplementedError("ERROR: points_list too short")
        return

    def norm(self):
        return sqrt(self.x**2 + self.y**2)

    # Prepares a joint point and width iterators
    try:
        if len(width) == len(points_list):
            width_iterator = iter(width)
        elif len(width) == 2:
            # assume width[0] is initial width and
            # width[1] is final width
            # interpolate with points_list
            L = curve_length(points_list)
            distance = 0
            widths_list = [width[0]]
            widths_func = lambda t: (1 - t) * width[0] + t * width[1]
            old_point = points_list[0]
            for point in points_list[1:]:
                distance += norm(point - old_point)
                old_point = point
                widths_list.append(widths_func(distance / L))
            width_iterator = iter(widths_list)
        else:
            width_iterator = repeat(width[0])
    except TypeError:
        width_iterator = repeat(width)
    finally:
        points_iterator = iter(points_list)

    points_low = list()
    points_high = list()

    def cos_angle(point1, point2):
        cos_angle = point1 * point2 / norm(point1) / norm(point2)

        # ensure it's between -1 and 1 (nontrivial numerically)
        if abs(cos_angle) > 1:
            return cos_angle / abs(cos_angle)
        else:
            return cos_angle

    def sin_angle(point1, point2):
        return cross_prod(point1, point2) / norm(point1) / norm(point2)

    point_width_list = list(zip(points_iterator, width_iterator))
    N = len(point_width_list)

    first_point, first_width = point_width_list[0]
    next_point, next_width = point_width_list[1]

    delta = next_point - first_point
    theta = np.arctan2(delta.y, delta.x)
    first_high_point = first_point + 0.5 * first_width * pya.DPoint(
        cos(theta + pi / 2), sin(theta + pi / 2))
    first_low_point = first_point + 0.5 * first_width * pya.DPoint(
        cos(theta - pi / 2), sin(theta - pi / 2))
    points_high.append(first_high_point)
    points_low.append(first_low_point)

    for i in range(1, N - 1):
        prev_point, prev_width = point_width_list[i - 1]
        point, width = point_width_list[i]
        next_point, next_width = point_width_list[i + 1]
        delta_prev = point - prev_point
        delta_next = next_point - point

        # based on these points, there are two algorithms available:
        # 1. arc algorithm. it detects you are trying to draw an arc
        # so it will compute the center and radius of that arc and
        # layout accordingly.
        # 2. linear trace algorithm. it is not an arc, and you want
        # straight lines with sharp corners.

        # to detect an arc, the points need to go in the same direction
        # and the width has to be bigger than the smallest distance between
        # two points.

        is_small = (min(delta_next.norm(), delta_prev.norm()) < width)
        is_arc = cos_angle(delta_next, delta_prev) > cos(30 * pi / 180)
        is_arc = is_arc and is_small
        center_arc, radius = find_arc(prev_point, point, next_point)
        if is_arc and radius < np.inf:  # algorithm 1
            ray = point - center_arc
            ray /= ray.norm()
            # if orientation is positive, the arc is going counterclockwise
            orientation = (cross_prod(ray, delta_prev) > 0) * 2 - 1
            points_low.append(point + orientation * width * ray / 2)
            points_high.append(point - orientation * width * ray / 2)
        else:  # algorithm 2
            theta_prev = np.arctan2(delta_prev.y, delta_prev.x)
            theta_next = np.arctan2(delta_next.y, delta_next.x)

            next_point_high = next_point + 0.5 * next_width * pya.DPoint(
                cos(theta_next + pi / 2), sin(theta_next + pi / 2))
            next_point_low = next_point + 0.5 * next_width * pya.DPoint(
                cos(theta_next - pi / 2), sin(theta_next - pi / 2))

            forward_point_high = point + 0.5 * width * pya.DPoint(
                cos(theta_next + pi / 2), sin(theta_next + pi / 2))
            forward_point_low = point + 0.5 * width * pya.DPoint(
                cos(theta_next - pi / 2), sin(theta_next - pi / 2))

            prev_point_high = prev_point + 0.5 * prev_width * pya.DPoint(
                cos(theta_prev + pi / 2), sin(theta_prev + pi / 2))
            prev_point_low = prev_point + 0.5 * prev_width * pya.DPoint(
                cos(theta_prev - pi / 2), sin(theta_prev - pi / 2))

            backward_point_high = point + 0.5 * width * pya.DPoint(
                cos(theta_prev + pi / 2), sin(theta_prev + pi / 2))
            backward_point_low = point + 0.5 * width * pya.DPoint(
                cos(theta_prev - pi / 2), sin(theta_prev - pi / 2))

            fix_angle = lambda theta: ((theta + pi) % (2 * pi)) - pi

            # High point decision
            next_high_edge = pya.DEdge(forward_point_high, next_point_high)
            prev_high_edge = pya.DEdge(backward_point_high, prev_point_high)

            if next_high_edge.crossed_by(prev_high_edge):
                intersect_point = next_high_edge.crossing_point(prev_high_edge)
                points_high.append(intersect_point)
            else:
                cos_dd = cos_angle(delta_next, delta_prev)
                if width * (1 - cos_dd) > dbu and fix_angle(theta_next -
                                                            theta_prev) < 0:
                    points_high.append(backward_point_high)
                    points_high.append(forward_point_high)
                else:
                    points_high.append(
                        (backward_point_high + forward_point_high) * 0.5)

            # Low point decision
            next_low_edge = pya.DEdge(forward_point_low, next_point_low)
            prev_low_edge = pya.DEdge(backward_point_low, prev_point_low)

            if next_low_edge.crossed_by(prev_low_edge):
                intersect_point = next_low_edge.crossing_point(prev_low_edge)
                points_low.append(intersect_point)
            else:
                cos_dd = cos_angle(delta_next, delta_prev)
                if width * (1 - cos_dd) > dbu and fix_angle(theta_next -
                                                            theta_prev) > 0:
                    points_low.append(backward_point_low)
                    points_low.append(forward_point_low)
                else:
                    points_low.append(
                        (backward_point_low + forward_point_low) * 0.5)

    last_point, last_width = point_width_list[-1]
    point, width = point_width_list[-2]
    delta = last_point - point
    theta = np.arctan2(delta.y, delta.x)
    final_high_point = last_point + 0.5 * last_width * pya.DPoint(
        cos(theta + pi / 2), sin(theta + pi / 2))
    final_low_point = last_point + 0.5 * last_width * pya.DPoint(
        cos(theta - pi / 2), sin(theta - pi / 2))
    if (final_high_point - points_high[-1]) * delta > 0:
        points_high.append(final_high_point)
    if (final_low_point - points_low[-1]) * delta > 0:
        points_low.append(final_low_point)

    # Append point only if the area of the triangle built with
    # neighboring edges is above a certain threshold.
    # In addition, if smooth is true:
    # Append point only if change in direction is less than 130 degrees.

    def smooth_append(point_list, point):
        if len(point_list) < 1:
            point_list.append(point)
            return point_list
        elif len(point_list) < 2:
            curr_edge = point - point_list[-1]
            if norm(curr_edge) > 0:
                point_list.append(point)
                return point_list

        curr_edge = point - point_list[-1]
        if norm(curr_edge) > 0:
            prev_edge = point_list[-1] - point_list[-2]

            # Only add new point if the area of the triangle built with
            # current edge and previous edge is greater than dbu^2/2
            if abs(cross_prod(prev_edge, curr_edge)) > dbu**2 / 2:
                if smooth:
                    # avoid corners when smoothing
                    if cos_angle(curr_edge, prev_edge) > cos(130 / 180 * pi):
                        point_list.append(point)
                    else:
                        # edge case when there is prev_edge is small and
                        # needs to be deleted to get rid of the corner
                        if norm(curr_edge) > norm(prev_edge):
                            point_list[-1] = point
                else:
                    point_list.append(point)
            # avoid unnecessary points
            else:
                point_list[-1] = point
        return point_list

    if debug and False:
        print("Points to be smoothed:")
        for point, width in point_width_list:
            print(point, width)

    smooth_points_high = list(reduce(smooth_append, points_high, list()))
    smooth_points_low = list(reduce(smooth_append, points_low, list()))
    # smooth_points_low = points_low
    # polygon_dpoints = points_high + list(reversed(points_low))
    # polygon_dpoints = list(reduce(smooth_append, polygon_dpoints, list()))
    polygon_dpoints = smooth_points_high + list(reversed(smooth_points_low))
    return pya.DSimplePolygon(polygon_dpoints)