Esempio n. 1
0
def boards(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the boards as specified
    in Rule 1.3 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    boards: a pandas dataframe of coordinates needed to plot the boards
    """
    boards = pd.DataFrame({
        'x': [0],
        'y': [42.5]
    }).append(create.circle(center=(-72, 14.5), start=.5, end=1, d=56)).append(
        pd.DataFrame({
            'x': [-100],
            'y': [0]
        })).append(create.circle(
            center=(-72, -14.5), start=1, end=1.5, d=56)).append(
                pd.DataFrame({
                    'x': [0, 0],
                    'y': [-42.5, -42.5 - (2 / 12)]
                })).append(
                    create.circle(center=(-72, -14.5),
                                  start=1.5,
                                  end=1,
                                  d=56 + (4 / 12))).append(
                                      pd.DataFrame({
                                          'x': [-100 - (2 / 12)],
                                          'y': [0]
                                      })).append(
                                          create.circle(
                                              center=(-72, 14.5),
                                              start=1,
                                              end=.5,
                                              d=56 + (4 / 12))).append(
                                                  pd.DataFrame({
                                                      'x': [0, 0],
                                                      'y':
                                                      [42.5 + (2 / 12), 42.5]
                                                  }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        boards = boards.append(transform.reflect(boards, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        boards = transform.rotate(boards, rotation_dir)

    return boards
Esempio n. 2
0
def outer_center_circle(full_surf = True, rotate = False,
                        rotation_dir = 'ccw'):
    """
    Generate the dataframe for the points that comprise the outer center circle
    as in the court diagram on page 8 of the NBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    outer_center_circle: A pandas dataframe containing the points that comprise
        the center circle of the court
    """
    # Draw the outer semicircle at half court. It has an outer radius of 6'
    # and thickness of 2"
    outer_center_circle = create.circle(
        d = 12 - (4/12),
        start = 1/2,
        end = 3/2
    ).append(
        pd.DataFrame({
            'x': [0],
            'y': [-12]
        })
    ).append(
        create.circle(
            d = 12,
            start = 3/2,
            end = 1/2
        )
    ).append(
        pd.DataFrame({
            'x': [0],
            'y': [12]
        })
    )
    
    # Reflect the x coordinates over the y axis
    if full_surf:
        outer_center_circle = outer_center_circle.append(
            transform.reflect(outer_center_circle, over_y = True)
        )
        
    # Rotate the coordinates if necessary
    if rotate:
        outer_center_circle = transform.rotate(
            outer_center_circle,
            rotation_dir
        )
    
    return outer_center_circle
Esempio n. 3
0
def net(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the rings as specified
    in the court diagram of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    nets: a pandas dataframe of the nets
    """
    # The ring's center is 15" from the backboard, and 63" from the baseline,
    # which means it is centered at (+/-41.75, 0). The ring has an interior
    # diameter of 18", which is where the net is visible from above
    net = create.circle(center=(-41.75, 0), d=1.5)

    # Reflect the x coordinates over the y axis
    if full_surf:
        net = net.append(transform.reflect(net, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        net = transform.rotate(net, rotation_dir)

    return net
Esempio n. 4
0
def goal_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goal line as
    specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goal_line: a pandas dataframe of coordinates needed to plot the goal line
    """
    # The goal line is a little tricky. It is 11' away from the boards (or 89'
    # from the center), but follows the curvature of the boards in the corner.
    # To get the curvature, a similar calculation to that of the face-off spot
    # interior can be performed
    theta1 = math.asin((17 - (1 / 12)) / 28) / np.pi
    theta2 = math.asin((17 + (1 / 12)) / 28) / np.pi

    goal_line = create.circle(center=(-72, 14.5),
                              start=.5 + theta1,
                              end=.5 + theta2,
                              d=56).append(
                                  create.circle(center=(-72, -14.5),
                                                start=1.5 - theta2,
                                                end=1.5 - theta1,
                                                d=56)).append(
                                                    create.circle(
                                                        center=(-72, 14.5),
                                                        start=.5 + theta1,
                                                        end=.5 + theta2,
                                                        d=56).iloc[0])

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal_line = goal_line.append(transform.reflect(goal_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal_line = transform.rotate(goal_line, rotation_dir)

    return goal_line
Esempio n. 5
0
def referee_crease(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the referee's crease
    as specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    referee_crease: a pandas dataframe of coordinates needed to plot the
        referee's crease
    """
    # The referee's crease is located at center ice in front of the penalty
    # timekeeper's seat. It is a 2" thick, 10' radius semi-circle
    referee_crease = create.circle(
        center=(0, -42.5), start=.5, end=1,
        d=20).append(pd.DataFrame({
            'x': [-10 + (2 / 12)],
            'y': [-42.5]
        })).append(
            create.circle(center=(0, -42.5), start=1, end=.5,
                          d=20 - (4 / 12))).append(
                              pd.DataFrame({
                                  'x': [0],
                                  'y': [10]
                              }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        referee_crease = referee_crease.append(
            transform.reflect(referee_crease, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        referee_crease = transform.rotate(referee_crease, rotation_dir)

    return referee_crease
Esempio n. 6
0
def goal(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goals as specified
    in the court diagram of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goals: a pandas dataframe of the goals
    """
    # Get the starting angle of the ring. The connector has a width of 5", so
    # 2.5" are on either side. The ring has a radius of 9", so the arcsine of
    # these measurements should give the angle at which point they connect
    start_angle = np.pi - math.asin(2.5 / 9)

    # The ending angle of the ring would be the negative of the starting angle
    end_angle = -start_angle

    # Define the coordinates for the goal
    goal = pd.DataFrame({
        'x': [-43, -41.75 - ((9 / 12) * math.cos(start_angle))],
        'y': [2.5 / 12, 2.5 / 12]
    }).append(
        create.circle(
            center=(-41.75, 0),
            start=start_angle,
            end=end_angle,
            d=1.5 + (4 / 12))).append(
                pd.DataFrame({
                    'x':
                    [-41.75 - ((9 / 12) * math.cos(start_angle)), -43, -43],
                    'y': [-2.5 / 12, -2.5 / 12, 2.5 / 12]
                }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal = goal.append(transform.reflect(goal, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal = transform.rotate(goal, rotation_dir)

    return goal
Esempio n. 7
0
def faceoff_spot(
        center=(0, 0), full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the face-off spots as
    specified in Rule 1.9 of the NHL rule book
    
    Parameters
    ----------
    center: a tuple containing the center coordinates of the spot to be drawn
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    spot_dict: a dictionary with keys of 'center' and 'spot', where 'center'
        corresponds to the center coordinates of a face-off spot, and 'spot'
        is a pandas dataframe of coordinates to plot to make a face-off spot
    """
    # Center faceoff spot
    if center == (0, 0):
        # The center face-off spot is 12" in diameter
        spot = create.circle(center=(0, 0), start=1 / 2, end=3 / 2, d=1)

        # Reflect the x coordinates over the y axis
        if full_surf:
            spot = create.circle(center=(0, 0), start=1 / 2, end=5 / 2, d=1)

        # Rotate the coordinates if necessary
        if rotate:
            spot = transform.rotate(spot, rotation_dir)

        spot_dict = {
            'center': center,
            'spot_outer': spot,
            'spot_inner': pd.DataFrame({
                'x': [],
                'y': []
            })
        }

        return spot_dict

    else:
        # The non-center face-off spots are 2' in diameter, with a 3" gap
        # between the top and bottom of the spot and the strip in the center.
        # First, find the angle at which to start the trace for the interior
        # of the spot.

        # The spot has a radius of 1', and a thickness of 2", so the inner
        # radius is 10". Since there is a 3" gap at theta = 180deg, this
        # indicates that the stripe's curve starts at x = -7" from the center.
        # Using trigonometry, the angle can be computed
        theta = math.asin(7 / 10) / np.pi

        # To draw this evenly, it's easiest to create two dataframes: one is
        # the underlying red spot of diameter 2', and the other being the
        # white inner portion described in the rule book. The starting point
        # is found using theta calculated above
        spot_outer = create.circle(center=(0, 0), start=0, end=2, d=2)

        spot_inner = create.circle(center=(0, 0),
                                   start=.5 + theta,
                                   end=1.5 - theta,
                                   d=2 - (4 / 12)).append(
                                       create.circle(center=(0, 0),
                                                     start=.5 + theta,
                                                     end=1.5 - theta,
                                                     d=2 - (4 / 12)).iloc[0])

        spot_inner = spot_inner.append(
            transform.reflect(spot_inner, over_y=True))

        # Rotate the coordinates if necessary
        if rotate:
            spot_outer = transform.rotate(spot_outer, rotation_dir)

            spot_inner = transform.rotate(spot_inner, rotation_dir)

        spot_outer = transform.translate(spot_outer,
                                         translate_x=center[0],
                                         translate_y=center[1])

        spot_inner = transform.translate(spot_inner,
                                         translate_x=center[0],
                                         translate_y=center[1])

        spot_dict = {
            'center': center,
            'spot_outer': spot_outer,
            'spot_inner': spot_inner
        }

        return spot_dict
Esempio n. 8
0
def goal_crease(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goal crease outline
    and blue inner area as specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goal_crease_dict: a dictionary with keys of 'goal_crease_outline' and
        'goal_crease_inner', where 'goal_crease_outline' is a pandas dataframe
        that corresponds to the coordinates of the red outline of the goal
        crease, and 'goal_crease_inner' is a pandas dataframe of coordinates
        of the boundary of the blue interior of the goal crease
    """
    theta = math.asin(4 / 6) / np.pi

    goal_crease_outline = pd.DataFrame({
        'x': [-89 + (1 / 12), -89 + 4.5 + (1 / 12)],
        'y': [4, 4]
    }).append(create.circle(
        center=(-89, 0), start=theta, end=-theta, d=12)).append(
            pd.DataFrame({
                'x': [
                    -89 + (1 / 12), -89 + (1 / 12), -85 + (1 / 12),
                    -85 + (1 / 12), -85 + (3 / 12), -85 + (3 / 12)
                ],
                'y': [
                    -4, -4 + (2 / 12), -4 + (2 / 12), -4 + (7 / 12),
                    -4 + (7 / 12), -4 + (2 / 12)
                ]
            })).append(
                create.circle(center=(-89, 0),
                              start=-theta,
                              end=theta,
                              d=12 - (4 / 12))).append(
                                  pd.DataFrame({
                                      'x': [
                                          -85 + (3 / 12), -85 + (3 / 12),
                                          -85 + (1 / 12), -85 + (1 / 12),
                                          -89 + (1 / 12), -89 + (1 / 12)
                                      ],
                                      'y': [
                                          4 - (2 / 12), 4 - (7 / 12),
                                          4 - (7 / 12), 4 - (2 / 12),
                                          4 - (2 / 12), 4
                                      ]
                                  }))

    goal_crease_inner = pd.DataFrame({
        'x': [
            -89 + (1 / 12), -85 + (1 / 12), -85 + (1 / 12), -85 + (3 / 12),
            -85 + (3 / 12)
        ],
        'y': [
            -4 + (2 / 12), -4 + (2 / 12), -4 + (7 / 12), -4 + (7 / 12),
            -4 + (2 / 12)
        ]
    }).append(
        create.circle(center=(-89, 0),
                      start=-theta,
                      end=theta,
                      d=12 - (4 / 12))).append(
                          pd.DataFrame({
                              'x': [
                                  -85 + (3 / 12), -85 + (3 / 12),
                                  -85 + (1 / 12), -85 + (1 / 12),
                                  -89 + (1 / 12), -89 + (1 / 12)
                              ],
                              'y': [
                                  4 - (2 / 12), 4 - (7 / 12), 4 - (7 / 12),
                                  4 - (2 / 12), 4 - (2 / 12), -4 + (2 / 12)
                              ]
                          }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal_crease_outline = goal_crease_outline.append(
            transform.reflect(goal_crease_outline, over_y=True))

        goal_crease_inner = goal_crease_inner.append(
            transform.reflect(goal_crease_inner, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal_crease_outline = transform.rotate(goal_crease_outline,
                                               rotation_dir)

        goal_crease_inner = transform.rotate(goal_crease_inner, rotation_dir)

    goal_crease_dict = {
        'goal_crease_outline': goal_crease_outline,
        'goal_crease_inner': goal_crease_inner
    }

    return goal_crease_dict
Esempio n. 9
0
def faceoff_circle(
        center=(0, 0), full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the face-off circles
    as specified in Rule 1.9 of the NHL rule book
    
    Parameters
    ----------
    center: a tuple containing the center coordinates of the spot to be drawn
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    faceoff_circle_dict: a dictionary with keys of 'center' and 'spot', where
        'center' corresponds to the center coordinates of the face-off spot,
        and 'spot' is a pandas dataframe of coordinates needed to plot a
        face-off circle
    """
    # The center circle has no external hash marks, so this circle just needs
    # to be a circle of 2" thickness
    if center == (0, 0):
        faceoff_circle = create.circle(
            center=(0, 0), start=.5, end=1.5, d=30).append(
                pd.DataFrame({
                    'x': [0, 0],
                    'y': [-15, -15 + (2 / 12)]
                })).append(
                    create.circle(center=(0, 0),
                                  start=1.5,
                                  end=.5,
                                  d=30 - (4 / 12))).append(
                                      pd.DataFrame({
                                          'x': [0, 0],
                                          'y': [15 - (2 / 12), 15]
                                      }))

    else:
        # Similar to the method described above, the starting angle to draw the
        # outer ring can be computed. The hash marks are 5' 11" (71") apart on
        # the exterior, so taking where this hash mark meets the circle to be
        # the center, the starting angle is computed as follows
        theta1 = math.asin((35.5 / 12) / 15) / np.pi

        # The same process gives the angle to find the point on the interior
        # of the hash mark, which are 5' 7" (67") apart
        theta2 = math.asin((33.5 / 12) / 15) / np.pi

        # Since the hash mark will be plotted on the top of the circle, the
        # starting angle will be theta + pi/2
        faceoff_circle = create.circle(
            center=(0, 0), start=.5 + theta1, end=1.5 - theta1, d=30).append(
                pd.DataFrame({
                    'x': [
                        -35.5 / 12,
                        -33.5 / 12,
                    ],
                    'y': [-17, -17]
                })).append(
                    create.circle(
                        center=(0, 0), start=1.5 - theta2, end=1.5,
                        d=30)).append(
                            pd.DataFrame({
                                'x': [0],
                                'y': [-15 + (2 / 12)]
                            })).append(
                                create.circle(
                                    center=(0, 0),
                                    start=1.5,
                                    end=.5,
                                    d=30 - (4 / 12))).append(
                                        pd.DataFrame({
                                            'x': [0],
                                            'y': [15]
                                        })).append(
                                            create.circle(
                                                center=(0, 0),
                                                start=.5,
                                                end=.5 + theta2,
                                                d=30)).append(
                                                    pd.DataFrame({
                                                        'x': [
                                                            -33.5 / 12,
                                                            -35.5 / 12,
                                                        ],
                                                        'y': [17, 17]
                                                    })).append(
                                                        create.circle(
                                                            center=(0, 0),
                                                            start=.5 + theta1,
                                                            end=1.5 - theta1,
                                                            d=30).iloc[0])

    # If the faceoff circle being drawn is the center circle, and only half
    # the ice is desired, return the semi-circle that is present
    if center == (0, 0) and not full_surf:
        pass

    # If the spot is in the neutral zone, there should not be a circle around
    # it
    elif abs(center[0]) == 20:
        faceoff_circle = pd.DataFrame({'x': [], 'y': []})

    else:
        # In all other cases, the entire circle should be generated, so the
        # current points set needs to be reflected over the y axis
        faceoff_circle = faceoff_circle.append(
            transform.reflect(faceoff_circle, over_y=True))

        faceoff_circle = transform.translate(faceoff_circle,
                                             translate_x=center[0],
                                             translate_y=center[1])

        # Rotate the coordinates if necessary. This is more for consistency
        # than necessity as a rotated circle is visually the same
        if rotate:
            faceoff_circle = transform.rotate(faceoff_circle, rotation_dir)

    faceoff_circle_dict = {'center': center, 'faceoff_circle': faceoff_circle}

    return faceoff_circle_dict
Esempio n. 10
0
def restricted_area_arc(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the restricted-area
    arcs as specified in the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    restricted_area_arcs: a pandas dataframe of the restricted-area arcs
    """
    # Following the same process as for the three-point line, the restricted
    # area arc's starting and ending angle can be computed
    start_y = -4 - (2 / 12)

    # The rule book describes the arc as having a radius of 4'
    radius_outer = 4 + (2 / 12)

    # From here, the calculation is relatively straightforward. To determine
    # the angle, the inverse sine is needed. It will be multiplied by pi
    # so that it can be passed to the create_circle() function
    start_angle_outer = math.asin(start_y / radius_outer) / np.pi
    end_angle_outer = -start_angle_outer

    # The same method can be used for the inner angles, however, since the
    # inner radius will be traced from bottom to top, the angle must be
    # negative to start
    radius_inner = 4
    start_angle_inner = -math.asin((start_y + (2 / 12)) / radius_inner) / np.pi
    end_angle_inner = -start_angle_inner

    # The restricted area arc is an arc of radius 4' from the center of the
    # basket, and extending in a straight line to the front face of the
    # backboard, and having thickness of 2"
    restricted_area_arc = pd.DataFrame({
        'x': [-43],
        'y': [-4 - (2 / 12)]
    }).append(
        create.circle(center=(-41.75, 0),
                      d=8 + (4 / 12),
                      start=start_angle_outer,
                      end=end_angle_outer)).append(
                          pd.DataFrame({
                              'x': [-43, -43],
                              'y': [4 + (2 / 12), 4]
                          })).append(
                              create.circle(center=(-41.75, 0),
                                            d=8,
                                            start=start_angle_inner,
                                            end=end_angle_inner)).append(
                                                pd.DataFrame({
                                                    'x': [-43, -43],
                                                    'y': [-4, -4 - (2 / 12)]
                                                }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        restricted_area_arc = restricted_area_arc.append(
            transform.reflect(restricted_area_arc, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        restricted_area_arc = transform.rotate(rotation_dir,
                                               restricted_area_arc)

    return restricted_area_arc
Esempio n. 11
0
def three_point_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the three-point line
    as specified in the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    three_point_lines: a pandas dataframe of the three-point line
    """
    # First, a bit of math is needed to determine the starting and ending
    # angles of the three-point arc, relative to 0 radians. Since in the end,
    # the angle is what matters, the units of measure do not. Inches are easier
    # to use for this calculation. The angle begins 3' from the interior edge
    # of the sideline
    start_y = (22 * 12)

    # The rule book describes the arc as having a radius of 23' 9"
    radius_outer = (22 * 12) + 1.75

    # From here, the calculation is relatively straightforward. To determine
    # the angle, the inverse sine is needed. It will be multiplied by pi
    # so that it can be passed to the create_circle() function
    start_angle_outer = math.asin(start_y / radius_outer) / np.pi
    end_angle_outer = -start_angle_outer

    # The same method can be used for the inner angles, however, since the
    # inner radius will be traced from bottom to top, the angle must be
    # negative to start
    radius_inner = (22 * 12) + 1.75 - 2
    start_angle_inner = -math.asin((start_y - 2) / radius_inner) / np.pi
    end_angle_inner = -start_angle_inner

    # According to the rulebook, the three-point line is 21' 7 7/8" in the
    # corners
    three_point_line = pd.DataFrame({
        'x': [-47],
        'y': [22]
    }).append(
        create.circle(center=(-41.75, 0),
                      d=2 * (radius_outer / 12),
                      start=start_angle_outer,
                      end=end_angle_outer)).append(
                          pd.DataFrame({
                              'x': [-47, -47],
                              'y': [-22, -22 + (2 / 12)]
                          })).append(
                              create.circle(center=(-41.75, 0),
                                            d=2 * (radius_inner / 12),
                                            start=start_angle_inner,
                                            end=end_angle_inner)).append(
                                                pd.DataFrame({
                                                    'x': [-47, -47],
                                                    'y': [22 - (2 / 12), 22]
                                                }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        three_point_line = three_point_line.append(
            transform.reflect(three_point_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        three_point_line = transform.rotate(rotation_dir, three_point_line)

    return three_point_line
Esempio n. 12
0
def free_throw_circle(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframes for the points that comprise the free-throw circles
    as specified in page 8 of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    free_throw_circles_dict: a dictionary with keys 'solid' and 'dash_x', where
        x is a number that corresponds to a dashed section of the free throw
        circle
    """
    # Per the WNBA rule book, the solid portion of the circle extends along an
    # arc of length 12.29" behind the free-throw line. The angle theta must be
    # calculated to determine where to start. It can be determined via the
    # relationship s = r*theta, where s is the arc length, r is the radius, and
    # theta is the angle (in radians)

    # First, define s to be the arc length in feet
    s = 12.29 / 12

    # The outer radius is 6'
    r = 6

    # Theta is therefore s/r, but since the create.circle() function takes an
    # angle in radians/pi, this must be divided out as well
    theta = (s / r) / np.pi

    # Since the circle must extend theta radians past +/-pi/2, theta must be
    # added/subtracted from 1/2 accordingly
    start_angle = (-1 / 2) - theta
    end_angle = (1 / 2) + theta

    # The free-throw circle is 6' in diameter from the center of the free-throw
    # line (exterior)
    free_throw_circle_solid = create.circle(
        center=(-28 - (1 / 12), 0), start=start_angle, end=end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=end_angle,
                          end=start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=start_angle,
                                            end=end_angle,
                                            d=12).iloc[0])

    # Reflect the x coordinates over the y axis
    if full_surf:
        free_throw_circle_solid = free_throw_circle_solid.append(
            transform.reflect(free_throw_circle_solid, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        free_throw_circle_solid = transform.rotate(free_throw_circle_solid,
                                                   rotation_dir)

    # The dashed sections of the free-throw circle are all of length 15.5", and
    # are spaced 15.5" from each other. Following a similar process as above,
    # the starting and ending angles can be computed

    # First, compute the arc length in feet
    s = 15.5 / 12

    # The outer radius is 6'
    r = 6

    # Finally, compute the angle traced by the dashed lines
    theta_dashes = (s / r) / np.pi

    # This theta must be added to start_angle above to get the starting angle
    # for each dash, and added twice to get the ending angle for each dash.
    # This pattern can be repeated, taking the end angle of the previous dash
    # as the start angle for the following dash
    dash_1_start_angle = start_angle - theta_dashes
    dash_1_end_angle = start_angle - (2 * theta_dashes)

    dash_2_start_angle = dash_1_end_angle - theta_dashes
    dash_2_end_angle = dash_1_end_angle - (2 * theta_dashes)

    dash_3_start_angle = dash_2_end_angle - theta_dashes
    dash_3_end_angle = dash_2_end_angle - (2 * theta_dashes)

    free_throw_circle_dash_1 = create.circle(
        center=(-28 - (1 / 12), 0),
        start=dash_1_start_angle,
        end=dash_1_end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=dash_1_end_angle,
                          end=dash_1_start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=dash_1_start_angle,
                                            end=dash_1_end_angle,
                                            d=12).iloc[0])

    free_throw_circle_dash_2 = create.circle(
        center=(-28 - (1 / 12), 0),
        start=dash_2_start_angle,
        end=dash_2_end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=dash_2_end_angle,
                          end=dash_2_start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=dash_2_start_angle,
                                            end=dash_2_end_angle,
                                            d=12).iloc[0])

    free_throw_circle_dash_3 = create.circle(
        center=(-28 - (1 / 12), 0),
        start=dash_3_start_angle,
        end=dash_3_end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=dash_3_end_angle,
                          end=dash_3_start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=dash_3_start_angle,
                                            end=dash_3_end_angle,
                                            d=12).iloc[0])

    # The remaining dashes are just the reflections of dashes 1, 2, and 3 over
    # the x axis
    free_throw_circle_dash_4 = transform.reflect(free_throw_circle_dash_3,
                                                 over_x=True,
                                                 over_y=False)

    free_throw_circle_dash_5 = transform.reflect(free_throw_circle_dash_2,
                                                 over_x=True,
                                                 over_y=False)

    free_throw_circle_dash_6 = transform.reflect(free_throw_circle_dash_1,
                                                 over_x=True,
                                                 over_y=False)

    # Reflect the x coordinates over the y axis
    if full_surf:
        free_throw_circle_dash_1 = free_throw_circle_dash_1.append(
            transform.reflect(free_throw_circle_dash_1, over_y=True))

        free_throw_circle_dash_2 = free_throw_circle_dash_2.append(
            transform.reflect(free_throw_circle_dash_2, over_y=True))

        free_throw_circle_dash_3 = free_throw_circle_dash_3.append(
            transform.reflect(free_throw_circle_dash_3, over_y=True))

        free_throw_circle_dash_4 = free_throw_circle_dash_4.append(
            transform.reflect(free_throw_circle_dash_4, over_y=True))

        free_throw_circle_dash_5 = free_throw_circle_dash_5.append(
            transform.reflect(free_throw_circle_dash_5, over_y=True))

        free_throw_circle_dash_6 = free_throw_circle_dash_6.append(
            transform.reflect(free_throw_circle_dash_6, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        free_throw_circle_dash_1 = transform.rotate(free_throw_circle_dash_1,
                                                    rotation_dir)

        free_throw_circle_dash_2 = transform.rotate(free_throw_circle_dash_2,
                                                    rotation_dir)

        free_throw_circle_dash_3 = transform.rotate(free_throw_circle_dash_3,
                                                    rotation_dir)

        free_throw_circle_dash_4 = transform.rotate(free_throw_circle_dash_4,
                                                    rotation_dir)

        free_throw_circle_dash_5 = transform.rotate(free_throw_circle_dash_5,
                                                    rotation_dir)

        free_throw_circle_dash_6 = transform.rotate(free_throw_circle_dash_6,
                                                    rotation_dir)

    free_throw_circle_dict = {
        'solid': free_throw_circle_solid,
        'dash_1': free_throw_circle_dash_1,
        'dash_2': free_throw_circle_dash_2,
        'dash_3': free_throw_circle_dash_3,
        'dash_4': free_throw_circle_dash_4,
        'dash_5': free_throw_circle_dash_5,
        'dash_6': free_throw_circle_dash_6
    }

    return free_throw_circle_dict