Skip to content
Snippets Groups Projects
Commit 5b4c6743 authored by David Sehnal's avatar David Sehnal
Browse files

GlobalModelTransformInfo

- support in volume streaming
- export in ModelServer if transform param is present
parent 99a39069
No related branches found
No related tags found
No related merge requests found
...@@ -18,6 +18,7 @@ import { AtomSiteAnisotrop } from './property/anisotropic'; ...@@ -18,6 +18,7 @@ import { AtomSiteAnisotrop } from './property/anisotropic';
import { ComponentBond } from './property/bonds/chem_comp'; import { ComponentBond } from './property/bonds/chem_comp';
import { StructConn } from './property/bonds/struct_conn'; import { StructConn } from './property/bonds/struct_conn';
import { Trajectory } from '../../mol-model/structure'; import { Trajectory } from '../../mol-model/structure';
import { GlobalModelTransformInfo } from '../../mol-model/structure/model/properties/global-transform';
function modelSymmetryFromMmcif(model: Model) { function modelSymmetryFromMmcif(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return; if (!MmcifFormat.is(model.sourceData)) return;
...@@ -69,6 +70,8 @@ function structConnFromMmcif(model: Model) { ...@@ -69,6 +70,8 @@ function structConnFromMmcif(model: Model) {
} }
StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif); StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif);
GlobalModelTransformInfo.Provider.formatRegistry.add('mmCIF', GlobalModelTransformInfo.fromMmCif, GlobalModelTransformInfo.hasData);
// //
export { MmcifFormat }; export { MmcifFormat };
......
...@@ -132,7 +132,8 @@ function getCustomPropCategories(customProp: CustomPropertyDescriptor, ctx: CifE ...@@ -132,7 +132,8 @@ function getCustomPropCategories(customProp: CustomPropertyDescriptor, ctx: CifE
type encode_mmCIF_categories_Params = { type encode_mmCIF_categories_Params = {
skipCategoryNames?: Set<string>, skipCategoryNames?: Set<string>,
exportCtx?: CifExportContext, exportCtx?: CifExportContext,
copyAllCategories?: boolean copyAllCategories?: boolean,
customProperties?: CustomPropertyDescriptor[]
} }
/** Doesn't start a data block */ /** Doesn't start a data block */
...@@ -144,7 +145,7 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures: ...@@ -144,7 +145,7 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures:
const ctx: CifExportContext = params?.exportCtx || CifExportContext.create(structures); const ctx: CifExportContext = params?.exportCtx || CifExportContext.create(structures);
if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) { if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) {
encode_mmCIF_categories_copyAll(encoder, ctx); encode_mmCIF_categories_copyAll(encoder, ctx, params);
} else { } else {
encode_mmCIF_categories_default(encoder, ctx, params); encode_mmCIF_categories_default(encoder, ctx, params);
} }
...@@ -168,6 +169,14 @@ function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExp ...@@ -168,6 +169,14 @@ function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExp
} }
} }
if (params?.customProperties) {
for (const customProp of params?.customProperties) {
for (const [cat, propCtx] of getCustomPropCategories(customProp, ctx, _params)) {
encoder.writeCategory(cat, propCtx);
}
}
}
for (const s of ctx.structures) { for (const s of ctx.structures) {
if (!s.hasCustomProperties) continue; if (!s.hasCustomProperties) continue;
for (const customProp of s.customPropertyDescriptors.all) { for (const customProp of s.customPropertyDescriptors.all) {
...@@ -178,7 +187,7 @@ function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExp ...@@ -178,7 +187,7 @@ function encode_mmCIF_categories_default(encoder: CifWriter.Encoder, ctx: CifExp
} }
} }
function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExportContext) { function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExportContext, params?: encode_mmCIF_categories_Params) {
const providedCategories = new Map<string, CifExportCategoryInfo>(); const providedCategories = new Map<string, CifExportCategoryInfo>();
for (const cat of Categories) { for (const cat of Categories) {
...@@ -188,11 +197,20 @@ function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExp ...@@ -188,11 +197,20 @@ function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExp
const mapping = atom_site_operator_mapping(ctx); const mapping = atom_site_operator_mapping(ctx);
if (mapping) providedCategories.set(mapping[0].name, mapping); if (mapping) providedCategories.set(mapping[0].name, mapping);
const _params = params || { };
for (const customProp of ctx.firstModel.customProperties.all) { for (const customProp of ctx.firstModel.customProperties.all) {
for (const info of getCustomPropCategories(customProp, ctx)) { for (const info of getCustomPropCategories(customProp, ctx, _params)) {
providedCategories.set(info[0].name, info);
}
}
if (params?.customProperties) {
for (const customProp of params?.customProperties) {
for (const info of getCustomPropCategories(customProp, ctx, _params)) {
providedCategories.set(info[0].name, info); providedCategories.set(info[0].name, info);
} }
} }
}
for (const s of ctx.structures) { for (const s of ctx.structures) {
if (!s.hasCustomProperties) continue; if (!s.hasCustomProperties) continue;
......
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// TODO add access to things like MOL2 charge ...
\ No newline at end of file
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Mat4, Tensor } from '../../../../mol-math/linear-algebra';
import { FormatPropertyProvider } from '../../../../mol-model-formats/structure/common/property';
import { CustomPropertyDescriptor } from '../../../custom-property';
import { CifExportContext } from '../../structure';
import { Model } from '../model';
import { Column, Table } from '../../../../mol-data/db';
import { CifWriter } from '../../../../mol-io/writer/cif';
import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
import { toTable } from '../../../../mol-io/reader/cif/schema';
export namespace GlobalModelTransformInfo {
const CategoryName = 'molstar_global_model_transform_info' as const;
export const Schema = {
[CategoryName]: {
matrix: Column.Schema.Matrix(4, 4, Column.Schema.float)
}
};
export type Schema = typeof Schema
export const Descriptor = CustomPropertyDescriptor({
name: CategoryName,
cifExport: {
categories: [{
name: CategoryName,
instance(ctx: CifExportContext) {
const mat = get(ctx.firstModel);
if (!mat) return CifWriter.Category.Empty;
const table = Table.ofRows(Schema.molstar_global_model_transform_info, [{ matrix: mat as unknown as Tensor.Data }]);
return CifWriter.Category.ofTable(table);
}
}],
prefix: 'molstar'
}
});
export const Provider = FormatPropertyProvider.create<Mat4>(Descriptor);
export function attach(model: Model, matrix: Mat4) {
if (!model.customProperties.has(Descriptor)) {
model.customProperties.add(Descriptor);
}
Provider.set(model, matrix);
}
export function get(model: Model): Mat4 | undefined {
return Provider.get(model);
}
export function fromMmCif(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return;
const cat = model.sourceData.data.frame.categories[CategoryName];
if (!cat) return;
const table = toTable(Schema[CategoryName], cat);
if (table._rowCount === 0) return;
return table.matrix.value(0) as unknown as Mat4;
}
export function hasData(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return false;
const cat = model.sourceData.data.frame.categories[CategoryName];
return !!cat && cat.rowCount > 0;
}
export function writeMmCif(encoder: CifWriter.Encoder, matrix: Mat4) {
encoder.writeCategory({
name: CategoryName,
instance() {
const table = Table.ofRows(Schema.molstar_global_model_transform_info, [{ matrix: matrix as unknown as Tensor.Data }]);
return CifWriter.Category.ofTable(table);
}
});
}
}
\ No newline at end of file
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
import { UniqueArray } from '../../../../mol-data/generic'; import { UniqueArray } from '../../../../mol-data/generic';
import { OrderedSet, SortedArray, Interval } from '../../../../mol-data/int'; import { OrderedSet, SortedArray, Interval } from '../../../../mol-data/int';
import { Vec3 } from '../../../../mol-math/linear-algebra'; import { Mat4, Vec3 } from '../../../../mol-math/linear-algebra';
import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder'; import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
import Structure from '../structure'; import Structure from '../structure';
import Unit from '../unit'; import Unit from '../unit';
...@@ -489,7 +489,7 @@ export namespace Loci { ...@@ -489,7 +489,7 @@ export namespace Loci {
const boundaryHelper = new BoundaryHelper('98'); const boundaryHelper = new BoundaryHelper('98');
const tempPosBoundary = Vec3(); const tempPosBoundary = Vec3();
export function getBoundary(loci: Loci): Boundary { export function getBoundary(loci: Loci, transform?: Mat4): Boundary {
boundaryHelper.reset(); boundaryHelper.reset();
for (const e of loci.elements) { for (const e of loci.elements) {
...@@ -499,6 +499,7 @@ export namespace Loci { ...@@ -499,6 +499,7 @@ export namespace Loci {
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) { for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
const eI = elements[OrderedSet.getAt(indices, i)]; const eI = elements[OrderedSet.getAt(indices, i)];
pos(eI, tempPosBoundary); pos(eI, tempPosBoundary);
if (transform) Vec3.transformMat4(tempPosBoundary, tempPosBoundary, transform);
boundaryHelper.includePositionRadius(tempPosBoundary, r(eI)); boundaryHelper.includePositionRadius(tempPosBoundary, r(eI));
} }
} }
...@@ -510,6 +511,7 @@ export namespace Loci { ...@@ -510,6 +511,7 @@ export namespace Loci {
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) { for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
const eI = elements[OrderedSet.getAt(indices, i)]; const eI = elements[OrderedSet.getAt(indices, i)];
pos(eI, tempPosBoundary); pos(eI, tempPosBoundary);
if (transform) Vec3.transformMat4(tempPosBoundary, tempPosBoundary, transform);
boundaryHelper.radiusPositionRadius(tempPosBoundary, r(eI)); boundaryHelper.radiusPositionRadius(tempPosBoundary, r(eI));
} }
} }
......
...@@ -10,7 +10,7 @@ import { PluginStateObject } from '../../../../mol-plugin-state/objects'; ...@@ -10,7 +10,7 @@ import { PluginStateObject } from '../../../../mol-plugin-state/objects';
import { Volume, Grid } from '../../../../mol-model/volume'; import { Volume, Grid } from '../../../../mol-model/volume';
import { VolumeServerHeader, VolumeServerInfo } from './model'; import { VolumeServerHeader, VolumeServerInfo } from './model';
import { Box3D } from '../../../../mol-math/geometry'; import { Box3D } from '../../../../mol-math/geometry';
import { Vec3 } from '../../../../mol-math/linear-algebra'; import { Mat4, Vec3 } from '../../../../mol-math/linear-algebra';
import { Color } from '../../../../mol-util/color'; import { Color } from '../../../../mol-util/color';
import { PluginBehavior } from '../../behavior'; import { PluginBehavior } from '../../behavior';
import { LRUCache } from '../../../../mol-util/lru-cache'; import { LRUCache } from '../../../../mol-util/lru-cache';
...@@ -23,6 +23,7 @@ import { StructureElement, Structure } from '../../../../mol-model/structure'; ...@@ -23,6 +23,7 @@ import { StructureElement, Structure } from '../../../../mol-model/structure';
import { PluginContext } from '../../../context'; import { PluginContext } from '../../../context';
import { EmptyLoci, Loci, isEmptyLoci } from '../../../../mol-model/loci'; import { EmptyLoci, Loci, isEmptyLoci } from '../../../../mol-model/loci';
import { Asset } from '../../../../mol-util/assets'; import { Asset } from '../../../../mol-util/assets';
import { GlobalModelTransformInfo } from '../../../../mol-model/structure/model/properties/global-transform';
export class VolumeStreaming extends PluginStateObject.CreateBehavior<VolumeStreaming.Behavior>({ name: 'Volume Streaming' }) { } export class VolumeStreaming extends PluginStateObject.CreateBehavior<VolumeStreaming.Behavior>({ name: 'Volume Streaming' }) { }
...@@ -302,6 +303,7 @@ export namespace VolumeStreaming { ...@@ -302,6 +303,7 @@ export namespace VolumeStreaming {
} }
} }
private _invTransform: Mat4 = Mat4();
private getBoxFromLoci(loci: StructureElement.Loci | EmptyLoci): Box3D { private getBoxFromLoci(loci: StructureElement.Loci | EmptyLoci): Box3D {
if (Loci.isEmpty(loci)) { if (Loci.isEmpty(loci)) {
return Box3D(); return Box3D();
...@@ -312,11 +314,16 @@ export namespace VolumeStreaming { ...@@ -312,11 +314,16 @@ export namespace VolumeStreaming {
const root = this.getStructureRoot(); const root = this.getStructureRoot();
if (!root || root.obj?.data !== parent.obj?.data) return Box3D(); if (!root || root.obj?.data !== parent.obj?.data) return Box3D();
const transform = GlobalModelTransformInfo.get(root.obj?.data.models[0]!);
if (transform) Mat4.invert(this._invTransform, transform);
const extendedLoci = StructureElement.Loci.extendToWholeResidues(loci); const extendedLoci = StructureElement.Loci.extendToWholeResidues(loci);
const box = StructureElement.Loci.getBoundary(extendedLoci).box; const box = StructureElement.Loci.getBoundary(extendedLoci, transform && !Number.isNaN(this._invTransform[0]) ? this._invTransform : void 0).box;
if (StructureElement.Loci.size(extendedLoci) === 1) { if (StructureElement.Loci.size(extendedLoci) === 1) {
Box3D.expand(box, box, Vec3.create(1, 1, 1)); Box3D.expand(box, box, Vec3.create(1, 1, 1));
} }
return box; return box;
} }
...@@ -360,7 +367,6 @@ export namespace VolumeStreaming { ...@@ -360,7 +367,6 @@ export namespace VolumeStreaming {
const switchedToSelection = params.entry.params.view.name === 'selection-box' && this.params && this.params.entry && this.params.entry.params && this.params.entry.params.view && this.params.entry.params.view.name !== 'selection-box'; const switchedToSelection = params.entry.params.view.name === 'selection-box' && this.params && this.params.entry && this.params.entry.params && this.params.entry.params.view && this.params.entry.params.view.name !== 'selection-box';
this.params = params; this.params = params;
let box: Box3D | undefined = void 0, emptyData = false; let box: Box3D | undefined = void 0, emptyData = false;
switch (params.entry.params.view.name) { switch (params.entry.params.view.name) {
......
...@@ -22,6 +22,7 @@ import { Box3D } from '../../../../mol-math/geometry'; ...@@ -22,6 +22,7 @@ import { Box3D } from '../../../../mol-math/geometry';
import { Vec3 } from '../../../../mol-math/linear-algebra'; import { Vec3 } from '../../../../mol-math/linear-algebra';
import { PluginConfig } from '../../../config'; import { PluginConfig } from '../../../config';
import { Model } from '../../../../mol-model/structure'; import { Model } from '../../../../mol-model/structure';
import { GlobalModelTransformInfo } from '../../../../mol-model/structure/model/properties/global-transform';
function addEntry(entries: InfoEntryProps[], method: VolumeServerInfo.Kind, dataId: string, emDefaultContourLevel: number) { function addEntry(entries: InfoEntryProps[], method: VolumeServerInfo.Kind, dataId: string, emDefaultContourLevel: number) {
entries.push({ entries.push({
...@@ -253,20 +254,22 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({ ...@@ -253,20 +254,22 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
channel: PD.Select<VolumeStreaming.ChannelType>('em', VolumeStreaming.ChannelTypeOptions, { isHidden: true }) channel: PD.Select<VolumeStreaming.ChannelType>('em', VolumeStreaming.ChannelTypeOptions, { isHidden: true })
} }
})({ })({
apply: ({ a, params: srcParams }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => { apply: ({ a, params: srcParams, spine }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
const channel = a.data.channels[srcParams.channel]; const channel = a.data.channels[srcParams.channel];
if (!channel) return StateObject.Null; if (!channel) return StateObject.Null;
const params = createVolumeProps(a.data, srcParams.channel); const params = createVolumeProps(a.data, srcParams.channel);
const provider = VolumeRepresentationRegistry.BuiltIn.isosurface; const provider = VolumeRepresentationRegistry.BuiltIn.isosurface;
const props = params.type.params || {}; const props = params.type.params || {};
const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.volume.themes }, provider.getParams); const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.volume.themes }, provider.getParams);
repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params)); repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params));
const structure = spine.getAncestorOfType(SO.Molecule.Structure)?.data;
const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!);
await repr.createOrUpdate(props, channel.data).runInContext(ctx); await repr.createOrUpdate(props, channel.data).runInContext(ctx);
if (transform) repr.setState({ transform });
return new SO.Volume.Representation3D({ repr, source: a }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` }); return new SO.Volume.Representation3D({ repr, source: a }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
}), }),
update: ({ a, b, oldParams, newParams }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => { update: ({ a, b, newParams, spine }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
// TODO : check if params/underlying data/etc have changed; maybe will need to export "data" or some other "tag" in the Representation for this to work // TODO : check if params/underlying data/etc have changed; maybe will need to export "data" or some other "tag" in the Representation for this to work
const channel = a.data.channels[newParams.channel]; const channel = a.data.channels[newParams.channel];
...@@ -277,6 +280,13 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({ ...@@ -277,6 +280,13 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...params.type.params }; const props = { ...b.data.repr.props, ...params.type.params };
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params)); b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params));
await b.data.repr.createOrUpdate(props, channel.data).runInContext(ctx); await b.data.repr.createOrUpdate(props, channel.data).runInContext(ctx);
// TODO: set the transform here as well in case the structure moves?
// doing this here now breaks the code for some reason...
// const structure = spine.getAncestorOfType(SO.Molecule.Structure)?.data;
// const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!);
// if (transform) b.data.repr.setState({ transform });
return StateTransformer.UpdateResult.Updated; return StateTransformer.UpdateResult.Updated;
}) })
}); });
......
# 0.9.5
* Support molstar_global_model_transform_info category.
# 0.9.4 # 0.9.4
* bug fix for /ligand queries on metal ions * bug fix for /ligand queries on metal ions
......
...@@ -30,6 +30,7 @@ import { MolEncoder } from '../../../mol-io/writer/mol/encoder'; ...@@ -30,6 +30,7 @@ import { MolEncoder } from '../../../mol-io/writer/mol/encoder';
import { Mol2Encoder } from '../../../mol-io/writer/mol2/encoder'; import { Mol2Encoder } from '../../../mol-io/writer/mol2/encoder';
import { ComponentAtom } from '../../../mol-model-formats/structure/property/atoms/chem_comp'; import { ComponentAtom } from '../../../mol-model-formats/structure/property/atoms/chem_comp';
import { Mat4 } from '../../../mol-math/linear-algebra'; import { Mat4 } from '../../../mol-math/linear-algebra';
import { GlobalModelTransformInfo } from '../../../mol-model/structure/model/properties/global-transform';
export interface Stats { export interface Stats {
structure: StructureWrapper, structure: StructureWrapper,
...@@ -247,6 +248,7 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc ...@@ -247,6 +248,7 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc
if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter); if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter);
if (result.length > 0) encode_mmCIF_categories(encoder, result, { copyAllCategories: entry.copyAllCategories }); if (result.length > 0) encode_mmCIF_categories(encoder, result, { copyAllCategories: entry.copyAllCategories });
if (entry.transform && !Mat4.isIdentity(entry.transform)) GlobalModelTransformInfo.writeMmCif(encoder, entry.transform);
if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(); if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter();
perf.end('encode'); perf.end('encode');
......
...@@ -4,4 +4,4 @@ ...@@ -4,4 +4,4 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
export default '0.9.4'; export default '0.9.5';
\ No newline at end of file \ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment