Esempio n. 1
0
    def arc_to(self, endpoint, center=None, start_slant=None, end_slant=None):
        """
        Draw an arc ending at the specified point, starting tangent to the
        current position and heading.
        """
        if points_equal(self._position, endpoint):
            return
        # Handle unspecified center.
        # We need to find the center of the arc, so we can find its radius. The
        # center of this arc is uniquely defined by the intersection of two
        # lines:
        # 1. The first line is perpendicular to the pen heading, passing
        #    through the pen position.
        # 2. The second line is the perpendicular bisector of the pen position
        #    and the target arc end point.
        v_pen = self._vector()
        v_perp = vec.perp(self._vector())
        v_chord = vec.vfrom(self._position, endpoint)
        if center is None:
            midpoint = vec.div(vec.add(self._position, endpoint), 2)
            v_bisector = vec.perp(v_chord)
            center = intersect_lines(
                self._position,
                vec.add(self._position, v_perp),
                midpoint,
                vec.add(midpoint, v_bisector),
            )

        # Determine true start heading. This may not be the same as the
        # original pen heading in some circumstances.
        assert not points_equal(center, self._position)
        v_radius_start = vec.vfrom(center, self._position)
        v_radius_perp = vec.perp(v_radius_start)
        if vec.dot(v_radius_perp, v_pen) < 0:
            v_radius_perp = vec.neg(v_radius_perp)
        start_heading = math.degrees(vec.heading(v_radius_perp))
        self.turn_to(start_heading)
        # Refresh v_pen and v_perp based on the new start heading.
        v_pen = self._vector()
        v_perp = vec.perp(self._vector())

        # Calculate the arc angle.
        # The arc angle is double the angle between the pen vector and the
        # chord vector. Arcing to the left is a positive angle, and arcing to
        # the right is a negative angle.
        arc_angle = 2 * math.degrees(vec.angle(v_pen, v_chord))
        radius = vec.mag(v_radius_start)
        # Check which side of v_pen the goes toward.
        if vec.dot(v_chord, v_perp) < 0:
            arc_angle = -arc_angle
            radius = -radius

        self._arc(
            center,
            radius,
            endpoint,
            arc_angle,
            start_slant,
            end_slant,
        )
Esempio n. 2
0
def intersect_lines(a, b, c, d, segment=False):
    """
    Find the intersection of lines a-b and c-d.

    If the "segment" argument is true, treat the lines as segments, and check
    whether the intersection point is off the end of either segment.
    """
    # Reference:
    # http://geomalgorithms.com/a05-_intersect-1.html
    u = vec.vfrom(a, b)
    v = vec.vfrom(c, d)
    w = vec.vfrom(c, a)

    u_perp_dot_v = vec.dot(vec.perp(u), v)
    if float_equal(u_perp_dot_v, 0):
        return None  # We have collinear segments, no single intersection.

    v_perp_dot_w = vec.dot(vec.perp(v), w)
    s = v_perp_dot_w / u_perp_dot_v
    if segment and (s < 0 or s > 1):
        return None

    u_perp_dot_w = vec.dot(vec.perp(u), w)
    t = u_perp_dot_w / u_perp_dot_v
    if segment and (t < 0 or t > 1):
        return None

    return vec.add(a, vec.mul(u, s))
Esempio n. 3
0
    def arc_to(self, endpoint, center=None, start_slant=None, end_slant=None):
        """
        Draw an arc ending at the specified point, starting tangent to the
        current position and heading.
        """
        if points_equal(self._position, endpoint):
            return
        # Handle unspecified center.
        # We need to find the center of the arc, so we can find its radius. The
        # center of this arc is uniquely defined by the intersection of two
        # lines:
        # 1. The first line is perpendicular to the pen heading, passing
        #    through the pen position.
        # 2. The second line is the perpendicular bisector of the pen position
        #    and the target arc end point.
        v_pen = self._vector()
        v_perp = vec.perp(self._vector())
        v_chord = vec.vfrom(self._position, endpoint)
        if center is None:
            midpoint = vec.div(vec.add(self._position, endpoint), 2)
            v_bisector = vec.perp(v_chord)
            center = intersect_lines(
                self._position,
                vec.add(self._position, v_perp),
                midpoint,
                vec.add(midpoint, v_bisector),
            )

        # Determine true start heading. This may not be the same as the
        # original pen heading in some circumstances.
        assert not points_equal(center, self._position)
        v_radius_start = vec.vfrom(center, self._position)
        v_radius_perp = vec.perp(v_radius_start)
        if vec.dot(v_radius_perp, v_pen) < 0:
            v_radius_perp = vec.neg(v_radius_perp)
        start_heading = math.degrees(vec.heading(v_radius_perp))
        self.turn_to(start_heading)
        # Refresh v_pen and v_perp based on the new start heading.
        v_pen = self._vector()
        v_perp = vec.perp(self._vector())

        # Calculate the arc angle.
        # The arc angle is double the angle between the pen vector and the
        # chord vector. Arcing to the left is a positive angle, and arcing to
        # the right is a negative angle.
        arc_angle = 2 * math.degrees(vec.angle(v_pen, v_chord))
        radius = vec.mag(v_radius_start)
        # Check which side of v_pen the goes toward.
        if vec.dot(v_chord, v_perp) < 0:
            arc_angle = -arc_angle
            radius = -radius

        self._arc(
            center,
            radius,
            endpoint,
            arc_angle,
            start_slant,
            end_slant,
        )
Esempio n. 4
0
def intersect_lines(a, b, c, d, segment=False):
    """
    Find the intersection of lines a-b and c-d.

    If the "segment" argument is true, treat the lines as segments, and check
    whether the intersection point is off the end of either segment.
    """
    # Reference:
    # http://geomalgorithms.com/a05-_intersect-1.html
    u = vec.vfrom(a, b)
    v = vec.vfrom(c, d)
    w = vec.vfrom(c, a)

    u_perp_dot_v = vec.dot(vec.perp(u), v)
    if float_equal(u_perp_dot_v, 0):
        return None  # We have collinear segments, no single intersection.

    v_perp_dot_w = vec.dot(vec.perp(v), w)
    s = v_perp_dot_w / u_perp_dot_v
    if segment and (s < 0 or s > 1):
        return None

    u_perp_dot_w = vec.dot(vec.perp(u), w)
    t = u_perp_dot_w / u_perp_dot_v
    if segment and (t < 0 or t > 1):
        return None

    return vec.add(a, vec.mul(u, s))
Esempio n. 5
0
    def set_slants(self, start_slant, end_slant):
        if start_slant is not None:
            start_slant = Heading(start_slant)
        if end_slant is not None:
            end_slant = Heading(end_slant)

        self.start_slant = start_slant
        self.end_slant = end_slant

        # Intersect the slant lines with the left and right offset lines
        # to find the corners.
        line_left = self.offset_line_left()
        line_right = self.offset_line_right()

        # Start corners.
        if start_slant is None:
            v_slant = vec.perp(self._vector())
        else:
            v_slant = vec.from_heading(start_slant.rad)
        a = self.a
        b = vec.add(self.a, v_slant)

        left = intersect_lines(a, b, line_left[0], line_left[1])
        right = intersect_lines(a, b, line_right[0], line_right[1])

        if left is None or right is None:
            self.start_joint_illegal = True
        else:
            self.a_left = Point(*left)
            self.a_right = Point(*right)

        # End corners.
        if end_slant is None:
            v_slant = vec.perp(self._vector())
        else:
            v_slant = vec.from_heading(end_slant.rad)
        a = self.b
        b = vec.add(self.b, v_slant)

        left = intersect_lines(a, b, line_left[0], line_left[1])

        right = intersect_lines(a, b, line_right[0], line_right[1])

        if left is None or right is None:
            self.end_joint_illegal = True
        else:
            self.b_left = Point(*left)
            self.b_right = Point(*right)

        # Done, make sure we didn't cross
        self.check_degenerate_segment()
Esempio n. 6
0
    def set_slants(self, start_slant, end_slant):
        if start_slant is not None:
            start_slant = Heading(start_slant)
        if end_slant is not None:
            end_slant = Heading(end_slant)

        self.start_slant = start_slant
        self.end_slant = end_slant

        # Intersect the slant lines with the left and right offset lines
        # to find the corners.
        line_left = self.offset_line_left()
        line_right = self.offset_line_right()

        # Start corners.
        if start_slant is None:
            v_slant = vec.perp(self._vector())
        else:
            v_slant = vec.from_heading(start_slant.rad)
        a = self.a
        b = vec.add(self.a, v_slant)

        left = intersect_lines(a, b, line_left[0], line_left[1])
        right = intersect_lines(a, b, line_right[0], line_right[1])

        if left is None or right is None:
            self.start_joint_illegal = True
        else:
            self.a_left = Point(*left)
            self.a_right = Point(*right)

        # End corners.
        if end_slant is None:
            v_slant = vec.perp(self._vector())
        else:
            v_slant = vec.from_heading(end_slant.rad)
        a = self.b
        b = vec.add(self.b, v_slant)

        left = intersect_lines(a, b, line_left[0], line_left[1])

        right = intersect_lines(a, b, line_right[0], line_right[1])

        if left is None or right is None:
            self.end_joint_illegal = True
        else:
            self.b_left = Point(*left)
            self.b_right = Point(*right)

        # Done, make sure we didn't cross
        self.check_degenerate_segment()
Esempio n. 7
0
 def interpret_controls(self):
     if not hasattr(self.input, 'turn_direction'):
         # Interpret controls using x and y axis to pick a target direction,
         # then translate into turn direction and thrust.
         # If the player is pushing towards a direction and not braking,
         # then it is thrusting.
         self.do_brake = self.input.brake
         self.turn_direction = 0
         self.do_thrust = False
         self.intended_direction = (self.input.x_axis, -self.input.y_axis)
         if self.intended_direction != (0, 0):
             if not self.do_brake:
                 self.do_thrust = True
             # Determine which direction we should turn to come closer to the
             # correct one.
             side = vec.dot(self.intended_direction,
                            vec.perp(self.direction))
             if (vec.angle(self.intended_direction, self.direction) <
                     c.player_intended_turn_threshold):
                 self.turn_direction = 0
             elif side < 0:
                 self.turn_direction = +1
             elif side > 0:
                 self.turn_direction = -1
     else:
         # Interpret controls using thrust, brake, and turn direction.
         self.turn_direction = self.input.turn_direction
         self.do_brake = self.input.brake
         self.do_thrust = self.input.thrust and not self.do_brake
Esempio n. 8
0
    def arc_left(
        self, arc_angle, radius=None, center=None, start_slant=None, end_slant=None,
    ):
        if (
            (radius is None and center is None)
            or (radius is not None and center is not None)
        ):
            raise TypeError('You must specify exactly one of center or radius.')

        arc_angle = Angle(arc_angle)
        # Create a radius vector, which is a vector from the arc center to the
        # current position. Subtract to find the center, then rotate the radius
        # vector to find the arc end point.
        if center is None:
            if arc_angle < 0:
                radius = -abs(radius)
            v_radius = vec.neg(vec.perp(self._vector(radius)))
            center = vec.sub(self._position, v_radius)
        elif radius is None:
            v_radius = vec.vfrom(center, self._position)
            radius = vec.mag(v_radius)
            if arc_angle < 0:
                radius = -radius

        endpoint = vec.add(center, vec.rotate(v_radius, arc_angle.rad))

        self._arc(
            center,
            radius,
            endpoint,
            arc_angle,
            start_slant=start_slant,
            end_slant=end_slant,
        )
Esempio n. 9
0
    def arc_left(
        self, arc_angle, radius=None, center=None, start_slant=None, end_slant=None,
    ):
        if (
            (radius is None and center is None) or
            (radius is not None and center is not None)
        ):
            raise TypeError('You must specify exactly one of center or radius.')

        arc_angle = Angle(arc_angle)
        # Create a radius vector, which is a vector from the arc center to the
        # current position. Subtract to find the center, then rotate the radius
        # vector to find the arc end point.
        if center is None:
            if arc_angle < 0:
                radius = -abs(radius)
            v_radius = vec.neg(vec.perp(self._vector(radius)))
            center = vec.sub(self._position, v_radius)
        elif radius is None:
            v_radius = vec.vfrom(center, self._position)
            radius = vec.mag(v_radius)
            if arc_angle < 0:
                radius = -radius

        endpoint = vec.add(center, vec.rotate(v_radius, arc_angle.rad))

        self._arc(
            center,
            radius,
            endpoint,
            arc_angle,
            start_slant=start_slant,
            end_slant=end_slant,
        )
Esempio n. 10
0
 def interpret_controls(self):
     if not hasattr(self.input, 'turn_direction'):
         # Interpret controls using x and y axis to pick a target direction,
         # then translate into turn direction and thrust.
         # If the player is pushing towards a direction and not braking,
         # then it is thrusting.
         self.do_brake = self.input.brake
         self.turn_direction = 0
         self.do_thrust = False
         self.intended_direction = (self.input.x_axis, -self.input.y_axis)
         if self.intended_direction != (0, 0):
             if not self.do_brake:
                 self.do_thrust = True
             # Determine which direction we should turn to come closer to the
             # correct one.
             side = vec.dot(self.intended_direction, vec.perp(self.direction))
             if (
                 vec.angle(self.intended_direction, self.direction) <
                 c.player_intended_turn_threshold
             ):
                 self.turn_direction = 0
             elif side < 0:
                 self.turn_direction = +1
             elif side > 0:
                 self.turn_direction = -1
     else:
         # Interpret controls using thrust, brake, and turn direction.
         self.turn_direction = self.input.turn_direction
         self.do_brake = self.input.brake
         self.do_thrust = self.input.thrust and not self.do_brake
Esempio n. 11
0
def collide_particles(p1, p2):
    restitution = p1.restitution * p2.restitution

    # Don't collide immovable particles.
    if p1.immovable and p2.immovable:
        return

    # Test if p1 and p2 are actually intersecting.
    if not intersect(p1, p2):
        return

    # If one particle is immovable, make it the first one.
    if not p1.immovable and p2.immovable:
        p1, p2 = p2, p1

    # Vector spanning between the centers, normal to contact surface.
    v_span = vec.vfrom(p1.pos, p2.pos)

    # Split into normal and tangential components and calculate
    # initial velocities.
    normal = vec.norm(v_span)
    tangent = vec.perp(normal)
    v1_tangent = vec.proj(p1.velocity, tangent)
    v2_tangent = vec.proj(p2.velocity, tangent)
    p1_initial = vec.dot(p1.velocity, normal)
    p2_initial = vec.dot(p2.velocity, normal)

    # Don't collide if particles were actually moving away from each other, so
    # they don't get stuck inside one another.
    if p1_initial - p2_initial < 0:
        return

    # Handle immovable particles specially.
    if p1.immovable:
        p2_final = -p2_initial * restitution
        p2.velocity = vec.add(
            v2_tangent,
            vec.mul(normal, p2_final),
        )
        return

    # Elastic collision equations along normal component.
    m1, m2 = p1.mass, p2.mass
    m1plusm2 = (m1 + m2) / restitution
    p1_final = (p1_initial * (m1 - m2) / m1plusm2 + p2_initial *
                (2 * m2) / m1plusm2)
    p2_final = (p2_initial * (m2 - m1) / m1plusm2 + p1_initial *
                (2 * m1) / m1plusm2)

    # Tangential component is unchanged, recombine.
    p1.velocity = vec.add(
        v1_tangent,
        vec.mul(normal, p1_final),
    )
    p2.velocity = vec.add(
        v2_tangent,
        vec.mul(normal, p2_final),
    )
Esempio n. 12
0
def intersect_circles(center1, radius1, center2, radius2):
    radius1 = abs(radius1)
    radius2 = abs(radius2)

    if radius2 > radius1:
        return intersect_circles(center2, radius2, center1, radius1)

    transverse = vec.vfrom(center1, center2)
    dist = vec.mag(transverse)

    # Check for identical or concentric circles. These will have either
    # no points in common or all points in common, and in either case, we
    # return an empty list.
    if points_equal(center1, center2):
        return []

    # Check for exterior or interior tangent.
    radius_sum = radius1 + radius2
    radius_difference = abs(radius1 - radius2)
    if (float_equal(dist, radius_sum) or float_equal(dist, radius_difference)):
        return [
            vec.add(center1, vec.norm(transverse, radius1)),
        ]

    # Check for non intersecting circles.
    if dist > radius_sum or dist < radius_difference:
        return []

    # If we've reached this point, we know that the two circles intersect
    # in two distinct points.
    # Reference:
    # http://mathworld.wolfram.com/Circle-CircleIntersection.html

    # Pretend that the circles are arranged along the x-axis.
    # Find the x-value of the intersection points, which is the same for both
    # points. Then find the chord length "a" between the two intersection
    # points, and use vector math to find the points.
    dist2 = vec.mag2(transverse)
    x = (dist2 - radius2**2 + radius1**2) / (2 * dist)
    a = ((1 / dist) * sqrt(
        (-dist + radius1 - radius2) * (-dist - radius1 + radius2) *
        (-dist + radius1 + radius2) * (dist + radius1 + radius2)))
    chord_middle = vec.add(
        center1,
        vec.norm(transverse, x),
    )
    perp = vec.perp(transverse)
    return [
        vec.add(chord_middle, vec.norm(perp, a / 2)),
        vec.add(chord_middle, vec.norm(perp, -a / 2)),
    ]
Esempio n. 13
0
 def rebound(self, normal, point=None, restitution=1):
     # Split into normal and tangential components.
     tangent = vec.perp(normal)
     v_tangent = vec.proj(self.velocity, tangent)
     v_normal = vec.proj(self.velocity, normal)
     # Invert normal component and recombine, with restitution.
     v_normal = vec.neg(v_normal)
     self.velocity = vec.add(v_tangent, vec.mul(v_normal, restitution))
     # If the particle is partially inside the wall, move it out.
     if point is not None:
         v = vec.vfrom(point, self.pos)
         if vec.mag2(v) < self.radius ** 2:
             v = vec.norm(v, self.radius)
             self.pos = vec.add(point, v)
Esempio n. 14
0
def collide_particles(p1, p2):
    restitution = p1.restitution * p2.restitution

    # Don't collide immovable particles.
    if p1.immovable and p2.immovable:
        return

    # Test if p1 and p2 are actually intersecting.
    if not intersect(p1, p2):
        return

    # If one particle is immovable, make it the first one.
    if not p1.immovable and p2.immovable:
        p1, p2 = p2, p1

    # Vector spanning between the centers, normal to contact surface.
    v_span = vec.vfrom(p1.pos, p2.pos)

    # Split into normal and tangential components and calculate
    # initial velocities.
    normal = vec.norm(v_span)
    tangent = vec.perp(normal)
    v1_tangent = vec.proj(p1.velocity, tangent)
    v2_tangent = vec.proj(p2.velocity, tangent)
    p1_initial = vec.dot(p1.velocity, normal)
    p2_initial = vec.dot(p2.velocity, normal)

    # Don't collide if particles were actually moving away from each other, so
    # they don't get stuck inside one another.
    if p1_initial - p2_initial < 0:
        return

    # Handle immovable particles specially.
    if p1.immovable:
        p2_final = -p2_initial * restitution
        p2.velocity = vec.add(v2_tangent, vec.mul(normal, p2_final))
        return

    # Elastic collision equations along normal component.
    m1, m2 = p1.mass, p2.mass
    m1plusm2 = (m1 + m2) / restitution
    p1_final = p1_initial * (m1 - m2) / m1plusm2 + p2_initial * (2 * m2) / m1plusm2
    p2_final = p2_initial * (m2 - m1) / m1plusm2 + p1_initial * (2 * m1) / m1plusm2

    # Tangential component is unchanged, recombine.
    p1.velocity = vec.add(v1_tangent, vec.mul(normal, p1_final))
    p2.velocity = vec.add(v2_tangent, vec.mul(normal, p2_final))
