|
@@ -15,15 +15,25 @@ import { PluginStateObject as PSO, PluginStateObject } from 'mol-plugin/state/ob
|
|
|
import { AnimateModelIndex } from 'mol-plugin/state/animation/built-in';
|
|
|
import { StateBuilder, StateObject } from 'mol-state';
|
|
|
import { EvolutionaryConservation } from './annotation';
|
|
|
-import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo } from './helpers';
|
|
|
+import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
|
|
|
import { RxEventHelper } from 'mol-util/rx-event-helper';
|
|
|
import { ControlsWrapper } 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 { BuiltInStructureRepresentations } from 'mol-repr/structure/registry';
|
|
|
+import { BuiltInColorThemes } from 'mol-theme/color';
|
|
|
+import { BuiltInSizeThemes } from 'mol-theme/size';
|
|
|
+import { ColorNames } from 'mol-util/color/tables';
|
|
|
+// import { Vec3 } from 'mol-math/linear-algebra';
|
|
|
+// import { ParamDefinition } from 'mol-util/param-definition';
|
|
|
+// import { Text } from 'mol-geo/geometry/text/text';
|
|
|
require('mol-plugin/skin/light.scss')
|
|
|
|
|
|
class MolStarProteopediaWrapper {
|
|
|
- static VERSION_MAJOR = 2;
|
|
|
- static VERSION_MINOR = 0;
|
|
|
+ static VERSION_MAJOR = 3;
|
|
|
+ static VERSION_MINOR = 1;
|
|
|
|
|
|
private _ev = RxEventHelper.create();
|
|
|
|
|
@@ -33,9 +43,14 @@ class MolStarProteopediaWrapper {
|
|
|
|
|
|
plugin: PluginContext;
|
|
|
|
|
|
- init(target: string | HTMLElement) {
|
|
|
+ init(target: string | HTMLElement, options?: {
|
|
|
+ customColorList?: number[]
|
|
|
+ }) {
|
|
|
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
|
|
|
...DefaultPluginSpec,
|
|
|
+ animations: [
|
|
|
+ AnimateModelIndex
|
|
|
+ ],
|
|
|
layout: {
|
|
|
initial: {
|
|
|
isExpanded: false,
|
|
@@ -47,6 +62,9 @@ class MolStarProteopediaWrapper {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+ const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
|
|
|
+
|
|
|
+ this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
|
|
|
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.Descriptor.name, EvolutionaryConservation.colorTheme!);
|
|
|
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider);
|
|
|
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider);
|
|
@@ -66,43 +84,86 @@ class MolStarProteopediaWrapper {
|
|
|
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
|
|
|
|
|
|
return parsed
|
|
|
- .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }, { ref: 'model' });
|
|
|
+ .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }, { ref: StateElements.Model });
|
|
|
}
|
|
|
|
|
|
private structure(assemblyId: string) {
|
|
|
- const model = this.state.build().to('model');
|
|
|
+ const model = this.state.build().to(StateElements.Model);
|
|
|
+
|
|
|
+ const s = model
|
|
|
+ .apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
|
|
+ .apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: StateElements.Assembly });
|
|
|
|
|
|
- return model
|
|
|
- .apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: 'props', props: { isGhost: false } })
|
|
|
- .apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
|
|
|
+ s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
|
|
|
+ s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }, { ref: StateElements.Het });
|
|
|
+ s.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }, { ref: StateElements.Water });
|
|
|
+
|
|
|
+ return s;
|
|
|
}
|
|
|
|
|
|
- private visual(ref: string, style?: RepresentationStyle) {
|
|
|
- const structure = this.getObj<PluginStateObject.Molecule.Structure>(ref);
|
|
|
+ private visual(_style?: RepresentationStyle, partial?: boolean) {
|
|
|
+ const structure = this.getObj<PluginStateObject.Molecule.Structure>(StateElements.Assembly);
|
|
|
if (!structure) return;
|
|
|
|
|
|
- const root = this.state.build().to(ref);
|
|
|
-
|
|
|
- root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'sequence' })
|
|
|
- .apply(StateTransforms.Representation.StructureRepresentation3D,
|
|
|
- StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
|
|
- (style && style.sequence && style.sequence.kind) || 'cartoon',
|
|
|
- (style && style.sequence && style.sequence.coloring) || 'unit-index', structure),
|
|
|
- { ref: 'sequence-visual' });
|
|
|
- root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }, { ref: 'het' })
|
|
|
- .apply(StateTransforms.Representation.StructureRepresentation3D,
|
|
|
- StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
|
|
- (style && style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
|
|
|
- (style && style.hetGroups && style.hetGroups.coloring), structure),
|
|
|
- { ref: 'het-visual' });
|
|
|
- root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }, { ref: 'water' })
|
|
|
- .apply(StateTransforms.Representation.StructureRepresentation3D,
|
|
|
- StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
|
|
- (style && style.water && style.water.kind) || 'ball-and-stick',
|
|
|
- (style && style.water && style.water.coloring), structure, { alpha: 0.51 }),
|
|
|
- { ref: 'water-visual' });
|
|
|
-
|
|
|
- return root;
|
|
|
+ const style = _style || { };
|
|
|
+
|
|
|
+ const update = this.state.build();
|
|
|
+
|
|
|
+ if (!partial || (partial && style.sequence)) {
|
|
|
+ const root = update.to(StateElements.Sequence);
|
|
|
+ if (style.sequence && style.sequence.hide) {
|
|
|
+ root.delete(StateElements.SequenceVisual);
|
|
|
+ } else {
|
|
|
+ root.applyOrUpdate(StateElements.SequenceVisual, StateTransforms.Representation.StructureRepresentation3D,
|
|
|
+ StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
|
|
+ (style.sequence && style.sequence.kind) || 'cartoon',
|
|
|
+ (style.sequence && style.sequence.coloring) || 'unit-index', structure));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!partial || (partial && style.hetGroups)) {
|
|
|
+ const root = update.to(StateElements.Het);
|
|
|
+ if (style.hetGroups && style.hetGroups.hide) {
|
|
|
+ root.delete(StateElements.HetVisual);
|
|
|
+ } else {
|
|
|
+ if (style.hetGroups && style.hetGroups.hide) {
|
|
|
+ root.delete(StateElements.HetVisual);
|
|
|
+ } else {
|
|
|
+ root.applyOrUpdate(StateElements.HetVisual, StateTransforms.Representation.StructureRepresentation3D,
|
|
|
+ StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
|
|
+ (style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
|
|
|
+ (style.hetGroups && style.hetGroups.coloring), structure));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!partial || (partial && style.snfg3d)) {
|
|
|
+ const root = update.to(StateElements.Het);
|
|
|
+ if (style.hetGroups && style.hetGroups.hide) {
|
|
|
+ root.delete(StateElements.HetVisual);
|
|
|
+ } else {
|
|
|
+ if (style.snfg3d && style.snfg3d.hide) {
|
|
|
+ root.delete(StateElements.Het3DSNFG);
|
|
|
+ } else {
|
|
|
+ root.applyOrUpdate(StateElements.Het3DSNFG, StateTransforms.Representation.StructureRepresentation3D,
|
|
|
+ StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin, 'carbohydrate', void 0, structure));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!partial || (partial && style.water)) {
|
|
|
+ const root = update.to(StateElements.Water);
|
|
|
+ if (style.water && style.water.hide) {
|
|
|
+ root.delete(StateElements.WaterVisual);
|
|
|
+ } else {
|
|
|
+ root.applyOrUpdate(StateElements.WaterVisual, StateTransforms.Representation.StructureRepresentation3D,
|
|
|
+ StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
|
|
+ (style.water && style.water.kind) || 'ball-and-stick',
|
|
|
+ (style.water && style.water.coloring), structure, { alpha: 0.51 }));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return update;
|
|
|
}
|
|
|
|
|
|
private getObj<T extends StateObject>(ref: string): T['data'] {
|
|
@@ -134,7 +195,7 @@ class MolStarProteopediaWrapper {
|
|
|
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
|
|
|
loadType = 'full';
|
|
|
} else if (this.loadedParams.url === url) {
|
|
|
- if (state.select('asm').length > 0) loadType = 'update';
|
|
|
+ if (state.select(StateElements.Assembly).length > 0) loadType = 'update';
|
|
|
}
|
|
|
|
|
|
if (loadType === 'full') {
|
|
@@ -146,18 +207,18 @@ class MolStarProteopediaWrapper {
|
|
|
await this.applyState(structureTree);
|
|
|
} else {
|
|
|
const tree = state.build();
|
|
|
- tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
|
|
|
+ tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
|
|
|
await this.applyState(tree);
|
|
|
}
|
|
|
|
|
|
await this.updateStyle(representationStyle);
|
|
|
|
|
|
this.loadedParams = { url, format, assemblyId };
|
|
|
- PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
|
|
+ Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }));
|
|
|
}
|
|
|
|
|
|
- async updateStyle(style?: RepresentationStyle) {
|
|
|
- const tree = this.visual('asm', style);
|
|
|
+ async updateStyle(style?: RepresentationStyle, partial?: boolean) {
|
|
|
+ const tree = this.visual(style, partial);
|
|
|
if (!tree) return;
|
|
|
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
|
|
|
}
|
|
@@ -186,7 +247,7 @@ class MolStarProteopediaWrapper {
|
|
|
|
|
|
coloring = {
|
|
|
evolutionaryConservation: async () => {
|
|
|
- await this.updateStyle({ sequence: { kind: 'spacefill' } });
|
|
|
+ await this.updateStyle({ sequence: { kind: 'spacefill' } }, true);
|
|
|
|
|
|
const state = this.state;
|
|
|
|
|
@@ -194,7 +255,7 @@ class MolStarProteopediaWrapper {
|
|
|
const tree = state.build();
|
|
|
const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
|
|
|
|
|
|
- tree.to('sequence-visual').update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
|
|
+ tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
|
|
// for (const v of visuals) {
|
|
|
// }
|
|
|
|
|
@@ -202,6 +263,69 @@ class MolStarProteopediaWrapper {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ hetGroups = {
|
|
|
+ reset: () => {
|
|
|
+ const update = this.state.build().delete(StateElements.HetGroupFocus);
|
|
|
+ PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
|
|
|
+ PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
|
|
+ },
|
|
|
+ focusFirst: async (resn: string) => {
|
|
|
+ if (!this.state.transforms.has(StateElements.Assembly)) return;
|
|
|
+
|
|
|
+ // const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
|
|
|
+
|
|
|
+ const update = this.state.build();
|
|
|
+
|
|
|
+ update.delete(StateElements.HetGroupFocus);
|
|
|
+
|
|
|
+ const surroundings = MS.struct.modifier.includeSurroundings({
|
|
|
+ 0: MS.struct.filter.first([
|
|
|
+ MS.struct.generator.atomGroups({
|
|
|
+ 'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), resn]),
|
|
|
+ 'group-by': MS.struct.atomProperty.macromolecular.residueKey()
|
|
|
+ })
|
|
|
+ ]),
|
|
|
+ radius: 5,
|
|
|
+ 'as-whole-residues': true
|
|
|
+ });
|
|
|
+
|
|
|
+ const sel = update.to(StateElements.Assembly)
|
|
|
+ .apply(StateTransforms.Model.StructureSelection, { label: resn, query: surroundings }, { ref: StateElements.HetGroupFocus });
|
|
|
+
|
|
|
+ sel.apply(StateTransforms.Representation.StructureRepresentation3D, this.createSurVisualParams());
|
|
|
+ // sel.apply(StateTransforms.Representation.StructureLabels3D, {
|
|
|
+ // target: { name: 'residues', params: { } },
|
|
|
+ // options: {
|
|
|
+ // ...ParamDefinition.getDefaultValues(Text.Params),
|
|
|
+ // background: true,
|
|
|
+ // backgroundMargin: 0.2,
|
|
|
+ // backgroundColor: ColorNames.snow,
|
|
|
+ // backgroundOpacity: 0.9,
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+
|
|
|
+ await PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
|
|
|
+
|
|
|
+ const focus = (this.state.select(StateElements.HetGroupFocus)[0].obj as PluginStateObject.Molecule.Structure).data;
|
|
|
+ const sphere = focus.boundary.sphere;
|
|
|
+ // const asmCenter = asm.boundary.sphere.center;
|
|
|
+ // const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
|
|
|
+ // Vec3.normalize(position, position);
|
|
|
+ // Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
|
|
|
+ const snapshot = this.plugin.canvas3d.camera.getFocus(sphere.center, 0.75 * sphere.radius);
|
|
|
+ PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private createSurVisualParams() {
|
|
|
+ const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
|
|
|
+ return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
|
|
|
+ repr: BuiltInStructureRepresentations['ball-and-stick'],
|
|
|
+ color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
|
|
|
+ size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
snapshot = {
|
|
|
get: () => {
|
|
|
return this.plugin.state.getSnapshot();
|