Animate text within a circle - Matplotlib - pandas

The following animates a circle and lines based on a given radius. I'm hoping combine the plot with animated text highlighting different segments within this circle. The text does get updated for each new frame but it doesn't remove the previous frames text.
Note: I'm trying to keep the animate function as I want to keep the axis the same and only animate updated data.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
import matplotlib.gridspec as gridspec
import math
df = pd.DataFrame({
'Time' : [1,1,1,1,2,2,2,2,3,3,3,3],
'X2' : [0,0,0,0,-1,-1,-1,-1,0,0,0,0],
'Y2' : [0,0,0,0,1,1,1,1,1,1,1,1],
'Angle' : [0,0,0,0,-45,-45,-45,-45,90,90,90,90],
})
fig = plt.figure(figsize = (8,10))
grid = gridspec.GridSpec(1, 2)
gridsize = (1, 2)
ax = plt.subplot2grid(gridsize, (0, 0))
ax2 = plt.subplot2grid(gridsize, (0, 1))
ax2.set_xlim(-10, 10)
ax2.set_ylim(-10, 10)
# the center of the compass
moving_x = np.array(df.groupby(['Time'])['X2'].apply(list))
moving_y = np.array(df.groupby(['Time'])['Y2'].apply(list))
moving_point = ax.scatter(moving_x[0], moving_y[0], c = 'black', marker = 'o', zorder = 3)
radius = df.drop_duplicates(subset = ['Time','X2', 'Y2'])[['X2', 'Y2']].values
circle = plt.Circle(radius[0], 10, color = 'black', fill = False, lw = 0.2)
circle2 = plt.Circle(radius[0], 10, color = 'red', fill = False, lw = 0.2)
ax.add_patch(circle)
ax2.add_patch(circle2)
line1, = ax.plot([],[], color = 'k', linewidth = 1)
line2, = ax.plot([],[], color = 'k', linewidth = 1)
t = df['Angle'][0]
angles = np.array(df.groupby(['Time'])['Angle'].apply(list))
angleText = df.drop_duplicates(subset = ['Time','Angle'])['Angle'].values
rot_mat = lambda theta:np.array([
[np.cos(np.deg2rad(theta)),-np.sin(np.deg2rad(theta))],
[np.sin(np.deg2rad(theta)),np.cos(np.deg2rad(theta))]
])
# compass tags
annotate_tags = ['N','E','S','W']
def animate(i) :
moving_point.set_offsets(np.c_[moving_x[0+i], moving_y[0+i]])
circle.center = (radius[i,0], radius[i,1])
xs1L1=-10.0/2**0.5
ys1L1=10.0/2**0.5
xs2L1=10.0/2**0.5
ys2L1=-10.0/2**0.5
xs1L2=-xs1L1
ys1L2=ys1L1
xs2L2=-xs1L2
ys2L2=ys2L1
cx=radius[i,0]
cy=radius[i,1]
theta=math.radians(angles[i][0])
x1L1=( (xs1L1+radius[i,0] - cx) * math.cos(theta) + (ys1L1+radius[i,1] - cy) * math.sin(theta) ) + cx
x2L1=( (xs2L1+radius[i,0] - cx) * math.cos(theta) + (ys2L1+radius[i,1]- cy) * math.sin(theta) ) + cx
y1L1=( -(xs1L1+radius[i,0] - cx) * math.sin(theta) + (ys1L1+radius[i,1] - cy) * math.cos(theta) ) + cy
y2L1=( -(xs2L1+radius[i,0] - cx) * math.sin(theta) + (ys2L1+radius[i,1] - cy) * math.cos(theta) ) + cy
line1.set_data([x1L1,x2L1],[y1L1,y2L1])
x1L2=( (xs1L2+radius[i,0] - cx) * math.cos(theta) + (ys1L2+radius[i,1] - cy) * math.sin(theta) ) + cx
x2L2=( (xs2L2+radius[i,0] - cx) * math.cos(theta) + (ys2L2+radius[i,1]- cy) * math.sin(theta) ) + cx
y1L2=( -(xs1L2+radius[i,0] - cx) * math.sin(theta) + (ys1L2+radius[i,1] - cy) * math.cos(theta) ) + cy
y2L2=( -(xs2L2+radius[i,0] - cx) * math.sin(theta) + (ys2L2+radius[i,1] - cy) * math.cos(theta) ) + cy
line2.set_data([x1L2,x2L2],[y1L2,y2L2])
# Animate compass tags for each frame
tag_pos = np.array([[0,8.5],[8.5,0],[0,-8.5],[-8.5,0]])
tag_pos = tag_pos # rot_mat(angleText[i])
for tag,pos in zip(annotate_tags,tag_pos):
ax.annotate(tag,xy=pos+radius[i], xycoords='data',
fontsize=10,horizontalalignment='right', verticalalignment='bottom')
ani = animation.FuncAnimation(fig, animate, np.arange(0,3), blit = False)

