From 75097c670e973e673749a3aae31445d0f4b45dc5 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Wed, 29 May 2019 13:03:19 +0200 Subject: [PATCH] wip superposition --- src/apps/basic-wrapper/helpers.ts | 67 +++++++++++++++++++ src/apps/basic-wrapper/index.html | 9 ++- src/apps/basic-wrapper/index.ts | 15 ++++- src/apps/basic-wrapper/superposition.ts | 55 +++++++++++++++ src/mol-math/geometry/symmetry-operator.ts | 6 +- .../structure/structure/structure.ts | 2 +- src/mol-plugin/state/transforms/model.ts | 23 ++++++- 7 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 src/apps/basic-wrapper/helpers.ts create mode 100644 src/apps/basic-wrapper/superposition.ts diff --git a/src/apps/basic-wrapper/helpers.ts b/src/apps/basic-wrapper/helpers.ts new file mode 100644 index 000000000..bbe9e92ab --- /dev/null +++ b/src/apps/basic-wrapper/helpers.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Mat4, Vec3 } from 'mol-math/linear-algebra'; +import { PluginContext } from 'mol-plugin/context'; +import { PluginStateObject as PSO } from 'mol-plugin/state/objects'; +import { StateTransforms } from 'mol-plugin/state/transforms'; +import { StructureRepresentation3DHelpers } from 'mol-plugin/state/transforms/representation'; +import { MolScriptBuilder as MS } from 'mol-script/language/builder'; +import { StateBuilder } from 'mol-state'; +type SupportedFormats = 'cif' | 'pdb' + +export namespace StateHelper { + export function download(b: StateBuilder.To<PSO.Root>, url: string, ref?: string) { + return b.apply(StateTransforms.Data.Download, { url, isBinary: false }, { ref }); + } + + export function getModel(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, modelIndex = 0) { + const parsed = format === 'cif' + ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif) + : b.apply(StateTransforms.Model.TrajectoryFromPDB); + + return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex }); + } + + export function structure(b: StateBuilder.To<PSO.Molecule.Model>) { + return b.apply(StateTransforms.Model.StructureFromModel, { tags: 'struct' }) + }; + + export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) { + const query = MS.struct.generator.atomGroups({ + 'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id]) + }) + return b.apply(StateTransforms.Model.StructureSelection, { query, label: `Chain ${auth_asym_id}` }); + } + + export function identityTransform(b: StateBuilder.To<PSO.Molecule.Structure>, m: Mat4) { + return b.apply(StateTransforms.Model.TransformStructureConformation, + { axis: Vec3.create(1, 0, 0), angle: 0, translation: Vec3.zero() }, + { tags: 'transform' }); + } + + export function transform(b: StateBuilder.To<PSO.Molecule.Structure>, matrix: Mat4) { + return b.apply(StateTransforms.Model.TransformStructureConformationByMatrix, { matrix }, { tags: 'transform' }); + } + + export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) { + return b.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: id || 'deposited' }, { tags: 'asm' }) + } + + export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) { + visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }) + .apply(StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'), { tags: 'seq-visual' }); + visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }) + .apply(StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'), { tags: 'het-visual' }); + // visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }) + // .apply(StateTransforms.Representation.StructureRepresentation3D, + // StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }), { tags: 'water-visual' }); + return visualRoot; + } + +} \ No newline at end of file diff --git a/src/apps/basic-wrapper/index.html b/src/apps/basic-wrapper/index.html index efa0b8c9f..1d68db266 100644 --- a/src/apps/basic-wrapper/index.html +++ b/src/apps/basic-wrapper/index.html @@ -75,8 +75,9 @@ BasicMolStarWrapper.init('app' /** or document.getElementById('app') */); BasicMolStarWrapper.setBackground(0xffffff); - BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId }); - BasicMolStarWrapper.toggleSpin(); + // BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId }); + // BasicMolStarWrapper.toggleSpin(); + addControl('Load Asym Unit', () => BasicMolStarWrapper.load({ url: url, format: format })); addControl('Load Assembly', () => BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId })); @@ -104,6 +105,10 @@ addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes()); + addHeader('Tests'); + + addControl('Static Super', () => BasicMolStarWrapper.tests.staticSuperposition()); + //////////////////////////////////////////////////////// function addControl(label, action) { diff --git a/src/apps/basic-wrapper/index.ts b/src/apps/basic-wrapper/index.ts index 17da1682a..fb4392f3d 100644 --- a/src/apps/basic-wrapper/index.ts +++ b/src/apps/basic-wrapper/index.ts @@ -15,7 +15,8 @@ import { PluginStateObject as PSO } from 'mol-plugin/state/objects'; import { AnimateModelIndex } from 'mol-plugin/state/animation/built-in'; import { StateBuilder } from 'mol-state'; import { StripedResidues } from './coloring'; -import { BasicWrapperControls } from './controls'; +// import { BasicWrapperControls } from './controls'; +import { StaticSuperpositionTestData, buildStaticSuperposition } from './superposition'; require('mol-plugin/skin/light.scss') type SupportedFormats = 'cif' | 'pdb' @@ -33,8 +34,8 @@ class BasicWrapper { showControls: false }, controls: { - left: 'none', - right: BasicWrapperControls + // left: 'none', + // right: BasicWrapperControls } } }); @@ -140,6 +141,14 @@ class BasicWrapper { await PluginCommands.State.Update.dispatch(this.plugin, { state, tree }); } } + + tests = { + staticSuperposition: async () => { + const state = this.plugin.state.dataState; + const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData); + await PluginCommands.State.Update.dispatch(this.plugin, { state, tree }); + } + } } (window as any).BasicMolStarWrapper = new BasicWrapper(); \ No newline at end of file diff --git a/src/apps/basic-wrapper/superposition.ts b/src/apps/basic-wrapper/superposition.ts new file mode 100644 index 000000000..1961aab48 --- /dev/null +++ b/src/apps/basic-wrapper/superposition.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO: move to an "example" + +import { PluginContext } from 'mol-plugin/context'; +import { Mat4 } from 'mol-math/linear-algebra'; +import { StateHelper } from './helpers'; + +export type SuperpositionTestInput = { + pdbId: string, + auth_asym_id: string, + matrix: Mat4 +}[]; + +// function getAxisAngleTranslation(m: Mat4) { +// const translation = Mat4.getTranslation(Vec3.zero(), m); +// const axis = Vec3.zero(); +// const angle = 180 / Math.PI * Quat.getAxisAngle(axis, Mat4.getRotation(Quat.zero(), m)); +// return { translation, axis, angle }; +// } + +export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionTestInput) { + const b = ctx.state.dataState.build().toRoot(); + for (const s of src) { + StateHelper.visual(ctx, + StateHelper.transform( + StateHelper.selectChain( + StateHelper.structure( + StateHelper.getModel(StateHelper.download(b, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`), 'cif')), + s.auth_asym_id + ), + s.matrix + ) + ); + } + return b; +} + +export const StaticSuperpositionTestData: SuperpositionTestInput = [ + { pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity() }, + { pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows( + [[0.406, 0.879, 0.248, -200.633], + [0.693, -0.473, 0.544, 73.403], + [0.596, -0.049, -0.802, -14.209], + [0, 0, 0, 1]] )}, + { pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows( + [[-0.053, -0.077, 0.996, -45.633], + [-0.312, 0.949, 0.057, -12.255], + [-0.949, -0.307, -0.074, 53.562], + [0, 0, 0, 1]] )} +]; \ No newline at end of file diff --git a/src/mol-math/geometry/symmetry-operator.ts b/src/mol-math/geometry/symmetry-operator.ts index 6f3a71fe5..847e967ce 100644 --- a/src/mol-math/geometry/symmetry-operator.ts +++ b/src/mol-math/geometry/symmetry-operator.ts @@ -33,13 +33,13 @@ namespace SymmetryOperator { export const DefaultName = '1_555' export const Default: SymmetryOperator = create(DefaultName, Mat4.identity(), { id: '', operList: [] }); - const RotationEpsilon = 0.0001; + export const RotationTranslationEpsilon = 0.005; export function create(name: string, matrix: Mat4, assembly: SymmetryOperator['assembly'], ncsId?: string, hkl?: Vec3): SymmetryOperator { const _hkl = hkl ? Vec3.clone(hkl) : Vec3.zero(); ncsId = ncsId || '' if (Mat4.isIdentity(matrix)) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, ncsId }; - if (!Mat4.isRotationAndTranslation(matrix, RotationEpsilon)) throw new Error(`Symmetry operator (${name}) must be a composition of rotation and translation.`); + if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) throw new Error(`Symmetry operator (${name}) must be a composition of rotation and translation.`); return { name, assembly, matrix, inverse: Mat4.invert(Mat4.zero(), matrix), isIdentity: false, hkl: _hkl, ncsId }; } @@ -51,7 +51,7 @@ namespace SymmetryOperator { } } Mat4.setTranslation(matrix, offset); - return Mat4.isRotationAndTranslation(matrix, RotationEpsilon); + return Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon); } export function ofRotationAndOffset(name: string, rot: Mat3, offset: Vec3, ncsId?: string) { diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 5243b670f..d2e50fcee 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -428,7 +428,7 @@ namespace Structure { export function transform(s: Structure, transform: Mat4) { if (Mat4.isIdentity(transform)) return s; - if (!Mat4.isRotationAndTranslation(transform)) throw new Error('Only rotation/translation combination can be applied.'); + if (!Mat4.isRotationAndTranslation(transform, SymmetryOperator.RotationTranslationEpsilon)) throw new Error('Only rotation/translation combination can be applied.'); const units: Unit[] = []; for (const u of s.units) { diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 0bc62ac0f..a114d8873 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -36,7 +36,8 @@ export { ModelFromTrajectory }; export { StructureFromModel }; export { StructureAssemblyFromModel }; export { StructureSymmetryFromModel }; -export { TransformStructureConformation } +export { TransformStructureConformation }; +export { TransformStructureConformationByMatrix }; export { StructureSelection }; export { UserStructureSelection }; export { StructureComplexElement }; @@ -307,6 +308,26 @@ const TransformStructureConformation = PluginStateTransform.BuiltIn({ } }); +type TransformStructureConformationByMatrix = typeof TransformStructureConformation +const TransformStructureConformationByMatrix = PluginStateTransform.BuiltIn({ + name: 'transform-structure-conformation-by-matrix', + display: { name: 'Transform Conformation' }, + from: SO.Molecule.Structure, + to: SO.Molecule.Structure, + params: { + matrix: PD.Value<Mat4>(Mat4.identity(), { isHidden: true }) + } +})({ + canAutoUpdate() { + return true; + }, + apply({ a, params }) { + const s = Structure.transform(a.data, params.matrix); + const props = { label: `${a.label}`, description: `Transformed` }; + return new SO.Molecule.Structure(s, props); + } +}); + type StructureSelection = typeof StructureSelection const StructureSelection = PluginStateTransform.BuiltIn({ name: 'structure-selection', -- GitLab