Esempio n. 15
0
 def rebound(self, normal, point=None, restitution=1):
     # Split into normal and tangential components.
     tangent = vec.perp(normal)
     v_tangent = vec.proj(self.velocity, tangent)
     v_normal = vec.proj(self.velocity, normal)
     # Invert normal component and recombine, with restitution.
     v_normal = vec.neg(v_normal)
     self.velocity = vec.add(
         v_tangent,
         vec.mul(v_normal, restitution),
     )
     # If the particle is partially inside the wall, move it out.
     if point is not None:
         v = vec.vfrom(point, self.pos)
         if vec.mag2(v) < self.radius**2:
             v = vec.norm(v, self.radius)
             self.pos = vec.add(point, v)
Esempio n. 16
0
def intersect_circle_line(center, radius, line_start, line_end):
    """
    Find the intersection of a circle with a line.
    """
    radius = abs(radius)

    # First check whether the line is too far away, or if we have a
    # single point of contact.
    # Reference:
    # http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
    r = vec.vfrom(center, line_start)
    v = vec.perp(vec.vfrom(line_start, line_end))
    d = vec.proj(r, v)
    dist = vec.mag(d)
    if float_equal(dist, radius):
        # Single intersection point, because the circle and line are tangent.
        point = vec.add(center, d)
        return [point]
    elif dist > radius:
        return []

    # Set up parametric equations for the line and the circle, and solve them.
    # Reference:
    # http://www.cs.cf.ac.uk/Dave/CM0268/PDF/circle_line_intersect_proof.pdf
    xc, yc = center
    x0, y0 = line_start
    x1, y1 = line_end
    line_x, line_y = (x1 - x0), (y1 - y0)  # f, g
    dx, dy = (x0 - xc), (y0 - yc)

    a = line_x**2 + line_y**2
    b = 2 * (line_x * dx + line_y * dy)
    c = dx**2 + dy**2 - radius**2
    t0, t1 = quadratic_formula(a, b, c)

    return [
        (
            x0 + line_x * t0,
            y0 + line_y * t0,
        ),
        (
            x0 + line_x * t1,
            y0 + line_y * t1,
        ),
    ]
Esempio n. 17
0
def intersect_circle_line(center, radius, line_start, line_end):
    """
    Find the intersection of a circle with a line.
    """
    radius = abs(radius)

    # First check whether the line is too far away, or if we have a
    # single point of contact.
    # Reference:
    # http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
    r = vec.vfrom(center, line_start)
    v = vec.perp(vec.vfrom(line_start, line_end))
    d = vec.proj(r, v)
    dist = vec.mag(d)
    if float_equal(dist, radius):
        # Single intersection point, because the circle and line are tangent.
        point = vec.add(center, d)
        return [point]
    elif dist > radius:
        return []

    # Set up parametric equations for the line and the circle, and solve them.
    # Reference:
    # http://www.cs.cf.ac.uk/Dave/CM0268/PDF/circle_line_intersect_proof.pdf
    xc, yc = center
    x0, y0 = line_start
    x1, y1 = line_end
    line_x, line_y = (x1 - x0), (y1 - y0)  # f, g
    dx, dy = (x0 - xc), (y0 - yc)

    a = line_x**2 + line_y**2
    b = 2 * (line_x * dx + line_y * dy)
    c = dx**2 + dy**2 - radius**2
    t0, t1 = quadratic_formula(a, b, c)

    return [
        (
            x0 + line_x * t0,
            y0 + line_y * t0,
        ),
        (
            x0 + line_x * t1,
            y0 + line_y * t1,
        ),
    ]
