How to get the angle between two vectors in pygame? - python

I want my player to look towards some certain coordinates in pygame. I tried to do this by getting the angle between the position of the player and the coordinates and then rotating it the player by that angle. I used a function for this ,the function works properly when I need my player to look towards the coordinates of the mouse , but fails when i use the same function to make a zombie look towards the player.
This was the function that is used.
def rotate_char(khiladi,vector,old_center,angle=False) :
x1, y1 = vector
x2, y2 = khiladi.pos
dx, dy = x2 - x1, y2 - y1
rads = atan2(dx, dy)
degs = degrees(rads)
r_khiladi = pygame.transform.rotate(khiladi.image, degs)
R_k = r_khiladi.get_rect()
R_k.center = old_center
screen.blit(r_khiladi, (R_k))
if angle : return degs
I used another method that used numpy
v1_u = list(khiladi.pos / linalg.norm(khiladi.pos))
v2_u = list(vector / linalg.norm(vector))
v1_u[1] , v2_u[1] = -v1_u[1] , -v2_u[1]
c_angle = degrees(arccos(clip(dot(v1_u, v2_u), -1.0, 1.0)))
When I tried to print the angles they were totally off mark and didn't even showed any pattern.
Please Help

I recommend to use pygame.math.Vector2 / angle_to() to compute the angle between 2 vectors in degrees.
If (x1, y1) and (x2, y2) are 2 vectors, then the angle between the vectors is:
v1 = pygame.math.Vector2(x1, y1)
v2 = pygame.math.Vector2(x2, y2)
angle_v1_v2_degree = v1.angle_to(v2)
In general The dot product of 2 vectors is equal the cosine of the angle between the 2 vectors multiplied by the magnitude (length) of both vectors.
dot( A, B ) == | A | * | B | * cos( angle_A_B )
This follows, that the dot product of 2 unit vectors is equal the cosine of the angle between the 2 vectors, because the length of a unit vector is 1.
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
Hence the angle between 2 vectors can be computed by math.acos of the dot product of the 2 normalized vectors (normalize()):
v1 = pygame.math.Vector2(x1, y1)
v2 = pygame.math.Vector2(x2, y2)
v1u_dot_v2u = v1.normalize().dot(v2.normalize())
angle_v1_v2_radian = math.acos(v1u_dot_v2u)

Related

Mathematical calculation in python abs function

I am trying to create the equation in python.
Sorry in advance if this has already been asked! If so, I couldn't find it, so please share the post!
I run into the problem that I don't know how to code the part in the red square (see equation ).
As I understand it the "|u1|" stands for the absolute value of u1. However, if I code it like the equation is written i.e. abs(u1)abs(u2) I get a syntax error (which I kind of expected).
My problem is the line of code:
angle = np.arccos((Mu1*Mu2)/(abs(Mu1)abs(Mu2)))
My complete code is:
import numpy as np
from math import sqrt
#first direction vector
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
#punt 2, RS
R = (2,3)
S = (4,1)
#direction vector = arctan(yq-yp/xq-xp)
#create function to calc direction vector of line
def dirvec(coord1, coord2):
#pull coordinates into x and y variables
x1 , y1 = coord1[0], coord1[1]
x2 , y2 = coord2[0], coord2[1]
#calc vector see article
v = np.arctan((y2-y1)/(x2-x1))
#outputs in radians, not degrees
v = np.degrees(v)
return v
print(dirvec(P,Q))
print(dirvec(R,S))
Mu1 = dirvec(P,Q)
Mu2 = dirvec(R,S)
angle = np.arccos((Mu1*Mu2)/(abs(Mu1)abs(Mu2)))
print(angle)
Thins I tried:
multiply the two abs, but then I'll get the same number (pi) every time:
np.arccos((Mu1*Mu2)/(abs(Mu1)*abs(Mu2)))
+ and - but I cannot imagine these are correct:
np.arccos((Mu1Mu2)/(abs(Mu1)+abs(Mu2))) np.arccos((Mu1Mu2)/(abs(Mu1)-abs(Mu2)))
In the formula, the numerator is the dot product of two vectors, and the denominator is the product of the norms of the two vectors.
Here is a simple way to write your formula:
import math
def dot_product(u, v):
(x1, y1) = u
(x2, y2) = v
return x1 * x2 + y1 * y2
def norm(u):
(x, y) = u
return math.sqrt(x * x + y * y)
def get_angle(u, v):
return math.acos( dot_product(u,v) / (norm(u) * norm(v)) )
def make_vector(p, q):
(x1, y1) = p
(x2, y2) = q
return (x2 - x1, y2 - y1)
#first direction vector
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
#punt 2, RS
R = (2,3)
S = (4,1)
angle = get_angle(make_vector(p,q), make_vector(r,s))
print(angle)
From what I see, the result of your code would always be pi or 0. It will be pi if one of the mu1 or mu2 is negative and when both are negative or positive it will be zero.
If I remember vectors properly :
Given two vectors P and Q, with say P = (x, y) and Q = (a, b)
Then abs(P) = sqrt(x^2 + y^2) and P. Q = xa+yb. So that cos# = P. Q/(abs(P) *abs(Q)). If am not clear you can give an example of what you intend to do
Okay so apparently I made a mistake in my interpretation.
I want to thank everyone for your solutions!
After some puzzling it appears that:
import math
import numpy as np
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
x1 = P[0]
x2 = Q[0]
y1 = P[1]
y2 = Q[1]
#punt 2, RS
R = (0,2)
S = (4,1)
x3 = R[0]
x4 = S[0]
y3 = R[1]
y4 = S[1]
angle = np.arccos(((x2 - x1) * (x4 - x3) + (y2 - y1) * (y4 - y3)) / (math.sqrt((x2 - x1)**2 + (y2 - y1)**2) * math.sqrt((x4 - x3)**2 + (y4 - y3)**2)))
print(angle)
Is the correct way to calculate the angle between two vectors.
This is obviously not pretty code, but it is the essence of how it works!
Again I want to thank you all for you reaction and solutions!

python - arc direction

I'm using a python caller in fme to create polygons from points with aixm 4.5 data
Somes of the polygons contains arcs, and theirs direction clockwise (CWA) or anti-clock wise (CCA) matters, I don't know how to handle this.
here's the code I have so far:
import fme
import fmeobjects
from math import hypot
def replaceWithArc(feature):
coords = feature.getAllCoordinates()
x0, y0 = coords[0][0], coords[0][1] # coordinates of start of arc
xc, yc = coords[1][0], coords[1][1] # coordinates of cetner of arc
x2, y2 = coords[2][0], coords[2][1] # coordinates of end of arc
vx0, vy0 = (x0 - xc), (y0 - yc) # vector: center -> start
vx2, vy2 = (x2 - xc), (y2 - yc) # vector: center -> end
vx1, vy1 = (vx0 + vx2), (vy0 + vy2) # vector: center -> middle
len = hypot(vx1, vy1) # length of the vector
radius = (hypot(vx0, vy0) + hypot(vx2, vy2)) * 0.5
x1, y1 = xc + vx1 / len * radius, yc + vy1 / len * radius # coordinates of middle point on arc
threePoints = (
fmeobjects.FMEPoint(x0, y0),
fmeobjects.FMEPoint(x1, y1),
fmeobjects.FMEPoint(x2, y2)
)
feature.setGeometry(fmeobjects.FMEArc(threePoints))
This looks to me like there is something wrong with the three points.
Could you please paste the values?
From the image above it looks slightly asymmetric, but I could be wrong.
Another thing that you could try is to use a different function to initialize FMEArc, e.g.
init(twoPoints, bulge)
init(centerPoint, rotation, primaryRadius, secondaryRadius, startAngle, sweepAngle, startPoint, endPoint)

Correct way to calculate triangle area from 3 vertices

Is this the correct way to calculate the area of a triangle given the 3 triangle points/vertices? The vertices will never be negative values.
def triangle_area(tri):
x1, y1, x2, y2, x3, y3 = tri[0][0], tri[0][1], tri[1][0], tri[1][1], tri[2][0], tri[2][1]
return 0.5 * (((x2-x1)*(y3-y1))-((x3-x1)*(y2-y1)))
It is necessary to add abs to this formula to avoid negative area value (sign depends on orientation, not on positive/negative coordinates)
Yes, this formula is correct and it implements the best approach if you have vertices coordinates. It is based on cross product properties.
def triangle_area(tri):
x1, y1, x2, y2, x3, y3 = tri[0][0], tri[0][1], tri[1][0], tri[1][1], tri[2][0], tri[2][1]
return abs(0.5 * (((x2-x1)*(y3-y1))-((x3-x1)*(y2-y1))))
Almost, but you need to take the absolute value at the end.
You're formula can be derived from the Shoelace formula which applies to any simple (no crossing edges, no holes) polygon.
If you want to calculate a triangles area, you can calculate the surrounding rectangles area and substract the 3 triangles which have 90° angles around it:
def triangle_area(tri):
x_min = min([point[0] for point in tri])
x_max = max([point[0] for point in tri])
y_min = min([point[1] for point in tri])
y_max = max([point[1] for point in tri])
area_rectangle = (y_max - y_min) * (x_max - x_min)
t1 = 0.5 * abs((tri[0][0] - tri[1][0]) * (tri[0][1] - tri[1][1]))
t2 = 0.5 * abs((tri[0][0] - tri[2][0]) * (tri[0][1] - tri[2][1]))
t3 = 0.5 * abs((tri[1][0] - tri[2][0]) * (tri[1][1] - tri[2][1]))
return area_rectangle - t1 - t2 - t3
Alternatively, you can use Herons Formula

Setting diagonal mirroring via Jython (user to set points)

Trying to get my my head around this program we need to create
What is needed is as per the notes:
create a function named
arbitraryMirror() that allows the user to place a mirror at an arbitrary angle, causing an intersect and therefore mirror the image.
This will need to be done on either a square or rectangle picture.
As per the pics below, this is the Output of what is required.
Output
I know how to mirror a pic (as shown below) with a square image, but i cannot work out if this can also be done with a rectangle image?
Cross
I had a look at a method of using y=mx+b but it seems overcomplicated?
Maybe there is some coordinate geometry i need? Or algebra?
Any help would be greatly appreciated!
The key formulas are (python):
# (x0, y0) and (x1, y1) are two points on the mirroring line
# dx, dy, L is the vector and lenght
dx, dy = x1 - x0, y1 - y0
L = (dx**2 + dy**2) ** 0.5
# Tangent (tx, ty) and normal (nx, ny) basis unit vectors
tx, ty = dx / L, dy / L
nx, ny = -dy / L, dx / L
# For each pixel
for y in range(h):
for x in range(w):
# Map to tangent/normal space
n = (x+0.5 - x0)*nx + (y+0.5 - y0)*ny
t = (x+0.5 - x0)*tx + (y+0.5 - y0)*ty
# If we're in the positive half-space
if n >= 0:
# Compute mirrored point in XY space
# (negate the normal component)
xx = int(x0 + t*tx - n*nx + 0.5)
yy = int(y0 + t*ty - n*ny + 0.5)
# If valid copy to destination
if 0 <= xx < w and 0 <= yy < h:
img[y][x] = img[yy][xx]
Here you can see an example of the results
The top-left red corner are pixels that would be mirroring pixels outside of the original image and they're left untouched by the above code.

Rotate line around center point given two vertices

I've been trying to rotate a bunch of lines by 90 degrees (that together form a polyline). Each line contains two vertices, say (x1, y1) and (x2, y2). What I'm currently trying to do is rotate around the center point of the line, given center points |x1 - x2| and |y1 - y2|. For some reason (I'm not very mathematically savvy) I can't get the lines to rotate correctly.
Could someone verify that the math here is correct? I'm thinking that it could be correct, however, when I set the line's vertices to the new rotated vertices, the next line may not be grabbing the new (x2, y2) vertex from the previous line, causing the lines to rotate incorrectly.
Here's what I've written:
def rotate_lines(self, deg=-90):
# Convert from degrees to radians
theta = math.radians(deg)
for pl in self.polylines:
self.curr_pl = pl
for line in pl.lines:
# Get the vertices of the line
# (px, py) = first vertex
# (ox, oy) = second vertex
px, ox = line.get_xdata()
py, oy = line.get_ydata()
# Get the center of the line
cx = math.fabs(px-ox)
cy = math.fabs(py-oy)
# Rotate line around center point
p1x = cx - ((px-cx) * math.cos(theta)) - ((py-cy) * math.sin(theta))
p1y = cy - ((px-cx) * math.sin(theta)) + ((py-cy) * math.cos(theta))
p2x = cx - ((ox-cx) * math.cos(theta)) - ((oy-cy) * math.sin(theta))
p2y = cy - ((ox-cx) * math.sin(theta)) + ((oy-cy) * math.cos(theta))
self.curr_pl.set_line(line, [p1x, p2x], [p1y, p2y])
The coordinates of the center point (cx,cy) of a line segment between points (x1,y1) and (x2,y2) are:
cx = (x1 + x2) / 2
cy = (y1 + y2) / 2
In other words it's just the average, or arithmetic mean, of the two pairs of x and y coordinate values.
For a multi-segmented line, or polyline, its logical center point's x and y coordinates are just the corresponding average of x and y values of all the points. An average is just the sum of the values divided by the number of them.
The general formulas to rotate a 2D point (x,y) θ radians around the origin (0,0) are:
x′ = x * cos(θ) - y * sin(θ)
y′ = x * sin(θ) + y * cos(θ)
To perform a rotation about a different center (cx, cy), the x and y values of the point need to be adjusted by first subtracting the coordinate of the desired center of rotation from the point's coordinate, which has the effect of moving (known in geometry as translating) it is expressed mathematically like this:
tx = x - cx
ty = y - cy
then rotating this intermediate point by the angle desired, and finally adding the x and y values of the point of rotation back to the x and y of each coordinate. In geometric terms, it's the following sequence of operations:  Tʀᴀɴsʟᴀᴛᴇ ─► Rᴏᴛᴀᴛᴇ ─► Uɴᴛʀᴀɴsʟᴀᴛᴇ.
This concept can be extended to allow rotating a whole polyline about any arbitrary point—such as its own logical center—by just applying the math described to each point of each line segment within it.
To simplify implementation of this computation, the numerical result of all three sets of calculations can be combined and expressed with a pair of mathematical formulas which perform them all simultaneously. So a new point (x′,y′) can be obtained by rotating an existing point (x,y), θ radians around the point (cx, cy) by using:
x′ = ( (x - cx) * cos(θ) + (y - cy) * sin(θ) ) + cx
y′ = ( -(x - cx) * sin(θ) + (y - cy) * cos(θ) ) + cy
Incorporating this mathematical/geometrical concept into your function produces the following:
from math import sin, cos, radians
def rotate_lines(self, deg=-90):
""" Rotate self.polylines the given angle about their centers. """
theta = radians(deg) # Convert angle from degrees to radians
cosang, sinang = cos(theta), sin(theta)
for pl in self.polylines:
# Find logical center (avg x and avg y) of entire polyline
n = len(pl.lines)*2 # Total number of points in polyline
cx = sum(sum(line.get_xdata()) for line in pl.lines) / n
cy = sum(sum(line.get_ydata()) for line in pl.lines) / n
for line in pl.lines:
# Retrieve vertices of the line
x1, x2 = line.get_xdata()
y1, y2 = line.get_ydata()
# Rotate each around whole polyline's center point
tx1, ty1 = x1-cx, y1-cy
p1x = ( tx1*cosang + ty1*sinang) + cx
p1y = (-tx1*sinang + ty1*cosang) + cy
tx2, ty2 = x2-cx, y2-cy
p2x = ( tx2*cosang + ty2*sinang) + cx
p2y = (-tx2*sinang + ty2*cosang) + cy
# Replace vertices with updated values
pl.set_line(line, [p1x, p2x], [p1y, p2y])
Your center point is going to be:
centerX = (x2 - x1) / 2 + x1
centerY = (y2 - y1) / 2 + y1
because you take half the length (x2 - x1) / 2 and add it to where your line starts to get to the middle.
As an exercise, take two lines:
line1 = (0, 0) -> (5, 5)
then: |x1 - x2| = 5, when the center x value is at 2.5.
line2 = (2, 2) -> (7, 7)
then: |x1 - x2| = 5, which can't be right because that's the center for
the line that's parallel to it but shifted downwards and to the left

Categories