Skip to content

Instantly share code, notes, and snippets.

@uwezi
Last active September 10, 2025 11:13
Show Gist options
  • Save uwezi/71c73da5c1e46afee23c53b75fb46b23 to your computer and use it in GitHub Desktop.
Save uwezi/71c73da5c1e46afee23c53b75fb46b23 to your computer and use it in GitHub Desktop.
[particles in a box] Animation of particles inside a polygon with additional polygons on the inside. #manim #physics #dt #updater #intersection #collision
# https://discord.com/channels/581738731934056449/1414961601127383170/1415086069568766024
from manim import *
class dotinbox(Scene):
def construct(self):
outerbox = RegularPolygon(n=6).rotate(5*DEGREES).scale_to_fit_height(7)
innerobjs = VGroup(
RegularPolygon(n=5, fill_opacity=1).scale_to_fit_height(2).shift(UR),
Square(side_length=1).shift(DL)
)
self.add(outerbox,innerobjs)
dot = Dot(point=[-1,1,0],color=RED)
dot.vel = np.array([3,0,0])
def dotupdater(mobj,dt):
mobj.shift(dt*mobj.vel)
ins = Intersection(outerbox,mobj)
if ((ins.width*ins.height) < (mobj.width*mobj.height)/2):
mp = mobj.get_center()
mobj.shift(-dt*mobj.vel)
vsdiff = [v - mp for v in outerbox.get_vertices()]
vsdiff.sort(key=np.linalg.norm)
norm = np.array([[0,-1,0],[1,0,0],[0,0,0]]) @ (vsdiff[1]-vsdiff[0])
projNV = ((mobj.vel @ norm)/(norm @ norm)) * norm
mobj.vel = mobj.vel - 2*projNV
mobj.shift(dt*mobj.vel)
for inner in innerobjs:
ins = Intersection(inner,mobj)
if ((ins.width*ins.height) > (mobj.width*mobj.height)/2):
mp = mobj.get_center()
mobj.shift(-dt*mobj.vel)
vsdiff = [v - mp for v in inner.get_vertices()]
vsdiff.sort(key=np.linalg.norm)
norm = np.array([[0,-1,0],[1,0,0],[0,0,0]]) @ (vsdiff[1]-vsdiff[0])
projNV = ((mobj.vel @ norm)/(norm @ norm)) * norm
mobj.vel = mobj.vel - 2*projNV
mobj.shift(dt*mobj.vel)
dot.add_updater(dotupdater)
dots = VGroup(
dot.copy().set_color(np.random.choice([RED,GREEN,YELLOW,TEAL,BLUE,ORANGE]))
for _ in range(10)
)
for d in dots:
d.vel = np.array([np.random.uniform(-2,2),np.random.uniform(-2,2),0])
self.add(dots)
self.wait(25)
# possibly improved version
class dotsbox(Scene):
def construct(self):
obox = RegularPolygon(n=6).rotate(5*DEGREES).scale_to_fit_height(7)
iobjs = VGroup(
RegularPolygon(n=5, fill_opacity=1).scale_to_fit_height(2).shift(UR),
Square(side_length=1).shift(DL)
)
self.add(obox,iobjs)
dot = Dot(point=[-1,1,0],color=RED)
dot.vel = np.array([3,0,0])
def dupd(mobj,dt):
mobj.shift(dt*mobj.vel)
ins = Intersection(obox,mobj)
if ((ins.width*ins.height) < (mobj.width*mobj.height)*.75):
mobj.shift(-dt*mobj.vel)
mp = mobj.get_center()
dif = [v - mp for v in obox.get_vertices()]
dif.sort(key=np.linalg.norm)
norm = np.array([[0,-1,0],[1,0,0],[0,0,0]]) @ (dif[1]-dif[0])
pNV = ((mobj.vel @ norm)/(norm @ norm)) * norm
mobj.vel = mobj.vel - 2*pNV
mobj.shift(dt*mobj.vel)
for i in iobjs:
ins = Intersection(i,mobj)
if ((ins.width*ins.height) > (mobj.width*mobj.height)/100):
mobj.shift(-dt*mobj.vel)
mp = mobj.get_center()
dif = [v - mp for v in i.get_vertices()]
dif.sort(key=np.linalg.norm)
norm = np.array([[0,-1,0],[1,0,0],[0,0,0]]) @ (dif[1]-dif[0])
pNV = ((mobj.vel @ norm)/(norm @ norm)) * norm
mobj.vel = mobj.vel - 2*pNV
mobj.shift(dt*mobj.vel)
dot.add_updater(dupd)
dots = VGroup(
dot.copy().set_color(np.random.choice([RED,GREEN,YELLOW,TEAL,BLUE,ORANGE]))
for _ in range(10)
)
for d in dots:
d.vel = np.array([np.random.uniform(-2,2),np.random.uniform(-2,2),0])
self.add(dots)
self.play(Rotate(iobjs[0],5*PI),rate_func=rate_functions.linear,run_time=25)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment