Python Depository on fire!

Enhance your data visualization with Python's powerful Matplotlib and PyGame libraries! In here you will find a curated collection of programs I've encountered during my learning journey. Each program is accompanied by a concise description, the requirements list, and the full code implementation to keep the flame going. We invite you to dive into the world of Python programming and unleash your creativity! Happy coding!

Flock Simulation

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • pandas==2.1.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8
  • pytz==2023.3.post1
  • seaborn==0.13.0
  • six==1.16.0
  • tzdata==2023.3
$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame
"""
boids.py 

Implementation of Craig Reynold's BOIDs

Author: Mahesh Venkitachalam
"""

import sys, argparse
import math
import numpy as np
import matplotlib.pyplot as plt 
import matplotlib.animation as animation
from scipy.spatial.distance import squareform, pdist, cdist
from numpy.linalg import norm
from matplotlib import animation as ani


width, height = 640, 480

class Boids:
    """Class that represents Boids simulation"""
    def __init__(self, N):
        """ initialize the Boid simulation"""
        # init position & velocities
        self.pos = [width/2.0, height/2.0] + 10*np.random.rand(2*N).reshape(N, 2)
        # normalized random velocities
        angles = 2*math.pi*np.random.rand(N)
        self.vel = np.array(list(zip(np.sin(angles), np.cos(angles))))
        self.N = N
        # min dist of approach
        self.minDist = 25.0
        # max magnitude of velocities calculated by "rules"
        self.maxRuleVel = 0.03
        # max maginitude of final velocity
        self.maxVel = 2.0

    def tick(self, frameNum, pts, beak):
        """Update the simulation by one time step."""
        # get pairwise distances
        self.distMatrix = squareform(pdist(self.pos))
        # apply rules:
        self.vel += self.applyRules()
        self.limit(self.vel, self.maxVel)
        self.pos += self.vel
        self.applyBC()
        # update data
        pts.set_data(self.pos.reshape(2*self.N)[::2], 
                        self.pos.reshape(2*self.N)[1::2])
        vec = self.pos + 10*self.vel/self.maxVel
        beak.set_data(vec.reshape(2*self.N)[::2], 
                        vec.reshape(2*self.N)[1::2])

    def limitVec(self, vec, maxVal):
        """limit magnitide of 2D vector"""
        mag = norm(vec)
        if mag > maxVal:
            vec[0], vec[1] = vec[0]*maxVal/mag, vec[1]*maxVal/mag
    
    def limit(self, X, maxVal):
        """limit magnitide of 2D vectors in array X to maxValue"""
        for vec in X:
            self.limitVec(vec, maxVal)
            
    def applyBC(self):
        """apply boundary conditions"""
        deltaR = 2.0
        for coord in self.pos:
            if coord[0] > width + deltaR:
                coord[0] = - deltaR
            if coord[0] < - deltaR:
                coord[0] = width + deltaR    
            if coord[1] > height + deltaR:
                coord[1] = - deltaR
            if coord[1] < - deltaR:
                coord[1] = height + deltaR
    
    def applyRules(self):
        # apply rule #1 - Separation
        D = self.distMatrix < 25.0
        vel = self.pos*D.sum(axis=1).reshape(self.N, 1) - D.dot(self.pos)
        self.limit(vel, self.maxRuleVel)

        # different distance threshold
        D = self.distMatrix < 50.0

        # apply rule #2 - Alignment
        vel2 = D.dot(self.vel)
        self.limit(vel2, self.maxRuleVel)
        vel += vel2;

        # apply rule #1 - Cohesion
        vel3 = D.dot(self.pos) - self.pos
        self.limit(vel3, self.maxRuleVel)
        vel += vel3

        return vel

    def buttonPress(self, event):
        """event handler for matplotlib button presses"""
        # left click - add a boid
        if event.button == 1:
            self.pos = np.concatenate((self.pos, 
                                        np.array([[event.xdata, event.ydata]])), 
                                        axis=0)
            # random velocity
            angles = 2*math.pi*np.random.rand(1)
            v = np.array(list(zip(np.sin(angles), np.cos(angles))))
            self.vel = np.concatenate((self.vel, v), axis=0)
            self.N += 1 
        # right click - scatter
        elif event.button == 3:
            # add scattering velocity 
            self.vel += 0.1*(self.pos - np.array([[event.xdata, event.ydata]]))
        
