22 May 2019

Pymunk tutorial

If you need to create a 2D physics environment quickly and without bothering about C++ compilation issues, it's better to choose Pymunk over Box2D. The only issue with Pymunk however, is that there's currently only one person (the Pymunk author)  answering questions about Pymunk on StackOverflow.

To get started, install Pymunk and Pygame:
pip install pygame
pip install pymunk


You can also install using pip3 if you use Python 3. Pymunk is used only for doing the 2D physics simulations. It is Pygame that sets up the environment to display the objects created in Pymunk. Instead of Pygame, you can also use Pyglet. Pyglet has features like multiple window support which Pygame doesn't have (yet), but the documentation is in my opinion, in need of improvement.

Do note that although Pymunk has a debug_draw() function to display Pymunk objects, the function is just there for the sake of being there. You are actually supposed to use Pygame's drawing functions to draw Pymunk objects. It's more efficient and supports culling of objects (not needing to draw objects outside screen bounds).


Simple program:


import sys
import pygame
from pygame.locals import * #for key codes etc
from pygame.color import *
import pymunk
import pymunk.pygame_util

fps = 60.0 #frames per second
worldWidth = 1200
worldHeight = 300
#---initializations and setup
pygame.init()
screen = pygame.display.set_mode((worldWidth, worldHeight))
clock = pygame.time.Clock()
space = pymunk.Space()
space.gravity = 0, 900
space.sleep_time_threshold = 1 #The time a group of bodies must remain idle in order to fall asleep
draw_options = pymunk.pygame_util.DrawOptions(screen)
pymunk.pygame_util.positive_y_is_up = False

#---setup line segments in space
x = 10; y = 150; thickness = 2; friction = 1.0
points = [(-100,0),(100,0),(112,12),(125,0),(137,12),(150,0),(162,12),(175,0),(200,0),(250,25),(250,0),(315,0),(320,70),(375,70),(385,0),(425,0),(425,12),(448,12),(448,0),(475,0),(475,12),(485,12),(485,0),(512,0),(512,12),(527,12),(527,0),(590,0),(590,10),(610,10),(610,15),(625,15),(625,25),(646,25),(646,35),(660,35),(660,45),(680,48),(710,-10),(755,5),(755,20),(780,25),(850,25),(825,-20),(865,-20),(895,25),(925,25),(935,0),(980,-75),(1100,-75)];
for i in range(1, len(points)):           
    floor = pymunk.Segment(space.static_body, (x+points[i-1][0], y+points[i-1][1]), (x+points[i][0], y+points[i][1]), thickness)
    floor.friction = friction
    space.add(floor)
   
while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): 
            sys.exit(0)

    #---updations
    space.step(1./fps)#Update the space for the given time step.
    screen.fill(pygame.color.THECOLORS["black"])#clear screen
    space.debug_draw(draw_options)#try using Pygame's drawing functions instead
    pygame.display.flip()#flips the draw buffer to display the objects in current frame
    dt = clock.tick(fps)#creates a delay



Adding ten cars to the space:


You'll notice the cars collide with each other. That's fixed in the next code example below.

import sys
import pygame
from pygame.locals import * #for key codes etc
from pygame.color import *
import pymunk
import pymunk.pygame_util

from pymunk import Vec2d
import random

