# # == Zooey by Aur Saraf, (c) 2007 # # = newton.rb # # Base class of all objects that behave physically. Handles physics. # require 'forwardable' require 'cairo' require 'helpers' require 'nvector-ext' require 'fx' module Zooey # class Newton V_ZERO = NVector[0.0, 0.0].freeze FX_ZERO = FXPair[V_ZERO.dup, V_ZERO.dup].freeze extend Forwardable include Properties attr_reader :matrix prop_reader :angle, :mass, :moment_of_inertia prop_accessor :friction_myu, :angular_friction_myu def initialize(*params) @matrix = Cairo::Matrix.identity @forces = [] @integration_data = {:accel => V_ZERO, :vel => V_ZERO, :angular_accel => 0.0, :angular_vel => 0.0} self[:pos] = NVector[0.0, 0.0] self[:vel] = NVector[0.0, 0.0] self[:accel] = NVector[0.0, 0.0] self[:friction_myu] = 0.3 self.mass = 1.0 self[:angle] = 0.0 self[:angular_vel] = 0.0 self[:angular_accel] = 0.0 self[:angular_friction_myu] = 0.5 self.moment_of_inertia = 1.0 end def x @matrix.x0 end def y @matrix.y0 end def mass=(m) self[:mass] = m self[:g_inv_mass] = 1.0/(ACCEL_CONSTANT_G*m) end def moment_of_inertia=(m) self[:moment_of_inertia] = m self[:g_inv_mom_inertia] = 1.0/(ACCEL_CONSTANT_G*m) end # Adds FXPair to force array def apply_fx(fx) fx.f *= FORCE_CONSTANT @forces << fx end # updates physics of element def update # remove all vectors that are equal to zero from @forces.delete_if{|fx| fx.remove?} # sum up vectors friction_force = if self[:vel] != V_ZERO -(self[:vel]/self[:vel].abs)* self[:mass]*self[:friction_myu] else V_ZERO end friction_moment = (-self[:angular_vel]<=>0)* self[:moment_of_inertia]*self[:angular_friction_myu] sf = [friction_force, friction_moment] unless @forces.empty? sf = @forces.inject(sf){|m, pair| [m[0]+pair.f, m[1] + pair.moment]} end # classical physics' motion equations # to allow more control on the movement, constants are introduced that # scale the unit of force. accel = self[:g_inv_mass]*sf[0] angular_accel = self[:g_inv_mom_inertia]*sf[1] self[:accel] = accel self[:angular_accel] = angular_accel t = 1.0/FRAMERATE self.integrate(:vel, :accel, t) self.move(*self.integrate(:pos, :vel, t)) self.integrate(:angular_vel, :angular_accel, t) self.rotate(self.integrate(:angle, :angular_vel, t)) end # df and f are symbols that index properties # t is a float # returns delta def integrate(df, f, dx) delta = ((self[f] + @integration_data[f])*dx/2) self[df] += delta @integration_data[f] = self[f] delta end def x=(x) move(x-self.x, 0) end def y=(y) move(0, y-self.y) end def move(x, y) delta = Cairo::Matrix.identity.rotate(-self[:angle]).transform_point(x, y) self[:pos] += NVector[*delta] @matrix.translate!(*delta) end def angle=(angle) self.rotate(angle-self[:angle]) self[:angle] = angle end def rotate(angle) self[:angle] += angle @matrix.rotate!(angle) end end end