Exemple #1
0
def draw_bbox(
        ax: matplotlib.axes.Axes,
        bbox: List,
        color: Tuple[int],
        text: str = None,
        alpha: float = 0.2,
) -> None:
    """ Draw a bounding box on the matplotlib axes object. """
    # TODO: fix the bordering in matplotlib so that the pixels
    #   line up appropriately bounding boxes are [x, y, w, h]
    log.debug(f'Drawing bbox {bbox} {color}')
    r = Rectangle((bbox[0], bbox[1]),
                  (bbox[2]),
                  (bbox[3]),
                  linewidth=3,
                  facecolor=color,
                  edgecolor=color,
                  alpha=alpha,
                  )
    # Add text above box
    if text is not None:
        ax.text(
            x=bbox[0],
            y=bbox[1],
            s=text,
            color=color,
            weight='bold',
            fontsize=6,
            ha='left',
            va='bottom',
        )
    ax.add_patch(r)
Exemple #2
0
def draw_elements(
    ax: mpl.axes.Axes,
    lattice: Lattice,
    *,
    labels: bool = True,
    location: str = "top",
):
    """Draw the elements of a lattice onto a matplotlib axes."""
    x_min, x_max = ax.get_xlim()
    y_min, y_max = ax.get_ylim()
    rect_height = 0.05 * (y_max - y_min)
    if location == "top":
        y0 = y_max = y_max + rect_height
    else:
        y0 = y_min - rect_height
        y_min -= 3 * rect_height
        plt.hlines(y0, x_min, x_max, color="black", linewidth=1)
    ax.set_ylim(y_min, y_max)

    sign = -1
    start = end = 0
    for element, group in groupby(lattice.sequence):
        start = end
        end += element.length * sum(1 for _ in group)
        if end <= x_min:
            continue
        elif start >= x_max:
            break

        try:
            color = ELEMENT_COLOR[type(element)]
        except KeyError:
            continue

        y0_local = y0
        if isinstance(element, Dipole) and element.angle < 0:
            y0_local += rect_height / 4

        ax.add_patch(
            plt.Rectangle(
                (max(start, x_min), y0_local - 0.5 * rect_height),
                min(end, x_max) - max(start, x_min),
                rect_height,
                facecolor=color,
                clip_on=False,
                zorder=10,
            ))
        if labels and type(element) in {Dipole, Quadrupole}:
            sign = -sign
            ax.annotate(
                element.name,
                xy=(0.5 * (start + end), y0 + sign * rect_height),
                fontsize=FONT_SIZE,
                ha="center",
                va="bottom" if sign > 0 else "top",
                annotation_clip=False,
                zorder=11,
            )
Exemple #3
0
def draw_segmentation(
    ax: matplotlib.axes.Axes,
    segmentation: List,
    color: Tuple[int],
    alpha: float = 0.6,
) -> None:
    """ Draw a segmentation polygon on the matplotlib axes object. """
    log.debug(f'Drawing segmentation {segmentation} {color}')
    for seg in segmentation:
        p = Polygon(np.array(seg).reshape((int(len(seg) / 2), 2)),
                    linewidth=3,
                    color=color,
                    alpha=alpha)
        ax.add_patch(p)
Exemple #4
0
def draw_poly(ax: mpl.axes.Axes, left: np.ndarray, bottom: np.ndarray,
              top: np.ndarray, right: np.ndarray, facecolor: str,
              edgecolor: str, zorder: int) -> None:
    '''Draw a set of polygrams given parrallel numpy arrays of left, bottom, top, right points'''
    XY = np.array([[left, left, right, right], [bottom, top, top, bottom]]).T

    barpath = path.Path.make_compound_path_from_polys(XY)

    # Clean path to get rid of 0, 0 points.  Seems to be a matplotlib bug.  If we don't ylim lower bound is set to 0
    v = []
    c = []
    for seg in barpath.iter_segments():
        vertices, command = seg
        if not (vertices[0] == 0. and vertices[1] == 0.):
            v.append(vertices)
            c.append(command)
    cleaned_path = path.Path(v, c)

    patch = mptch.PathPatch(cleaned_path,
                            facecolor=facecolor,
                            edgecolor=edgecolor,
                            zorder=zorder)
    ax.add_patch(patch)
