flbulgarelli/headbreaker

View on GitHub
src/connector.js

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * @private
 * @module Connector
 */
const {pivot} = require('./prelude');

/**
 * @typedef {(one: import('./piece'), other: import('./piece')) => boolean} ConnectionRequirement
 */

/**
 * @type {ConnectionRequirement}
 */
function noConnectionRequirements(_one, _other) {
  return true;
}

/**
 * @private
 */
class Connector {

  /**
   * @param {"x" | "y"} axis
   * @param {"right" | "down"} forward
   * @param {"left" | "up"} backward
   */
  constructor(axis, forward, backward) {

    this.axis = axis;

    this.forward = forward;
    this.backward = backward;

    this.forwardAnchor = `${forward}Anchor`;
    this.backwardAnchor = `${backward}Anchor`;

    this.forwardConnection = `${forward}Connection`;
    this.backwardConnection = `${backward}Connection`;

    /** @type {ConnectionRequirement} */
    this.requirement = noConnectionRequirements;
  }

  /**
   * @param {boolean} [back]
   */
  attract(one, other, back = false) {
    const [iron, magnet] = pivot(one, other, back);
    let dx, dy;
    if (magnet.centralAnchor[this.axis] > iron.centralAnchor[this.axis]) {
      [dx, dy] = magnet[this.backwardAnchor].diff(iron[this.forwardAnchor])
    } else {
      [dx, dy] = magnet[this.forwardAnchor].diff(iron[this.backwardAnchor])
    }
    iron.push(dx, dy);
  }


   /**
   * @param {number} delta
   * @returns {boolean}
   */
  openMovement(one, delta) {
    return (delta > 0 && !one[this.forwardConnection]) || (delta < 0 && !one[this.backwardConnection]) || delta == 0;
  }

  /**
   * @param {number} proximity
   * @returns {boolean}
   */
  canConnectWith(one, other, proximity) {
    return this.closeTo(one, other, proximity) && this.match(one, other) && this.requirement(one, other);
  }

  /**
   *
   * @param {number} proximity
   * @returns {boolean}
   */
  closeTo(one, other, proximity) {
    return one[this.forwardAnchor].closeTo(other[this.backwardAnchor], proximity);
  }

  /**
   * @returns {boolean}
   */
  match(one, other) {
    return one[this.forward].match(other[this.backward]);
  }

  /**
   * @param {import('./piece')} one
   * @param {*} other
   * @param {number} proximity
   * @param {boolean} back
   */
  connectWith(one, other, proximity, back) {
    if (!this.canConnectWith(one, other, proximity)) {
      throw new Error(`can not connect ${this.forward}!`);
    }
    if (one[this.forwardConnection] !== other) {
      this.attract(other, one, back);
      one[this.forwardConnection] = other;
      other[this.backwardConnection] = one;
      one.fireConnect(other);
    }
  }

  /**
   * @param {ConnectionRequirement} requirement
   */
  attachRequirement(requirement) {
    this.requirement = requirement;
  }

  /**
   * Returns a new right-to-left connector
   *
   * @returns {Connector}
   */
  static horizontal() {
    return new Connector("x", "right", "left")
  }

   /**
   * Returns a new down-to-up connector
   *
   * @returns {Connector}
   */
  static vertical() {
    return new Connector("y", "down", "up")
  }
}

module.exports = {
  Connector,
  noConnectionRequirements
};