class Car:
    space = None
    physics = None
    x = 0
    y = 0
    friction = 1.5  
    #---range of values possible for properties of car
    speed_range = range(10, 30, 5)#15
    chWd_range = range(5, 70, 5)#50#chassis width
    chHt_range = range(5, 50, 5)#10#chassis height
    wheel1Radius_range = range(6, 25, 5)#15
    wheel2Radius_range = range(6, 25, 5)#15
    chassisMass_range = range(10, 500, 50)#100
    wheel1Mass_range = range(10, 500, 50)#100
    wheel2Mass_range = range(10, 500, 50)#100 
    #---properties of car   
    speed = 0
    chWd = 0#chassis width
    chHt = 0#chassis height
    wheel1Radius = 0
    wheel2Radius = 0
    chassisMass = 0
    wheel1Mass = 0
    wheel2Mass = 0 
    #---parts to add or remove from space
    chassis_b = None
    chassis_s = None
    wheel1_b = None
    wheel1_s = None
    wheel2_b = None
    wheel2_s = None 
    pin1 = None
    pin2 = None
    pin3 = None
    pin4 = None
    motorJoint1 = None
    motorJoint2 = None
   
    def __init__(self, space, pymunk, xStartPos, yStartPos):
        self.space = space
        self.pymunk = pymunk
        self.x = xStartPos
        self.y = yStartPos
        self.reinitializeWithRandomValues()   
       
    def reinitializeWithRandomValues(self):
        self.speed = random.choice(self.speed_range)
        self.chWd = random.choice(self.chWd_range)
        self.chHt = random.choice(self.chHt_range)
        self.wheel1Radius = random.choice(self.wheel1Radius_range)
        self.wheel2Radius = random.choice(self.wheel2Radius_range)
        self.chassisMass = random.choice(self.chassisMass_range)
        self.wheel1Mass = random.choice(self.wheel1Mass_range)
        self.wheel2Mass = random.choice(self.wheel2Mass_range)    
       
    def setValues(self, v):
        i = 0
        self.speed = v[i]; i = i + 1
        if self.speed < self.speed_range[0] or self.speed > self.speed_range[-1]:
            self.speed = random.choice(self.speed_range)
        self.chWd = v[i]; i = i + 1
        if self.chWd < self.chWd_range[0] or self.chWd > self.chWd_range[-1]:
            self.chWd = random.choice(self.chWd_range)       
        self.chHt = v[i]; i = i + 1
        if self.chHt < self.chHt_range[0] or self.chHt > self.chHt_range[-1]:
            self.chHt = random.choice(self.chHt_range)       
        self.wheel1Radius = v[i]; i = i + 1
        if self.wheel1Radius < self.wheel1Radius_range[0] or self.wheel1Radius > self.wheel1Radius_range[-1]:
            self.wheel1Radius = random.choice(self.wheel1Radius_range)       
        self.wheel2Radius = v[i]; i = i + 1
        if self.wheel2Radius < self.wheel2Radius_range[0] or self.wheel2Radius > self.wheel2Radius_range[-1]:
            self.wheel2Radius = random.choice(self.wheel2Radius_range)       
        self.chassisMass = v[i]; i = i + 1
        if self.chassisMass < self.chassisMass_range[0] or self.chassisMass > self.chassisMass_range[-1]:
            self.chassisMass = random.choice(self.chassisMass_range)       
        self.wheel1Mass = v[i]; i = i + 1
        if self.wheel1Mass < self.wheel1Mass_range[0] or self.wheel1Mass > self.wheel1Mass_range[-1]:
            self.wheel1Mass = random.choice(self.wheel1Mass_range)       
        self.wheel2Mass = v[i]; i = i + 1
        if self.wheel2Mass < self.wheel2Mass_range[0] or self.wheel2Mass > self.wheel2Mass_range[-1]:
            self.wheel2Mass = random.choice(self.wheel2Mass_range)       
   
    def createCar(self):
        chassisXY = Vec2d(self.x, self.y)
        self.fitness = 0
        moment = self.pymunk.moment_for_box(self.chassisMass, (self.chWd, self.chHt))
        self.chassis_b = self.pymunk.Body(self.chassisMass, moment)
        self.chassis_s = self.pymunk.Poly.create_box(self.chassis_b, (self.chWd, self.chHt))
        self.chassis_s.color = 10,150,40
        self.chassis_b.position = chassisXY + (0, 0)
        self.space.add(self.chassis_b, self.chassis_s)          
       
        #---wheel1 (left side wheel)
        #w1 = self.addChassis(chassisXY, -chWd, 0, mass, 5, 30) #special rectangular wheel       
        moment = self.pymunk.moment_for_circle(self.wheel1Mass, 0, self.wheel1Radius)
        self.wheel1_b = self.pymunk.Body(self.wheel1Mass, moment)
        self.wheel1_s = self.pymunk.Circle(self.wheel1_b, self.wheel1Radius)
        self.wheel1_s.friction = self.friction
        self.wheel1_s.color = 180,180,180
        self.wheel1_b.position = chassisXY - (self.chWd, 0)
        self.space.add(self.wheel1_b, self.wheel1_s)         

        #---wheel2 (right side wheel)
        #w1 = self.addChassis(chassisXY, -chWd, 0, mass, 5, 30) #special rectangular wheel       
        moment = self.pymunk.moment_for_circle(self.wheel2Mass, 0, self.wheel2Radius)
        self.wheel2_b = self.pymunk.Body(self.wheel2Mass, moment)
        self.wheel2_s = self.pymunk.Circle(self.wheel2_b, self.wheel2Radius)
        self.wheel2_s.friction = self.friction
        self.wheel2_s.color = 180,180,180
        self.wheel2_b.position = chassisXY - (-self.chWd, 0)
        self.space.add(self.wheel2_b, self.wheel2_s)        
       
        self.pin1 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (-self.chWd/2,0))
        self.pin2 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (self.chWd/2,0))
        self.pin3 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.pin4 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.space.add(self.pin1, self.pin2, self.pin3, self.pin4)
        self.motorJoint1 = self.pymunk.SimpleMotor(self.wheel1_b, self.chassis_b, self.speed); self.motorJoint1.max_force = 10000000
        self.motorJoint2 = self.pymunk.SimpleMotor(self.wheel2_b, self.chassis_b, self.speed); self.motorJoint2.max_force = 10000000
        self.space.add(self.motorJoint1, self.motorJoint2)
       
    def stop(self):
        self.motorJoint1.rate(0.0)
        self.motorJoint2.rate(0.0)
       
    def removeCar(self):
        self.space.remove(self.chassis_b)
        self.space.remove(self.chassis_s)
        self.space.remove(self.wheel1_b)
        self.space.remove(self.wheel1_s)
        self.space.remove(self.wheel2_b)
        self.space.remove(self.wheel2_s)
        self.space.remove(self.pin1)
        self.space.remove(self.pin2)
        self.space.remove(self.pin3)
        self.space.remove(self.pin4)
        self.space.remove(self.motorJoint1)
        self.space.remove(self.motorJoint2)
           