Esempio n. 18
0
    def calc_rudder_force(self):
        # We continuously bring the direction of the player's movement to be
        # closer in line with the direction it is facing.
        target_velocity = vec.norm(self.direction, self.speed)
        force = vec.vfrom(self.velocity, target_velocity)
        if force == (0, 0):
            return (0, 0)

        # The strength of the rudder is highest when acting perpendicular to
        # the direction of movement.
        v_perp = vec.norm(vec.perp(self.velocity))
        angle_multiplier = abs(vec.dot(v_perp, self.direction))
        strength = self.speed * c.player_rudder_strength
        strength = min(strength, c.player_max_rudder_strength)
        strength *= angle_multiplier
        if strength == 0:
            return (0, 0)

        force = vec.norm(force, strength)
        return force
Esempio n. 19
0
    def calc_rudder_force(self):
        # We continuously bring the direction of the player's movement to be
        # closer in line with the direction it is facing.
        target_velocity = vec.norm(self.direction, self.speed)
        force = vec.vfrom(self.velocity, target_velocity)
        if force == (0, 0):
            return (0, 0)

        # The strength of the rudder is highest when acting perpendicular to
        # the direction of movement.
        v_perp = vec.norm(vec.perp(self.velocity))
        angle_multiplier = abs(vec.dot(v_perp, self.direction))
        strength = self.speed * c.player_rudder_strength
        strength = min(strength, c.player_max_rudder_strength)
        strength *= angle_multiplier
        if strength == 0:
            return (0, 0)

        force = vec.norm(force, strength)
        return force
def partition(points, l1, l2, s=None):
    """
    Partition a set of points by a line.

    The line is defined by l1, l2. The desired side of the line is given by the
    point s.

    If s is not given, return points to the right of the line.

    If eq is True, also include points on the line.

    >>> sorted(partition([(-1,0), (0,0), (1,0)], (0,1), (0,-1), (2,0)))
    [(0, 0), (1, 0)]
    >>> sorted(partition([(-1,0), (0,0), (1,0)], (0,1), (0,-1), (-2,0)))
    [(-1, 0), (0, 0)]
    >>> points = [(-2,2), (-1,0), (0,0), (1,0)]
    >>> sorted(partition(points, (-1,0), (0,1), (3,0)))
    [(-1, 0), (0, 0), (1, 0)]
    >>> sorted(partition(points, (-1,0), (0,1), (-3,0)))
    [(-2, 2), (-1, 0)]

    You can omit the argument "s" if you don't care.
    >>> sorted(partition([(-1,0), (0,0), (1,0)], (0,1), (0,-1)))
    [(-1, 0), (0, 0)]
    """
    if s is None:
        s = vec.add(l1, vec.perp(vec.vfrom(l1, l2)))

    if l1 == l2:
        raise ValueError('l1 equals l2')
    sign = sign_of(cmp_line(l1, l2, s))
    if sign == 0:
        raise ValueError('s is on the line l1 l2')

    for p in points:
        c = cmp_line(l1, l2, p)
        if c == sign:
            yield p
        elif c == 0:
            yield p