def tick(frameNum, pts, beak, boids):
    #print frameNum
    """update function for animation"""
    boids.tick(frameNum, pts, beak)
    return pts, beak

# main() function
def main():
    # use sys.argv if needed
    print('starting boids...')

    parser = argparse.ArgumentParser(description="Implementing Craig Reynold's Boids...")
    # add arguments
    parser.add_argument('--num-boids', dest='N', required=False)
    args = parser.parse_args()

    # number of boids
    N = 10
    if args.N:
        N = int(args.N)

    # create boids
    boids = Boids(N)

    # setup plot
    fig = plt.figure()
    ax = plt.axes(xlim=(0, width), ylim=(0, height))

    pts, = ax.plot([], [], markersize=10, 
                    c='k', marker='o', ls='None')
    beak, = ax.plot([], [], markersize=4, 
                    c='r', marker='o', ls='None')
    anim = animation.FuncAnimation(fig, tick, fargs=(pts, beak, boids), 
                                    interval=50)

    # add a "button press" event handler
    cid = fig.canvas.mpl_connect('button_press_event', boids.buttonPress)


    plt.show()

# call main
if __name__ == '__main__':
    main()
    

                        
Your GIF

Sine Wave

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • pandas==2.1.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8
  • pytz==2023.3.post1
  • seaborn==0.13.0
  • six==1.16.0
  • tzdata==2023.3
$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame
# main.py
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation

plt.rcParams["figure.figsize"] = [7.50, 3.50]
plt.rcParams["figure.autolayout"] = True

fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

def init():
    line.set_data([], [])
    return line,

def animate(i):
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    line.set_data(x, y)
    return line,

anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
plt.show()
    

                        
Your GIF

Progresive Sine Wave

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • pandas==2.1.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8
  • pytz==2023.3.post1
  • seaborn==0.13.0
  • six==1.16.0
  • tzdata==2023.3
$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

'''
Animation where the sine and cosine functions are plotted
progressively on the screen
'''

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

from tqdm.autonotebook import tqdm

fig = plt.figure(figsize=(7,2), dpi=100)
ax = plt.subplot()
X = np.linspace(-np.pi, np.pi, 256, endpoint=True)

C, S = np.cos(X), np.sin(X)

line1, = ax.plot(X, C, marker="o", markevery=[-1],
    markeredgecolor="white")
line2, = ax.plot(X, S, marker="o", markevery=[-1],
    markeredgecolor="white")

def update(frame):
    line1.set_data(X[:frame], C[:frame])
    line2.set_data(X[:frame], S[:frame])

# save animation as video
#writer = animation.FFMpegWriter(fps=30)
#anim = animation.FuncAnimation(fig, update, interval=10, frames=len(X))
#anim.save("sine-cosine.mp4", writer=writer, dpi=100)

# show video rendering progress
#bar = tqdm(total=len(X))
#anim.save("../sine-cosine.mp4", writer=writer, dpi=300,
#progress_callback = lambda i, n: bar.update(1))
#bar.close()
    
ani = animation.FuncAnimation(fig, update, frames=len(X), interval=10)

# save as a gif
ani.save('progressive-sine.gif', writer='pillow', fps=30)
plt.show()
                            
    

                        
Your GIF

3D Animation

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame
# main.py

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import animation
from PIL import Image


# Create the figure and 3D axis
# fig = plt.figure()
fig = plt.figure(figsize=(7,5))
ax = fig.add_subplot(111, projection='3d')
# ax.view_init(elev=30, azim=115)  # Set the elevation and azimuthal angle
# Set the initial view
elev = 30
azim = 115
ax.view_init(elev=elev, azim=azim)


# Create the data for the sine wave
# x = np.linspace(0, 2*np.pi, 100)
# y = np.linspace(0, 2*np.pi, 100)
x = np.linspace(0, 10, 50)
y = np.linspace(0, 10, 50)
X, Y = np.meshgrid(x, y)
# Z = np.sin(X + Y)
Z = (np.sin(X))



# Create the initial plot
surf = ax.plot_surface(X, Y, Z, alpha=0.5) # cmap=plt.cm.coolwarm, alpha=0.5)

# Define the animation function
def animate(i):
    ax.clear()
    Z = np.sin(X + Y + i/10)
    surf = ax.plot_surface(X, Y, Z, alpha=0.5) # cmap=plt.cm.coolwarm, alpha=0.5)

# Function to update the view (azimuthal angle) for the animation
def update_view(frame):
    global azim
    azim += 2  # Change the azimuthal angle incrementally
    ax.view_init(elev=elev, azim=azim)
    return line,

# Create the animation
ani = animation.FuncAnimation(fig, animate, frames=200, interval=20)

# ani = animation.FuncAnimation(fig, update_view, frames=range(180), repeat=True, blit=True)

# Save the animation frames as individual images
frames = []
for i in range(200):
    animate(i)
    fig.canvas.draw()
    img = np.fromstring(fig.canvas.tostring_rgb(), dtype='uint8')
    img = img.reshape(fig.canvas.get_width_height()[::-1] + (3,))
    frames.append(Image.fromarray(img))

# Save the frames as a GIF
frames[0].save('animation.gif', save_all=True, append_images=frames[1:], duration=50, loop=0)


# Show the plot
plt.show()
    
Your GIF

Crisp-looking Feynman Diagrams

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame
"""
VBH
===
Vector Boson Fusion.
"""
import matplotlib.pyplot as plt
from feynman import Diagram

fig = plt.figure(figsize=(10.,10.))
ax = fig.add_axes([0,0,1,1], frameon=False)

diagram = Diagram(ax)
diagram.text(.5,0.9,"Vector Boson Fusion (VBF)",fontsize=40)
in1 = diagram.vertex(xy=(.1,.8), marker='')
in2= diagram.vertex(xy=(.1,.2), marker='')
v1 = diagram.vertex(xy=(.4,.7))
v2 = diagram.vertex(xy=(.4,.3))
v3 = diagram.vertex(xy=(.6,.5))
out1 = diagram.vertex(xy=(.9,.8), marker='')
out2 = diagram.vertex(xy=(.9,.2), marker='')
higgsout = diagram.vertex(xy=(.9,.5))

q1 = diagram.line(in1, v1, arrow=False)
q2 = diagram.line(in2, v2, arrow=False)
wz1 = diagram.line(v1, v3, style='wiggly')
wz2 = diagram.line(v2, v3, style='wiggly')
higgs = diagram.line(v3, higgsout, style='dashed', arrow=False)
q3 = diagram.line(v1, out1, arrow=False)
q4 = diagram.line(v2, out2, arrow=False)

q1.text("$q_1$",fontsize=30)
q2.text("$q_2$",fontsize=30)
diagram.text(v3.xy[0], v3.xy[1]+0.11, "$Z/W^\pm$",fontsize=35)
wz2.text("$Z/W^\pm$",fontsize=35)
q3.text("$q_3$",fontsize=30)
q4.text("$q_4$",fontsize=30)
higgs.text("$H$",fontsize=30)

diagram.plot()
plt.show()
                            
    
Your GIF

Delaunay triangulation & Voronoi Diagram

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay, Voronoi, voronoi_plot_2d

# Step 1: Generate Random Points
num_points = 10  # Adjust the number of points as needed
points = np.random.rand(num_points, 2)  # Random points in a unit square (0, 1) x (0, 1)

# Step 2: Perform Delaunay Triangulation
tri = Delaunay(points)

