Rephrase spirograph code into function - python

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.

Related

Intersection of a 3d curve with a rectangle

I couldnt find a solution to my problem :
I have a 3D curve defined in python by 3 lists, X Y and Z.
I want to know whether this curve intersects with a certain rectangle (for instance it's normal to the y axis) using a function looking like this :
def curve_intersects(X,Y,Z,x,y,z,height, width):
return True # Or False
Thank you ! :)
EDIT :
def collision_filet(X,Y,Z):
for i in range(len(X)):
if (abs(X[i]) + radius <= WIDTH_TABLE/2.):
if Y[i]>=1.37 - 0.015 and Y[i]<= 1.37 + 0.015:
if Z[i]>=0 and Z[i]<= 0.1525:
return True
return False
The curve I have is defined by consecutive points of the lists X Y and Z.
The way I do it right now is by checking if each point is within a box that's a little bigger than the rectangle I want to check the of the curve collision with.
The problem of this technique is that it requires to have a curve with a lot of points which takes a lot of computing time and that I dont want.
EDIT 2 : I can give you more details about my problem to help you seeing it better. So I have the curve of a ping pong ball as it goes over the ball the ping pong table. The thing is that I need to create a function that checks whether the ball trajectory intersects with the net of the table (length : 1.83m, height : 0.15m, we can consider it's width to be null and the bottom line of the rectangle touches the table at z = 0).
So this is what it looks like
So since my curve is defined by consecutive points of the lists, I want to check for each consecutive points whether the line that separates them intersects with the rectangle of the net and this is where I'm stuck at.
EDIT3 : I managed to solve my problem by splitting it in two segment intersection problems

Particle collisions with walls inside box

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.

Creating a fool proof graphing calculator using python - Python 2.7

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.

How do I fill a polygon made up of randomized points in Pygame?

currently for a project i have a list variable containing 36 (x,y) coordinates. The intent is to draw a polygon that's inside is filled. all of the points are randomized but limited to a large square around the border of the screen so there will always be a border of points. When i use the pygame.draw.polygon command. It seems no matter how many different random points are generated the polygon that comes out is always messed up with lots of holes in the center and triangle like shapes that form, just not a filled shape.
Are there some kind of rules with using the draw.polygon command that i don't understand?
does the amount of points i use negatively effect the shape?
why is it that i am making a closed shape but it is not filling all the way?
Are their alternative methods to make a filled polygon also?
Thanks for your help and time.
(picture is rotated look at mouse direction)
This picture is right side up
My main question is why the polygon isnt filling entirely and why the bottom never attaches all the way.
See the code below:
def build(self):
for i in range(1,5):
for b in range(1,7):
if i == 1:
x = R.randrange(b*100,b*1000)
y = R.randrange(0,1000)
points.append([x,y])
elif i == 2:
x = R.randrange(9000,10000)
y = R.randrange(b*1000-1000,b*1000)
points.append([x,y])
elif i == 3:
x = R.randrange(b*1000,b*1000+1000)
y = R.randrange(9000,10000)
points.append([x,y])
elif i == 4 and b!=9:
x = R.randrange(0,1000)
y = R.randrange(b*1000-1000,b*1000)
points.append([x,y])
this is my func to make the points
screen.fill(blue)
pygame.draw.polygon(screen,green,(points))
draw statement
I believe there are two issues.
The first issue is causing the mess at the top of the image. There is an error in this line in the case where i == 1:
x = R.randrange(b*100,b*1000)
This selects an x value each time through the loop, but the selections are from overlapping ranges, rather than strictly increasing ones. I suspect you wanted to use the logic more like the i == 3 case:
x = R.randrange(b*1000,b*1000+1000)
The second issue is more of a logic issue. You're i == 1 and i == 2 cases describe the top and right edges of your shape with increasing x and y values, respectively. Your i == 3 and i == 4 cases describe the bottom and left side, but they also are described with increasing x and y values. This breaks the connections between the corners of the polygon, and is responsible for the reversed colors and the long diagonal line (between the end of the bottom edge and the start of the left edge).
Here's my suggested solution, which generalizes your code a bit. Rather than looping on a meaningless i number and using if statements to handle the different edges, I describe each edge by a 4-tuple of values (the starting x and y values, and the offsets for each successive point) and loop over a list of tuples. The loop body is very simple, as an additional random offset is added to each coordinate as it is calculated:
for x, dx, y, dy in [(0,1000, 0, 0), (9000, 0, 0, 1000),
(9000, -1000, 9000, 0), (0, 0, 9000, -1000)]:
for b in range(9):
points.append([x + b*dx + R.randrange(1000), y + b*dy + R.randrange(1000)])
I don't currently have a working PyGame install so I haven't been able to verify that this will draw exactly right as a polygon, but it should at least get you on the right track.

Is a top-left origin coordinate system what's stopping this equation from working?

I am trying to implement this equation to determine the centre of a circle from three user-selected points: http://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates
First, the points are acquired and assembled into a list by means of this OpenCV mouse callback function:
def setupPoints(event, x, y, flags, points):
#Populates a provided list 'points' with
#three coordinates representing the edge of
#a circle
if points[0] == 0:
points[0] = (x,y)
elif points[1] == 0:
points[1] = (x,y)
else:
points[2] = (x,y)
Then I pass the list of points to this function, which does the work:
def findCircle(p):
#Returns the circle centre
#from three provided points provided as tuples
#in a list
#See http://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates
ax = float(p[0][0])
ay = float(p[0][1])
bx = float(p[1][0])
by = float(p[1][1])
cx = float(p[2][0])
cy = float(p[2][1])
d = 2*(ax*(by-cy)+bx*(cy-ay)+cx*(ay-by))
centrex = ((pow(ax,2)+pow(ay,2))*(by-cy)+(pow(bx,2)+pow(by,2))*(cy-ay)+(pow(cx,2)+pow(cy,2))*(ay-by))/d
centrey = ((pow(ax,2)+pow(ay,2))*(cx-bx)+(pow(bx,2)+pow(by,2))*(ax-cx)+(pow(cx,2)+pow(cy,2))*(bx-ax))/d
return (int(round(centrex)), int(round(centrey)), int(round(d)))
However, it's not working. The returned numbers aren't massively off, but they are definitely incorrect. Could this be to do with the fact that the coordinate system used by OpenCV has its origin in the top-left of the image (points within the image are still positive, so it could be said to be counting 'backwards', vertically at least).
Or is that guess wrong?
More than likely it's because the operands of your division are both integers, so the result is a (floored) integer. In Python, 2/3 == 0. This will throw off your calculations a little since they won't be rounded properly. Try dividing by float(d) rather than just d.
The answer to my question regarding whether the wonky (from a maths point of view, not a computer graphics points of view) coordinate system was preventing this from working is NO.
I simply forgot to add a check in my mouse callback function to make sure the event was a click and not simply a mouse movement; my points weren't what I was clicking!
Thanks for looking at this, maybe this will help someone in future...

Categories