Esempio n. 21
0
def partition(points, l1, l2, s=None):
    """
    Partition a set of points by a line.

    The line is defined by l1, l2. The desired side of the line is given by the
    point s.

    >>> partition([(-1,0), (0,0), (1,0)], (0,1), (0,-1), (2,0))[0]
    {(1, 0)}
    >>> partition([(-1,0), (0,0), (1,0)], (0,1), (0,-1), (-2,0))[0]
    {(-1, 0)}
    >>> points = [(-2,2), (-1,0), (0,0), (1,0)]
    >>> sorted(partition(points, (-1,0), (0,1), (3,0))[0])
    [(0, 0), (1, 0)]
    >>> sorted(partition(points, (-1,0), (0,1), (-3,0))[0])
    [(-2, 2)]

    You can omit the argument "s" if you don't care.
    >>> partition([(-1,0), (0,0), (1,0)], (0,1), (0,-1))
    ({(-1, 0)}, {(1, 0)})
    """
    if s is None:
        s = vec.add(l1, vec.perp(vec.vfrom(l1, l2)))

    if l1 == l2:
        raise ValueError('l1 equals l2')
    sign = cmp_line(l1, l2, s)
    if sign == 0:
        raise ValueError('s is on the line l1 l2')

    forward = set()
    reverse = set()
    for p in points:
        c = cmp_line(l1, l2, p)
        if c == sign:
            forward.add(p)
        elif c == -sign:
            reverse.add(p)
    return forward, reverse
Esempio n. 22
0
def intersect_circles(center1, radius1, center2, radius2):
    radius1 = abs(radius1)
    radius2 = abs(radius2)

    if radius2 > radius1:
        return intersect_circles(center2, radius2, center1, radius1)

    transverse = vec.vfrom(center1, center2)
    dist = vec.mag(transverse)

    # Check for identical or concentric circles. These will have either
    # no points in common or all points in common, and in either case, we
    # return an empty list.
    if points_equal(center1, center2):
        return []

    # Check for exterior or interior tangent.
    radius_sum = radius1 + radius2
    radius_difference = abs(radius1 - radius2)
    if (
        float_equal(dist, radius_sum) or
        float_equal(dist, radius_difference)
    ):
        return [
            vec.add(
                center1,
                vec.norm(transverse, radius1)
            ),
        ]

    # Check for non intersecting circles.
    if dist > radius_sum or dist < radius_difference:
        return []

    # If we've reached this point, we know that the two circles intersect
    # in two distinct points.
    # Reference:
    # http://mathworld.wolfram.com/Circle-CircleIntersection.html

    # Pretend that the circles are arranged along the x-axis.
    # Find the x-value of the intersection points, which is the same for both
    # points. Then find the chord length "a" between the two intersection
    # points, and use vector math to find the points.
    dist2 = vec.mag2(transverse)
    x = (dist2 - radius2**2 + radius1**2) / (2 * dist)
    a = (
        (1 / dist) *
        sqrt(
            (-dist + radius1 - radius2) *
            (-dist - radius1 + radius2) *
            (-dist + radius1 + radius2) *
            (dist + radius1 + radius2)
        )
    )
    chord_middle = vec.add(
        center1,
        vec.norm(transverse, x),
    )
    perp = vec.perp(transverse)
    return [
        vec.add(chord_middle, vec.norm(perp, a / 2)),
        vec.add(chord_middle, vec.norm(perp, -a / 2)),
    ]
Esempio n. 23
0
 def _width_vector(self):
     v = self._vector()
     v = vec.perp(v)
     v = vec.norm(v, self.width / 2)
     return v
Esempio n. 24
0
 def _width_vector(self):
     v = self._vector()
     v = vec.perp(v)
     v = vec.norm(v, self.width / 2)
     return v
Esempio n. 25
0
 def __init__(self, p1, p2, restitution=c.restitution_wall):
     self.p1 = p1
     self.p2 = p2
     self.restitution = restitution
     self.tangent = vec.vfrom(self.p1, self.p2)
     self.normal = vec.perp(self.tangent)
Esempio n. 26
0
 def __init__(self, p1, p2, restitution=c.restitution_wall):
     self.p1 = p1
     self.p2 = p2
     self.restitution = restitution
     self.tangent = vec.vfrom(self.p1, self.p2)
     self.normal = vec.perp(self.tangent)