I'm trying to write a script in python, to automatically force the movement of the mouse pointer without the user's input (it quits through the keyboard), and experimenting with PyAutoGUI, PyUserInput and ctypes, I've been figuring out ways to move the pointer with constant speed, instead of having it teleport across the screen(I need the user to be able to see the path it makes). However, I need it to be able to perform curves, and particularly, circles, and I haven't found a way to do so with the aforementioned libraries. Does anybody know of a way to code them into making the mouse describe circles across the screen at constant speed, instead of just straight lines? Thank you beforehand for any input or help you may provide.
This is my attempt at making circle at the center of the screen of radius R - also note if I don't pass parameter duration then the mouse pointer moves to the next coordinates instantly. So for a circle divided into 360 parts you can set the pace using a modulus.
import pyautogui
import math
# Radius
R = 400
# measuring screen size
(x,y) = pyautogui.size()
# locating center of the screen
(X,Y) = pyautogui.position(x/2,y/2)
# offsetting by radius
pyautogui.moveTo(X+R,Y)
for i in range(360):
# setting pace with a modulus
if i%6==0:
pyautogui.moveTo(X+R*math.cos(math.radians(i)),Y+R*math.sin(math.radians(i)))
There is a way to do this using sin, cos, and tan. (I haven't been able to test this code yet, It might not work.)
Import math
Import pyautogui
def circle(radius = 5, accuracy = 360, xpos=0, ypos=0, speed = 5):
local y
local x
local angle
angle = 360/accuracy
local CurAngle
CurAngle = 0
x = []
y = []
sped = speed/accuracy
for i in range(accuracy):
x.append(xpos + radius*math.sin(math.radians(CurAngle)))
y.append(ypos + radius*math.cos(math.radians(CurAngle)))
CurAngle += angle
for i in len(x):
pyautogui.moveTo(x[i], y[i], duration = sped)
You put this near the top of your script, and pass arguments like this:
circle(radius, accuracy, xpos, ypos, speed)
Radius controls the width of the circle
Accuracy controls how many equi-distant points the circle is to be broken up into, setting accuracy to 4 will put 4 invisible points along the circle for the mouse to travel tom which will make a square, not a circle, 5 makes a pentagon, 6 a hexagon, etc.. the bigger the radius, the bigger you will want the accuracy
Xpos controls the x position of where the circle is centered
Ypos controls the y position of where the circle is centered
Speed controls how many seconds you want it to take to draw the circle.
Hope this helps :) Would you mind elaborating what you are wanting when you say 'curves'
Related
I'm completely new in pygame and I'm making trying to make a platformer game. I'm also planning to add multiplayer capability in it using sockets. The players are like tanks that can jump and you can move your mouse around to aim the tank's gun in your cursor's direction and shoot when you click.
What I did was I drew a rect, which is the body of the tank, and a line from the center of the rect to the cursor's position, which should be the gun of the tank. Now, the line that was drawn isn't fixed, if i move my mouse away from the tank the line's length will follow the mouse cursor. This is how it looks like:
I have no idea how to make it a fixed length, I made some kind of math illustration of the problem that might help:
Where A is the center of the tank, B is the position of the mouse, and R is the length of the gun of the tank, and C is the end point of the gun of the tank. The goal is to get C given A, B, and r, while also taking into account that the distance of A and B can be lower than the radius (because the mouse can go wherever it wants). Although im not sure if the illustration is correct, because it's possible that the mouse might enter the circle and honestly im confused beyond that..
Also one last thing, is there a best way for implementing collision? Because my solution is to just save the coordinates of each platform and and just do if statements like in this way:
if playerY > platform1Y: stop falling
if playerX > platform2X: stop moving left
if playerY < platform3Y: stop moving up
if playerX < platform4Y: stop moving right
You have to compute the direction vector from A to B. Compute the unique vector (vector with length 1) and scale it to the radius r. Add the vector to the point A. The result is the point C.
In pygame this computation is very simple. Use pygame.math.Vector2 and the operation scale_to_length() to get the vector (V) form A to C. Finally add V to C:
def point_on_circle(ax, ay, bx, by, radius):
if ax == bx and ay == by:
return ax, ay
A = pygame.math.Vector2(ax, ay)
B = pygame.math.Vector2(bx, by)
V = B - A
V.scale_to_length(radius)
C = A + V
return C.x, C.y
You have pass the center of the object to the arguments (ax, ay). You can use pygame.Rect to compute the center of the rectangle. For instance:
player_rect = pygame.Rect(playerX, playerY, playerWidth, playerHeight)
ax, ay = player_rect.center
I'm trying to draw an ellipse using Turtle module in Python, my plan is as follow:
Let the starting point be the focal point of the ellipse
Set the initial theta value to 0
Let the turtle forward, let the distance of the forwarding be a*(1-ee)/(1-emath.cos(theta))
Let it turn around and back to the original spot
Make a very small turn, update the theta value
Repeat the above process
Here's my actual code:
import turtle
import math
wn = turtle.getscreen()
wn.bgcolor("red")
My_Turtle = turtle.Turtle()
My_Turtle.penup()
My_Turtle.speed(9)
i=0
j=0
a=200
e=0.5
x_0 = 20
theta = 0
while(i<5000):
#Plotting squares
My_Turtle.penup()
ellipse = a*(1-e*e)/(1-e*math.cos(theta))
My_Turtle.forward(ellipse)
My_Turtle.pendown()
My_Turtle.forward(1)
My_Turtle.left(180)
My_Turtle.penup()
My_Turtle.forward(ellipse+1)
However, the results were really off like this:(Not the complete image but can see that it's already off)
enter image description here
Can anyone explain to me where I get it wrong ? Thank you very much!
I'm used to drawing ellipses from the center, not from one focal point so I read up on ellipse math to get my head around this. Your key formula appears to be correct:
ellipse = a*(1-e*e)/(1-e*math.cos(theta))
The issue is how you do your drawing. First you need to add setheading() to point your turtle in the correct direction. (Remember that by default it's in degrees so we need to either convert or change turtle's default). Second, how you bridge between steps in your drawing isn't sufficient.
I've reworked your code below, and have compared it to a center-based solution to confirm it generates the same ellipse:
import math
from turtle import Turtle, Screen
my_screen = Screen()
my_turtle = Turtle(visible=False)
my_turtle.speed('fastest')
my_turtle.radians()
my_turtle.penup()
e = 0.5 # linear eccentricity
a = 200 # semi major axis
c = e * a # distance from center to focal point
my_turtle.goto(-c, 0) # starting at a focal point
there = (2 * c, 0) # initialize previous point visited
step = 0.1
theta = step # already at starting point, so calculate next point
while theta < math.pi * 2 + step: # overshoot to close curve
# draw ellipse from one focus point
my_turtle.setheading(theta)
distance = a * (1 - e * e) / (1 - e * math.cos(theta))
my_turtle.forward(distance)
here = my_turtle.position()
my_turtle.pendown()
my_turtle.goto(there) # draw line from last position to this one
my_turtle.penup() # comment out to demonstrate algorithm
my_turtle.goto(-c, 0) # return to focal point
there = here
theta += step
my_screen.exitonclick()
OUTPUT
I left the pen down for this illustrution so it's obvious that it's forming the ellipse from one focal point.
Im using Pythonista 2 on my IPhone, and I'm trying to create a touch joystick. Its a simple concept, I touch somewhere on my screen, the joystick and the boundary snap to that position. Then, when I move my finger, the boundary stays still but the joystick inside moves, until i get to the edge of the boundary, then it follows on the circumference of the circle in between the center of the boundary and my finger. Here is the code:
def touch_moved(self, touch):
global r
r = 90
l = (touch.location-c[0],touch.location-c[1])
a = math.degrees(math.tan(l[1]/l[0]))
if (touch.location[0] - c[0])**2 + (touch.location[1] - c[1])**2 < r**2:
self.joystick.position = touch.location
else:
self.joystick.position = ((math.cos(a)*r)+c[0],(math.sin(a)*r)+c[1])`
But the joystick spazzes out on the circumference, so any help is appreciated.
You're using a as a parameter to sin and cos, but it came from a call to math.degrees(); trig functions always operate on radians, not degrees.
a is derived from the result of a call to tan, which should be a warning sign: tan uses an angle as input, not normally as an output. You probably want the inverse function here, atan. Actually, what you really want is math.atan2(), which takes the horizontal and vertical values as separate parameters. As your code is written, you will get a divide-by-zero error if the touch position is directly above or below the center point.
Your calculation of l seems wrong - shouldn't you be using indexes [0] and [1] with touch.location, like you're doing two lines later?
I'm currently working on a project about 3D rendering, and I'm trying to make simplistic program that can display a simple 3D room (static shading, no player movement, only rotation) with pygame
So far I've worked through the theory:
Start with a list of coordinates for the X and Z of each "Node"
Nodes are kept in an order which forms a closed loop, so that a pair of nodes will form either side of a wall
The height of the wall is determined when it is rendered, being relative to distance from the camera
Walls are rendered using painter's algorithm, so closer objects are drawn on top of further ones
For shading "fake contrast", which brightens/darkens walls based on the gradient between it's two nodes
While it seems simple enough, the process behind translating the 3D coordinates into 2D points on the screen is proving the difficult for me to understand.
Googling this topic has so far only yeilded these equations:
screenX = (worldX/worldZ)
screenY = (worldY/worldZ)
Which seem flawed to me, as you would get a divide by zero error if any Z coordinate is 0.
So if anyone could help explain this, I'd be really greatful.
Well the
screenX = (worldX/worldZ)
screenY = (worldY/worldZ)
is not the whole stuff that is just the perspective division by z and it is not meant for DOOM or Wolfenstein techniques.
Well in Doom there is only single angle of viewing (you can turn left/right but cannot look up/down only duck or jump which is not the same). So we need to know our player position and direction px,py,pz,pangle. The z is needed only if you want to implement also z axis movement/looking...
If you are looking in a straight line (Red) all the object that cross that line in the 3D are projected to single x coordinate in the player screen...
So if we are looking at some direction (red) any object/point crossing/touching this red line will be place at the center of screen (in x axis). What is left from it will be rendered on the left and similarly whats on right will be rendered on the right too...
With perspective we need to define how large viewing angle we got...
This limits our view so any point touches the green line will be projected on the edge of view (in x axis). From this we can compute screen x coordinate sx of any point (x,y,z) directly:
// angle of point relative to player direction
sx = point_ang - pangle;
if (sx<-M_PI) sx+=2.0*M_PI;
if (sx>+M_PI) sx-=2.0*M_PI;
// scale to pixels
sx = screen_size_x/2 + sx*screen_size_x/FOVx
where screen_size_x is resolution of our view area and point ang is angle of point x,y,z relative to origin px,py,pz. You can compute it like this:
point_ang = atan2(y-py,x-px)
but if you truly do a DOOM ray-casting then you already got this angle.
Now we need to compute the screen y coordinate sy which is dependent on the distance from player and wall size. We can exploit triangle similarity.
so:
sy = screen_size_y/2 (+/-) wall_height*focal_length/distance
Where focal length is the distance at which wall with 100% height will cover exactly the whole screen in y axis. As you can see we dividing by distance which might be zero. Such state must be avoided so you need to make sure your rays will be evaluated at the next cell if standing directly on cell boundary. Also we need to select the focal length so square wall will be projected as square.
Here a piece of code from mine Doom engine (putted all together):
double divide(double x,double y)
{
if ((y>=-1e-30)&&(y<=+1e-30)) return 0.0;
return x/y;
}
bool Doom3D::cell2screen(int &sx,int &sy,double x,double y,double z)
{
double a,l;
// x,y relative to player
x-=plrx;
y-=plry;
// convert z from [cell] to units
z*=_Doom3D_cell_size;
// angle -> sx
a=atan2(y,x)-plra;
if (a<-pi) a+=pi2;
if (a>+pi) a-=pi2;
sx=double(sxs2)*(1.0+(2.0*a/view_ang));
// perpendicular distance -> sy
l=sqrt((x*x)+(y*y))*cos(a);
sy=sys2+divide((double(plrz+_Doom3D_cell_size)-z-z)*wall,l);
// in front of player?
return (fabs(a)<=0.5*pi);
}
where:
_Doom3D_cell_size=100; // [units] cell cube size
view_ang=60.0*deg; // FOVx
focus=0.25; // [cells] view focal length (uncorrected)
wall=double(sxs)*(1.25+(0.288*a)+(2.04*a*a))*focus/double(_Doom3D_cell_size); // [px] projected wall size ratio size = height*wall/distance
sxs,sys = screen resolution
sxs2,sys2 = screen half resolution
pi=M_PI, pi2=2.0*M_PI
Do not forget to use perpendicular distances (multiplied by cos(a) as I did) otherwise serious fish-eye effect will occur. For more info see:
Ray Casting with different height size
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.