Skip to content
Snippets Groups Projects
util.js 6.85 KiB
(function(exports) {

  exports.camelToSnake = s => s.replace(/[A-Z]/g, s => `_${s.toLowerCase()}`)

  const flattenReducer = (acc, curr) => acc.concat(curr)
  exports.flattenReducer = flattenReducer

  const getIdObj = fullId => {
    if (!fullId) return null
    if (typeof fullId === 'string') {
      const re = /([a-z]{1,}\/[a-z]{1,}\/[a-z]{1,}\/v[0-9.]{1,})\/([0-9a-f-]{1,}$)/.exec(fullId)
      if (re) return { kgSchema: re[1], kgId: re[2] }
      return null
    } else {
      const { kg = {} } = fullId
      const { kgSchema , kgId } = kg
      if (!kgSchema || !kgId) return null
      return { kgSchema, kgId }
    }
  }

  const setsContain = (set1, set2) => {
    for (const el of set2){
      if (!set1.has(el)) return false
    }
    return true
  }

  const HEMISPHERE = {
    LEFT_HEMISPHERE: `left`,
    RIGHT_HEMISPHERE: `right`
  }

  exports.getRegionHemisphere = region => {
    if (!region) return null
    return (region.name && region.name.includes(' right') || (!!region.status && region.status.includes('right')))
      ? HEMISPHERE.RIGHT_HEMISPHERE
      : (region.name && region.name.includes(' left') || (!!region.status && region.status.includes('left')))
        ? HEMISPHERE.LEFT_HEMISPHERE
        : null
  }

  exports.setsContain = setsContain

  exports.setsEql = (set1, set2) => {
    if (set1.size !== set2.size) return false
    if (!setsContain(set1, set2)) return false
    if (!setsContain(set2, set1)) return false
    return true
  }

  exports.arrayOrderedEql = function arrayOrderedEql(arr1, arr2) {
    if (arr1.length !== arr2.length) return false
    for (const idx in arr1) {
      if (arr1[idx] !== arr2[idx]) return false
    }
    return true
  }

  exports.strToRgb = str => {
    if (typeof str !== 'string') throw new Error(`strToRgb input must be typeof string !`)

    let hash = 0
    // run at least 2 cycles, or else, len 1 string does not get hashed well
    for (let i = 0; i < str.length || i < 5; i++) {
      hash = str.charCodeAt(i % str.length) + ((hash << 5) - hash);
    }
    const returnV = []
    for (let i = 0; i < 3; i++) {
      const value = (hash >> (i * 8)) & 0xFF;
      returnV.push(value)
    }
    return returnV
  }

  exports.getUniqueRegionId = (template, parcellation, region) => {
    const templateId = template ? (template['@id'] || template['name']) : `untitled-template`
    const parcId = parcellation ? (parcellation['@id'] || parcellation['name']) : `untitled-parcellation`
    const regionId = region ? region['name'] : `untitled-region`
    return `${templateId}/${parcId}/${regionId}`
  }

  exports.getIdObj = getIdObj

  exports.getIdFromKgIdObj = kg => {
    if(kg.kgId && kg.kgSchema) {
      return `${kg.kgSchema}/${kg.kgId}`
    }
    return null
  }

  exports.getIdFromFullId = fullId => {
    const idObj = getIdObj(fullId)
    if (!idObj) return null
    const { kgSchema, kgId } = idObj
    return `${kgSchema}/${kgId}`
  }

  const getIdsObj = fullId => {
    const returnArray = []
    if (!fullId) return returnArray
    const legacyFullId = getIdObj(fullId)
    if (legacyFullId) returnArray.push(`${legacyFullId['kgSchema']}/${legacyFullId['kgId']}`)

    const { ['minds/core/parcellationregion/v1.0.0']: uniMindsParcRegScheIds} = fullId
    for (const key in uniMindsParcRegScheIds || {}) {
      returnArray.push(`minds/core/parcellationregion/v1.0.0/${key}`)
    }
    return returnArray
  }

  exports.getStringIdsFromRegion = region => {
    const { fullId, relatedAreas = [] } = region
    /**
     * other ways of getting id?
     */
    const relatedAreasId = relatedAreas
      .map(({ fullId }) => getIdsObj(fullId))
      .reduce(flattenReducer, [])
    return [
      ...getIdsObj(fullId),
      ...relatedAreasId
    ]
  }

  const defaultConfig = {
    timeout: 1000,
    retries: 3
  }

  exports.retry = async (fn, { timeout = defaultConfig.timeout, retries = defaultConfig.retries } = defaultConfig) => {
    let retryNo = 0
    while (retryNo < retries) {
      retryNo ++
      try {
        const result = await fn()
        return result
      } catch (e) {
        console.warn(`fn failed, retry after ${timeout} milliseconds`)
        await (() => new Promise(rs => setTimeout(rs, timeout)))()
      }
    }

    throw new Error(`fn failed ${retries} times. Aborting.`)
  }

  exports.race = async (fn, { timeout = defaultConfig.timeout } = {}) => {
    return Promise.race([
      fn(),
      new Promise((_rs, rj) => setTimeout(rj, timeout, `timed out: ${timeout}`))
    ])
  }

  const flattenRegions = regions => regions.concat(
    ...regions.map(region => region.children && region.children instanceof Array
      ? flattenRegions(region.children)
      : [])
  )

  exports.flattenRegions = flattenRegions

  exports.hexToRgb = hex => hex && hex.match(/[a-fA-F0-9]{2}/g).map(v => parseInt(v, 16))
  exports.rgbToHex = rgb => rgb && `#${rgb.map(color => color.toString(16).padStart(2, '0')).join("")}`
  exports.getRandomHex = (digit = 1024 * 1024 * 1024 * 1024) => Math.round(Math.random() * digit).toString(16)

  /**
   * No license defined
   * https://gist.github.com/mjackson/5311256#file-color-conversion-algorithms-js
   */
  exports.rgbToHsl = (r, g, b) => {

    r /= 255, g /= 255, b /= 255;

    var max = Math.max(r, g, b), min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;

    if (max == min) {
      h = s = 0; // achromatic
    } else {
      var d = max - min;
      s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

      switch (max) {
        case r: h = (g - b) / d + (g < b ? 6 : 0); break;
        case g: h = (b - r) / d + 2; break;
        case b: h = (r - g) / d + 4; break;
      }

      h /= 6;
    }

    return [ h, s, l ];
  }

  exports.verifyPositionArg = val => {
    return (
      Array.isArray(val) &&
      val.length === 3 &&
      val.every(n =>
        typeof n === 'number' &&
        !Number.isNaN(n)
      )
    )
  }

  const getPad = ({ length, pad }) => {
    if (pad.length !== 1) throw new Error(`pad needs to be precisely 1 character`)
    return input => {
      const padNum = Math.max(input.toString().length - length, 0)
      const padString = Array(padNum).fill(pad).join('')
      return `${padString}${input}`
    }
  }

  exports.getDateString = () => {
    const d = new Date()
    const pad2 = getPad({ pad: '0', length: 2 })

    const year = d.getFullYear()
    const month = d.getMonth() + 1
    const date = d.getDate()

    const hr = d.getHours()
    const min = d.getMinutes()
    return `${year}${pad2(month)}${pad2(date)}_${pad2(hr)}${pad2(min)}`
  }

  function isVec4(input) {
    if (!Array.isArray(input)) return false
    if (input.length !== 4) return false
    return input.every(v => typeof v === "number")
  }
  function isMat4(input) {
    if (!Array.isArray(input)) return false
    if (input.length !== 4) return false
    return input.every(isVec4)
  }

  exports.isVec4 = isVec4
  exports.isMat4 = isMat4


})(typeof exports === 'undefined' ? module.exports : exports)