You want to create text objects for as many tags outside the animate function. And, inside the animate function set_text and set_position as shown here:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
import matplotlib.gridspec as gridspec
import math
df = pd.DataFrame({
'Time' : [1,1,1,1,2,2,2,2,3,3,3,3],
'X2' : [0,0,0,0,-1,-1,-1,-1,0,0,0,0],
'Y2' : [0,0,0,0,1,1,1,1,1,1,1,1],
'Angle' : [0,0,0,0,-45,-45,-45,-45,90,90,90,90],
})
fig = plt.figure(figsize = (20,10))
grid = gridspec.GridSpec(1, 2)
gridsize = (1, 2)
ax = plt.subplot2grid(gridsize, (0, 0))
ax2 = plt.subplot2grid(gridsize, (0, 1))
ax2.set_xlim(-10, 10)
ax2.set_ylim(-10, 10)
# the center of the compass
moving_x = np.array(df.groupby(['Time'])['X2'].apply(list))
moving_y = np.array(df.groupby(['Time'])['Y2'].apply(list))
moving_point = ax.scatter(moving_x[0], moving_y[0], c = 'black', marker = 'o', zorder = 3)
radius = df.drop_duplicates(subset = ['Time','X2', 'Y2'])[['X2', 'Y2']].values
circle = plt.Circle(radius[0], 10, color = 'black', fill = False, lw = 0.2)
circle2 = plt.Circle(radius[0], 10, color = 'red', fill = False, lw = 0.2)
ax.add_patch(circle)
ax2.add_patch(circle2)
line1, = ax.plot([],[], color = 'k', linewidth = 1)
line2, = ax.plot([],[], color = 'k', linewidth = 1)
t = df['Angle'][0]
angles = np.array(df.groupby(['Time'])['Angle'].apply(list))
angleText = df.drop_duplicates(subset = ['Time','Angle'])['Angle'].values
rot_mat = lambda theta:np.array([
[np.cos(np.deg2rad(theta)),-np.sin(np.deg2rad(theta))],
[np.sin(np.deg2rad(theta)),np.cos(np.deg2rad(theta))]
])
# compass tags
annotate_tags = ['N','E','S','W']
annotation=[None]*len(annotate_tags)
for i in range(len(annotate_tags)):
annotation[i] = ax.annotate('', xy=(0, 0),xycoords='data',fontsize=10,horizontalalignment='right', verticalalignment='bottom')
def animate(i) :
moving_point.set_offsets(np.c_[moving_x[0+i], moving_y[0+i]])
circle.center = (radius[i,0], radius[i,1])
xs1L1=-10.0/2**0.5
ys1L1=10.0/2**0.5
xs2L1=10.0/2**0.5
ys2L1=-10.0/2**0.5
xs1L2=-xs1L1
ys1L2=ys1L1
xs2L2=-xs1L2
ys2L2=ys2L1
cx=radius[i,0]
cy=radius[i,1]
theta=math.radians(angles[i][0])
x1L1=( (xs1L1+radius[i,0] - cx) * math.cos(theta) + (ys1L1+radius[i,1] - cy) * math.sin(theta) ) + cx
x2L1=( (xs2L1+radius[i,0] - cx) * math.cos(theta) + (ys2L1+radius[i,1]- cy) * math.sin(theta) ) + cx
y1L1=( -(xs1L1+radius[i,0] - cx) * math.sin(theta) + (ys1L1+radius[i,1] - cy) * math.cos(theta) ) + cy
y2L1=( -(xs2L1+radius[i,0] - cx) * math.sin(theta) + (ys2L1+radius[i,1] - cy) * math.cos(theta) ) + cy
line1.set_data([x1L1,x2L1],[y1L1,y2L1])
x1L2=( (xs1L2+radius[i,0] - cx) * math.cos(theta) + (ys1L2+radius[i,1] - cy) * math.sin(theta) ) + cx
x2L2=( (xs2L2+radius[i,0] - cx) * math.cos(theta) + (ys2L2+radius[i,1]- cy) * math.sin(theta) ) + cx
y1L2=( -(xs1L2+radius[i,0] - cx) * math.sin(theta) + (ys1L2+radius[i,1] - cy) * math.cos(theta) ) + cy
y2L2=( -(xs2L2+radius[i,0] - cx) * math.sin(theta) + (ys2L2+radius[i,1] - cy) * math.cos(theta) ) + cy
line2.set_data([x1L2,x2L2],[y1L2,y2L2])
# Animate compass tags for each frame
tag_pos = np.array([[0,8.5],[8.5,0],[0,-8.5],[-8.5,0]])
tag_pos = tag_pos # rot_mat(angleText[i])
k=0
for tag,pos in zip(annotate_tags,tag_pos):
annotation[k].set_text(tag)
annotation[k].set_position(xy=pos+radius[i])
k=k+1
ani = animation.FuncAnimation(fig, animate, np.arange(0,3), blit = False)
ani.save('test.gif', writer='imagemagick', fps=5)
plt.show()

