Exemplo n.º 1
0
def ball_robot_collided(ball: 'Ball', bot: 'Robot') -> bool:
    """
    If the ball/robot collided, either:
        (1) One of the corners of the bot is inside the ball -OR-
        (2) One of the edges of the bot is intersecting the circle -OR-
        (3) Entire ball is within the bot

    3 should never happen (unless our ball speed goes nuts) and, if it does,
    the collision logic won't work anyways so... ignore for now.
    """
    for corner in bot.rectDbl.corners:
        if distance(corner, ball.rectDbl.center) < const.BALL_RADIUS:
            return True

    # get diameters parallel/perp to the robot sides
    _rectBallInner.center = ball.rectDbl.center
    _rectBallInner.rotation = bot.rectDbl.rotation + 45
    lst_diameters = [
        Line(a=_rectBallInner.corner(FloatRect.CornerType.TOP_LEFT),
             b=_rectBallInner.corner(FloatRect.CornerType.BOTTOM_RIGHT)),
        Line(a=_rectBallInner.corner(FloatRect.CornerType.TOP_RIGHT),
             b=_rectBallInner.corner(FloatRect.CornerType.BOTTOM_LEFT))
    ]
    # if those lines intersect, ball is colliding
    for side in bot.rectDbl.sides:
        for diameter in lst_diameters:
            tpl_i = get_line_intersection(side, diameter)
            if point_within_line(tpl_i, side) and point_within_line(tpl_i, diameter):
                return True

    return False
Exemplo n.º 2
0
def robots_collided(bot1: 'Robot', bot2: 'Robot') -> bool:
    for side1 in bot1.rectDbl.sides:
        for side2 in bot2.rectDbl.sides:
            tpl_i = get_line_intersection(side1, side2)
            if point_within_line(tpl_i, side1) and point_within_line(tpl_i, side2):
                return True
    return False
Exemplo n.º 3
0
def two_way_lidar_rect(tpl1: Tuple[float, float],
                       tpl2: Tuple[float, float],
                       lst_rect: List[FloatRect]) -> Tuple[float, float]:
    """
    Returns the distance to the nearest object (FloatRect) along the line formed by the two points. This is "two way" so
    two values are returned for both "forwards" (1->2) and "backwards" (2->1).
    """
    start = Point(*tpl1)
    end = Point(*tpl2)

    lst_front = [float("inf")]
    lst_back = [float("inf")]
    for rect in lst_rect:
        for side in rect.sides:
            intersection = Point(*get_line_intersection(side, (start, end)))
            if intersection.x is None or intersection.y is None:
                pass  # doesn't intersect

            else:
                dist_endpoint = distance(intersection, end)
                dist_startpoint = distance(intersection, start)
                if dist_endpoint <= dist_startpoint:
                    lst_front.append(dist_endpoint)
                if dist_startpoint <= dist_endpoint:
                    lst_back.append(dist_startpoint)

    return min(lst_front), min(lst_back)
Exemplo n.º 4
0
def apply_force_to_ball(spr_robot: 'Robot', spr_ball: 'Ball') -> None:
    rect_bot = spr_robot.rectDbl  # type: FloatRect
    rect_bot_prev = spr_robot.rectDblPriorFrame  # type: FloatRect

    # create list of diameter lines that are parallel/perpendicular to the sides of the bot's position
    _rectBallInner.rotation = 45 + rect_bot.rotation  # fudging this for simplicity's sake
    _rectBallInner.center = spr_ball.rectDbl.center
    lst_diameters = [
        Line(
            a=_rectBallInner.corner(FloatRect.CornerType.BOTTOM_LEFT),
            b=_rectBallInner.corner(FloatRect.CornerType.TOP_RIGHT)
        ),
        Line(
            a=_rectBallInner.corner(FloatRect.CornerType.BOTTOM_RIGHT),
            b=_rectBallInner.corner(FloatRect.CornerType.TOP_LEFT)
        )
    ]

    """ CHECK FOR SURFACE COLLISION """
    # if either of these lines intersect with a side of the bot,
    # treat this as a "surface collision" and not a corner collision
    dbl_contact_buffer = .5
    for enm_side in FloatRect.SideType:
        bot_side = rect_bot.side(enm_side)
        bot_side_prev = rect_bot_prev.side(enm_side)
        for diameter in lst_diameters:
            tpl_i = get_line_intersection(bot_side, diameter)
            if point_within_line(tpl_i, bot_side) and point_within_line(tpl_i, diameter, buffer=dbl_contact_buffer):
                dist_a = distance(diameter.a, rect_bot.center)
                dist_b = distance(diameter.b, rect_bot.center)
                tpl_cp_ball = diameter.a if dist_a < dist_b else diameter.b
                tpl_cp_ball_opp = diameter.a if dist_a >= dist_b else diameter.b
                tpl_contact_buffer = Vec2D(
                    x=(tpl_cp_ball_opp.x - tpl_cp_ball.x) * dbl_contact_buffer / (const.BALL_RADIUS + const.BALL_RADIUS),
                    y=(tpl_cp_ball_opp.y - tpl_cp_ball.y) * dbl_contact_buffer / (const.BALL_RADIUS + const.BALL_RADIUS)
                )
                # Calculate minimum distance for ball to leave the robot according to current contact vector
                tpl_contact = Vec2D(x=tpl_i[0] - tpl_cp_ball.x, y=tpl_i[1] - tpl_cp_ball.y)
                spr_ball.dbl_force_x += tpl_contact.x + tpl_contact_buffer.x
                spr_ball.dbl_force_y += tpl_contact.y + tpl_contact_buffer.y
                spr_ball.lngFrameMass = max(spr_robot.lngFrameMass, spr_ball.lngFrameMass)
                return  # can only be one pending collision point with this logic

    """ CHECK FOR CORNER COLLISION """
    for enm_corner in FloatRect.CornerType:
        bot_corner = rect_bot.corner(enm_corner)
        dbl_dist_center = distance(bot_corner, spr_ball.rectDbl.center)
        if dbl_dist_center < const.BALL_RADIUS + dbl_contact_buffer:
            bot_corner_prev = rect_bot_prev.corner(enm_corner)
            # Let's just... fudge this a bit... instead of actually calc'ing (weighted average)
            tpl_contact = Vec2D(
                x=spr_ball.rectDbl.centerx - (bot_corner.x * 3 + bot_corner_prev.x) / 4,
                y=spr_ball.rectDbl.centery - (bot_corner.y * 3 + bot_corner_prev.y) / 4)

            # Fudge this a bit, since we're not calc'ing the exact contact point on the ball
            contact_dist = distance((0, 0), tpl_contact)
            tpl_contact_buffer = Vec2D(
                x=tpl_contact.x * dbl_contact_buffer / contact_dist,
                y=tpl_contact.y * dbl_contact_buffer / contact_dist
            )
            exit_dist = (const.BALL_RADIUS - dbl_dist_center) * 1.2
            spr_ball.dbl_force_x += (tpl_contact.x * exit_dist / contact_dist) + tpl_contact_buffer.x
            spr_ball.dbl_force_y += (tpl_contact.y * exit_dist / contact_dist) + tpl_contact_buffer.y
            spr_ball.lngFrameMass = max(spr_robot.lngFrameMass, spr_ball.lngFrameMass)
            return
Exemplo n.º 5
0
def bounce_ball_off_bot(spr_robot: 'Robot', spr_ball: 'Ball'):
    if spr_ball.dbl_velocity_x == spr_ball.dbl_velocity_y == 0:
        return  # no velocity to "bounce"

    dbl_contact_buffer = .5
    rect_bot = spr_robot.rectDbl  # type: FloatRect
    rect_bot_prev = spr_robot.rectDblPriorFrame  # type: FloatRect

    # create list of diameter lines that are parallel/perpendicular to the sides of the bot's position
    _rectBallInner.rotation = 45 + rect_bot.rotation  # fudging this for simplicity's sake
    _rectBallInner.center = spr_ball.rectDbl.center
    lst_diameters = [
        Line(
            a=_rectBallInner.corner(FloatRect.CornerType.BOTTOM_LEFT),
            b=_rectBallInner.corner(FloatRect.CornerType.TOP_RIGHT)
        ),
        Line(
            a=_rectBallInner.corner(FloatRect.CornerType.BOTTOM_RIGHT),
            b=_rectBallInner.corner(FloatRect.CornerType.TOP_LEFT)
        )
    ]

    """ CHECK FOR SURFACE COLLISION """
    # if either of these lines intersect with a side of the bot,
    # treat this as a "surface collision" and not a corner collision
    for enm_side in FloatRect.SideType:
        bot_side = rect_bot.side(enm_side)
        bot_side_prev = rect_bot_prev.side(enm_side)
        for diameter in lst_diameters:
            tpl_i = get_line_intersection(bot_side, diameter)
            tpl_i_prev = get_line_intersection(bot_side_prev, diameter)
            if point_within_line(tpl_i, bot_side) and point_within_line(tpl_i, diameter):
                dist_a = distance(diameter.a, tpl_i_prev)
                dist_b = distance(diameter.b, tpl_i_prev)
                tpl_cp_ball = diameter.a if dist_a < dist_b else diameter.b
                tpl_cp_ball_opp = diameter.a if dist_a >= dist_b else diameter.b

                tpl_contact = Vec2D(x=tpl_cp_ball_opp.x - tpl_cp_ball.x, y=tpl_cp_ball_opp.y - tpl_cp_ball.y)
                tpl_ball_vel = Vec2D(x=spr_ball.dbl_velocity_x, y=spr_ball.dbl_velocity_y)

                # Projected components of the ball's velocity
                dbl_contact_dist_sq = tpl_contact.x ** 2 + tpl_contact.y ** 2
                dbl_ball_proj_term = ((tpl_contact.x * tpl_ball_vel.x) + (tpl_contact.y * tpl_ball_vel.y)) \
                                     / dbl_contact_dist_sq
                tpl_ball_proj = Vec2D(x=dbl_ball_proj_term * tpl_contact.x, y=dbl_ball_proj_term * tpl_contact.y)

                if (tpl_ball_proj.x < 0 and tpl_contact.x > 0) or (tpl_ball_proj.x > 0 and tpl_contact.x < 0):
                    spr_ball.dbl_velocity_x = -tpl_ball_proj.x * const.BOUNCE_K_WALL * const.BOUNCE_K_ROBOT
                if (tpl_ball_proj.y < 0 and tpl_contact.y > 0) or (tpl_ball_proj.y > 0 and tpl_contact.y < 0):
                    spr_ball.dbl_velocity_y = -tpl_ball_proj.y * const.BOUNCE_K_WALL * const.BOUNCE_K_ROBOT

                # Create a buffer vector
                tpl_contact_buffer = Vec2D(
                    x=tpl_contact.x * dbl_contact_buffer / dbl_contact_dist_sq**.5,
                    y=tpl_contact.y * dbl_contact_buffer / dbl_contact_dist_sq**.5
                )

                tpl_exit_point = Point(*tpl_i)
                spr_ball.rectDbl.centerx += (tpl_exit_point.x - tpl_cp_ball.x) + tpl_contact_buffer.x
                spr_ball.rectDbl.centery += (tpl_exit_point.y - tpl_cp_ball.y) + tpl_contact_buffer.y
                return

    """ CHECK FOR CORNER COLLISION """
    for enm_corner in FloatRect.CornerType:
        bot_corner = rect_bot.corner(enm_corner)
        dbl_dist_center = distance(bot_corner, spr_ball.rectDbl.center)
        if dbl_dist_center < const.BALL_RADIUS:
            bot_corner_prev = rect_bot_prev.corner(enm_corner)
            # Let's just... fudge this a bit... instead of actually calc'ing (weighted average)
            tpl_cp_bot = Point(x=(bot_corner.x*3 + bot_corner_prev.x)/4, y=(bot_corner.y*3 + bot_corner_prev.y)/4)
            tpl_contact = Vec2D(x=spr_ball.rectDbl.centerx - tpl_cp_bot.x, y=spr_ball.rectDbl.centery - tpl_cp_bot.y)

            # Project the ball vel onto the contact vector
            tpl_ball_vel = Vec2D(x=spr_ball.dbl_velocity_x, y=spr_ball.dbl_velocity_y)
            dbl_contact_dist_sq = tpl_contact.x ** 2 + tpl_contact.y ** 2
            dbl_ball_proj_term = ((tpl_contact.x * tpl_ball_vel.x) + (tpl_contact.y * tpl_ball_vel.y)) \
                                 / dbl_contact_dist_sq
            tpl_ball_proj = Vec2D(x=dbl_ball_proj_term * tpl_contact.x, y=dbl_ball_proj_term * tpl_contact.y)

            # Apply bounce, if applicable
            if (tpl_ball_proj.x < 0 and tpl_contact.x > 0) or (tpl_ball_proj.x > 0 and tpl_contact.x < 0):
                spr_ball.dbl_velocity_x = -tpl_ball_proj.x * const.BOUNCE_K_WALL * const.BOUNCE_K_ROBOT
            if (tpl_ball_proj.y < 0 and tpl_contact.y > 0) or (tpl_ball_proj.y > 0 and tpl_contact.y < 0):
                spr_ball.dbl_velocity_y = -tpl_ball_proj.y * const.BOUNCE_K_WALL * const.BOUNCE_K_ROBOT

            # Resolve collision - contact point is fudged, fudge travel dist accordingly (by using bot corner prev)
            dbl_exit_dist = distance(bot_corner_prev, spr_ball.rectDbl.center)
            dbl_contact_dist = dbl_contact_dist_sq ** .5
            spr_ball.rectDbl.centerx += tpl_contact.x * dbl_exit_dist / dbl_contact_dist
            spr_ball.rectDbl.centery += tpl_contact.y * dbl_exit_dist / dbl_contact_dist
            return