# Step 3: Create Voronoi Diagram
vor = Voronoi(points)

# Step 4: Plotting the graphs side by side
fig, axs = plt.subplots(1, 3, figsize=(15, 5))

# Plot 1: Points
axs[0].plot(points[:, 0], points[:, 1], 'bo')
axs[0].set_title('Random Points')
axs[0].set_xlim(0, 1)
axs[0].set_ylim(0, 1)
axs[0].set_aspect('equal', adjustable='box')

# Plot 2: Delaunay Triangulation
axs[1].triplot(points[:, 0], points[:, 1], tri.simplices, 'go-', lw=1.5)
axs[1].plot(points[:, 0], points[:, 1], 'bo')
axs[1].set_title('Delaunay Triangulation')
axs[1].set_xlim(0, 1)
axs[1].set_ylim(0, 1)
axs[1].set_aspect('equal', adjustable='box')

# Plot 3: Voronoi Diagram
voronoi_plot_2d(vor, ax=axs[2], show_vertices=False, line_colors='orange', line_width=2)
axs[2].plot(points[:, 0], points[:, 1], 'bo')
axs[2].set_title('Voronoi Diagram')
axs[2].set_xlim(0, 1)
axs[2].set_ylim(0, 1)
axs[2].set_aspect('equal', adjustable='box')

# Display the plots
plt.tight_layout()
plt.show()
                            
    
Your IMG

Animated Delaunay Triangulation

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

'''
Animating the scipy.spatial.Delaunay (as above) was 
too complicated so I did it the long way.
'''                            
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import math
from PIL import Image

def points_to_triangle(point1, point2, point3):
    '''
    Convert a set of three disparate points into a triangle.
    '''
    return [list(point1), list(point2), list(point3)]

def get_distance(point1, point2):
    '''
    Calculate the distance between any two points using the Pythagorean theorem.
    '''
    return math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)

def triangle_to_circumcenter(triangle):
    '''
    Find the circumcenter and circumradius for any given triangle.
    '''
    x, y, z = (complex(triangle[0][0], triangle[0][1]),
                complex(triangle[1][0], triangle[1][1]),
                complex(triangle[2][0], triangle[2][1]))
    w = z - x
    w /= y - x
    c = (x - y) * (w - abs(w) ** 2) / (2j * w.imag) - x
    radius = abs(c + x)
    return (0 - c.real, 0 - c.imag), radius

def gen_delaunay(points):
    '''
    Generate Delaunay triangulation for a set of points.
    '''
    delaunay = [points_to_triangle([-5, -5], [-5, 10], [10, -5])]
    for point in points:
        invalid_triangles = []
        for triangle in delaunay:
            circumcenter, radius = triangle_to_circumcenter(triangle)
            if get_distance(circumcenter, point) < radius:
                invalid_triangles.append(triangle)
        
        for triangle in invalid_triangles:
            delaunay.remove(triangle)

        points_in_invalid = []
        for triangle in invalid_triangles:
            points_in_invalid.extend(triangle)

        points_in_invalid = [list(x) for x in set(tuple(x) for x in points_in_invalid)]
        
        for i in range(len(points_in_invalid)):
            for j in range(i + 1, len(points_in_invalid)):
                count_occurrences = sum(1 for t in invalid_triangles if points_in_invalid[i] in t and points_in_invalid[j] in t)
                if count_occurrences == 1:
                    delaunay.append(points_to_triangle(points_in_invalid[i], points_in_invalid[j], point))
    
    return delaunay

def plot_delaunay(triangles, ax):
    ax.clear()
    ax.set_xlim([-0.1, 1.1])
    ax.set_ylim([-0.1, 1.1])
    
    lines = []
    for triangle in triangles:
        xs = [triangle[0][0], triangle[1][0], triangle[2][0], triangle[0][0]]
        ys = [triangle[0][1], triangle[1][1], triangle[2][1], triangle[0][1]]
        ax.plot(xs, ys, 'b-')
        center, radius = triangle_to_circumcenter(triangle)
        ax.plot(center[0], center[1], 'ro')

    ax.set_title('Delaunay Triangulation Animation')