Related

Update parameters of a function in a while loop

I am trying to execute a while loop that holds a function with parameters. However, I have noticed that the parameters inside the while loop are not updated which leads to an infinite while loop. Is there a reason behind the fact that the function parameters are not being updated after every loop?
import shapefile
from osgeo import gdal
#import rasterio
print (gdal.VersionInfo())
def pointInRect(x, y, x1, y1, w, h): # check if a raster point is in another raster
x2, y2 = x1+w, y1+h
if (x1 < x and x < x2):
if (y1 < y and y < y2):
return True
return False
# Open the shapes centroids
shp_cntrds = 'Path to centroids'
sf_cntrds = shapefile.Reader(shp_cntrds)
shapes_cntrds = sf_cntrds.shapes()
records_cntrds = sf_cntrds.records()
# adjust labels position according to its shapes centroids position
for i in range(len(records_cntrds)):
print(i)
tods = gdal.Open(str(records_cntrds[i][1]))
width = tods.RasterXSize
height = tods.RasterYSize
tods.SetGeoTransform([shapes_cntrds[i].points[0][0] - (width * 0.005), 0.01, 0,
shapes_cntrds[i].points[0][1] + (height * 0.005), 0, -0.01])
gt = tods.GetGeoTransform()
left = gt[0]
bottom = gt[3] + width * gt[4] + height * gt[5]
right = gt[0] + width * gt[1] + height * gt[2]
top = gt[3]
srs = osr.SpatialReference()
srs.SetUTM(32, 1) # set crs
srs.SetWellKnownGeogCS('WGS84') # set crs
tods.SetProjection(srs.ExportToWkt()) # set Projection and save file
print(width, height)
tods = None
# iterate through Labels and move labels away from each others if they overlapp
for i in range(len(records_cntrds)):
tods1 = gdal.Open(str(records_cntrds[i][1])) # records of the centroid shapefile contains the raster file path
width = tods1.RasterXSize
height = tods1.RasterYSize
gt = tods1.GetGeoTransform()
left = gt[0]
bottom = gt[3] + width * gt[4] + height * gt[5]
right = gt[0] + width * gt[1] + height * gt[2]
top = gt[3]
face = [x for x in list(range(len(records_cntrds))) if x != i]
tods1 = None
for j in face:
if str(records_cntrds[i][1]) == str(records_cntrds[j][1]):
pass
else:
ds_raster_face = gdal.Open(str(records_cntrds[j][1]))
#print(str(records_cntrds[i][1]))
#print(str(records_cntrds[j][1]))
gt_face = ds_raster_face.GetGeoTransform()
width_face = ds_raster_face.RasterXSize
height_face = ds_raster_face.RasterYSize
left_face = gt_face[0]
bottom_face = gt_face[3] + width_face * gt_face[4] + height_face * gt_face[5]
right_face = gt_face[0] + width_face * gt_face[1] + height_face * gt_face[2]
top_face = gt_face[3]
width1 = width
left1 = left
height1 = height
bottom1 = bottom
while pointInRect(left_face, bottom_face, left1, bottom1, width1*0.01, height1*0.01) :
tods2 = gdal.Open(str(records_cntrds[i][1]))
gt = tods2.GetGeoTransform()
width1 = tods2.RasterXSize
height1 = tods2.RasterYSize
left1 = gt[0]
bottom1 = gt[3] + width1 * gt[4] + height1 * gt[5]
print("while executed")
tods2.SetGeoTransform([(shapes_cntrds[i].points[0][0] - (width1 * 0.005)) - 2.7, 0.01, 0,
(shapes_cntrds[i].points[0][1] + (height1 * 0.005)) - 2.8, 0, -0.01])
print("coordinates changed to",(i, left1, bottom1, width1, height1))
tods2 = None
The while loop should break when the function return false but it is repeating the same thing. Are the gt values not updatet or are they initialized again ?

