From 944cb36f574a17bced30017b43ec2b7b0d93aa83 Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Sat, 19 Mar 2022 20:59:52 -0700 Subject: [PATCH 1/3] Docs fixes for proper JSDoc formatting (#131) --- entrypoints/chs.js | 10 +- entrypoints/windowBinder.js | 11 ++- package.json | 2 +- src/DOCSREADME.md | 44 +++++++++ src/console/index.js | 54 ++++++++--- src/datastructures/grid.js | 4 +- src/datastructures/queue.js | 11 ++- src/datastructures/set.js | 5 + src/datastructures/stack.js | 9 +- src/datastructures/vector.js | 24 +++-- src/graphics/arc.js | 13 +-- src/graphics/circle.js | 9 +- src/graphics/color.js | 35 ++++--- src/graphics/graphics-utils.js | 13 ++- src/graphics/group.js | 3 +- src/graphics/index.js | 149 +++++++++++++++++++++++++++++- src/graphics/keyboard.js | 161 +++++++++++++++++++++++---------- src/graphics/line.js | 27 +++--- src/graphics/oval.js | 10 +- src/graphics/polygon.js | 3 +- src/graphics/rectangle.js | 4 +- src/graphics/text.js | 4 +- src/graphics/thing.js | 151 ++++++++++++++++++++++++++----- src/graphics/webimage.js | 4 +- src/graphics/webvideo.js | 1 + src/manager.js | 4 +- src/randomizer.js | 24 ++--- src/sound/audio.js | 14 ++- src/sound/audioContext.js | 1 + src/sound/index.js | 7 +- src/sound/sound.js | 8 +- 31 files changed, 634 insertions(+), 185 deletions(-) create mode 100644 src/DOCSREADME.md diff --git a/entrypoints/chs.js b/entrypoints/chs.js index d6bab39d..81d6ad7d 100644 --- a/entrypoints/chs.js +++ b/entrypoints/chs.js @@ -1,16 +1,17 @@ export { default as Console } from '../src/console/index.js'; -export { default as Queue } from '../src/datastructures/queue.js'; -export { default as Stack } from '../src/datastructures/stack.js'; export { default as Grid } from '../src/datastructures/grid.js'; +export { default as Queue } from '../src/datastructures/queue.js'; export { default as ExtendedSet } from '../src/datastructures/set.js'; +export { default as Stack } from '../src/datastructures/stack.js'; export { default as Vector } from '../src/datastructures/vector.js'; export { default as Arc } from '../src/graphics/arc.js'; export { default as Circle } from '../src/graphics/circle.js'; export { default as Color } from '../src/graphics/color.js'; -export { map, getDistance } from '../src/graphics/graphics-utils.js'; +export { getDistance, map } from '../src/graphics/graphics-utils.js'; export { default as Group } from '../src/graphics/group.js'; +export { default as ImageLibrary } from '../src/graphics/imagelibrary.js'; export { default as Graphics } from '../src/graphics/index.js'; -export { default as Keyboard } from '../src/graphics/keyboard.js'; +export * as Keyboard from '../src/graphics/keyboard.js'; export { default as Line } from '../src/graphics/line.js'; export { default as Oval } from '../src/graphics/oval.js'; export { default as Polygon } from '../src/graphics/polygon.js'; @@ -19,7 +20,6 @@ export { default as Text } from '../src/graphics/text.js'; export { default as Thing } from '../src/graphics/thing.js'; export { default as WebImage } from '../src/graphics/webimage.js'; export { default as WebVideo } from '../src/graphics/webvideo.js'; -export { default as ImageLibrary } from '../src/graphics/imagelibrary.js'; export * as Randomizer from '../src/randomizer.js'; export { default as AudioManager } from '../src/sound/'; export { default as Audio } from '../src/sound/audio.js'; diff --git a/entrypoints/windowBinder.js b/entrypoints/windowBinder.js index 1317f3bd..68953c8e 100644 --- a/entrypoints/windowBinder.js +++ b/entrypoints/windowBinder.js @@ -1,16 +1,17 @@ import { default as Console } from '../src/console/index.js'; -import { default as Queue } from '../src/datastructures/queue.js'; -import { default as Stack } from '../src/datastructures/stack.js'; import { default as Grid } from '../src/datastructures/grid.js'; +import { default as Queue } from '../src/datastructures/queue.js'; import { default as ExtendedSet } from '../src/datastructures/set.js'; +import { default as Stack } from '../src/datastructures/stack.js'; import { default as Vector } from '../src/datastructures/vector.js'; import { default as Arc } from '../src/graphics/arc.js'; import { default as Circle } from '../src/graphics/circle.js'; import { default as Color } from '../src/graphics/color.js'; -import { map, getDistance } from '../src/graphics/graphics-utils.js'; +import { getDistance, map } from '../src/graphics/graphics-utils.js'; import { default as Group } from '../src/graphics/group.js'; +import { default as ImageLibrary } from '../src/graphics/imagelibrary.js'; import { default as GraphicsManager } from '../src/graphics/index.js'; -import { default as Keyboard } from '../src/graphics/keyboard.js'; +import * as Keyboard from '../src/graphics/keyboard.js'; import { default as Line } from '../src/graphics/line.js'; import { default as Oval } from '../src/graphics/oval.js'; import { default as Polygon } from '../src/graphics/polygon.js'; @@ -19,7 +20,6 @@ import { default as Text } from '../src/graphics/text.js'; import { default as Thing } from '../src/graphics/thing.js'; import { default as WebImage } from '../src/graphics/webimage.js'; import { default as WebVideo } from '../src/graphics/webvideo.js'; -import { default as ImageLibrary } from '../src/graphics/imagelibrary.js'; import * as Randomizer from '../src/randomizer.js'; import { default as Audio } from '../src/sound/audio.js'; import { default as AudioManager } from '../src/sound/index.js'; @@ -60,6 +60,7 @@ window.mouseDownMethod = GraphicsInstance.mouseDownMethod.bind(GraphicsInstance) window.mouseDragMethod = GraphicsInstance.mouseDragMethod.bind(GraphicsInstance); window.mouseUpMethod = GraphicsInstance.mouseUpMethod.bind(GraphicsInstance); window.mouseMoveMethod = GraphicsInstance.mouseMoveMethod.bind(GraphicsInstance); +window.waitForClick = GraphicsInstance.waitForClick.bind(GraphicsInstance); window.stopAllTimers = GraphicsInstance.stopAllTimers.bind(GraphicsInstance); window.setMainTimer = GraphicsInstance.setMainTimer.bind(GraphicsInstance); window.stopTimer = GraphicsInstance.stopTimer.bind(GraphicsInstance); diff --git a/package.json b/package.json index 9e60ad9b..77ca3a05 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build": "node build.js", "dev": "node build.js watch", "dist": "tsc && node build.js dist", - "docs": "npm run build && cp -r dist/* ./site/assets/ && jsdoc src/**/*.js --template node_modules/docdash --destination _site/docs", + "docs": "npm run build && cp -r dist/* ./site/assets/ && jsdoc --readme src/DOCSREADME.md src/**/*.js src/*.js --template node_modules/docdash --destination _site/docs", "prepare": "husky install", "site": "npm run docs && npx @11ty/eleventy", "site:watch": "npm run docs && npx @11ty/eleventy --watch" diff --git a/src/DOCSREADME.md b/src/DOCSREADME.md new file mode 100644 index 00000000..f25e45cb --- /dev/null +++ b/src/DOCSREADME.md @@ -0,0 +1,44 @@ +# CodeHS JavaScript Library + +Welcome! This is documentation for the CodeHS JavaScript Library. + +## Graphics + +- [Color]{@link module:Color~Color} +- [Keyboard]{@link module:Keyboard} + +### Shapes + +- {@link Arc} +- {@link Circle} +- {@link Group} +- {@link Line} +- {@link Oval} +- {@link Polygon} +- {@link Rectangle} +- {@link Text} +- {@link Thing} +- {@link WebImage} +- {@link WebVideo} + +## Console + +- {@link readInt} +- {@link readLine} +- {@link readFloat} +- {@link readBoolean} +- {@link print} +- {@link println} + +## Data Structures + +- {@link Grid} +- {@link Queue} +- {@link ExtendedSet} +- {@link Stack} +- {@link Vector} + +## Audio + +- {@link Audio} +- {@link Sound} diff --git a/src/console/index.js b/src/console/index.js index 30baa4fb..d76843d4 100644 --- a/src/console/index.js +++ b/src/console/index.js @@ -1,16 +1,36 @@ -/* Console - * - * A console represents a text console that allows the user to print, println, - * and read an integer using readInt, and read a float using readFloat. These - * functions pop up prompt dialogs and make sure that the results are actually - * of the desired type. - * - * @author Jeremy Keeshin July 9, 2012 - * @author Andy Bayer Fall 2021 +/** + * Console provides utilities for interacting with a text console. + * {@link Console#readInt}, {@link Console#readFloat}, {@link Console#readBoolean}, and {@link Console#readLine} + * prompt the user for input and parse it to the corresponding type. This prompt will use the blocking + * browser prompt by default, but can be configured using {@link Console#onPrompt}. * + * Console also exposes {@link Console#print} and {@link Console#println}, which are used for + * emitting output. By default the output will print to the console, but can be configured using + * {@link Console#onPrint}. */ +class Console { + /** + * Function invoked when asking for user input. + * This function is invoked with the string of the prompt, i.e. readInt('give me an int!'). + * The result of invoking onPrompt will be subjected to parsing functions for confirming + * it's an appropriate data type (a float, in the case of readFloat, for example). If + * onPrompt is undefined, window.prompt is used as a fallback. + * @type {function} + */ + onPrompt = window.prompt.bind(window); + /** + * Function invoked when printing. + * This function is invoked with any output, either in the case of explicit calls to `print` + * or `println` or internal calls within the library. If onPrint is undefined, console.log + * is used as a fallback. + * @type {function} + */ + onPrint = window.console.log.bind(window.console); + /** + * Function invoked when {@link Console#clear} is called. + */ + onClear = window.console.clear.bind(window.console); -export default class Console { /** * Initialize the console class, additionally configuring any event handlers. * @constructor @@ -28,7 +48,7 @@ export default class Console { constructor(options = {}) { this.onPrompt = options.onPrompt ?? window.prompt.bind(window); this.onPrint = options.onPrint ?? window.console.log.bind(window.console); - this.onClear = options.onClear ?? (() => {}); + this.onClear = options.onClear ?? window.console.clear.bind(window.console); } /** @@ -63,6 +83,7 @@ export default class Console { /** * Clear the console. + * @global */ clear() { this.onClear(); @@ -71,6 +92,7 @@ export default class Console { /** * Print a value to the console. * @param {...any} args - Anything to print. + * @global */ print(...args) { if (args.length < 1) { @@ -82,6 +104,7 @@ export default class Console { /** * Print a value to the console, followed by a newline character. * @param {any} value - The value to print. + * @global */ println(value) { if (arguments.length === 0) { @@ -112,6 +135,7 @@ export default class Console { * why a given input was rejected. For example, the errorMsgType "an integer," would result in * printing "That was not an integer. Please try again." if parseFn failed. * @returns {number} + * @global */ readNumber(str, parseFn, errorMsgType) { const DEFAULT = 0; // If we get into an infinite loop, return DEFAULT. @@ -120,10 +144,10 @@ export default class Console { let promptString = str; let loopCount = 0; /** - * @type {boolean} * indicates whether the parsing has been successful, meaning * it hasn't hit the INFINITE_LOOP_CHECK or null cases, and the input from the user has * satisfied parseFn. in this case, the input should be printed. + * @type {boolean} * */ let successful = false; let parsedResult; @@ -163,6 +187,7 @@ export default class Console { * Read a line from the user. * @param {str} str - A message associated with the modal asking for input. * @returns {str} The result of the readLine prompt. + * @global */ readLine(str) { if (arguments.length !== 1) { @@ -179,6 +204,7 @@ export default class Console { * Read a bool from the user. * @param {str} str - A message associated with the modal asking for input. * @returns {str} The result of the readBoolean prompt. + * @global */ readBoolean(str) { if (arguments.length !== 1) { @@ -208,6 +234,7 @@ export default class Console { * though they are successfully parsed as ints. * @param {str} str - A message associated with the modal asking for input. * @returns {str} The result of the readInt prompt. + * @global */ readInt(str) { if (arguments.length !== 1) { @@ -233,6 +260,7 @@ export default class Console { * Read a float with our safe helper function. * @param {str} str - A message associated with the modal asking for input. * @returns {str} The result of the readFloat prompt. + * @global */ readFloat(str) { if (arguments.length !== 1) { @@ -242,3 +270,5 @@ export default class Console { return this.readNumber(str, parseFloat, 'a float'); } } + +export default Console; diff --git a/src/datastructures/grid.js b/src/datastructures/grid.js index 9fdb5b26..d03cf3c8 100644 --- a/src/datastructures/grid.js +++ b/src/datastructures/grid.js @@ -1,13 +1,13 @@ -/** @module Grid */ - /** * A grid is an abstraction around a two-dimensional array. + * @class */ class Grid { type = 'Grid'; /** * Constructs a grid. + * @constructor * @param {number} rows * @param {number} cols */ diff --git a/src/datastructures/queue.js b/src/datastructures/queue.js index 8d41b3cf..169c3c36 100644 --- a/src/datastructures/queue.js +++ b/src/datastructures/queue.js @@ -1,6 +1,9 @@ -'use strict'; - -export default class Queue extends Array { +/** + * A Queue is an Array subclass that implements First In, First Out ordering + * @class + * @extends Array + */ +class Queue extends Array { /** * Get the number of objects in the queue. * @returns {number} Number of elements in the queue. @@ -52,3 +55,5 @@ export default class Queue extends Array { return this.length === 0; } } + +export default Queue; diff --git a/src/datastructures/set.js b/src/datastructures/set.js index dcad6329..9cc3fef1 100644 --- a/src/datastructures/set.js +++ b/src/datastructures/set.js @@ -1,3 +1,8 @@ +/** + * The ExtendedSet extends the native Set implementation, adding some functionality. + * @class + * @extends Set + */ class ExtendedSet extends Set { /** * Returns whether the set is empty. diff --git a/src/datastructures/stack.js b/src/datastructures/stack.js index 64ce874d..a8d3c99f 100644 --- a/src/datastructures/stack.js +++ b/src/datastructures/stack.js @@ -1,4 +1,9 @@ -export default class Stack extends Array { +/** + * A Stack is a subclass of an Array that implements First In, Last Out ordering. + * @class + * @extends Array + */ +class Stack extends Array { /** * Get the number of objects in the stack. * @returns {number} Number of elements in the stack. @@ -38,3 +43,5 @@ export default class Stack extends Array { return this.length === 0; } } + +export default Stack; diff --git a/src/datastructures/vector.js b/src/datastructures/vector.js index c4535ec5..714bc3e1 100644 --- a/src/datastructures/vector.js +++ b/src/datastructures/vector.js @@ -1,5 +1,9 @@ import { degreesToRadians, radiansToDegrees } from '../graphics/arc.js'; +/** + * The Vector class is ued to model a 2 or 3 dimensional vector. + * @class + */ class Vector { /** * @constructor @@ -27,13 +31,10 @@ class Vector { /** * Add a vector to this one, modifying this one. - * @method add - * @param {number} x the x component of the vector to be added + * @param {number|Vector|number[]} x the x component of the vector to be added. + * Alternatively, a Vector or list of numbers to add. * @param {number} [y] the y component of the vector to be added * @param {number} [z] the z component of the vector to be added - * - * @method add - * @param {Vector|number[]} vector vector or array to add * @returns {Vector} this vector, modified */ add(x, y, z) { @@ -57,13 +58,11 @@ class Vector { /** * Subtract a vector from this one, modifying this one. - * @method subtract - * @param {number} x the x component of the vector to be subtracted + * @param {number|Vector|number[]} x the x component of the vector to be subtracted + * Alternatively, a Vector or list of numbers to substract. * @param {number} [y] the y component of the vector to be subtracted * @param {number} [z] the z component of the vector to be subtracted * - * @method subtract - * @param {Vector|number[]} vector vector or array to subtract * @returns {Vector} this vector, modified */ subtract(x, y, z) { @@ -87,13 +86,12 @@ class Vector { /** * Multiply this vector by a vector, scalar, or array, modifying it in place and returning it. - * @method multiply - * @param {number} x scalar to multiply the x component by + * @param {number|Vector|number[]} x scalar to multiply the x component by + * Alternatively, a Vector or list of numbers to multiply * @param {number} [y] scalar to multiply the y component by * @param {number} [z] scalar to multiply the z component by * - * @method add - * @param {Vector|number[]} vector vector or array to multiply + k * @returns {Vector} this vector, modified */ multiply(x, y, z) { diff --git a/src/graphics/arc.js b/src/graphics/arc.js index 91633fb8..2b617182 100644 --- a/src/graphics/arc.js +++ b/src/graphics/arc.js @@ -1,11 +1,10 @@ import Thing from './thing.js'; import { getDistance } from './graphics-utils.js'; -/** @module Arc */ - /** - * An arc is a continuous slice of a circle described by the position of its center, its radius, - * and the angles it is drawn between + * An Arc is a continuous slice of a circle described by the position of its center, its radius, + * and the angles it is drawn between. + * An Arc draws relative to its center, just like a {@link Circle}. * @extends Thing */ class Arc extends Thing { @@ -21,8 +20,9 @@ class Arc extends Thing { * Constructs a new arc. * @constructor * @example - * const a = new Arc(30, 0, 90, 0); - * + * // create an Arc with radius 30 with an angle from 0 to -90 + * const arc = new Arc(30, 0, 90, Arc.DEGREES); + * add(arc); * @param {number} radius - Desired radius of the arc. * @param {number} startAngle - Start angle of the arc. * @param {number} endAngle - End angle of the arc. @@ -207,6 +207,7 @@ class Arc extends Thing { /** * Checks if a given point is contained within the arc. * + * @alias Arc#containsPoint * @param {number} x - x coordinate of the point being tested. * @param {number} y - y coordinate of the point being tested. * @return {boolean} diff --git a/src/graphics/circle.js b/src/graphics/circle.js index 16fa37a4..6e4bb2b9 100644 --- a/src/graphics/circle.js +++ b/src/graphics/circle.js @@ -2,10 +2,8 @@ import Thing from './thing.js'; import Color from './color.js'; import { getDistance } from './graphics-utils.js'; -/** @module Circle */ - /** - * A circle defined by its radius. + * A Circle defined by its radius. Circles draw with their center at their x, y position. * @extends Thing */ class Circle extends Thing { @@ -17,8 +15,10 @@ class Circle extends Thing { * @constructor * @param {number} radius - Radius of the circle. * @example + * // create a circle with radius 20 * const c = new Circle(20); - * + * c.setPosition(25, 25); + * add(c); */ constructor(radius) { super(); @@ -130,6 +130,7 @@ class Circle extends Thing { /** * Checks if the passed point is contained in the circle. * + * @alias Circle#containsPoint * @example * const c = new Circle(20); * c.setPosition(0, 0); diff --git a/src/graphics/color.js b/src/graphics/color.js index e7f70e7a..6027684b 100644 --- a/src/graphics/color.js +++ b/src/graphics/color.js @@ -1,14 +1,12 @@ -/** @module Color */ +/** + * @module Color + * @global + * The Color module is available globally, and provides both constants for colors as well as utilities for working with colors. + */ import * as Randomizer from '../randomizer.js'; -/** - * @class Color - * @param {number} r - Red value. - * @param {number} g - Green value. - * @param {number} b - Blue value. - */ -export default class Color { +class Color { static random = Randomizer.nextColor; static red = '#FF0000'; static RED = '#FF0000'; @@ -38,6 +36,7 @@ export default class Color { /** * Construct a new color. * + * @constructor * @param {number} r * @param {number} g * @param {number} b @@ -249,13 +248,12 @@ export default class Color { /** * Convert RGB to a hex string. * - * @memberof Color * @param {number} r - Red component. * @param {number} g - Green component. * @param {number} b - Blue component. * @returns {string} Hex representation. */ -export const rgbToHex = (r, g, b) => { +export function rgbToHex(r, g, b) { r = Math.floor(r); g = Math.floor(g); b = Math.floor(b); @@ -263,7 +261,7 @@ export const rgbToHex = (r, g, b) => { throw 'Invalid color component'; } return ((r << 16) | (g << 8) | b).toString(16); -}; +} /** * Get an [r, g, b] array from a hex string. @@ -271,14 +269,14 @@ export const rgbToHex = (r, g, b) => { * @param {string} hexString - Hex string (#RRGGBB) * @returns {Array.} An array of [r, g, b] */ -export const hexToRgb = hexString => { +export function hexToRgb(hexString) { hexString = hexString.slice(1); return [ parseInt(hexString.slice(0, 2), 16), parseInt(hexString.slice(2, 4), 16), parseInt(hexString.slice(4, 6), 16), ]; -}; +} /** * Get a hex string (#RRGGBB) from r, g, b components. @@ -288,24 +286,25 @@ export const hexToRgb = hexString => { * @param {number} b * @returns {string} - Hex color (#RRGGBB) */ -export const getColor = (r, g, b) => { +export function getColor(r, g, b) { return '#' + ('000000' + rgbToHex(r, g, b)).slice(-6); -}; +} /** * Converts an HSL (?) representation to RGB. * - * @memberof Color * @param {number} p * @param {number} q * @param {number} t * @returns {number} RGB representation of component. */ -export const hue2rgb = (p, q, t) => { +export function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; -}; +} + +export default Color; diff --git a/src/graphics/graphics-utils.js b/src/graphics/graphics-utils.js index 5516761e..68accba0 100644 --- a/src/graphics/graphics-utils.js +++ b/src/graphics/graphics-utils.js @@ -6,12 +6,15 @@ * @param {number} y2 * @returns {number} Distance between the two points. */ -export const getDistance = function (x1, y1, x2, y2) { +export function getDistance(x1, y1, x2, y2) { return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); -}; +} /** - * + * Maps a value from one range to another. + * @example + * // maps the sine of 1 from the range of sine (-1, 1) to the range (0, getHeight()); + * map(Math.sin(1), -1, 1, 0, getHeight()); * @param {number} value - Value to remap to a range * @param {number} start1 - Lower bound of the current range * @param {number} end1 - Upper bound of the current range @@ -19,6 +22,6 @@ export const getDistance = function (x1, y1, x2, y2) { * @param {number} end2 - Upper bound of the desired range * @returns {number} */ -export const map = (value, start1, end1, start2, end2) => { +export function map(value, start1, end1, start2, end2) { return ((value - start1) / (end1 - start1)) * (end2 - start2) + start2; -}; +} diff --git a/src/graphics/group.js b/src/graphics/group.js index f99a540f..9bbfe7c2 100644 --- a/src/graphics/group.js +++ b/src/graphics/group.js @@ -1,7 +1,5 @@ import Thing, { rotatePointAboutPosition } from './thing.js'; -/** @module Group */ - /** * Represents a collection of graphical elements that can be acted on together. * @class @@ -212,6 +210,7 @@ class Group extends Thing { * Return whether this group contains the point, which is true if any element in this group * contains it. * + * @alias Group#containsPoint * @param {number} x * @param {number} y * @returns {boolean} diff --git a/src/graphics/index.js b/src/graphics/index.js index 2476e108..7b3f11ac 100644 --- a/src/graphics/index.js +++ b/src/graphics/index.js @@ -11,9 +11,18 @@ export const HIDDEN_KEYBOARD_NAVIGATION_DOM_ELEMENT_STYLE = export const HIDDEN_KEYBOARD_NAVIGATION_DOM_ELEMENT_ID = id => `${id}focusbutton`; -/** @type {Object.} */ +/** + * @type {Object.} + * @private + */ export let GraphicsInstances = {}; -/** @type {Array.} */ +/** + * @type {Array.} + * @example + * if (pressedKeys.indexOf(Keyboard.SPACE) > -1) { + * alert('you are pressing space!'); + * } + */ export let pressedKeys = []; let graphicsInstanceID = 0; @@ -150,6 +159,8 @@ class GraphicsManager extends Manager { /** * Get all living elements. + * @global + * @returns {Array.} */ getElements() { return this.elementPool.filter(element => element.alive); @@ -157,6 +168,11 @@ class GraphicsManager extends Manager { /** * Add an element to the graphics instance. + * @example + * let circle = new Circle(20); + * add(circle); + * + * @global * @param {Thing} elem - A subclass of Thing to be added to the graphics instance. */ add(elem) { @@ -166,6 +182,7 @@ class GraphicsManager extends Manager { /** * Creates a hidden DOM element that can be navigated with a screen reader. + * @private * @param {Thing} elem */ createAccessibleDOMElement(elem) { @@ -205,17 +222,30 @@ class GraphicsManager extends Manager { elem._hasAccessibleDOMElement = true; } + /** + * Exits keyboard navigation mode. + * @private + */ exitKeyboardNavigation() { this.userNavigatingWithKeyboard = false; this.hideKeyboardNavigationDOMElements(); } + /** + * Makes DOM elements designed to be navigated with keyboard visible so they can be tabbed. + * @private + */ showKeyboardNavigationDOMElements() { this.accessibleDOMElements.forEach( element => (element.style = KEYBOARD_NAVIGATION_DOM_ELEMENT_STYLE) ); } + /** + * Makes DOM elements designed to be navigated with keyboard invisible. + * This is to make sure they don't accidentally appear and affect layout if they are not needed. + * @private + */ hideKeyboardNavigationDOMElements() { this.accessibleDOMElements.forEach( element => (element.style = HIDDEN_KEYBOARD_NAVIGATION_DOM_ELEMENT_STYLE) @@ -224,6 +254,9 @@ class GraphicsManager extends Manager { /** * Record a click. + * This will cause all timers to be postponed until a click event happens. + * @deprecated + * @global */ waitForClick() { this.clickCount++; @@ -231,6 +264,11 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for click (mouse down, mouse up) events. + * @example + * mouseClickMethod(e => { + * alert('You just clicked at ' + e.getX() + ', ' + e.getY()); + * }); + * @global * @param {function} fn - A callback to be triggered on click events. */ mouseClickMethod(fn) { @@ -239,6 +277,11 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for mouse move events. + * @example + * mouseMoveMethod(e => { + * alert('You moved your mouse to ' + e.getX() + ', ' + e.getY()); + * }); + * @global * @param {function} fn - A callback to be triggered on mouse move events. */ mouseMoveMethod(fn) { @@ -247,6 +290,11 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for mouse down events. + * @example + * mouseDownMethod(e => { + * alert('You depressed your mouse button at ' + e.getX() + ', ' + e.getY()); + * }); + * @global * @param {function} fn - A callback to be triggered on mouse down. */ mouseDownMethod(fn) { @@ -255,6 +303,11 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for mouse up events. + * @example + * mouseUpMethod(e => { + * alert('You lifted your mouse button at ' + e.getX() + ', ' + e.getY()); + * }); + * @global * @param {function} fn - A callback to be triggered on mouse up events. */ mouseUpMethod(fn) { @@ -263,6 +316,11 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for drag events. + * @example + * mouseDragMethod(e => { + * alert('You dragged your mouse to' + e.getX() + ', ' + e.getY()); + * }); + * @global * @param {function} fn - A callback to be triggered on drag events. */ mouseDragMethod(fn) { @@ -271,6 +329,13 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for keydown events. + * @example + * keyDownMethod(e => { + * if (e.keyCode === Keyboard.letter('A')) { + * alert('You just pushed the a key!'); + * } + * }) + * @global * @param {function} fn - A callback to be triggered on keydown events. */ keyDownMethod(fn) { @@ -279,6 +344,13 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for key up events. + * @example + * keyUpMethod(e => { + * if (e.keyCode === Keyboard.letter('A')) { + * alert('You just lifted the a key!'); + * } + * }) + * @global * @param {function} fn - A callback to be triggered on key up events. */ keyUpMethod(fn) { @@ -287,6 +359,7 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for device orientation events. + * @global * @param {function} fn - A callback to be triggered on device orientation * events. */ @@ -296,6 +369,7 @@ class GraphicsManager extends Manager { /** * Assign a function as a callback for device motion events. + * @global * @param {function} fn - A callback to be triggered device motion events. */ deviceMotionMethod(fn) { @@ -304,6 +378,11 @@ class GraphicsManager extends Manager { /** * Check if a key is currently pressed + * @example + * if (isKeyPressed(Keyboard.letter('a'))) { + * alert('Youre currently pressing A!'); + * } + * @global * @param {integer} keyCode - Key code of key being checked. * @returns {boolean} Whether or not that key is being pressed. */ @@ -313,6 +392,11 @@ class GraphicsManager extends Manager { /** * Get the width of the entire graphics canvas. + * @example + * if (getWidth() > 200) { + * alert('The canvas is wider than 200 pixels!'); + * } + * @global * @returns {float} The width of the canvas. */ getWidth() { @@ -322,6 +406,11 @@ class GraphicsManager extends Manager { /** * Get the height of the entire graphics canvas. + * @example + * if (getHeight() > 200) { + * alert('The canvas is taller than 200 pixels!'); + * } + * @global * @returns {float} The height of the canvas. */ getHeight() { @@ -331,6 +420,7 @@ class GraphicsManager extends Manager { /** * Stop all timers. + * @global */ stopAllTimers() { for (let i = 1; i < 99999; i++) { @@ -343,6 +433,7 @@ class GraphicsManager extends Manager { /** * Create a new timer. * {@link Manager#setTimer} + * @global * @param {function} fn - Function to be called at intervals. * @param {integer} time - Time interval to call function `fn` * @param {dictionary} data - Any data associated with the timer. @@ -390,6 +481,7 @@ class GraphicsManager extends Manager { /** * Set the background color of the canvas. + * @global * @param {Color} color - The desired color of the canvas. */ setBackgroundColor(color) { @@ -398,6 +490,7 @@ class GraphicsManager extends Manager { /** * Clear everything from the canvas. + * @private */ clear(context) { var ctx = context || this.getContext(); @@ -407,6 +500,13 @@ class GraphicsManager extends Manager { /** * Get an element at a specific point. * If several elements are present at the position, return the one put there first. + * @example + * let circle = new Circle(20); + * circle.setPosition(100, 100); + * add(circle); + * + * getElementAt(100, 100) === circle; + * @global * @param {number} x - The x coordinate of a point to get element at. * @param {number} y - The y coordinate of a point to get element at. * @returns {Thing|null} The object at the point (x, y), if there is one (else null). @@ -420,6 +520,23 @@ class GraphicsManager extends Manager { return null; } + /** + * Get all elements at a specific point. + * @example + * let circle = new Circle(20); + * circle.setPosition(100, 100); + * add(circle); + * + * let rectangle = new Rectangle(30, 30); + * rectangle.setPosition(80, 80); + * add(rectangle); + * + * getElementsAt(100, 100)[1] === rectangle; + * @global + * @param {number} x - The x coordinate of a point to get element at. + * @param {number} y - The y coordinate of a point to get element at. + * @returns {Array.} The objects at the point (x, y). + */ getElementsAt(x, y) { return this.elementPool.filter(e => { return e.alive && e.containsPoint(x, y); @@ -428,6 +545,7 @@ class GraphicsManager extends Manager { /** * Check if an element exists with the given paramenters. + * @global * @param {object} params - Dictionary of parameters for the object. * Includes x, y, heigh, width, color, radius, label and type. * @returns {boolean} @@ -448,6 +566,12 @@ class GraphicsManager extends Manager { /** * Remove all elements from the canvas. + * @example + * add(new Circle(10)); + * add(new Rectangle(30, 30)); + * removeAll(); + * + * @global */ removeAll() { this.stopAllVideo(); @@ -459,6 +583,7 @@ class GraphicsManager extends Manager { /** * Remove a specific element from the canvas. + * @global * @param {Thing} elem - The element to be removed from the canvas. */ remove(elem) { @@ -483,6 +608,7 @@ class GraphicsManager extends Manager { /** * Resizes the canvas, creating a temporary canvas to prevent flickering and * perform size adjustments based on the devices's devicePixelRatio. + * @private * @param {number} w * @param {number} h */ @@ -513,6 +639,7 @@ class GraphicsManager extends Manager { /** * Set the size of the canvas. + * @global * @param {number} w - Desired width of the canvas. * @param {number} h - Desired height of the canvas. */ @@ -523,6 +650,7 @@ class GraphicsManager extends Manager { /** * Set the canvas to take up the entire parent element + * @global */ setFullscreen() { this.fullscreenMode = true; // when this is true, canvas will resize with parent @@ -534,6 +662,7 @@ class GraphicsManager extends Manager { /** * Resets all the timers to time 0. + * @global */ resetAllTimers() { for (var cur in this.timers) { @@ -541,6 +670,10 @@ class GraphicsManager extends Manager { } } + /** + * Stop all video elements. + * @private + */ stopAllVideo() { for (var i = this.elementPool.length; i--; ) { if (this.elementPool[i] instanceof WebVideo) { @@ -551,6 +684,7 @@ class GraphicsManager extends Manager { /** * Resets the graphics instance to a clean slate. + * @private */ resetAllState() { this.backgroundColor = null; @@ -579,6 +713,7 @@ class GraphicsManager extends Manager { /** * Reset all timers to 0 and clear timers and canvas. + * @private */ fullReset() { this.stopAllVideo(); @@ -589,6 +724,7 @@ class GraphicsManager extends Manager { /** * Return if the graphics canvas exists. + * @private * @returns {boolean} Whether or not the canvas exists. */ canvasExists() { @@ -634,6 +770,7 @@ class GraphicsManager extends Manager { /** * Draw the background color for the current object. + * @private */ drawBackground() { if (this.backgroundColor) { @@ -677,6 +814,7 @@ class GraphicsManager extends Manager { /** * Redraw this graphics canvas. + * @private */ redraw() { this.clear(); @@ -715,12 +853,17 @@ class GraphicsManager extends Manager { /** * Set the main timer for graphics. + * @private */ setMainTimer() { this.shouldUpdate = true; this.update(); } + /** + * The main update loop for the Graphics manager. + * @private + */ update() { if (this.shouldUpdate) { requestAnimationFrame(this.update.bind(this)); @@ -743,6 +886,7 @@ class GraphicsManager extends Manager { /** * Whether the selected canvas already has an instance associated. + * @private */ canvasHasInstance(canvas) { let instance; @@ -757,6 +901,7 @@ class GraphicsManager extends Manager { /** * Set up the graphics instance to prepare for interaction + * @private */ setup() { var drawingCanvas = this.getCanvas(); diff --git a/src/graphics/keyboard.js b/src/graphics/keyboard.js index b307027b..a26c84ee 100644 --- a/src/graphics/keyboard.js +++ b/src/graphics/keyboard.js @@ -1,56 +1,123 @@ /** @module Keyboard */ -// TODO: make this a class for JSdoc purposes -// and consistency within the lib -/** Constants for key values. */ -const Keyboard = { - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - ENTER: 13, - SHIFT: 16, - SPACE: 32, - BACKSPACE: 8, - TAB: 9, - CTRL: 17, - ALT: 18, - CAPS_LOCK: 20, - LEFT_COMMAND: 91, - LEFT_WINDOW: 91, - RIGHT_WINDOW: 92, - RIGHT_COMMAND: 93, - SELECT: 93, -}; +/** + * Constant for the left arrow key + * @type {number} + */ +export const LEFT = 37; +/** + * Constant for the up arrow key + * @type {number} + */ +export const UP = 38; +/** + * Constant for the right arrow key + * @type {number} + */ +export const RIGHT = 39; +/** + * Constant for the down arrow key + * @type {number} + */ +export const DOWN = 40; +/** + * Constant for the enter key + * @type {number} + */ +export const ENTER = 13; +/** + * Constant for the shift key + * @type {number} + */ +export const SHIFT = 16; +/** + * Constant for the space key + * @type {number} + */ +export const SPACE = 32; +/** + * Constant for the backspace key + * @type {number} + */ +export const BACKSPACE = 8; +/** + * Constant for the tab key + * @type {number} + */ +export const TAB = 9; +/** + * Constant for the control key + * @type {number} + */ +export const CTRL = 17; +/** + * Constant for the alt key + * @type {number} + */ +export const ALT = 18; +/** + * Constant for the caps lock key + * @type {number} + */ +export const CAPS_LOCK = 20; +/** + * Constant for the left command key + * @type {number} + */ +export const LEFT_COMMAND = 91; +/** + * Constant for the left window key + * @type {number} + */ +export const LEFT_WINDOW = 91; +/** + * Constant for the right windowkey + * @type {number} + */ +export const RIGHT_WINDOW = 92; +/** + * Constant for the right command key + * @type {number} + */ +export const RIGHT_COMMAND = 93; +/** + * Constant for the select key + * @type {number} + */ +export const SELECT = 93; -/** Modifiers and keys that don't produce or change input. */ -Keyboard.nonEditingKeys = [ - Keyboard.LEFT, - Keyboard.RIGHT, - Keyboard.UP, - Keyboard.DOWN, - Keyboard.CTRL, - Keyboard.SHIFT, - Keyboard.ALT, - Keyboard.CAPS_LOCK, - Keyboard.LEFT_COMMAND, - Keyboard.RIGHT_COMMAND, - Keyboard.SELECT, - Keyboard.LEFT_WINDOW, - Keyboard.RIGHT_WINDOW, +/** + * Modifiers and keys that don't produce or change input. + * @type {Array.} + */ +export const nonEditingKeys = [ + LEFT, + RIGHT, + UP, + DOWN, + CTRL, + SHIFT, + ALT, + CAPS_LOCK, + LEFT_COMMAND, + RIGHT_COMMAND, + SELECT, + LEFT_WINDOW, + RIGHT_WINDOW, ]; /** * Get the keyboard code for a numeric digit. - * @example - * const code3 = Keyboard.digit(3); * @param {number} digit - The number value to be converted to key code. * @return {number} Key code corresponding to digit. + * @example + * const code3 = Keyboard.digit(3); + * */ -Keyboard.digit = function (dig) { +export function digit(dig) { dig = dig % 10; return dig + 48; -}; +} /** * Get the keyboard code for a character. @@ -61,12 +128,12 @@ Keyboard.digit = function (dig) { * @param {string} letter - The letter to be converted to key code. * @return {number} Key code corresponding to letter. */ -Keyboard.letter = function (letter) { +export function letter(letter) { if (letter.length !== 1) { return -1; } return letter.toUpperCase().charCodeAt(0); -}; +} /** * Check if a key is an editing key. @@ -74,8 +141,6 @@ Keyboard.letter = function (letter) { * @param {number} keyCode - Key code corresponding to key pressed. * @return {boolean} Whether or not the key is an editing key. */ -Keyboard.isEditingKey = function (keyCode) { - return Keyboard.nonEditingKeys.indexOf(keyCode) === -1; -}; - -export default Keyboard; +export function isEditingKey(keyCode) { + return nonEditingKeys.indexOf(keyCode) === -1; +} diff --git a/src/graphics/line.js b/src/graphics/line.js index 3dff7efe..f445bf37 100644 --- a/src/graphics/line.js +++ b/src/graphics/line.js @@ -1,16 +1,20 @@ import Thing, { rotatePointAboutPosition } from './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. + * A Line is a line segment from its start point to end point, stored as x1, y1, and x2, y2 respectively. + * @class + * @extends Thing */ -export default class Line extends Thing { +class Line extends Thing { type = 'Line'; + /** + * @constructor + * @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. + */ constructor(x1, y1, x2, y2) { super(); if (arguments.length !== 4) { @@ -129,7 +133,6 @@ export default class Line extends Thing { /** * Sets the color of a line. - * * @param {Color} color - Sets the color of the line. */ setColor(color) { @@ -314,7 +317,7 @@ export default class Line extends Thing { } /** * Gets the new points based on their rotated values. - * + * @private * @param {number} x1 X coordinate of start point * @param {number} y1 Y coordinate of start point * @param {number} x2 X coordinate of end point @@ -322,7 +325,7 @@ export default class Line extends Thing { * @param {number} rotation radians rotated (Expected in radians) * @return {array} List of coordinates of both points. */ -export const getRotatedPoints = function (x1, y1, x2, y2, rotation) { +export function getRotatedPoints(x1, y1, x2, y2, rotation) { var midX = (x1 + x2) / 2; var midY = (y1 + y2) / 2; var sinAngle = Math.sin(rotation); @@ -346,4 +349,6 @@ export const getRotatedPoints = function (x1, y1, x2, y2, rotation) { y2 = newY + midY; return [x1, y1, x2, y2]; -}; +} + +export default Line; diff --git a/src/graphics/oval.js b/src/graphics/oval.js index ffc19099..bf4f4c39 100644 --- a/src/graphics/oval.js +++ b/src/graphics/oval.js @@ -1,12 +1,11 @@ import Thing from './thing.js'; -/** @module Oval */ - /** - * @class Oval + * An Oval is an ellipse, with its horizontal width defined by its .width property and its height defined by its .height property. + * @class * @extends Thing */ -export default class Oval extends Thing { +class Oval extends Thing { type = 'Oval'; anchor = { vertical: 0.5, horizontal: 0.5 }; @@ -113,6 +112,7 @@ export default class Oval extends Thing { * Checks if the passed point is contained in the oval. * Uses the equation for an oval. * + * @alias Oval#containsPoint * @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 circle. @@ -130,3 +130,5 @@ export default class Oval extends Thing { return result <= 1; } } + +export default Oval; diff --git a/src/graphics/polygon.js b/src/graphics/polygon.js index ce871243..d6afc2da 100644 --- a/src/graphics/polygon.js +++ b/src/graphics/polygon.js @@ -1,7 +1,5 @@ import Thing from './thing.js'; -/** @module Polygon */ - /** * A polygon is a shape with any number of points, and will * be drawn as a continuous shape contained by those points. @@ -65,6 +63,7 @@ class Polygon extends Thing { /** * Checks if the coordinates are contained in the Polygon. + * @alias Polygon#containsPoint * @example * const p = new Polygon(); * p.addPoint(10, 10); diff --git a/src/graphics/rectangle.js b/src/graphics/rectangle.js index c8e5dcdd..6dc37f50 100644 --- a/src/graphics/rectangle.js +++ b/src/graphics/rectangle.js @@ -1,7 +1,8 @@ import Thing from './thing.js'; /** - * @class Rectangle + * A Rectangle is defined by its width and height. + * @class * @extends Thing */ class Rectangle extends Thing { @@ -111,6 +112,7 @@ class Rectangle extends Thing { /** * Checks if the passed point is contained in the rectangle. * + * @alias Rectangle#containsPoint * @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 rectangle. diff --git a/src/graphics/text.js b/src/graphics/text.js index 1a1be49d..42ad1d45 100644 --- a/src/graphics/text.js +++ b/src/graphics/text.js @@ -1,7 +1,8 @@ import Thing from './thing.js'; /** - * @class Text + * Text is used to display words on the canvas. + * @class * @extends Thing */ class Text extends Thing { @@ -185,6 +186,7 @@ class Text extends Thing { /** * Checks if the passed point is contained in the text. * + * @alias Text#containsPoint * @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 text. diff --git a/src/graphics/thing.js b/src/graphics/thing.js index 2002e934..3acf4ef5 100644 --- a/src/graphics/thing.js +++ b/src/graphics/thing.js @@ -114,6 +114,8 @@ class Thing { /** * Gets the x position of the Thing. + * @example + * thing.x === thing.getX(); * * @return {number} The x position of the Thing. */ @@ -124,6 +126,9 @@ class Thing { /** * Gets the y position of the Thing. * + * @example + * thing.y === thing.getY(); + * * @return {number} The y position of the Thing. */ getY() { @@ -168,6 +173,11 @@ class Thing { * Sets a Thing object to filled. * Throws an error if an argument is not passed. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setFilled(false); + * * @param {bool} filled - A boolean of whether or not Thing is filled. */ setFilled(filled) { @@ -184,6 +194,11 @@ class Thing { /** * Returns if a Thing is filled. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.isFilled(); + * * @return {boolean} True if the Thing is filled. */ isFilled() { @@ -194,6 +209,11 @@ class Thing { * Sets a Thing object to filled. * Throws an error if an argument is not passed. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setBorder(true); + * * @param {bool} hasBorder - A boolean of whether or not Thing has a border. */ setBorder(hasBorder) { @@ -211,6 +231,11 @@ class Thing { /** * Returns if a Thing has a border. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.hasBorder(); + * * @return {boolean} True if the Thing has a border. */ hasBorder() { @@ -220,6 +245,11 @@ class Thing { /** * Set the opacity of the Thing. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setOpacity(0.5); + * * @param {number} opacity */ setOpacity(opacity) { @@ -231,6 +261,11 @@ class Thing { * Throws an error if there are fewer than 2 params or if * they are not numbers. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setPosition(30, 30); + * * @param {number} x - The destination x coordinate of this Thing. * @param {number} y - The destination y coordinate of this Thing. */ @@ -257,6 +292,12 @@ class Thing { * Throws an error if there are fewer than 1 params or if they * are not numbers. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setRotation(90); + * thing.setRotation(Math.PI / 2, Thing.RADIANS); + * * @param {number} degrees - The degrees to rotate degrees. * @param {number} angleUnit - Whether it is degrees or radians. Defaults to * degrees. @@ -290,6 +331,12 @@ class Thing { /** * Rotates a Thing an additional amount of degrees. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.rotate(90); + * thing.rotate(Math.PI / 2, Thing.RADIANS); + * * @param {number} degrees - The degrees to rotate degrees. * @param {number} angleUnit - Whether it is degrees or radians. Defaults to * degrees. @@ -324,6 +371,13 @@ class Thing { * Throws an error if there are fewer than 1 params or if * the param is undefined. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setColor('red'); + * thing.setColor(Color.orange); + * thing.setColor('#ff0000'); + * * @param {Color} color - The resulting color of Thing. */ setColor(color) { @@ -341,6 +395,11 @@ class Thing { /** * Gets the color of a Thing. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.getColor(); // #000000, by default + * * @return {Color} The destination y coordinate of this Thing. */ getColor() { @@ -351,6 +410,14 @@ class Thing { * Sets the border color of a Thing. * Throws an error if there are fewer than 1 params or if * the param is undefined. + * This will automatically give the Thing a border, as if you had called + * thing.setBorder(true); + * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setBorderColor('orange'); + * * * @param {Color} color - The resulting color of the Thing's border. */ @@ -368,6 +435,11 @@ class Thing { /** * Gets the border color of a Thing. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.getBorderColor(); + * * @return {Color} The color of the Thing's border. */ getBorderColor() { @@ -377,6 +449,13 @@ class Thing { /** * Sets the width of a Thing's border. * Throws an error if there is not 1 argument. + * This will automatically set the Thing to draw with a border, as if you had called + * thing.setBorder(true); + * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.setBorderWidth(5); * * @param {number} width - The resulting width of the Thing's border. */ @@ -396,6 +475,11 @@ class Thing { /** * Gets the width of the Thing's border. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.getBorderWidth(); + * * @return {number} The width of the Thing's border. */ getBorderWidth() { @@ -405,6 +489,11 @@ class Thing { /** * Changes the possition of a thing by a specified x and y amount. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * thing.move(10, 10); + * * @param {number} dx - The resulting change in the Thing's x position. * @param {number} dy - The resulting change in the Thing's y position. */ @@ -496,6 +585,8 @@ class Thing { /** * Focuses the element for use with screen readers. + * This isn't something you should need to call manually, but you can if you'd + * like to provide focus to an element even if it wasn't navigated to with the keyboard. */ focus() { this.focused = true; @@ -503,6 +594,8 @@ class Thing { /** * Unfocuses the element for use with screen readers. + * This isn't something you should need to call manually, but you can if you'd + * like to unfocus to an element even if it wasn't navigated from with the keyboard. */ unfocus() { this.focused = false; @@ -510,6 +603,8 @@ class Thing { /** * Describes the element for use with screen readers. + * This isn't something you should need to call manually, but you can if you'd like + * to print a text descriptino of the Thing. */ describe() { return `A ${this.type} at ${this.x}, ${this.y}. Colored: ${this.color}.`; @@ -519,6 +614,13 @@ class Thing { * Check if a given point is within the Thing. * This function only works in subclasses of Thing. * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * if (thing.containsPoint(100, 100)) { + * alert('contains 100, 100!'); + * } + * * @param {number} x - The x coordinate of the point being checked. * @param {number} y - The y coordinate of the point being checked. * @return {boolean} Whether the point x, y is within the Thing. @@ -536,6 +638,16 @@ class Thing { /** * Sets the Anchor for the object. + * This alters how the shape will draw relative to its position. + * An anchor of 0, 0 will cause the shape to draw with its position at its top left corner. + * An anchor of 1, 1 will cause the shape to draw with its position at its bottom right corner. + * + * @example + * // this method is on every Shape + * let thing = new Thing(); + * // center the object around its position + * thing.setPosition({vertical: 0.5, horizontal: 0.5}); + * * @param {{vertical: number, horizontal: number}} anchor */ setAnchor(anchor) { @@ -544,7 +656,7 @@ class Thing { } /** - * Gets the jelement's anchor. + * Gets the element's anchor. * @returns {{vertical: number, horizontal: number}} */ getAnchor() { @@ -553,6 +665,13 @@ class Thing { /** * Get the elements bounds. + * This is an internal property that you shouldn't need to use, but it can be useful + * for doing quick calculations for the bounding box of a shape. + * @example + * // this method is on every Shape + * let thing = new Thing(); + * let height = thing.getBounds().bottom - this.getBounds().top; + * * @returns {{top: number, bottom: number, left: number, right: number}} */ getBounds() { @@ -564,6 +683,7 @@ class Thing { /** * Mark this element's bounds as invalidated. + * @private */ _invalidateBounds() { this._boundsInvalidated = true; @@ -581,25 +701,6 @@ class Thing { let right = Math.ceil(this.x + (1 - this.anchor.horizontal) * this.width); let top = Math.ceil(this.y - this.anchor.vertical * this.height); let bottom = Math.ceil(this.y + (1 - this.anchor.vertical) * this.height); - //if (this.rotation) { - // const rotX = (right - left) / 2 + left; - // const rotY = (bottom - top) / 2 + top; - // let topLeft = rotatePointAboutPosition([left, top], [rotX, rotY], this.rotation); - // let topRight = rotatePointAboutPosition([right, top], [rotX, rotY], this.rotation); - // let bottomLeft = rotatePointAboutPosition([left, bottom], [rotX, rotY], this.rotation); - // let bottomRight = rotatePointAboutPosition( - // [right, bottom], - // [rotX, rotY], - // this.rotation - // ); - // const points = [topLeft, topRight, bottomLeft, bottomRight]; - // const xCoordinates = points.map(point => point[0]); - // const yCoordinates = points.map(point => point[1]); - // left = Math.min(...xCoordinates); - // right = Math.max(...xCoordinates); - // top = Math.min(...yCoordinates); - // bottom = Math.max(...yCoordinates); - //} this.bounds = { left, right, @@ -612,17 +713,23 @@ class Thing { } /** + * Rotate a point defined by an [x, y] pair around another point defined by an [x, y] pair by + * an angle in radians. + * @example + * let center = [100, 100]; + * let point = [20, 30]; + * let rotated = rotatePointAboutPosition(center, point, Math.PI / 2); * * @param {number[]} point - [x, y] of the point to rotate * @param {number[]} origin - [x, y] point of rotation * @param {number} angle - angle in radians * @returns {number[]} - [x, y] rotated point */ -export const rotatePointAboutPosition = ([x, y], [rotX, rotY], angle) => { +export function rotatePointAboutPosition([x, y], [rotX, rotY], angle) { return [ (x - rotX) * Math.cos(angle) - (y - rotY) * Math.sin(angle) + rotX, (x - rotX) * Math.sin(angle) + (y - rotY) * Math.cos(angle) + rotY, ]; -}; +} export default Thing; diff --git a/src/graphics/webimage.js b/src/graphics/webimage.js index 2b4918a1..4270b2b5 100644 --- a/src/graphics/webimage.js +++ b/src/graphics/webimage.js @@ -10,7 +10,8 @@ const BLUE = 2; const ALPHA = 3; /** - * @class WebImage + * A WebImage is used to display an image from a URL or underlying image data. + * @class * @extends Thing */ class WebImage extends Thing { @@ -153,6 +154,7 @@ class WebImage extends Thing { /** * Checks if the passed point is contained in the WebImage. * + * @alias WebImage#containsPoint * @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 WebImage. diff --git a/src/graphics/webvideo.js b/src/graphics/webvideo.js index 1b3abe27..2b6c1d62 100644 --- a/src/graphics/webvideo.js +++ b/src/graphics/webvideo.js @@ -83,6 +83,7 @@ class WebVideo extends Thing { /** * Checks if the passed point is contained in the WebVideo. * + * @alias WebVideo#containsPoint * @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 WebVideo. diff --git a/src/manager.js b/src/manager.js index d9b1216d..618fadc9 100644 --- a/src/manager.js +++ b/src/manager.js @@ -38,7 +38,9 @@ class Manager { * fourth parameter to the `setTimer` function: * @example * const timerID = Randomizer.nextInt(1000); - * setTimer(fn, 50, {}, timerID); + * setTimer(() => { + * console.log('50 milliseconds has elapsed'); + * }, 50, {}, timerID); * //... * stopTimer(timerID); * diff --git a/src/randomizer.js b/src/randomizer.js index 452ddfd6..074a4e4e 100644 --- a/src/randomizer.js +++ b/src/randomizer.js @@ -12,7 +12,7 @@ import Vector from './datastructures/vector.js'; * @param {number} max - Upper bound on range of random int. * @returns {number} Random number between low and high, inclusive. */ -export const nextInt = function (min, max) { +export function nextInt(min, max) { if (max === undefined) { max = min - 1; min = 0; @@ -21,7 +21,7 @@ export const nextInt = function (min, max) { min = Math.floor(min); var r = Math.random(); return min + Math.floor(r * (max - min + 1)); -}; +} /** * Get a random float between low to high, inclusive. @@ -31,36 +31,36 @@ export const nextInt = function (min, max) { * @param {number} max - Upper bound on range of random int. * @returns {number} Random number between low and high, inclusive. */ -export const nextFloat = function (min, max) { +export function nextFloat(min, max) { if (max === undefined) { max = min; min = 0; } return min + (max - min) * Math.random(); -}; +} /** * Generates a random number in range (0,255) in hexadecimal. * @returns {string} Random number in hexadecimal form. */ -export const nextHex = function () { +export function nextHex() { var val = nextInt(0, 255); if (val < 16) { return '0' + val.toString(16); } return val.toString(16); -}; +} /** * Generate a random hexadecimal color code of the format #RRGGBB. * @returns {string} Hexadecimal representation of random color. */ -export const nextColor = function () { +export function nextColor() { var r = nextHex(); var g = nextHex(); var b = nextHex(); return '#' + r + g + b; -}; +} /** * Generate a random boolean via fair probability coin toss. @@ -68,13 +68,13 @@ export const nextColor = function () { * @param {number} probabilityTrue - Skewed probability of true. * @returns {boolean} Result of coin flip skewed toward `probabilityTrue`. */ -export const nextBoolean = probabilityTrue => { +export function nextBoolean(probabilityTrue) { if (probabilityTrue === undefined) { probabilityTrue = 0.5; } return Math.random() < probabilityTrue; -}; +} // stores numbers 0-1 let perlin; @@ -98,7 +98,7 @@ const fade = t => { * @param {number} [y] - Any number. If y is present, 2d will be used. * @returns {number} */ -export const noise = (x, y) => { +export function noise(x, y) { if (!perlin) { perlin = new Array(PERLIN_SIZE + 1); for (let i = 0; i < PERLIN_SIZE + 1; i++) { @@ -176,4 +176,4 @@ export const noise = (x, y) => { const xMax = (xMin + 1) % PERLIN_SIZE; return lerp(perlin[xMin], perlin[xMax], fade(t)); -}; +} diff --git a/src/sound/audio.js b/src/sound/audio.js index b8aba058..fef0c92f 100644 --- a/src/sound/audio.js +++ b/src/sound/audio.js @@ -1,9 +1,21 @@ const NativeAudio = Audio; -export default class CrossOriginAudio { +/** + * A wrapper around the native Audio class that performs requests with `crossOrigin = 'anonymous'`. + * @alias Audio + */ +class CrossOriginAudio { + /** + * Construct a new Audio object. + * @param {string} url Link to sound file + * @returns Audio object + * @constructor + */ constructor(url) { const audioElement = new NativeAudio(url); audioElement.crossOrigin = 'anonymous'; return audioElement; } } + +export default CrossOriginAudio; diff --git a/src/sound/audioContext.js b/src/sound/audioContext.js index 4c3cf521..f4df2e88 100644 --- a/src/sound/audioContext.js +++ b/src/sound/audioContext.js @@ -5,6 +5,7 @@ /** * Gets an audiocontext for the browser if possible. + * @private * @returns {AudioContext} */ export const getAudioContext = () => { diff --git a/src/sound/index.js b/src/sound/index.js index 78fbf5b5..e02ab3e4 100644 --- a/src/sound/index.js +++ b/src/sound/index.js @@ -1,7 +1,10 @@ import Manager from '../manager'; import { getAudioContext } from './audioContext'; -export default class AudioManager extends Manager { +/** + * @class + */ +class AudioManager extends Manager { /** * @constructor * @param {{onError: function}} options @@ -52,3 +55,5 @@ export default class AudioManager extends Manager { }); } } + +export default AudioManager; diff --git a/src/sound/sound.js b/src/sound/sound.js index 40927498..d7e4b528 100644 --- a/src/sound/sound.js +++ b/src/sound/sound.js @@ -11,7 +11,10 @@ import { } from 'tone'; import { getAudioContext } from './audioContext'; -export default class Sound { +/** + * @class + */ +class Sound { type = 'Sound'; /** @@ -29,6 +32,7 @@ export default class Sound { * cymbal instrument: "metal" * https://tonejs.github.io/docs/13.8.25/OmniOscillator * @param {AudioContext} - context + * @constructor */ constructor(frequency, oscillatorType) { setContext(getAudioContext()); @@ -220,3 +224,5 @@ export default class Sound { } } } + +export default Sound; From 2ceb8608af31a88cd6cef4464a6013805539baec Mon Sep 17 00:00:00 2001 From: Andy Bayer Date: Sat, 19 Mar 2022 21:08:41 -0700 Subject: [PATCH 2/3] If the element pool needs to be resorted during a redraw, perform the redraw immediately so the next frame has the most up-to-date changes. (#133) --- src/graphics/index.js | 69 ++++++++++++++++++++++++++++--------------- test/graphics.test.js | 26 ++++++++++++++++ test/thing.test.js | 17 +++++++++++ 3 files changed, 88 insertions(+), 24 deletions(-) diff --git a/src/graphics/index.js b/src/graphics/index.js index 7b3f11ac..40a2ca69 100644 --- a/src/graphics/index.js +++ b/src/graphics/index.js @@ -44,6 +44,14 @@ class GraphicsManager extends Manager { * @type {number} */ devicePixelRatio = Math.ceil(window.devicePixelRatio) ?? 1; + /** + * Used to record when a resort is necessary as a result of adding an element with + * an invalidated sort. Sorting will be performed on next redraw when _sortInvalidated + * is true. + * @type {boolean} + * @private + */ + _sortInvalidated = false; /** * Set up an instance of the graphics library. @@ -178,6 +186,9 @@ class GraphicsManager extends Manager { add(elem) { elem.alive = true; this.elementPool[this.elementPoolSize++] = elem; + if (elem._sortInvalidated) { + this._sortInvalidated = true; + } } /** @@ -812,6 +823,24 @@ class GraphicsManager extends Manager { ]; } + /** + * Sort the element pool, putting all elements with .alive=false at the end and + * all elements with lower layer before elements with higher layer. + * @private + */ + sortElementPool() { + this.elementPool.sort((a, b) => b.alive - a.alive || a.layer - b.layer); + let lastAliveElementIndex = -1; + for (let i = this.elementPool.length - 1; i >= 0; i--) { + if (this.elementPool[i].alive) { + lastAliveElementIndex = i; + break; + } + } + this.elementPoolSize = lastAliveElementIndex + 1; + this._sortInvalidated = false; + } + /** * Redraw this graphics canvas. * @private @@ -820,34 +849,26 @@ class GraphicsManager extends Manager { this.clear(); this.drawBackground(); let elem; - let sortPool; - const context = this.getContext(); + let sortPool = this._sortInvalidated; for (let i = 0; i < this.elementPoolSize; i++) { elem = this.elementPool[i]; - - if (elem._sortInvalidated) { - sortPool = true; - elem._sortInvalidated = false; - } - if (elem.alive) { - elem.draw(context); - } else { - sortPool = true; - } + // the pool needs to be resorted if: + // - the graphics manager has an invalid sort (as a result of adding a new element), + // - if an element has an invalid sort (as a result of having its layer changed), + // - or if an element has been removed, which will be true if .alive is false and it + // is within the elementPool < elementPoolSize + sortPool = sortPool || elem._sortInvalidated || !elem.alive; + // mark the element as having valid sort, even though it has not yet been sorted. + // it will be sorted immediately after in sortElementPool + elem._sortInvalidated = false; } - // sort all dead elements to the end of the pool - // and all elements with lower layer before elements - // with higher layer if (sortPool) { - this.elementPool.sort((a, b) => b.alive - a.alive || a.layer - b.layer); - let lastAliveElementIndex = -1; - for (let i = this.elementPool.length - 1; i >= 0; i--) { - if (this.elementPool[i].alive) { - lastAliveElementIndex = i; - break; - } - } - this.elementPoolSize = lastAliveElementIndex + 1; + this.sortElementPool(); + } + const context = this.getContext(); + for (let i = 0; i < this.elementPoolSize; i++) { + elem = this.elementPool[i]; + elem.draw(context); } } diff --git a/test/graphics.test.js b/test/graphics.test.js index 76be801d..1a8189eb 100644 --- a/test/graphics.test.js +++ b/test/graphics.test.js @@ -238,6 +238,18 @@ describe('Graphics', () => { g.add(c); expect(g.elementPool[0]).toBe(c); }); + it('Forces a re-sort on next draw', () => { + const g = new Graphics({ shouldUpdate: false }); + const rTop = new Rectangle(5, 5); + const rBottom = new Rectangle(5, 5); + rTop.layer = 2; + rTop.setColor('red'); + rBottom.setColor('blue'); + g.add(rTop); + g.add(rBottom); + g.redraw(); + expect(g.getPixel(0, 0)).toEqual([255, 0, 0, 255]); + }); }); describe('Removing', () => { it("Doesn't remove the element from the internal element pool, only marks it as not alive", () => { @@ -323,6 +335,20 @@ describe('Graphics', () => { g.remove(Circle); }).not.toThrow(); }); + it('Removing an element causes it to be gone in the very next draw', () => { + const g = new Graphics({ shouldUpdate: false }); + const rRed = new Rectangle(5, 5); + const rBlue = new Rectangle(5, 5); + rRed.setColor('red'); + rBlue.setColor('blue'); + g.add(rRed); + g.add(rBlue); + g.redraw(); + expect(g.getPixel(0, 0)).toEqual([0, 0, 255, 255]); + g.remove(rBlue); + g.redraw(); + expect(g.getPixel(0, 0)).toEqual([255, 0, 0, 255]); + }); }); describe('fullReset', () => { it('Clears all elements', () => { diff --git a/test/thing.test.js b/test/thing.test.js index 04947dd3..64947ef1 100644 --- a/test/thing.test.js +++ b/test/thing.test.js @@ -1,4 +1,5 @@ import Graphics from '../src/graphics/index.js'; +import Rectangle from '../src/graphics/rectangle.js'; import Thing from '../src/graphics/thing.js'; describe('Thing', () => { @@ -20,6 +21,22 @@ describe('Thing', () => { expect(g.elementPool.indexOf(t1)).toBe(1); expect(g.elementPool.indexOf(t2)).toBe(0); }); + it('Forces a re-sort before the next draw', () => { + const g = new Graphics({ shouldUpdate: false }); + const rRed = new Rectangle(5, 5); + const rBlue = new Rectangle(5, 5); + rRed.setColor('red'); + rBlue.setColor('blue'); + g.add(rRed); + g.add(rBlue); + g.redraw(); + // before changing layer, the blue square is on top + expect(g.getPixel(0, 0)).toEqual([0, 0, 255, 255]); + rRed.layer = 2; + g.redraw(); + // after changing layer, on very next draw the red square is on top + expect(g.getPixel(0, 0)).toEqual([255, 0, 0, 255]); + }); it('Layering works through removal', () => { const g = new Graphics(); const t1 = new Thing(); From 45ed320f55338db851f46bf94c114fb441a7e70b Mon Sep 17 00:00:00 2001 From: anderoonies Date: Sat, 19 Mar 2022 21:09:16 -0700 Subject: [PATCH 3/3] 0.2.22 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0c6fac4..e712af8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "chs-js-lib", - "version": "0.2.21", + "version": "0.2.22", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.2.21", + "version": "0.2.22", "license": "ISC", "dependencies": { "tone": "^14.7.77", diff --git a/package.json b/package.json index 77ca3a05..2fb31246 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chs-js-lib", - "version": "0.2.21", + "version": "0.2.22", "description": "JavaScript graphics library used in CodeHS's platform.", "main": "dist/chs.cjs", "module": "dist/chs.mjs",