{"version":3,"file":"regular.mjs","sources":["../../../../../../../packages/components/overlays/wind/grid/regular.ts"],"sourcesContent":["// https://en.wikipedia.org/wiki/Regular_grid\n\nimport { RAD } from './consts'\nimport { decimalize, floorMod } from './math'\n// import REGULAR_FRAG from './regular.frag'\n\n/**\n * Creates a regular geographic grid. Each axis defines the \"start\" value in degrees, \"delta\" degrees between\n * ticks, and the \"size\" (number of ticks from the origin, inclusive). Positive deltas move eastward/northward. This\n * example creates a full 1° x 1° grid covering the earth starting at the south pole:\n *\n *     λaxis: {start: 0, delta: 1, size: 360}    where λ in the range [0, 359]\n *     φaxis: {start: -90, delta: 1, size: 181}  where φ in the range [-90, 90]\n *\n * A grid maps from [λ, φ] coordinates to grid point indices.\n *\n * @param λaxis longitude axis\n * @param φaxis latitude axis\n * @returns {*}\n */\nexport function regularGrid(λaxis, φaxis) {\n  const nx = Math.floor(λaxis.size) // number of lon points\n  const ny = Math.floor(φaxis.size) // number of lat points\n  const np = nx * ny // total number of points\n  const Δλ = decimalize(λaxis.delta) // distance between lon points\n  const Δφ = decimalize(φaxis.delta) // distance between lat points\n  const λ0 = decimalize(λaxis.start) // lon origin\n  const φ0 = decimalize(φaxis.start) // lat origin, expected to be on range [-90, 90]\n  const λ1 = λ0 + Δλ * (nx - 1) // lon upper bound\n  const φ1 = φ0 + Δφ * (ny - 1) // lat upper bound\n\n  const λlow = (λ0 - Δλ / 2) * RAD\n  const λhigh = (λ1 + Δλ / 2) * RAD\n  const λsize = λhigh - λlow\n  const φlow = (φ0 - Δφ / 2) * RAD\n  const φhigh = (φ1 + Δφ / 2) * RAD\n  const φsize = φhigh - φlow\n  const low = [λlow, φlow]\n  const size = [λsize, φsize]\n\n  const isCylinder = Math.floor(nx * Δλ) >= 360 // true if the grid forms a cylinder\n\n  //function iterator() {\n  //    const i = 0;\n  //    return {\n  //        next: function() {\n  //            if (i >= np) {\n  //                return {done: true};\n  //            }\n  //            const x = i % nx;\n  //            const y = Math.floor(i / nx);\n  //            const λ = λ0 + x * Δλ;\n  //            const φ = φ0 + y * Δφ;\n  //            return {value: [λ, φ, i++], done: false};\n  //        },\n  //    };\n  //}\n\n  /** @returns {{width: number, height: number}} dimensions of this grid */\n  function dimensions() {\n    return {\n      width: nx,\n      height: ny\n    }\n  }\n\n  /** @returns {boolean} true if the grid fully wraps around longitudinal axis */\n  function isCylindrical() {\n    return isCylinder\n  }\n\n  /**\n   * @param {Function} cb the callback ƒ(λ, φ, i), where a truthy return value terminates the iteration.\n   * @param {number?} start the starting grid index.\n   * @returns {number} the grid index of the next iteration, or NaN if iteration is finished.\n   */\n  function forEach(cb, start) {\n    for (let i = start || 0; i < np; i++) {\n      const x = i % nx\n      const y = Math.floor(i / nx)\n      const λ = λ0 + x * Δλ\n      const φ = φ0 + y * Δφ\n      if (cb(λ, φ, i)) {\n        return i + 1 // Terminate iteration and return next grid index.\n      }\n    }\n    return NaN // Iteration is finished.\n  }\n\n  /**\n   * @param {number} λ latitude (degrees)\n   * @param {number} φ longitude (degrees)\n   * @returns {number} index of closest grid point or NaN if further than Δλ/2 or Δφ/2 from the grid boundary.\n   */\n  function closest(λ, φ) {\n    if (λ === λ && φ === φ) {\n      const x = floorMod(λ - λ0, 360) / Δλ\n      const y = (φ - φ0) / Δφ\n      const rx = Math.round(x)\n      const ry = Math.round(y)\n\n      if (0 <= ry && ry < ny && 0 <= rx && (rx < nx || (rx === nx && isCylinder))) {\n        const i = ry * nx + rx\n        return rx === nx ? i - nx : i\n      }\n    }\n    return NaN\n  }\n\n  /**\n   * Identifies the four points surrounding the specified coordinates. The result is a six-element array:\n   *\n   *     0-3: the indices of the four points, in increasing order.\n   *     4,5: the fraction that λ,φ is away from the first point, normalized to the range [0, 1].\n   *\n   * Example:\n   * <pre>\n   *          1      2           After converting λ and φ to positions on the x and y grid axes, we find the\n   *         fx  x   cx          four points that enclose point [x, y]. These points are at the four\n   *          | =1.4 |           corners specified by the floor and ceiling of x and y. For example, given\n   *       --i00-|--i10-- fy 8   x = 1.4 and y = 8.3, the four surrounding grid points are [1, 8], [2, 8],\n   *     y ___|_ .   |           [1, 9] and [2, 9]. These points have index i00, i10, i01, i11, respectively,\n   *   =8.3   |      |           and result of this function is an array [i00, i10, i01, i11, 0.4, 0.3].\n   *       --i01----i11-- cy 9\n   *          |      |\n   * </pre>\n   *\n   * @param {number} λ latitude (degrees)\n   * @param {number} φ longitude (degrees)\n   * @returns {number[]} the indices of the four grid points surrounding the coordinate pair and the (x,y) fraction,\n   *          or [NaN, NaN, NaN, NaN, NaN, NaN] if all points are not found.\n   */\n  function closest4(λ, φ) {\n    if (λ === λ && φ === φ) {\n      const x = floorMod(λ - λ0, 360) / Δλ\n      const y = (φ - φ0) / Δφ\n      const fx = Math.floor(x)\n      const fy = Math.floor(y)\n      const cx = fx + 1\n      const cy = fy + 1\n      const Δx = x - fx\n      const Δy = y - fy\n\n      if (0 <= fy && cy < ny && 0 <= fx && (cx < nx || (cx === nx && isCylinder))) {\n        const i00 = fy * nx + fx\n        let i10 = i00 + 1\n        const i01 = i00 + nx\n        let i11 = i01 + 1\n        if (cx === nx) {\n          i10 -= nx\n          i11 -= nx\n        }\n        return [i00, i10, i01, i11, Δx, Δy]\n      }\n    }\n    return [NaN, NaN, NaN, NaN, NaN, NaN]\n  }\n\n  // function webgl() {\n  //   return {\n  //     shaderSource: REGULAR_FRAG,\n  //     uniforms: {\n  //       u_Low: low,\n  //       u_Size: size,\n  //     },\n  //   }\n  // }\n\n  return {\n    dimensions: dimensions,\n    isCylindrical: isCylindrical,\n    forEach: forEach,\n    closest: closest,\n    closest4: closest4\n    // webgl: webgl,\n  }\n}\n"],"names":[],"mappings":";;;AAEO,SAAS,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE;AACpD,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACzC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACzC,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACrB,EAAE,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACpD,EAAE,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACpD,EAAE,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAC/C,EAAE,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAC/C,EAAE,MAAM,OAAO,GAAG,OAAO,GAAG,YAAY,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AACpD,EAAE,MAAM,OAAO,GAAG,OAAO,GAAG,YAAY,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AACpD,EAAE,MAAM,SAAS,GAAG,CAAC,OAAO,GAAG,YAAY,GAAG,CAAC,IAAI,GAAG,CAAC;AACvD,EAAE,MAAM,UAAU,GAAG,CAAC,OAAO,GAAG,YAAY,GAAG,CAAC,IAAI,GAAG,CAAC;AACxD,EAAE,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAC5C,EAAE,MAAM,SAAS,GAAG,CAAC,OAAO,GAAG,YAAY,GAAG,CAAC,IAAI,GAAG,CAAC;AACvD,EAAE,MAAM,UAAU,GAAG,CAAC,OAAO,GAAG,YAAY,GAAG,CAAC,IAAI,GAAG,CAAC;AACxD,EAAE,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAC5C,EAAE,MAAM,GAAG,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACrC,EAAE,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AACxC,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,YAAY,CAAC,IAAI,GAAG,CAAC;AAC1D,EAAE,SAAS,UAAU,GAAG;AACxB,IAAI,OAAO;AACX,MAAM,KAAK,EAAE,EAAE;AACf,MAAM,MAAM,EAAE,EAAE;AAChB,KAAK,CAAC;AACN,GAAG;AACH,EAAE,SAAS,aAAa,GAAG;AAC3B,IAAI,OAAO,UAAU,CAAC;AACtB,GAAG;AACH,EAAE,SAAS,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE;AAC9B,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;AAC1C,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;AACvB,MAAM,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;AACnC,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,GAAG,YAAY,CAAC;AAChD,MAAM,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE;AACjC,QAAQ,OAAO,CAAC,GAAG,CAAC,CAAC;AACrB,OAAO;AACP,KAAK;AACL,IAAI,OAAO,GAAG,CAAC;AACf,GAAG;AACH,EAAE,SAAS,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE;AACnC,IAAI,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE;AAChD,MAAM,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,CAAC,GAAG,YAAY,CAAC;AAC/D,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,IAAI,YAAY,CAAC;AAClD,MAAM,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/B,MAAM,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/B,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,EAAE;AACjF,QAAQ,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC/B,QAAQ,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACtC,OAAO;AACP,KAAK;AACL,IAAI,OAAO,GAAG,CAAC;AACf,GAAG;AACH,EAAE,SAAS,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE;AACpC,IAAI,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE;AAChD,MAAM,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,CAAC,GAAG,YAAY,CAAC;AAC/D,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,IAAI,YAAY,CAAC;AAClD,MAAM,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/B,MAAM,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/B,MAAM,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACxB,MAAM,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACxB,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,CAAC;AAC7B,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,CAAC;AAC7B,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,EAAE;AACjF,QAAQ,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjC,QAAQ,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAC1B,QAAQ,MAAM,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AAC7B,QAAQ,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAC1B,QAAQ,IAAI,EAAE,KAAK,EAAE,EAAE;AACvB,UAAU,GAAG,IAAI,EAAE,CAAC;AACpB,UAAU,GAAG,IAAI,EAAE,CAAC;AACpB,SAAS;AACT,QAAQ,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACtD,OAAO;AACP,KAAK;AACL,IAAI,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC1C,GAAG;AACH,EAAE,OAAO;AACT,IAAI,UAAU;AACd,IAAI,aAAa;AACjB,IAAI,OAAO;AACX,IAAI,OAAO;AACX,IAAI,QAAQ;AACZ,GAAG,CAAC;AACJ;;;;"}