All pastes #623908 Raw Edit

Someone

public text v1 · immutable
·published 2007-07-17 22:37 UTC
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