Jelajahi Sumber

Merge branch 'master' into atomcount

Alexander Rose 1 tahun lalu
induk
melakukan
9e91a242bf

+ 2 - 0
CHANGELOG.md

@@ -9,6 +9,8 @@ Note that since we don't clearly distinguish between a public and private interf
 - Do not call `updateFocusRepr` if default `StructureFocusRepresentation` isn't present.
 - Treat "tap" as a click in `InputObserver`
 - ModelServer ligand queries: fix atom count reported by SDF/MOL/MOL2 export
+- CCD extension: Make visuals for aromatic bonds configurable
+- Add optional `file?: CifFile` to `MmcifFormat.data`
 
 ## [v3.39.0] - 2023-09-02
 

+ 7 - 5
src/extensions/wwpdb/ccd/representation.ts

@@ -22,6 +22,7 @@ const CCDParams = (a: PluginStateObject.Molecule.Trajectory | undefined, plugin:
     representationPresetParams: PD.Optional(PD.Group(StructureRepresentationPresetProvider.CommonParams)),
     showOriginalCoordinates: PD.Optional(PD.Boolean(true, { description: `Show original coordinates for 'model' and 'ideal' structure and do not align them.` })),
     shownCoordinateType: PD.Select('ideal', PD.arrayToOptions(['ideal', 'model', 'both'] as const), { description: `What coordinate sets are visible.` }),
+    aromaticBonds: PD.Boolean(false, { description: 'Display aromatic bonds with dashes' }),
     ...TrajectoryHierarchyPresetProvider.CommonParams(a, plugin)
 });
 
@@ -77,8 +78,8 @@ export const ChemicalCompontentTrajectoryHierarchyPreset = TrajectoryHierarchyPr
             }
         }
 
-        await builder.representation.applyPreset(idealStructureProperties, representationPreset, { ...representationPresetParams, coordinateType: 'ideal', isHidden: params.shownCoordinateType === 'model' });
-        await builder.representation.applyPreset(modelStructureProperties, representationPreset, { ...representationPresetParams, coordinateType: 'model', isHidden: params.shownCoordinateType === 'ideal' });
+        await builder.representation.applyPreset(idealStructureProperties, representationPreset, { ...representationPresetParams, aromaticBonds: params.aromaticBonds, coordinateType: 'ideal', isHidden: params.shownCoordinateType === 'model' });
+        await builder.representation.applyPreset(modelStructureProperties, representationPreset, { ...representationPresetParams, aromaticBonds: params.aromaticBonds, coordinateType: 'model', isHidden: params.shownCoordinateType === 'ideal' });
 
         return { models: [idealModel, modelModel], structures: [idealStructure, modelStructure] };
     }
