UNPKG

codehs-graphics

Version:

Helpers used to run graphics problems in the CodeHS editor.

399 lines (373 loc) 12.6 kB
'use strict'; var Thing = require('./thing.js'); /** * @class Line * @augments Thing * @param {number} x1 - x coordinate of starting point of line. * @param {number} y1 - y coordinate of starting point of line. * @param {number} x2 - x coordinate of end point of line. * @param {number} y2 - y coordinate of end point of line. */ function Line(x1, y1, x2, y2) { if (arguments.length !== 4) { throw new Error( 'You should pass exactly 4 argument to <span ' + 'class="code">new Line(x1, y1, x2, y2)</span>' ); } if ( typeof x1 !== 'number' || typeof y1 !== 'number' || typeof x2 !== 'number' || typeof y2 !== 'number' ) { throw new TypeError( 'You must pass 4 numbers to <span class="code">' + 'new Line(x1, y1, x2, y2)</span>. Make sure each parameter you' + 'are passing is a number.' ); } if (!isFinite(x1) || !isFinite(y1) || !isFinite(x2) || !isFinite(y2)) { throw new TypeError( 'One or more of the values you passed to <span ' + 'class="code">new Line(x1, y1, x2, y2)</span> is an illegal ' + 'number. Did you forget the parentheses in <span class="code">' + 'getWidth()</span> or <span class="code">getHeight()</span>? Or ' + 'did you perform a calculation on a variable that is not a ' + 'number?' ); } Thing.call(this); this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.lineWidth = 2; this.type = 'Line'; } Line.prototype = new Thing(); Line.prototype.constructor = Line; /** * Sets the color of a line. * * @param {Color} color - Sets the color of the line. */ Line.prototype.setColor = function(color) { if (arguments.length !== 1) { throw new Error( 'You should pass exactly 1 argument to <span ' + 'class="code">setColor(color)</span>.' ); } if (color === undefined) { throw new TypeError('Invalid color'); } this.stroke = color; }; /** * Gets the color of a line. * * @returns {Color} Color of the line. */ Line.prototype.getColor = function() { return this.stroke; }; /** * Draws the line in the canvas. * * @param {CodeHSGraphics} __graphics__ - Instance of the __graphics__ module. */ Line.prototype.draw = function(__graphics__) { var context = __graphics__.getContext(); // http://stackoverflow.com/questions/17125632/html5-canvas-rotate-object-without-moving-coordinates context.save(); context.fillStyle = this.color.toString(); context.beginPath(); context.strokeStyle = this.stroke.toString(); context.lineWidth = this.lineWidth; var rotatedPoints = getRotatedPoints(this.x1, this.y1, this.x2, this.y2, this.rotation); context.moveTo(rotatedPoints[0], rotatedPoints[1]); context.lineTo(rotatedPoints[2], rotatedPoints[3]); context.closePath(); context.stroke(); context.restore(); }; /** * Gets the new points based on their rotated values. * */ /** * Gets the new points based on their rotated values. * * @param {number} x1 X coordinate of start point * @param {number} y1 Y coordinate of start point * @param {number} x2 X coordinate of end point * @param {number} y2 Y Coordinate of end point * @param {number} rotation radians rotated (Expected in radians) * @return {array} List of coordinates of both points. */ var getRotatedPoints = function(x1, y1, x2, y2, rotation) { var midX = (x1 + x2) / 2; var midY = (y1 + y2) / 2; var sinAngle = Math.sin(rotation); var cosAngle = Math.cos(rotation); var newX; var newY; // Rotate point 1 x1 -= midX; y1 -= midY; newX = x1 * cosAngle - y1 * sinAngle; newY = x1 * sinAngle + y1 * cosAngle; x1 = newX + midX; y1 = newY + midY; // Rotate point 2 x2 -= midX; y2 -= midY; newX = x2 * cosAngle - y2 * sinAngle; newY = x2 * sinAngle + y2 * cosAngle; x2 = newX + midX; y2 = newY + midY; return [x1, y1, x2, y2]; }; /** * Checks if a given point is contained in the line. * * @param {number} x - x coordinate of the point being tested. * @param {number} y - y coordinate of the point being tested. */ Line.prototype.containsPoint = function(x, y) { var betweenXs = (this.x1 <= x && x <= this.x2) || (this.x2 <= x && x <= this.x1); var betweenYs = (this.y1 <= y && y <= this.y2) || (this.y2 <= y && y <= this.y1); if (this.x1 == this.x2) { return this.x1 == x && betweenYs; } else { var slope = (this.y2 - this.y1) / (this.x2 - this.x1); return ( Math.abs(slope * (x - this.x1) - (y - this.y1)) <= this.lineWidth && betweenXs && betweenYs ); } }; /** * Returns the width of the line. * * @returns {number} The width of the line. */ Line.prototype.getWidth = function() { return this.width; }; /** * Returns the height of the line. * * @returns {number} The width of the line. */ Line.prototype.getHeight = function() { return this.height; }; /** * Sets the width of the line. * * @param {number} width - The resulting width of the line. */ Line.prototype.setLineWidth = function(width) { if (arguments.length !== 1) { throw new Error( 'You should pass exactly 1 argument to <span ' + 'class="code">setLineWidth</span>' ); } if (typeof width !== 'number' || !isFinite(width)) { throw new TypeError( 'You must pass a finite number to <span class=' + '"code">setLineWidth(width)</span>. Did you perform a ' + 'calculation on a variable that is not a number?' ); } this.lineWidth = width; }; /** * Sets the *starting* point of the line. * * @param {number} x - The x coordinate of the resulting ending point. * @param {number} y - The y coordinate of the resulting ending point. */ Line.prototype.setStartpoint = function(x, y) { if (arguments.length !== 2) { throw new Error( 'You should pass exactly 2 arguments to <span ' + 'class="code">setStartpoint(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">setStartpoint(x, y)</span>. Did you ' + 'forget the parentheses in <span class="code">getWidth()</span> ' + 'or <span class="code">getHeight()</span>? Or did you perform a ' + 'calculation on a variable that is not a number?' ); } 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">setStartpoint(x, y)</span>. Did you ' + 'forget the parentheses in <span class="code">getWidth()</span> ' + 'or <span class="code">getHeight()</span>? Or did you perform a ' + 'calculation on a variable that is not a number?' ); } this.setPosition(x, y); }; /** * Sets the *starting* point of the line. * * @param {number} x - The x coordinate of the resulting starting point. * @param {number} y - The y coordinate of the resulting starting point. */ Line.prototype.setPosition = function(x, y) { if (arguments.length !== 2) { throw new Error( 'You should pass exactly 2 arguments to <span ' + 'class="code">setPosition(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">setPosition(x, y)</span>. Did you ' + 'forget the parentheses in <span class="code">getWidth()</span> ' + 'or <span class="code">getHeight()</span>? Or did you perform a ' + 'calculation on a variable that is not a number?' ); } 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">setPosition(x, y)</span>. Did you ' + 'forget the parentheses in <span class="code">getWidth()</span> ' + 'or <span class="code">getHeight()</span>? Or did you perform a ' + 'calculation on a variable that is not a number?' ); } this.x1 = x; this.y1 = y; }; /** * Sets the *ending* point of the line. * * @param {number} x - The x coordinate of the resulting ending point. * @param {number} y - The y coordinate of the resulting ending point. */ Line.prototype.setEndpoint = function(x, y) { if (arguments.length !== 2) { throw new Error( 'You should pass exactly 2 arguments to <span ' + 'class="code">setEndpoint(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">setEndpoint(x, y)</span>. Did you ' + 'forget the parentheses in <span class="code">getWidth()</span> ' + 'or <span class="code">getHeight()</span>? Or did you perform a ' + 'calculation on a variable that is not a number?' ); } 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">setEndpoint(x, y)</span>. Did you ' + 'forget the parentheses in <span class="code">getWidth()</span> ' + 'or <span class="code">getHeight()</span>? Or did you perform a ' + 'calculation on a variable that is not a number?' ); } this.x2 = x; this.y2 = y; }; /** * Moves the entire line. * * @param {number} dx - The change in x coordinate of both starting and ending points. * @param {number} dy - The change in y coordinate of both starting and ending points. */ Line.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>' ); } this.x1 += dx; this.y1 += dy; this.x2 += dx; this.y2 += dy; }; /** * Gets the x coordinate of the Line's start point. * * @returns {number} The x coordinate of the Line's start point. */ Line.prototype.getX = function() { return this.x1; }; /** * Gets the y coordinate of the Line's start point. * * @returns {number} The y coordinate of the Line's start point. */ Line.prototype.getY = function() { return this.y1; }; /** * Gets the x coordinate of the Line's start point. * * @returns {number} The x coordinate of the Line's start point. */ Line.prototype.getStartX = function() { return this.x1; }; /** * Gets the y coordinate of the Line's start point. * * @returns {number} The y coordinate of the Line's start point. */ Line.prototype.getStartY = function() { return this.y1; }; /** * Gets the x coordinate of the Line's end point. * * @returns {number} The x coordinate of the Line's end point. */ Line.prototype.getEndX = function() { return this.x2; }; /** * Gets the y coordinate of the Line's end point. * * @returns {number} The y coordinate of the Line's end point. */ Line.prototype.getEndY = function() { return this.y2; }; module.exports = Line;