Source: Physics.js

import * as BAGEL from "./index.js";

/**
 *  The Physics class performs position-velocity-acceleration {@link Vector} related calculations
 *   for more advanced {@link Sprite} movement. 
 *  <br/>
 *  For example, can set speed and motion angle of a sprite with constant velocity (no deceleration),
 *   or player character can accelerate at an angle and automatically decelerate at a fixed rate.
 *  <br/>
 *  Similar to the {@link Rectangle} class design, 
 *   the Physics position Vector can be set to reference the position of a {@link Sprite},
 *   which keeps them synchronized.
 *  <br/>
 *  A Physics object needs to be updated once per frame; for Sprites, this is handled by
 *   their {@link Sprite#update|update} method.
 */
class Physics
{
	/**
	 * Initialize position, velocity, and acceleration vectors,
	 *   and set acceleration, maximum speed, and deceleration values.
	 * @param {number} accValue - default magnitude of acceleration when using {@link Physics#accelerateAtAngle|accelerateAtAngle}
	 * @param {number} maxSpeed - maximum speed: if speed is ever above this amount, it will be reduced to this amount
	 * @param {number} decValue - when not accelerating, object will decelerate (decrease speed) by this amount
	 * @constructor
	 */
	constructor(accValue, maxSpeed, decValue)
	{
		this.positionVector     = new BAGEL.Vector(0,0);
		this.velocityVector     = new BAGEL.Vector(0,0);
		this.accelerationVector = new BAGEL.Vector(0,0);
		this.accelerationValue = accValue
		this.maximumSpeed      = maxSpeed;
		this.decelerationValue = decValue;
	}

    
    /**
     * Get the current speed of this object (pixels/second).
     * @return {number} current speed
     */	
	getSpeed() 
	{ 
		return this.velocityVector.getLength(); 
	}

    /**
     * Set the speed (pixels/second) for this object (without changing the current angle of motion).
     * @param {number} speed - the new speed
     */
	setSpeed(speed) 
	{ 
		this.velocityVector.setLength(speed); 
	}
	
    /**
     * Get the current angle of motion (in degrees) of this object.
     * <br/>
     * (The angle is measured from the positive x-axis, 
     * and angles increase in clockwise direction, since positive y-axis is down.)
     * @return {number} current angle of motion
     */	
	getMotionAngle()
	{
		return this.velocityVector.getAngle();
	}
	
    /**
     * Set the angle of motion (in degrees) for this object (without changing the current speed).
     * <br/>
     * (The angle is measured from the positive x-axis, 
     * and angles increase in clockwise direction, since positive y-axis is down.)
     * @param {number} angleDegrees - the new angle of motion
     */
    setMotionAngle(angleDegrees)
	{
		this.velocityVector.setAngle(angleDegrees);
	}

	/**
     * Accelerate this object by a multiple of base acceleration value in the given direction.
     * @param {number} percent - percentage of acceleration value (specified in constructor) to apply
     * @param {number} angleDegrees - the direction of acceleration
     */
	acceleratePercentAtAngle(percent, angleDegrees)
	{
		let v = new BAGEL.Vector();
		v.setLength(this.accelerationValue * percent);
		v.setAngle(angleDegrees);
		this.accelerationVector.addVector( v );
	}

	/**
     * Accelerate this object in the given direction.
     * <br/>
     * (Uses the constant acceleration value specified in constructor.)
     * @param {number} angleDegrees - the direction of acceleration
     */
	accelerateAtAngle(angleDegrees)
	{
		this.acceleratePercentAtAngle( 1.00, angleDegrees );
	}

	/**
	 *  Update the values for position, velocity, and acceleration vectors.
	 *  <br/>
	 *  This method should be called once per frame (automatically handled for {@link Sprite} objects). 
	 */
	update(deltaTime)
	{
		// apply acceleration to velocity
        this.velocityVector.addValues( 
			this.accelerationVector.x * deltaTime, 
			this.accelerationVector.y * deltaTime );

        let speed = this.getSpeed();

        // decrease speed (decelerate) when not accelerating
        if (this.accelerationVector.getLength() < 0.001)
            speed -= this.decelerationValue * deltaTime;

        // keep speed within set bounds
		if (speed < 0)
			speed = 0;
		if (speed > this.maximumSpeed)
			speed = this.maximumSpeed;

        // update velocity
        this.setSpeed(speed);

        // apply velocity to position
        this.positionVector.addValues( 
			this.velocityVector.x * deltaTime, 
			this.velocityVector.y * deltaTime );

        // reset acceleration
        this.accelerationVector.setValues(0,0);
    }

}

export { Physics };