I want to simulate particle movement inside a box. When the particles hit one of the 4 walls in the box I want them to bounce back. Elastic collision [Drawing of velocity changes after collision][1]
EDIT
Added more code. Changed if test for (x,y,z) limit. I know that when particles hit the wall the velocity parallell to the wall will stay the same, but the velocity perpendicular to the wall will change to -v_perpendicular_vel_before
Full code
fig0 = plt.figure()
fig1 = plt.figure()
n = 1000 # time steps
M = 1000. #[kg]
N = 1000 #10**5
a = 0
There's no way I can reproduce the run, since you haven't posted full code; I'm doing this from visual inspection.
Among other things, you've left variables k, T, m undefined, so I have no idea of the magnitude of the velocities. Is the upper limit high enough for a particle to skip totally past a collision? If v*dt can be > tol*2, you can totally miss collisions. When I code things like this, I make sure that tol is bounded by the maximum change.
Other than that, your position update is correct.
What happens when you run this with a single particle? Can you get it to bounce off all three walls? What happened when you hand-coded two particles headed straight at one another along a single dimension? I would like to think that you tested these basic functions before you loaded in 10000 particles.
I'm not sure what L is in your code; the only rational idea I have is that it's the upper limit of the box dimensions, which I'm reading as 1.2^10-6 on your plot.
The biggest problem that I see is that your only collision check is against the x+ side of the box. You haven't check the other dimensions or the lower limit (0?) for anything, and there's no comparison of one particle to another. As a result, the only change you'll get is to have roughly half your particles hit the right wall (x+) and reverse direction. Other than that, everything will drift forever, with ever-decreasing x values.
HANDLE A BOUNCE
First of all, get your velocity and position expressions in synch with one another: each of these should be a triple (tuple or list), so you can index them together. You currently have them as the second index of your particle list (good idea), but three separate variables elsewhere (poor idea). Instead, make newVel a list, so you can simply loop through the particles and update:
for dim in range(3):
if r[i, dim] < tol or # hit the lower wall
r[i, dim] > b - tol: # hit the upper wall
v[i, dim] = -v[i, dim]
Also, please just update the positions appropriately; no need to involve the local, temporary variables:
for dim in range(3):
r[i, dim] += v[i, dim] * dt
WRITE SOME SERVICE FUNCTIONS
It often helps to write some general-use functions, just to get them out of the way when you're writing your main code. At the moment, you're handling a lot of details, when you should be worrying about only one technique at a time. For instance, since you're going to be handling particle collisions, you'll want a distance computation. Don't keep this in the middle of your active code; just write a function to do it. For instance:
def dist(a, b): # a, b are positions: triples of coordinates
return math.sqrt(sum([(a[dim] - b[dim])**2 for dim in range(3)]))
I won't comment on how to implement the particle collisions, because you haven't shown any effort to solve the problem yet, and you're not yet at a point to attack that extension. First, get this part working: one particle bouncing around, then two particles bouncing around, then maybe 4 particles to show that you can do it for an arbitrary quantity of particles.
Once you have that, you can worry about particle collisions. When you get to that point, make a good attempt; if you get stuck, post a new question.
Related
This is what I am currently doing:
Creating 4 axis that are perpendicular to 4 edges of 2 rectangles. Since they are rectangles I do not need to generate an axis (normal) per edge.
I then loop over my 4 axes.
So for each axis:
I get the projection of every corner of a rectangle on to the axis.
There are 2 lists (arrays) containing those projections. One for each rectangle.
I then get the dot product of each projection and the axis. This returns a scalar value
that can be used to to determine the min and max.
Now the 2 lists contain scalars and not vectors. I sort the lists so I can easily select the min and max values. If the min of box B >= the max of box A OR the max of box B <= the min of box A then there is no collision on that axis and no collision between the objects.
At this point the function finishes and the loop breaks.
If those conditions are never met for all the axis then we have a collision
I hope this was the correct way of doing it.
The python code itself can be found here http://pastebin.com/vNFP3mAb
Also:
http://www.gamedev.net/page/reference/index.html/_/reference/programming/game-programming/collision-detection/2d-rotated-rectangle-collision-r2604
The problem i was having is that the code above does not work. It always detects a a collision even where there is not a collision. What i typed out is exactly what the code is doing. If I am missing any steps or just not understanding how SAT works please let me know.
In general it is necessary to carry out the steps outlined in the Question to determine if the rectangles "collide" (intersect), noting as the OP does that we can break (with a conclusion of non-intersection) as soon as a separating axis is found.
There are a couple of simple ways to "optimize" in the sense of providing chances for earlier exits. The practical value of these depends on the distribution of rectangles being checked, but both are easily incorporated in the existing framework.
(1) Bounding Circle Check
One quick way to prove non-intersection is by showing the bounding circles of the two rectangles do not intersect. The bounding circle of a rectangle shares its center, the midpoint of either diagonal, and has diameter equal to the length of either diagonal. If the distance between the two centers exceeds the sum of the two circles' radii, then the circles do not intersect. Thus the rectangles also cannot intersect. If the purpose was to find an axis of separation, we haven't accomplished that yet. However if we only want to know if the rectangles "collide", this allows an early exit.
(2) Vertex of one rectangle inside the other
The projection of a vertex of one rectangle on axes parallel to the other rectangle's edges provides enough information to detect when that vertex is inside the other rectangle. This check is especially easy when the latter rectangle has been translated and unrotated to the origin (with edges parallel to the ordinary axes). If it happens that a vertex of one rectangle is inside the other, the rectangles obviously intersect. Of course this is a sufficient condition for intersection, not a necessary one. But it allows for an early exit with a conclusion of intersection (and of course without finding an axis of separation because none will exist).
I see two things wrong. First, the projection should simply be the dot product of a vertex with the axis. What you're doing is way too complicated. Second, the way you get your axis is incorrect. You write:
Axis1 = [ -(A_TR[0] - A_TL[0]),
A_TR[1] - A_TL[1] ]
Where it should read:
Axis1 = [ -(A_TR[1] - A_TL[1]),
A_TR[0] - A_TL[0] ]
The difference is coordinates does give you a vector, but to get the perpendicular you need to exchange the x and y values and negate one of them.
Hope that helps.
EDIT Found another bug
In this code:
if not ( B_Scalars[0] <= A_Scalars[3] or B_Scalars[3] >= A_Scalars[0] ):
#no overlap so no collision
return 0
That should read:
if not ( B_Scalars[3] <= A_Scalars[0] or A_Scalars[3] <= B_Scalars[0] ):
Sort gives you a list increasing in value. So [1,2,3,4] and [10,11,12,13] do not overlap because the minimum of the later is greater than the maximum of the former. The second comparison is for when the input sets are swapped.
For a project, I am trying to determine the time it would take for a photon to leave the Sun. However, I am having trouble with my code (found below).
More specifically, I set up a for loop with an if statement, and if some randomly generated probability is less than the probability of collision, that means the photon collides and it changes direction.
What I am having trouble with is setting up a condition where the for loop stops if the photon escapes (when distance > Sun radius). The one I have set up already doesn't appear to work.
I use a very scaled down measurement of the Sun's radius because if I didn't it would take a long time for the photon to escape in my simulation.
from numpy.random import random as rng # we want them random numbers
import numpy as np # for the math functions
import matplotlib.pyplot as plt # to make pretty pretty class
mass_proton = 1.67e-27
mass_electron = 9.11e-31
Thompson_cross = 6.65e-29
Sun_density = 150000
Sun_radius = .005
Mean_Free = (mass_proton + mass_electron)/(Thompson_cross*Sun_density*np.sqrt(2))
time_step= 10**-13 # Used this specifically in order for the path length to be < Mean free Path
path_length = (3e8)*time_step
Probability = 1-np.exp(-path_length/Mean_Free) # Probability of the photon colliding
def Random_walk():
x = 0 # Start at origin (0,0)
y = 0
N = 1000
m=0 # This is a counter I have set up for the number of collisions
for i in range(1,N+1):
prand = rng(N+1) # Randomly generated probability
if prand[i] < Probability: # If my prand is less than the probability
# of collision, the photon collides and changes
# direction
x += Mean_Free*np.cos(2*np.pi*prand)
y += Mean_Free*np.sin(2*np.pi*prand)
m += 1 # Everytime a collision occurs 1 is added to my collision counter
distance = np.sqrt(x**2 + y**2) # Final distance the photon covers
if np.all(distance) > Sun_radius: # if the distance the photon travels
break # is greater than the Radius of the Sun,
# the for loop stops, meaning the
#photon escaped
print(m)
return x,y,distance
x,y,d = Random_walk()
plt.plot(x,y, '--')
plt.plot(x[-1], y[-1], 'ro')
Any criticisms of my code are welcome, this is for a grade and I do want to learn how to do this correctly, so please tell me if you notice any other errors.
I don't understand the motivation for the formulas you've implemented. I'll explain my own motivation here, but if your instructor told you to do something else, I guess you should listen to them instead.
If I were going to do this, I would generate a sequence of movements of a photon, stopping when distance of the photon to the center of the sun is greater than the solar radius. Each movement is a sample from a distribution which has two components: one for the distance, and one for the direction. I will assume that these are independent (this may be questioned in a more careful simulation).
It seems plausible that the distribution of distance is an exponential distribution with parameter 1/(mean free path). Then the density is p(d) = (1/MFP) exp(-d/MFP). Its cdf is 1 - exp(-d/MFP) and the inverse of the cdf is -MFP log(1 - p) where p = cdf(d). Now you can sample from the distribution of distances: let p = rand(0, 1) where rand = uniform random and plug it into the inverse cdf to get d. This is called the inverse cdf method of sampling; a web search will find more info about it.
As for the direction, you can let angle = rand(0, 2*pi) and then (x, y) = (cos(angle), sin(angle)).
Now you can construct the series of positions. From an initial location, let the new location = previous + d*(x, y). Stop when distance of location to center is greater than radius.
Looks like a great problem! Good luck and have fun. Let me know if you have any questions.
Here is a way of thinking about the problem that you may find helpful. At each moment, the photon has a position (x, y) and a direction (dx, dy). The (dx, dy) variables are coefficients of the unit vector, so sqrt(dx**2 + dy**2) = 1.0. The distance traveled by the photon during one step is path_length * direction.
At each step you do 4 things:
calculate the photon's new position
figure out if the photon has left the sun by computing its distance from the center point
determine, with a single random number, whether or not the photon collides. If it does you randomly generate a new direction.
Append the photon's current position to a list. You might want to do this as a function of distance from the center rather than x,y.
At the end, you plot the list you have built up.
You should also choose a random direction at the very start.
I don't know how you will terminate the loop, for the photon isn't ever guaranteed to leave the sun - just like in the real world. In principle the program might run forever (or until the sun burns out).
There is a slight inaccuracy in that the photon can collide at any instant, not just at the end of one step. But since the steps are small, so is the error introduced by this simplification.
I will point out that you do not need numpy for any of this except perhaps the final plot. The standard Python library has all the math functions you need. Numpy is of course great for manipulating arrays of data, but the only array you will have here is the one you build, a step at a time, of photon position versus time.
As I pointed out in one of my comments, you are modeling the sun as a 2-dimensional object. If you want to do this calculation in three dimensions, you don't need to change this basic approach.
I'm writing a python spirograph program, and I need some help with converting part of it into a function. The code is attempting to reproduce the result illustrated in the video I found here. One line rotates around the origin, and then another rotates off the end of that, etc.
With a little bit of research into (what I think is) trigonometry, I put together a function rotate(point, angle, center=(0, 0)). The user inputs a point to be rotated, the angle (clockwise) that it is to be rotated by, and the centerpoint for it to be rotated around.
Then, I implemented an initial test, whereby one line rotates around the other. The end of the second line draws as if it were holding a pen. The code's a little messy, but it looks like this.
x, y = 0, 0
lines = []
while 1:
point1 = rotate((0,50), x)
point2 = map(sum,zip(rotate((0, 50), y), point1))
if x == 0:
oldpoint2 = point2
else:
canvas.create_line(oldpoint2[0], oldpoint2[1], point2[0], point2[1])
lines.append( canvas.create_line(0, 0, point1[0], point1[1]) )
lines.append( canvas.create_line(point1[0], point1[1], point2[0], point2[1]) )
oldpoint2 = point2
tk.update()
x += 5
if x > 360 and y > 360:
x -= 360
canvas.delete("all")
time.sleep(1)
y += 8.8
if y > 360: y -= 360
for line in lines:
canvas.delete(line)
lines = []
Great, works perfectly. My ultimate goal is what's in the video, however. In the video, the user can input any arbitrary number of arms, then define the length and angular velocity for each arm. Mine only works with two arms. My question, ultimately, is how to put the code I posted into a function that looks like drawSpiral(arms, lenlist, velocitylist). It would take the number of arms, a list of the velocities for each arm, and a list of the length of each arm as arguments.
What I've Tried
I've already attempted this several times. Initially, I had something that didn't work at all. I got some cool shapes, but definitely not the desired output. I've worked for a few hours, and the closest I could get was this:
def drawSpiral(arms, lenlist, velocitylist):
if not arms == len(lenlist) == len(velocitylist):
raise ValueError("The lists don't match the provided number of arms")
iteration = 0
while 1:
tk.update()
iteration += 1
#Empty the list of points
pointlist = []
pointlist.append((0, 0))
#Create a list of the final rotation degrees for each point
rotations = []
for vel in velocitylist:
rotations.append(vel*iteration)
for n in range(arms):
point = tuple(map(sum,zip(rotate((0, lenlist[n]), rotations[n], pointlist[n]))))
pointlist.append(point)
for point in pointlist:
create_point(point)
for n in range(arms):
print pointlist[n], pointlist[n+1]
This is fairly close to my solution, I feel, but not quite there. Calling drawSpiral(2, [50, 75], [1, 5]) looks like it might be producing some of the right points, but not connecting the right sets. Staring at it for about an hour and trying a few things, I haven't made any progress. I've also gotten pretty confused looking at my own code. I'm stuck! The point rotating around the center is attached to a point that is just flying diagonally across the screen and back. The line attached to the center is stretching back and forth. Can someone point me in the right direction?
Results of further tests
I've set up both functions to plot points at the ends of each arm, and found some interesting results. The first arm, in both cases, is rotating at a speed of 5, and the second at a speed of -3. The loop, outside of the function, is producing the pattern:
The function, called with drawSpiral(2, [50, 50], [5, -3]), produces the result of
It seems to be stretching the top half. With both arms having a velocity of 5, the function would be expected to produce two circles, one larger than the other. However, it produces an upside-down cardioid shape, with the point connected to the center.
Now there's more evidence, can anyone who understands math more than me help me?
Your error is in
for n in range(arms):
point = tuple(map(sum,zip(rotate((0, lenlist[n]), rotations[n], pointlist[n]))))
pointlist.append(point)
Specifically,
rotate((0, lenlist[n])
replace it with
for n in range(arms):
point = tuple(map(sum,zip(rotate((pointlist[n][0], lenlist[n]), rotations[n], pointlist[n]))))
pointlist.append(point)
You go against the usual mathematical notation for polars (circular graphs) and that caused your confusion and eventual issues. As far as I can tell your function is plotting an (X,Y) point (0,length) and then finding the difference between that point and the center point (which is correctly defined as the last point you found) and rotating it around that center. The issue is that (0,length) is not 'length' away from the center. By replacing the (0,lenlist[n]) with (pointlist[n][0],lenlist[n]) makes the next point based upon the last point.
Also I would recommend editing your rotate function to be rotate(length,angle,centerpoint) which would simplify the inputs to a more traditional representation.
I am trying to create a fool proof graphing calculator using python and pygame.
I created a graphing calculator that works for most functions. It takes a user string infix expression and converts it to postfix for easier calculations. I then loop through and pass in x values into the postfix expression to get a Y value for graphing using pygame.
The first problem I ran into was when taking calculations of impossible things. (like dividing by zero, square root of -1, 0 ^ non-positive number). If something like this would happen I would output None and that pixel wouldn't be added to the list of points to be graphed.
* I have showed all the different attempts I have made at this to help you understand where I cam coming from. If you would like to only see my most current code and method, jump down to where it says "current".
Method 1
My first method was after I acquired all my pixel values, I would paint them using the pygame aalines function. This worked, except it wouldn't work when there were missing points in between actual points because it would just draw the line across the points. (1/x would not work but something like 0^x would)
This is what 1/x looks like using the aalines method
Method 1.1
My next Idea was to split the line into two lines every time a None was printed back. This worked for 1/x, but I quickly realized that it would only work if one of the passed in X values exactly landed on a Y value of None. 1/x might work, but 1/(x+0.0001) wouldn't work.
Method 2
My next method was to convert the each pixel x value into the corresponding x point value in the window (for example, (0,0) on the graphing window actually would be pixel (249,249) on a 500x500 program window). I would then calculate every y value with the x values I just created. This would work for any line that doesn't have a slope > 1 or < -1.
This is what 1/x would look like using this method.
Current
My most current method is supposed to be a advanced working version of method 2.
Its kind of hard to explain. Basically I would take the x value in between each column on the display window. For every pixel I would do this just to the left and just to the right of it. I would then plug those two values into the expression to get two Y values. I would then loop through each y value on that column and check if the current value is in between both of the Y values calculated earlier.
size is a list of size two that is the dimensions of the program window.
xWin is a list of size two that holds the x Min and x Max of the graphing window.
yWin is a list of size two that holds the y Min and y Max of the graphing window.
pixelToPoint is a function that takes scalar pixel value (just x or just y) and converts it to its corresponding value on the graphing window
pixels = []
for x in range(size[0]):
leftX = pixelToPoint(x,size[0]+1, xWin, False)
rightX = pixelToPoint(x+1, size[0]+1, xWin, False)
leftY = calcPostfix(postfix, leftX)
rightY = calcPostfix(postfix, rightX)
for y in range(size[1]):
if leftY != None and rightY != None:
yPoint = pixelToPoint(y,size[1],yWin, True)
if (rightY <= yPoint <= leftY) or (rightY >= yPoint >= leftY):
pixels.append((x,y))
for p in pixels:
screen.fill(BLACK, (p, (1, 1)))
This fixed the problem in method 2 of having the pixels not connected into a continuous line. However, it wouldn't fix the problem of method 1 and when graphing 1/x, it looked exactly the same as the aalines method.
-------------------------------------------------------------------------------------------------------------------------------
I am stuck and can't think of a solution. The only way I can think of fixing this is by using a whole bunch of x values. But this way seems really inefficient. Also I am trying to make my program as resizable and customizable as possible so everything must be variably driven and I am not sure what type of calculations are needed to find out how many x values are needed to be used depending on the program window size and the graph's window size.
I'm not sure if I am on the right track or if there is a completely different method of doing this, but I want to create my graphing calculator to able to graph any function (just like my actual graphing calculator).
Edit 1
I just tried using as many x values as there are pixels (500x500 display window calculates 250,000 y values).
Its worked for every function I've tried with it, but it is really slow. It takes about 4 seconds to calculate (it fluctuates depending on the equation). I've looked around online and have found graphing calculators that are almost instantaneous in their graphing, but I cant figure out how they do it.
This online graphing calcuator is extremely fast and effective. There must be some algorithm other than using a bunch of x values than can achieve what I want because that site is doing it..
The problem you have is that to be able to know if between two point you can reasonably draw a line you have to know if the function is continuous in the interval.
It is a complex problem in General what you could do is use the following heuristic. If the slope of the line have changed too much from the previous one guess you have a non continuous point in the interval and don't draw a line.
Another solution would be based on solution 2.
After have draw the points that correspond to every value of the x axis try to draw for every adjacent x: (x1, x2) the y within (y1 = f(x1), y2 = f(x2)) that can be reach by an x within (x1, x2).
This can be done by searching by dichotomy or via the Newton search heuristic an x that could fit.
This is what I am currently doing:
Creating 4 axis that are perpendicular to 4 edges of 2 rectangles. Since they are rectangles I do not need to generate an axis (normal) per edge.
I then loop over my 4 axes.
So for each axis:
I get the projection of every corner of a rectangle on to the axis.
There are 2 lists (arrays) containing those projections. One for each rectangle.
I then get the dot product of each projection and the axis. This returns a scalar value
that can be used to to determine the min and max.
Now the 2 lists contain scalars and not vectors. I sort the lists so I can easily select the min and max values. If the min of box B >= the max of box A OR the max of box B <= the min of box A then there is no collision on that axis and no collision between the objects.
At this point the function finishes and the loop breaks.
If those conditions are never met for all the axis then we have a collision
I hope this was the correct way of doing it.
The python code itself can be found here http://pastebin.com/vNFP3mAb
Also:
http://www.gamedev.net/page/reference/index.html/_/reference/programming/game-programming/collision-detection/2d-rotated-rectangle-collision-r2604
The problem i was having is that the code above does not work. It always detects a a collision even where there is not a collision. What i typed out is exactly what the code is doing. If I am missing any steps or just not understanding how SAT works please let me know.
In general it is necessary to carry out the steps outlined in the Question to determine if the rectangles "collide" (intersect), noting as the OP does that we can break (with a conclusion of non-intersection) as soon as a separating axis is found.
There are a couple of simple ways to "optimize" in the sense of providing chances for earlier exits. The practical value of these depends on the distribution of rectangles being checked, but both are easily incorporated in the existing framework.
(1) Bounding Circle Check
One quick way to prove non-intersection is by showing the bounding circles of the two rectangles do not intersect. The bounding circle of a rectangle shares its center, the midpoint of either diagonal, and has diameter equal to the length of either diagonal. If the distance between the two centers exceeds the sum of the two circles' radii, then the circles do not intersect. Thus the rectangles also cannot intersect. If the purpose was to find an axis of separation, we haven't accomplished that yet. However if we only want to know if the rectangles "collide", this allows an early exit.
(2) Vertex of one rectangle inside the other
The projection of a vertex of one rectangle on axes parallel to the other rectangle's edges provides enough information to detect when that vertex is inside the other rectangle. This check is especially easy when the latter rectangle has been translated and unrotated to the origin (with edges parallel to the ordinary axes). If it happens that a vertex of one rectangle is inside the other, the rectangles obviously intersect. Of course this is a sufficient condition for intersection, not a necessary one. But it allows for an early exit with a conclusion of intersection (and of course without finding an axis of separation because none will exist).
I see two things wrong. First, the projection should simply be the dot product of a vertex with the axis. What you're doing is way too complicated. Second, the way you get your axis is incorrect. You write:
Axis1 = [ -(A_TR[0] - A_TL[0]),
A_TR[1] - A_TL[1] ]
Where it should read:
Axis1 = [ -(A_TR[1] - A_TL[1]),
A_TR[0] - A_TL[0] ]
The difference is coordinates does give you a vector, but to get the perpendicular you need to exchange the x and y values and negate one of them.
Hope that helps.
EDIT Found another bug
In this code:
if not ( B_Scalars[0] <= A_Scalars[3] or B_Scalars[3] >= A_Scalars[0] ):
#no overlap so no collision
return 0
That should read:
if not ( B_Scalars[3] <= A_Scalars[0] or A_Scalars[3] <= B_Scalars[0] ):
Sort gives you a list increasing in value. So [1,2,3,4] and [10,11,12,13] do not overlap because the minimum of the later is greater than the maximum of the former. The second comparison is for when the input sets are swapped.