Conversion ECEF XYZ to LLH (LAT/LONG/HEIGHT) and translation back - not accurate / possible error in IronPython script

I've modeled a 3D earth with gridpoints, as below:
The points are represented in 3D space as XYZ coordinates.
I then convert XYZ to Lat/Long/Height(elevation) based on the script I took from here:
JSFiddle
For some reason I got really strange results when trying to find XYZ of LLH not from my set, so I tried to verify the initial script by converting XYZ to LLH and then the same LLH back to XYZ to see if I get the same coordinate.
Instead, the resulting coordinate is some XYZ on earth, unrelated to the original XYZ position.
XYZ to LLH script:
Source: JSFiddle
def xyzllh(x,y,z):
""" xyz vector to lat,lon,height
output:
llhvec[3] with components
flat geodetic latitude in deg
flon longitude in deg
altkm altitude in km
"""
dtr = math.pi/180.0
rrnrm = [0.0] * 3
llhvec = [0.0] * 3
geodGBL()
esq = EARTH_Esq
rp = math.sqrt( x*x + y*y + z*z )
flatgc = math.asin( z / rp )/dtr
testval= abs(x) + abs(y)
if ( testval < 1.0e-10):
flon = 0.0
else:
flon = math.atan2( y,x )/dtr
if (flon < 0.0 ):
flon = flon + 360.0
p = math.sqrt( x*x + y*y )
# on pole special case
if ( p < 1.0e-10 ):
flat = 90.0
if ( z < 0.0 ):
flat = -90.0
altkm = rp - rearth(flat)
llhvec[0] = flat
llhvec[1] = flon
llhvec[2] = altkm
return llhvec
# first iteration, use flatgc to get altitude
# and alt needed to convert gc to gd lat.
rnow = rearth(flatgc)
altkm = rp - rnow
flat = gc2gd(flatgc,altkm)
rrnrm = radcur(flat)
rn = rrnrm[1]
for x in range(5):
slat = math.sin(dtr*flat)
tangd = ( z + rn*esq*slat ) / p
flatn = math.atan(tangd)/dtr
dlat = flatn - flat
flat = flatn
clat = math.cos( dtr*flat )
rrnrm = radcur(flat)
rn = rrnrm[1]
altkm = (p/clat) - rn
if ( abs(dlat) < 1.0e-12 ):
break
llhvec[0] = flat
llhvec[1] = flon
llhvec[2] = altkm
return llhvec
# globals
EARTH_A = 0
EARTH_B = 0
EARTH_F = 0
EARTH_Ecc = 0
EARTH_Esq = 0
# starting function do_llhxyz()
CallCount = 0
llh = [0.0] * 3
dtr = math.pi/180
CallCount = CallCount + 1
sans = " \n"
llh = xyzllh(x,y,z)
latitude = llh[0]
longitude= llh[1]
hkm = llh[2]
height = 1000.0 * hkm
latitude = fformat(latitude,5)
longitude = fformat(longitude,5)
height = fformat(height,1)
sans = sans +"Latitude,Longitude, Height (ellipsoidal) from ECEF\n"
sans = sans + "\n"
sans = sans +"Latitude : " + str(latitude) + " deg N\n"
sans = sans +"Longitude : " + str(longitude - 180) + " deg E\n"
sans = sans +"Height : " + str(height) + " m\n"
lats = []
longs = []
heights = []
lats.append(str(latitude))
longs.append(str(longitude - 180))
heights.append(str(height))
And this is the LLH to XYZ script:
Source: www.mathworks.com
a = 6378137
t = 8.1819190842622e-2
# (prime vertical radius of curvature)
N = a / math.sqrt(1 - (t*t) * (math.sin(lat)*math.sin(lat)))
x = []
y = []
z = []
# results:
x.append( ((N+height) * math.cos(lat) * math.cos(long))/1000 )
y.append( ((N+height) * math.cos(lat) * math.sin(long))/1000 )
z.append( (((1-t*t) * N + height) * math.sin(lat))/1000 )
Anyone know what I'm doing wrong here?
Thanks!

