def integrate(self, duration): ''' method which updates the particle properties each time step --------- args: duration - double - duration of time step ''' assert (duration > 0) #update position by velocity # Pn = Pn-1 + v*t self.position.addScaledVector(self.velocity, duration) #Calculate acceleration from force resultAcceleration = Vector(self.acceleration.x, self.acceleration.y, self.acceleration.z) resultAcceleration.addScaledVector(self.forceAccum, self.inverseMass) #update velocity using acceleration self.velocity.addScaledVector(resultAcceleration, duration) #Calculate drag self.velocity *= self.damping**duration #clear all forces self.clearAccumulator()
def create(self, firework, parent=None): ''' create a firework and optionally apply a parent to it --------- args: firework - Firework - Firework to be filled parent - Firework = None - Parent of firework if it is a child (payload) ''' firework.type = self.type firework.age = uniform(self.minAge, self.maxAge) velocity = Vector(0, 0, 0) firework.ball.visible = True if (parent): pos = parent.getPosition() firework.setPosition(pos.x, pos.y, pos.z) velocity += parent.getVelocity() else: randposx = uniform(0, 50) randposy = uniform(0, 100) firework.setPosition(randposx, randposy, 0) randx = uniform(self.minVelocity.x, self.maxVelocity.x) randy = uniform(self.minVelocity.y, self.maxVelocity.y) randz = uniform(self.minVelocity.z, self.maxVelocity.z) velocity += Vector(randx, randy, randz) firework.setVelocity(velocity.x, velocity.y, velocity.z) firework.setMass(1) firework.setDamping(self.damping) firework.setAcceleration(0, 0, -20) #Acceleration due to gravity firework.clearAccumulator()
def initiateFireworkRule(self): ''' Method which stores all rules for simulation ''' rule = FireworkRule(1, 0.5, 2.1, Vector(-5, -5, 25), Vector(5, 5, 28), 0.8, 2) rule.payloads.append(FireworkRule.PayLoad(3, 5)) rule.payloads.append(FireworkRule.PayLoad(5, 1)) self.rules.append(rule) rule = FireworkRule(2, 0.5, 1, Vector(-5, -5, 20), Vector(5, 5, 30), 0.1, 1) rule.payloads.append(FireworkRule.PayLoad(4, 7)) self.rules.append(rule) rule = FireworkRule(3, 0.5, 3, Vector(-20, 10, 40), Vector(5, 20, 80), 0.2, 0) self.rules.append(rule) rule = FireworkRule(4, 0.2, 2.1, Vector(-20, 5, 50), Vector(20, 5, 95), 0.2, 0) self.rules.append(rule) rule = FireworkRule(5, 0.2, 1.7, Vector(-20, 5, 5), Vector(20, 5, 30), 0.2, 2) rule.payloads.append(FireworkRule.PayLoad(2, 2)) rule.payloads.append(FireworkRule.PayLoad(3, 3)) self.rules.append(rule)
def __init__(self): ''' Class constractor ''' self.particles = [None] * 2 self.restitution = 0 self.contactNormal = Vector(0, 0, 0) self.penetration = 0 self.particleMovement = [Vector(0, 0, 0), Vector(0, 0, 0)]
def __mul__(self, o): ''' operation overload for * operator - returns a 3x3 Matrix if o is Matrix3 - return a vector if o is a Vector --------- args: o - Matrix3 o - Vector ''' if isinstance(o, Matrix3): return Matrix3(\ self.data[0]*o.data[0] + self.data[1]*o.data[3] + self.data[2]*o.data[6],\ self.data[0]*o.data[1] + self.data[1]*o.data[4] + self.data[2]*o.data[7],\ self.data[0]*o.data[2] + self.data[1]*o.data[5] + self.data[2]*o.data[8],\ self.data[3]*o.data[0] + self.data[4]*o.data[3] + self.data[5]*o.data[6],\ self.data[3]*o.data[1] + self.data[4]*o.data[4] + self.data[5]*o.data[7],\ self.data[3]*o.data[2] + self.data[4]*o.data[5] + self.data[5]*o.data[8],\ self.data[6]*o.data[0] + self.data[7]*o.data[3] + self.data[8]*o.data[6],\ self.data[6]*o.data[1] + self.data[7]*o.data[4] + self.data[8]*o.data[7],\ self.data[6]*o.data[2] + self.data[7]*o.data[5] + self.data[8]*o.data[8] ) else: return Vector(\ o.x * self.data[0] + o.y * self.data[1] + o.z * self.data[2],\ o.x * self.data[3] + o.y * self.data[4] + o.z * self.data[5],\ o.x * self.data[6] + o.y * self.data[7] + o.z * self.data[8])
def getAcceleration(self): ''' Returns a new vector which represents the acceleration of the particle ''' return Vector(self.acceleration.x, self.acceleration.y, self.acceleration.z)
def getAxisVector(self, i): ''' Return a new vector representing a column in this matrix --------- args: i - int - index of column ''' return Vector(self.data[i], self.data[i + 4], self.data[i + 8])
def transformInverse(self, vector): ''' Return a new vector of the transform of the given direction vector by transformational inverse of this matrix It is assumed that this matrix is a pure rotation matrix (inverse is transpose) --------- args: vector - Vector ''' tmp = Vector(vector.x, vector.y, vector.z) tmp.x -= self.data[3] tmp.y -= self.data[7] tmp.z -= self.data[11] return Vector( tmp.x * self.data[0] + tmp.y * self.data[4] + tmp.z * self.data[8], tmp.x * self.data[1] + tmp.y * self.data[5] + tmp.z * self.data[9], tmp.x * self.data[2] + tmp.y * self.data[6] + tmp.z * self.data[10])
def getAxisVector(self, i): ''' return a vector representing an a column in this matrix --------- args: i - int - number of column to return ''' return Vector(self.data[i], self.data[i + 3], self.data[i + 6])
def getRowVector(self, i): ''' return a vector representing row i in this matrix --------- args: i - int - number of row to return ''' return Vector(self.data[i * 3], self.data[i * 3 + 1], self.data[i * 3 + 2])
def __mul__(self, o): ''' operation overload for * operator - returns a 3x4 Matrix if o is Matrix4 - return a vector if o is a Vector --------- args: o - Matrix4 o - Vector ''' if isinstance(o, Matrix4): result = Matrix4() result.data[0] = (o.data[0] * self.data[0]) + ( o.data[4] * self.data[1]) + (o.data[8] * self.data[2]) result.data[4] = (o.data[0] * self.data[4]) + ( o.data[4] * self.data[5]) + (o.data[8] * self.data[6]) result.data[8] = (o.data[0] * self.data[8]) + ( o.data[4] * self.data[9]) + (o.data[8] * self.data[10]) result.data[1] = (o.data[1] * self.data[0]) + ( o.data[5] * self.data[1]) + (o.data[9] * self.data[2]) result.data[5] = (o.data[1] * self.data[4]) + ( o.data[5] * self.data[5]) + (o.data[9] * self.data[6]) result.data[9] = (o.data[1] * self.data[8]) + ( o.data[5] * self.data[9]) + (o.data[9] * self.data[10]) result.data[2] = (o.data[2] * self.data[0]) + ( o.data[6] * self.data[1]) + (o.data[10] * self.data[2]) result.data[6] = (o.data[2] * self.data[4]) + ( o.data[6] * self.data[5]) + (o.data[10] * self.data[6]) result.data[10] = (o.data[2] * self.data[8]) + ( o.data[6] * self.data[9]) + (o.data[10] * self.data[10]) result.data[3] = (o.data[3] * self.data[0]) + ( o.data[7] * self.data[1]) + (o.data[11] * self.data[2]) + self.data[3] result.data[7] = (o.data[3] * self.data[4]) + ( o.data[7] * self.data[5]) + (o.data[11] * self.data[6]) + self.data[7] result.data[11] = (o.data[3] * self.data[8]) + ( o.data[7] * self.data[9]) + (o.data[11] * self.data[10]) + self.data[11] return result else: return Vector( o.x * self.data[0] + o.y * self.data[1] + o.z * self.data[2] + self.data[3], o.x * self.data[4] + o.y * self.data[5] + o.z * self.data[6] + self.data[7], o.x * self.data[8] + o.y * self.data[9] + o.z * self.data[10] + self.data[11])
def transformTranspose(self, vector): ''' Transform a vector by transpose of this matrix --------- args: vector - Vector ''' return Vector( vector.x * self.data[0] + vector.y * self.data[3] + vector.z * self.data[6], vector.x * self.data[1] + vector.y * self.data[4] + vector.z * self.data[7], vector.x * self.data[2] + vector.y * self.data[5] + vector.z * self.data[8])
def transformDirection(self, vector): ''' Return a new vector of the transform of the given direction vector by this matrix. --------- args: vector - Vector ''' return Vector( vector.x * self.data[0] + vector.y * self.data[1] + vector.z * self.data[2], vector.x * self.data[4] + vector.y * self.data[5] + vector.z * self.data[6], vector.x * self.data[8] + vector.y * self.data[9] + vector.z * self.data[10])
def transformInverseDirection(self, vector): ''' Return a new vector of the transform of the given direction vector by inverse of this matrix It is assumed that this matrix is a pure rotation matrix (inverse is transpose) --------- args: vector - Vector ''' return Vector( vector.x * self.data[0] + vector.y * self.data[4] + vector.z * self.data[8], vector.x * self.data[1] + vector.y * self.data[5] + vector.z * self.data[9], vector.x * self.data[2] + vector.y * self.data[6] + vector.z * self.data[10])
def __init__(self): ''' Class constractor ''' self.position = Vector(0, 0, 0) self.velocity = Vector(0, 0, 0) self.acceleration = Vector(0, 0, 0) #Daming is used to make the paricle lose energy #damping = 0 -> object stops, damping = 1 -> velocity doesnt change self.damping = 1 #inverse mass = 0 -> infinte mass whichis dealt to immovable objects #inverse mass = infinity -> object is super fast (which is not useful in games) self.inverseMass = 0 #the value of the forceAccum is zeroed in each intergration step self.forceAccum = Vector(0, 0, 0)
def addContact(self, contacts, limit, iterator): ''' Fills the given contact structure with the generated contact and return the number of contacts that have been written. --------- args: contacts - list - particle on which the force is applied limit - int - maximum number of contacts in the array that can be written to iterator - int - index to contact to access in the contacts array ''' count = 0 for particle in self.particles: y = particle.getPosition().y if y < 0: contacts[iterator].contactNormal = Vector(0, 1, 0) contacts[iterator].particles[0] = particle contacts[iterator].particles[1] = None contacts[iterator].penetration = -y contacts[iterator].restitution = 0 count += 1 iterator += 1 if (count >= limit): return count return count
def getPosition(self): ''' Returns a new vector which represents the location of the particle ''' return Vector(self.position.x, self.position.y, self.position.z)
class Particle: ''' Class responsible for simulating particle behaviour --------- properties: position - Vector - represent location of particle in 3D space velocity - Vector - represent velocity of particle in 3 axes acceleration - Vector - represent acceleration of particle in 3 axes damping - double - represent damping applied to velocity of particle each time step inverseMass - double - store inverse mass of particle forceAccum - vector - represent force applied to particle in the next time step only --------- methods: setters & getters integrate - Update the particle properties each time step hasFiniteMass - Return true if object is movable clearAccumulator - clears the force applied to the particle in this time step addForce - add force to the forceAccum in this time step ''' def __init__(self): ''' Class constractor ''' self.position = Vector(0, 0, 0) self.velocity = Vector(0, 0, 0) self.acceleration = Vector(0, 0, 0) #Daming is used to make the paricle lose energy #damping = 0 -> object stops, damping = 1 -> velocity doesnt change self.damping = 1 #inverse mass = 0 -> infinte mass whichis dealt to immovable objects #inverse mass = infinity -> object is super fast (which is not useful in games) self.inverseMass = 0 #the value of the forceAccum is zeroed in each intergration step self.forceAccum = Vector(0, 0, 0) def integrate(self, duration): ''' method which updates the particle properties each time step --------- args: duration - double - duration of time step ''' assert (duration > 0) #update position by velocity # Pn = Pn-1 + v*t self.position.addScaledVector(self.velocity, duration) #Calculate acceleration from force resultAcceleration = Vector(self.acceleration.x, self.acceleration.y, self.acceleration.z) resultAcceleration.addScaledVector(self.forceAccum, self.inverseMass) #update velocity using acceleration self.velocity.addScaledVector(resultAcceleration, duration) #Calculate drag self.velocity *= self.damping**duration #clear all forces self.clearAccumulator() def setMass(self, mass): ''' method which set mass particle properties each time step --------- args: mass - double - mass of the object, cannot be zero ''' assert (mass != 0) self.inverseMass = 1 / mass def getMass(self): if self.inverseMass == 0: return float_info.max else: return 1 / self.inverseMass def setInverseMass(self, inverseMass): self.inverseMass = inverseMass def getInverseMass(self): return self.inverseMass def hasFiniteMass(self): if self.inverseMass >= 0: return True return False def setDamping(self, damping): self.damping = damping def getDamping(self): return self.damping def setPosition(self, x, y, z): self.position.x = x self.position.y = y self.position.z = z def getPosition(self): ''' Returns a new vector which represents the location of the particle ''' return Vector(self.position.x, self.position.y, self.position.z) def setVelocity(self, x, y, z): self.velocity.x = x self.velocity.y = y self.velocity.z = z def getVelocity(self): ''' Returns a new vector which represents the velocity of the particle ''' return Vector(self.velocity.x, self.velocity.y, self.velocity.z) def setAcceleration(self, x, y, z): self.acceleration.x = x self.acceleration.y = y self.acceleration.z = z def getAcceleration(self): ''' Returns a new vector which represents the acceleration of the particle ''' return Vector(self.acceleration.x, self.acceleration.y, self.acceleration.z) def clearAccumulator(self): ''' Clears the accumulated force on the particle ''' self.forceAccum.clear() def addForce(self, force): ''' Add force to the accumulated force on the object --------- args: force - Vector - additional force applied to the particle ''' self.forceAccum += force def __str__(self): return "position: " + str(self.position) + "\n" \ "velocity: " + str(self.velocity) + "\n" \ "acceleration: " + str(self.acceleration) + "\n" \ "force: " + str(self.forceAccum) + "\n" \ "damping: " + str(self.damping) + "\n" \ "mass: " + str(self.getMass()) + "n"
def getVelocity(self): ''' Returns a new vector which represents the velocity of the particle ''' return Vector(self.velocity.x, self.velocity.y, self.velocity.z)