Matplotlib animate space vs time plot - animation

I'm currently working on traffic jams analysis and was wondering if there's a way to animate the generation of a plot of such jams.
A plot of this things grow from up to the lower end of the figure, each 'row' is a time instance. The horizontal axis is just the road indicating at each point the position of each vehicle and, with a certain numeric value, the velocity of it. So applying different colors to different velocities, you get a plot that shows how a jam evolves through time in a given road.
My question is, how can I use matplotlib to generate an animation of each instance of the road in time to get such a plot?
The plot is something like this:
I'm simulating a road with vehicles with certain velocities through time, so I wish to animate a plot showing how the traffic jams evolve...
EDIT:
I add some code to make clear what I'm already doing
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation, rc
plt.rcParams['animation.ffmpeg_path'] = u'/usr/bin/ffmpeg'
# model params
vmax = 5
lenroad = 50
prob = 0.4
# sim params
numiters = 10
# traffic model
def nasch():
gaps = np.full(road.shape, -1)
road_r4 = np.full(road.shape, -1)
for n,x in enumerate(road):
if x > -1:
d = 1
while road[(n+d) % len(road)] < 0:
d += 1
d -= 1
gaps[n] = d
road_r1 = np.where(road!=-1, np.minimum(road+1, vmax), -1)
road_r2 = np.where(road_r1!=-1, np.minimum(road_r1, gaps), -1)
road_r3 = np.where(road_r2!=-1, np.where(np.random.rand() < prob, np.maximum(road-1, 0), road), -1)
for n,x in enumerate(road_r3):
if x > -1:
road_r4[(n+x) % len(road_r3)] = x
return road_r4
def plot_nasch(*args):
road = nasch()
plot.set_array([road])
return plot,
# init road
road = np.random.randint(-10, vmax+1, [lenroad])
road = np.where(road>-1, road, -1)
# simulate
fig = plt.figure()
plot = plt.imshow([road], cmap='Pastel2', interpolation='nearest')
for i in range(numiters):
ani = animation.FuncAnimation(fig, plot_nasch, frames=100, interval=500, blit=True)
plt.show()
And I get the following figure, just one road instead of each road painted at the bottom of the previous one:

This is possibly what you want, although I'm not sure why you want to animate the time, since time is already one of the axes in the plot.
The idea here is to store the simulation results of a time-step row by row in an array and replot this array. Thereby previous simulation results are not lost.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation, rc
# model params
vmax = 5
lenroad = 50
prob = 0.4
# sim params
numiters = 25
# traffic model
def nasch():
global road
gaps = np.full(road.shape, -1)
road_r4 = np.full(road.shape, -1)
for n,x in enumerate(road):
if x > -1:
d = 1
while road[(n+d) % len(road)] < 0:
d += 1
d -= 1
gaps[n] = d
road_r1 = np.where(road!=-1, np.minimum(road+1, vmax), -1)
road_r2 = np.where(road_r1!=-1, np.minimum(road_r1, gaps), -1)
road_r3 = np.where(road_r2!=-1, np.where(np.random.rand() < prob, np.maximum(road-1, 0), road), -1)
for n,x in enumerate(road_r3):
if x > -1:
road_r4[(n+x) % len(road_r3)] = x
return road_r4
def plot_nasch(i):
print i
global road
road = nasch()
#store result in array
road_over_time[i+1,:] = road
# plot complete array
plot.set_array(road_over_time)
# init road
road = np.random.randint(-10, vmax+1, [lenroad])
road = np.where(road>-1, road, -1)
# initiate array
road_over_time = np.zeros((numiters+1, lenroad))*np.nan
road_over_time[0,:] = road
fig = plt.figure()
plot = plt.imshow(road_over_time, cmap='Pastel2', interpolation='nearest', vmin=-1.5, vmax=6.5)
plt.colorbar()
ani = animation.FuncAnimation(fig, plot_nasch, frames=numiters, init_func=lambda : 1, interval=400, blit=False, repeat=False)
plt.show()

Related

Fast Radial Symmetry Transform (FRST) implementation (python) results in unusual cross-hair looking artifacts

I am trying to implement FRST on python to detect centroids of elliptical objects (e.g. cells in microscopy images), but my implementation does not find seed points (more or less center points) of elliptical objects. This effort comes from duplicating FRST from Segmentation of Overlapping Elliptical Objects in Silhouette Images (https://ieeexplore.ieee.org/document/7300433). I don't know why I have these artifacts. An interesting thing is that I see these patterns (crosses) all in the same direction per object. Any point in the right direction to generate the same result as in the paper (just to find the seed points) will be most welcome.
Original Paper: A Fast Radial Symmetry Transform for Detecting Points of Interest by Loy and Zelinsky (ECCV 2002)
I have also tried the pre-existing python package for FRST: https://pypi.org/project/frst/. This somehow results in the same artifacts. Weird.
First image: Original Image
Second image: Sobel-operated Image
Third image: Magnitude Projection Image
Fourth image: Magnitude Projection Image with positively affected pixels only
Fifth image: FRST'd image: end-product with original image overlaid (shadowed)
Sixth image: FRST'd image by the pre-existing python package with original image overlaid (shadowed).
from scipy.ndimage import gaussian_filter
import numpy as np
from scipy.signal import convolve
# Get orientation projection image
def get_proj_img(image, radius):
workingDims = tuple((e + 2*radius) for e in image.shape)
h,w = image.shape
ori_img = np.zeros(workingDims) # Orientation Projection Image
mag_img = np.zeros(workingDims) # Magnitutde Projection Image
# Kenels for the sobel operator
a1 = np.matrix([1, 2, 1])
a2 = np.matrix([-1, 0, 1])
Kx = a1.T * a2
Ky = a2.T * a1
# Apply the Sobel operator
sobel_x = convolve(image, Kx)
sobel_y = convolve(image, Ky)
sobel_norms = np.hypot(sobel_x, sobel_y)
# Distances to afpx, afpy (affected pixels)
dist_afpx = np.multiply(np.divide(sobel_x, sobel_norms, out = np.zeros(sobel_x.shape), where = sobel_norms!=0), radius)
dist_afpx = np.round(dist_afpx).astype(int)
dist_afpy = np.multiply(np.divide(sobel_y, sobel_norms, out = np.zeros(sobel_y.shape), where = sobel_norms!=0), radius)
dist_afpy = np.round(dist_afpy).astype(int)
for cords, sobel_norm in np.ndenumerate(sobel_norms):
i, j = cords
pos_aff_pix = (i+dist_afpx[i,j], j+dist_afpy[i,j])
neg_aff_pix = (i-dist_afpx[i,j], j-dist_afpy[i,j])
ori_img[pos_aff_pix] += 1
ori_img[neg_aff_pix] -= 1
mag_img[pos_aff_pix] += sobel_norm
mag_img[neg_aff_pix] -= sobel_norm
ori_img = ori_img[:h, :w]
mag_img = mag_img[:h, :w]
print ("Did it go back to the original image size? ")
print (ori_img.shape == image.shape)
# try normalizing ori and mag img
return ori_img, mag_img
def get_sn(ori_img, mag_img, radius, kn, alpha):
ori_img_limited = np.minimum(ori_img, kn)
fn = np.multiply(np.divide(mag_img,kn), np.power((np.absolute(ori_img_limited)/kn), alpha))
# convolute fn with gaussian filter.
sn = gaussian_filter(fn, 0.25*radius)
return sn
def do_frst(image, radius, kn, alpha, ksize = 3):
ori_img, mag_img = get_proj_img(image, radius)
sn = get_sn(ori_img, mag_img, radius, kn, alpha)
return sn
Parameters:
radius = 50
kn = 10
alpha = 2
beta = 0
stdfactor = 0.25

Updating matplotlib live graph in wxPython panel with scrolling x-axis

I am trying to animate a live graph in a wx.Panel. I would like to have the x-axis update like this example. Many of the examples I see are basic and don't take into consideration other controls and functions in the class. Others have so many extras that I get lost in the weeds. I can't get the animation command in the right place or update the x-axis. Here is the code:
import wx
import logging
import numpy as np
import matplotlib
import time
import matplotlib.animation as animation
matplotlib.use('WXAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
fTemp = ""
x = 0
class TempClass(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, title="", size=(600,500))
panel = wx.Panel(self)
self.fig = Figure(figsize=(6,4), dpi=75, facecolor='lightskyblue', edgecolor='r')
self.canvas = FigureCanvas(self, -1, self.fig)
self.ax = self.fig.add_subplot(111)
self.ax2 = self.ax.twinx()
self.ax.set_ylim(60,90)
self.ax.set_xlim(0,24)
self.ax2.set_ylim(0,100)
# major ticks every 5, minor ticks every 1
xmajor_ticks = np.arange(0, 24, 5)
xminor_ticks = np.arange(0, 24, 1)
self.ax.set_xticks(xmajor_ticks)
self.ax.set_xticks(xminor_ticks, minor=True)
self.ax.grid()
self.ax.set_xlabel('Hour')
self.ax.set_ylabel('Temp')
self.ax2.set_ylabel('Humidity')
self.ax.set_title('Temperature')
# The graph does not show in the panel when this in uncommented
#self.ani = animation.FuncAnimation(self.fig, self.onPlotTemp, interval=1000)
self.fanSensorTimer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.onPlotTemp, self.fanSensorTimer)
self.fanSensorBtn = wx.Button(self, -1, "Start Sensor")
self.Bind(wx.EVT_BUTTON, self.onStartTempPlot, self.fanSensorBtn)
font1 = wx.Font(18, wx.DEFAULT,wx.NORMAL,wx.BOLD)
self.displayTemp = wx.StaticText(self, -1, "Current Tempurature")
self.curTempTxt = wx.TextCtrl(self, -1, "0",size=(100,40), style=wx.TE_READONLY|wx.TE_CENTRE|wx.BORDER_NONE)
self.curTempTxt.SetFont(font1)
self.displayHum = wx.StaticText(self, -1, "Current Humidity")
self.curHumTxt = wx.TextCtrl(self, -1,"0", size=(100,40), style=wx.TE_READONLY|wx.TE_CENTRE|wx.BORDER_NONE)
self.curHumTxt.SetFont(font1)
self.displayBox = wx.GridBagSizer(hgap=5,vgap=5)
self.displayBox.Add(self.displayTemp, pos=(0,0), flag=wx.TOP|wx.LEFT, border=5)
self.displayBox.Add(self.displayHum, pos=(0,1), flag=wx.TOP, border=5)
self.displayBox.Add(self.curTempTxt, pos=(1,0), flag=wx.ALL, border=5)
self.displayBox.Add(self.curHumTxt, pos=(1,1), flag=wx.ALL, border=5)
#---------
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, wx.ALIGN_CENTER|wx.ALL, 1)
self.vbox.Add(self.fanSensorBtn)
self.vbox.Add(self.displayBox, wx.ALIGN_CENTER|wx.ALL, 1)
self.SetSizer(self.vbox)
self.vbox.Fit(self)
def start(self):
# get temp/humidity reading from node
pass
def readTemp(self, data1, data2):
"Populates Current Temp"
global fTemp
self.curTempTxt.Clear()
a = format(data1, '08b')
b = format(data2, '08b')
x = a+b
y = int(x, base=2)
cTemp = ((175.72 * y)/65536)-46.85
fTemp = cTemp *1.8+32
cel = format(cTemp,'.1f')
far = format(fTemp,'.1f')
self.curTempTxt.WriteText(far + (u'\u00b0')+"F")
def rh1(self, data1, data2):
"Populates Current RH"
global relhum
self.curHumTxt.Clear()
a = format(data1, '08b')
b = format(data2, '08b')
x = a+b
y = int(x, base=2)
rh = ((125 * y)/65536)-6
relhum = format(rh,'.1f')
self.curHumTxt.WriteText(relhum + " %")
def onStartTempPlot(self,event):
#set for a short time period for testing purposes
self.fanSensorTimer.Start(5000)
print "Timer Started"
def onPlotTemp(self,event):
global fTemp, x, relhum
x +=1
y = int(fTemp)
y2 = float(relhum)
self.ax.plot(x,y,'r.')
self.ax2.plot(x,y2,'k.')
self.fig.canvas.draw()
# send message to node for another reading of temp/humidity
if __name__ == "__main__":
app = wx.App(False)
frame = TempClass()
frame.Show()
frame.start()
logging.basicConfig(level=logging.DEBUG)
app.MainLoop()
I would like to see the x axis increment as the data is plotted beyond the 24 hour point on the graph; when data for point 25 appears, the first point is dropped and the x axis shows '25'. The animation is commented out because it causes the graph to disappear until a point is plotted.
Here is a runnable example of what I am trying to achieve with the x axis:
import numpy
from matplotlib.pylab import *
from mpl_toolkits.axes_grid1 import host_subplot
import matplotlib.animation as animation
# Sent for figure
font = {'size' : 9}
matplotlib.rc('font', **font)
# Setup figure and subplots
f0 = figure(num = 0, figsize = (6, 4))#, dpi = 100)
f0.suptitle("Oscillation decay", fontsize=12)
ax01 = subplot2grid((2, 2), (0, 0))
# Set titles of subplots
ax01.set_title('Position vs Time')
# set y-limits
ax01.set_ylim(0,2)
# sex x-limits
ax01.set_xlim(0,1)
# Turn on grids
ax01.grid(True)
# set label names
ax01.set_xlabel("x")
ax01.set_ylabel("py")
# Data Placeholders
yp1=zeros(0)
yv1=zeros(0)
yp2=zeros(0)
yv2=zeros(0)
t=zeros(0)
# set plots
p011, = ax01.plot(t,yp1,'b-', label="yp1")
p012, = ax01.plot(t,yp2,'g-', label="yp2")
# set lagends
ax01.legend([p011,p012], [p011.get_label(),p012.get_label()])
# Data Update
xmin = 0
xmax = 24
x = 0
def updateData(self):
global x
global yp1
global yv1
global yp2
global yv2
global t
tmpp1 = 1 + exp(-x) *sin(2 * pi * x)
tmpv1 = - exp(-x) * sin(2 * pi * x) + exp(-x) * cos(2 * pi * x) * 2 * pi
yp1=append(yp1,tmpp1)
yv1=append(yv1,tmpv1)
yp2=append(yp2,0.5*tmpp1)
yv2=append(yv2,0.5*tmpv1)
t=append(t,x)
x += 1
p011.set_data(t,yp1)
p012.set_data(t,yp2)
if x >= xmax-1:
p011.axes.set_xlim(x-xmax+1,x+1)
return p011
# interval: draw new frame every 'interval' ms
# frames: number of frames to draw
simulation = animation.FuncAnimation(f0, updateData, blit=False, frames=200, interval=20, repeat=False)
plt.show()
You are not incrementing the X axis limit or the ticks.
def onPlotTemp(self,event):
global fTemp, x, relhum
x +=1
y = int(fTemp)
y2 = float(relhum)
if x >= 24-1:
self.ax.set_xlim(x-24+1,x+1)
xmajor_ticks = np.arange(x-24+1,x+5, 5)
xminor_ticks = np.arange(x-24+1, x+1,1)
self.ax.set_xticks(xmajor_ticks)
self.ax.set_xticks(xminor_ticks, minor=True)
self.ax.plot(x,y,'r.')
self.ax2.plot(x,y2,'k.')
self.fig.canvas.draw()
I'm not sure if the above resets the ticks the way you want them but you get the idea. Obviously I have hard-coded 24 as your limit, you may want to create a variable to sort that out.