Errors on parameters using scipy.curve_fit

I am fitting the following function (variables A, D, μ and τ) and x and E are fixed:
I created some example data using the equation and added some noise. The fit looks very good and has a low chi-squared however the errors from the covariance matrix are odd; some are very large whereas others are smaller. What am I doing wrong?
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# Constants
E_field = 1
x = 1
def function(t, A, D, μ, τ):
return A/np.sqrt(4*np.pi*D*t) * np.exp(-pow(x-μ*E_field*t, 2)/(4*D*t) - t/τ)
def chi(E, O):
return np.sum(np.ma.masked_invalid(pow(O-E, 2)/E))
def fit(t, n, m, p0):
ddof = n.size - m
popt, pcov = curve_fit(function, t, n, p0=p0)
fitted_n = function(t, *popt)
reduced_χ_squared = chi(n, fitted_n) / ddof
σ = np.sqrt(np.diag(pcov))
return popt, σ, reduced_χ_squared
# Choose random variables to generate data
x, t = 1, np.linspace(0.01, 5, num=100)
A, D, μ, τ = 1, 0.2, 1, 1
n = function(t, A, D, μ, τ)
n_noise = n + 0.005 * np.random.normal(size=n.size)
n_noise += abs(min(n_noise)) # Shift data to lie on y = 0
p0 = [1, 0.25, 1, 1]
vars, σ, reduced_χ_squared = fit(t, n_noise, 4, p0)
fitted_A, fitted_D, fitted_μ, fitted_τ = vars
σ_A, σ_D, σ_μ, σ_τ = σ
fitted_n = function(t, *vars)
fig, ax = plt.subplots()
ax.plot(t, n_noise)
ax.plot(t, fitted_n)
#ax.text(0.82, 0.75, "χᵣ²={:.4f}".format(reduced_χ_squared), transform = ax.transAxes)
ax.legend(["Observed n", "Expected n"])
print("Fitted parameters: A = {:.4f}, D = {:.4f}, μ = {:.4f}, τ = {:.4f}".format(*vars))
print("Fitted parameter errors: σ_A = {:.4f}, σ_D = {:.4f}, σ_μ = {:.4f}, σ_τ = {:.4f}".format(*σ))
print("Reduced χ² = {:.4f}".format(reduced_χ_squared))
Running this code gives me the following output
As mentioned in my comment above, correlation is a big problem here. Biggest problem though is that you fit more parameters than required.
Let us transform:
A = exp( alpha) i.e alpha = log(A)
delta = 4 * D
epsilon = mu * E
We then get:
1 / sqrt( pi* delta ) * exp( -( x**2 + epsilon**2 * t**2 -2*x*epsilon t) / ( delta * t ) -t / tau + alpha )
= 1 / sqrt( pi* delta ) * exp( -( x**2 + epsilon**2 * t**2 -2*x*epsilon t) / ( delta * t ) -delta / tau * t**2/( delta * t) + delta * alpha * t/ ( delta * t ) )
= 1 / sqrt( pi* delta ) * exp( -( x**2 + epsilon**2 * t**2 -2*x*epsilon t + delta / tau * t**2 - delta * alpha * t ) / ( delta * t ) )
= 1 / sqrt( pi* delta ) * exp( -( x**2 + ( epsilon**2 + delta / tau ) * t**2 -x * ( 2 * epsilon + delta * alpha ) * t ) / ( delta * t ) )
now renaming:
( epsilon**2 + delta / tau ) -> gamma**2
( 2 * epsilon + delta * alpha ) -> eta
we get
= 1 / sqrt( pi * delta ) * exp( -( x**2 + gamma**2 * t**2 - x * eta * t ) / ( delta * t ) )
So there are actually only 3 parameters to fit and it looks like this:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# Constants
E_field = 1
x = 1
def function(t, A, D, μ, τ):
return A/np.sqrt(4*np.pi*D*t) * np.exp(-pow(x-μ*E_field*t, 2)/(4*D*t) - t/τ)
def alt_func( t, gamma, eta, delta ):
return np.exp( -( x**2 + gamma**2 * t**2 - eta * t ) / ( delta * t ) ) / np.sqrt( np.pi * delta * t )
# Choose random variables to generate data
x, t = 1, np.linspace(0.01, 5, num=100)
A, D, μ, τ = 1, 0.2, 1, 1
n = function(t, A, D, μ, τ)
n_noise = n + 0.005 * np.random.normal(size=n.size)
n_noise += abs(min(n_noise)) # Shift data to lie on y = 0
guess=[1.34, 2, .8]
palt, covalt = curve_fit( alt_func, t, n_noise)
print( covalt )
print( palt )
yt = alt_func( t, *palt )
yg = alt_func( t, *guess )
yorg = function( t, A, D, μ, τ )
fig, ax = plt.subplots()
ax.plot(t, n_noise)
ax.plot(t, yg )
ax.plot(t, yt, ls="--")
ax.plot(t, yorg, ls=":" )
plt.show()
This has a reasonable covariance matrix. One can get the original parameters easily via error propagation.
Altzernatively, it should be enough to fix A=1 and only fit the three left parameters in the original function.
Concerning the transformation and back calculation one has to keep in mind that this is of course from R³ to R⁴, so it is naturally not unique either. Again one can just fix one value, or one might to try to spread the error evenly between the parameters or who knows....