def update(frame, points, velocities, ax):
    for i, point in enumerate(points):
        point[0] += velocities[i][0] # update x position
        
        # Wrap points around the opposite side if they go out of bounds
        if point[0] < 0:
            point[0] = 1
        elif point[0] > 1:
            point[0] = 0

        if point[1] < 0:
            point[1] = 1
        elif point[1] > 1:
            point[1] = 0

    # Generate new triangulation based on updated points
    delaunay_triangles = gen_delaunay(points)
    plot_delaunay(delaunay_triangles, ax)

def main():
    # Create initial random points
    N = 20
    np.random.seed(5201314)
    points = np.random.rand(N, 2).tolist()
    # Initialize random velocities for each point
    velocities = np.random.rand(N, 2) * 0.01

    fig, ax = plt.subplots()
    ani = FuncAnimation(fig, update, fargs=(points, velocities, ax), frames=200, interval=100, repeat=True)

    plt.show()

if __name__ == "__main__":
    main()            
    
Your gif

Two subplots

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

import numpy as np
import matplotlib.pyplot as plt

# Create some fake data.
x1 = np.linspace(0.0, 5.0)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
x2 = np.linspace(0.0, 2.0)
y2 = np.cos(2 * np.pi * x2)

fig, (ax1, ax2) = plt.subplots(2, 1)
fig.suptitle('A tale of 2 subplots')

ax1.plot(x1, y1, 'o-')
ax1.set_ylabel('Damped oscillation')

ax2.plot(x2, y2, '.-')
ax2.set_xlabel('time (s)')
ax2.set_ylabel('Undamped')

plt.show()
                            
                            
    
Your IMG

3D Surface Animation

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib.animation import FuncAnimation

def generate_3d_surface_animation():
    fig = plt.figure(figsize=(10, 7))
    ax = fig.add_subplot(111, projection='3d')

    # Generate meshgrid
    x = np.arange(-5, 5, 0.25)
    y = np.arange(-5, 5, 0.25)
    x, y = np.meshgrid(x, y)

    # Initial radius and z values
    r = np.sqrt(x**2 + y**2)
    z = np.sin(r)

    # Create surface plot
    surf = ax.plot_surface(x, y, z, cmap='viridis', edgecolor='none')
    ax.set_title('3D Surface Plot - Wave Collapsing')
    fig.colorbar(surf, shrink=0.5, aspect=5)

    # Update function for animation
    def update(frame):
        z = np.sin(r - 0.1 * frame) #* np.exp(-0.1 * frame)  # Wave collapsing effect
        ax.clear()  # Clear previous surface
        ax.plot_surface(x, y, z, cmap='viridis', edgecolor='none')
        ax.set_title('3D Surface Plot - Wave Collapsing')
        ax.set_zlim(-1, 1)  # Keep consistent z-axis limits

        # Rotate the camera around the X-axis by updating the elevation
        ax.view_init(elev=30, azim=45 + frame)  # Adjust elev for X-axis rotation

        
    # Create animation
    ani = FuncAnimation(fig, update, frames=100, interval=100)



    plt.show()

if __name__ == "__main__":
    generate_3d_surface_animation()
                            
                            
                            
    
Your IMG

Software 3D Engine Object Renderer

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame
'''
source: https://github.com/StanislavPetrovV/Software_3D_engine/blob/main/main.py

# requires .obj file!
'''

from object_3d import *
from camera import *
from projection import *
import pygame as pg


