I am working on a game where the player has to collect coins by landing on chests. However, if they land on the same chest three times, it will delete the chest that is there and will insert a bandit in its place - which will reset their coins back to zero (this part is not included in the code provided as it runs correctly).
try:
import tkinter as tk
from tkinter import ttk
except ImportError:
import Tkinter as tk
from Tkinter import ttk
import random
from collections import Counter
CELL_WIDTH = 50
CELL_HEIGHT = 50
rows = 8
columns = 8
bandit_number = 5
chest_number = 10
moves = 0
coins = 0
chests_visited = ()
def create_grid():
game_frame.pack()
game_grid.pack()
data = {}
for col in range(columns):
for row in range(rows):
x1 = col * CELL_WIDTH
y1 = row * CELL_HEIGHT
x2 = x1 + CELL_WIDTH
y2 = y1 + CELL_HEIGHT
data[row, col] = game_grid.create_rectangle(x1, y1, x2, y2,
fill="green",
tags="rect")
return data
def create_chests(chest_image):
global chest_dict
chest_dict = {}
for i in range(chest_number):
while True:
row = random.randint(0, rows-1)
col = random.randint(0, columns-1)
if (row,col) not in chest_dict:
break
x1 = col * CELL_WIDTH + 24
y1 = row * CELL_HEIGHT - 26
x2 = x1 + CELL_WIDTH
y2 = y1 + CELL_HEIGHT
chest_dict[row,col] = game_grid.create_image(x1, y1, image=chest_image,
tags="chest")
return chest_dict
def create_bandits(bandit_image):
global bandit_dict
bandit_dict = {}
for i in range(bandit_number):
while True:
row = random.randint(0, rows-1)
col = random.randint(0, columns-1)
if (row,col) not in bandit_dict:
break
x = col * CELL_WIDTH + 22
y = row * CELL_HEIGHT - 22
x2 = x + CELL_WIDTH
y2 = y + CELL_HEIGHT
bandit_dict[row,col] = game_grid.create_image(x, y, image=bandit_image)
return bandit_dict
def position_player(player_image):
global arrow
arrow = game_grid.create_image(26, 375, image=player_image)
display_widgets()
return arrow
def display_widgets():
global move_entry_x, move_entry_y, help_lbl
help_lbl = tk.Label(game_grid, text="Enter the x value in the first entry"+
" and the y value in the second." + '\n' +
"Use negatives to move left and down.")
game_grid.create_window(200, 420, window=help_lbl)
move_entry_x = tk.Entry(game_grid)
game_grid.create_window(70, 450, window=move_entry_x)
move_entry_y = tk.Entry(game_grid)
game_grid.create_window(200, 450, window=move_entry_y)
enter_btn = ttk.Button(game_grid, text="Enter", command=check_move)
game_grid.create_window(305, 450, window=enter_btn)
def check_move():
global help_lbl
if (
move_entry_x.get()[0] == "-" or
move_entry_y.get()[0] == "-"
):
try:
if (
int(move_entry_x.get()[1])*CELL_WIDTH < 26 or
int(move_entry_x.get()[1])*CELL_WIDTH > int(rows)*CELL_WIDTH
):
print("Illegal move! Enter a different value")
elif (
int(move_entry_y.get()[1])*CELL_WIDTH < 26 or
int(move_entry_y.get()[1])*CELL_WIDTH > int(rows)*CELL_HEIGHT
):
print("Illegal move! Enter a different value")
else:
move_player(arrow)
except ValueError:
print("Please enter a number!")
else:
try:
if (
int(move_entry_x.get())*CELL_WIDTH < 26 or
int(move_entry_x.get())*CELL_WIDTH > int(rows)*CELL_WIDTH
):
print("Illegal move! Enter a different value")
elif (
int(move_entry_y.get())*CELL_WIDTH < 26 or
int(move_entry_y.get())*CELL_WIDTH > int(rows)*CELL_HEIGHT
):
print("Illegal move! Enter a different value")
else:
move_player(arrow)
except ValueError:
print("Please enter a number!")
def move_player(arrow):
global move_entry_x, move_entry_y, help_lbl, moves
x_move = move_entry_x.get()
y_move = move_entry_y.get()
x = int(x_move)*CELL_WIDTH
y = int(y_move)*CELL_HEIGHT
game_grid.move(arrow, x, -y)
moves += 1
print("Moves = "+str(moves))
check_position(arrow, chest_dict)
def check_position(arrow, chest_dict):
global coins, arrow_coords, chests_visited
arrow_coords = game_grid.coords(arrow)
for i in chest_dict:
chest_coords = game_grid.coords(chest_dict[i])
if (
int(arrow_coords[0])-2 in chest_coords and
int(arrow_coords[1])-1 in chest_coords
):
coins += 10
chests_visited += tuple(arrow_coords)
print("Chests visited: "+str(chests_visited))
check_chests()
return arrow, chest_dict
def check_chests():
global chests_visited, chest_dict, bandit_dict
cnt = Counter(chests_visited)
if (
[k for k, v in cnt.items() if v == 3]
):
game_grid.create_image(arrow_coords[0],arrow_coords[1],
image=bandit_image)
print("bandit_time")
window = tk.Tk()
game_frame = tk.Frame(window)
game_grid = tk.Canvas(game_frame, width=500, height=500, borderwidth=0,
highlightthickness=0)
game_grid.itemconfig("rect", fill="green")
bandit_image = tk.PhotoImage(file="Bandit.png")
chest_image = tk.PhotoImage(file="Treasure Chest.png")
player_image = tk.PhotoImage(file="Arrow.png")
rects = create_grid()
bandits = create_bandits(bandit_image)
chests = create_chests(chest_image)
player = position_player(player_image)
window.mainloop()
I know you can use canvas.delete(item_id) to remove an object, given that it has been defined but my problem is that since I created my objects with a dictionary, they do not have specific names which I could use and I would like to know how I can delete an object from a canvas based on what it's coordinates are rather than it's name.
Also, as a side note, since I am using images, according to answers I have found on , the format has to be GIF, but I am able to use the PNG format and it still works fine but when I try my game on a different device, I get the expected error. Is there a reason for this?
With the help of furas, I have found a solution:
Since each chest has its own unique row and column, you can refer to an individual chest using its row and column, meaning we must find out what that row and column could be.
Because x1 = col * CELL_WIDTH + 24 and y1 = row * CELL_HEIGHT - 26 where x1 is the x coordinate and y1 is the y coordinate, the equations for the row and column must be (x1 - 24) / CELL_WIDTH = col and (y1 + 26) / CELL_HEIGHT = row.
You would then substitute for the x and y coordinates using arrow_coords[0] and arrow_coords[1]. And to get the specific row and column from the chest_dict, you would then round it to the nearest whole number and it will delete that specific chest from the canvas:
game_grid.delete(chest_dict[int(round(int(arrow_coords[0]-24)/50, 0)),
int(round(int(arrow_coords[1]+26)/50, 0))])
Related
I am trying to use nvprof to monitor the performance of the GPU. I would like to know the time consuming of HtoD(host to device), DtoH(device to host) and device execution.
It worked very well with a standard code from numba cuda website:
from numba import cuda
#cuda.jit
def add_kernel(x, y, out):
tx = cuda.threadIdx.x # this is the unique thread ID within a 1D block
ty = cuda.blockIdx.x # Similarly, this is the unique block ID within the 1D grid
block_size = cuda.blockDim.x # number of threads per block
grid_size = cuda.gridDim.x # number of blocks in the grid
start = tx + ty * block_size
stride = block_size * grid_size
# assuming x and y inputs are same length
for i in range(start, x.shape[0], stride):
out[i] = x[i] + y[i]
if __name__ == "__main__":
import numpy as np
n = 100000
x = np.arange(n).astype(np.float32)
y = 2 * x
out = np.empty_like(x)
threads_per_block = 128
blocks_per_grid = 30
add_kernel[blocks_per_grid, threads_per_block](x, y, out)
print(out[:10])
Here is the out come from nvprfo:
However, when I add the usage of multiprocessing with the following code:
import multiprocessing as mp
from numba import cuda
def fun():
#cuda.jit
def add_kernel(x, y, out):
tx = cuda.threadIdx.x # this is the unique thread ID within a 1D block
ty = cuda.blockIdx.x # Similarly, this is the unique block ID within the 1D grid
block_size = cuda.blockDim.x # number of threads per block
grid_size = cuda.gridDim.x # number of blocks in the grid
start = tx + ty * block_size
stride = block_size * grid_size
# assuming x and y inputs are same length
for i in range(start, x.shape[0], stride):
out[i] = x[i] + y[i]
import numpy as np
n = 100000
x = np.arange(n).astype(np.float32)
y = 2 * x
out = np.empty_like(x)
threads_per_block = 128
blocks_per_grid = 30
add_kernel[blocks_per_grid, threads_per_block](x, y, out)
print(out[:10])
return out
# check gpu condition
p = mp.Process(target = fun)
p.daemon = True
p.start()
p.join()
nvprof seems to monitor the process but it doesn't outcome anything though it reports that nvprof is profiling:
Furthermore, when I used Ray (a package for doing distributed computation):
if __name__ == "__main__":
import multiprocessing
def fun():
from numba import cuda
import ray
#ray.remote(num_gpus=1)
def call_ray():
#cuda.jit
def add_kernel(x, y, out):
tx = cuda.threadIdx.x # this is the unique thread ID within a 1D block
ty = cuda.blockIdx.x # Similarly, this is the unique block ID within the 1D grid
block_size = cuda.blockDim.x # number of threads per block
grid_size = cuda.gridDim.x # number of blocks in the grid
start = tx + ty * block_size
stride = block_size * grid_size
# assuming x and y inputs are same length
for i in range(start, x.shape[0], stride):
out[i] = x[i] + y[i]
import numpy as np
n = 100000
x = np.arange(n).astype(np.float32)
y = 2 * x
out = np.empty_like(x)
threads_per_block = 128
blocks_per_grid = 30
add_kernel[blocks_per_grid, threads_per_block](x, y, out)
print(out[:10])
return out
ray.shutdown()
ray.init(redis_address = "***")
out = ray.get(call_ray.remote())
# check gpu condition
p = multiprocessing.Process(target = fun)
p.daemon = True
p.start()
p.join()
nvprof doesn't show anything! It even doesn't show the line telling that nvprof is profiling the process (but the code is indeed executed):
Does anyone know how to figure this out? Or do I have any other choices to acquire these data for distributed computation?
https://en.wikipedia.org/wiki/Superellipse
I have read the SO questions on how to point-pick from a circle and an ellipse.
How would one uniformly select random points from the interior of a super-ellipse?
More generally, how would one uniformly select random points from the interior of the curve described by an arbitrary super-formula?
https://en.wikipedia.org/wiki/Superformula
The discarding method is not considered a solution, as it is mathematically unenlightening.
In order to sample the superellipse, let's assume without loss of generality that a = b = 1. The general case can be then obtained by rescaling the corresponding axis.
The points in the first quadrant (positive x-coordinate and positive y-coordinate) can be then parametrized as:
x = r * ( cos(t) )^(2/n)
y = r * ( sin(t) )^(2/n)
with 0 <= r <= 1 and 0 <= t <= pi/2:
Now, we need to sample in r, t so that the sampling transformed into x, y is uniform. To this end, let's calculate the Jacobian of this transform:
dx*dy = (2/n) * r * (sin(2*t)/2)^(2/n - 1) dr*dt
= (1/n) * d(r^2) * d(f(t))
Here, we see that as for the variable r, it is sufficient to sample uniformly the value of r^2 and then transform back with a square root. The dependency on t is a bit more complicated. However, with some effort, one gets
f(t) = -(n/2) * 2F1(1/n, (n-1)/n, 1 + 1/n, cos(t)^2) * cos(t)^(2/n)
where 2F1 is the hypergeometric function.
In order to obtain uniform sampling in x,y, we need now to sample uniformly the range of f(t) for t in [0, pi/2] and then find the t which corresponds to this sampled value, i.e., to solve for t the equation u = f(t) where u is a uniform random variable sampled from [f(0), f(pi/2)]. This is essentially the same method as for r, nevertheless in that case one can calculate the inverse directly.
One small issue with this approach is that the function f is not that well-behaved near zero - the infinite slope makes it quite challenging to find a root of u = f(t). To circumvent this, we can sample only the "upper part" of the first quadrant (i.e., area between lines x=y and x=0) and then obtain all the other points by symmetry (not only in the first quadrant but also for all the other ones).
An implementation of this method in Python could look like:
import numpy as np
from numpy.random import uniform, randint, seed
from scipy.optimize import brenth, ridder, bisect, newton
from scipy.special import gamma, hyp2f1
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
seed(100)
def superellipse_area(n):
#https://en.wikipedia.org/wiki/Superellipse#Mathematical_properties
inv_n = 1. / n
return 4 * ( gamma(1 + inv_n)**2 ) / gamma(1 + 2*inv_n)
def sample_superellipse(n, num_of_points = 2000):
def f(n, x):
inv_n = 1. / n
return -(n/2)*hyp2f1(inv_n, 1 - inv_n, 1 + inv_n, x)*(x**inv_n)
lb = f(n, 0.5)
ub = f(n, 0.0)
points = [None for idx in range(num_of_points)]
for idx in range(num_of_points):
r = np.sqrt(uniform())
v = uniform(lb, ub)
w = bisect(lambda w: f(n, w**n) - v, 0.0, 0.5**(1/n))
z = w**n
x = r * z**(1/n)
y = r * (1 - z)**(1/n)
if uniform(-1, 1) < 0:
y, x = x, y
x = (2*randint(0, 2) - 1)*x
y = (2*randint(0, 2) - 1)*y
points[idx] = [x, y]
return points
def plot_superellipse(ax, n, points):
coords_x = [p[0] for p in points]
coords_y = [p[1] for p in points]
ax.set_xlim(-1.25, 1.25)
ax.set_ylim(-1.25, 1.25)
ax.text(-1.1, 1, '{n:.1f}'.format(n = n), fontsize = 12)
ax.scatter(coords_x, coords_y, s = 0.6)
params = np.array([[0.5, 1], [2, 4]])
fig = plt.figure(figsize = (6, 6))
gs = gridspec.GridSpec(*params.shape, wspace = 1/32., hspace = 1/32.)
n_rows, n_cols = params.shape
for i in range(n_rows):
for j in range(n_cols):
n = params[i, j]
ax = plt.subplot(gs[i, j])
if i == n_rows-1:
ax.set_xticks([-1, 0, 1])
else:
ax.set_xticks([])
if j == 0:
ax.set_yticks([-1, 0, 1])
else:
ax.set_yticks([])
#ensure that the ellipses have similar point density
num_of_points = int(superellipse_area(n) / superellipse_area(2) * 4000)
points = sample_superellipse(n, num_of_points)
plot_superellipse(ax, n, points)
fig.savefig('fig.png')
This produces:
i have a setup like this:
2 coordinate systems. (x,y) is the main coordinate system and (x',y') is a coordinate system that lives inside (x,y). The system (x',y') is defined by the points x1 or x2 and if i move these 2 points around then (x',y') moves accordingly. The origin of (x',y') is defined as the middle of the vector going from x1 to x2, and the y' axis is the normal vector on x1->x2 going through the origin. If i have a point x3 defined in (x',y') and i move either of x1 or x2 to make the origin shift place, how do i then move x3 accordingly such that it maintains its position in the new (x',y') ?
And how do i make a transformation which always converts a point in (x,y) to a point in (x',y') nomatter how x1 and x2 have been set?
I was thinking that if i had more points than just the one i am moving (x1 or x2) i guess i could try to estimate theta, tx, ty of the transformation
[x2'] [cos(theta) , sin(theta), tx][x2]
[y2'] = [-sin(theta), cos(theta), ty][y2]
[ 1 ] [ 0 , 0 , 1 ][1 ]
and just apply that estimated transformation to x3 and i would be good...mmm but i think i would need 3 points in order to estimate theta, tx and ty right?
I mean i could estimate using some least squares approach...but 3 unknowns requires 3 coordinate sets right?
I tried to implement this and calculate an example. I hope you understand the syntax. Its not really giving me what i expect:
import math
import numpy as np
x1=[ 0,10]
x2=[10,20]
rx = x2[0] - x1[0]
ry = x2[1] - x1[1]
rlen = math.sqrt(rx*rx+ry*ry)
c = rx / rlen
s = ry / rlen
dx = - ( x1[0] + x2[0] )/2 # changing the sign to be negative seems to
dy = - ( x1[1] + x2[1] )/2 # rectify translation. Rotation still is wrong
M = np.array([[c, -s, 0],[s, c, 0],[dx, dy, 1]])
print( np.dot(x2 + [1],M) )
# Yields -> [ 15.92031022 -8.63603897 1. ] and should yield [5,0,1]
Since I am trying to transform the x2 coordinate, should the result then not have the value 0 in the y-component since its located in the x-axis?
Ok, I tried doing the implementation for x3 from dynamic1 to dynamic2 which the check is that x3 should end up with the same coordinate in both d1 and d2. I did that as you suggested, but I do not get the same coordinate in both d1 and d2. Did i misunderstand something?
import math
import numpy as np
x1=[ 1,1]
x2=[ 7,9]
x3=[4,3]
rx = (x2[0] - x1[0])
ry = (x2[1] - x1[1])
rlen = math.sqrt( rx*rx + ry*ry )
c = rx / rlen
s = ry / rlen
dx = ( x1[0] + x2[0] )/2
dy = ( x1[1] + x2[1] )/2
M = np.array([[c, -s, 0],[s, c, 0],[-dx*c-dy*s, dx*s-dy*c, 1]])
Minv = np.array([[c, s, 0],[-s, c, 0],[dx, dy, 1]])
x1new=[ 1,1]
x2new=[ 17,4]
rxnew = (x2new[0] - x1new[0])
rynew = (x2new[1] - x1new[1])
rlennew = math.sqrt( rxnew*rxnew + rynew*rynew )
cnew = rxnew / rlennew
snew = rynew / rlennew
dxnew = ( x1new[0] + x2new[0] )/2
dynew = ( x1new[1] + x2new[1] )/2
Mnew = np.array([[cnew, -snew, 0],[snew, cnew, 0],[-dxnew*cnew-dynew*snew, dxnew*snew-dynew*cnew, 1]])
Mnewinv = np.array([[cnew, snew, 0],[-snew, cnew, 0],[dxnew, dynew, 1]])
M_dyn1_to_dyn2 = np.dot(Minv,Mnew)
print( np.dot(x3 + [1], M) )
print( np.dot(x3 + [1], M_dyn1_to_dyn2))
#yields these 2 outputs which should be the same:
[-1.6 -1.2 1. ]
[-3.53219692 8.29298408 1. ]
Edit. Matrix correction.
To translate coordinates from static system to (x1,x2) defined one, you have to apply affine transformation.
Matrix of this transformation M consists of shift matrix S and rotation about origin R.
Matrix M is combination of S and R:
c -s 0
M = s c 0
-dx*c-dy*s dx*s-dy*c 1
Here c and s are cosine and sine of rotation angle, their values are respectively x- and y- components of unit (normalized) vector x1x2.
rx = x2.x - x1.x
ry = x2.y - x1.y
len = Sqrt(rx*rx+ry*ry)
c = rx / Len
s = ry / Len
And shift components:
dx = (x1.x + x2.x)/2
dy = (x1.y + x2.y)/2
To translate (xx,yy) coordinates from static system to rotate one, we have to find
xx' = xx*c+yy*s-dx*c-dy*s = c*(xx-dx) + s*(yy-dy)
yy' = -xx*s+yy*c+dx*s-dy*c = -s*(xx-dx) + c*(yy-dy)
Quick check:
X1 = (1,1)
X2 = (7,9)
dx = 4
dy = 5
rx = 6
ry = 8
Len = 10
c = 0.6
s = 0.8
for point (4,5):
xx-dx = 0
yy-dy = 0
xx',yy' = (0, 0) - right
for point X2 =(7,9):
xx-dx = 3
yy-dy = 4
xx' = 0.6*3 + 0.8*4 = 5 -right
yy' = -0.8*3 + 0.6*4 = 0 -right
P.S. Note that matrix to transform dyn.coordinates to static ones is inverse of M and it is simpler:
c s 0
M' = -s c 0
dx dy 1
P.P.S. You need three pairs of corresponding points to define general affine transformations. It seems here you don't need scaling and sheer, so you may determine needed transform with your x1,x2 points
I think you need double dimension array to save and set your value in that
the structure gonna be like this
=============|========|========|
index number |x |y |
=============|========|========|
first point | [0][0] | [0][1] |
second point | [1][0] | [1][1] |
third point | [2][0] | [2][1] |
=============|========|========|
I will use java in my answer
//declare the double dimension array
double matrix[][] = new double[3][2];
//setting location first point, x
matrix[0][0] = 1;
//setting location first point, y
matrix[0][1] = 1;
//fill with your formula, i only give example
//fill second point with first point and plus 1
//setting location second point, x
matrix[1][0] = matrix[0][0] + 1;
//setting location second point, y
matrix[1][1] = matrix[0][1] + 1;
//fill with your formula, i only give example
//fill third point with second point and plus 1
//setting location third point, x
matrix[2][0] = matrix[1][0] + 1;
//setting location third point, y
matrix[2][1] = matrix[1][1] + 1;
I understand that:
atan2(vector.y, vector.x) = the angle between the vector and the X axis.
But I wanted to know how to get the angle between two vectors using atan2. So I came across this solution:
atan2(vector1.y - vector2.y, vector1.x - vector2.x)
My question is very simple:
Will the two following formulas produce the same number?
atan2(vector1.y - vector2.y, vector1.x - vector2.x)
atan2(vector2.y - vector1.y, vector2.x - vector1.x)
If not: How do I know what vector comes first in the subtractions?
atan2(vector1.y - vector2.y, vector1.x - vector2.x)
is the angle between the difference vector (connecting vector2 and vector1) and the x-axis,
which is problably not what you meant.
The (directed) angle from vector1 to vector2 can be computed as
angle = atan2(vector2.y, vector2.x) - atan2(vector1.y, vector1.x);
and you may want to normalize it to the range [0, 2 π):
if (angle < 0) { angle += 2 * M_PI; }
or to the range (-π, π]:
if (angle > M_PI) { angle -= 2 * M_PI; }
else if (angle <= -M_PI) { angle += 2 * M_PI; }
A robust way to do it is by finding the sine of the angle using the cross product, and the cosine of the angle using the dot product and combining the two with the Atan2() function.
In C# this is:
public struct Vector2
{
public double X, Y;
/// <summary>
/// Returns the angle between two vectos
/// </summary>
public static double GetAngle(Vector2 A, Vector2 B)
{
// |A·B| = |A| |B| COS(θ)
// |A×B| = |A| |B| SIN(θ)
return Math.Atan2(Cross(A,B), Dot(A,B));
}
public double Magnitude { get { return Math.Sqrt(Dot(this,this)); } }
public static double Dot(Vector2 A, Vector2 B)
{
return A.X*B.X+A.Y*B.Y;
}
public static double Cross(Vector2 A, Vector2 B)
{
return A.X*B.Y-A.Y*B.X;
}
}
class Program
{
static void Main(string[] args)
{
Vector2 A=new Vector2() { X=5.45, Y=1.12};
Vector2 B=new Vector2() { X=-3.86, Y=4.32 };
double angle=Vector2.GetAngle(A, B) * 180/Math.PI;
// angle = 120.16850967865749
}
}
See the test case above in GeoGebra.
I think a better formula was posted here:
http://www.mathworks.com/matlabcentral/answers/16243-angle-between-two-vectors-in-3d
angle = atan2(norm(cross(a,b)), dot(a,b))
So this formula works in 2 or 3 dimensions.
For 2 dimensions this formula simplifies to the one stated above.
Nobody pointed out that if you have a single vector, and want to find the angle of the vector from the X axis, you can take advantage of the fact that the argument to atan2() is actually the slope of the line, or (delta Y / delta X). So if you know the slope, you can do the following:
given:
A = angle of the vector/line you wish to determine (from the X axis).
m = signed slope of the vector/line.
then:
A = atan2(m, 1)
Very useful!
If you care about accuracy for small angles, you want to use this:
angle = 2*atan2(|| ||b||a - ||a||b ||, || ||b||a + ||a||b ||)
Where "||" means absolute value, AKA "length of the vector". See https://math.stackexchange.com/questions/1143354/numerically-stable-method-for-angle-between-3d-vectors/1782769
However, that has the downside that in two dimensions, it loses the sign of the angle.
As a complement to the answer of #martin-r one should note that it is possible to use the sum/difference formula for arcus tangens.
angle = atan2(vec2.y, vec2.x) - atan2(vec1.y, vec1.x);
angle = -atan2(vec1.x * vec2.y - vec1.y * vec2.x, dot(vec1, vec2))
where dot = vec1.x * vec2.x + vec1.y * vec2.y
Caveat 1: make sure the angle remains within -pi ... +pi
Caveat 2: beware when the vectors are getting very similar, you might get extinction in the first argument, leading to numerical inaccuracies
You don't have to use atan2 to calculate the angle between two vectors. If you just want the quickest way, you can use dot(v1, v2)=|v1|*|v2|*cos A
to get
A = Math.acos( dot(v1, v2)/(v1.length()*v2.length()) );
angle(vector.b,vector.a)=pi/2*((1+sgn(xa))*(1-sgn(ya^2))-(1+sgn(xb))*(1-sgn(yb^2)))
+pi/4*((2+sgn(xa))*sgn(ya)-(2+sgn(xb))*sgn(yb))
+sgn(xa*ya)*atan((abs(xa)-abs(ya))/(abs(xa)+abs(ya)))
-sgn(xb*yb)*atan((abs(xb)-abs(yb))/(abs(xb)+abs(yb)))
xb,yb and xa,ya are the coordinates of the two vectors
The formula, angle(vector.b,vector.a), that I sent, give results
in the four quadrants and for any coordinates xa,ya and xb,yb.
For coordinates xa=ya=0 and or xb=yb=0 is undefined.
The angle can be bigger or smaller than pi, and can be positive
or negative.
Here a little program in Python that uses the angle between vectors to determine if a point is inside or outside a certain polygon
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from shapely.geometry import Point, Polygon
from pprint import pprint
# Plot variables
x_min, x_max = -6, 12
y_min, y_max = -3, 8
tick_interval = 1
FIG_SIZE = (10, 10)
DELTA_ERROR = 0.00001
IN_BOX_COLOR = 'yellow'
OUT_BOX_COLOR = 'black'
def angle_between(v1, v2):
""" Returns the angle in radians between vectors 'v1' and 'v2'
The sign of the angle is dependent on the order of v1 and v2
so acos(norm(dot(v1, v2))) does not work and atan2 has to be used, see:
https://stackoverflow.com/questions/21483999/using-atan2-to-find-angle-between-two-vectors
"""
arg1 = np.cross(v1, v2)
arg2 = np.dot(v1, v2)
angle = np.arctan2(arg1, arg2)
return angle
def point_inside(point, border):
""" Returns True if point is inside border polygon and False if not
Arguments:
:point: x, y in shapely.geometry.Point type
:border: [x1 y1, x2 y2, ... , xn yn] in shapely.geomettry.Polygon type
"""
assert len(border.exterior.coords) > 2,\
'number of points in the polygon must be > 2'
point = np.array(point)
side1 = np.array(border.exterior.coords[0]) - point
sum_angles = 0
for border_point in border.exterior.coords[1:]:
side2 = np.array(border_point) - point
angle = angle_between(side1, side2)
sum_angles += angle
side1 = side2
# if wn is 1 then the point is inside
wn = sum_angles / 2 / np.pi
if abs(wn - 1) < DELTA_ERROR:
return True
else:
return False
class MainMap():
#classmethod
def settings(cls, fig_size):
# set the plot outline, including axes going through the origin
cls.fig, cls.ax = plt.subplots(figsize=fig_size)
cls.ax.set_xlim(-x_min, x_max)
cls.ax.set_ylim(-y_min, y_max)
cls.ax.set_aspect(1)
tick_range_x = np.arange(round(x_min + (10*(x_max - x_min) % tick_interval)/10, 1),
x_max + 0.1, step=tick_interval)
tick_range_y = np.arange(round(y_min + (10*(y_max - y_min) % tick_interval)/10, 1),
y_max + 0.1, step=tick_interval)
cls.ax.set_xticks(tick_range_x)
cls.ax.set_yticks(tick_range_y)
cls.ax.tick_params(axis='both', which='major', labelsize=6)
cls.ax.spines['left'].set_position('zero')
cls.ax.spines['right'].set_color('none')
cls.ax.spines['bottom'].set_position('zero')
cls.ax.spines['top'].set_color('none')
#classmethod
def get_ax(cls):
return cls.ax
#staticmethod
def plot():
plt.tight_layout()
plt.show()
class PlotPointandRectangle(MainMap):
def __init__(self, start_point, rectangle_polygon, tolerance=0):
self.current_object = None
self.currently_dragging = False
self.fig.canvas.mpl_connect('key_press_event', self.on_key)
self.plot_types = ['o', 'o-']
self.plot_type = 1
self.rectangle = rectangle_polygon
# define a point that can be moved around
self.point = patches.Circle((start_point.x, start_point.y), 0.10,
alpha=1)
if point_inside(start_point, self.rectangle):
_color = IN_BOX_COLOR
else:
_color = OUT_BOX_COLOR
self.point.set_color(_color)
self.ax.add_patch(self.point)
self.point.set_picker(tolerance)
cv_point = self.point.figure.canvas
cv_point.mpl_connect('button_release_event', self.on_release)
cv_point.mpl_connect('pick_event', self.on_pick)
cv_point.mpl_connect('motion_notify_event', self.on_motion)
self.plot_rectangle()
def plot_rectangle(self):
x = [point[0] for point in self.rectangle.exterior.coords]
y = [point[1] for point in self.rectangle.exterior.coords]
# y = self.rectangle.y
self.rectangle_plot, = self.ax.plot(x, y,
self.plot_types[self.plot_type], color='r', lw=0.4, markersize=2)
def on_release(self, event):
self.current_object = None
self.currently_dragging = False
def on_pick(self, event):
self.currently_dragging = True
self.current_object = event.artist
def on_motion(self, event):
if not self.currently_dragging:
return
if self.current_object == None:
return
point = Point(event.xdata, event.ydata)
self.current_object.center = point.x, point.y
if point_inside(point, self.rectangle):
_color = IN_BOX_COLOR
else:
_color = OUT_BOX_COLOR
self.current_object.set_color(_color)
self.point.figure.canvas.draw()
def remove_rectangle_from_plot(self):
try:
self.rectangle_plot.remove()
except ValueError:
pass
def on_key(self, event):
# with 'space' toggle between just points or points connected with
# lines
if event.key == ' ':
self.plot_type = (self.plot_type + 1) % 2
self.remove_rectangle_from_plot()
self.plot_rectangle()
self.point.figure.canvas.draw()
def main(start_point, rectangle):
MainMap.settings(FIG_SIZE)
plt_me = PlotPointandRectangle(start_point, rectangle) #pylint: disable=unused-variable
MainMap.plot()
if __name__ == "__main__":
try:
start_point = Point([float(val) for val in sys.argv[1].split()])
except IndexError:
start_point= Point(0, 0)
border_points = [(-2, -2),
(1, 1),
(3, -1),
(3, 3.5),
(4, 1),
(5, 1),
(4, 3.5),
(5, 6),
(3, 4),
(3, 5),
(-0.5, 1),
(-3, 1),
(-1, -0.5),
]
border_points_polygon = Polygon(border_points)
main(start_point, border_points_polygon)
function(deltaTime) {
x = x * FACTOR; // FACTOR = 0.9
}
This function is called in a game loop. First assume that it's running at a constant 30 FPS, so deltaTime is always 1/30.
Now the game is changed so deltaTime isn't always 1/30 but becomes variable. How can I incorporate deltaTime in the calculation of x to keep the "effect per second" the same?
And what about
function(deltaTime) {
x += (target - x) * FACTOR; // FACTOR = 0.2
}
x = x * Math.pow(0.9, deltaTime*30)
Edit
For your new update:
x = (x-target) * Math.pow(1-FACTOR, deltaTime*30) + target;
To show how I got there:
Let x0 be the initial value, and xn be the value after n/30 seconds. Also let T=target, F=factor. Then:
x1 = x0 + (T-x0)F = (1-F)x0 + TF
x2 = (1-F)x1 + TF = (1-F)^2 * x0 + (1-F)TF + TF
Continuing with x3,x4,... will show:
xn = (1-F)^n * x0 + TF * (1 + (1-F) + (1-F)^2 + ... + (1-F)^(n-1))
Now substituting the formula for the sum of a geometric sequence will give the result above. This really only proves the result for integer n, but it should work for all values.
x = x * powf(0.9, deltaTime / (1.0f / 30.0f))