Compare commits

..

2 Commits

Author SHA1 Message Date
David Sehnal
50b615e86c 1.2.3 2020-11-28 14:50:15 +01:00
David Sehnal
5b4c6743e7 GlobalModelTransformInfo
- support in volume streaming
- export in ModelServer if transform param is present
2020-11-28 14:46:58 +01:00
12 changed files with 139 additions and 22 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "1.2.2",
"version": "1.2.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "1.2.2",
"version": "1.2.3",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {

View File

@@ -18,6 +18,7 @@ import { AtomSiteAnisotrop } from './property/anisotropic';
import { ComponentBond } from './property/bonds/chem_comp';
import { StructConn } from './property/bonds/struct_conn';
import { Trajectory } from '../../mol-model/structure';
import { GlobalModelTransformInfo } from '../../mol-model/structure/model/properties/global-transform';
function modelSymmetryFromMmcif(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return;
@@ -69,6 +70,8 @@ function structConnFromMmcif(model: Model) {
}
StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif);
GlobalModelTransformInfo.Provider.formatRegistry.add('mmCIF', GlobalModelTransformInfo.fromMmCif, GlobalModelTransformInfo.hasData);
//
export { MmcifFormat };

View File

@@ -132,7 +132,8 @@ function getCustomPropCategories(customProp: CustomPropertyDescriptor, ctx: CifE
type encode_mmCIF_categories_Params = {
skipCategoryNames?: Set<string>,
exportCtx?: CifExportContext,
copyAllCategories?: boolean
copyAllCategories?: boolean,
customProperties?: CustomPropertyDescriptor[]
}
/** Doesn't start a data block */
@@ -144,7 +145,7 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures:
const ctx: CifExportContext = params?.exportCtx || CifExportContext.create(structures);
if (params?.copyAllCategories && MmcifFormat.is(models[0].sourceData)) {
encode_mmCIF_categories_copyAll(encoder, ctx);
encode_mmCIF_categories_copyAll(encoder, ctx, params);
} else {
encode_mmCIF_categories_default(encoder, ctx, params);
}
@@ -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) {
if (!s.hasCustomProperties) continue;
for (const customProp of s.customPropertyDescriptors.all) {
@@ -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>();
for (const cat of Categories) {
@@ -188,12 +197,21 @@ function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExp
const mapping = atom_site_operator_mapping(ctx);
if (mapping) providedCategories.set(mapping[0].name, mapping);
const _params = params || { };
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);
}
}
}
for (const s of ctx.structures) {
if (!s.hasCustomProperties) continue;
for (const customProp of s.customPropertyDescriptors.all) {

View File

@@ -1,7 +0,0 @@
/**
* 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 ...

View File

@@ -0,0 +1,80 @@
/**
* 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);
}
});
}
}

View File

@@ -7,7 +7,7 @@
import { UniqueArray } from '../../../../mol-data/generic';
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 Structure from '../structure';
import Unit from '../unit';
@@ -489,7 +489,7 @@ export namespace Loci {
const boundaryHelper = new BoundaryHelper('98');
const tempPosBoundary = Vec3();
export function getBoundary(loci: Loci): Boundary {
export function getBoundary(loci: Loci, transform?: Mat4): Boundary {
boundaryHelper.reset();
for (const e of loci.elements) {
@@ -499,6 +499,7 @@ export namespace Loci {
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
const eI = elements[OrderedSet.getAt(indices, i)];
pos(eI, tempPosBoundary);
if (transform) Vec3.transformMat4(tempPosBoundary, tempPosBoundary, transform);
boundaryHelper.includePositionRadius(tempPosBoundary, r(eI));
}
}
@@ -510,6 +511,7 @@ export namespace Loci {
for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
const eI = elements[OrderedSet.getAt(indices, i)];
pos(eI, tempPosBoundary);
if (transform) Vec3.transformMat4(tempPosBoundary, tempPosBoundary, transform);
boundaryHelper.radiusPositionRadius(tempPosBoundary, r(eI));
}
}

View File

@@ -10,7 +10,7 @@ import { PluginStateObject } from '../../../../mol-plugin-state/objects';
import { Volume, Grid } from '../../../../mol-model/volume';
import { VolumeServerHeader, VolumeServerInfo } from './model';
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 { PluginBehavior } from '../../behavior';
import { LRUCache } from '../../../../mol-util/lru-cache';
@@ -23,6 +23,7 @@ import { StructureElement, Structure } from '../../../../mol-model/structure';
import { PluginContext } from '../../../context';
import { EmptyLoci, Loci, isEmptyLoci } from '../../../../mol-model/loci';
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' }) { }
@@ -302,6 +303,7 @@ export namespace VolumeStreaming {
}
}
private _invTransform: Mat4 = Mat4();
private getBoxFromLoci(loci: StructureElement.Loci | EmptyLoci): Box3D {
if (Loci.isEmpty(loci)) {
return Box3D();
@@ -312,11 +314,16 @@ export namespace VolumeStreaming {
const root = this.getStructureRoot();
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 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) {
Box3D.expand(box, box, Vec3.create(1, 1, 1));
}
return box;
}
@@ -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';
this.params = params;
let box: Box3D | undefined = void 0, emptyData = false;
switch (params.entry.params.view.name) {

View File

@@ -22,6 +22,7 @@ import { Box3D } from '../../../../mol-math/geometry';
import { Vec3 } from '../../../../mol-math/linear-algebra';
import { PluginConfig } from '../../../config';
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) {
entries.push({
@@ -253,20 +254,22 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
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];
if (!channel) return StateObject.Null;
const params = createVolumeProps(a.data, srcParams.channel);
const provider = VolumeRepresentationRegistry.BuiltIn.isosurface;
const props = params.type.params || {};
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));
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);
if (transform) repr.setState({ transform });
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
const channel = a.data.channels[newParams.channel];
@@ -277,6 +280,13 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
const props = { ...b.data.repr.props, ...params.type.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);
// 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;
})
});

View File

@@ -1,3 +1,6 @@
# 0.9.5
* Support molstar_global_model_transform_info category.
# 0.9.4
* bug fix for /ligand queries on metal ions

View File

@@ -30,6 +30,7 @@ import { MolEncoder } from '../../../mol-io/writer/mol/encoder';
import { Mol2Encoder } from '../../../mol-io/writer/mol2/encoder';
import { ComponentAtom } from '../../../mol-model-formats/structure/property/atoms/chem_comp';
import { Mat4 } from '../../../mol-math/linear-algebra';
import { GlobalModelTransformInfo } from '../../../mol-model/structure/model/properties/global-transform';
export interface Stats {
structure: StructureWrapper,
@@ -247,6 +248,7 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc
if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter);
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();
perf.end('encode');

View File

@@ -4,4 +4,4 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
export default '0.9.4';
export default '0.9.5';