class SoftwareRender:
    def __init__(self):
        pg.init()
        self.RES = self.WIDTH, self.HEIGHT = 1600, 900
        self.H_WIDTH, self.H_HEIGHT = self.WIDTH // 2, self.HEIGHT // 2
        self.FPS = 60
        self.screen = pg.display.set_mode(self.RES)
        self.clock = pg.time.Clock()
        self.create_objects()

    def create_objects(self):
        self.camera = Camera(self, [-5, 6, -55])
        self.projection = Projection(self)
        self.object = self.get_object_from_file('resources/t_34_obj.obj')
        self.object.rotate_y(-math.pi / 4)

    def get_object_from_file(self, filename):
        vertex, faces = [], []
        with open(filename) as f:
            for line in f:
                if line.startswith('v '):
                    vertex.append([float(i) for i in line.split()[1:]] + [1])
                elif line.startswith('f'):
                    faces_ = line.split()[1:]
                    faces.append([int(face_.split('/')[0]) - 1 for face_ in faces_])
        return Object3D(self, vertex, faces)

    def draw(self):
        self.screen.fill(pg.Color('darkslategray'))
        self.object.draw()

    def run(self):
        while True:
            self.draw()
            self.camera.control()
            [exit() for i in pg.event.get() if i.type == pg.QUIT]
            pg.display.set_caption(str(self.clock.get_fps()))
            pg.display.flip()
            self.clock.tick(self.FPS)


if __name__ == '__main__':
    app = SoftwareRender()
    app.run()
                            
    
Your GIF

Particle Collision Simulation

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib import animation
from itertools import combinations

class Particle:
    """A class representing a two-dimensional particle."""

    def __init__(self, x, y, vx, vy, radius=0.01, styles=None):
        """Initialize the particle's position, velocity, and radius.

        Any key-value pairs passed in the styles dictionary will be passed
        as arguments to Matplotlib's Circle patch constructor.

        """

        self.r = np.array((x, y))
        self.v = np.array((vx, vy))
        self.radius = radius
        self.mass = self.radius**2

        self.styles = styles
        if not self.styles:
            # Default circle styles
            self.styles = {'edgecolor': 'b', 'fill': False}

    # For convenience, map the components of the particle's position and
    # velocity vector onto the attributes x, y, vx and vy.
    @property
    def x(self):
        return self.r[0]
    @x.setter
    def x(self, value):
        self.r[0] = value
    @property
    def y(self):
        return self.r[1]
    @y.setter
    def y(self, value):
        self.r[1] = value
    @property
    def vx(self):
        return self.v[0]
    @vx.setter
    def vx(self, value):
        self.v[0] = value
    @property
    def vy(self):
        return self.v[1]
    @vy.setter
    def vy(self, value):
        self.v[1] = value

    def overlaps(self, other):
        """Does the circle of this Particle overlap that of other?"""

        return np.hypot(*(self.r - other.r)) < self.radius + other.radius

    def draw(self, ax):
        """Add this Particle's Circle patch to the Matplotlib Axes ax."""

        circle = Circle(xy=self.r, radius=self.radius, **self.styles)
        ax.add_patch(circle)
        return circle

    def advance(self, dt):
        """Advance the Particle's position forward in time by dt."""

        self.r += self.v * dt

