import math
class Point:
def __init__(self, x,y):
self.x = x
self.y = y
def magnitude(self):
return math.sqrt(self.x*self.x + self.y*self.y)
def _applyVectorFunc(self, other, f):
return Point(f(self.x, other.x), f(self.y, other.y))
def _applyScalarFunc(self, a, f):
return self._applyVectorFunc(Point(a,a), f)
def __add__(self, other):
return self._applyVectorFunc(other, lambda a,b: a+b)
def __sub__(self, other):
return self._applyVectorFunc(other, lambda a,b: a-b)
def __mul__(self, a):
return self._applyScalarFunc(a, lambda b,c: b*c)
def __div__(self, a):
return self._applyScalarFunc(a, lambda b,c: b/c)
def __repr__(self):
return "Point({}, {})".format(self.x, self.y)
def dot_product(a,b):
return a.x * b.x + a.y * b.y
def angle_between(b,c):
return math.acos(dot_product(b,c) / (b.magnitude() * c.magnitude()))
def to_degrees(a):
return a * 360 / (2*math.pi)
def find_collision_point(target_pos, target_vel, interceptor_pos, interceptor_speed):
k = target_vel.magnitude() / interceptor_speed
c = (interceptor_pos - target_pos).magnitude()
b_hat = target_vel
c_hat = interceptor_pos - target_pos
CAB = angle_between(b_hat, c_hat)
ABC = math.asin(math.sin(CAB) * k)
ACB = (math.pi) - (CAB + ABC)
j = c / math.sin(ACB)
a = j * math.sin(CAB)
b = j * math.sin(ABC)
t = b / target_vel.magnitude()
collision_pos = target_pos + (target_vel * t)
print "Collision pos: {}".format(collision_pos)
print "Time: {}".format(t)
print "Angle A: {}".format(to_degrees(CAB))
print "Angle B: {}".format(to_degrees(ABC))
print "Angle C: {}".format(to_degrees(ACB))
print "a: {}".format(a)
print "b: {}".format(b)
print "c: {}".format(c)
target_pos = Point(120,40)
target_vel = Point(5,2)
interceptor_pos = Point(80,80)
interceptor_speed = 10
find_collision_point(target_pos, target_vel, interceptor_pos, interceptor_speed)
aW1wb3J0IG1hdGgKCmNsYXNzIFBvaW50OgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHgseSk6CiAgICAgICAgc2VsZi54ID0geAogICAgICAgIHNlbGYueSA9IHkKCiAgICBkZWYgbWFnbml0dWRlKHNlbGYpOgogICAgICAgIHJldHVybiBtYXRoLnNxcnQoc2VsZi54KnNlbGYueCArIHNlbGYueSpzZWxmLnkpCgogICAgZGVmIF9hcHBseVZlY3RvckZ1bmMoc2VsZiwgb3RoZXIsIGYpOgogICAgICAgIHJldHVybiBQb2ludChmKHNlbGYueCwgb3RoZXIueCksIGYoc2VsZi55LCBvdGhlci55KSkKICAgIGRlZiBfYXBwbHlTY2FsYXJGdW5jKHNlbGYsIGEsIGYpOgogICAgICAgIHJldHVybiBzZWxmLl9hcHBseVZlY3RvckZ1bmMoUG9pbnQoYSxhKSwgZikKCiAgICBkZWYgX19hZGRfXyhzZWxmLCBvdGhlcik6CiAgICAgICAgcmV0dXJuIHNlbGYuX2FwcGx5VmVjdG9yRnVuYyhvdGhlciwgbGFtYmRhIGEsYjogYStiKQogICAgZGVmIF9fc3ViX18oc2VsZiwgb3RoZXIpOgogICAgICAgIHJldHVybiBzZWxmLl9hcHBseVZlY3RvckZ1bmMob3RoZXIsIGxhbWJkYSBhLGI6IGEtYikKICAgIGRlZiBfX211bF9fKHNlbGYsIGEpOgogICAgICAgIHJldHVybiBzZWxmLl9hcHBseVNjYWxhckZ1bmMoYSwgbGFtYmRhIGIsYzogYipjKQogICAgZGVmIF9fZGl2X18oc2VsZiwgYSk6CiAgICAgICAgcmV0dXJuIHNlbGYuX2FwcGx5U2NhbGFyRnVuYyhhLCBsYW1iZGEgYixjOiBiL2MpCgogICAgZGVmIF9fcmVwcl9fKHNlbGYpOgogICAgICAgIHJldHVybiAiUG9pbnQoe30sIHt9KSIuZm9ybWF0KHNlbGYueCwgc2VsZi55KQoKZGVmIGRvdF9wcm9kdWN0KGEsYik6CiAgICByZXR1cm4gYS54ICogYi54ICsgYS55ICogYi55CgpkZWYgYW5nbGVfYmV0d2VlbihiLGMpOgogICAgcmV0dXJuIG1hdGguYWNvcyhkb3RfcHJvZHVjdChiLGMpIC8gKGIubWFnbml0dWRlKCkgKiBjLm1hZ25pdHVkZSgpKSkKCmRlZiB0b19kZWdyZWVzKGEpOgogICAgcmV0dXJuIGEgKiAzNjAgLyAoMiptYXRoLnBpKQoKZGVmIGZpbmRfY29sbGlzaW9uX3BvaW50KHRhcmdldF9wb3MsIHRhcmdldF92ZWwsIGludGVyY2VwdG9yX3BvcywgaW50ZXJjZXB0b3Jfc3BlZWQpOgogICAgayA9IHRhcmdldF92ZWwubWFnbml0dWRlKCkgLyBpbnRlcmNlcHRvcl9zcGVlZAogICAgYyA9IChpbnRlcmNlcHRvcl9wb3MgLSB0YXJnZXRfcG9zKS5tYWduaXR1ZGUoKQoKICAgIGJfaGF0ID0gdGFyZ2V0X3ZlbAogICAgY19oYXQgPSBpbnRlcmNlcHRvcl9wb3MgLSB0YXJnZXRfcG9zCiAgICAKICAgIENBQiA9IGFuZ2xlX2JldHdlZW4oYl9oYXQsIGNfaGF0KQogICAgQUJDID0gbWF0aC5hc2luKG1hdGguc2luKENBQikgKiBrKQogICAgQUNCID0gKG1hdGgucGkpIC0gKENBQiArIEFCQykKICAgIAogICAgaiA9IGMgLyBtYXRoLnNpbihBQ0IpCiAgICBhID0gaiAqIG1hdGguc2luKENBQikKICAgIGIgPSBqICogbWF0aC5zaW4oQUJDKQoKCiAgICB0ID0gYiAvIHRhcmdldF92ZWwubWFnbml0dWRlKCkKICAgIGNvbGxpc2lvbl9wb3MgPSB0YXJnZXRfcG9zICsgKHRhcmdldF92ZWwgKiB0KQoKICAgIHByaW50ICJDb2xsaXNpb24gcG9zOiB7fSIuZm9ybWF0KGNvbGxpc2lvbl9wb3MpCiAgICBwcmludCAiVGltZToge30iLmZvcm1hdCh0KQoKICAgIHByaW50ICJBbmdsZSBBOiB7fSIuZm9ybWF0KHRvX2RlZ3JlZXMoQ0FCKSkKICAgIHByaW50ICJBbmdsZSBCOiB7fSIuZm9ybWF0KHRvX2RlZ3JlZXMoQUJDKSkKICAgIHByaW50ICJBbmdsZSBDOiB7fSIuZm9ybWF0KHRvX2RlZ3JlZXMoQUNCKSkKCiAgICBwcmludCAiYToge30iLmZvcm1hdChhKQogICAgcHJpbnQgImI6IHt9Ii5mb3JtYXQoYikKICAgIHByaW50ICJjOiB7fSIuZm9ybWF0KGMpCgp0YXJnZXRfcG9zID0gUG9pbnQoMTIwLDQwKQp0YXJnZXRfdmVsID0gUG9pbnQoNSwyKQppbnRlcmNlcHRvcl9wb3MgPSBQb2ludCg4MCw4MCkKaW50ZXJjZXB0b3Jfc3BlZWQgPSAxMAoKZmluZF9jb2xsaXNpb25fcG9pbnQodGFyZ2V0X3BvcywgdGFyZ2V0X3ZlbCwgaW50ZXJjZXB0b3JfcG9zLCBpbnRlcmNlcHRvcl9zcGVlZCk=