Related
I'm trying to do a simple animation of a surface with Mayavi, but due to the lack of online examples (the few are extremely unclear or not useful) and official documentation, I'm struggling a lot. I wrote a very simple code to show where I've gone so far
import numpy as np
from mayavi import mlab
import time
#Meshgrid + u + Surface
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
u = np.ones((10,10))
surf = mlab.mesh(X,Y,u)
#Surface animation
#mlab.animate(delay=1000)
def anim():
for n in range(1,10):
global u
print(n)
u = u+1
surf.mlab_source.scalars = u
yield
anim()
mlab.show()
So very simple, every iteration I pass from a 10x10 matrix of ones to a 10x10 matrix of twos and so on.
The problems I've encountered are 3
I really can't understand what mlab_source.scalars does: fundamentally I don't know how to tell it that the z axis is changing and in doing so it follows the variation of u
Mayavi opens up, but the grid is all plane and black, like there is some issues with u
I don't understand what yield does: in the actual code I'm writing it stops the computation in a lot of advance (like in 300 steps it stops it at the step 29), but I can't remove it
I really can't get my head around, it's weeks I'm trying, hope somebody helps. Thanks in advance.
Edit:
I tried to extend #E.Klahn code in a case resembling more mine
import numpy as np
from mayavi import mlab
s = 0.01
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
Z = np.ones((10,10))
m = mlab.mesh(X, Y, Z)
#mlab.animate(delay=20)
def anim():
for i in range(1,100):
m.mlab_source.z = np.ones((10,10))*s*i
yield
anim()
mlab.show()
But I obtain only a flat surface going up, not a tridimensional object evolving, such as in his code.
Edit2:
Here the working code just copying the example of #E.Klahn, which I thank very very much
import numpy as np
from mayavi import mlab
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
X,Y=X.T,Y.T #seems an important command
u = np.ones((10,10))
surf = mlab.surf(X,Y,u)
#mlab.animate(delay=500)
def anim():
for n in range(1,10):
print(n)
surf.mlab_source.scalars = 1+np.sin(X)*np.sin(Y)*np.sin(n)
yield
anim()
mlab.show()
I just print it to give continuity to code above and to spot the differences.
Here I've shown the code to plot a cube and animate it so that it grows out from a height of 0.01 to a height of 1 using mlab.mesh. What yield does is that it returns control to the decorator so that the scene can be updated.
import numpy as np
from mayavi import mlab
s = 0.01
X = np.array([[0,0,1,1],[0,0,1,1],[0,0,0,0],[1,1,1,1]])
Y = np.array([[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]])
Z = np.array([[0,0,0,0],[s,s,s,s],[0,0,s,s],[0,0,s,s]])
m = mlab.mesh(X, Y, Z)
#mlab.animate(delay=20)
def anim():
for i in range(1,101):
print(i, end='\r')
m.mlab_source.z = np.array([[0,0,0,0],[s*i,s*i,s*i,s*i],[0,0,s*i,s*i],[0,0,s*i,s*i]])
yield
anim()
mlab.show()
scalars is one way to access the underlying data structure if that data structure has the scalars attribute. However, for mesh that attribute is not used, rather you want to access z and set those values. I would not know what your specific issue with u if unless there is a traceback to work from. The same with yield - I see no reason why that keyword would stop the animation at any particular step.
The code below shows, instead of a box evolving over time, a surface evolving with time.
import numpy as np
from mayavi import mlab
def evolving_function(X, Y, t, Lx=1, Ly=1):
return np.sin(X+Lx*t)*np.sin(Y+Ly*t)
Lx, Ly = 1,2
x = np.linspace(-10,10,100)
y = np.linspace(-10,10,100)
X,Y = np.meshgrid(x,y)
X, Y = X.T, Y.T
s = evolving_function(X, Y, 0, Lx=Lx, Ly=Ly)
m = mlab.surf(X, Y, s)
dt = 50
t = 10
steps = int(10*1000/dt)
#mlab.animate(delay=dt)
def anim():
for i in range(1,steps):
m.mlab_source.scalars = evolving_function(X, Y, dt*i, Lx=Lx, Ly=Ly)
yield
anim()
mlab.show()
I'm trying to move the mouse in a bezier curve motion in Pyautogui to simulate more of a human movement as seen here:
There is some tweening / easing functions within pyautogui but none of which represent a bezier curve type movement. I created a small script to calculate the random places it will hit before ultimately hitting its destination.
Default "Robot" linear path:
Unfortunately, which each destination the mouse temporarily stops.
import pyautogui
import time
import random
print "Randomized Mouse Started."
destx = 444;
desty = 631;
x, y = pyautogui.position() # Current Position
moves = random.randint(2,4)
pixelsx = destx-x
pixelsy = desty-y
if moves >= 4:
moves = random.randint(2,4)
avgpixelsx = pixelsx/moves
avgpixelsy = pixelsy/moves
print "Pixels to be moved X: ", pixelsx," Y: ",pixelsy, "Number of mouse movements: ", moves, "Avg Move X: ", avgpixelsx, " Y: ", avgpixelsy
while moves > 0:
offsetx = (avgpixelsx+random.randint(-8, random.randint(5,10)));
offsety = (avgpixelsy+random.randint(-8, random.randint(5,10)));
print x + offsetx, y + offsety, moves
pyautogui.moveTo(x + offsetx, y + offsety, duration=0.2)
moves = moves-1
avgpixelsx = pixelsx / moves
avgpixelsy = pixelsy / moves
Info:
Windows 10
Python 2.7
Willing to use other libraries, Python version if necessary
I've seen this post: python random mouse movements
but can't figure out how to define a "start and stop" position. The answer is pretty close to what I'm looking for.
Any ideas on how to accomplish this?
Using scipy, numpy and anything that can simply move mouse cursor:
import pyautogui
import random
import numpy as np
import time
from scipy import interpolate
import math
def point_dist(x1,y1,x2,y2):
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
cp = random.randint(3, 5) # Number of control points. Must be at least 2.
x1, y1 = pyautogui.position() # Starting position
# Distribute control points between start and destination evenly.
x = np.linspace(x1, x2, num=cp, dtype='int')
y = np.linspace(y1, y2, num=cp, dtype='int')
# Randomise inner points a bit (+-RND at most).
RND = 10
xr = [random.randint(-RND, RND) for k in range(cp)]
yr = [random.randint(-RND, RND) for k in range(cp)]
xr[0] = yr[0] = xr[-1] = yr[-1] = 0
x += xr
y += yr
# Approximate using Bezier spline.
degree = 3 if cp > 3 else cp - 1 # Degree of b-spline. 3 is recommended.
# Must be less than number of control points.
tck, u = interpolate.splprep([x, y], k=degree)
# Move upto a certain number of points
u = np.linspace(0, 1, num=2+int(point_dist(x1,y1,x2,y2)/50.0))
points = interpolate.splev(u, tck)
# Move mouse.
duration = 0.1
timeout = duration / len(points[0])
point_list=zip(*(i.astype(int) for i in points))
for point in point_list:
pyautogui.moveTo(*point)
time.sleep(timeout)
And you can remove any built-in delay in pyautogui by setting:
# Any duration less than this is rounded to 0.0 to instantly move the mouse.
pyautogui.MINIMUM_DURATION = 0 # Default: 0.1
# Minimal number of seconds to sleep between mouse moves.
pyautogui.MINIMUM_SLEEP = 0 # Default: 0.05
# The number of seconds to pause after EVERY public function call.
pyautogui.PAUSE = 0 # Default: 0.1
P.S.: Example above doesn't require any of those settings as it doesnt use public moveTo method.
For a simple solution, you can try using numpy with the bezier library:
import pyautogui
import bezier
import numpy as np
# Disable pyautogui pauses (from DJV's answer)
pyautogui.MINIMUM_DURATION = 0
pyautogui.MINIMUM_SLEEP = 0
pyautogui.PAUSE = 0
# We'll wait 5 seconds to prepare the starting position
start_delay = 5
print("Drawing curve from mouse in {} seconds.".format(start_delay))
pyautogui.sleep(start_delay)
# For this example we'll use four control points, including start and end coordinates
start = pyautogui.position()
end = start[0]+600, start[1]+200
# Two intermediate control points that may be adjusted to modify the curve.
control1 = start[0]+125, start[1]+100
control2 = start[0]+375, start[1]+50
# Format points to use with bezier
control_points = np.array([start, control1, control2, end])
points = np.array([control_points[:,0], control_points[:,1]]) # Split x and y coordinates
# You can set the degree of the curve here, should be less than # of control points
degree = 3
# Create the bezier curve
curve = bezier.Curve(points, degree)
# You can also create it with using Curve.from_nodes(), which sets degree to len(control_points)-1
# curve = bezier.Curve.from_nodes(points)
curve_steps = 50 # How many points the curve should be split into. Each is a separate pyautogui.moveTo() execution
delay = 1/curve_steps # Time between movements. 1/curve_steps = 1 second for entire curve
# Move the mouse
for i in range(1, curve_steps+1):
# The evaluate method takes a float from [0.0, 1.0] and returns the coordinates at that point in the curve
# Another way of thinking about it is that i/steps gets the coordinates at (100*i/steps) percent into the curve
x, y = curve.evaluate(i/curve_steps)
pyautogui.moveTo(x, y) # Move to point in curve
pyautogui.sleep(delay) # Wait delay
I came up with this trying to write something to draw SVG Paths with the mouse. Running the above code will make your mouse move along the same path as below. The red dots are positioned at each of the control points that define the curve.
Note that you'll have to add pyautogui.mouseDown() before and pyautogui.mouseUp() after the loop at the end of the script if you want to click and drag like I did here in GIMP:
You can check out the bezier docs here: https://bezier.readthedocs.io/en/stable/index.html
you just need know is the move_mouse((300,300))will let you mouse arrive (300,300),then never change.look at the implement,it just call the WIN32 api mouse_event.read something about it,you will find there are no "start and stop" position.i don't know how to draw bezier curve.
while True:
pos = (random.randrange(*x_bound),random.randrange(*y_bound))
move_mouse(pos)
time.sleep(1.0/steps_per_second)
look,that is the secret of animation.all you need do is write a pos = draw_bezier_curve(t)
So what I'm doing is creating sine waves with normally distributed amplitudes and frequencies - within given ranges. Eg 5V with 2-10Hz. So my attempt at this is to get my function with the given amplitude and frequency and then run it till the first turning point. From there I calculate the next function and add the y value of the previous functions turning point (as a shift) so it starts from that point. My problem is for some of the function changes I get straight lines rather than curves. If someone could tell me where I'm going wrong I'd appreciate it. Just to note, I use 8ms increments for each value to be plotted.
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
import serial
newlist = np.zeros(1)
timesnew = np.zeros(1)
volts = []
def main(amp, lowerFreq, upperFreq, time, incr):
#Creates graph and saves it in newlist and timesnew
amt = np.int(time / incr)
list = []
timels = [] # np.zeros(amt+amt)
curtime = 0
loweramp = -amp
mu, sigma = 0, 1
ybefore = 0
rand = stats.truncnorm((loweramp - mu) / sigma, (amp - mu) / sigma, loc=mu, scale=sigma)
freqr = stats.truncnorm((lowerFreq - mu) / sigma, (upperFreq - mu) / sigma, loc=mu, scale=sigma)
i = 0
while i < amt:
# get amp
thisAmp = rand.rvs()
angleFreq = 2 * np.pi * freqr.rvs()
xtp = np.arccos(0) / angleFreq #x value of turning point
yval = thisAmp * np.sin(angleFreq * xtp)
# check that yvalue(voltage) is okay to be used - is within +-amp range
while not loweramp <= yval + ybefore <= amp:
thisAmp = rand.rvs()
angleFreq = 2 * np.pi * freqr.rvs()
xtp = np.arccos(0) / angleFreq
yval = thisAmp * np.sin(angleFreq * xtp)
# now add values to list
t = 0
while t <= xtp:
ynow = thisAmp * np.sin(angleFreq * t) + ybefore
# print ynow
list.append(ynow)
curtime += incr
timels.append(curtime)
t += incr
i += 1
print i
ybefore = ynow
newlist = np.asarray(list)
timesnew = np.asarray(timels)
#a = np.column_stack((timesnew, newlist))
np.savetxt("C://foo.csv", a, delimiter=";", fmt='%.10f')
addvolts()
plt.plot(timels,list)
plt.show()
if __name__ == "__main__":
main(5, 1, 2, 25, 0.00008)
EDIT:
Basically here is the problem, after the turning point the function does not seem to be sinusodial (the line seems to be linear) and I can't understand why or atleast how to get the functions to end up being more "curvy" and not "sharp" at the turning points.
I'm thinking maybe the function changes shouldn't be too different from the previous function but then I would lose the randomness. I'd like it to "look better" but I'm not sure how to achieve that unless I ran the frequencies in order. I'm trying to emulate a "whitenoise file" that was given to me as part of a job that I applied for - the whitenoise would be sent to a digital to analog converter and be used to test equipment. Obviously I didn't get the position BUT for knowledge purposes I want to complete this.
Here is the graph of the whitenoise file I was given - 700 mins long:
From the last pic the difference between mine and the given can be seen, I think I'm going to attempt to run each function for an entire period rather than a single turning point.
True white noise is completely random, so trying to emulate white noise using some kind of function already is contradictory.
If the file you have is really supposed to be white noise than it has already undergone some kind of filtering. You can of course do the same in your program: Create some truely random numbers and use a filter function to obtain some "smoothing" effect.
For example you can use a Hann filter and colvolute the random noise with the filter. This is shown below.
import numpy as np
import scipy.signal
import matplotlib.pyplot as plt
y = np.random.rand(1600)
win = scipy.signal.hann(15)
filtered = scipy.signal.convolve(y, win, mode='same') / sum(win)
fig, (ax, ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
ax.plot(y, linestyle="-", marker=".", lw=0.3, markersize=1, color="r", alpha=0.5)
ax.set_title("random noise")
ax2.plot(y, linestyle="", marker=".", color="r", markersize=1)
ax2.plot(filtered)
ax2.set_title("filterred")
plt.show()
You might want to zoom in to better see the effect or use different parameter for the filter window.
I want to know why it will be very slow when I use matplotlib to draw lines? How to fix it?
Belows are the demo code. It used plot() to draw a line between two randomly generated points.
On my computer, 'END=100/200/500' results 'FPS=36.9/28.6/20'. I need to endless draw lines and it will get worse while time being. How to solve it?
Thanks!
import numpy as np
import matplotlib.pyplot as plt
import time
def draw_demo():
x = 100
plt.axis([0, x, 0, 1])
plt.ion()
last = 50
TIME = 5
END = 1000
time_start = time.time()
for i in range(0, END):
random_num = np.random.random()
if i > 70:
plt.axis([x - 100, x + 1, 0, 1])
x += 1
plt.plot([i, i + 1], [last, random_num])
last = random_num
plt.pause(0.0001)
print ('FPS:', END/(time.time()-time_start))
raw_input()
if __name__ == '__main__':
draw_demo()
Try something like:
import numpy as np
import matplotlib.pyplot as plt
import time
def draw_demo2(ax):
x = 100
ax.set_xlim([x-100, x + 250])
ax.set_ylim([0, 1])
END = 250
time_start = time.time()
ln, = ax.plot([], [])
x_data = []
y_data = []
for i in range(0, END):
random_num = np.random.random()
if i%100 == 99:
cur_xlim = ax.get_xlim()
ax.set_xlim(np.array(cur_xlim) + 100)
x += 1
x_data.append(x)
y_data.append(random_num)
ln.set_data(x_data, y_data)
ax.figure.canvas.draw_idle()
ax.figure.canvas.flush_events()
print ('FPS:', END/(time.time()-time_start))
if __name__ == '__main__':
draw_demo()
It might be worth truncating the x and y data buffers to the view range (as the lists are converted to arrays every time the screen is drawn.
If you need to go really fast look into blitting, however that does not interact well with changing the limits, redrawing the text is one of the slowest parts of drawing the figure.
Also try qt instead of tk, I saw a 4x speed up from that change.
matplotlib is getting slower as the script progresses because it is redrawing all of the lines that you have previously plotted - even the ones that have scrolled off the screen.
I'm new to Python am trying to plot a graph based on the pyODE tutorial found here. I'm using pylab for the plotting.
Below is the main part of the code and #added represents the code I've added in order to try and display the graph. When looking at the values themselves, y and v are the ones that change and x,z,u,w remain 0.000. When I run the program, the axis scale keeps scaling, implying that something is happening regarding the values, but no line is displayed. What am I doing wrong?
Thanks
yplot = 0 #added
#do the simulation
total_time = 0.0
dt = 0.04
while total_time<2.0:
x,y,z = body.getPosition()
u,v,w = body.getLinearVel()
print "%1.2fsec: pos=(%6.3f,%6.3f,%6.3f) vel=(%6.3f,%6.3f,%6.3f)" % \
(total_time, x,y,z,u,v,w)
world.step(dt)
total_time += dt
yplot += y #added
plot(total_time, yplot) #added
xlabel('Time') #added
ylabel('Height') #added
show() #added
The trick is to accumulate all the values you want to plot first, and then just call plot once.
yplot = 0 #added
#do the simulation
total_time = 0.0
dt = 0.04
times=[]
yvals=[]
while total_time<2.0:
x,y,z = body.getPosition()
u,v,w = body.getLinearVel()
print "%1.2fsec: pos=(%6.3f,%6.3f,%6.3f) vel=(%6.3f,%6.3f,%6.3f)" % \
(total_time, x,y,z,u,v,w)
world.step(dt)
total_time += dt
yplot += y
times.append(total_time)
yvals.append(yplot)
plot(times, yvals,'r-')
xlabel('Time') #added
ylabel('Height') #added
show() #added
The third argument to plot, 'r-', tells pylab to draw a red line connecting the points listed in times,yvals. When you plot points one-at-a-time, there is no way to tell pylab to connect the dots because each plot contains only a single point. Calling plot for each point is also highly inefficient.