@@ -134,14 +135,15 @@ export const ChemicalComponentPreset = StructureRepresentationPresetProvider({
     },
     params: () => ({
         ...StructureRepresentationPresetProvider.CommonParams,
+        aromaticBonds: PD.Boolean(true),
         coordinateType: PD.Select<CCDFormat.CoordinateType>('ideal', PD.arrayToOptions(['ideal', 'model'])),
-        isHidden: PD.Boolean(false)
+        isHidden: PD.Boolean(false),
     }),
     async apply(ref, params, plugin) {
         const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
         if (!structureCell) return {};
 
-        const { coordinateType, isHidden } = params;
+        const { aromaticBonds, coordinateType, isHidden } = params;
         const components = {
             [coordinateType]: await presetStaticComponent(plugin, structureCell, 'all', { label: capitalize(coordinateType), tags: [coordinateType] })
         };
@@ -150,7 +152,7 @@ export const ChemicalComponentPreset = StructureRepresentationPresetProvider({
         const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
 
         const representations = {
-            [coordinateType]: builder.buildRepresentation(update, components[coordinateType], { type: 'ball-and-stick', typeParams }, { initialState: { isHidden } }),
+            [coordinateType]: builder.buildRepresentation(update, components[coordinateType], { type: 'ball-and-stick', typeParams: { ...typeParams, aromaticBonds } }, { initialState: { isHidden } }),
         };
         // sync UI state
         if (components[coordinateType]?.cell?.state && isHidden) {

+ 3 - 3
src/mol-canvas3d/canvas3d.ts

@@ -107,13 +107,13 @@ export { Canvas3DContext };
 
 /** Can be used to create multiple Canvas3D objects */
 interface Canvas3DContext {
-    readonly canvas: HTMLCanvasElement
+    readonly canvas?: HTMLCanvasElement
     readonly webgl: WebGLContext
     readonly input: InputObserver
     readonly passes: Passes
     readonly attribs: Readonly<Canvas3DContext.Attribs>
-    readonly contextLost: BehaviorSubject<now.Timestamp>
-    readonly contextRestored: BehaviorSubject<now.Timestamp>
+    readonly contextLost?: BehaviorSubject<now.Timestamp>
+    readonly contextRestored?: BehaviorSubject<now.Timestamp>
     readonly assetManager: AssetManager
     dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => void
 }

+ 6 - 5
src/mol-model-formats/structure/mmcif.ts

@@ -9,7 +9,7 @@
 import { Model } from '../../mol-model/structure/model/model';
 import { RuntimeContext, Task } from '../../mol-task';
 import { ModelFormat } from '../format';
-import { CifFrame, CIF } from '../../mol-io/reader/cif';
+import { CifFrame, CIF, CifFile } from '../../mol-io/reader/cif';
 import { mmCIF_Database } from '../../mol-io/reader/cif/schema/mmcif';
 import { createModels } from './basic/parser';
 import { ModelSymmetry } from './property/symmetry';
@@ -88,6 +88,7 @@ namespace MmcifFormat {
     export type Data = {
         db: mmCIF_Database,
         frame: CifFrame,
+        file?: CifFile,
         /**
          * Original source format. Some formats, including PDB, are converted
          * to mmCIF before further processing.
@@ -98,14 +99,14 @@ namespace MmcifFormat {
         return x?.kind === 'mmCIF';
     }
 
-    export function fromFrame(frame: CifFrame, db?: mmCIF_Database, source?: ModelFormat): MmcifFormat {
+    export function fromFrame(frame: CifFrame, db?: mmCIF_Database, source?: ModelFormat, file?: CifFile): MmcifFormat {
         if (!db) db = CIF.schema.mmCIF(frame);
-        return { kind: 'mmCIF', name: db._name, data: { db, frame, source } };
+        return { kind: 'mmCIF', name: db._name, data: { db, file, frame, source } };
     }
 }
 
-export function trajectoryFromMmCIF(frame: CifFrame): Task<Trajectory> {
-    const format = MmcifFormat.fromFrame(frame);
+export function trajectoryFromMmCIF(frame: CifFrame, file?: CifFile): Task<Trajectory> {
+    const format = MmcifFormat.fromFrame(frame, undefined, undefined, file);
     const basic = createBasic(format.data.db, true);
     return Task.create('Create mmCIF Model', ctx => createModels(basic, format, ctx));
 }

+ 1 - 1
src/mol-plugin-state/transforms/model.ts

@@ -304,7 +304,7 @@ 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 isCcd = block.categoryNames.includes('chem_comp_atom') && !block.categoryNames.includes('atom_site') && !block.categoryNames.includes('ihm_sphere_obj_site') && !block.categoryNames.includes('ihm_gaussian_obj_site');
-                trajectory = isCcd ? await trajectoryFromCCD(block).runInContext(ctx) : await trajectoryFromMmCIF(block).runInContext(ctx);
+                trajectory = isCcd ? await trajectoryFromCCD(block).runInContext(ctx) : await trajectoryFromMmCIF(block, a.data).runInContext(ctx);
             }
             if (trajectory.frameCount === 0) throw new Error('No models found.');
             const props = trajectoryProps(trajectory);

+ 1 - 1
src/mol-plugin/behavior/dynamic/camera.ts

@@ -213,7 +213,7 @@ export const CameraControls = PluginBehavior.create<CameraControlsProps>({
                         trackball: { flyMode }
                     });
 
-                    if (this.ctx.canvas3dContext) {
+                    if (this.ctx.canvas3dContext?.canvas) {
                         this.ctx.canvas3dContext.canvas.style.cursor = flyMode ? 'crosshair' : 'unset';
                     }
                 }

+ 7 - 2
src/mol-plugin/util/headless-screenshot.ts

@@ -57,8 +57,13 @@ export class HeadlessScreenshotHelper {
             const webgl = createContext(glContext);
             const input = InputObserver.create();
             const attribs = { ...Canvas3DContext.DefaultAttribs };
-            const passes = new Passes(webgl, new AssetManager(), attribs);
-            this.canvas3d = Canvas3D.create({ webgl, input, passes, attribs } as Canvas3DContext, options?.canvas ?? defaultCanvas3DParams());
+            const assetManager = new AssetManager();
+            const passes = new Passes(webgl, assetManager, attribs);
+            const dispose = () => {
+                input.dispose();
+                webgl.destroy();
+            };
+            this.canvas3d = Canvas3D.create({ webgl, input, passes, attribs, assetManager, dispose }, options?.canvas ?? defaultCanvas3DParams());
         }
 
         this.imagePass = this.canvas3d.getImagePass(options?.imagePass ?? defaultImagePassParams());