class Simulation:
    """A class for a simple hard-circle molecular dynamics simulation.

    The simulation is carried out on a square domain: 0 <= x < 1, 0 <= y < 1.

    """

    ParticleClass = Particle

    def __init__(self, n, radius=0.01, styles=None):
        """Initialize the simulation with n Particles with radii radius.

        radius can be a single value or a sequence with n values.

        Any key-value pairs passed in the styles dictionary will be passed
        as arguments to Matplotlib's Circle patch constructor when drawing
        the Particles.

        """

        self.init_particles(n, radius, styles)
        self.dt = 0.01

    def place_particle(self, rad, styles):
        # Choose x, y so that the Particle is entirely inside the
        # domain of the simulation.
        x, y = rad + (1 - 2*rad) * np.random.random(2)
        # Choose a random velocity (within some reasonable range of
        # values) for the Particle.
        vr = 0.1 * np.sqrt(np.random.random()) + 0.05
        vphi = 2*np.pi * np.random.random()
        vx, vy = vr * np.cos(vphi), vr * np.sin(vphi)
        particle = self.ParticleClass(x, y, vx, vy, rad, styles)
        # Check that the Particle doesn't overlap one that's already
        # been placed.
        for p2 in self.particles:
            if p2.overlaps(particle):
                break
        else:
            self.particles.append(particle)
            return True
        return False

    def init_particles(self, n, radius, styles=None):
        """Initialize the n Particles of the simulation.

        Positions and velocities are chosen randomly; radius can be a single
        value or a sequence with n values.

        """

        try:
            iterator = iter(radius)
            assert n == len(radius)
        except TypeError:
            # r isn't iterable: turn it into a generator that returns the
            # same value n times.
            def r_gen(n, radius):
                for i in range(n):
                    yield radius
            radius = r_gen(n, radius)

        self.n = n
        self.particles = []
        for i, rad in enumerate(radius):
            # Try to find a random initial position for this particle.
            while not self.place_particle(rad, styles):
                pass

    def change_velocities(self, p1, p2):
        """
        Particles p1 and p2 have collided elastically: update their
        velocities.

        """
        
        m1, m2 = p1.mass, p2.mass
        M = m1 + m2
        r1, r2 = p1.r, p2.r
        d = np.linalg.norm(r1 - r2)**2
        v1, v2 = p1.v, p2.v
        u1 = v1 - 2*m2 / M * np.dot(v1-v2, r1-r2) / d * (r1 - r2)
        u2 = v2 - 2*m1 / M * np.dot(v2-v1, r2-r1) / d * (r2 - r1)
        p1.v = u1
        p2.v = u2

    def handle_collisions(self):
        """Detect and handle any collisions between the Particles.

        When two Particles collide, they do so elastically: their velocities
        change such that both energy and momentum are conserved.

        """ 

        # We're going to need a sequence of all of the pairs of particles when
        # we are detecting collisions. combinations generates pairs of indexes
        # into the self.particles list of Particles on the fly.
        pairs = combinations(range(self.n), 2)
        for i,j in pairs:
            if self.particles[i].overlaps(self.particles[j]):
                self.change_velocities(self.particles[i], self.particles[j])

    def handle_boundary_collisions(self, p):
        """Bounce the particles off the walls elastically."""

        if p.x - p.radius < 0:
            p.x = p.radius
            p.vx = -p.vx
        if p.x + p.radius > 1:
            p.x = 1-p.radius
            p.vx = -p.vx
        if p.y - p.radius < 0:
            p.y = p.radius
            p.vy = -p.vy
        if p.y + p.radius > 1:
            p.y = 1-p.radius
            p.vy = -p.vy

    def apply_forces(self):
        """Override this method to accelerate the particles."""
        pass

    def advance_animation(self):
        """Advance the animation by dt, returning the updated Circles list."""

        for i, p in enumerate(self.particles):
            p.advance(self.dt)
            self.handle_boundary_collisions(p)
            self.circles[i].center = p.r
        self.handle_collisions()
        self.apply_forces()
        return self.circles

    def advance(self):
        """Advance the animation by dt."""
        for i, p in enumerate(self.particles):
            p.advance(self.dt)
            self.handle_boundary_collisions(p)
        self.handle_collisions()
        self.apply_forces()

    def init(self):
        """Initialize the Matplotlib animation."""

        self.circles = []
        for particle in self.particles:
            self.circles.append(particle.draw(self.ax))
        return self.circles

    def animate(self, i):
        """The function passed to Matplotlib's FuncAnimation routine."""

        self.advance_animation()
        return self.circles

    def setup_animation(self):
        self.fig, self.ax = plt.subplots()
        for s in ['top','bottom','left','right']:
            self.ax.spines[s].set_linewidth(2)
        self.ax.set_aspect('equal', 'box')
        self.ax.set_xlim(0, 1)
        self.ax.set_ylim(0, 1)
        self.ax.xaxis.set_ticks([])
        self.ax.yaxis.set_ticks([])

    def save_or_show_animation(self, anim, save, filename='collision.mp4'):
        if save:
            Writer = animation.writers['ffmpeg']
            writer = Writer(fps=10, bitrate=1800)
            anim.save(filename, writer=writer)
        else:
            plt.show()

    def do_animation(self, save=False, interval=1, filename='collision.mp4'):
        """Set up and carry out the animation of the molecular dynamics.

        To save the animation as a MP4 movie, set save=True.
        """

        self.setup_animation()
        anim = animation.FuncAnimation(self.fig, self.animate,
                init_func=self.init, frames=800, interval=interval, blit=True)
        self.save_or_show_animation(anim, save, filename)