How do I plot two animations in a single plot with matplotlib?

In the following code I have two separate animations and I have plotted them in a two separate subplots. I want both of them to run in a single plot instead of this. I have tried the approach explained below but it is giving me issues as explained below. Please help
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time as t
x = np.linspace(0,5,100)
fig = plt.figure()
p1 = fig.add_subplot(2,1,1)
p2 = fig.add_subplot(2,1,2)
def gen1():
i = 0.5
while(True):
yield i
i += 0.1
def gen2():
j = 0
while(True):
yield j
j += 1
def run1(c):
p1.clear()
p1.set_xlim([0,15])
p1.set_ylim([0,100])
y = c*x
p1.plot(x,y,'b')
def run2(c):
p2.clear()
p2.set_xlim([0,15])
p2.set_ylim([0,100])
y = c*x
p2.plot(x,y,'r')
ani1 = animation.FuncAnimation(fig,run1,gen1,interval=1)
ani2 = animation.FuncAnimation(fig,run2,gen2,interval=1)
fig.show()
I tried creating a single subplot instead of p1 and p2 and have both the plots graphed in that single subplot. That is just plotting one graph and not both of them. As far as I can say it is because one of them is getting cleared right after it is plotted.
How do I get around this problem?
As you do not show the code that is actually producing the problem, it's hard to tell where the problem lies.
But to answer the question of how to animate two lines in the same axes (subplot), we can just get rid of the clear() command and update the lines, instead of producing a new plot for every frame (which is more efficient anyways).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,15,100)
fig = plt.figure()
p1 = fig.add_subplot(111)
p1.set_xlim([0,15])
p1.set_ylim([0,100])
# set up empty lines to be updates later on
l1, = p1.plot([],[],'b')
l2, = p1.plot([],[],'r')
def gen1():
i = 0.5
while(True):
yield i
i += 0.1
def gen2():
j = 0
while(True):
yield j
j += 1
def run1(c):
y = c*x
l1.set_data(x,y)
def run2(c):
y = c*x
l2.set_data(x,y)
ani1 = animation.FuncAnimation(fig,run1,gen1,interval=1)
ani2 = animation.FuncAnimation(fig,run2,gen2,interval=1)
plt.show()

Speeding up vectorized eye-tracking algorithm in numpy

I'm trying to implement Fabian Timm's eye-tracking algorithm [http://www.inb.uni-luebeck.de/publikationen/pdfs/TiBa11b.pdf] (found here: [http://thume.ca/projects/2012/11/04/simple-accurate-eye-center-tracking-in-opencv/]) in numpy and OpenCV and I've hit a snag. I think I've vectorized my implementation decently enough, but it's still not fast enough to run in real time and it doesn't detect pupils with as much accuracy as I had hoped. This is my first time using numpy, so I'm not sure what I've done wrong.
def find_pupil(eye):
eye_len = np.arange(eye.shape[0])
xx,yy = np.meshgrid(eye_len,eye_len) #coordinates
XX,YY = np.meshgrid(xx.ravel(),yy.ravel()) #all distance vectors
Dx,Dy = [YY-XX, YY-XX] #y2-y1, x2-x1 -- simpler this way because YY = XXT
Dlen = np.sqrt(Dx**2+Dy**2)
Dx,Dy = [Dx/Dlen, Dy/Dlen] #normalized
Gx,Gy = np.gradient(eye)
Gmagn = np.sqrt(Gx**2+Gy**2)
Gx,Gy = [Gx/Gmagn,Gy/Gmagn] #normalized
GX,GY = np.meshgrid(Gx.ravel(),Gy.ravel())
X = (GX*Dx+GY*Dy)**2
eye = cv2.bitwise_not(cv2.GaussianBlur(eye,(5,5),0.005*eye.shape[1])) #inverting and blurring eye for use as w
eyem = np.repeat(eye.ravel()[np.newaxis,:],eye.size,0)
C = (np.nansum(eyem*X, axis=0)/eye.size).reshape(eye.shape)
return np.unravel_index(C.argmax(), C.shape)
and the rest of the code:
def find_eyes(face):
left_x, left_y = [int(floor(0.5 * face.shape[0])), int(floor(0.2 * face.shape[1]))]
right_x, right_y = [int(floor(0.1 * face.shape[0])), int(floor(0.2 * face.shape[1]))]
area = int(floor(0.2 * face.shape[0]))
left_eye = (left_x, left_y, area, area)
right_eye = (right_x, right_y, area, area)
return [left_eye,right_eye]
faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
video_capture = cv2.VideoCapture(0)
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=cv2.CASCADE_SCALE_IMAGE
)
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
roi_gray = gray[y:y+h, x:x+w]
roi_color = frame[y:y+h, x:x+w]
eyes = find_eyes(roi_gray)
for (ex,ey,ew,eh) in eyes:
eye_gray = roi_gray[ey:ey+eh,ex:ex+ew]
eye_color = roi_color[ey:ey+eh,ex:ex+ew]
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(255,0,0),2)
px,py = find_pupil(eye_gray)
cv2.rectangle(eye_color,(px,py),(px+1,py+1),(255,0,0),2)
# Display the resulting frame
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()
You can perform many of those operations that save replicated elements and then perform some mathematical opertaions by directly performing the mathematical operatrions after creating singleton dimensions that would allow NumPy broadcasting. Thus, there would be two benefits - On the fly operations to save workspace memory and performance boost. Also, at the end, we can replace the nansum calculation with a simplified version. Thus, with all of that philosophy in mind, here's one modified approach -
def find_pupil_v2(face, x, y, w, h):
eye = face[x:x+w,y:y+h]
eye_len = np.arange(eye.shape[0])
N = eye_len.size**2
eye_len_diff = eye_len[:,None] - eye_len
Dlen = np.sqrt(2*((eye_len_diff)**2))
Dxy0 = eye_len_diff/Dlen
Gx0,Gy0 = np.gradient(eye)
Gmagn = np.sqrt(Gx0**2+Gy0**2)
Gx,Gy = [Gx0/Gmagn,Gy0/Gmagn] #normalized
B0 = Gy[:,:,None]*Dxy0[:,None,:]
C0 = Gx[:,None,:]*Dxy0
X = ((C0.transpose(1,0,2)[:,None,:,:]+B0[:,:,None,:]).reshape(N,N))**2
eye1 = cv2.bitwise_not(cv2.GaussianBlur(eye,(5,5),0.005*eye.shape[1]))
C = (np.nansum(X,0)*eye1.ravel()/eye1.size).reshape(eye1.shape)
return np.unravel_index(C.argmax(), C.shape)
There's one repeat still left in it at Dxy. It might be possible to avoid that step and Dxy0 could be fed directly into the step that uses Dxy to give us X, but I haven't worked through it. Everything's converted to broadcasting based!
Runtime test and output verification -
In [539]: # Inputs with random elements
...: face = np.random.randint(0,10,(256,256)).astype('uint8')
...: x = 40
...: y = 60
...: w = 64
...: h = 64
...:
In [540]: find_pupil(face,x,y,w,h)
Out[540]: (32, 63)
In [541]: find_pupil_v2(face,x,y,w,h)
Out[541]: (32, 63)
In [542]: %timeit find_pupil(face,x,y,w,h)
1 loops, best of 3: 4.15 s per loop
In [543]: %timeit find_pupil_v2(face,x,y,w,h)
1 loops, best of 3: 529 ms per loop
It seems we are getting close to 8x speedup!

