def collide_wall(self, p): restitution = self.restitution * p.restitution # First, check that we haven't crossed through the wall due to # extreme speed and low framerate. intersection = intersect_segments(self.p1, self.p2, p.last_pos, p.pos) if intersection: p.pos = p.last_pos p.rebound(self.normal, intersection, restitution) return # Find vectors to each endpoint of the segment. v1 = vec.vfrom(self.p1, p.pos) v2 = vec.vfrom(self.p2, p.pos) # Find a perpendicular vector from the wall to p. v_dist = vec.proj(v1, self.normal) # Test distance from the wall. radius2 = p.radius**2 if vec.mag2(v_dist) > radius2: return # Test for collision with the endpoints of the segment. # Check whether p is too far off the end of the segment, by checking # the sign of the vector projection, then a radius check for the # distance from the endpoint. if vec.dot(v1, self.tangent) < 0: if vec.mag2(v1) <= radius2: p.rebound(v1, self.p1, restitution) return if vec.dot(v2, self.tangent) > 0: if vec.mag2(v2) <= radius2: p.rebound(v2, self.p2, restitution) return # Test that p is headed toward the wall. if vec.dot(p.velocity, v_dist) >= c.epsilon: return # We are definitely not off the ends of the segment, and close enough # that we are colliding. p.rebound(self.normal, vec.sub(p.pos, v_dist), restitution)
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)), ]
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)
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)
def intersect(p1, p2): distance2 = vec.mag2(vec.vfrom(p1.pos, p2.pos)) return distance2 <= (p1.radius + p2.radius) ** 2
def closest_point_to(target, points): return min( points, key=lambda p: vec.mag2(vec.vfrom(target, p)) )
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)), ]
def closest_point_to(target, points): return min(points, key=lambda p: vec.mag2(vec.vfrom(target, p)))
def intersect(p1, p2): distance2 = vec.mag2(vec.vfrom(p1.pos, p2.pos)) return distance2 <= (p1.radius + p2.radius)**2