MarbleModel.java

/*
 * Copyright © 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */

package com.nokia.example.amaze.model;

/**
 * Model of the marble. 
 */
public class MarbleModel {
	// Constants
	public static final float DEFAULT_SIZE = 4.0f; // Marble width and height
	public static final float DEFAULT_SIZE_HALVED = DEFAULT_SIZE / 2;
	private static final float DEFAULT_Y = 10.0f;
	public static final float HYPOTENUSE = (float)Math.sqrt(DEFAULT_SIZE_HALVED * DEFAULT_SIZE_HALVED + DEFAULT_SIZE_HALVED * DEFAULT_SIZE_HALVED);
	private static final float DEFAULT_DRAG = 0.35f; // Acts as a mass
	public static final float DEFAULT_BOUNCE_DRAG = DEFAULT_DRAG * 0.4f;
	private static final int DEFAULT_MAX_VELOCITY_PER_AXIS = 5;
	private static final float FRICTION = 0.1f;
	
	// Members
	private float[] _position = null;
	private float[] _velocity = null;
	private float[] _newVelocity = null;
	private float _drag = DEFAULT_DRAG;
	private int _maxVelocityPerAxis = DEFAULT_MAX_VELOCITY_PER_AXIS;
	
	/**
	 * Constructor.
	 */
	public MarbleModel() {
		_position = new float[3];
		_position[1] = DEFAULT_Y;
		_velocity = new float[3];
		_newVelocity = new float[3];
	}

	/** 
	 * @param drag The drag to set ]0.0, 1.0]. This value defines how much the
	 * 			   marble resists the acceleration.
	 */
	public void setDrag(final float drag) {
		if (drag > 0 && drag <= 1.0) {
			// Subtract the given mass from 100 so that we don't have to do it
			// later in the calculations
			_drag = drag;
		}
	}
	
	/** 
	 * @return The drag.
	 */
	public final float drag() {
		return _drag;
	}
	
	/** 
	 * @param velocity The velocity to set.
	 */
	public void setVelocity(final float[] velocity) {
		_velocity = velocity;
	}
	
	/** 
	 * @return The velocity.
	 */
	public final float[] velocity() {
		return _velocity;
	}
	
	/**
	 * @param maxVelocity The maximum velocity per axis to set.
	 */
	public void setMaxVelocityPerAxis(final int maxVelocity) {
		if (maxVelocity >= 0) {
			_maxVelocityPerAxis = maxVelocity;
		}
	}
	
	/**
	 * Sets the marble position.
	 * @param position The new position.
	 */
	public void setPosition(final float[] position) {
		_position = position;
		_position[1] = DEFAULT_Y;
	}
	
	/**
	 * Sets the marble position.
	 * @param x X coordinate
	 * @param y Y coordinate
	 * @param z Z coordinate
	 */
	public void setPosition(final float x, final float y, final float z) {
		_position[0] = x;
		//_position[1] = y;
		_position[2] = z;
	}	
	
	/**
	 * Moves the marble.
	 * @param x X coordinate
	 * @param y Y coordinate
	 * @param z Z coordinate
	 */
	public void move(final float x, final float y, final float z) {
		_position[0] += x;
		//_position[1] += y;
		_position[2] += z;
	}
	
	/**
	 * @return The marble position.
	 */
	public final float[] position() {
		return _position;
	}

	/**
	 * Calculates the marble velocity based on the current velocity and the
	 * given values of acceleration.
	 * @param accelerationX
	 * @param accelerationY
	 * @return The modified velocity.
	 */
	public final float[] calculateVelocity(final double accelerationX,
										   final double accelerationY)
	{
		_newVelocity[1] = _velocity[1];
		_newVelocity[0] = _velocity[0] - (float)accelerationX * _drag;
		_newVelocity[2] = _velocity[2] + (float)accelerationY * _drag; // Note: Y => Z
		float absVelocity;
		
		for (int i = 0; i < 3; ++i) {
			if (i == 1) {
				continue;
			}
			
			absVelocity = Math.abs(_newVelocity[i]);

			// Apply friction
			if (absVelocity < FRICTION) {
				_newVelocity[i] = 0;
			}
			else {
				_newVelocity[i] -= FRICTION * (_newVelocity[i] / absVelocity);
			}
			
			// Limit the velocity based on maximum
			if (absVelocity > _maxVelocityPerAxis) {
				_newVelocity[i] = _maxVelocityPerAxis
						* (_newVelocity[i] / absVelocity);
			}
		}
		
		return _newVelocity;
	}
}