Bouncing text animation issue in Pygame

I'm trying to code a program that can take text and animate it to bounce on a loop, like a ball bouncing to the floor. I used a similar piece of code I found a starting point as I'm still fairly new to Pygame (thank you Pete Shinners, whoever you are), but after updating the code and playing with it for a long time I still can't get it to blit to the screen correctly. The text starts above the rendered area and then gradually falls into view, but the top part of the text is cut off.
I've tried moving the blitted region around the window and resizing the rectangles and surface the program is using, but nothing seems to fix it.
import os, sys, math, pygame, pygame.font, pygame.image
from pygame.locals import *
def bounce():
# define constants
G = 0.98
FLOOR = 0
COEFFICIENT = 0.8
#define variables
ball = 500
direction = 'DOWN'
v = 0
count = 0
#create array to store data
array = [ball]
while True:
if count == 4:
return array
elif ball > FLOOR and direction == 'DOWN':
v += G
if (ball - v) >= FLOOR:
ball = ball - v
array.append(round(ball,2))
else:
ball = FLOOR
array.append(round(ball,2))
direction = 'UP'
v *= COEFFICIENT
count += 1
elif ball >= FLOOR and direction == 'UP':
v -= G
if (ball + v) >= FLOOR:
ball = ball + v
array.append(round(ball,2))
if v <= 0:
direction = 'DOWN'
else:
ball = FLOOR
array.append(ball)
direction = 'UP'
v *= COEFFICIENT
class textBouncy:
array = bounce()
def __init__(self, font, message, fontcolor, amount=10):
# Render the font message
self.base = font.render(message, 0, fontcolor)
# bounce amount (height)
self.amount = amount
#size = rect of maximum height/width of text
self.size = self.base.get_rect().inflate(0, amount).size
#normalise array to meet height restriction
self.array = [round(-x/(500/amount),2) for x in array]
def animate(self):
# create window surface s
s = pygame.Surface(self.size)
# height = max inflated height
height = self.size[1]
# define a step-sized rectangle in the location of the step
src = Rect(0, 0, self.base.get_width(), height)
# moves the message according to the array list.
dst = src.move(0, self.array[i])
if (i + 1) == len(self.array):
global i
i = 0
# blits the information onto the screen
s.blit(self.base, dst, src)
return s
entry_info = 'Bouncing ball text'
if __name__ == '__main__':
pygame.init()
#create text renderer
i = 0
array = bounce()
bigfont = pygame.font.Font(None, 60)
white = 255, 255, 255
renderer = textBouncy(bigfont, entry_info, white, 16)
text = renderer.animate()
#create a window the correct size
win = pygame.display.set_mode(text.get_size())
win.blit(text, (0, 10))
pygame.display.flip()
#run animation loop
finished = 0
while True:
pygame.time.delay(10)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
text = renderer.animate()
i += 1
win.blit(text, (0, 10)) # blits the finished product from animate
pygame.display.flip()
(Quote) "it all comes down to math really" Kay so you need to - the y axis when you want to make it go up and + the x axis to make it go side ways you could make it go up and down will moveing it horizontally and then when it reaches a point it will stop moving horizontally and just bonce up and down +ing it more every time
That was my 100$ which took me 5 mins to write
After revisiting this I managed to work this out - I needed to add everything I blitted down to compensate for the bounce up. So in the __init__function:
self.array = [round(-x/(500/amount),2)**+self.amount** for x in array]
Works perfectly now :)

Resources