All files / src coordinates.ts

100% Statements 60/60
100% Branches 67/67
100% Functions 14/14
100% Lines 59/59

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 2075x         5x                             434x 434x                 430x 430x 4x                         1649x             6x   1643x 1643x 1629x   14x                     52x 12x   40x 2x   74x 38x 2x   36x 8x   28x                       16x 7x   9x 1x   8x 8x 8x 4x   4x                     36x 36x 12x   24x 24x                   390x 7x   383x 383x 383x 4x   379x 379x               581x                   432x 432x 4x   428x 428x               571x                   435x 435x 8x   427x 427x                 17x               14x      
import { isString } from 'lodash';
 
/**
 * Coordinates is the class representing the 2-Dimension coordinates (x,y)
 */
export class Coordinates {
  /**
   * The x non-negative integer number
   */
  protected _x: number;
 
  /**
   * The y non-negative integer number
   */
  protected _y: number;
 
  /**
   * Reset the initial default values (0,0)
   */
  protected resetInternalCoordinates() {
    this._x = 0;
    this._y = 0;
  }
 
  /**
   * The protected constructor
   * @param x The x non-negative integer number
   * @param y The y non-negative integer number
   */
  protected constructor(x: number, y: number) {
    this.resetInternalCoordinates();
    if (!this.setX(x) || !this.setY(y)) {
      this.resetInternalCoordinates();
    }
  }
 
  /**
   * Utility static method to check that the 'val' in input in a non-negative
   * integer number, or a "stringified" non-negative integer which will be
   * transformed into a number
   * @param val the input which can be a string or a number
   * @returns - false if the input is not a non-negative ("stringified") integer
   * - otherwise the numeric value of the input
   */
  protected static checkNonNegativeInteger(val: string | number): number | false {
    if (
      val === null ||
      val === undefined ||
      (val as unknown) === true ||
      (val as unknown) === false ||
      Array.isArray(val)
    ) {
      return false;
    }
    const num = Number(val);
    if (Number.isInteger(num) && num >= 0) {
      return num;
    }
    return false;
  }
 
  /**
   * Utility function to transform a stringified coordinates 'x-y' into the array of coordinates [x,y]
   * @param strCoords the stringified coordinates. Ex: '4-7'
   * @param separator the separator of the stringified coordinates. Default '-'
   * @returns - false if there's any error in the strCoords parameter or the separator
   * - otherwise it returns the array of coordinates [x,y]. Ex: [4,7]
   */
  public static stringCoordsToArrCoords(strCoords: string, separator: string = '-'): false | [number, number] {
    if (!isString(strCoords)) {
      return false;
    }
    if (!isString(separator) && separator !== null && separator !== undefined) {
      return false;
    }
    const coords = strCoords.split(separator ?? '-').map((elem: string) => Number(elem.trim()));
    if (coords.length < 2) {
      return false;
    }
    if (!Number.isInteger(coords[0]) || coords[0] < 0 || !Number.isInteger(coords[1]) || coords[1] < 0) {
      return false;
    }
    return [coords[0], coords[1]];
  }
 
  /**
   * Utility function to transform an array of coordinates [x,y] into its "stringified" version 'x-y'
   * based on the value of the separator (which defaults to '-').
   * @param coordsArr the array of coordinates [x,y]. Ex: [2,7]
   * @param separator the separator of the stringified coordinates in output
   * @returns - false if there's any error in the coordsArr parameter or in the separator
   * - otherwise the stringified version of the coordinates. Ex: '2-7'
   */
  public static arrayCoordsToStringCoords(coordsArr: Array<string | number>, separator: string = '-'): false | string {
    if (!Array.isArray(coordsArr) || coordsArr.length < 2) {
      return false;
    }
    if (!isString(separator) && separator !== null && separator !== undefined) {
      return false;
    }
    const numX = Coordinates.checkNonNegativeInteger(coordsArr[0]);
    const numY = Coordinates.checkNonNegativeInteger(coordsArr[1]);
    if (numX === false || numY === false) {
      return false;
    }
    return `${numX}${separator ?? '-'}${numY}`;
  }
 
  /**
   * Build a Coordinates class instance from string coordinates 'x-y'
   * @param strCoords the stringified coordinates. Ex: '4-7'
   * @param separator the separator of the stringified coordinates. Default '-'
   * @returns - if there's any error in the strCoords parameter or the separator it returns a default Coordinate instance with (0,0)
   * - otherwise it returns an instance of the Coordinates class with (x,y)
   */
  public static fromStringCoords(strCoords: string, separator: string = '-'): Coordinates {
    const coordsArr = Coordinates.stringCoordsToArrCoords(strCoords, separator);
    if (coordsArr === false) {
      return new Coordinates(0, 0);
    }
    const coordinatesObj = new Coordinates(coordsArr[0], coordsArr[1]);
    return coordinatesObj;
  }
 
  /**
   * Build a Coordinates class instance from array coordinates [x,y]
   * @param coordsArr the array of coordinates [x,y]. Ex: [5,1]
   * @returns - if there's any error in the coordsArr parameter it returns a default Coordinate instance with (0,0)
   * - otherwise it returns an instance of the Coordinates class with (x,y)
   */
  public static fromArrayCoords(coordsArr: Array<string | number>): Coordinates {
    if (!Array.isArray(coordsArr) || coordsArr.length < 2) {
      return new Coordinates(0, 0);
    }
    const numX = Coordinates.checkNonNegativeInteger(coordsArr[0]);
    const numY = Coordinates.checkNonNegativeInteger(coordsArr[1]);
    if (numX === false || numY === false) {
      return new Coordinates(0, 0);
    }
    const coordinatesObj = new Coordinates(numX, numY);
    return coordinatesObj;
  }
 
  /**
   * The x getter
   * @returns The x non-negative integer number
   */
  public getX() {
    return this._x;
  }
 
  /**
   * The x setter
   * @param x The x non-negative integer ("stringified") number
   * @returns - false if the input is not a correct value for the coordinates
   * - otherwise true and the numeric value of the input is assigned to the `protected _x` class member
   */
  public setX(x: string | number): boolean {
    const numX = Coordinates.checkNonNegativeInteger(x);
    if (numX === false) {
      return false;
    }
    this._x = numX;
    return true;
  }
 
  /**
   * The y getter
   * @returns The y non-negative integer number
   */
  public getY() {
    return this._y;
  }
 
  /**
   * The y setter
   * @param y The y non-negative integer ("stringified") number
   * @returns - false if the input is not a correct value for the coordinates
   * - otherwise true and the numeric value of the input is assigned to the `protected _y` class member
   */
  public setY(y: string | number): boolean {
    const numY = Coordinates.checkNonNegativeInteger(y);
    if (numY === false) {
      return false;
    }
    this._y = numY;
    return true;
  }
 
  /**
   * Get the coordinates in string format separated by a separator
   * @param separator the separator of the stringified coordinates. Default '-'
   * @returns the stringified version of the coordinates. Ex: '2-7'
   */
  public toString(separator = '-') {
    return `${this._x}${separator ?? '-'}${this._y}`;
  }
 
  /**
   * Get the coordinates as array [x,y]
   * @returns the coordinates as array [x,y]. Ex: [3,4]
   */
  public toArr() {
    return [this._x, this._y];
  }
}