graphics/polygon.js

'use strict';

var Thing = require('./thing.js');

/**
 * @class Polygon
 * @augments Thing
 */
function Polygon() {
    if (arguments.length !== 0) {
        throw new Error(
            'You should pass exactly 0 arguments to <span class="code">new Polygon()</span>'
        );
    }

    Thing.call(this);
    this.points = [];
    this.width = 0; // max x-distance of points in the polygon
    this.height = 0; // max y-distance of points in the polygon
    this.type = 'Polygon';
}

Polygon.prototype = new Thing();
Polygon.prototype.constructor = Polygon;

/**
 * Draws the polygon in the canvas.
 *
 * @param {CodeHSGraphics} __graphics__ - Instance of the __graphics__ module.
 */
Polygon.prototype.draw = function(__graphics__) {
    if (this.points.length === 0) {
        return;
    }

    var context = __graphics__.getContext();
    context.fillStyle = this.color.toString();
    context.beginPath();

    var first = this.points[0];
    context.moveTo(first.x, first.y);
    for (var i = 1; i < this.points.length; i++) {
        var cur = this.points[i];
        context.lineTo(cur.x, cur.y);
    }
    context.closePath();
    context.fill();
};

/**
 * Checks if the passed point is contained in the polygon.
 *
 * @param {number} x - The x coordinate of the point being tested.
 * @param {number} y - The y coordinate of the point being tested.
 * @returns {boolean} Whether the passed point is contained in the polygon.
 */
Polygon.prototype.containsPoint = function(x, y) {
    // https://www.eecs.umich.edu/courses/eecs380/HANDOUTS/PROJ2/InsidePoly.html
    // solution 3 from above
    var edges = [];
    var previousOrientation = -1;
    var x1, x2, y1, y2;
    for (var i = 0; i < this.points.length; i++) {
        x1 = this.points[i].x;
        y1 = this.points[i].y;
        x2 = this.points[(i + 1) % this.points.length].x;
        y2 = this.points[(i + 1) % this.points.length].y;
        var orientation = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) <= 0;
        if (previousOrientation < 0) {
            previousOrientation = orientation;
        } else {
            if (previousOrientation !== orientation) {
                return false;
            }
        }
    }
    return true;
};

/**
 * Gets the width of the rectangle.
 *
 * @returns {number} Width of the rectangle.
 */
Polygon.prototype.getWidth = function() {
    return this.width;
};

/**
 * Gets the height of the rectangle.
 *
 * @returns {number} Height of the rectangle.
 */
Polygon.prototype.getHeight = function() {
    return this.height;
};

/**
 * Adds a vertex to the polygon.
 *
 * @param {number} x - The x coordinate of the desired new vertex.
 * @param {number} y - The y coordinate of the desired new vertex.
 */
Polygon.prototype.addPoint = function(x, y) {
    if (arguments.length !== 2) {
        throw new Error(
            'You should pass exactly 2 arguments to <span class="code">addPoint(x, y)</span>'
        );
    }
    if (typeof x !== 'number' || !isFinite(x)) {
        throw new TypeError(
            'Invalid value for x-coordinate. ' +
                'Make sure you are passing finite numbers to ' +
                '<span class="code">addPoint(x, y)</span>.'
        );
    }
    if (typeof y !== 'number' || !isFinite(y)) {
        throw new TypeError(
            'Invalid value for y-coordinate. ' +
                'Make sure you are passing finite numbers to ' +
                '<span class="code">addPoint(x, y)</span>.'
        );
    }

    for (var i = 0; i < this.points.length; i++) {
        if (Math.abs(x - this.points[i].x) > this.width) {
            this.width = Math.abs(x - this.points[i].x);
        }
        if (Math.abs(y - this.points[i].y) > this.height) {
            this.height = Math.abs(y - this.points[i].y);
        }
    }
    this.points.push({x: x, y: y});
};

/**
 * Moves the entire polygon.
 *
 * @param {number} dx - The change in x coordinate of all starting and ending points.
 * @param {number} dy - The change in y coordinate of all starting and ending points.
 */
Polygon.prototype.move = function(dx, dy) {
    if (arguments.length !== 2) {
        throw new Error('You should pass exactly 2 arguments to <span class="code">move</span>');
    }
    if (typeof dx !== 'number' || !isFinite(dx)) {
        throw new TypeError(
            'Invalid number passed for <span class="code">' +
                'dx</span>. Make sure you are passing finite numbers to <span ' +
                'class="code">move(dx, dy)</span>'
        );
    }
    if (typeof dy !== 'number' || !isFinite(dy)) {
        throw new TypeError(
            'Invalid number passed for <span class="code">' +
                'dy</span>. Make sure you are passing finite numbers to <span ' +
                'class="code">move(dx, dy)</span>'
        );
    }

    for (var i = 0; i < this.points.length; i++) {
        this.points[i].x += dx;
        this.points[i].y += dy;
    }
};

module.exports = Polygon;