Created
August 24, 2012 18:13
-
-
Save gavinblair/3453781 to your computer and use it in GitHub Desktop.
@berdandy's Particle Fountain
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function setup() | |
displayMode(STANDARD) | |
availableScenes = { Scene1() } | |
currentScene = availableScenes[1] | |
iparameter("SceneSelect",1,#availableScenes,1) | |
parameter("Size",50,500,150) | |
parameter("CamHeight", 0, 1000, 300) | |
-- parameter("Angle",-360, 360, 0) | |
parameter("FieldOfView", 10, 140, 45) | |
Angle = 0 | |
end | |
function draw() | |
currentScene = availableScenes[SceneSelect] | |
-- First arg is FOV, second is aspect | |
perspective(FieldOfView, WIDTH/HEIGHT) | |
camera(0,CamHeight,-300, 0,0,0, 0,1,0) | |
-- This sets a dark background color | |
background(40, 40, 50) | |
currentScene:draw() | |
-- Restore orthographic projection | |
ortho() | |
viewMatrix(matrix()) | |
-- label | |
fill(255) | |
font("MyriadPro-Bold") | |
fontSize(30) | |
text(currentScene:name(), WIDTH/2, HEIGHT - 30) | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Particle = class() | |
function Particle:init() | |
self.live = false | |
end | |
function Particle:draw() | |
if self.live then | |
resetMatrix() | |
translate(self.pos.x, self.pos.y, self.pos.z) | |
fill(191, 26, 26, 255) | |
noStroke() | |
-- sprite("Tyrian Remastered:Bullet Fire B", 0, 0) | |
ellipse(0, 0, 10) | |
end | |
end | |
function Particle:update() | |
if self.live then | |
-- change velocity based on acceleration | |
self.vel = self.vel + self.acc | |
-- move particle based on velocity | |
self.pos = self.pos + self.vel | |
-- manage lifetime | |
if self.pos.y < 0 then | |
self.live = false | |
end | |
else | |
self.pos = Vec3(0,0,0) | |
self.vel = Vec3(math.random()*2-1, math.random()+0.5, math.random()*2-1) | |
self.acc = Vec3(0,-0.01,0) | |
self.live = true | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Scene1 = class() | |
function Scene1:name() | |
return "Particle Fountain" | |
end | |
function Scene1:init() | |
print("Scene1::init") | |
-- you can accept and set parameters here | |
self.particles = {} | |
for i = 1,100 do | |
self.particles[i] = Particle( | |
Vec3(0,0,0), -- pos | |
Vec3(math.random(0, 1), math.random(0.5, 1.5), math.random(0, 1)), -- vel | |
Vec3(0,-0.01,0) --accel | |
) | |
end | |
end | |
function Scene1:draw() | |
for i = 1, #self.particles do | |
self.particles[i]:update() | |
end | |
-- todo: sort? | |
-- Angle = ( Angle + 1 ) % 360 | |
-- Preserve existing transform and style | |
pushMatrix() | |
pushStyle() | |
-- This sets the line thickness | |
strokeWidth(1) | |
smooth() | |
rectMode(CENTER) | |
-- Make a floor | |
translate(0,-Size/2,0) | |
rotate(Angle,0,1,0) | |
rotate(90,1,0,0) | |
fill(88, 92, 175, 255) | |
sprite("Planet Cute:Water Block", 0, 0, 300, 300) | |
for i = 1, #self.particles do | |
self.particles[i]:draw() | |
end | |
-- Restore transform and style | |
popStyle() | |
popMatrix() | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Vec3 class | |
-- Author: Andrew Stacey | |
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html | |
-- Licence: CC0 http://wiki.creativecommons.org/CC0 | |
--[[ | |
The "Vec3" class is for handling 3 dimensional vectors and defining a | |
variety of methods on them. | |
--]] | |
Vec3 = class() | |
--[[ | |
A 3-vector is three numbers. | |
--]] | |
function Vec3:init(x,y,z) | |
self.x = x | |
self.y = y | |
self.z = z | |
end | |
--[[ | |
Test for zero vector. | |
--]] | |
function Vec3:is_zero() | |
if self.x ~= 0 or self.y ~= 0 or self.z ~= 0 then | |
return false | |
end | |
return true | |
end | |
--[[ | |
Check entries against nan | |
--]] | |
function Vec3:is_finite() | |
if self.x ~= self.x then | |
return false | |
end | |
if self.y ~= self.y then | |
return false | |
end | |
if self.z ~= self.z then | |
return false | |
end | |
return true | |
end | |
--[[ | |
Test for equality. | |
--]] | |
function Vec3:is_eq(v) | |
if self.x ~= v.x or self.y ~= v.y or self.z ~= v.z then | |
return false | |
end | |
return true | |
end | |
--[[ | |
Inner product. | |
--]] | |
function Vec3:dot(v) | |
return self.x * v.x + self.y * v.y + self.z * v.z | |
end | |
--[[ | |
Cross product. | |
--]] | |
function Vec3:cross(v) | |
local x,y,z | |
x = self.y * v.z - self.z * v.y | |
y = self.z * v.x - self.x * v.z | |
z = self.x * v.y - self.y * v.x | |
return Vec3(x,y,z) | |
end | |
--[[ | |
Apply a given matrix (which is specified as a triple of vectors). | |
--]] | |
function Vec3:applyMatrix(a,b,c) | |
local u,v,w | |
u = a:scale(self.x) | |
v = b:scale(self.y) | |
w = c:scale(self.z) | |
u = u:add(v) | |
u = u:add(w) | |
return u | |
end | |
--[[ | |
Length of the vector | |
--]] | |
function Vec3:len() | |
return math.sqrt(math.pow(self.x,2) + math.pow(self.y,2) + math.pow(self.z,2)) | |
end | |
--[[ | |
Squared length of the vector. | |
--]] | |
function Vec3:lenSqr() | |
return math.pow(self.x,2) + math.pow(self.y,2) + math.pow(self.z,2) | |
end | |
--[[ | |
Distance of the vector to another | |
--]] | |
function Vec3:dist(v) | |
return math.sqrt(math.pow((self.x-v.x),2) + math.pow((self.y-v.y),2) + math.pow((self.z-v.z),2)) | |
end | |
--[[ | |
Squared distance of the vector to another | |
--]] | |
function Vec3:distSqr(v) | |
return math.pow((self.x-v.x),2) + math.pow((self.y-v.y),2) + math.pow((self.z-v.z),2) | |
end | |
--[[ | |
Normalise the vector (if possible) to length 1. | |
--]] | |
function Vec3:normalise() | |
local l | |
if self:is_zero() then | |
print("Unable to normalise a zero-length vector") | |
return false | |
end | |
l = 1/self:len() | |
return self:scale(l) | |
end | |
--[[ | |
Scale the vector. | |
--]] | |
function Vec3:scale(l) | |
return Vec3(self.x * l,self.y * l,self.z * l) | |
end | |
--[[ | |
Add vectors. | |
--]] | |
function Vec3:add(v) | |
return Vec3(self.x + v.x, self.y + v.y, self.z + v.z) | |
end | |
--[[ | |
Subtract vectors. | |
--]] | |
function Vec3:subtract(v) | |
return Vec3(self.x - v.x, self.y - v.y, self.z - v.z) | |
end | |
--[[ | |
Apply a transformation between "absolute" coordinates and "relative" | |
coordinates. In the "relative" system, xy are in the iPad screen and | |
z points straight out. In the "absolute" system, y is in the | |
direction of the gravity vector, x is in the plane of the iPad screen, | |
and z is orthogonal to those two. | |
This function interprets the vector as being with respect to the | |
absolute coordinates and returns the corresponding vector in the | |
relative system. | |
--]] | |
function Vec3:absCoords() | |
local gxy,l,ga,gb,gc | |
gxy = Vec3(-Gravity.y,Gravity.x,0) | |
l = gxy:len() | |
if l == 0 then | |
print("Unable to compute coordinate system, gravity vector is (" .. Gravity.x .. "," .. Gravity.y .. "," .. Gravity.z .. ")") | |
return false | |
end | |
ga = gxy:scale(1/l) | |
gb = Vec3(-Gravity.x,-Gravity.y,-Gravity.z) | |
gc = Vec3(-Gravity.x * Gravity.z /l, -Gravity.y * Gravity.z /l, l) | |
return self:applyMatrix(ga,gb,gc) | |
end | |
--[[ | |
Determine whether or not the vector is in front of the "eye" (crude | |
test for visibility). | |
--]] | |
function Vec3:isInFront(e) | |
if not e then | |
e = Vec3.eye | |
end | |
if self:dot(e) < e:dot(e) then | |
return true | |
else | |
return false | |
end | |
end | |
--[[ | |
Project the vector onto the screen using stereographic projection from | |
the "eye". | |
--]] | |
function Vec3:stereoProject(e) | |
local t,v | |
if not e then | |
e = Vec3.eye | |
end | |
if self.z == e.z then | |
-- can't project | |
return false | |
end | |
t = 1 / (1 - self.z / e.z) | |
v = self:subtract(e) | |
v = v:scale(t) | |
v = v:add(e) | |
-- hopefully v.z is now 0! | |
return vec2(v.x,v.y) | |
end | |
--[[ | |
Partial inverse to stereographic projection: given a point on the | |
screen and a height, we find the point in space at that height which | |
would project to the point on the screen. | |
--]] | |
function Vec3.stereoInvProject(v,e,h) | |
local t,u | |
if not e then | |
e = Vec3.eye | |
end | |
u = Vec3(v.x,v.y,0) | |
t = h / e.z | |
u = (1 - t) * u + t * e | |
-- hopefully u.z is now h! | |
return u | |
end | |
--[[ | |
Returns the distance from the eye; useful for sorting objects. | |
--]] | |
function Vec3:stereoLevel(e) | |
local v | |
if not e then | |
e = Vec3.eye | |
end | |
v = self:subtract(e) | |
return e:len() - v:len() | |
end | |
--[[ | |
Applies a rotation as specified by another 3-vector, with direction | |
being the axis and magnitude the angle. | |
--]] | |
function Vec3:rotate(w) | |
local theta, u, v, a, b, c, x | |
if w:is_zero() then | |
return self | |
else | |
theta = w:len() | |
w = w:normalise() | |
if w.x ~= 0 then | |
u = Vec3(-w.y/w.x,1,0) | |
u = u:normalise() | |
else | |
u = Vec3(1,0,0) | |
end | |
v = w:cross(u) | |
a = self:dot(u) | |
b = self:dot(v) | |
c = self:dot(w) | |
x = w:scale(c) | |
u = u:scale(a * math.cos(theta) + b * math.sin(theta)) | |
v = v:scale(-a * math.sin(theta) + b * math.cos(theta)) | |
x = x:add(u) | |
x = x:add(v) | |
return x | |
end | |
end | |
--[[ | |
Promote to a quaternion with 0 real part. | |
--]] | |
function Vec3:toQuaternion() | |
return Quaternion(0,self.x,self.y,self.z) | |
end | |
--[[ | |
Apply a quaternion as a rotation. | |
--]] | |
function Vec3:applyQuaternion(q) | |
local x = self:toQuaternion() | |
x = q:multiplyRight(x) | |
x = x:multiplyRight(q:conjugate()) | |
return x:vector() | |
end | |
--[[ | |
Inline operators: | |
u + v | |
u - v | |
-u | |
u * v : cross product (possibly bad choice) or scaling | |
u / v : scaling | |
u ^ q : apply quaternion as rotation | |
u == v : equality | |
u .. v : dot product (possibly bad choice) | |
The notation for cross product and dot product may be removed in a | |
later version. | |
--]] | |
function Vec3:__add(v) | |
return self:add(v) | |
end | |
function Vec3:__sub(v) | |
return self:subtract(v) | |
end | |
function Vec3:__unm() | |
return self:scale(-1) | |
end | |
function Vec3:__mul(v) | |
if type(self) == "number" then | |
return v:scale(self) | |
elseif type(v) == "number" then | |
return self:scale(v) | |
elseif type(v) == "table" then | |
if v:is_a(Vec3) then | |
return self:cross(v) | |
end | |
end | |
return false | |
end | |
function Vec3:__div(l) | |
if type(l) == "number" then | |
return self:scale(1/l) | |
else | |
return false | |
end | |
end | |
function Vec3:__pow(q) | |
if type(q) == "table" then | |
if q:is_a(Quaternion) then | |
return self:applyQuaternion(q) | |
end | |
end | |
return false | |
end | |
function Vec3:__eq(v) | |
return self:is_eq(v) | |
end | |
function Vec3:__concat(v) | |
if type(v) == "table" | |
and v:is_a(Vec3) | |
and type(self) == "table" | |
and self:is_a(Vec3) | |
then | |
return self:dot(v) | |
else | |
if type(v) == "table" | |
and v:is_a(Vec3) then | |
return self .. v:tostring() | |
else | |
return self:tostring() .. v | |
end | |
end | |
end | |
function Vec3:tostring() | |
return "(" .. self.x .. "," .. self.y .. "," .. self.z .. ")" | |
end | |
function Vec3:__tostring() | |
return self:tostring() | |
end | |
function Vec3:tovec3() | |
return vec3(self.x,self.y,self.z) | |
end | |
function Vec3:tomatrix() | |
return matrix( | |
1,0,0,0, | |
0,1,0,0, | |
0,0,1,0, | |
self.x,-self.y,self.z,1 | |
) | |
end | |
--[[ | |
The following functions are not class methods but are still related to | |
vectors. | |
--]] | |
--[[ | |
Sets the "eye" for stereographic projection. Input can either be a | |
Vec3 object or the information required to specify one. | |
--]] | |
function Vec3.SetEye(...) | |
if arg.n == 1 then | |
Vec3.eye = arg[1] | |
elseif arg.n == 3 then | |
Vec3.eye = Vec3(unpack(arg)) | |
else | |
print("Wrong number of arguments to Vec3.SetEye (1 or 3 expected, got " .. arg.n .. ")") | |
return false | |
end | |
return true | |
end | |
function GramSchmidt(t) | |
local o = {} | |
local w | |
for k,v in ipairs(t) do | |
w = v | |
for l,u in ipairs(o) do | |
w = w - w:dot(u)*u | |
end | |
if not w:is_zero() then | |
w = w:normalise() | |
table.insert(o,w) | |
end | |
end | |
return o | |
end | |
function Vec3.Random() | |
local th = 2*math.pi*math.random() | |
local z = 2*math.random() - 1 | |
local r = math.sqrt(1 - z*z) | |
return Vec3(r*math.cos(th),r*math.sin(th),z) | |
end | |
function Vec3.RandomBasis() | |
local th = 2*math.pi*math.random() | |
local cth = math.cos(th) | |
local sth = math.sin(th) | |
local a = Vec3(cth,sth,0) | |
local b = Vec3(-sth,cth,0) | |
local c = Vec3(0,0,1) | |
local v = Vec3.Random() | |
a = a - 2*v:dot(a)*v | |
b = b - 2*v:dot(b)*v | |
c = c - 2*v:dot(c)*v | |
return {a,b,c} | |
end | |
function Vec3.SO(u,v) | |
if u:is_zero() and v:is_zero() then | |
return {Vec3.e1,Vec3.e2,Vec3.e3} | |
end | |
if u:is_zero() then | |
u,v = v,u | |
end | |
if u:cross(v):is_zero() then | |
if u.x == 0 and u.y == 0 then | |
v = Vec3.e3 | |
else | |
v = Vec3(u.y,-u.x,0) | |
end | |
end | |
local t = GramSchmidt({u,v}) | |
t[3] = t[1]:cross(t[2]) | |
return t | |
end | |
--[[ | |
Some useful Vec3 objects. | |
--]] | |
Vec3.eye = Vec3(0,0,1) | |
Vec3.origin = Vec3(0,0,0) | |
Vec3.e1 = Vec3(1,0,0) | |
Vec3.e2 = Vec3(0,1,0) | |
Vec3.e3 = Vec3(0,0,1) | |
--[[ | |
Is the line segment a-b over c-d when seen from e? | |
--]] | |
function Vec3.isOverLine(a,b,c,d,e) | |
if not e then | |
e = Vec3.eye | |
end | |
-- rebase at a | |
b = b - a | |
c = c - a | |
d = d - a | |
e = e - a | |
-- test signs of various determinants | |
a = c:cross(d) | |
if a:dot(b) * a:dot(e) < 0 then | |
return false | |
end | |
a = d:cross(e) | |
if a:dot(b) * a:dot(c) < 0 then | |
return false | |
end | |
a = e:cross(c) | |
if a:dot(b) * a:dot(d) < 0 then | |
return false | |
end | |
-- right direction, is it far enough? | |
c = c:subtract(e) | |
d = d:subtract(e) | |
a = c:cross(d) | |
local l = a:dot(b) | |
local m = a:dot(e) | |
if l * m > 0 and math.abs(l) > math.abs(m) then | |
return true | |
else | |
return false | |
end | |
end | |
--[[ | |
Is the line segment a-b over the point c when seen from e? | |
r is the "significant distance" | |
--]] | |
function Vec3.isOverPoint(a,b,c,r,e) | |
if not e then | |
e = Vec3.eye | |
end | |
local aa,bb,ab,ac,bc,d,l | |
-- rebase at e | |
a = a - e | |
b = b - e | |
c = c - e | |
d = a:cross(b) | |
if math.abs(d:dot(c)) > r * d:len() then | |
return false | |
end | |
aa = a:lenSqr() | |
bb = b:lenSqr() | |
ab = a:dot(b) | |
ac = a:dot(c) | |
bc = b:dot(c) | |
if aa * bc < ab * ac then | |
return false | |
end | |
if bb * ac < ab * bc then | |
return false | |
end | |
l = math.sqrt((aa * bb - ab * ab) * (aa + bb - 2 * ab)) | |
if (bb - ab) * ac + (aa - ab) * bc < aa * bb - ab * ab + r * l then | |
return false | |
end | |
return true | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
All code is by @berdandy