Exemple #5
0
def draw_keypoints(
    ax: matplotlib.axes.Axes,
    keypoints: List,
    skeleton: Dict,
    color: Tuple[int],
    alpha: float = 0.8,
) -> None:
    """
    Draws keypoints of an instance and follows the rules for keypoint connections
    to draw lines between appropriate keypoints.

    "keypoints": [x1,y1,v1,...,xk,yk,vk]
    - Keypoint coordinates are floats measured from the top left image corner (and are 0-indexed).
    - We recommend rounding coordinates to the nearest pixel to reduce file size.
    - v indicates visibility
            v=0: not labeled (in which case x=y=0)
            v=1: labeled but not visible
            v=2: labeled and visible

    """
    log.debug("Drawing keypoints")
    for k1, k2 in skeleton:

        # HACK: 0 indexed versus 1 indexed skeleton
        if min(min(skeleton)) == 1:
            k1 -= 1
            k2 -= 1

        k1_x = keypoints[3 * k1 + 0]
        k1_y = keypoints[3 * k1 + 1]
        k1_v = keypoints[3 * k1 + 2]

        k2_x = keypoints[3 * k2 + 0]
        k2_y = keypoints[3 * k2 + 1]
        k2_v = keypoints[3 * k2 + 2]

        if k1_v == 1:
            circle = Circle((k1_x, k1_y),
                            radius=5,
                            edgecolor=color,
                            facecolor="w",
                            alpha=alpha)
            ax.add_patch(circle)

        if k1_v == 2:
            circle = Circle((k1_x, k1_y),
                            radius=5,
                            edgecolor=color,
                            facecolor=color,
                            alpha=alpha)
            ax.add_patch(circle)

        if k2_v == 1:
            circle = Circle((k2_x, k2_y),
                            radius=5,
                            edgecolor=color,
                            facecolor="w",
                            alpha=alpha)
            ax.add_patch(circle)

        if k2_v == 2:
            circle = Circle((k2_x, k2_y),
                            radius=5,
                            edgecolor=color,
                            facecolor=color,
                            alpha=alpha)
            ax.add_patch(circle)

        if k1_v != 0 and k2_v != 0:
            line = Arrow(k1_x,
                         k1_y,
                         k2_x - k1_x,
                         k2_y - k1_y,
                         color=color,
                         alpha=alpha)
            ax.add_patch(line)
Exemple #6
0
def floor_plan(
    ax: mpl.axes.Axes,
    lattice: Lattice,
    *,
    start_angle: float = 0,
    labels: bool = True,
):
    ax.set_aspect("equal")
    codes = Path.MOVETO, Path.LINETO
    current_angle = start_angle
    start = np.zeros(2)
    end = np.zeros(2)
    x_min = y_min = 0
    x_max = y_max = 0
    sign = 1
    for element, group in groupby(lattice.sequence):
        start = end.copy()
        length = element.length * sum(1 for _ in group)
        if isinstance(element, Drift):
            color = Color.BLACK
            line_width = 1
        else:
            color = ELEMENT_COLOR[type(element)]
            line_width = 6

        # TODO: refactor current angle
        angle = 0
        if isinstance(element, Dipole):
            angle = element.k0 * length
            radius = length / angle
            vec = radius * np.array([np.sin(angle), 1 - np.cos(angle)])
            sin = np.sin(current_angle)
            cos = np.cos(current_angle)
            rot = np.array([[cos, -sin], [sin, cos]])
            end += rot @ vec

            angle_center = current_angle + 0.5 * np.pi
            center = start + radius * np.array(
                [np.cos(angle_center),
                 np.sin(angle_center)])
            diameter = 2 * radius
            arc_angle = -90
            theta1 = current_angle * 180 / np.pi
            theta2 = (current_angle + angle) * 180 / np.pi
            if angle < 0:
                theta1, theta2 = theta2, theta1

            line = patches.Arc(
                center,
                width=diameter,
                height=diameter,
                angle=arc_angle,
                theta1=theta1,
                theta2=theta2,
                color=color,
                linewidth=line_width,
            )
            current_angle += angle
        else:
            end += length * np.array(
                [np.cos(current_angle),
                 np.sin(current_angle)])
            line = patches.PathPatch(Path((start, end), codes),
                                     color=color,
                                     linewidth=line_width)

        x_min = min(x_min, end[0])
        y_min = min(y_min, end[1])
        x_max = max(x_max, end[0])
        y_max = max(y_max, end[1])

        ax.add_patch(line)  # TODO: currently splitted elements get drawn twice

        if labels and isinstance(element, (Dipole, Quadrupole)):
            angle_center = (current_angle - 0.5 * angle) + 0.5 * np.pi
            sign = -sign
            center = 0.5 * (start + end) + 0.5 * sign * np.array(
                [np.cos(angle_center),
                 np.sin(angle_center)])
            ax.annotate(
                element.name,
                xy=center,
                fontsize=6,
                ha="center",
                va="center",
                # rotation=(current_angle * 180 / np.pi -90) % 180,
                annotation_clip=False,
                zorder=11,
            )

    margin = 0.01 * max((x_max - x_min), (y_max - y_min))
    ax.set_xlim(x_min - margin, x_max + margin)
    ax.set_ylim(y_min - margin, y_max + margin)
    return ax
Exemple #7
0
def plot_reachable_zone(
        ax: matplotlib.axes.Axes,
        mount_model: MountModel,
        axis_0_west_limit: float = 110,
        axis_0_east_limit: float = 110,
    ) -> None:
    """Plot area(s) of sky reachable by the mount.

    This only accounts for what area of the sky is reachable. It does not take into account whether
    the mount can keep up with a specific target's trajectory. Some trajectories, especially
    those that pass near the mount pole, may require slew rates that exceed what the mount is
    capable of.

    This function assumes an equatorial mount with limits on the right ascension axis.

    Args:
        ax: Axes object this function will plot on. This should be generated by `make_sky_plot()`.
        mount_model: Mount model from which this plot will be generated.
        axis_0_west_limit: Western limit on axis 0 in degrees from the meridian.
        axis_0_east_limit: Eastern limit on axis 0 in degrees from the meridian.
    """
    if axis_0_west_limit < 90 or axis_0_east_limit < 90:
        # current logic would not shade the correct regions of the polar plot
        raise ValueError('Axis limits less than 90 degrees from meridian are not supported')

    # convert from arg values to encoder position angles
    axis_0_west_limit = 180 - axis_0_west_limit
    axis_0_east_limit = 180 + axis_0_east_limit

    # place a dot at the position of the mount pole
    mount_pole_topo = mount_model.spherical_to_topocentric(
        UnitSphericalRepresentation(
            lon=0*u.deg,
            lat=90*u.deg,
        )
    )
    ax.plot(np.radians(mount_pole_topo.az.deg), 90.0 - mount_pole_topo.alt.deg, 'k.')

    for meridian_side in MeridianSide:
        if meridian_side == MeridianSide.EAST:
            color = 'blue'
            legend_label = 'east of mount meridian'
            axis_1_range = np.linspace(0, 180, 100) + mount_model.model_params.axis_1_offset.deg
            az = np.linspace(mount_pole_topo.az.deg, mount_pole_topo.az.deg + 180, 100)
        else:
            axis_1_range = np.linspace(180, 360, 100) + mount_model.model_params.axis_1_offset.deg
            color = 'red'
            legend_label = 'west of mount meridian'
            az = np.linspace(mount_pole_topo.az.deg - 180, mount_pole_topo.az.deg, 100)

        # add a circle patch outside the visible area of the plot purely for the purpose of
        # generating an entry in the legend for this region
        ax.add_patch(Circle((0, 100), radius=0, color=color, alpha=0.2, label=legend_label))

        alt = 90*np.ones_like(az)
        fill_to_horizon(ax, az, alt, color=color)

        for axis_0 in (axis_0_west_limit, axis_0_east_limit):
            az = []
            alt = []
            for axis_1 in axis_1_range:
                topo = mount_model.encoders_to_topocentric(
                    MountEncoderPositions(
                        Longitude(axis_0*u.deg),
                        Longitude(axis_1*u.deg),
                    )
                )
                az.append(topo.az.deg)
                alt.append(topo.alt.deg)
            az = np.array(az)
            alt = np.array(alt)
            ax.plot(np.radians(az), 90.0 - alt, ':', color=color)

            if axis_0 == axis_0_east_limit and meridian_side == MeridianSide.EAST:
                fill_to_horizon(ax, az, alt, color=color)
            elif axis_0 == axis_0_west_limit and meridian_side == MeridianSide.EAST:
                fill_to_zenith(ax, az, alt, color=color)
            elif axis_0 == axis_0_east_limit and meridian_side == MeridianSide.WEST:
                fill_to_zenith(ax, az, alt, color=color)
            elif axis_0 == axis_0_west_limit and meridian_side == MeridianSide.WEST:
                fill_to_horizon(ax, az, alt, color=color)