diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a640d8cf55e3c410167136d5970a68b184dc32..457280c245f264d6cb8d8bd8f71081f29b404a9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Note that since we don't clearly distinguish between a public and private interf - Remove `JSX` reference from `loci-labels.ts` - Fix overpaint/transparency/substance smoothing not updated when geometry changes - Fix camera project/unproject when using offset viewport +- Add support for loading all blocks from a mmcif file as a trajectory - Add `Frustum3D` and `Plane3D` math primitives ## [v3.32.0] - 2023-03-20 diff --git a/src/mol-plugin-state/transforms/model.ts b/src/mol-plugin-state/transforms/model.ts index 3f1fff5864b424c68583e6e96672cbeed7d7d3d8..9852c18773be225d3d4891c2090b462968123e3e 100644 --- a/src/mol-plugin-state/transforms/model.ts +++ b/src/mol-plugin-state/transforms/model.ts @@ -272,25 +272,42 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ params(a) { if (!a) { return { - blockHeader: PD.Optional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' })) + loadAllBlocks: PD.Optional(PD.Boolean(false, { description: 'If True, ignore Block Header parameter and parse all datablocks into a single trajectory.' })), + blockHeader: PD.Optional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.', hideIf: p => p.loadAllBlocks === true })), }; } const { blocks } = a.data; return { - blockHeader: PD.Optional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })) + loadAllBlocks: PD.Optional(PD.Boolean(false, { description: 'If True, ignore Block Header parameter and parse all data blocks into a single trajectory.' })), + blockHeader: PD.Optional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse', hideIf: p => p.loadAllBlocks === true })), }; } })({ isApplicable: a => a.data.blocks.length > 0, apply({ a, params }) { return Task.create('Parse mmCIF', async ctx => { - const header = params.blockHeader || a.data.blocks[0].header; - 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.frameCount === 0) throw new Error('No models found.'); - const props = trajectoryProps(models); - return new SO.Molecule.Trajectory(models, props); + let trajectory: Trajectory; + if (params.loadAllBlocks) { + const models: Model[] = []; + for (const block of a.data.blocks) { + if (ctx.shouldUpdate) { + await ctx.update(`Parsing ${block.header}...`); + } + const t = await trajectoryFromMmCIF(block).runInContext(ctx); + for (let i = 0; i < t.frameCount; i++) { + models.push(await Task.resolveInContext(t.getFrameAtIndex(i), ctx)); + } + } + trajectory = new ArrayTrajectory(models); + } else { + const header = params.blockHeader || a.data.blocks[0].header; + const block = a.data.blocks.find(b => b.header === header); + if (!block) throw new Error(`Data block '${[header]}' not found.`); + trajectory = await trajectoryFromMmCIF(block).runInContext(ctx); + } + if (trajectory.frameCount === 0) throw new Error('No models found.'); + const props = trajectoryProps(trajectory); + return new SO.Molecule.Trajectory(trajectory, props); }); } });