Source: rigid_shapes/rigid_rectangle_main.js

/* 
 * File: rigid_rectangle_main.js
 * 
 *       RigidRectangle class definition file
 */
"use strict";

import RigidShape from "./rigid_shape.js";
import * as debugDraw from "../core/debug_draw.js";

class RigidRectangle extends RigidShape {
    /**
     * @classdesc Defines a rigid rectangle shape supported by the physics systems
     * <p>Found in Chapter 9, page 537 of the textbook</p>
     *
     * Examples:
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter9/9.1.rigid_shapes_and_bounds/index.html 9.1 Rigid Shape and Bounds},
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter9/9.3.rectangle_collisions/index.html 9.3 Rectangle Collisions},
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter9/9.6.collision_position_correction/index.html 9.6 Collision Position Correction},
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter9/9.9.physics_presets/index.html 9.9 Physics Presets}
     * 
     * 
     * @extends RigidShape
     * @constructor
     * @param {Transform} xf - the Transform for this RigidRectangle
     * @param {float} width - the width of the rectangle
     * @param {float} height - the height of the rectangle
     * @return {RigidRectangle} a new RigidRectangle instance
     */
    constructor(xf, width, height) {
        super(xf);
        this.mType = "RigidRectangle";
        this.mWidth = width;
        this.mHeight = height;
        this.mBoundRadius = 0;
        this.mVertex = [];
        this.mFaceNormal = [];

        this.setVertices();
        this.computeFaceNormals();

        this.updateInertia();
    }

    /**
     * Update the inertia of this RigidRectangle
     * @method
     */
    updateInertia() {
        // Expect this.mInvMass to be already inverted!
        if (this.mInvMass === 0) {
            this.mInertia = 0;
        } else {
            // inertia=mass*width^2+height^2
            this.mInertia = (1 / this.mInvMass) * (this.mWidth * this.mWidth + this.mHeight * this.mHeight) / 12;
            this.mInertia = 1 / this.mInertia;
        }
    }

    /**
     * Adds a value to the width and height of this RigidRectangle.
     * Updates the vertices and inertia automatically
     * @param {float} dt - the value to add
     */
    incShapeSizeBy(dt) {
        this.mHeight += dt;
        this.mWidth += dt;
        this.setVertices();
        this.rotateVertices();
        this.updateInertia();
    }

    /**
     * Set the world coordinate position of this RigidRectangle.
     * Updates the vertices
     * @method
     * @param {float} x - horizontal position
     * @param {float} y - vertical position
     */
    setPosition(x, y) {
        super.setPosition(x, y);
        this.setVertices();
        this.rotateVertices();
    }

    /**
     * Adjust the position of this RigidRectangle by moving the position a distance along a specified vector.
     * Updates the vertices
     * @method
     * @param {vec2} v - the [X,Y] direction vector
     * @param {float} delta - the distance to move
     */
    adjustPositionBy(v, delta) {
        super.adjustPositionBy(v, delta);
        this.setVertices();
        this.rotateVertices();
    }

    /**
     * Calculates the world coordinates of the vertices of this RigidRectangle 
     * @method
     */
    setVertices() {
        this.mBoundRadius = Math.sqrt(this.mWidth * this.mWidth + this.mHeight * this.mHeight) / 2;
        let center = this.mXform.getPosition();
        let hw = this.mWidth / 2;
        let hh = this.mHeight / 2;
        // 0--TopLeft;1--TopRight;2--BottomRight;3--BottomLeft
        this.mVertex[0] = vec2.fromValues(center[0] - hw, center[1] - hh);
        this.mVertex[1] = vec2.fromValues(center[0] + hw, center[1] - hh);
        this.mVertex[2] = vec2.fromValues(center[0] + hw, center[1] + hh);
        this.mVertex[3] = vec2.fromValues(center[0] - hw, center[1] + hh);
    }

    /**
     * Change the Transform for this RigidRectangle
     * @method
     * @param {Transform} xf - the Transform for this RigidRectangle
     */
    setTransform(xf) {
        super.setTransform(xf);
        this.setVertices();
        this.rotateVertices();
    }

    /**
     * Computes the normal vectors for each of this RigidRectangle's sides 
     * @method
     */
    computeFaceNormals() {
        // 0--Top;1--Right;2--Bottom;3--Left
        // mFaceNormal is normal of face toward outside of rectangle    
        for (let i = 0; i < 4; i++) {
            let v = (i + 1) % 4;
            let nv = (i + 2) % 4;
            this.mFaceNormal[i] = vec2.clone(this.mVertex[v]);
            vec2.subtract(this.mFaceNormal[i], this.mFaceNormal[i], this.mVertex[nv]);
            vec2.normalize(this.mFaceNormal[i], this.mFaceNormal[i]);
        }
    }

    /**
     * Match the rotation of the vertices and face normals to the rotation of the Transform for this RigidRectangle
     * @method
     */
    rotateVertices() {
        let center = this.mXform.getPosition();
        let r = this.mXform.getRotationInRad();
        for (let i = 0; i < 4; i++) {
            vec2.rotateWRT(this.mVertex[i], this.mVertex[i], r, center);
        }
        this.computeFaceNormals();
    }

    /**
     * Draws this RigidRectangle to aCamera
     * @method
     * @param {Camera} aCamera - the Camera to draw to
     */
    draw(aCamera) {
        super.draw(aCamera);  // the cross marker at the center
        debugDraw.drawRectangle(aCamera, this.mVertex, this._shapeColor());
    }

    /**
     * Updates the motion and verticies of this RigidRectangle
     * @method
     */
    update() {
        super.update();
        this.setVertices();
        this.rotateVertices();
    }
}

export default RigidRectangle;