arrow.py

from visual import *

myarrow = arrow(shaftwidth=0.01,length=5,color=color.red)
ground  = box(size=(50,.1,200),pos=(0,-.05,50),color=color.green)
target  = ring(pos=(0,1,100),radius=1,axis=(0,0,-1),thickness=.5,color=(1,1,0))

numblocks=20
width=target.pos.z/numblocks
for i in range(numblocks):
    z=i*target.pos.z/numblocks
    box(size=(width,.1,width),pos=(0,.01,z),axis=(1,0,1),color=(z/target.pos.z,1-z/target.pos.z,1))

scene.center=(myarrow.pos+target.pos)/2.0
scene.forward=(.1,0,1)
scene.range=3
scene.lights=[norm(vector(-1,1,-1))]


def distance(p1,p2):
    return mag(p1-p2)

def collision(torus,point):
    if mag(torus.pos-point)<torus.radius:
        if point.z>=torus.z:
            if (point.x-torus.pos.x)**2+(point.y-torus.pos.y)**2 < torus.radius**2:
                return True
    return False

def fire(arrow,v0,phi,theta):
    vx=v0*cos(phi)*cos(theta)
    vy=v0*sin(phi)
    vz=v0*cos(phi)*sin(theta)
    v=vector(vx,vy,vz)
    arrow.axis=norm(v)

    dt=1.0/128
    a=vector(0,-9.8,0)
    arrow.point = arrow.pos+arrow.axis
    while arrow.point.y>=0 and not collision(target,arrow.point):
        v = v + a*dt
        arrow.pos += v*dt
        arrow.axis=norm(v)*5
        scene.center=arrow.pos
        arrow.point = arrow.pos+arrow.axis
        rate(100)

error=distance(target.pos,myarrow.pos+myarrow.axis)
v0=50
phi=.1
phi_upper=.3
phi_lower=0
theta=pi/3
theta_upper=pi
theta_lower=0
tries=0
while error >.1:
    tries += 1
    myarrow.pos=(0,0,0)
    fire(myarrow,v0,phi,theta)
    point = myarrow.point
    error=distance(target.pos,point)
    print tries,phi,error

    #This solution looks both for a phi and a theta value assuming we
    #aren't already aiming with the correct theta

    #binary search for phi
    if point.y>0:
        if target.pos.y-point.y>.0001:
            phi_lower=phi
        else:
            phi_upper=phi
    else:
        if point.z<target.pos.z:
            phi_lower=phi
        else:
            phi_upper=phi
    phi=(phi_upper+phi_lower)/2.0 

    
    #binary search for theta
    theta_error = target.pos.x-point.x
    if theta_error < -.0005:
        theta_lower=theta
    elif theta_error > .0005:
        theta_upper=theta
    theta=(theta_upper+theta_lower)/2.0

    print "p=",phi,"t=",theta


Generated by GNU enscript 1.6.3.