mirror of
https://github.com/molstar/molstar.git
synced 2026-06-07 23:34:23 +08:00
Compare commits
18 Commits
v0.7.0-dev
...
v0.7.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d2a53f28e | ||
|
|
1f7ffabef9 | ||
|
|
16d5c07224 | ||
|
|
2392bfb579 | ||
|
|
b4036f576c | ||
|
|
690d6812dc | ||
|
|
a44aa02f13 | ||
|
|
65ddd6d68a | ||
|
|
754025b3b1 | ||
|
|
f0649c5aa3 | ||
|
|
6df045211c | ||
|
|
8a00540de0 | ||
|
|
0d78905686 | ||
|
|
6edab203c2 | ||
|
|
0abfdb5ee3 | ||
|
|
88369158c9 | ||
|
|
8926575283 | ||
|
|
15b0288ce4 |
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.7.0-dev.7",
|
||||
"version": "0.7.0-dev.12",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.7.0-dev.7",
|
||||
"version": "0.7.0-dev.12",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -92,6 +92,12 @@
|
||||
// var format = 'pdb';
|
||||
// var assemblyId = 'deposited';
|
||||
|
||||
function loadAndSnapshot(params) {
|
||||
PluginWrapper.load(params).then(() => {
|
||||
setTimeout(() => snapshot = PluginWrapper.plugin.state.getSnapshot({ canvas3d: false /* do not save spinning state */ }), 500);
|
||||
});
|
||||
}
|
||||
|
||||
var representationStyle = {
|
||||
// sequence: { coloring: 'proteopedia-custom' }, // or just { }
|
||||
hetGroups: { kind: 'ball-and-stick' }, // or 'spacefill
|
||||
@@ -103,7 +109,7 @@
|
||||
customColorList: CustomColors
|
||||
});
|
||||
PluginWrapper.setBackground(0xffffff);
|
||||
PluginWrapper.load({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
|
||||
loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
|
||||
PluginWrapper.toggleSpin();
|
||||
|
||||
PluginWrapper.events.modelInfo.subscribe(function (info) {
|
||||
@@ -111,8 +117,8 @@
|
||||
listHetGroups(info);
|
||||
});
|
||||
|
||||
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format, isBinary }));
|
||||
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, isBinary, assemblyId: assemblyId }));
|
||||
addControl('Load Asym Unit', () => loadAndSnapshot({ url: url, format: format, isBinary }));
|
||||
addControl('Load Assembly', () => loadAndSnapshot({ url: url, format: format, isBinary, assemblyId: assemblyId }));
|
||||
|
||||
addSeparator();
|
||||
|
||||
@@ -185,10 +191,10 @@
|
||||
PluginWrapper.snapshot.set(snapshot);
|
||||
});
|
||||
addControl('Download State', () => {
|
||||
snapshot = PluginWrapper.snapshot.download('molj');
|
||||
PluginWrapper.snapshot.download('molj');
|
||||
});
|
||||
addControl('Download Session', () => {
|
||||
snapshot = PluginWrapper.snapshot.download('molx');
|
||||
PluginWrapper.snapshot.download('molx');
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
@@ -5,30 +5,29 @@
|
||||
*/
|
||||
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
|
||||
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
|
||||
import { EvolutionaryConservation } from './annotation';
|
||||
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { volumeStreamingControls } from './ui/controls';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { Scheduler } from '../../mol-task';
|
||||
import { createProteopediaCustomTheme } from './coloring';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
import { download } from '../../mol-util/download';
|
||||
import { getFormattedTime } from '../../mol-util/date';
|
||||
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { getFormattedTime } from '../../mol-util/date';
|
||||
import { download } from '../../mol-util/download';
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { EvolutionaryConservation } from './annotation';
|
||||
import { createProteopediaCustomTheme } from './coloring';
|
||||
import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
|
||||
import './index.html';
|
||||
import { volumeStreamingControls } from './ui/controls';
|
||||
require('../../mol-plugin-ui/skin/light.scss');
|
||||
|
||||
class MolStarProteopediaWrapper {
|
||||
@@ -233,7 +232,6 @@ class MolStarProteopediaWrapper {
|
||||
await this.updateStyle(representationStyle);
|
||||
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
|
||||
}
|
||||
|
||||
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
|
||||
|
||||
@@ -27,61 +27,73 @@ import { Column } from '../../mol-data/db';
|
||||
import { createModels } from '../../mol-model-formats/structure/basic/parser';
|
||||
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { readFromFile } from '../../mol-util/data-source';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`;
|
||||
}
|
||||
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, file?: Asset.File) {
|
||||
class TrajectoryCache {
|
||||
private map = new Map<string, Model.Trajectory>();
|
||||
set(id: string, trajectory: Model.Trajectory) { this.map.set(id, trajectory); }
|
||||
get(id: string) { return this.map.get(id); }
|
||||
}
|
||||
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
|
||||
const assetManager = plugin.managers.asset;
|
||||
const model_id = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
|
||||
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
|
||||
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
|
||||
let model: Model;
|
||||
let trajectory = trajCache.get(id);
|
||||
let assets: Asset.Wrapper[] = [];
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const cif = (await parseCif(plugin, text.data)).blocks[0];
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
|
||||
assets.push(binary);
|
||||
const cif = (await parseCif(plugin, binary.data)).blocks[0];
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(cif)))[model_id];
|
||||
} else if (file.name.endsWith('.pdb')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const pdb = await parsePDBfile(plugin, text.data, id);
|
||||
model = (await plugin.runTask(trajectoryFromPDB(pdb)))[model_id];
|
||||
} else {
|
||||
throw new Error(`unsupported file type '${file.name}'`);
|
||||
}
|
||||
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
|
||||
if (surface){
|
||||
const data = await getFromOPM(plugin, id, assetManager);
|
||||
if (data.asset){
|
||||
assets.push(data.asset);
|
||||
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
|
||||
if (!trajectory) {
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const cif = (await parseCif(plugin, text.data)).blocks[0];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
|
||||
assets.push(binary);
|
||||
const cif = (await parseCif(plugin, binary.data)).blocks[0];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
|
||||
} else if (file.name.endsWith('.pdb')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const pdb = await parsePDBfile(plugin, text.data, id);
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(pdb));
|
||||
} else {
|
||||
throw new Error(`unsupported file type '${file.name}'`);
|
||||
}
|
||||
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
|
||||
if (surface){
|
||||
const data = await getFromOPM(plugin, id, assetManager);
|
||||
if (data.asset){
|
||||
assets.push(data.asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} else {
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
|
||||
}
|
||||
} else {
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
|
||||
}
|
||||
} else {
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(mmcif)))[model_id];
|
||||
}
|
||||
} else {
|
||||
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
|
||||
assets.push(data.asset);
|
||||
if ('pdb' in data) {
|
||||
model = (await plugin.runTask(trajectoryFromPDB(data.pdb)))[model_id];
|
||||
} else {
|
||||
model = (await plugin.runTask(trajectoryFromMmCIF(data.mmcif)))[model_id];
|
||||
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
|
||||
assets.push(data.asset);
|
||||
if ('pdb' in data) {
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} else {
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(data.mmcif));
|
||||
}
|
||||
}
|
||||
trajCache.set(id, trajectory);
|
||||
}
|
||||
const model = trajectory[modelIndex];
|
||||
return { model, assets };
|
||||
}
|
||||
|
||||
@@ -133,7 +145,6 @@ function getTransform(trans: Vec3, rot: Quat) {
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
|
||||
if (legacy) return results.map((r: Ingredient['results'][0]) => getTransformLegacy(r[0], r[1]));
|
||||
else return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]));
|
||||
@@ -288,7 +299,7 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
|
||||
return getStructure(plugin, curveModel, ingredient.source);
|
||||
}
|
||||
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) {
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache) {
|
||||
const { name, source, results, nbCurve } = ingredient;
|
||||
if (source.pdb === 'None') return;
|
||||
|
||||
@@ -303,7 +314,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
}
|
||||
|
||||
// model id in case structure is NMR
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, file);
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
|
||||
if (!model) return;
|
||||
|
||||
let structure: Structure;
|
||||
@@ -354,10 +365,11 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
|
||||
return Task.create('Create Packing Structure', async ctx => {
|
||||
const { ingredients, name } = packing;
|
||||
const assets: Asset.Wrapper[] = [];
|
||||
const trajCache = new TrajectoryCache();
|
||||
const structures: Structure[] = [];
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) await ctx.update(iName);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
|
||||
if (ingredientStructure) {
|
||||
structures.push(ingredientStructure.structure);
|
||||
assets.push(...ingredientStructure.assets);
|
||||
@@ -444,6 +456,8 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
}
|
||||
|
||||
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
|
||||
const ingredientFiles = params.ingredients.files || [];
|
||||
|
||||
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
|
||||
if (params.source.name === 'id') {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
|
||||
@@ -451,12 +465,25 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
|
||||
} else {
|
||||
const file = params.source.params;
|
||||
if (file === null) {
|
||||
if (!file?.file) {
|
||||
plugin.log.error('No file selected');
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonFile: Asset.File;
|
||||
if (file.name.toLowerCase().endsWith('.zip')) {
|
||||
const data = await readFromFile(file.file, 'zip').runInContext(runtime);
|
||||
jsonFile = Asset.File(new File([data['model.json']], 'model.json'));
|
||||
objectForEach(data, (v, k) => {
|
||||
if (k === 'model.json') return;
|
||||
ingredientFiles.push(Asset.File(new File([v], k)));
|
||||
});
|
||||
} else {
|
||||
jsonFile = file;
|
||||
}
|
||||
|
||||
cellPackJson = state.build().toRoot()
|
||||
.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
|
||||
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
|
||||
}
|
||||
|
||||
const cellPackBuilder = cellPackJson
|
||||
@@ -469,7 +496,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
await handleHivRna(plugin, packings, params.baseUrl);
|
||||
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles: params.ingredients.files };
|
||||
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles };
|
||||
|
||||
const packing = await state.build()
|
||||
.to(cellPackBuilder.ref)
|
||||
@@ -497,8 +524,8 @@ const LoadCellPackModelParams = {
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['ExosomeModel.json', 'ExosomeModel'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
] as const),
|
||||
'file': PD.File({ accept: 'id' }),
|
||||
] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
|
||||
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
|
||||
}, { options: [['id', 'Id'], ['file', 'File']] }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
ingredients : PD.Group({
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { VolumeData, VolumeIsoValue } from './data';
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
|
||||
|
||||
export namespace Volume {
|
||||
@@ -86,7 +86,8 @@ export namespace Volume {
|
||||
boundaryHelper.radiusPosition(tmpBoundaryPos);
|
||||
}
|
||||
|
||||
return boundaryHelper.getSphere(boundingSphere);
|
||||
const bs = boundaryHelper.getSphere(boundingSphere);
|
||||
return Sphere3D.expand(bs, bs, Mat4.getMaxScaleOnAxis(transform) * 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,13 @@ const atomicDetail = StructureRepresentationPresetProvider({
|
||||
|
||||
const components = {
|
||||
all: await presetStaticComponent(plugin, structureCell, 'all'),
|
||||
branched: undefined
|
||||
};
|
||||
if (params.showCarbohydrateSymbol) {
|
||||
Object.assign(components, {
|
||||
branched: await presetStaticComponent(plugin, structureCell, 'branched', { label: 'Carbohydrate' }),
|
||||
});
|
||||
}
|
||||
|
||||
const { update, builder, typeParams, color } = reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
@@ -245,7 +251,7 @@ const atomicDetail = StructureRepresentationPresetProvider({
|
||||
};
|
||||
if (params.showCarbohydrateSymbol) {
|
||||
Object.assign(representations, {
|
||||
snfg3d: builder.buildRepresentation(update, components.all, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
|
||||
snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -114,10 +114,10 @@ export const Provider3dg: TrajectoryFormatProvider = {
|
||||
};
|
||||
|
||||
export const MolProvider: TrajectoryFormatProvider = {
|
||||
label: 'MOL',
|
||||
description: 'MOL',
|
||||
label: 'MOL/SDF',
|
||||
description: 'MOL/SDF',
|
||||
category: Category,
|
||||
stringExtensions: ['mol', 'sdf'],
|
||||
stringExtensions: ['mol', 'sdf', 'sd'],
|
||||
parse: directTrajectory(StateTransforms.Model.TrajectoryFromMOL),
|
||||
visuals: defaultVisuals
|
||||
};
|
||||
|
||||
@@ -394,63 +394,68 @@ const wholeResidues = StructureSelectionQuery('Whole Residues of Selection', MS.
|
||||
});
|
||||
|
||||
const StandardAminoAcids = [
|
||||
[['HIS'], 'HISTIDINE'],
|
||||
[['ARG'], 'ARGININE'],
|
||||
[['LYS'], 'LYSINE'],
|
||||
[['ILE'], 'ISOLEUCINE'],
|
||||
[['PHE'], 'PHENYLALANINE'],
|
||||
[['LEU'], 'LEUCINE'],
|
||||
[['TRP'], 'TRYPTOPHAN'],
|
||||
[['ALA'], 'ALANINE'],
|
||||
[['MET'], 'METHIONINE'],
|
||||
[['PRO'], 'PROLINE'],
|
||||
[['CYS'], 'CYSTEINE'],
|
||||
[['ASN'], 'ASPARAGINE'],
|
||||
[['VAL'], 'VALINE'],
|
||||
[['GLY'], 'GLYCINE'],
|
||||
[['SER'], 'SERINE'],
|
||||
[['GLN'], 'GLUTAMINE'],
|
||||
[['TYR'], 'TYROSINE'],
|
||||
[['ASP'], 'ASPARTIC ACID'],
|
||||
[['GLU'], 'GLUTAMIC ACID'],
|
||||
[['THR'], 'THREONINE'],
|
||||
[['SEC'], 'SELENOCYSTEINE'],
|
||||
[['PYL'], 'PYRROLYSINE'],
|
||||
[['UNK'], 'UNKNOWN'],
|
||||
[['HIS'], 'Histidine'],
|
||||
[['ARG'], 'Arginine'],
|
||||
[['LYS'], 'Lysine'],
|
||||
[['ILE'], 'Isoleucine'],
|
||||
[['PHE'], 'Phenylalanine'],
|
||||
[['LEU'], 'Leucine'],
|
||||
[['TRP'], 'Tryptophan'],
|
||||
[['ALA'], 'Alanine'],
|
||||
[['MET'], 'Methionine'],
|
||||
[['PRO'], 'Proline'],
|
||||
[['CYS'], 'Cysteine'],
|
||||
[['ASN'], 'Asparagine'],
|
||||
[['VAL'], 'Valine'],
|
||||
[['GLY'], 'Glycine'],
|
||||
[['SER'], 'Serine'],
|
||||
[['GLN'], 'Glutamine'],
|
||||
[['TYR'], 'Tyrosine'],
|
||||
[['ASP'], 'Aspartic Acid'],
|
||||
[['GLU'], 'Glutamic Acid'],
|
||||
[['THR'], 'Threonine'],
|
||||
[['SEC'], 'Selenocysteine'],
|
||||
[['PYL'], 'Pyrrolysine'],
|
||||
[['UNK'], 'Unknown'],
|
||||
].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
|
||||
|
||||
const StandardNucleicBases = [
|
||||
[['A', 'DA'], 'ADENOSINE'],
|
||||
[['C', 'DC'], 'CYTIDINE'],
|
||||
[['T', 'DT'], 'THYMIDINE'],
|
||||
[['G', 'DG'], 'GUANOSINE'],
|
||||
[['I', 'DI'], 'INOSINE'],
|
||||
[['U', 'DU'], 'URIDINE'],
|
||||
[['N', 'DN'], 'UNKNOWN'],
|
||||
[['A', 'DA'], 'Adenosine'],
|
||||
[['C', 'DC'], 'Cytidine'],
|
||||
[['T', 'DT'], 'Thymidine'],
|
||||
[['G', 'DG'], 'Guanosine'],
|
||||
[['I', 'DI'], 'Inosine'],
|
||||
[['U', 'DU'], 'Uridine'],
|
||||
[['N', 'DN'], 'Unknown'],
|
||||
].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
|
||||
|
||||
export function ResidueQuery([names, label]: [string[], string], category: string, priority = 0) {
|
||||
return StructureSelectionQuery(`${label} (${names.join(', ')})`, MS.struct.modifier.union([
|
||||
const description = names.length === 1 && !StandardResidues.has(names[0])
|
||||
? `[${names[0]}] ${label}`
|
||||
: `${label} (${names.join(', ')})`;
|
||||
return StructureSelectionQuery(description, MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
|
||||
})
|
||||
]), { category, priority, description: label });
|
||||
]), { category, priority, description });
|
||||
}
|
||||
|
||||
export function ElementSymbolQuery([names, label]: [string[], string], category: string, priority: number) {
|
||||
return StructureSelectionQuery(`${label} (${names.join(', ')})`, MS.struct.modifier.union([
|
||||
const description = `${label} (${names.join(', ')})`;
|
||||
return StructureSelectionQuery(description, MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'atom-test': MS.core.set.has([MS.set(...names), MS.acp('elementSymbol')])
|
||||
})
|
||||
]), { category, priority });
|
||||
]), { category, priority, description });
|
||||
}
|
||||
|
||||
export function EntityDescriptionQuery([description, label]: [string[], string], category: string, priority: number) {
|
||||
export function EntityDescriptionQuery([names, label]: [string[], string], category: string, priority: number) {
|
||||
const description = `${label}`;
|
||||
return StructureSelectionQuery(`${label}`, MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.list.equal([MS.list(...description), MS.ammp('entityDescription')])
|
||||
'entity-test': MS.core.list.equal([MS.list(...names), MS.ammp('entityDescription')])
|
||||
})
|
||||
]), { category, priority, description: description.join(', ') });
|
||||
]), { category, priority, description });
|
||||
}
|
||||
|
||||
const StandardResidues = SetUtils.unionMany(
|
||||
|
||||
@@ -88,21 +88,18 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// .msp-btn-icon, .msp-btn-icon-small {
|
||||
// svg {
|
||||
// display: inline-flex;
|
||||
// vertical-align: middle;
|
||||
// font-size: 1rem;
|
||||
// margin-bottom: 3px;
|
||||
// }
|
||||
// }
|
||||
|
||||
.msp-material-icon {
|
||||
svg {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
font-size: 1rem;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 3px;
|
||||
|
||||
fill: currentColor;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +267,7 @@ select[multiple],
|
||||
select[size] {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
// Reset height for `textarea`s
|
||||
textarea.msp-form-control {
|
||||
height: auto;
|
||||
|
||||
@@ -173,10 +173,10 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
|
||||
return <>
|
||||
<div className='msp-flex-row' style={{ background: 'none' }}>
|
||||
<PureSelectControl title={`Picking Level for selecting and highlighting`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
|
||||
<ToggleButton icon={Union} title={ActionHeader.get('add')} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={Subtract} title={ActionHeader.get('remove')} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={Intersect} title={ActionHeader.get('intersect')} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={SetSvg} title={ActionHeader.get('set')} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={Union} title={`${ActionHeader.get('add')}. Hold shift key to keep menu open.`} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={Subtract} title={`${ActionHeader.get('remove')}. Hold shift key to keep menu open.`} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={Intersect} title={`${ActionHeader.get('intersect')}. Hold shift key to keep menu open.`} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
|
||||
<ToggleButton icon={SetSvg} title={`${ActionHeader.get('set')}. Hold shift key to keep menu open.`} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
|
||||
|
||||
<ToggleButton icon={Brush} title='Color Selection' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }} />
|
||||
<ToggleButton icon={CubeSvg} title='Create Representation of Selection' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
|
||||
|
||||
@@ -148,7 +148,7 @@ function createElementText(ctx: VisualContext, structure: Structure, theme: Them
|
||||
const { label_atom_id, label_alt_id } = StructureProperties.atom;
|
||||
const { cumulativeUnitElementCount } = serialMapping;
|
||||
|
||||
const sizeTheme = PhysicalSizeTheme({}, {});
|
||||
const sizeTheme = PhysicalSizeTheme({}, { scale: 1 });
|
||||
|
||||
const count = structure.elementCount;
|
||||
const { elementScale } = props;
|
||||
|
||||
@@ -175,7 +175,7 @@ export function getUnitConformationAndRadius(structure: Structure, unit: Unit, p
|
||||
const boundary = unit === rootUnit ? unit.boundary : getBoundary(position);
|
||||
|
||||
const l = StructureElement.Location.create(structure, rootUnit);
|
||||
const sizeTheme = PhysicalSizeTheme({}, {});
|
||||
const sizeTheme = PhysicalSizeTheme({}, { scale: 1 });
|
||||
const radius = (index: number) => {
|
||||
l.element = index as ElementIndex;
|
||||
return sizeTheme.size(l);
|
||||
@@ -186,7 +186,7 @@ export function getUnitConformationAndRadius(structure: Structure, unit: Unit, p
|
||||
|
||||
export function getStructureConformationAndRadius(structure: Structure, ignoreHydrogens: boolean, traceOnly: boolean) {
|
||||
const l = StructureElement.Location.create(structure);
|
||||
const sizeTheme = PhysicalSizeTheme({}, {});
|
||||
const sizeTheme = PhysicalSizeTheme({}, { scale: 1 });
|
||||
|
||||
let xs: ArrayLike<number>;
|
||||
let ys: ArrayLike<number>;
|
||||
|
||||
@@ -173,7 +173,7 @@ export function _bundleLabel(bundle: Loci.Bundle<any>, options: LabelOptions) {
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
for (let i = 0, il = Math.min(...labels.map(l => l.length)); i < il; ++i) {
|
||||
for (let i = 0, il = Math.min(...labels.map(l => l.length)) - 1; i < il; ++i) {
|
||||
let areIdentical = true;
|
||||
for (let j = 1, jl = labels.length; j < jl; ++j) {
|
||||
if (labels[0][i] !== labels[j][i]) {
|
||||
|
||||
@@ -14,7 +14,9 @@ import { ThemeDataContext } from '../../mol-theme/theme';
|
||||
const DefaultSize = 1;
|
||||
const Description = 'Assigns a physical size, i.e. vdW radius for atoms or given radius for coarse spheres.';
|
||||
|
||||
export const PhysicalSizeThemeParams = {};
|
||||
export const PhysicalSizeThemeParams = {
|
||||
scale: PD.Numeric(1, { min: 0.1, max: 5, step: 0.1 })
|
||||
};
|
||||
export type PhysicalSizeThemeParams = typeof PhysicalSizeThemeParams
|
||||
export function getPhysicalSizeThemeParams(ctx: ThemeDataContext) {
|
||||
return PhysicalSizeThemeParams; // TODO return copy
|
||||
@@ -35,14 +37,16 @@ export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
|
||||
* i.e. vdw for atoms and radius for coarse spheres
|
||||
*/
|
||||
export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PD.Values<PhysicalSizeThemeParams>): SizeTheme<PhysicalSizeThemeParams> {
|
||||
const scale = props.scale === void 0 ? 1 : props.scale;
|
||||
|
||||
function size(location: Location): number {
|
||||
let size: number;
|
||||
if (StructureElement.Location.is(location)) {
|
||||
size = getPhysicalRadius(location.unit, location.element);
|
||||
size = scale * getPhysicalRadius(location.unit, location.element);
|
||||
} else if (Bond.isLocation(location)) {
|
||||
size = getPhysicalRadius(location.aUnit, location.aUnit.elements[location.aIndex]);
|
||||
size = scale * getPhysicalRadius(location.aUnit, location.aUnit.elements[location.aIndex]);
|
||||
} else {
|
||||
size = DefaultSize;
|
||||
size = scale * DefaultSize;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ export namespace ParamDefinition {
|
||||
getLabel(t: T): string
|
||||
}
|
||||
export function ObjectList<T>(element: For<T>, getLabel: (e: T) => string, info?: Info & { defaultValue?: T[], ctor?: () => T }): ObjectList<Normalize<T>> {
|
||||
return setInfo<ObjectList<Normalize<T>>>({ type: 'object-list', element: element as any as Params, getLabel, ctor: _defaultObjectListCtor, defaultValue: (info?.defaultValue) || [] }, info);
|
||||
return setInfo<ObjectList<Normalize<T>>>({ type: 'object-list', element: element as any as Params, getLabel, ctor: _defaultObjectListCtor, defaultValue: (info?.defaultValue) || [] }, info);
|
||||
}
|
||||
function _defaultObjectListCtor(this: ObjectList) { return getDefaultValues(this.element) as any; }
|
||||
|
||||
@@ -316,7 +316,7 @@ export namespace ParamDefinition {
|
||||
export type ValuesFor<T extends For<any>> = Normalize<{ [k in keyof T]: T[k]['defaultValue'] }>
|
||||
|
||||
type Optionals<P> = { [K in keyof P]-?: undefined extends P[K] ? K : never }[keyof P]
|
||||
type NonOptionals<P> = { [K in keyof P]-?: undefined extends P[K] ? never: K }[keyof P]
|
||||
type NonOptionals<P> = { [K in keyof P]-?: undefined extends P[K] ? never : K }[keyof P]
|
||||
export type Normalize<P> = Pick<P, NonOptionals<P>> & Partial<Pick<P, Optionals<P>>>
|
||||
export type For<P> = { [K in keyof P]-?: Base<P[K]> }
|
||||
export type Def<P> = { [K in keyof P]: Any }
|
||||
@@ -435,8 +435,8 @@ export namespace ParamDefinition {
|
||||
}
|
||||
|
||||
export function mergeParam(p: Any, a: any, b: any): any {
|
||||
if (a === undefined) return typeof b === 'object' ? { ...b } : b;
|
||||
if (b === undefined) return typeof a === 'object' ? { ...a } : a;
|
||||
if (a === undefined) return typeof b === 'object' && !Array.isArray(b) ? { ...b } : b;
|
||||
if (b === undefined) return typeof a === 'object' && !Array.isArray(a) ? { ...a } : a;
|
||||
|
||||
if (p.type === 'group') {
|
||||
return merge(p.params, a, b);
|
||||
@@ -448,7 +448,12 @@ export namespace ParamDefinition {
|
||||
name: v.name,
|
||||
params: mergeParam(map, u.params, v.params)
|
||||
};
|
||||
} else if (p.type === 'value') {
|
||||
return b;
|
||||
} else if (typeof a === 'object' && typeof b === 'object') {
|
||||
if (Array.isArray(b)) {
|
||||
return b;
|
||||
}
|
||||
return { ...a, ...b };
|
||||
} else {
|
||||
return b;
|
||||
|
||||
Reference in New Issue
Block a user