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()

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()

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()

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()

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()

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()

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()

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()

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()

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()

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)

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()
