anchor.diff: -%%% @doc Physics: 4th-order Runge-Kutta integration
  -%%%
  -%%% @copyright 2012 David H. Bronke
  -%%% Licensed under the MIT license; see the LICENSE file for details.
  -%%% -------------------------------------------------------------------------------------------------------------------
  -%%% This module is loosely based on concepts from the following articles:
  -%%% * http://gafferongames.com/game-physics/integration-basics/
  -%%% * http://gafferongames.com/game-physics/physics-in-3d/
  -%%% -------------------------------------------------------------------------------------------------------------------
  +%% ------------------------------------------------------------------------
  +%% @doc Physics: 4th-order Runge-Kutta integration
  +%%
  +%% @copyright 2012 David H. Bronke
  +%% Licensed under the MIT license; see the LICENSE file for details.
  +%% ------------------------------------------------------------------------
  +%% This module is loosely based on concepts from the following articles:
  +%% * http://gafferongames.com/game-physics/integration-basics/
  +%% * http://gafferongames.com/game-physics/physics-in-3d/
  +%% ------------------------------------------------------------------------
   
   -module(pre_physics_rk4).
   
  -%% --------------------------------------------------------------------------------------------------------------------
  +% -------------------------------------------------------------------------
   
   % external api
   -export([simulate/2]).
   
  -%% --------------------------------------------------------------------------------------------------------------------
  +% -------------------------------------------------------------------------
   
   -include("log.hrl").
   
  -%% --------------------------------------------------------------------------------------------------------------------
  +% -------------------------------------------------------------------------
  +
  +-record(physical, {
  +        % Updated values (assume these change every frame)
  +        position = {0, 0, 0} :: vector:vec(),
  +        linear_momentum = {0, 0, 0} :: vector:vec(),
  +        orientation = {1, 0, 0, 0} :: quaternion:quat(),
  +        angular_momentum = {0, 0, 0} :: vector:vec(),
  +
  +        % Input-only values
  +        force_absolute = {0, 0, 0} :: vector:vec(),
  +        force_relative = {0, 0, 0} :: vector:vec(),
  +        torque_absolute = {0, 0, 0} :: vector:vec(),
  +        torque_relative = {0, 0, 0} :: vector:vec(),
  +
  +        % Purely calculated values (DON'T try to change these externally)
  +        last_update :: erlang:timestamp(),
  +        linear_velocity = {0, 0, 0} :: vector:vec(),
  +        angular_velocity = {0, 0, 0} :: vector:vec(),
  +        spin = {1, 0, 0, 0} :: quaternion:quat(),
  +
  +        % Intrinsic values (should NOT change during the life of an object)
  +        mass = 1 :: float(),
  +        inverse_mass = 1 :: float(),
  +        inertia_tensor = 1 :: float(),
  +        inverse_inertia_tensor = 1 :: float()
  +}).
  +
  +%% ------------------------------------------------------------------------
   %% External API
  -%% --------------------------------------------------------------------------------------------------------------------
  +%% ------------------------------------------------------------------------
   
   -spec evaluate(TimeDelta, Velocity, Force, Spin, Torque, State) -> {Velocity, Force, Spin, Torque, State} when
   	TimeDelta :: float(),
  
positive.code: 			Orientation
  		)
  	),
  
  	NewState = dict:store(linear_velocity, LinearVelocity, State),
  	NewState1 = dict:store(angular_velocity, AngularVelocity, NewState),
  	NewState2 = dict:store(spin, Spin, NewState1),
  
  	{Spin, NewState2}.
  
  %% --------------------------------------------------------------------------------------------------------------------
  %% @doc Simulate physical movement of the 'physical' object represented by `State`, over the given `TimeDelta`.
  
  simulate(TimeDelta, State) ->
  	Position = dict:fetch(position, State),
  	Velocity = dict:fetch(linear_velocity, State),
  	{OrientW, OrientX, OrientY, OrientZ} = dict:fetch(orientation, State),
  	AngularMomentum = dict:fetch(angular_momentum, State),
  
  	{Velocity1, Force1, Spin1, Torque1, State1} = evaluate(0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0}, State),
  	{Velocity2, Force2, Spin2, Torque2, State2} = evaluate(TimeDelta * 0.5, Velocity1, Force1, Spin1, Torque1, State1),
  
negative.code: 	},
  	NextAngularMomentum = vector:add(AngularMomentum, vector:multiply(TimeDelta, Torque)),
  
  	State1 = State#physical{
  		position = NextPosition,
  		linear_velocity = NextVelocity,
  		orientation = quaternion:unit(NextOrientation),
  		angular_momentum = NextAngularMomentum
  	},
  	%TODO: Should _NextAngularVelocity even be returned?
  	{NextSpin, State2} = update_state(State1),
  
  	{NextForce, NextTorque} = forces(TimeDelta, State2),
  
  	{NextVelocity, NextForce, NextSpin, NextTorque, State2}.
  
  %% ------------------------------------------------------------------------
  
  -spec forces(TimeDelta, State) -> {Force, Torque} when
  	TimeDelta :: float(),
  	State :: #physical{},
  	Force :: vector:vec(),
  	Torque :: vector:vec().
  
  %FIXME: This should be a callback so the behavior can do stuff like target-velocity calculations.
  forces(_TimeDelta, State) ->
  	#physical{
  		force_absolute = ForceAbs,
  		force_relative = ForceRel,
  		orientation = Orientation,
  		torque_absolute = TorqueAbs,
  		torque_relative = TorqueRel
  	} = State,
  
  	Force = vector:add(ForceAbs, quaternion:rotate(ForceRel, Orientation)),
  	Torque = vector:add(TorqueAbs, quaternion:rotate(TorqueRel, Orientation)),
  
  	{Force, Torque}.
  
  %% ------------------------------------------------------------------------
  
  -spec update_state(State) -> {Spin, AngularVelocity} when
  	State :: #physical{},
  	Spin :: quaternion:quat(),
  	AngularVelocity :: vector:vec().
  
  update_state(State) ->
  	#physical{
  		linear_momentum = LinearMomentum,
  		orientation = Orientation,
  		angular_momentum = AngularMomentum,
  		inverse_mass = InverseMass,
  		inverse_inertia_tensor = InverseInertia
  	} = State,
  
  	LinearVelocity = vector:multiply(InverseMass, LinearMomentum),
  
  