Skip to content

Instantly share code, notes, and snippets.

@mroavi
Last active February 6, 2022 15:25
Show Gist options
  • Save mroavi/d10cd90b1d895e0f00e14774b5f01537 to your computer and use it in GitHub Desktop.
Save mroavi/d10cd90b1d895e0f00e14774b5f01537 to your computer and use it in GitHub Desktop.
A simple particle system animation in Julia.
using Plots, Distributions
theme(:juno) # sets theme and resets all attributes of the Plots package
gr(
xlims=[-10,10],
ylims=[-10,10],
aspect_ratio=:equal,
framestyle=:grid,
fillalpha=0.3,
leg=false,
size=(1200,800),
thickness_scaling = 1.5,
linealpha=0.5,
markerstrokewidth=0,
markersize=10,
markeralpha=0.8,
); plot()
import Base.+, Base.-, Base.*, Base./
mutable struct PVector
x::Float64
y::Float64
end
+(v1::PVector, v2::PVector) = PVector(v1.x + v2.x, v1.y + v2.y)
-(v1::PVector, v2::PVector) = PVector(v1.x - v2.x, v1.y - v2.y)
-(v::PVector) = PVector(-v.x, -v.y)
*(v::PVector, n::Float64) = PVector(n * v.x, n * v.y)
*(n::Float64, v::PVector) = v * n
/(v::PVector, n::Float64) = PVector(v.x/n, v.y/n)
mag(v::PVector) = sqrt(v.x^2 + v.y^2)
normalize(v::PVector) = (mag(v) != 0 ) ? PVector(v.x/mag(v), v.y/mag(v)) : PVector(0,0)
limit(v::PVector, n) = (mag(v) > n) ? normalize(v) * n : v
mutable struct Particle
pos::PVector
vel::PVector
acc::PVector
mass::Float64
lifespan::Float64
function Particle(pos, vel, acc, mass, lifespan=1)
new(pos, vel, acc, mass, lifespan)
end
end
function Particle(p::Particle)
Particle(p.pos, p.vel, p.acc, p.mass, p.lifespan)
end
function rescale(x, start1, stop1, start2, stop2)
start2 + (((x - start1)*(stop2 - start2))/(stop1-start1))
end
isDead(p::Particle) = (p.pos.y < -10) ? true : false
function update(p::Particle)
p.vel = p.vel + p.acc
p.pos = p.pos + p.vel
p.acc = PVector(0,0) # clear acceleration
p.lifespan -= 0.05
return Particle(p.pos, p.vel, p.acc, p.mass, p.lifespan)
end
function disp(particles::Array{Particle})
plot(ylims=(-10,10)) # fixes unwanted padding
for p in particles
scatter!([p.pos.x], [p.pos.y],
m=:circle,
markersize=p.mass*20,
markeralpha=rescale(p.pos.y, 10, -10, 1, 0),
c = floor(Int64, p.mass*100)
)
end
end
function applyForce(p::Particle, f::PVector)
ret = Particle(p)
ret.acc = ret.acc + f/p.mass # Newton's 2nd law of motion
return ret
end
function newParticle()
pos = PVector(0, 5)
vel = PVector(rand(Uniform(-0.2, 0.2)), rand(Uniform(0, 0.7)))
acc = PVector(0,0)
mass = rand(Uniform(0.4, 0.6))
return Particle(pos, vel, PVector(0, 0), mass)
end
particles = Array{Particle}(undef, 0) # initialize particles
anim = @animate for _ in 1:100
push!(particles, newParticle())
push!(particles, newParticle())
for i in length(particles):-1:1
gravity = PVector(0, -0.03)
particles[i] = applyForce(particles[i], gravity)
particles[i] = update(particles[i])
if isDead(particles[i])
deleteat!(particles, i) # delete dead particle
push!(particles, newParticle()) # create a new particle
end
end
disp(particles)
end
gif(anim, joinpath(@__DIR__, "4.1.gif"), fps = 15)
@PlasmaThias
Copy link

PlasmaThias commented Jan 25, 2022

Hi!
Just I don't understand those two lines:
p.vel = p.vel + p.acc
p.pos = p.pos + p.vel

Why do you sum two different entities. I mean why not, there is no unit when coding and it doesn't have to be physically true.

But you're using the second law of motion:
ret.acc = ret.acc + f/p.mass # Newton's 2nd law of motion

So I'm a bit confuse.

@mroavi
Copy link
Author

mroavi commented Feb 6, 2022

@PlasmaThias Not really sure what you mean there but I advise you to take a look at this video: https://youtu.be/TQ_WZU5s_VA. They explain where these equations come from.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment