predefined/items.js

'use strict';

const { CanvasSequence } = require('canvas-sequencer');
const { Circle, Oval, Polygon2D, Rectangle, RoundedLine } = require('../shared.js');

/**
 * Factories for predefined items.
 *
 * @namespace items
 * @memberof module:predefined
 */

/**
 * Provides an image description, complete with a hitbox only if both 'width'
 * and 'height' properties are present. Note that the hitbox will be
 * rectangular, with the top left corner at the 'x' and 'y' coordinates.
 *
 * @memberof module:predefined.items
 *
 * @param {string} src - Route path to the image.
 * @param {Object} properties - Location and orientation options for the image
 * item. See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for an image item using the
 * given source.
 */
function image(src, properties = {}) {
  let hitbox = null;
  if ('width' in properties && 'height' in properties) {
    hitbox = new Rectangle(properties.width, properties.height);
  }
  const type = 'item/image';
  return { src, hitbox, type, ...properties };
}

/**
 * Generate a line segment item.
 *
 * @memberof module:predefined.items
 *
 * @param {number} dx - length of the x portion of the line
 * @param {number} dy - length of the y portion of the line
 * @param {number} [width=1] - The width of the line.
 * @param {string} [colour='black'] - The colour of the line.
 * @param {Object} properties - Location and orientation options for the item.
 * See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for a line item with the
 * given endpoints, width, and colour.
 */
function line(dx, dy, width = 1, colour = 'black', properties = {}) {
  const hitbox = new RoundedLine(0, 0, dx, dy, width);
  const sequence = new CanvasSequence();
  sequence.strokeStyle = colour;
  sequence.lineWidth = width;
  sequence.lineCap = 'round';
  sequence.beginPath();
  sequence.moveTo(0, 0);
  sequence.lineTo(dx, dy);
  sequence.stroke();
  return { hitbox, sequence, type: 'item', ...properties };
}

/**
 * Generate a rectangular block item centered on the x, y coordinates given in `properties`.
 *
 * @memberof module:predefined.items
 *
 * @param {number} width
 * @param {number} height
 * @param {string} [colour='blue'] - Fill colour for the rectangle.
 * @param {Object} properties - Location and orientation options for the item.
 * See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for a rectangular item with
 * the given width and height, filled in with the given colour.
 */
function rectangle(width, height, colour = 'blue', properties = {}) {
  const x = 0 - width / 2; // Relative -- WAMS handles positioning
  const y = 0 - height / 2; // Relative -- WAMS handles positioning
  const hitbox = new Rectangle(width, height, x, y);
  const sequence = new CanvasSequence();
  sequence.fillStyle = colour;
  sequence.strokeStyle = 'black';
  sequence.beginPath();
  sequence.rect(x, y, width, height);
  sequence.fill();
  sequence.stroke();
  return { hitbox, sequence, type: 'item', ...properties };
}

/**
 * Generate a square block item centered on the x, y coordinates given in `properties`.
 *
 * @memberof module:predefined.items
 *
 * @param {number} length
 * @param {string} [colour='red'] - Fill colour for the square.
 * @param {Object} properties - Location and orientation options for the item.
 * See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for a square item with the
 * given side length, filled in with the given colour.
 */
function square(length, colour = 'red', properties = {}) {
  return rectangle(length, length, colour, properties);
}

/**
 * Generate a circle item.
 *
 * @memberof module:predefined.items
 *
 * @param {number} radius
 * @param {string} [colour='yellow'] - Fill colour for the circle.
 * @param {Object} properties - Location and orientation options for the item.
 * See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for a circle item with the
 * given radius, filled in with the given colour.
 */
function circle(radius, colour = 'yellow', properties = {}) {
  const hitbox = new Circle(radius, 0, 0);
  const sequence = new CanvasSequence();
  sequence.fillStyle = colour;
  sequence.strokeStyle = 'black';
  sequence.beginPath();
  sequence.arc(
    0, // x  -- WAMS handles positioning
    0, // y  -- WAMS handles positioning
    radius,
    0, // startAngle
    2 * Math.PI // endAngle  -- WAMS handles rotation
  );
  sequence.fill();
  sequence.stroke();
  return { hitbox, sequence, type: 'item', ...properties };
}

/**
 * Generate an oval item.
 *
 * @memberof module:predefined.items
 *
 * @param {number} radiusX
 * @param {number} radiusY
 * @param {string} [colour='yellow'] - Fill colour for the oval.
 * @param {Object} properties - Location and orientation options for the item.
 * See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for an oval item.
 */
function oval(radiusX, radiusY, colour = 'yellow', properties = {}) {
  const hitbox = new Oval(radiusX, radiusY);
  const sequence = new CanvasSequence();
  sequence.fillStyle = colour;
  sequence.strokeStyle = 'black';
  sequence.beginPath();
  sequence.ellipse(
    0, // x  -- WAMS handles positioning
    0, // y  -- WAMS handles positioning
    radiusX,
    radiusY,
    0, // rotation -- WAMS handles rotation
    0, // startAngle
    2 * Math.PI // endAngle
  );
  sequence.fill();
  sequence.stroke();
  return { hitbox, sequence, type: 'item', ...properties };
}

/**
 * Generate a polygonal item.
 *
 * @memberof module:predefined.items
 *
 * @param {module:shared.Point2D[]} points - Not necessarily actual Point2D
 * objects, can just be objects with x and y properties.
 * @param {string} [colour='green'] - Fill colour for the polygon.
 * @param {Object} properties - Location and orientation options for the item.
 * See {@link module:shared.Item} members for available parameters.
 *
 * @returns {Object} An object with the parameters for a generic polygon, filled
 * in with the given colour.
 */
function polygon(points = [], colour = 'green', properties = {}) {
  if (points.length < 3) throw Error('Polygon must consist of at least 3 points');
  const hitbox = new Polygon2D(points);
  const sequence = new CanvasSequence();
  sequence.fillStyle = colour;
  sequence.strokeStyle = 'black';
  sequence.beginPath();
  sequence.moveTo(points[0].x, points[0].y);
  points.forEach((p) => sequence.lineTo(p.x, p.y));
  sequence.closePath();
  sequence.fill();
  sequence.stroke();
  return { hitbox, sequence, type: 'item', ...properties };
}

/**
 * Generate a rectangular element.
 *
 * @memberof module:predefined.items
 *
 * @param {number} x
 * @param {number} y
 * @param {number} width
 * @param {number} height
 * @param {Object} properties - Location and orientation options for the item,
 * plus any appropriate attributes.
 * See {@link module:shared.WamsElement} members for available parameters.
 *
 * @returns {Object} An object with the parameters for a rectangular item with
 * the given width and height, filled in with the given colour.
 */
function element(x, y, width, height, properties = {}) {
  const hitbox = new Rectangle(width, height, x, y);
  return { hitbox, type: 'item/element', ...properties };
}

/**
 * Generate an item that wraps the given html.
 *
 * @memberof module:predefined.items
 *
 * @param {number} width
 * @param {number} height
 * @param {Object} properties - Location and orientation options for the item,
 * plus any appropriate attributes.
 * See {@link module:shared.WamsElement} members for available parameters.
 *
 * @returns {Object} An object with the parameters for an iframe with the given
 * HTML content.
 */
function html(html, width, height, properties = {}) {
  const baseattrs = properties.attributes || {};
  delete properties.attributes;
  return {
    hitbox: new Rectangle(width, height),
    attributes: {
      ...baseattrs,
      innerHTML: html,
    },
    tagname: 'div',
    type: 'item/element',
    ...properties,
  };
}

module.exports = {
  circle,
  element,
  image,
  line,
  oval,
  polygon,
  rectangle,
  square,
  html,
};