UNPKG

molstar

Version:

A comprehensive macromolecular library.

140 lines (139 loc) 5.07 kB
/** * Copyright (c) 2023-2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Adam Midlik <midlik@gmail.com> * @author David Sehnal <david.sehnal@gmail.com> */ import { hashString } from '../../../mol-data/util'; import { Color } from '../../../mol-util/color'; import { ColorNames } from '../../../mol-util/color/names'; /** Try to await a promise and return an object with its result (if resolved) or with the error (if rejected) */ export async function safePromise(promise) { try { const value = await promise; return { ok: true, value }; } catch (error) { return { ok: false, error }; } } /** A map where values are arrays. Handles missing keys when adding values. */ export class MultiMap { constructor() { this._map = new Map(); } /** Return the array of values assidned to a key (or `undefined` if no such values) */ get(key) { return this._map.get(key); } /** Append value to a key (handles missing keys) */ add(key, value) { if (!this._map.has(key)) { this._map.set(key, []); } this._map.get(key).push(value); } } /** Implementation of `Map` where keys are integers * and most keys are expected to be from interval `[0, limit)`. * For the keys within this interval, performance is better than `Map` (implemented by array). * For the keys out of this interval, performance is slightly worse than `Map`. */ export class NumberMap { constructor(limit) { this.limit = limit; this.array = new Array(limit); this.map = new Map(); } get(key) { if (0 <= key && key < this.limit) return this.array[key]; else return this.map.get(key); } set(key, value) { if (0 <= key && key < this.limit) this.array[key] = value; else this.map.set(key, value); } } /** Return `true` if `value` is not `undefined` or `null`. * Prefer this over `value !== undefined` * (for maybe if we want to allow `null` in `AnnotationRow` in the future) */ export function isDefined(value) { return value !== undefined && value !== null; } /** Return `true` if at least one of `values` is not `undefined` or `null`. */ export function isAnyDefined(...values) { return values.some(v => isDefined(v)); } /** Return filtered array containing all original elements except `undefined` or `null`. */ export function filterDefined(elements) { return elements.filter(x => x !== undefined && x !== null); } /** Create an 8-hex-character hash for a given input string, e.g. 'spanish inquisition' -> '7f9ac4be' */ function stringHash32(input) { const uint32hash = hashString(input) >>> 0; // >>>0 converts to uint32, LOL return uint32hash.toString(16).padStart(8, '0'); } /** Create an 16-hex-character hash for a given input string, e.g. 'spanish inquisition' -> '7f9ac4be544330be'*/ export function stringHash(input) { const reversed = input.split('').reverse().join(''); return stringHash32(input) + stringHash32(reversed); } /** Convert `colorString` (either X11 color name like 'magenta' or hex code like '#ff00ff') to Color. * Return `undefined` if `colorString` cannot be converted. */ export function decodeColor(colorString) { if (colorString === undefined || colorString === null) return undefined; let result; if (HexColor.is(colorString)) { if (colorString.length === 4) { // convert short form to full form (#f0f -> #ff00ff) colorString = `#${colorString[1]}${colorString[1]}${colorString[2]}${colorString[2]}${colorString[3]}${colorString[3]}`; } result = Color.fromHexStyle(colorString); if (result !== undefined && !isNaN(result)) return result; } result = ColorNames[colorString.toLowerCase()]; if (result !== undefined) return result; return undefined; } /** Regular expression matching a hexadecimal color string, e.g. '#FF1100' or '#f10' */ const hexColorRegex = /^#([0-9A-F]{3}){1,2}$/i; export const HexColor = { /** Decide if a string is a valid hexadecimal color string (6-digit or 3-digit, e.g. '#FF1100' or '#f10') */ is(str) { return typeof str === 'string' && hexColorRegex.test(str); }, }; export const ColorName = { /** Decide if a string is a valid named color string */ is(str) { return str in ColorNames; }, }; export function collectMVSReferences(type, dependencies) { const ret = {}; for (const key of Object.keys(dependencies)) { const o = dependencies[key]; let okType = false; for (const t of type) { if (t.is(o)) { okType = true; break; } } if (!okType || !o.tags) continue; for (const tag of o.tags) { if (tag.startsWith('mvs-ref:')) { ret[tag.substring(8)] = o.data; break; } } } return ret; }