Source: cameras/camera_manipulation.js

/*
 * File: camera_manipulation.js
 *
 * Adds the manipulation functions to the Camera class
 */
"use strict";

import Camera from "./camera_main.js"
import { eBoundCollideStatus } from "../utils/bounding_box.js";
import CameraShake from "./camera_shake.js";

/**
 * Update function to be called from GameLoop
 * @memberof Camera
 */
Camera.prototype.update = function () {
    if (this.mCameraShake !== null) {
        if (this.mCameraShake.done()) {
            this.mCameraShake = null;
        } else {
            this.mCameraShake.setRefCenter(this.getWCCenter());
            this.mCameraShake.update();
        }
    }
    this.mCameraState.update();
}

// For LERP function configuration
/**
 * Configure the linear interpolation of this Camera
 * @memberof Camera
 * @param {float} stiffness - the rate for the interpolations
 * @param {integer} duration - the number of cycles for the interpolations
 */
Camera.prototype.configLerp = function (stiffness, duration) {
    this.mCameraState.config(stiffness, duration);
}

// Define camera shake
/**
 * Define a new CameraShake for this Camera
 * @memberof Camera
 * @param {vec2} deltas - the [x,y] magnitudes of the shake
 * @param {float} freqs - the frequency of the shaking in revolutions per cycle
 * @param {float} shakeDuration - the number of cycles for the shaking
 */
Camera.prototype.shake = function (deltas, freqs, duration) {
    this.mCameraShake = new CameraShake(this.mCameraState, deltas, freqs, duration);
}

// Restart the shake
/**
 * Restart the shaking of this Camera
 * @memberof Camera
 * @returns {boolean} true if this Camera has a defined CameraShake
 */
Camera.prototype.reShake = function () {
    let success = (this.mCameraShake !== null);
    if (success)
        this.mCameraShake.reShake();
    return success;
}

// pan the camera to ensure aXform is within camera bounds
// this is complementary to the ClampAtBound: instead of clamping aXform, now, move the camera
/**
 * Pan this Camera to follow the Transform argument when it moves outside the zone
 * @memberof Camera
 * @param {Transform} aXform - Transform to follow
 * @param {float} zone - percentage from the center of this Camera
 */
Camera.prototype.panWith = function (aXform, zone) {
    let status = this.collideWCBound(aXform, zone);
    if (status !== eBoundCollideStatus.eInside) {
        let pos = aXform.getPosition();
        let newC = vec2.clone(this.getWCCenter());
        if ((status & eBoundCollideStatus.eCollideTop) !== 0) {
            newC[1] = pos[1] + (aXform.getHeight() / 2) - (zone * this.getWCHeight() / 2);
        }
        if ((status & eBoundCollideStatus.eCollideBottom) !== 0) {
            newC[1] = pos[1] - (aXform.getHeight() / 2) + (zone * this.getWCHeight() / 2);
        }
        if ((status & eBoundCollideStatus.eCollideRight) !== 0) {
            newC[0] = pos[0] + (aXform.getWidth() / 2) - (zone * this.getWCWidth() / 2);
        }
        if ((status & eBoundCollideStatus.eCollideLeft) !== 0) {
            newC[0] = pos[0] - (aXform.getWidth() / 2) + (zone * this.getWCWidth() / 2);
        }
        this.mCameraState.setCenter(newC);
    }
}
/**
 * Pan the Camera by dx,dy
 * @memberof Camera
 * @param {float} dx - change in x world coordinate
 * @param {float} dy - change in y world coordinate
 */
Camera.prototype.panBy = function (dx, dy) {
    let newC = vec2.clone(this.getWCCenter());
    newC[0] += dx;
    newC[1] += dy;
    this.mCameraState.setCenter(newC);
}

/**
 * Pan the Camera to be centered at cx,cy
 * @memberof Camera
 * @param {float} cx - x world coordinate
 * @param {float} cy - y world coordinate
 */
Camera.prototype.panTo = function (cx, cy) {
    this.setWCCenter(cx, cy);
}

// zoom with respect to the center
// zoom > 1 ==> zooming out, see more of the world
// zoom < 1 ==> zooming in, see less of the world, more detailed
// zoom < 0 is ignored
/**
 * Change the zoom level of this Camera with respect to the center
 * @memberof Camera
 * @param {float} zoom - scaling factor for the width of this Camera
 */
Camera.prototype.zoomBy = function (zoom) {
    if (zoom > 0) {
        this.setWCWidth(this.getWCWidth() * zoom);
    }
}

// zoom towards (pX, pY) by zoom: 
// zoom > 1 ==> zooming out, see more of the world
// zoom < 1 ==> zooming in, see less of the world, more detailed
// zoom < 0 is ignored
/**
 * Change zoom level of this Camera with respect to pos agrument
 * @memberof Camera
 * @param {vec2} pos - Point to scale this Camera with respect to
 * @param {float} zoom - sacling factor for the width of this Camera
 */
Camera.prototype.zoomTowards = function (pos, zoom) {
    let delta = [];
    let newC = [];
    vec2.sub(delta, pos, this.getWCCenter());
    vec2.scale(delta, delta, zoom - 1);
    vec2.sub(newC, this.getWCCenter(), delta);
    this.zoomBy(zoom);
    this.mCameraState.setCenter(newC);
}

export default Camera;