fps = 60.0 #frames per second
worldWidth = 1200
worldHeight = 300
#---initializations and setup
pygame.init()
screen = pygame.display.set_mode((worldWidth, worldHeight))
clock = pygame.time.Clock()
space = pymunk.Space()
space.gravity = 0, 900
space.sleep_time_threshold = 1 #The time a group of bodies must remain idle in order to fall asleep
draw_options = pymunk.pygame_util.DrawOptions(screen)
pymunk.pygame_util.positive_y_is_up = False

#---setup line segments in space
x = 10; y = 200; thickness = 2; friction = 1.0
points = [(-100,0),(100,0),(112,12),(125,0),(137,12),(150,0),(162,12),(175,0),(200,0),(250,25),(250,0),(315,0),(320,70),(375,70),(385,0),(425,0),(425,12),(448,12),(448,0),(475,0),(475,12),(485,12),(485,0),(512,0),(512,12),(527,12),(527,0),(590,0),(590,10),(610,10),(610,15),(625,15),(625,25),(646,25),(646,35),(660,35),(660,45),(680,48),(710,-10),(755,5),(755,20),(780,25),(850,25),(825,-20),(865,-20),(895,25),(925,25),(935,0),(980,-75),(1100,-75)];
for i in range(1, len(points)):           
    floor = pymunk.Segment(space.static_body, (x+points[i-1][0], y+points[i-1][1]), (x+points[i][0], y+points[i][1]), thickness)
    floor.friction = friction
    space.add(floor)
#---create cars
xStartPos = 20; yStartPos = 0; numCars = 10; cars = []
for i in range(0, numCars):
    cars.append(Car(space, pymunk, xStartPos, yStartPos))
    cars[i].createCar()

while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): 
            sys.exit(0)

    #---updations
    space.step(1./fps)#Update the space for the given time step.
    screen.fill(pygame.color.THECOLORS["black"])#clear screen
    space.debug_draw(draw_options)#try using Pygame's drawing functions instead
    pygame.display.flip()#flips the draw buffer to display the objects in current frame
    dt = clock.tick(fps)#creates a delay


You'll notice that the cars collide with each other and the wheels of the cars collide with the car's own body too. There's a way to avoid that using ShapeFilters. The shape filter helps categorize objects into "player", "enemy" etc. to help decide which objects should collide and which shouldn't. This is very helpful because if the parts of the body of players collide, it produces very jittery motion.


Cars with ShapeFilters and text display:


import sys
import time
import pygame
from pygame.locals import * #for key codes etc
from pygame.color import *
import pymunk
import pymunk.pygame_util

from pymunk import Vec2d
import random

