rendered paste body#
# == 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