• Advertisement
Sign in to follow this  

Need nice way to create parabolic trajectory

This topic is 3891 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, I have made basic parabolic paths (by subtracting from a pre-set Y-amount), but I am looking for something more elaborate. I'm using this to create a trajectory for a mortar/projectile. I would really like a function which takes three parameters: the launching position(Vector), the landing position(Vector), and the angle of arc (float). Anyone have a function like this sitting on their hard drive? I also wouldn't mind finding a Class which does this and offers more features; this is something I do fairly often and would love more flexibility. I am happy to answer any questions if it helps to convey what I'm looking for. Thanks in advance!! Regards, -joshua joshua >>at<< spaceweather.com

Share this post


Link to post
Share on other sites
Advertisement
You could take a look at my two responses here for a couple of trajectory equations and functions, you could also do it iteratively (as you seem to be currently) as was also suggested there.

Unfortunately neither method I presented uses the angle of the arc, although im sure you could use the angle to calculate an initial speed/direction vector, but you did say you were interested in alternatives [smile]

Good luck

Share this post


Link to post
Share on other sites
Hey DMatter,

That's a helpful thread, but of course it's not exactly what I'm looking for. I really want a function with this signature:

Vector3 calculateTrajectory(Vector3 orgin, Vector3 destination, double arc);


Any ideas :-)



Thanks!!!!! Best,
-joshua

Share this post


Link to post
Share on other sites
R = horizontal difference of points
H = altitude difference to target
A = arc angle from ground
G = gravity constant
S = initial speed of the projectile

t = R / cos(A)

solve for S:
H = G * t * t / 2 + sin(A) * S * t

S = (H - G * t * t / 2) / (sin(A) * t)


Now that you have S, just make a new vector in the direction of the target, at the arc angle with magnitude of S.

I just did this now, there may be errors. But this should get you started.

Share this post


Link to post
Share on other sites
Quote:
Original post by meteors
Hey DMatter,

That's a helpful thread, but of course it's not exactly what I'm looking for. I really want a function with this signature:

Vector3 calculateTrajectory(Vector3 orgin, Vector3 destination, double arc);

I'm afraid that's not well-defined. If 'arc' defines the angle at the origin, then the (function) parameter set will uniquely define a parabola. To determine a point on the trajectory you'd also need to pass an input (mathematical) parameter, most likely time-from-launch or current-angle-from-some-give-point.

But yes, the best way to proceed would be to solve the quadratic equations of motion to describe the parabola as an appropriate polynomial, then evaluate it using the passed parameter.

Think carefully about exactly what inputs will be available and what form you want the output to be in, then report back.

Admiral

Share this post


Link to post
Share on other sites
Hi Admiral,

Argh...math...

Do you not know of any library which makes this sort of thing easy?



Cheers!
-joshua

Share this post


Link to post
Share on other sites
Quote:
Original post by meteors
Do you not know of any library which makes this sort of thing easy?

The mathematics isn't too tricky. I'm sure there are a few of us who'd be happy to walk you through the calculation and corresponding code. But first we need to know exactly what you're trying to achieve, 'cause it's not clear at the moment.

Take a step back and describe your goal (rather than your method) and we'll see how we can help.

Admiral

Share this post


Link to post
Share on other sites
Here's a way to do that's alot simpler since I cheated a bit with the equations:

""" turretGrav.py
Side view of turrent gun
Jaime Moreno
4/13/07
"""


import pygame, math
pygame.init()

class Label(pygame.sprite.Sprite):
""" Label Class (simplest version)
Properties:
font: any pygame font object
text: text to display
center: desired position of label center (x, y)
"""

def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.font = pygame.font.SysFont("None", 30)
self.text = ""
self.center = (320, 240)

def update(self):
self.image = self.font.render(self.text, 1, (0, 0, 0))
self.rect = self.image.get_rect()
self.rect.center = self.center

class Turret(pygame.sprite.Sprite):
def __init__(self, shell):
self.shell = shell
pygame.sprite.Sprite.__init__(self)
self.imageMaster = pygame.image.load("turret.gif")
self.imageMaster = self.imageMaster.convert()
self.imageMaster = pygame.transform.scale2x(self.imageMaster)
self.rect = self.imageMaster.get_rect()
self.rect.center = (100, 300)
self.turnRate = 10
self.dir = 0
self.charge = 5

def update(self):
self.checkKeys()
self.rotate()

def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.dir += self.turnRate
if self.dir > 360:
self.dir = self.turnRate
if keys[pygame.K_RIGHT]:
self.dir -= self.turnRate
if self.dir < 0:
self.dir = 360 - self.turnRate
if keys[pygame.K_UP]:
self.charge += 1
if self.charge > 20:
self.charge = 20
if keys[pygame.K_DOWN]:
self.charge -= 1
if self.charge < 0:
self.charge = 0

if keys[pygame.K_SPACE]:
self.shell.x = self.rect.centerx
self.shell.y = self.rect.centery
self.shell.speed = self.charge
self.shell.dir = self.dir
self.shell.calcVector()

def rotate(self):
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMaster, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter

class Shell(pygame.sprite.Sprite):
def __init__(self, screen, background): # add background to draw path
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.background = background

self.image = pygame.Surface((10, 10))
self.image.fill((0xff, 0xff, 0xff))
self.image.set_colorkey((0xff, 0xff, 0xff))
pygame.draw.circle(self.image, (0, 0, 0), (5, 5), 5)
self.image = pygame.transform.scale(self.image, (5, 5))
self.rect = self.image.get_rect()
self.rect.center = (-100, -100)

self.gravity = .5 # modify gravity as needed
self.speed = 0
self.dir =0
self.dx = 0
self.dy = 0
self.reset()

def update(self):
# self.calcVector()
# since we commented out calcVector() need to add self.dx,dy above!
self.calcPos()
self.checkBounds()
self.rect.center = (self.x, self.y)

def calcVector(self):
radians = self.dir * math.pi / 180

self.dx = self.speed * math.cos(radians)
self.dy = self.speed * math.sin(radians)
self.dy *= -1

# clear the background each time we fire so old paths don't show up
#self.background.fill((0x00,0xcc,0x00))

def calcPos(self):
# compensate for gravity


self.dy += self.gravity

# draw shell path
# get old position for drawing
oldx = self.x
oldy = self.y
self.x += self.dx
self.y += self.dy

pygame.draw.line(self.background, (0,0,0), (oldx, oldy),(self.x,self.y))



def checkBounds(self):
screen = self.screen
if self.x > screen.get_width():
self.reset()
if self.x < 0:
self.reset()
if self.y > screen.get_height():
self.reset()
if self.y < 0:
self.reset()

def reset(self):
""" move off stage and stop"""
self.x = -100
self.y = -100
self.speed = 0

def main():
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption ("Firing a Shell")

background = pygame.Surface(screen.get_size())
background.fill((0x00, 0xCC, 0x00))
screen.blit(background, (0, 0))

shell = Shell(screen, background)
turret = Turret(shell)
lblOutput = Label()
lblOutput.center = (100, 20)

allSprites = pygame.sprite.Group(shell, turret, lblOutput)

clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False

#update label
lblOutput.text = "dir: %d speed %d" % (turret.dir, turret.charge)

# blit background for drawings
screen.blit(background, (0,0))

#allSprites.clear(screen, background)
allSprites.update()
allSprites.draw(screen)
pygame.display.flip()

if __name__ == "__main__":
main()


Share this post


Link to post
Share on other sites
TheAdmiral,

Later today (when I have free time), I will explain in detail what I want to do. I really appreciate your offer to help :-)


Best,
-joshua

Share this post


Link to post
Share on other sites
Alright. Consider the following. You have a generic parabola which would be expressed as

y(x) = Ax^2 + Bx + C

You also have two points, and initial point (x0, y0) and a final point (x1, y1). This gives us two equations:

y0 = A(x0)^2+B(x0)+C
y1 = A(x1)^2+B(x1)+C

But with 3 unknowns (A,B,C) we don't have a solution yet. But we also have an initial angle theta. This corresponds to a slope of tan(theta). We can evaluate the slope of the parabola by taking a derivative so:

y'(x) = 2A*x+B

y' is our slope which we know at the initial point x=x0 so a third equation is

tan(theta) = 2*A*(x0)+B

This gives three equations:

tan(theta) = 2*A*(x0)+B
y0 = A(x0)^2+B(x0)+C
y1 = A(x1)^2+B(x1)+C

Which can be solved in the usual ways for A, B, and C.

At this point we have the equation we need for the parabola based off the initial point, final point and initial slope.

Share this post


Link to post
Share on other sites
Ill have a go at walking through the mathematics although I've posted code at the bottom if you just want to skip to that.
My approach seems a bit drawn out for what is really a simple problem, I'd be interested to see how anyone else does it?

Okay,
You have an origin point (x0, y0, z0) and a destination point (x1, y1, z1) and an angle we'll call theta.

The curve function
The curve itself lies on a plane that coincides with our origin point and destination point, I will use axes called w and y, where w lies arbitrarily along the xz-plane and y is the conventional 'up' axis. The planes origin maybe translated horizontally from world-space so that the origin is (x0, 0, z0).

The general equation of the curve is

y = Aw^2 + Bw + C

Where A,B,C are three unknowns that we must resolve.

We can tell from inspection that when w=0 then y = C, and that y must also equal y0 (the origin's y-component). So, obviously C = y0.

This leaves us with only A and B to resolve.

The differential of this curve is therefore

dy/dw = 2Aw + B

From the angle of the curve we know that when w=0 the gradient of the curve must equal TAN(theta) (I'm capitalising the tan to avoid confusion with t which I will introduce later on).

So:

dy/dw = TAN(theta) when w=0

therefore: B = TAN(theta)

So now we also have B, only A to go [smile]
We know that our curve has a single turning point and at this point its gradient is zero, some value for w will produce this point, lets call this value m.
Substituting in what we know for B and m inplace of w we have

2Am + TAN(theta) = 0

Rearranging to get A

A = -TAN(theta) / 2m

So we have A although its in terms of yet another unknown, m, but thats OK, we can substitute A, B and C into our curves general equation:

y = (w^2)(-TAN(theta)/(2m)) + (w)TAN(theta) + y0

We need to resolve m, but thats more complicated.
If we know a position on our current curve function (y = f(w)) then we could substitute that in and get m.
Luckily we know that our destination point lies somewhere on our curve, we can find its position on our wy-plane as so:

w = sqrt((x1-x0)^2 + (y1-yo)^2))
y = y1

Conceptually sqrt((x1-x0)^2 + (y1-yo)^2)) is the horizontal distance of the curve, since its a-lot to type I will assign it to an alias and call it L, therefore w = L.

Substituting these in:

y1 = (-TAN(theta)/(2m))(L^2) + (L)TAN(theta) + y0

we can now rearrange to get m, although actually we need only go as far as -1/(2m)

-1/(2m) = ((y1-y0)/TAN(theta) - L) / (L^2)

And now we must substitute this back into the general equation (the one in bold-italics):

y = (w^2)TAN(theta)(((y1-y0)/TAN(theta) - L) / (L^2)) + (w)TAN(theta) + y0

At this point we can simplify the w^2 coefficients:

y = (w^2)(((y1-y0) - (L)TAN(theta)) / (L^2)) + (w)TAN(theta) + y0

Now we have the working function: y = f(w), where w is between 0 and L.

Ideally we would have a parametric function: y = f(t), where t is between 0 and 1

Its not hard to see that with the relationship w = tL then f(w) = f(tL)

So substituting tL inplace of w we get:

y = ((tL)^2)(((y1-y0) - (L)TAN(theta)) / (L^2)) + (tL)TAN(theta) + y0

The L^2 terms cancel out and we can multiply through with the t^2 term:

y = (t^2)(y1-y0) - (t^2)(L)TAN(theta) + (tL)TAN(theta) + y0

Now factorise out (tL)TAN(theta)

y = (tL)(1-t)TAN(theta) + (t^2)(y1-y0) + y0

And so finally we now have something of the form: y = f(t) [smile]

Now the x and z axes
The x and z axes are much easier to calculate.
We want functions, g and h such that:

x = g(t)
z = h(t)


Where t is in the range 0 to 1.

Both functions take the form:

p = o + t(d - o)

where p is the output position (x or z), o is the origin component and d is the destination component.

Therefore:

x = x0 + t(x1 - x0)
z = z0 + t(z1 - z0)


Now we've done the maths, lets see some code
As TheAdmiral rightly pointed out you will be needing a fourth parameter in your function definition, this is the t parameter, its in the range 0 to 1 so it needs to be floating point (float or double in C/C++ terms).

With that said, here's your function (though untested):

Vector3 calculateTrajectory(Vector3 origin, Vector3 destination, double angle, float t)
{
Vector3 output;

// Just for convenience
float xDisplacement = (destination.x - origin.x);
float yDisplacement = (destination.y - origin.y);
float zDisplacement = (destination.z - origin.z);
float tanTheta = std::tan(angle);

// Calculate the x and z components
output.x = origin.x + t * xDisplacement;
output.z = origin.z + t * zDisplacement;

// Calculate the horizontal distance covered by our curve
float distance = std::sqrt(xDisplacement*xDisplacement + zDisplacement*zDisplacement);

// Calculate the y component
output.y = (t*distance*(1-t)*tanTheta) + (t*t * yDisplacement) + origin.y;

return output;
}

Note that this function expects the angle in radians, if you desire degrees then you can convert using:

radians = degrees/180*pi


Well yes that was a very long post, but I found it interesting to write anyway [smile], my proof-reading is usually not brilliant but with any luck it makes sense.

Hope that was some help anyway
Have a nice day

David

[Edited by - dmatter on June 30, 2007 12:37:25 PM]

Share this post


Link to post
Share on other sites
Ok guys, I need a day to digest all this.

Thank you!!!! ...and I'll post more shortly.



All the best,
-joshua

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement