diff --git a/src/cli/structure-info/model.ts b/src/cli/structure-info/model.ts index 87ca5e63a04de70a7d5f856ab52ad17d2dde0664..55e7ef0874682ce642da7423afc2131f93158842 100644 --- a/src/cli/structure-info/model.ts +++ b/src/cli/structure-info/model.ts @@ -10,7 +10,7 @@ import * as argparse from 'argparse'; require('util.promisify').shim(); import { CifFrame } from '../../mol-io/reader/cif'; -import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing } from '../../mol-model/structure'; +import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing, Trajectory } from '../../mol-model/structure'; // import { Run, Progress } from '../../mol-task' import { OrderedSet } from '../../mol-data/int'; import { openCif, downloadCif } from './helpers'; @@ -19,6 +19,7 @@ import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif'; import { Sequence } from '../../mol-model/sequence'; import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure'; import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry'; +import { Task } from '../../mol-task'; async function downloadFromPdb(pdb: string) { @@ -183,10 +184,11 @@ export function printSymmetryInfo(model: Model) { console.log(`NCS operators: ${symmetry.ncsOperators && symmetry.ncsOperators.map(a => a.name).join(', ')}`); } -export function printModelStats(models: ReadonlyArray<Model>) { +export async function printModelStats(models: Trajectory) { console.log('\nModels\n============='); - for (const m of models) { + for (let i = 0; i < models.frameCount; i++) { + const m = await Task.resolveInContext(models.getFrameAtIndex(i)); if (m.coarseHierarchy.isDefined) { console.log(`${m.label} ${m.modelNum}: ${m.atomicHierarchy.atoms._rowCount} atom(s), ${m.coarseHierarchy.spheres.count} sphere(s), ${m.coarseHierarchy.gaussians.count} gaussian(s)`); } else { @@ -198,7 +200,7 @@ export function printModelStats(models: ReadonlyArray<Model>) { export async function getModelsAndStructure(frame: CifFrame) { const models = await trajectoryFromMmCIF(frame).run(); - const structure = Structure.ofModel(models[0]); + const structure = Structure.ofModel(models.representative); return { models, structure }; } @@ -206,13 +208,13 @@ async function run(frame: CifFrame, args: Args) { const { models, structure } = await getModelsAndStructure(frame); if (args.models) printModelStats(models); - if (args.seq) printSequence(models[0]); + if (args.seq) printSequence(models.representative); if (args.units) printUnits(structure); - if (args.sym) printSymmetryInfo(models[0]); + if (args.sym) printSymmetryInfo(models.representative); if (args.rings) printRings(structure); if (args.intraBonds) printBonds(structure, true, false); if (args.interBonds) printBonds(structure, false, true); - if (args.sec) printSecStructure(models[0]); + if (args.sec) printSecStructure(models.representative); } async function runDL(pdb: string, args: Args) { diff --git a/src/extensions/cellpack/model.ts b/src/extensions/cellpack/model.ts index cf6f2ef997772b37bebdfce9b704e5f4f2d4e9fd..2043f93f62e4f6b18f6edb8f54cf4a25a240129a 100644 --- a/src/extensions/cellpack/model.ts +++ b/src/extensions/cellpack/model.ts @@ -10,7 +10,7 @@ import { PluginStateObject as PSO } from '../../mol-plugin-state/objects'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { Ingredient, IngredientSource, CellPacking } from './data'; import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util'; -import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../mol-model/structure'; +import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit, Trajectory } from '../../mol-model/structure'; import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif'; import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb'; import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra'; @@ -36,8 +36,8 @@ function getCellPackModelUrl(fileName: string, baseUrl: string) { } class TrajectoryCache { - private map = new Map<string, Model.Trajectory>(); - set(id: string, trajectory: Model.Trajectory) { this.map.set(id, trajectory); } + private map = new Map<string, Trajectory>(); + set(id: string, trajectory: Trajectory) { this.map.set(id, trajectory); } get(id: string) { return this.map.get(id); } } @@ -94,9 +94,9 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien trajectory = await plugin.runTask(trajectoryFromMmCIF(data.mmcif)); } } - trajCache.set(id, trajectory); + trajCache.set(id, trajectory!); } - const model = trajectory[modelIndex]; + const model = await plugin.resolveTask(trajectory?.getFrameAtIndex(modelIndex)!); return { model, assets }; } @@ -303,7 +303,7 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi const curveModelTask = Task.create('Curve Model', async ctx => { const format = MmcifFormat.fromFrame(cif); const models = await createModels(format.data.db, format, ctx); - return models[0]; + return models.representative; }); const curveModel = await plugin.runTask(curveModelTask); diff --git a/src/extensions/g3d/model.ts b/src/extensions/g3d/model.ts index 5ed757cb662be82d83a073c22869e90342fde493..353e8d3a10bd70862b0c01446bfba7438f2df5ee 100644 --- a/src/extensions/g3d/model.ts +++ b/src/extensions/g3d/model.ts @@ -4,7 +4,6 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Model } from '../../mol-model/structure/model'; import { Task } from '../../mol-task'; import { Column, Table } from '../../mol-data/db'; import { MoleculeType } from '../../mol-model/structure/model/types'; @@ -13,6 +12,7 @@ import { BasicSchema, createBasic } from '../../mol-model-formats/structure/basi import { createModels } from '../../mol-model-formats/structure/basic/parser'; import { G3dDataBlock } from './data'; import { objectForEach } from '../../mol-util/object'; +import { Trajectory } from '../../mol-model/structure'; interface Columns { entity_id: string[], @@ -106,7 +106,7 @@ function getBasic(data: G3dDataBlock) { }); } -export function trajectoryFromG3D(data: G3dDataBlock): Task<Model.Trajectory> { +export function trajectoryFromG3D(data: G3dDataBlock): Task<Trajectory> { return Task.create('Parse G3D', async ctx => { const basic = getBasic(data); return createModels(basic, { kind: 'g3d', name: 'G3D', data }, ctx); diff --git a/src/mol-model-formats/structure/3dg.ts b/src/mol-model-formats/structure/3dg.ts index 0e829bdafa8ebaa753e5c7c4212178b5bce02529..18aeab4730f8c4897a90049795fc5136a81784f7 100644 --- a/src/mol-model-formats/structure/3dg.ts +++ b/src/mol-model-formats/structure/3dg.ts @@ -4,7 +4,6 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Model } from '../../mol-model/structure/model'; import { Task } from '../../mol-task'; import { ModelFormat } from '../format'; import { Column, Table } from '../../mol-data/db'; @@ -14,6 +13,7 @@ import { fillSerial } from '../../mol-util/array'; import { MoleculeType } from '../../mol-model/structure/model/types'; import { BasicSchema, createBasic } from './basic/schema'; import { createModels } from './basic/parser'; +import { Trajectory } from '../../mol-model/structure'; function getBasic(table: File3DG['table']) { const entityIds = new Array<string>(table._rowCount); @@ -74,7 +74,7 @@ namespace Format3dg { } } -export function trajectoryFrom3DG(file3dg: File3DG): Task<Model.Trajectory> { +export function trajectoryFrom3DG(file3dg: File3DG): Task<Trajectory> { return Task.create('Parse 3DG', async ctx => { const format = Format3dg.from3dg(file3dg); const basic = getBasic(file3dg.table); diff --git a/src/mol-model-formats/structure/basic/parser.ts b/src/mol-model-formats/structure/basic/parser.ts index 3262dad5f0aea636590d70dde03c42c20e812a1e..d8a42f4195d6feb604775cbdc344791f96bc547c 100644 --- a/src/mol-model-formats/structure/basic/parser.ts +++ b/src/mol-model-formats/structure/basic/parser.ts @@ -21,6 +21,7 @@ import { AtomSite, BasicData } from './schema'; import { getProperties } from './properties'; import { getEntities } from './entities'; import { getModelGroupName } from './util'; +import { ArrayTrajectory } from '../../../mol-model/structure/trajectory'; export async function createModels(data: BasicData, format: ModelFormat, ctx: RuntimeContext) { const properties = getProperties(data); @@ -32,7 +33,7 @@ export async function createModels(data: BasicData, format: ModelFormat, ctx: Ru Model.TrajectoryInfo.set(models[i], { index: i, size: models.length }); } - return models; + return new ArrayTrajectory(models); } /** Standard atomic model */ diff --git a/src/mol-model-formats/structure/cif-core.ts b/src/mol-model-formats/structure/cif-core.ts index 532e1fb0f453407669fad09b82701ea0c89a786a..8c09d0fb28a0c442b4ac33da694d300479a5ee3d 100644 --- a/src/mol-model-formats/structure/cif-core.ts +++ b/src/mol-model-formats/structure/cif-core.ts @@ -21,6 +21,7 @@ import { ModelSymmetry } from './property/symmetry'; import { IndexPairBonds } from './property/bonds/index-pair'; import { AtomSiteAnisotrop } from './property/anisotropic'; import { guessElementSymbolString } from './util'; +import { Trajectory } from '../../mol-model/structure'; function getSpacegroupNameOrNumber(space_group: CifCore_Database['space_group']) { const groupNumber = space_group.IT_number.value(0); @@ -45,7 +46,7 @@ function getSymmetry(db: CifCore_Database): Symmetry { }; } -async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: RuntimeContext): Promise<Model[]> { +async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: RuntimeContext) { const atomCount = db.atom_site._rowCount; const MOL = Column.ofConst('MOL', atomCount, Column.Schema.str); @@ -152,8 +153,10 @@ async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: Runti const models = await createModels(basics, format, ctx); - if (models.length > 0) { - ModelSymmetry.Provider.set(models[0], symmetry); + if (models.frameCount > 0) { + const first = models.representative; + + ModelSymmetry.Provider.set(first, symmetry); const bondCount = db.geom_bond._rowCount; if(bondCount > 0) { @@ -179,7 +182,7 @@ async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: Runti symmetryB[i] = site_symmetry_2.value(i) || '1_555'; } - IndexPairBonds.Provider.set(models[0], IndexPairBonds.fromData({ pairs: { + IndexPairBonds.Provider.set(first, IndexPairBonds.fromData({ pairs: { indexA: Column.ofIntArray(indexA), indexB: Column.ofIntArray(indexB), order: Column.ofIntArray(order), @@ -238,7 +241,7 @@ namespace CifCoreFormat { } } -export function trajectoryFromCifCore(frame: CifFrame): Task<Model.Trajectory> { +export function trajectoryFromCifCore(frame: CifFrame): Task<Trajectory> { const format = CifCoreFormat.fromFrame(frame); return Task.create('Parse CIF Core', ctx => getModels(format.data.db, format, ctx)); } diff --git a/src/mol-model-formats/structure/cube.ts b/src/mol-model-formats/structure/cube.ts index 3b72a034b774b8378a99bf0a85c1984f2332fcba..9983693f094727d89f92e1e3e91708f7e8bb3cef 100644 --- a/src/mol-model-formats/structure/cube.ts +++ b/src/mol-model-formats/structure/cube.ts @@ -5,7 +5,6 @@ */ import { Column, Table } from '../../mol-data/db'; -import { Model } from '../../mol-model/structure/model'; import { MoleculeType, getElementFromAtomicNumber, ElementSymbol } from '../../mol-model/structure/model/types'; import { RuntimeContext, Task } from '../../mol-task'; import { createModels } from './basic/parser'; @@ -14,8 +13,9 @@ import { ComponentBuilder } from './common/component'; import { EntityBuilder } from './common/entity'; import { ModelFormat } from '../format'; import { CubeFile } from '../../mol-io/reader/cube/parser'; +import { Trajectory } from '../../mol-model/structure'; -async function getModels(cube: CubeFile, ctx: RuntimeContext): Promise<Model[]> { +async function getModels(cube: CubeFile, ctx: RuntimeContext) { const { atoms } = cube; const MOL = Column.ofConst('MOL', cube.atoms.count, Column.Schema.str); @@ -78,6 +78,6 @@ namespace MolFormat { } } -export function trajectoryFromCube(cube: CubeFile): Task<Model.Trajectory> { +export function trajectoryFromCube(cube: CubeFile): Task<Trajectory> { return Task.create('Parse Cube', ctx => getModels(cube, ctx)); } diff --git a/src/mol-model-formats/structure/gro.ts b/src/mol-model-formats/structure/gro.ts index f0228eed99de42ce40e055167a8eab208803ea61..af13e6c015866896c1b37f4fde48c6e44a74647e 100644 --- a/src/mol-model-formats/structure/gro.ts +++ b/src/mol-model-formats/structure/gro.ts @@ -16,6 +16,8 @@ import { getChainId } from './common/util'; import { EntityBuilder } from './common/entity'; import { BasicData, BasicSchema, createBasic } from './basic/schema'; import { createModels } from './basic/parser'; +import { Trajectory } from '../../mol-model/structure'; +import { ArrayTrajectory } from '../../mol-model/structure/trajectory'; function getBasic(atoms: GroAtoms, modelNum: number): BasicData { const auth_atom_id = atoms.atomName; @@ -116,15 +118,17 @@ namespace GroFormat { // TODO reuse static model parts when hierarchy is identical // need to pass all gro.structures as one table into createModels -export function trajectoryFromGRO(gro: GroFile): Task<Model.Trajectory> { +export function trajectoryFromGRO(gro: GroFile): Task<Trajectory> { return Task.create('Parse GRO', async ctx => { const format = GroFormat.fromGro(gro); const models: Model[] = []; for (let i = 0, il = gro.structures.length; i < il; ++i) { const basic = getBasic(gro.structures[i].atoms, i + 1); const m = await createModels(basic, format, ctx); - if (m.length === 1) models.push(m[0]); + if (m.frameCount === 1) { + models.push(m.representative); + } } - return models; + return new ArrayTrajectory(models); }); } diff --git a/src/mol-model-formats/structure/mmcif.ts b/src/mol-model-formats/structure/mmcif.ts index 9a4503f1d831ce8b7fbc492a9c5289fe20699625..48e9970cecf8844b86fe7a2312c9f2f1cbd1c55b 100644 --- a/src/mol-model-formats/structure/mmcif.ts +++ b/src/mol-model-formats/structure/mmcif.ts @@ -17,6 +17,7 @@ import { Table } from '../../mol-data/db'; 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'; function modelSymmetryFromMmcif(model: Model) { if (!MmcifFormat.is(model.sourceData)) return; @@ -86,7 +87,7 @@ namespace MmcifFormat { } } -export function trajectoryFromMmCIF(frame: CifFrame): Task<Model.Trajectory> { +export function trajectoryFromMmCIF(frame: CifFrame): Task<Trajectory> { const format = MmcifFormat.fromFrame(frame); return Task.create('Create mmCIF Model', ctx => createModels(format.data.db, format, ctx)); } \ No newline at end of file diff --git a/src/mol-model-formats/structure/mol.ts b/src/mol-model-formats/structure/mol.ts index dbfc113907915813dc2667ae0e40a9d872c516a3..633d3ab820d4269dcdadf4f1b0f37732df19d0b5 100644 --- a/src/mol-model-formats/structure/mol.ts +++ b/src/mol-model-formats/structure/mol.ts @@ -7,7 +7,6 @@ import { Column, Table } from '../../mol-data/db'; import { MolFile } from '../../mol-io/reader/mol/parser'; -import { Model } from '../../mol-model/structure/model'; import { MoleculeType } from '../../mol-model/structure/model/types'; import { RuntimeContext, Task } from '../../mol-task'; import { createModels } from './basic/parser'; @@ -16,8 +15,9 @@ import { ComponentBuilder } from './common/component'; import { EntityBuilder } from './common/entity'; import { ModelFormat } from '../format'; import { IndexPairBonds } from './property/bonds/index-pair'; +import { Trajectory } from '../../mol-model/structure'; -async function getModels(mol: MolFile, ctx: RuntimeContext): Promise<Model[]> { +async function getModels(mol: MolFile, ctx: RuntimeContext) { const { atoms, bonds } = mol; const MOL = Column.ofConst('MOL', mol.atoms.count, Column.Schema.str); @@ -63,12 +63,12 @@ async function getModels(mol: MolFile, ctx: RuntimeContext): Promise<Model[]> { const models = await createModels(basics, MolFormat.create(mol), ctx); - if (models.length > 0) { + if (models.frameCount > 0) { const indexA = Column.ofIntArray(Column.mapToArray(bonds.atomIdxA, x => x - 1, Int32Array)); const indexB = Column.ofIntArray(Column.mapToArray(bonds.atomIdxB, x => x - 1, Int32Array)); const order = Column.asArrayColumn(bonds.order, Int32Array); const pairBonds = IndexPairBonds.fromData({ pairs: { indexA, indexB, order }, count: bonds.count }); - IndexPairBonds.Provider.set(models[0], pairBonds); + IndexPairBonds.Provider.set(models.representative, pairBonds); } return models; @@ -90,6 +90,6 @@ namespace MolFormat { } } -export function trajectoryFromMol(mol: MolFile): Task<Model.Trajectory> { +export function trajectoryFromMol(mol: MolFile): Task<Trajectory> { return Task.create('Parse MOL', ctx => getModels(mol, ctx)); } diff --git a/src/mol-model-formats/structure/mol2.ts b/src/mol-model-formats/structure/mol2.ts index 14f63fc85e96ff7e21b0ee91badbf0e877001dc0..d3ba36bdbd359790ea153bcfcc3db0e02788495f 100644 --- a/src/mol-model-formats/structure/mol2.ts +++ b/src/mol-model-formats/structure/mol2.ts @@ -16,8 +16,9 @@ import { ModelFormat } from '../format'; import { IndexPairBonds } from './property/bonds/index-pair'; import { Mol2File } from '../../mol-io/reader/mol2/schema'; import { AtomPartialCharge } from './property/partial-charge'; +import { Trajectory, ArrayTrajectory } from '../../mol-model/structure'; -async function getModels(mol2: Mol2File, ctx: RuntimeContext): Promise<Model[]> { +async function getModels(mol2: Mol2File, ctx: RuntimeContext) { const models: Model[] = []; for (let i = 0, il = mol2.structures.length; i < il; ++i) { @@ -64,23 +65,25 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext): Promise<Model[]> const _models = await createModels(basics, Mol2Format.create(mol2), ctx); - if (_models.length > 0) { + if (_models.frameCount > 0) { const indexA = Column.ofIntArray(Column.mapToArray(bonds.origin_atom_id, x => x - 1, Int32Array)); const indexB = Column.ofIntArray(Column.mapToArray(bonds.target_atom_id, x => x - 1, Int32Array)); const order = Column.ofIntArray(Column.mapToArray(bonds.bond_type, x => x === 'ar' ? 1 : parseInt(x), Int8Array)); const pairBonds = IndexPairBonds.fromData({ pairs: { indexA, indexB, order }, count: bonds.count }); - IndexPairBonds.Provider.set(_models[0], pairBonds); - AtomPartialCharge.Provider.set(_models[0], { + const first = _models.representative; + IndexPairBonds.Provider.set(first, pairBonds); + + AtomPartialCharge.Provider.set(first, { data: atoms.charge, type: molecule.charge_type }); - models.push(_models[0]); + models.push(first); } } - return models; + return new ArrayTrajectory(models); } // @@ -99,6 +102,6 @@ namespace Mol2Format { } } -export function trajectoryFromMol2(mol2: Mol2File): Task<Model.Trajectory> { +export function trajectoryFromMol2(mol2: Mol2File): Task<Trajectory> { return Task.create('Parse MOL2', ctx => getModels(mol2, ctx)); } diff --git a/src/mol-model-formats/structure/pdb.ts b/src/mol-model-formats/structure/pdb.ts index cd7388bd1e77e8ac2034c78dd0f3d786ac86b195..95ab30881f337ded93eba78090b018e55d408798 100644 --- a/src/mol-model-formats/structure/pdb.ts +++ b/src/mol-model-formats/structure/pdb.ts @@ -7,14 +7,14 @@ import { PdbFile } from '../../mol-io/reader/pdb/schema'; import { pdbToMmCif } from './pdb/to-cif'; -import { Model } from '../../mol-model/structure/model'; import { Task } from '../../mol-task'; import { MmcifFormat } from './mmcif'; import { createModels } from './basic/parser'; import { Column } from '../../mol-data/db'; import { AtomPartialCharge } from './property/partial-charge'; +import { Trajectory } from '../../mol-model/structure'; -export function trajectoryFromPDB(pdb: PdbFile): Task<Model.Trajectory> { +export function trajectoryFromPDB(pdb: PdbFile): Task<Trajectory> { return Task.create('Parse PDB', async ctx => { await ctx.update('Converting to mmCIF'); const cif = await pdbToMmCif(pdb); @@ -24,8 +24,9 @@ export function trajectoryFromPDB(pdb: PdbFile): Task<Model.Trajectory> { if (partial_charge) { // TODO works only for single, unsorted model, to work generally // would need to do model splitting again - if (models.length === 1) { - const srcIndex = models[0].atomicHierarchy.atoms.sourceIndex; + if (models.frameCount === 1) { + const first = models.representative; + const srcIndex = first.atomicHierarchy.atoms.sourceIndex; const isIdentity = Column.isIdentity(srcIndex); const srcIndexArray = isIdentity ? void 0 : srcIndex.toArray({ array: Int32Array }); @@ -34,7 +35,7 @@ export function trajectoryFromPDB(pdb: PdbFile): Task<Model.Trajectory> { ? Column.ofFloatArray(Column.mapToArray(srcIndex, i => q[i], Float32Array)) : Column.ofFloatArray(q); - AtomPartialCharge.Provider.set(models[0], { + AtomPartialCharge.Provider.set(first, { data: partialCharge, type: 'GASTEIGER' // from PDBQT }); diff --git a/src/mol-model/structure.ts b/src/mol-model/structure.ts index d8ab4a1a8c79065037c93708cc2864720beda46d..3ee18d9b5118c062b74bae7139112221ef3e732d 100644 --- a/src/mol-model/structure.ts +++ b/src/mol-model/structure.ts @@ -9,4 +9,5 @@ export * from './structure/coordinates'; export * from './structure/topology'; export * from './structure/model'; export * from './structure/structure'; -export * from './structure/query'; \ No newline at end of file +export * from './structure/query'; +export * from './structure/trajectory'; \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index aee5a4411ec0cecb263ac558fe325d007eda4c04..46bbd7456614f497d79bd2b975cc373629b59cde 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -15,7 +15,6 @@ import { SaccharideComponentMap } from '../structure/carbohydrates/constants'; import { ModelFormat } from '../../../mol-model-formats/format'; import { calcModelCenter, getAsymIdCount } from './util'; import { Vec3 } from '../../../mol-math/linear-algebra'; -import { Mutable } from '../../../mol-util/type-helpers'; import { Coordinates } from '../coordinates'; import { Topology } from '../topology'; import { Task } from '../../../mol-task'; @@ -27,6 +26,7 @@ import { SymmetryOperator } from '../../../mol-math/geometry'; import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry'; import { Column } from '../../../mol-data/db'; import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property'; +import { Trajectory, ArrayTrajectory } from '../trajectory'; /** * Interface to the "source data" of the molecule. @@ -86,10 +86,8 @@ export interface Model extends Readonly<{ } { } export namespace Model { - export type Trajectory = ReadonlyArray<Model> - function _trajectoryFromModelAndCoordinates(model: Model, coordinates: Coordinates) { - const trajectory: Mutable<Model.Trajectory> = []; + const trajectory: Model[] = []; const { frames } = coordinates; const srcIndex = model.atomicHierarchy.atoms.sourceIndex; @@ -117,7 +115,7 @@ export namespace Model { } export function trajectoryFromModelAndCoordinates(model: Model, coordinates: Coordinates): Trajectory { - return _trajectoryFromModelAndCoordinates(model, coordinates).trajectory; + return new ArrayTrajectory(_trajectoryFromModelAndCoordinates(model, coordinates).trajectory); } export function invertIndex(xs: ArrayLike<number>) { @@ -130,8 +128,9 @@ export namespace Model { export function trajectoryFromTopologyAndCoordinates(topology: Topology, coordinates: Coordinates): Task<Trajectory> { return Task.create('Create Trajectory', async ctx => { - const model = (await createModels(topology.basic, topology.sourceData, ctx))[0]; - if (!model) throw new Error('found no model'); + const models = await createModels(topology.basic, topology.sourceData, ctx); + if (models.frameCount === 0) throw new Error('found no model'); + const model = models.representative; const { trajectory, srcIndexArray } = _trajectoryFromModelAndCoordinates(model, coordinates); // TODO: cache the inverted index somewhere? @@ -152,7 +151,7 @@ export namespace Model { IndexPairBonds.Provider.set(m, indexPairBonds); TrajectoryInfo.set(m, { index: index++, size: trajectory.length }); } - return trajectory; + return new ArrayTrajectory(trajectory); }); } diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 020fe97332bc708f232eb50a85edf437ad3d4f9e..64d926486d2f0d3ca9a65be235e7edcc9f38b01a 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -31,6 +31,8 @@ import { StructureSelection } from '../query/selection'; import { getBoundary } from '../../../mol-math/geometry/boundary'; import { ElementSymbol } from '../model/types'; import { CustomStructureProperty } from '../../../mol-model-props/common/custom-structure-property'; +import { Trajectory } from '../trajectory'; +import { RuntimeContext, Task } from '../../../mol-task'; class Structure { /** Maps unit.id to unit */ @@ -639,14 +641,17 @@ namespace Structure { return new Structure(units, props); } - export function ofTrajectory(trajectory: ReadonlyArray<Model>): Structure { - if (trajectory.length === 0) return Empty; + export async function ofTrajectory(trajectory: Trajectory, ctx: RuntimeContext): Promise<Structure> { + if (trajectory.frameCount === 0) return Empty; const units: Unit[] = []; + let first: Model | undefined = void 0; let count = 0; - for (let i = 0, il = trajectory.length; i < il; ++i) { - const structure = ofModel(trajectory[i]); + for (let i = 0, il = trajectory.frameCount; i < il; ++i) { + const frame = await Task.resolveInContext(trajectory.getFrameAtIndex(i), ctx); + if (!first) first = frame; + const structure = ofModel(frame); for (let j = 0, jl = structure.units.length; j < jl; ++j) { const u = structure.units[j]; const invariantId = u.invariantId + count; @@ -657,7 +662,7 @@ namespace Structure { count = units.length; } - return create(units, { representativeModel: trajectory[0], label: trajectory[0].label }); + return create(units, { representativeModel: first!, label: first!.label }); } const PARTITION = false; diff --git a/src/mol-model/structure/trajectory.ts b/src/mol-model/structure/trajectory.ts new file mode 100644 index 0000000000000000000000000000000000000000..69bcbaad3a591b94709e5c0e5bd0889cc77dd768 --- /dev/null +++ b/src/mol-model/structure/trajectory.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Task } from '../../mol-task'; +import { Model } from '../structure'; + +export type TrajectoryFrameType = + | { type: 'default' } + /** Returns the closest available frame to the requested index */ + | { type: 'snap' } + /** Interpolates between two available adjacent frames */ + | { type: 'interpolate', kind?: 'linear' } + +/** + * A generic interface for representing (partial) trajectories + */ +export interface Trajectory { + readonly duration: number, + readonly frameCount: number, + + /** Statically available representative model. Required for example by certain UI actions. */ + readonly representative: Model, + + /** Allows to asynchronously query data from a server or interpolate frames on the fly */ + getFrameAtIndex(i: number, type?: TrajectoryFrameType): Task<Model> | Model +} + +export class ArrayTrajectory implements Trajectory { + readonly duration: number; + readonly frameCount: number; + readonly representative: Model; + + getFrameAtIndex(i: number) { + return this.frames[i]; + } + + constructor(private frames: Model[]) { + this.frameCount = frames.length; + this.representative = frames[0]; + this.duration = frames.length; + } +} \ No newline at end of file diff --git a/src/mol-plugin-state/actions/structure.ts b/src/mol-plugin-state/actions/structure.ts index a29f719eb48a1e26a620d27dd6e4ec8bb9f3f122..babb24352dcc51908914fc487727573317335ef3 100644 --- a/src/mol-plugin-state/actions/structure.ts +++ b/src/mol-plugin-state/actions/structure.ts @@ -191,8 +191,8 @@ export const UpdateTrajectory = StateAction.build({ if (!parent || !parent.obj) continue; const traj = parent.obj; update.to(m).update(old => { - let modelIndex = (old.modelIndex + params.by!) % traj.data.length; - if (modelIndex < 0) modelIndex += traj.data.length; + let modelIndex = (old.modelIndex + params.by!) % traj.data.frameCount; + if (modelIndex < 0) modelIndex += traj.data.frameCount; return { modelIndex }; }); } diff --git a/src/mol-plugin-state/animation/built-in.ts b/src/mol-plugin-state/animation/built-in.ts index a9b8244f50e7078a2c925cf56e25d2c0422e24f4..50ddce363099717234a445dda528fd56025170fa 100644 --- a/src/mol-plugin-state/animation/built-in.ts +++ b/src/mol-plugin-state/animation/built-in.ts @@ -28,7 +28,7 @@ export const AnimateModelIndex = PluginStateAnimation.create({ const models = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Model.ModelFromTrajectory)); for (const m of models) { const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]); - if (parent && parent.obj && parent.obj.data.length > 1) return { canApply: true }; + if (parent && parent.obj && parent.obj.data.frameCount > 1) return { canApply: true }; } return { canApply: false, reason: 'No trajectory to animate' }; }, @@ -57,10 +57,10 @@ export const AnimateModelIndex = PluginStateAnimation.create({ const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]); if (!parent || !parent.obj) continue; const traj = parent.obj; - if (traj.data.length <= 1) continue; + if (traj.data.frameCount <= 1) continue; update.to(m).update(old => { - const len = traj.data.length; + const len = traj.data.frameCount; if (len !== 1) { allSingles = false; } else { diff --git a/src/mol-plugin-state/builder/structure/hierarchy-preset.ts b/src/mol-plugin-state/builder/structure/hierarchy-preset.ts index e007906d1c82084e0cbe60bfacf235dce91a1c86..55bd35af41afe523566fdf2ed22419961d00efc5 100644 --- a/src/mol-plugin-state/builder/structure/hierarchy-preset.ts +++ b/src/mol-plugin-state/builder/structure/hierarchy-preset.ts @@ -80,7 +80,7 @@ const allModels = TrajectoryHierarchyPresetProvider({ description: 'Shows all models; colored by model-index.' }, isApplicable: o => { - return o.data.length > 1; + return o.data.frameCount > 1; }, params: CommonParams, async apply(trajectory, params, plugin) { @@ -91,7 +91,7 @@ const allModels = TrajectoryHierarchyPresetProvider({ const models = [], structures = []; - for (let i = 0; i < tr.length; i++) { + for (let i = 0; i < tr.frameCount; i++) { const model = await builder.createModel(trajectory, { modelIndex: i }); const modelProperties = await builder.insertModelProperties(model, params.modelProperties, { isCollapsed: true }); const structure = await builder.createStructure(modelProperties || model, { name: 'model', params: {} }); @@ -100,7 +100,7 @@ const allModels = TrajectoryHierarchyPresetProvider({ models.push(model); structures.push(structure); - const quality = structure.obj ? getStructureQuality(structure.obj.data, { elementCountFactor: tr.length }) : 'medium'; + const quality = structure.obj ? getStructureQuality(structure.obj.data, { elementCountFactor: tr.frameCount }) : 'medium'; await builder.representation.applyPreset(structureProperties, params.representationPreset || 'auto', { theme: { globalName: 'model-index' }, quality }); } @@ -145,7 +145,7 @@ const unitcell = TrajectoryHierarchyPresetProvider({ description: 'Shows the fully populated unit cell.' }, isApplicable: o => { - return Model.hasCrystalSymmetry(o.data[0]); + return Model.hasCrystalSymmetry(o.data.representative); }, params: CrystalSymmetryParams, async apply(trajectory, params, plugin) { @@ -160,7 +160,7 @@ const supercell = TrajectoryHierarchyPresetProvider({ description: 'Shows the super cell, i.e. the central unit cell and all adjacent unit cells.' }, isApplicable: o => { - return Model.hasCrystalSymmetry(o.data[0]); + return Model.hasCrystalSymmetry(o.data.representative); }, params: CrystalSymmetryParams, async apply(trajectory, params, plugin) { @@ -180,7 +180,7 @@ const crystalContacts = TrajectoryHierarchyPresetProvider({ description: 'Showsasymetric unit and chains from neighbours within 5 \u212B, i.e., symmetry mates.' }, isApplicable: o => { - return Model.hasCrystalSymmetry(o.data[0]); + return Model.hasCrystalSymmetry(o.data.representative); }, params: CrystalContactsParams, async apply(trajectory, params, plugin) { diff --git a/src/mol-plugin-state/objects.ts b/src/mol-plugin-state/objects.ts index a9b18d77da426094dc91deb5d34415de6973afe6..9c3fade63ea32e43f608e5554635389ba94f0f99 100644 --- a/src/mol-plugin-state/objects.ts +++ b/src/mol-plugin-state/objects.ts @@ -13,7 +13,7 @@ import { Dsn6File } from '../mol-io/reader/dsn6/schema'; import { PlyFile } from '../mol-io/reader/ply/schema'; import { PsfFile } from '../mol-io/reader/psf/parser'; import { ShapeProvider } from '../mol-model/shape/provider'; -import { Coordinates as _Coordinates, Model as _Model, Structure as _Structure, StructureElement, Topology as _Topology } from '../mol-model/structure'; +import { Coordinates as _Coordinates, Model as _Model, Structure as _Structure, Trajectory as _Trajectory, StructureElement, Topology as _Topology } from '../mol-model/structure'; import { Volume as _Volume } from '../mol-model/volume'; import { PluginBehavior } from '../mol-plugin/behavior/behavior'; import { Representation } from '../mol-repr/representation'; @@ -100,7 +100,7 @@ export namespace PluginStateObject { export class Coordinates extends Create<_Coordinates>({ name: 'Coordinates', typeClass: 'Object' }) { } export class Topology extends Create<_Topology>({ name: 'Topology', typeClass: 'Object' }) { } export class Model extends Create<_Model>({ name: 'Model', typeClass: 'Object' }) { } - export class Trajectory extends Create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { } + export class Trajectory extends Create<_Trajectory>({ name: 'Trajectory', typeClass: 'Object' }) { } export class Structure extends Create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { } export namespace Structure { diff --git a/src/mol-plugin-state/transforms/model.ts b/src/mol-plugin-state/transforms/model.ts index 91bc6cfd115075ec3d7e4ab9f7ff7f0ede7f1d86..4d3bd65152264f5cd4addb5803f5877e5ab3c9c5 100644 --- a/src/mol-plugin-state/transforms/model.ts +++ b/src/mol-plugin-state/transforms/model.ts @@ -17,7 +17,7 @@ import { trajectoryFromGRO } from '../../mol-model-formats/structure/gro'; import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif'; import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb'; import { topologyFromPsf } from '../../mol-model-formats/structure/psf'; -import { Coordinates, Model, Queries, QueryContext, Structure, StructureElement, StructureQuery, StructureSelection as Sel, Topology } from '../../mol-model/structure'; +import { Coordinates, Model, Queries, QueryContext, Structure, StructureElement, StructureQuery, StructureSelection as Sel, Topology, ArrayTrajectory, Trajectory } from '../../mol-model/structure'; import { PluginContext } from '../../mol-plugin/context'; import { MolScriptBuilder } from '../../mol-script/language/builder'; import Expression from '../../mol-script/language/expression'; @@ -142,7 +142,7 @@ const TrajectoryFromModelAndCoordinates = PluginStateTransform.BuiltIn({ return Task.create('Create trajectory from model/topology and coordinates', async ctx => { const coordinates = dependencies![params.coordinatesRef].data as Coordinates; const trajectory = await getTrajectory(ctx, dependencies![params.modelRef], coordinates); - const props = { label: 'Trajectory', description: `${trajectory.length} model${trajectory.length === 1 ? '' : 's'}` }; + const props = { label: 'Trajectory', description: `${trajectory.frameCount} model${trajectory.frameCount === 1 ? '' : 's'}` }; return new SO.Molecule.Trajectory(trajectory, props); }); } @@ -162,16 +162,25 @@ const TrajectoryFromBlob = PluginStateTransform.BuiltIn({ if (e.kind !== 'cif') continue; const block = e.data.blocks[0]; const xs = await trajectoryFromMmCIF(block).runInContext(ctx); - if (xs.length === 0) throw new Error('No models found.'); - for (const x of xs) models.push(x); + if (xs.frameCount === 0) throw new Error('No models found.'); + + for (let i = 0; i < xs.frameCount; i++) { + const x = await Task.resolveInContext(xs.getFrameAtIndex(i), ctx); + models.push(x); + } } const props = { label: 'Trajectory', description: `${models.length} model${models.length === 1 ? '' : 's'}` }; - return new SO.Molecule.Trajectory(models, props); + return new SO.Molecule.Trajectory(new ArrayTrajectory(models), props); }); } }); +function trajectoryProps(trajectory: Trajectory) { + const first = trajectory.representative; + return { label: `${first.entry}`, description: `${trajectory.frameCount} model${trajectory.frameCount === 1 ? '' : 's'}` }; +} + type TrajectoryFromMmCif = typeof TrajectoryFromMmCif const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ name: 'trajectory-from-mmcif', @@ -197,8 +206,8 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ const block = a.data.blocks.find(b => b.header === header); if (!block) throw new Error(`Data block '${[header]}' not found.`); const models = await trajectoryFromMmCIF(block).runInContext(ctx); - if (models.length === 0) throw new Error('No models found.'); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + if (models.frameCount === 0) throw new Error('No models found.'); + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -219,7 +228,7 @@ const TrajectoryFromPDB = PluginStateTransform.BuiltIn({ const parsed = await parsePDB(a.data, a.label, params.isPdbqt).runInContext(ctx); if (parsed.isError) throw new Error(parsed.message); const models = await trajectoryFromPDB(parsed.result).runInContext(ctx); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -237,7 +246,7 @@ const TrajectoryFromGRO = PluginStateTransform.BuiltIn({ const parsed = await parseGRO(a.data).runInContext(ctx); if (parsed.isError) throw new Error(parsed.message); const models = await trajectoryFromGRO(parsed.result).runInContext(ctx); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -255,7 +264,7 @@ const TrajectoryFromMOL = PluginStateTransform.BuiltIn({ const parsed = await parseMol(a.data).runInContext(ctx); if (parsed.isError) throw new Error(parsed.message); const models = await trajectoryFromMol(parsed.result).runInContext(ctx); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -273,7 +282,7 @@ const TrajectoryFromMOL2 = PluginStateTransform.BuiltIn({ const parsed = await parseMol2(a.data, a.label).runInContext(ctx); if (parsed.isError) throw new Error(parsed.message); const models = await trajectoryFromMol2(parsed.result).runInContext(ctx); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -289,7 +298,7 @@ const TrajectoryFromCube = PluginStateTransform.BuiltIn({ apply({ a }) { return Task.create('Parse MOL', async ctx => { const models = await trajectoryFromCube(a.data).runInContext(ctx); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -319,8 +328,8 @@ const TrajectoryFromCifCore = PluginStateTransform.BuiltIn({ const block = a.data.blocks.find(b => b.header === header); if (!block) throw new Error(`Data block '${[header]}' not found.`); const models = await trajectoryFromCifCore(block).runInContext(ctx); - if (models.length === 0) throw new Error('No models found.'); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + if (models.frameCount === 0) throw new Error('No models found.'); + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -338,7 +347,7 @@ const TrajectoryFrom3DG = PluginStateTransform.BuiltIn({ const parsed = await parse3DG(a.data).runInContext(ctx); if (parsed.isError) throw new Error(parsed.message); const models = await trajectoryFrom3DG(parsed.result).runInContext(ctx); - const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = trajectoryProps(models); return new SO.Molecule.Trajectory(models, props); }); } @@ -355,17 +364,19 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({ if (!a) { return { modelIndex: PD.Numeric(0, {}, { description: 'Zero-based index of the model' }) }; } - return { modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) }; + return { modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.frameCount, step: 1 }, { description: 'Model Index' })) }; } })({ - isApplicable: a => a.data.length > 0, + isApplicable: a => a.data.frameCount > 0, apply({ a, params }) { - let modelIndex = params.modelIndex % a.data.length; - if (modelIndex < 0) modelIndex += a.data.length; - const model = a.data[params.modelIndex]; - const label = `Model ${model.modelNum}`; - const description = a.data.length === 1 ? undefined : `of ${a.data.length}`; - return new SO.Molecule.Model(model, { label, description }); + return Task.create('Model from Trajectory', async ctx => { + let modelIndex = params.modelIndex % a.data.frameCount; + if (modelIndex < 0) modelIndex += a.data.frameCount; + const model = await Task.resolveInContext(a.data.getFrameAtIndex(modelIndex), ctx); + const label = `Model ${modelIndex + 1}`; + let description = a.data.frameCount === 1 ? undefined : `of ${a.data.frameCount}`; + return new SO.Molecule.Model(model, { label, description }); + }); }, dispose({ b }) { b?.data.customProperties.dispose(); @@ -381,7 +392,7 @@ const StructureFromTrajectory = PluginStateTransform.BuiltIn({ })({ apply({ a }) { return Task.create('Build Structure', async ctx => { - const s = Structure.ofTrajectory(a.data); + const s = await Structure.ofTrajectory(a.data, ctx); const props = { label: 'Ensemble', description: Structure.elementDescription(s) }; return new SO.Molecule.Structure(s, props); }); diff --git a/src/mol-plugin-ui/controls.tsx b/src/mol-plugin-ui/controls.tsx index a820782a29d2ca5233f02d9f002b8b8c3b127392..94b105c256babcb817080879a4313ee0b0549d4b 100644 --- a/src/mol-plugin-ui/controls.tsx +++ b/src/mol-plugin-ui/controls.tsx @@ -44,7 +44,7 @@ export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: bo const parent = state.cells.get(m.sourceRef)!.obj as PluginStateObject.Molecule.Trajectory; if (!parent) continue; - if (parent.data.length > 1) { + if (parent.data.frameCount > 1) { if (parents.has(m.sourceRef)) { // do not show the controls if there are 2 models of the same trajectory present this.setState({ show: false }); @@ -55,7 +55,7 @@ export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: bo count++; if (!label) { const idx = (m.transform.params! as StateTransformer.Params<ModelFromTrajectory>).modelIndex; - label = `Model ${idx + 1} / ${parent.data.length}`; + label = `Model ${idx + 1} / ${parent.data.frameCount}`; } } } diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 5e7f67ae9c518506a90cc0436470f71fbb677b91..3f0d31f395c01abf7738dbe6cb3abe1b9545125e 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -62,6 +62,11 @@ import { filter, take } from 'rxjs/operators'; export class PluginContext { runTask = <T>(task: Task<T>) => this.tasks.run(task); + resolveTask = <T>(object: Task<T> | T | undefined) => { + if (!object) return void 0; + if (Task.is(object)) return this.runTask(object); + return object; + } private disposed = false; private ev = RxEventHelper.create(); diff --git a/src/mol-task/task.ts b/src/mol-task/task.ts index da0bf7228c7323c9b1220178d13150b6f5dab32d..d258948912370845ad7a19753e2c483218d854a3 100644 --- a/src/mol-task/task.ts +++ b/src/mol-task/task.ts @@ -71,6 +71,11 @@ namespace Task { export function empty(): Task<void> { return create('', async ctx => {}); } export function fail(name: string, reason: string): Task<any> { return create(name, async ctx => { throw new Error(reason); }); } + export function resolveInContext<T>(object: Task<T> | T, ctx?: RuntimeContext) { + if (is(object)) return ctx ? object.runInContext(ctx) : object.run(); + return object; + } + export interface Progress { taskId: number, taskName: string, diff --git a/src/perf-tests/lookup3d.ts b/src/perf-tests/lookup3d.ts index 1c4c45af5af92796714493be07430b47eb6b393d..81cbd9e9a8367a511c096a611f6b58f0f19bc1f4 100644 --- a/src/perf-tests/lookup3d.ts +++ b/src/perf-tests/lookup3d.ts @@ -34,9 +34,9 @@ export async function readCIF(path: string) { } const models = await trajectoryFromMmCIF(parsed.result.blocks[0]).run(); - const structures = models.map(Structure.ofModel); + const structures = [Structure.ofModel(models.representative)]; - return { mmcif: models[0].sourceData.data as MmcifFormat.Data, models, structures }; + return { mmcif: models.representative.sourceData.data as MmcifFormat.Data, models, structures }; } export async function test() { diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index e8314ec9647c93b1f02833ce801ab515567cd70f..c769e7aa0a80a567a01b70a1563c0aee7bd8fdb9 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -73,9 +73,9 @@ export async function readCIF(path: string) { console.time('buildModels'); const models = await trajectoryFromMmCIF(data).run(); console.timeEnd('buildModels'); - const structures = models.map(Structure.ofModel); + const structures = [Structure.ofModel(models.representative)]; - return { mmcif: models[0].sourceData.data, models, structures }; + return { mmcif: models.representative.sourceData.data, models, structures }; } const DATA_DIR = './build/data'; @@ -411,7 +411,7 @@ export namespace PropertyAccess { // return; - console.log('bs', baseline(models[0])); + console.log('bs', baseline(models.representative)); console.log('sp', sumProperty(structures[0], l => l.unit.model.atomicConformation.atomId.value(l.element))); // console.log(sumPropertySegmented(structures[0], l => l.unit.model.atomSiteConformation.atomId.value(l.element))); @@ -459,7 +459,7 @@ export namespace PropertyAccess { console.log(StructureSelection.structureCount(q2r)); // console.log(q1(structures[0])); - const col = models[0].atomicConformation.atomId.value; + const col = models.representative.atomicConformation.atomId.value; const suite = new B.Suite(); suite // .add('test q', () => q1(structures[0])) diff --git a/src/servers/model/server/structure-wrapper.ts b/src/servers/model/server/structure-wrapper.ts index 9fdb746a2c087034d363798f9ce5451ee37cfdaa..e1eb1fe848d91b41e2b9a100b296f8198d4f53b4 100644 --- a/src/servers/model/server/structure-wrapper.ts +++ b/src/servers/model/server/structure-wrapper.ts @@ -17,6 +17,7 @@ import { ConsoleLogger } from '../../../mol-util/console-logger'; import { ModelPropertiesProvider } from '../property-provider'; import { trajectoryFromMmCIF } from '../../../mol-model-formats/structure/mmcif'; import { fetchRetry } from '../utils/fetch-retry'; +import { Task } from '../../../mol-task'; require('util.promisify').shim(); @@ -160,11 +161,15 @@ function readOrFetch(jobId: string, key: string, sourceId: string | '_local_', e export async function readStructureWrapper(key: string, sourceId: string | '_local_', entryId: string, jobId: string | undefined, propertyProvider: ModelPropertiesProvider | undefined) { const { data, frame, isBinary } = await readOrFetch(jobId || '', key, sourceId, entryId); perf.start('createModel'); - const models = await trajectoryFromMmCIF(frame).run(); + const trajectory = await trajectoryFromMmCIF(frame).run(); perf.end('createModel'); + const models: Model[] = []; const modelMap = new Map<number, Model>(); - for (const m of models) { + + for (let i = 0; i < trajectory.frameCount; i++) { + const m = await Task.resolveInContext(trajectory.getFrameAtIndex(i)); + models.push(m); modelMap.set(m.modelNum, m); } diff --git a/src/tests/browser/render-structure.ts b/src/tests/browser/render-structure.ts index ab79a6f81ec764fbbdecaf63344b23cf694ceb5d..8e155735c17df92c6989bc9e5cf7cf6e39ed8140 100644 --- a/src/tests/browser/render-structure.ts +++ b/src/tests/browser/render-structure.ts @@ -127,7 +127,7 @@ async function init() { const cif = await downloadFromPdb('3pqr'); const models = await getModels(cif); - const structure = await getStructure(models[0]); + const structure = await getStructure(models.representative); console.time('compute SecondaryStructure'); await SecondaryStructureProvider.attach(ctx, structure);