class Car:
    space = None
    physics = None
    x = 0
    y = 0
    friction = 1.5  
    #---range of values possible for properties of car
    speed_range = range(10, 30, 5)#15
    chWd_range = range(5, 70, 5)#50#chassis width
    chHt_range = range(5, 50, 5)#10#chassis height
    wheel1Radius_range = range(6, 25, 5)#15
    wheel2Radius_range = range(6, 25, 5)#15
    chassisMass_range = range(10, 500, 50)#100
    wheel1Mass_range = range(10, 500, 50)#100
    wheel2Mass_range = range(10, 500, 50)#100 
    #---properties of car   
    speed = 0
    chWd = 0#chassis width
    chHt = 0#chassis height
    wheel1Radius = 0
    wheel2Radius = 0
    chassisMass = 0
    wheel1Mass = 0
    wheel2Mass = 0 
    #---parts to add or remove from space
    chassis_b = None
    chassis_s = None
    wheel1_b = None
    wheel1_s = None
    wheel2_b = None
    wheel2_s = None 
    pin1 = None
    pin2 = None
    pin3 = None
    pin4 = None
    motorJoint1 = None
    motorJoint2 = None
   
    def __init__(self, space, pymunk, xStartPos, yStartPos):
        self.space = space
        self.pymunk = pymunk
        self.x = xStartPos
        self.y = yStartPos
        self.reinitializeWithRandomValues()   
       
    def reinitializeWithRandomValues(self):
        self.speed = random.choice(self.speed_range)
        self.chWd = random.choice(self.chWd_range)
        self.chHt = random.choice(self.chHt_range)
        self.wheel1Radius = random.choice(self.wheel1Radius_range)
        self.wheel2Radius = random.choice(self.wheel2Radius_range)
        self.chassisMass = random.choice(self.chassisMass_range)
        self.wheel1Mass = random.choice(self.wheel1Mass_range)
        self.wheel2Mass = random.choice(self.wheel2Mass_range)    
       
    def setValues(self, v):
        i = 0
        self.speed = v[i]; i = i + 1
        if self.speed < self.speed_range[0] or self.speed > self.speed_range[-1]:
            self.speed = random.choice(self.speed_range)
        self.chWd = v[i]; i = i + 1
        if self.chWd < self.chWd_range[0] or self.chWd > self.chWd_range[-1]:
            self.chWd = random.choice(self.chWd_range)       
        self.chHt = v[i]; i = i + 1
        if self.chHt < self.chHt_range[0] or self.chHt > self.chHt_range[-1]:
            self.chHt = random.choice(self.chHt_range)       
        self.wheel1Radius = v[i]; i = i + 1
        if self.wheel1Radius < self.wheel1Radius_range[0] or self.wheel1Radius > self.wheel1Radius_range[-1]:
            self.wheel1Radius = random.choice(self.wheel1Radius_range)       
        self.wheel2Radius = v[i]; i = i + 1
        if self.wheel2Radius < self.wheel2Radius_range[0] or self.wheel2Radius > self.wheel2Radius_range[-1]:
            self.wheel2Radius = random.choice(self.wheel2Radius_range)       
        self.chassisMass = v[i]; i = i + 1
        if self.chassisMass < self.chassisMass_range[0] or self.chassisMass > self.chassisMass_range[-1]:
            self.chassisMass = random.choice(self.chassisMass_range)       
        self.wheel1Mass = v[i]; i = i + 1
        if self.wheel1Mass < self.wheel1Mass_range[0] or self.wheel1Mass > self.wheel1Mass_range[-1]:
            self.wheel1Mass = random.choice(self.wheel1Mass_range)       
        self.wheel2Mass = v[i]; i = i + 1
        if self.wheel2Mass < self.wheel2Mass_range[0] or self.wheel2Mass > self.wheel2Mass_range[-1]:
            self.wheel2Mass = random.choice(self.wheel2Mass_range)       
   
    def createCar(self, shapeFilter):
        chassisXY = Vec2d(self.x, self.y)
        self.fitness = 0
        moment = self.pymunk.moment_for_box(self.chassisMass, (self.chWd, self.chHt))
        self.chassis_b = self.pymunk.Body(self.chassisMass, moment)
        self.chassis_s = self.pymunk.Poly.create_box(self.chassis_b, (self.chWd, self.chHt))
        self.chassis_s.color = 10,150,40
        self.chassis_b.position = chassisXY + (0, 0)
        self.chassis_s.filter = shapeFilter
        self.space.add(self.chassis_b, self.chassis_s)          
       
        #---wheel1 (left side wheel)      
        moment = self.pymunk.moment_for_circle(self.wheel1Mass, 0, self.wheel1Radius)
        self.wheel1_b = self.pymunk.Body(self.wheel1Mass, moment)
        self.wheel1_s = self.pymunk.Circle(self.wheel1_b, self.wheel1Radius)
        self.wheel1_s.friction = self.friction
        self.wheel1_s.color = 180,180,180
        self.wheel1_b.position = chassisXY - (self.chWd, 0)
        self.wheel1_s.filter = shapeFilter
        self.space.add(self.wheel1_b, self.wheel1_s)         

        #---wheel2 (right side wheel)               
        moment = self.pymunk.moment_for_circle(self.wheel2Mass, 0, self.wheel2Radius)
        self.wheel2_b = self.pymunk.Body(self.wheel2Mass, moment)
        self.wheel2_s = self.pymunk.Circle(self.wheel2_b, self.wheel2Radius)
        self.wheel2_s.friction = self.friction
        self.wheel2_s.color = 180,180,180
        self.wheel2_b.position = chassisXY - (-self.chWd, 0)
        self.wheel2_s.filter = shapeFilter
        self.space.add(self.wheel2_b, self.wheel2_s)        
       
        self.pin1 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (-self.chWd/2,0))
        self.pin2 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (self.chWd/2,0))
        self.pin3 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.pin4 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.space.add(self.pin1, self.pin2, self.pin3, self.pin4)
        self.motorJoint1 = self.pymunk.SimpleMotor(self.wheel1_b, self.chassis_b, self.speed); self.motorJoint1.max_force = 10000000
        self.motorJoint2 = self.pymunk.SimpleMotor(self.wheel2_b, self.chassis_b, self.speed); self.motorJoint2.max_force = 10000000
        self.space.add(self.motorJoint1, self.motorJoint2)
       
    def stop(self):
        self.motorJoint1.rate(0.0)
        self.motorJoint2.rate(0.0)
       
    def removeCar(self):
        self.space.remove(self.chassis_b)
        self.space.remove(self.chassis_s)
        self.space.remove(self.wheel1_b)
        self.space.remove(self.wheel1_s)
        self.space.remove(self.wheel2_b)
        self.space.remove(self.wheel2_s)
        self.space.remove(self.pin1)
        self.space.remove(self.pin2)
        self.space.remove(self.pin3)
        self.space.remove(self.pin4)
        self.space.remove(self.motorJoint1)
        self.space.remove(self.motorJoint2)
           