if __name__ == '__main__':
    nparticles = 10
    radii = np.random.random(nparticles)*0.03+0.02
    styles = {'edgecolor': 'C0', 'linewidth': 2, 'fill': None}
    sim = Simulation(nparticles, radii, styles)
    sim.do_animation(save=False)
                            
                            
    
Your IMG

ASCII Art

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas optio earum quia rem quidem veniam molestias reiciendis corporis sed dignissimos tenetur voluptates quaerat blanditiis, fuga dicta ullam perspiciatis! Alias, vel.

Dependencies

  • contourpy==1.1.0
  • cycler==0.11.0
  • fonttools==4.42.1
  • kiwisolver==1.4.5
  • matplotlib==3.7.2
  • numpy==1.25.2
  • packaging==23.1
  • Pillow==10.0.0
  • pyparsing==3.0.9
  • python-dateutil==2.8.2
  • six==1.16.0

$ pip install pandas numpy matplotlib Pillow psycogp2 Django Flask pygame

'''
# ascii.py

# to use run:
$ python3 ascii.py --file path_to_image.jpg --scale 0.5 --out output.txt --cols 100 --morelevels

# increase cols for improved definition
'''
import sys
import argparse
import numpy as np
from PIL import Image

# Grayscale character sets
gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
gscale2 = '@%#*+=-:. '

def getAverageL(image):
    """Given PIL Image, return average value of grayscale value."""
    im = np.array(image)
    return np.average(im)

def convertImageToAscii(fileName, cols, scale, moreLevels):
    """Convert image to ASCII art."""
    global gscale1, gscale2
    image = Image.open(fileName).convert('L')
    W, H = image.size
    w = W / cols
    h = w / scale
    rows = int(H / h)

    if cols > W or rows > H:
        print("Image too small for specified cols!")
        exit(0)

    aimg = []
    for j in range(rows):
        y1 = int(j * h)
        y2 = int((j + 1) * h) if j != rows - 1 else H
        aimg.append("")
        for i in range(cols):
            x1 = int(i * w)
            x2 = int((i + 1) * w) if i != cols - 1 else W
            img = image.crop((x1, y1, x2, y2))
            avg = int(getAverageL(img))
            gsval = gscale1[int((avg * 69) / 255)] if moreLevels else gscale2[int((avg * 9) / 255)]
            aimg[j] += gsval
    
    return aimg

def main():
    parser = argparse.ArgumentParser(description="This program converts an image into ASCII art.")
    parser.add_argument('--file', dest='imgFile', required=True)
    parser.add_argument('--scale', dest='scale', type=float, default=0.43)
    parser.add_argument('--out', dest='outFile', default='out.txt')
    parser.add_argument('--cols', dest='cols', type=int, default=80)
    parser.add_argument('--morelevels', dest='moreLevels', action='store_true')

    args = parser.parse_args()
    
    imgFile = args.imgFile
    outFile = args.outFile
    scale = args.scale
    cols = args.cols

    print('Generating ASCII art...')
    aimg = convertImageToAscii(imgFile, cols, scale, args.moreLevels)

    with open(outFile, 'w') as f:
        for row in aimg:
            f.write(row + '\n')
    print(f"ASCII art written to {outFile}")

if __name__ == '__main__':
    main()
                        
                        
Your IMG