Calculation problem within opencv, Python

I was calculating Y from yCbCr for histogram equalization.
The kb value I want is "kb / (b + g + r)" => ratio of b to RGB in a pixel.
I thought it was a normal calculation, but errors continue to occur in this part.
It is as follows.
RuntimeWarning: overflow encountered in ubyte_scalars
kb[y][x] = b[y][x] / (b[y][x] + g[y][x] + r[y][x])
What should I do to solve this problem?
import numpy as np
import cv2
def transform(img) :
height, width, color = img.shape
result = np.zeros((height, width), np.uint8)
b,g,r = cv2.split(img)
kb = [[0.0] * width] * height
kg = [[0.0] * width] * height
kr = [[0.0] * width] * height
yi = [[0.0] * width] * height
yo = [[0.0] * width] * height
list(b), list(g), list(r), list(kb), list(kg), list(kr), list(yi), list(yo)
for x in range(width):
for y in range(height):
kb[y][x] = b[y][x] / (b[y][x] + g[y][x] + r[y][x])
kg[y][x] = g[y][x] / (b[y][x] + g[y][x] + r[y][x])
kr[y][x] = b[y][x] / (b[y][x] + g[y][x] + r[y][x])
# ...
return result

Can't figure out, why script on Jython working differently from script on Pascal

Long story short: I'm writing script, which should move mouse and do clicks like human (it's a bot, actually), using SikuliX. SikuliX uses Jython 2.7 as lang for scritps.
I found nice lib for my purposes (moving mouse like human): mouse.simba written in Pascal-like lang, and rewrite function _humanWindMouse() in jython. It works, but not like I expected it would be.
Test run of my script, drawing rectangle:
https://prtscr.cx.ua/storage/5b/5b2203.jpg
Result of using original function with same coords:
https://prtscr.cx.ua/storage/bb/bb3ff5.jpg
sorry for links, I can't post images yet (
My code:
import random
import time
import math
from time import sleep
from math import sqrt
from math import ceil
from math import hypot
from java.awt import Robot
def distance(x1, y1, x2, y2):
return math.hypot(x2 - x1, y2 - y1)
def myrandom(x):
return random.randint(0, x-1)
def myround(x):
return int(round(x))
# function MMouseMove (MyMouseMove) for moving mouse using only coord
def MMouseMove(x,y):
robot = Robot()
robot.mouseMove(x,y)
# function HumanWindMouse by BenLand100 & Flight, python implementation
def humanWindMouse(xs, ys, xe, ye, gravity, wind):
veloX = veloY = windX=windY=veloMag=dist=randomDist=lastDist=D=0
lastX=lastY=MSP=W=TDist=0
mouseSpeed = 20
MSP = mouseSpeed
sqrt2 = sqrt(2)
sqrt3 = sqrt(3)
sqrt5 = sqrt(5)
TDist = distance(myround(xs), myround(ys), myround(xe), myround(ye))
t = time.time() + 10000
while True:
if time.time() > t:
break
dist = hypot(xs - xe, ys - ye)
wind = min(wind, dist)
if dist < 1:
dist = 1
D = (myround((myround(TDist)*0.3))/7)
if D > 25:
D = 25
if D < 5:
D = 5
rCnc = myrandom(6)
if rCnc == 1:
D = random.randint(2,3)
if D <= myround(dist):
maxStep = D
else:
maxStep = myround(dist)
windX= windX / sqrt2
windY= windY / sqrt2
veloX= veloX + windX
veloY= veloY + windY
veloX= veloX + gravity * (xe - xs) / dist
veloY= veloY + gravity * (ye - ys) / dist
if hypot(veloX, veloY) > maxStep:
temp = int(myround(maxStep) // 2)
if temp == 0:
temp = 1
randomDist= maxStep / 2.0 + myrandom(temp)
veloMag= sqrt(veloX * veloX + veloY * veloY)
veloX= (veloX / veloMag) * randomDist
veloY= (veloY / veloMag) * randomDist
lastX= myround(xs)
lastY= myround(ys)
xs= xs + veloX
ys= ys + veloY
if lastX <> myround(xs) or lastY <> myround(ys):
MMouseMove(myround(xs), myround(ys))
W = (myrandom((myround(100/MSP)))*6)
if W < 5:
W = 5
W = myround(W*0.9)
sleep(W/1000.0)
lastdist= dist
if hypot(xs - xe, ys - ye) < 1:
break
if myround(xe) <> myround(xs) or myround(ye) <> myround(ys):
MMouseMove(myround(xe), myround(ye))
mouseSpeed = MSP
return;
def MMouse(x,y):
mouseSpeed = 20
randSpeed = (myrandom(mouseSpeed) / 2.0 + mouseSpeed) / 10.0
curPos = Mouse.at()
x1 = curPos.x
y1 = curPos.y
humanWindMouse(x1, y1, x, y, 5, 10.0/randSpeed)
return;
And I used this in such a way:
MMouseMove(227, 146)
mouseDown(Button.LEFT)
MMouse(396, 146)
MMouse(396, 252)
MMouse(227, 252)
MMouse(227, 146)
mouseUp(Button.LEFT)
exit()
mouseDown() and mouseUp() are built-in functions in SikuliX
And I didn't use built-in mouseMove(), because with it going from A to B is too slow.
Any help would be appreciated
After few hours of debugging i figured out the problem: in source code for unknowing reason author passed constant called MOUSE_HUMAN to variable named gravity when caling his function _humanWindMouse(), this looks like an error to me. Thats why I decided to fix this in my code, and throw out one argument of the function and a few lines of code (and that was wrong move). After re-adding needed code my function working, as I expected.
So, here's the working code:
# function HumanWindMouse by BenLand100 & Flight,
# python implementation by Nokse
def humanWindMouse(xs, ys, xe, ye, gravity, wind, targetArea):
veloX = veloY = windX=windY=veloMag=dist=randomDist=lastDist=D=0
lastX=lastY=MSP=W=TDist=0
mouseSpeed = 20
MSP = mouseSpeed
sqrt2 = sqrt(2)
sqrt3 = sqrt(3)
sqrt5 = sqrt(5)
TDist = distance(myround(xs), myround(ys), myround(xe), myround(ye))
t = time.time() + 10000
while True:
if time.time() > t:
break
dist = hypot(xs - xe, ys - ye)
wind = min(wind, dist)
if dist < 1:
dist = 1
D = (myround((myround(TDist)*0.3))/7)
if D > 25:
D = 25
if D < 5:
D = 5
rCnc = myrandom(6)
if rCnc == 1:
D = random.randint(2,3)
if D <= myround(dist):
maxStep = D
else:
maxStep = myround(dist)
if dist >= targetArea:
windX = windX / sqrt3 + (myrandom(myround(wind) * 2 + 1) - wind) / sqrt5
windY = windY / sqrt3 + (myrandom(myround(wind) * 2 + 1) - wind) / sqrt5
else:
windX = windX / sqrt2
windY = windY / sqrt2
veloX = veloX + windX
veloY = veloY + windY
veloX = veloX + gravity * (xe - xs) / dist
veloY = veloY + gravity * (ye - ys) / dist
if hypot(veloX, veloY) > maxStep:
halfSteps = int(myround(maxStep) // 2)
if halfSteps == 0:
halfSteps = 1
randomDist = maxStep / 2.0 + myrandom(halfSteps)
veloMag = sqrt(veloX * veloX + veloY * veloY)
veloX = (veloX / veloMag) * randomDist
veloY = (veloY / veloMag) * randomDist
lastX = myround(xs)
lastY = myround(ys)
xs = xs + veloX
ys = ys + veloY
if lastX <> myround(xs) or lastY <> myround(ys):
MMouseMove(myround(xs), myround(ys))
W = (myrandom((myround(100/MSP)))*6)
if W < 5:
W = 5
W = myround(W*0.9)
sleep(W/1000.0)
lastdist = dist
#condition for exiting while loop
if hypot(xs - xe, ys - ye) < 1:
break
if myround(xe) <> myround(xs) or myround(ye) <> myround(ys):
MMouseMove(myround(xe), myround(ye))
mouseSpeed = MSP
return;
I tested it with different parameters, and choose this one:
humanWindMouse(xs, ys, x, y, 9, 10.0/randSpeed, 10.0*randSpeed)
but I recommend to play with parameters first, to understand, how do they affect the behavior of the mouse.
How to calc randSpeed, what should be imported, and sub-functions, such as myround(), could be found at my first post.
Hope, this code will help somebody someday)