API Docs for: v0.1.0
Show:

File: src\geom\Polygon.js

var inherit = require('../utils/inherit'),
    Vector = require('../math/Vector'),
    C = require('../constants');

/**
 * A *convex* clockwise Polygon.
 *
 * @class Polygon
 * @constructor
 * @param x {Number} The X origin of the polygon, all X coords for all points are relative to this
 * @param y {Number} The Y origin of the polygon, all Y coords for all points are relative to this
 * @param points {Array<Vector>|Array<Number>} This can be an array of Vectors that form the polygon,
 *      a flat array of numbers that will be interpreted as [x,y, x,y, ...]
 * @param scale {Number} The scale of the polygon
 */
var Polygon = function(x, y, points, scale) {
    /**
     * The origin point of the polygon, all points are relative to this
     *
     * @property position
     * @type Vector
     */
    this.position = new Vector();

    /**
     * The unscaled points of the polygon, the X & Y values here should be
     * relative to the origin X & Y.
     *
     * @property _points
     * @type Array<Vector>
     * @default []
     * @private
     */
    this._points = null;

    /**
     * The scale of the polygon
     *
     * @property scale
     * @type Vector
     * @default new Vector(1, 1)
     */
    this.scale = scale || new Vector(1, 1);

    /**
     * The points of the polygon, the X & Y values here should be
     * relative to the origin X & Y values.
     *
     * @property points
     * @type Array<Vector>
     * @default []
     */
    this.points = [];

    /**
     * These vectors are calculated by `this.recalc()` and represent the edges
     * of the polygon defined by it's points.
     *
     * @property edges
     * @type Array<Vector>
     * @default []
     * @readOnly
     */
    this.edges = [];

    /**
     * These vectors are calculated by `this.recalc()` and represent the normals
     * of the polygon edges defined by it's points.
     *
     * @property normals
     * @type Array<Vector>
     * @default []
     * @readOnly
     */
    this.normals = [];

    //if this is a flat array of numbers, convert it to points
    if(typeof points[0] === 'number') {
        var p = [];
        for(var i = 0, il = points.length; i < il; i+=2) {
            p.push(
                new Vector(points[i], points[i + 1])
            );
        }

        points = p;
    }

    //assign the points
    this._points = points;

    //set position
    this.x = x || 0;
    this.y = y || 0;

    //recalculate scaled points, edges, and normals
    this.recalc();

    //internal shape type
    this._shapetype = C.SHAPE.POLYGON;
};

inherit(Polygon, Object, {
    /**
     * Creates a clone of this polygon
     *
     * @method clone
     * @return {Polygon} a copy of the polygon
     */
    clone: function() {
        var points = [];
        for (var i=0; i<this._points.length; i++) {
            points.push(this._points[i].clone());
        }

        return new Polygon(points, this.scale);
    },

    /**
     * Copies the values from another polygon to this one
     *
     * @method copy
     * @param polygon {Polygon} The polygon to copy vlaues from
     * @return {Polygon} Returns itself.
     * @chainable
     */
    copy: function(poly) {
        //copy the position
        this.position.copy(poly.position);

        //clone the points to this polygon
        this._points.length = this.points.length = 0;
        for(var i = 0; i < poly._points.length; ++i) {
            this._points.push(poly._points[i].clone());
        }

        this.scale.copy(poly.scale);

        //update our edges and normals
        this.recalc();

        return this;
    },

    /**
     * Checks if the x, and y coords passed to this function are contained within this polygon
     *
     * @method contains
     * @param x {Number} The X coord of the point to test
     * @param y {Number} The Y coord of the point to test
     * @return {Boolean} if the x/y coords are within this polygon
     */
    contains: function(x, y) {
        var inside = false;

        // use some raycasting to test hits
        // https://github.com/substack/point-in-polygon/blob/master/index.js
        for(var i = 0, j = this.points.length - 1; i < this.points.length; j = i++) {
            var xi = this.points[i].x, yi = this.points[i].y,
                xj = this.points[j].x, yj = this.points[j].y,
                intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);

            if(intersect) inside = !inside;
        }

        return inside;
    },

    /**
     * Checks if this polygon's values are equal to anothers
     *
     * @method equals
     * @param polygon {Polygon} The polygon to check against
     * @return {Boolean} True if they are equal
     */
    equals: function(poly) {
        //check position and points array length
        if(!this.position.equals(poly.position) || this.points.length !== poly.points.length) {
            return false;
        }

        //check each point
        for(var i = 0; i < poly.points.length; ++i) {
            if(!this.points[i].equals(poly.points[i])) {
                return false;
            }
        }

        return true;
    },

    /**
     * Recalculates the scaled points, edges, and normals of this polygon
     * based on the relative points
     *
     * @method recalc
     * @return {Polygon} Returns itself.
     * @chainable
     */
    recalc: function() {
        var points = this._points,
            len = points.length,
            p1, p2, e, n, i = 0;

        //scale our points
        for(i = 0; i < len; i++) {
            if(!this.points[i])
                this.points[i] = new Vector();

            this.points[i].set(
                this._points[i].x * this.scale.x,
                this._points[i].y * this.scale.y
            );
        }

        // reset edges and normals
        this.edges.length = this.normals.length = 0;

        //calculate edges and normals
        for(i = 0; i < len; ++i) {
            p1 = points[i];
            p2 = i < len - 1 ? points[i + 1] : points[0];
            e = p2.clone().sub(p1);
            n = e.clone().perp().normalize();

            this.edges.push(e);
            this.normals.push(n);
        }

        return this;
    }
});

/**
 * The origin X coord of the polygon
 *
 * @property x
 * @type Number
 * @default 0
 */
Object.defineProperty(Polygon.prototype, 'x', {
    get: function() {
        return this.position.x;
    },
    set: function(v) {
        this.position.x = v;
    }
});

/**
 * The origin Y coord of the polygon
 *
 * @property x
 * @type Number
 * @default 0
 */
Object.defineProperty(Polygon.prototype, 'y', {
    get: function() {
        return this.position.y;
    },
    set: function(v) {
        this.position.y = v;
    }
});

module.exports = Polygon;