fps = 60.0 #frames per second
worldWidth = 1200
worldHeight = 300
#---initializations and setup
pygame.init()
screen = pygame.display.set_mode((worldWidth, worldHeight))
clock = pygame.time.Clock()
space = pymunk.Space()
space.gravity = 0, 900
space.sleep_time_threshold = 1 #The time a group of bodies must remain idle in order to fall asleep
draw_options = pymunk.pygame_util.DrawOptions(screen)
pymunk.pygame_util.positive_y_is_up = False
font = pygame.font.SysFont("Arial", 20)

#---setup line segments in space
x = 10; y = 200; thickness = 2; friction = 1.0
points = [(-100,0),(100,0),(112,12),(125,0),(137,12),(150,0),(162,12),(175,0),(200,0),(250,25),(250,0),(315,0),(320,70),(375,70),(385,0),(425,0),(425,12),(448,12),(448,0),(475,0),(475,12),(485,12),(485,0),(512,0),(512,12),(527,12),(527,0),(590,0),(590,10),(610,10),(610,15),(625,15),(625,25),(646,25),(646,35),(660,35),(660,45),(680,48),(710,-10),(755,5),(755,20),(780,25),(850,25),(825,-20),(865,-20),(895,25),(925,25),(935,0),(980,-75),(1100,-75)];
for i in range(1, len(points)):           
    floor = pymunk.Segment(space.static_body, (x+points[i-1][0], y+points[i-1][1]), (x+points[i][0], y+points[i][1]), thickness)
    floor.friction = friction
    space.add(floor)
#---create cars
xStartPos = 20; yStartPos = 0; numCars = 10; cars = []
sf = pymunk.ShapeFilter(group=1)
for i in range(0, numCars):
    cars.append(Car(space, pymunk, xStartPos, yStartPos))
    cars[i].createCar(sf)

while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): 
            sys.exit(0)

    #---updations
    space.step(1./fps)#Update the space for the given time step.
    screen.fill(pygame.color.THECOLORS["black"])#clear screen
    space.debug_draw(draw_options)#try using Pygame's drawing functions instead
    screen.blit(font.render("Cars: "+str(numCars)+". Time: "+str(time.time()), 1, THECOLORS["darkgrey"]), (5, 5))
    pygame.display.flip()#flips the draw buffer to display the objects in current frame
    dt = clock.tick(fps)#creates a delay

Hope this gives you an easy-to-understand intro to the awesomeness of Pymunk. I've created a slightly more advanced version of the above code, where the cars try to reach the end of the terrain using learning via an evolutionary algorithm.
I'm not ready to share the code yet, but when I am, I'll be posting the link on this page.

Here's the demo on YouTube:


Few other things:
  • If you are looking to create something like a walking robot, there's an example here
  • I'm relatively new to Python, so forgive me if my code is not "Pythonic" enough :-)
  • You can also look through the examples and test cases in Pymunk to see more examples of how to use various functions.

21 April 2019

How to dynamically update matplotlib plot in Python?

When you first start using matplotlib and plot a graph, it can get annoying to find out that the code control doesn't move forward until you close the plot window. There are obvious alternatives like using show(block=True) or functions like ion(), but I found that the fastest is to use blit, because it updates only the portions of the graph that needs to be updated. You could try timing it to check.



import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
plt.show()
 

There are also some other simpler (but slower) examples I found on StackOverflow:

#----scatter plot randomly
import numpy as np
import matplotlib.pyplot as plt

plt.axis([0, 10, 0, 1])

for i in range(10):
    y = np.random.random()
    plt.scatter(i, y)
    plt.pause(0.05)

plt.show()


and

#---plot random graph
import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

and one with a dynamically scaling axis:

#----using animation and autoscale. The best yet
from datetime import datetime
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from random import randrange

x_data, y_data = [], []

figure = pyplot.figure()
line, = pyplot.plot_date(x_data, y_data, '-')

def update(frame):
    x_data.append(datetime.now())
    y_data.append(randrange(0, 100))
    line.set_data(x_data, y_data)
    figure.gca().relim()
    figure.gca().autoscale_view()
    return line,

animation = FuncAnimation(figure, update, interval=200)

pyplot.show()


30 March 2019

We use cookies. We use cookies. Accept/Decline. Hey! Stop that please!

While it's great that the GDPR regulations were created and the privacy of Users is now taken a wee bit more seriously, it gave rise to a poorly conceived idea of displaying notifications about cookies on websites.

Visit almost any website and you are shown a message that's meaningless to most Users. "We use cookies. Accept/Decline". For most Users, the reaction is "Uh...ok...what does this mean anyway?".


In the same way that popup's and ad's annoyed Users, now we have a perfectly legal regulation that created new popups that take up screen space.

Why should you be concerned?
Because this notification appears only when the website can also access already-stored information to get to know more about you.
According to Directive 2009/136/EC, Article 5(3):
"Member States shall ensure that the storing of information, or the gaining of access to information already stored, in the terminal equipment of a subscriber or user is only allowed on condition that the subscriber or user concerned has given his or her consent..." 

More explanations here.
There was a P3P protocol idea floated long ago, but it didn't come through.

From a User eXperience point of view, people are annoyed with the banner, and would prefer to have an alternative.
So today I made it a point to give a feedback to Firefox and Chrome to provide a notification icon similar to the green HTTPS icon on the addressbar. If the User is concerned about cookies, they can click the icon to clear the cookies or revoke the website's privileges, instead of having to always click on an accept/decline button that takes up screen space on every single visit.
This isn't really a decision for a browser team to take, but perhaps it would be possible for them to convince website owners to put up metadata on their website that allows the browser to know that it needs to display an icon on the addressbar. Or perhaps if the browser detects the notification popup, then instead of displaying the popup, it could substitute the popup with the notification icon on the addressbar.
Anything to remove a common annoyance.

25 March 2019

Which IDE to choose for Python?

If you have a computer with a minimum of 4GB (but recommended 8GB) RAM, definitely go for PyCharm.

For those who don't want to upgrade their RAM so soon, the next best option is LiClipse. A light-weight version of Eclipse that has PyDev pre-installed. So no hassles of a separate PyDev installation. Do note that the only reason I recommend LiClipse is because of refactoring and autocomplete. It has the same bloated, unintuitive, annoying interface of Eclipse where you have to spend an eternity searching the IDE for commonly used functionalities. If you dislike Eclipse, try another IDE.
I'm not an Eclipse fan and was rather amused and almost nodding my head in agreement when I read Jason Fruit's review which said "Full disclosure: the word "Eclipse" in a programming context (or a literary one, for that matter) fills me with dread; in my mind, Eclipse is a pig strapped to a dog strapped to a whale. I'm reluctant to install Eclipse because it's such a large application; some would say bloated".


Other IDE's
  • I first tried IDLE and didn't like it because it didn't have a good number of IDE capabilities.
  • Then tried Netbeans (which I have a good amount of respect for), but the Python plugin for Netbeans didn't support autocomplete and refactoring.
  • This made me switch to Spyder, which had a nice UI, dark theme and autocomplete features, but no refactoring. I was also surprised they needed to create an entirely separate Spyder3 IDE too.
  • WingIDE supports refactoring only in the paid version. 
  • There's Komodo IDE which might be good but I didn't look into because it has a paid version and I was quite sure many features would be disabled in the free version. 
  • Eric IDE already had some poor reviews, so didn't bother with it. I personally didn't like the look of the IDE either. 
  • Visual Studio Code IDE and Sublime Text IDE's were surprisingly un-intuitive. Given their popularity, I guess it's just me that is unfamiliar with the capabilities of the IDE.

Finally found out that LiClipse existed, and after the annoying run configuration settings, I'm actually liking it. Especially because of the refactoring support. LiClipse does still have bugs with its graphics and menu's, but it wasn't too annoying.

Putting LiClipse into the Ubuntu start menu:


In the terminal, run the command:
sudo -H gedit /usr/share/applications/LiClipse.desktop

In the file, paste the following:
[Desktop Entry]
Version=1.0
Name=LiClipse
Comment=Lightweight Eclipse
GenericName=IDE
Keywords=IDE, PyDev, Eclipse, LiClipse
Exec=/home/nav/liclipse/LiClipse %u
Terminal=false
X-MultipleArgs=false
Type=Application
Icon=/home/nav/liclipse/icon.xpm
Categories=GNOME;GTK;
StartupNotify=true

Make sure you modify the file paths (shown in green) in the above code. The "%u" is so the desktop manager knows what to do if you open multiple selected objects at the same time or drop one or more objects on it. It's not really necessary.

That's it. Save and exit and you'll see LiClipse in the start menu.


16 March 2019

An impressive display of group projects at Ramaiah University of Applied Sciences

February 2019 saw a rather impressive display of technology and knowledge by the full-time students of the 2017 batch of Ramaiah University of Applied Sciences.
I went to the exhibition at the Dwaraka auditorium expecting to see some simple projects by the technical teams, but was surprised to see the dental, MBA, pharma students, product design students and many more batches presenting their projects.

Here are some of them:

Meant to be a flying machine
Pothole counter using ultrasonic and image sensors

Wind tunnel to check forces on bridge


Building work completion tracker
Water filter

Component sorter

Another component sorter
A low energy EV motor (as I remember)


A blood glucose checker using IR

Helping satellites communicate using laser
Device that uses touch, sound and visual inputs to help people communicate with the visually impaired and the hearing impaired

Cricket player performance predictor based on past data
Software tool to select the best model for a particular algorithm, using residual errors

Convolutional Neural Network to identify scenery and concoct a story based on it
Some capabilities of the techno center

Study on organ donation awareness in Bangalore

IR robot to pick up difficult-to-locate dental tools fallen on floor


Assessment of impacted third molars

Restraining seat design for children
Good brushing habits teaching aid for visually impaired children

Study on leveraging e-commerce for farming

Skill India impact assessment

Study on effectiveness of water kiosks in Bangalore
 
Study on opportunities for the transgender population

An electric vehicle

Crowd structure interaction on pedestrian bridge

Modern design for an old bike model

Foldable stall design
Mechanized coconut husk remover

Automatic vehicle classification for highway monitoring
Blade guard to safely load and unload surgical scalpel

Directly compressible excepients for drug delivery
Mandibular retractor and fracture reduction forceps

Piezoelectric transducers to generate electricity from treadmill
Semi-automatic plastering machine
IoT based headcount monitoring system for buildings
Single row paddy reaper

 What was surprising was that projects were interdisciplinary. The dental students were creating robots, the civil engineering students were doing image processing, electronics students were using machine learning to assess models. The blade-guard innovator even applied for a patent.