UNPKG

pdbe-molstar

Version:
618 lines (617 loc) 32.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PluginConfigUtils = exports.PreemptiveQueue = exports.StructureComponentTags = exports.Tags = exports.ModelInfo = exports.QueryHelper = exports.LigandView = exports.AlphafoldView = exports.PDBeVolumes = void 0; exports.queryParamsToMvsComponentExpressions = queryParamsToMvsComponentExpressions; exports.runWithProgressMessage = runWithProgressMessage; exports.getStructureUrl = getStructureUrl; exports.combineUrl = combineUrl; exports.addDefaults = addDefaults; exports.normalizeColor = normalizeColor; exports.applyOverpaint = applyOverpaint; exports.getComponentTypeFromTags = getComponentTypeFromTags; exports.distinct = distinct; exports.groupElements = groupElements; exports.createIndex = createIndex; exports.nonnegativeModulo = nonnegativeModulo; exports.pluginLayoutStateFromInitParams = pluginLayoutStateFromInitParams; exports.getRotationMat4 = getRotationMat4; const tslib_1 = require("tslib"); const prop_1 = require("molstar/lib/extensions/model-archive/quality-assessment/prop"); const linear_algebra_1 = require("molstar/lib/mol-math/linear-algebra"); const structure_1 = require("molstar/lib/mol-model/structure"); const query_1 = require("molstar/lib/mol-model/structure/query/query"); const transforms_1 = require("molstar/lib/mol-plugin-state/transforms"); const transformers_1 = require("molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/transformers"); const commands_1 = require("molstar/lib/mol-plugin/commands"); const layout_1 = require("molstar/lib/mol-plugin/layout"); const builder_1 = require("molstar/lib/mol-script/language/builder"); const compiler_1 = require("molstar/lib/mol-script/runtime/query/compiler"); const mol_state_1 = require("molstar/lib/mol-state"); const mol_task_1 = require("molstar/lib/mol-task"); const color_1 = require("molstar/lib/mol-util/color"); const names_1 = require("molstar/lib/mol-util/color/names"); const sleep_1 = require("molstar/lib/mol-util/sleep"); const sifts_mapping_1 = require("./sifts-mapping"); var PDBeVolumes; (function (PDBeVolumes) { function mapParams(defaultParams, mapParams, ref) { const pdbeParams = Object.assign({}, defaultParams); pdbeParams.options.behaviorRef = 'volume-streaming' + '' + Math.floor(Math.random() * Math.floor(100)); pdbeParams.options.emContourProvider = 'pdbe'; pdbeParams.options.serverUrl = 'https://www.ebi.ac.uk/pdbe/volume-server'; const MAIN_MAP_DEFAULTS = { opacity: 0.49, wireframe: false }; const DIFF_MAP_DEFAULTS = { opacity: 0.3, wireframe: true }; pdbeParams.options.channelParams['em'] = addDefaults(mapParams === null || mapParams === void 0 ? void 0 : mapParams['em'], MAIN_MAP_DEFAULTS); pdbeParams.options.channelParams['2fo-fc'] = addDefaults(mapParams === null || mapParams === void 0 ? void 0 : mapParams['2fo-fc'], MAIN_MAP_DEFAULTS); pdbeParams.options.channelParams['fo-fc(+ve)'] = addDefaults(mapParams === null || mapParams === void 0 ? void 0 : mapParams['fo-fc(+ve)'], DIFF_MAP_DEFAULTS); pdbeParams.options.channelParams['fo-fc(-ve)'] = addDefaults(mapParams === null || mapParams === void 0 ? void 0 : mapParams['fo-fc(-ve)'], DIFF_MAP_DEFAULTS); return pdbeParams; } PDBeVolumes.mapParams = mapParams; function displayUsibilityMessage(plugin) { commands_1.PluginCommands.Toast.Show(plugin, { title: 'Volume', message: 'Streaming enabled, click on a residue or an atom to view the data.', key: 'toast-1', timeoutMs: 7000, }); } PDBeVolumes.displayUsibilityMessage = displayUsibilityMessage; function toggle(plugin) { const state = plugin.state.data; const streamingState = state.select(mol_state_1.StateSelection.Generators.ofTransformer(transformers_1.CreateVolumeStreamingInfo))[0]; if (streamingState) { commands_1.PluginCommands.State.ToggleVisibility(plugin, { state: state, ref: streamingState.transform.ref }); return; } } PDBeVolumes.toggle = toggle; })(PDBeVolumes || (exports.PDBeVolumes = PDBeVolumes = {})); var AlphafoldView; (function (AlphafoldView) { function getLociByPLDDT(score, contextData) { const queryExp = builder_1.MolScriptBuilder.struct.modifier.union([ builder_1.MolScriptBuilder.struct.modifier.wholeResidues([ builder_1.MolScriptBuilder.struct.modifier.union([ builder_1.MolScriptBuilder.struct.generator.atomGroups({ 'chain-test': builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp('objectPrimitive'), 'atomistic']), 'residue-test': builder_1.MolScriptBuilder.core.rel.gr([prop_1.QualityAssessment.symbols.pLDDT.symbol(), score]), }), ]), ]), ]); const query = (0, compiler_1.compile)(queryExp); const sel = query(new structure_1.QueryContext(contextData)); return structure_1.StructureSelection.toLociWithSourceUnits(sel); } AlphafoldView.getLociByPLDDT = getLociByPLDDT; })(AlphafoldView || (exports.AlphafoldView = AlphafoldView = {})); var LigandView; (function (LigandView) { function query(ligandViewParams) { const atomGroupsParams = { 'group-by': builder_1.MolScriptBuilder.core.str.concat([builder_1.MolScriptBuilder.struct.atomProperty.core.operatorName(), builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.residueKey()]), }; // Residue Param let residueParam; if (ligandViewParams.auth_seq_id !== undefined) { residueParam = builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.auth_seq_id(), ligandViewParams.auth_seq_id]); } else if (ligandViewParams.label_comp_id) { residueParam = builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.label_comp_id(), ligandViewParams.label_comp_id]); } if (residueParam) atomGroupsParams['residue-test'] = residueParam; // Chain Param if (ligandViewParams.auth_asym_id) { atomGroupsParams['chain-test'] = builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.auth_asym_id(), ligandViewParams.auth_asym_id]); } else if (ligandViewParams.struct_asym_id) { atomGroupsParams['chain-test'] = builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.label_asym_id(), ligandViewParams.struct_asym_id]); } // Construct core query const core = ligandViewParams.show_all ? builder_1.MolScriptBuilder.struct.generator.atomGroups(atomGroupsParams) : builder_1.MolScriptBuilder.struct.filter.first([ builder_1.MolScriptBuilder.struct.generator.atomGroups(atomGroupsParams), ]); // Construct surroundings query const surroundings = builder_1.MolScriptBuilder.struct.modifier.includeSurroundings({ 0: core, radius: 5, 'as-whole-residues': true }); return { core, surroundings, }; } LigandView.query = query; function branchedQuery(params) { const entityObjArray = []; params.atom_site.forEach((param) => { const qEntities = { 'group-by': builder_1.MolScriptBuilder.core.str.concat([builder_1.MolScriptBuilder.struct.atomProperty.core.operatorName(), builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.residueKey()]), 'residue-test': builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.struct.atomProperty.macromolecular.auth_seq_id(), param.auth_seq_id]), }; entityObjArray.push(qEntities); }); const atmGroupsQueries = []; entityObjArray.forEach((entityObj) => { atmGroupsQueries.push(builder_1.MolScriptBuilder.struct.generator.atomGroups(entityObj)); }); const core = builder_1.MolScriptBuilder.struct.modifier.union([ atmGroupsQueries.length === 1 ? atmGroupsQueries[0] // Need to union before merge for fast performance : builder_1.MolScriptBuilder.struct.combinator.merge(atmGroupsQueries.map(q => builder_1.MolScriptBuilder.struct.modifier.union([q]))), ]); // Construct surroundings query const surroundings = builder_1.MolScriptBuilder.struct.modifier.includeSurroundings({ 0: core, radius: 5, 'as-whole-residues': true }); return { core, surroundings, }; } LigandView.branchedQuery = branchedQuery; })(LigandView || (exports.LigandView = LigandView = {})); function queryParamsToMvsComponentExpressions(params) { const broadcasted = broadcast(params, ['atoms', 'atom_id']); return broadcasted.map(item => { var _a; return ({ label_entity_id: item.entity_id, label_asym_id: item.struct_asym_id, auth_asym_id: item.auth_asym_id, label_seq_id: item.residue_number, auth_seq_id: (_a = item.auth_seq_id) !== null && _a !== void 0 ? _a : item.auth_residue_number, pdbx_PDB_ins_code: item.auth_ins_code_id, beg_label_seq_id: item.start_residue_number, end_label_seq_id: item.end_residue_number, beg_auth_seq_id: item.start_auth_residue_number, end_auth_seq_id: item.end_auth_residue_number, label_atom_id: item.atoms, auth_atom_id: undefined, type_symbol: undefined, atom_id: item.atom_id, atom_index: undefined, label_comp_id: item.label_comp_id, auth_comp_id: undefined, }); }); } /** `broadcastSingleKey([{chain: 'A', residue: 5, atom: ['C', 'O']}], 'atom')` * -> `[{chain: 'A', residue: 5, atom: 'C'}], {chain: 'A', residue: 5, atom: 'O'}` */ function broadcastSingleKey(objs, key) { const out = []; for (const obj of objs) { if (obj[key] === undefined) { out.push(obj); } else { for (const value of obj[key]) { out.push(Object.assign(Object.assign({}, obj), { [key]: value })); } } } return out; } /** `broadcast([{chain: 'A', residue: [5, 6], atom: ['C', 'O']}], ['residue', 'atom'])` * -> `[{chain: 'A', residue: 5, atom: 'C'}], {chain: 'A', residue: 5, atom: 'O', {chain: 'A', residue: 6, atom: 'C'}], {chain: 'A', residue: 6, atom: 'O'}` */ function broadcast(objs, keys) { return keys.reduce((list, key) => broadcastSingleKey(list, key), objs); } var QueryHelper; (function (QueryHelper) { function getQueryObject(params, contextData) { const selections = []; /** `undefined` means SIFTSMappingMapping has not been retrieved yet. `null` means SIFTSMappingMapping has been retrieved unsuccessfully. */ let _siftMappings; function getSiftsMappings() { var _a; if (_siftMappings === undefined) _siftMappings = (_a = sifts_mapping_1.SIFTSMapping.Provider.get(contextData.models[0]).value) !== null && _a !== void 0 ? _a : null; return _siftMappings; } for (const param of params) { const predicates = { entity: [], chain: [], residue: [], atom: [], }; // entity if (param.entity_id !== undefined) { predicates.entity.push(l => structure_1.StructureProperties.entity.id(l.element) === param.entity_id); } // chain if (param.struct_asym_id !== undefined) { predicates.chain.push(l => structure_1.StructureProperties.chain.label_asym_id(l.element) === param.struct_asym_id); } if (param.auth_asym_id !== undefined) { predicates.chain.push(l => structure_1.StructureProperties.chain.auth_asym_id(l.element) === param.auth_asym_id); } // residue if (param.label_comp_id !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.atom.label_comp_id(l.element) === param.label_comp_id); } if (param.residue_number !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.label_seq_id(l.element) === param.residue_number); } if (param.start_residue_number !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.label_seq_id(l.element) >= param.start_residue_number); } if (param.end_residue_number !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.label_seq_id(l.element) <= param.end_residue_number); } if (param.auth_seq_id !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) === param.auth_seq_id); } if (param.auth_residue_number !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) === param.auth_residue_number); } if (param.auth_ins_code_id !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l.element) === param.auth_ins_code_id); } if (param.start_auth_residue_number !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) >= param.start_auth_residue_number); if (param.start_auth_ins_code_id !== undefined) { // This assumes insertion code come in alphabetical order, which is not always true (e.g. 1ucy chain A [auth L]). However, authors of such monstrosities do not deserve their structures to be displayed correctly. predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) > param.start_auth_residue_number || structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l.element) >= param.start_auth_ins_code_id); } } if (param.end_auth_residue_number !== undefined) { predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) <= param.end_auth_residue_number); if (param.end_auth_ins_code_id !== undefined) { // This assumes insertion code come in alphabetical order, which is not always true (e.g. 1ucy chain A [auth L]). However, authors of such monstrosities do not deserve their structures to be displayed correctly. predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) < param.end_auth_residue_number || structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l.element) <= param.end_auth_ins_code_id); } } if (param.uniprot_accession !== undefined) { predicates.residue.push(l => { var _a; return ((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.accession[structure_1.StructureProperties.residue.key(l.element)]) === param.uniprot_accession; }); } if (param.uniprot_residue_number !== undefined) { predicates.residue.push(l => { var _a; return Number((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.num[structure_1.StructureProperties.residue.key(l.element)]) === param.uniprot_residue_number; }); } if (param.start_uniprot_residue_number !== undefined) { predicates.residue.push(l => { var _a; return Number((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.num[structure_1.StructureProperties.residue.key(l.element)]) >= param.start_uniprot_residue_number; }); } if (param.end_uniprot_residue_number !== undefined) { predicates.residue.push(l => { var _a; return Number((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.num[structure_1.StructureProperties.residue.key(l.element)]) <= param.end_uniprot_residue_number; }); } // atom if (param.atoms) { predicates.atom.push(l => param.atoms.includes(structure_1.StructureProperties.atom.label_atom_id(l.element))); } if (param.atom_id) { predicates.atom.push(l => param.atom_id.includes(structure_1.StructureProperties.atom.id(l.element))); } selections.push({ entityTest: predicateConjunction(predicates.entity), chainTest: predicateConjunction(predicates.chain), residueTest: predicateConjunction(predicates.residue), atomTest: predicateConjunction(predicates.atom), }); } const atmGroupsQueries = selections.map(selection => structure_1.Queries.generators.atoms(selection)); return structure_1.Queries.combinators.merge(atmGroupsQueries); } QueryHelper.getQueryObject = getQueryObject; function predicateConjunction(predicates) { if (predicates.length === 0) return undefined; if (predicates.length === 1) return predicates[0]; return (x => predicates.every(pred => pred(x))); } function getInteractivityLoci(params, contextData) { const sel = query_1.StructureQuery.run(QueryHelper.getQueryObject(params, contextData), contextData); return structure_1.StructureSelection.toLociWithSourceUnits(sel); } QueryHelper.getInteractivityLoci = getInteractivityLoci; function getHetLoci(queryExp, contextData) { const query = (0, compiler_1.compile)(queryExp); const sel = query(new structure_1.QueryContext(contextData)); return structure_1.StructureSelection.toLociWithSourceUnits(sel); } QueryHelper.getHetLoci = getHetLoci; })(QueryHelper || (exports.QueryHelper = QueryHelper = {})); var ModelInfo; (function (ModelInfo) { function get(model, structures) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { _rowCount: residueCount } = model.atomicHierarchy.residues; const { offsets: residueOffsets } = model.atomicHierarchy.residueAtomSegments; const chainIndex = model.atomicHierarchy.chainAtomSegments.index; const hetNames = []; let carbEntityCount = 0; for (let rI = 0; rI < residueCount; rI++) { const cI = chainIndex[residueOffsets[rI]]; const eI = model.atomicHierarchy.index.getEntityFromChain(cI); const entityType = model.entities.data.type.value(eI); if (entityType !== 'non-polymer' && entityType !== 'branched') continue; // const comp_id = model.atomicHierarchy.atoms.label_comp_id.value(rI); const comp_id = model.atomicHierarchy.atoms.label_comp_id.value(residueOffsets[rI]); if (entityType === 'branched') { carbEntityCount++; } else { if (hetNames.indexOf(comp_id) === -1) hetNames.push(comp_id); } } return { hetNames, carbEntityCount, }; }); } ModelInfo.get = get; })(ModelInfo || (exports.ModelInfo = ModelInfo = {})); /** Run `action` with showing a message in the bottom-left corner of the plugin UI */ function runWithProgressMessage(plugin, progressMessage, action) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const task = mol_task_1.Task.create(progressMessage !== null && progressMessage !== void 0 ? progressMessage : 'Task', (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () { let done = false; try { if (progressMessage) { setTimeout(() => { if (!done) ctx.update(progressMessage); }, 1000); // Delay the first update to force showing message in UI } yield action(); } finally { done = true; } })); yield plugin.runTask(task); }); } /** Return URL for a ModelServer request. * If `queryType` is 'full' and `lowPrecisionCoords` is false, return URL of the static file instead (updated mmCIF or bCIF). */ function getStructureUrl(initParams, request) { const pdbeUrl = initParams.pdbeUrl.replace(/\/$/, ''); // without trailing slash const useStaticFile = request.queryType === 'full' && !initParams.lowPrecisionCoords; if (useStaticFile) { const suffix = initParams.encoding === 'bcif' ? '.bcif' : '_updated.cif'; return `${pdbeUrl}/entry-files/download/${request.pdbId}${suffix}`; } else { const queryParams = Object.assign(Object.assign({}, request.queryParams), { encoding: initParams.encoding, lowPrecisionCoords: initParams.lowPrecisionCoords ? 1 : undefined }); const queryString = Object.entries(queryParams).filter(([key, value]) => value !== undefined).map(([key, value]) => `${key}=${value}`).join('&'); const url = `${pdbeUrl}/model-server/v1/${request.pdbId}/${request.queryType}`; return (queryString !== '') ? `${url}?${queryString}` : url; } } /** Combine URL parts into one URL while avoiding double slashes. Examples: * combineUrl('https://example.org', '1tqn') -> 'https://example.org/1tqn'; * combineUrl('https://example.org/', '1tqn') -> 'https://example.org/1tqn'; */ function combineUrl(firstPart, ...moreParts) { let result = firstPart; for (const part of moreParts) { result = result.replace(/\/$/, '') + '/' + part; // removing extra trailing slash } return result; } /** Create a copy of object `object`, fill in missing/undefined keys using `defaults`. * This is similar to {...defaults,...object} but `undefined` in `object` will not override a value from `defaults`. */ function addDefaults(object, defaults) { var _a; const result = Object.assign({}, object); for (const key in defaults) { (_a = result[key]) !== null && _a !== void 0 ? _a : (result[key] = defaults[key]); } return result; } /** Convert `colorVal` from any of supported color formats (e.g. 'yellow', '#ffff00', {r:255,g:255,b:0}) to `Color`. * Return default color (gray) if `colorVal` is undefined or null. */ function normalizeColor(colorVal, defaultColor = color_1.Color.fromRgb(170, 170, 170)) { var _a, _b, _c; try { if (colorVal === undefined || colorVal === null) return defaultColor; if (typeof colorVal === 'number') return (0, color_1.Color)(colorVal); if (typeof colorVal === 'string' && colorVal[0] === '#') return (0, color_1.Color)(Number(`0x${colorVal.substring(1)}`)); if (typeof colorVal === 'string' && colorVal in names_1.ColorNames) return names_1.ColorNames[colorVal]; if (typeof colorVal === 'object') return color_1.Color.fromRgb((_a = colorVal.r) !== null && _a !== void 0 ? _a : 0, (_b = colorVal.g) !== null && _b !== void 0 ? _b : 0, (_c = colorVal.b) !== null && _c !== void 0 ? _c : 0); } catch (_d) { // do nothing } return defaultColor; } /** Apply overpaint to every representation of every component in a structure. * Excludes representations created as "added representations" by `PDBeMolstarPlugin.visual.select`. */ function applyOverpaint(plugin, structRef, overpaintLayers) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; if (overpaintLayers.length === 0) return; const update = plugin.build(); for (const component of structRef.components) { if ((_a = component.cell.transform.tags) === null || _a === void 0 ? void 0 : _a.includes(exports.Tags.AddedComponent)) continue; for (const repr of component.representations) { const currentOverpaint = plugin.state.data.select(mol_state_1.StateSelection.Generators .ofTransformer(transforms_1.StateTransforms.Representation.OverpaintStructureRepresentation3DFromBundle, repr.cell.transform.ref) .withTag(exports.Tags.Overpaint)); if (currentOverpaint.length === 0) { // Create a new overpaint update.to(repr.cell.transform.ref).apply(transforms_1.StateTransforms.Representation.OverpaintStructureRepresentation3DFromBundle, { layers: overpaintLayers }, { tags: exports.Tags.Overpaint }); } else { // Add layers to existing overpaint update.to(currentOverpaint[0]).update(old => ({ layers: old.layers.concat(overpaintLayers) })); } } } yield update.commit(); }); } exports.Tags = { /** Tag needed for `clearStructureOverpaint`; defined in src/mol-plugin-state/helpers/structure-overpaint.ts but private */ Overpaint: 'overpaint-controls', /** Marks structure components added by `select` */ AddedComponent: 'pdbe-molstar.added-component', }; exports.StructureComponentTags = { polymer: ['structure-component-static-polymer'], het: ['structure-component-static-ligand', 'structure-component-static-ion'], water: ['structure-component-static-water'], carbs: ['structure-component-static-branched'], nonStandard: ['structure-component-static-non-standard'], coarse: ['structure-component-static-coarse'], maps: ['volume-streaming-info'], }; /** Return component type based on the component's PluginStateObject tags */ function getComponentTypeFromTags(tags) { let type; for (type in exports.StructureComponentTags) { const typeTags = exports.StructureComponentTags[type]; if (typeTags.some(tag => tags === null || tags === void 0 ? void 0 : tags.includes(tag))) { return type; } } return undefined; } /** Return a new array containing `values` without duplicates (only first occurrence will be kept). * Values v1, v2 are considered duplicates when `key(v1)===key(v2)`. */ function distinct(values, key = (value => value)) { const out = []; const seenKeys = new Set(); for (const value of values) { const theKey = key(value); if (!seenKeys.has(theKey)) { out.push(value); seenKeys.add(theKey); } } return out; } /** Group elements by result of `groupFunction` applied to them */ function groupElements(elements, groupFunction) { const groups = []; const members = new Map(); for (const elem of elements) { const g = groupFunction(elem); if (members.has(g)) { members.get(g).push(elem); } else { groups.push(g); members.set(g, [elem]); } } return { /** Groups (results of `groupFunction`) in order as they first appeared in `elements` */ groups, /** Mapping of groups to lists of they members */ members, }; } /** Return a mapping of elements to their index in the `elements` array */ function createIndex(elements) { const index = new Map(); elements.forEach((elem, i) => index.set(elem, i)); return index; } /** Return modulo of two numbers (a % b) within range [0, b) */ function nonnegativeModulo(a, b) { const modulo = a % b; return (modulo < 0) ? modulo + b : modulo; } /** Queue for running jobs where enqueued jobs get discarded when a new job is enqueued. * (Discarded jobs may or may not actually be executed, but their result is not accessible anyway.) */ class PreemptiveQueue { constructor(run) { this.run = run; } /** Enqueue a job which will execute `run(args)`. * Return a promise that either resolves to `{ status: 'completed', result }` where `result` is `await run(args)`, * or resolves to `{ status: 'cancelled' }` if the job starts being executed but another jobs gets enqueued before its completion, * or resolves to `{ status: 'skipped' }` if the job does not even start before another jobs gets enqueued, * or rejects if the job is completed but throws an error. */ requestRun(args) { if (this.running) { this.running.cancelled = true; this.running.callbacks.resolve({ status: 'cancelled' }); } if (this.queuing) { this.queuing.callbacks.resolve({ status: 'skipped' }); } const promise = new Promise((resolve, reject) => { this.queuing = { args, callbacks: { resolve, reject } }; }); if (!this.running) { this.handleRequests(); // do not await } return promise; } /** Request handling loop. Resolves when there are no more requests. Not to be awaited, should run in the background. */ handleRequests() { return tslib_1.__awaiter(this, void 0, void 0, function* () { while (this.queuing) { this.running = this.queuing; this.queuing = undefined; yield (0, sleep_1.sleep)(0); // let other things happen (this pushes the rest of the function to the end of the queue) try { const result = yield this.run(this.running.args); if (!this.running.cancelled) { this.running.callbacks.resolve({ status: 'completed', result }); } } catch (error) { if (!this.running.cancelled) { this.running.callbacks.reject(error); } } this.running = undefined; } }); } } exports.PreemptiveQueue = PreemptiveQueue; /** Functions for working with plugin config items */ var PluginConfigUtils; (function (PluginConfigUtils) { /** Retrieve config values for items in `configItems` from the current plugin config */ function getConfigValues(plugin, configItems, defaults) { var _a; const values = {}; for (const name in configItems) { values[name] = (_a = plugin === null || plugin === void 0 ? void 0 : plugin.config.get(configItems[name])) !== null && _a !== void 0 ? _a : defaults[name]; } return values; } PluginConfigUtils.getConfigValues = getConfigValues; })(PluginConfigUtils || (exports.PluginConfigUtils = PluginConfigUtils = {})); function pluginLayoutStateFromInitParams(initParams) { return { isExpanded: initParams.expanded, showControls: !initParams.hideControls, regionState: { left: initParams.leftPanel ? 'full' : 'hidden', right: initParams.rightPanel ? 'full' : 'hidden', top: initParams.sequencePanel ? 'full' : 'hidden', bottom: initParams.logPanel ? 'full' : 'hidden', }, controlsDisplay: initParams.reactive ? 'reactive' : initParams.landscape ? 'landscape' : layout_1.PluginLayoutStateParams.controlsDisplay.defaultValue, }; } function getRotationMat4(view) { switch (view) { case 'front': return linear_algebra_1.Mat4.identity(); case 'back': return [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1]; case 'right': return [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]; case 'left': return [0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1]; case 'top': return [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1]; case 'bottom': return [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1]; default: { const out = linear_algebra_1.Mat4.fromMat3((0, linear_algebra_1.Mat4)(), view); out[15] = 1; return out; } } }