UNPKG

vitessce

Version:

Vitessce app and React component library

115 lines (105 loc) 3.69 kB
/* eslint-disable no-plusplus */ // Adapted from https://github.com/feross/fromentries/blob/29b52a850bb3a47c390937631c2638edf3443942/index.js export function fromEntries(iterable) { return [...iterable] .reduce((obj, { 0: key, 1: val }) => Object.assign(obj, { [key]: val }), {}); } /** * Select between a singular and plural version of a word, * based on an item count. * @param {string} singular The singular version of the word. * @param {string} plural The plural version of the word. * @param {number} count The number of items. * @returns {string} Singular if count is one, else plural. */ export function pluralize(singular, plural, count) { return (count === 1 ? singular : plural); } /** * Capitalize a the first letter of a string. * @param {string} word A string to capitalize. * @returns {string} The word parameter with the first letter capitalized. */ export function capitalize(word) { return word.charAt(0).toUpperCase() + word.slice(1); } /** * Get the longest string in the array of strings. * @param {string[]} strings The array of strings. * @returns The longest string. */ export function getLongestString(strings) { return strings.reduce( (prevLongest, currentValue) => ( prevLongest.length > currentValue.length ? prevLongest : currentValue ), ); } /** * Generate a new scope name which does not * conflict / overlap with a previous scope name. * Really these just need to be unique within the coordination object. * So in theory they could be String(Math.random()) or uuidv4() or something. * However it may be good to make them more human-readable and memorable * since eventually we will want to expose a UI to update the coordination. * @param {string[]} prevScopes Previous scope names. * @returns {string} The new scope name. */ export function getNextScope(prevScopes) { // Keep an ordered list of valid characters. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Store the value of the next character for each position // in the new string. // For example, [0] -> "A", [1] -> "B", [0, 1] -> "AB" const nextCharIndices = [0]; // Generate a new scope name, // potentially conflicting with an existing name. // Reference: https://stackoverflow.com/a/12504061 function next() { const r = []; nextCharIndices.forEach((charIndex) => { r.unshift(chars[charIndex]); }); let increment = true; for (let i = 0; i < nextCharIndices.length; i++) { const val = ++nextCharIndices[i]; if (val >= chars.length) { nextCharIndices[i] = 0; } else { increment = false; break; } } if (increment) { nextCharIndices.push(0); } return r.join(''); } let nextScope; do { nextScope = next(); } while (prevScopes.includes(nextScope)); return nextScope; } /** * Get a representative PixelSource from a loader object returned from * the Vitessce imaging loaders * @param {object} loader { data: (PixelSource[]|PixelSource), metadata, channels } object * @param {number=} level Level of the multiscale loader from which to get a PixelSource * @returns {object} PixelSource object */ export function getSourceFromLoader(loader, level) { const { data } = loader; const source = Array.isArray(data) ? data[(level || data.length - 1)] : data; return source; } /* * Helper method to determine whether pixel data is interleaved and rgb or not. * @param {object} loader */ export function isRgb(loader) { const source = getSourceFromLoader(loader); const { shape, dtype, labels } = source; const channelSize = shape[labels.indexOf('c')]; return (channelSize === 3) && dtype === 'Uint8'; }