molstar
Version:
A comprehensive macromolecular library.
136 lines (135 loc) • 4.67 kB
JavaScript
/**
* Copyright (c) 2023-2025 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.js';
import { Color } from '../../../mol-util/color/index.js';
import { decodeColor as _decodeColor } from '../../../mol-util/color/utils.js';
/** 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(isDefined);
}
/** 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 (typeof colorString === 'number') {
return Color(colorString);
}
return _decodeColor(colorString);
}
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;
}
export function getMVSReferenceObject(type, dependencies, ref) {
if (!dependencies)
return undefined;
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:')) {
if (tag.substring(8) === ref)
return o;
}
}
}
}