diff --git a/src/viewer/helpers/chain/preset.ts b/src/viewer/helpers/chain/preset.ts new file mode 100644 index 0000000..2450e4d --- /dev/null +++ b/src/viewer/helpers/chain/preset.ts @@ -0,0 +1,60 @@ + + +import { StructureRepresentationPresetProvider } from 'molstar/lib/mol-plugin-state/builder/structure/representation-preset'; +import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition'; +import { StateObjectRef } from 'molstar/lib/mol-state'; +import reprBuilder = StructureRepresentationPresetProvider.reprBuilder; +import updateFocusRepr = StructureRepresentationPresetProvider.updateFocusRepr; +import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder'; +import { StateTransform } from 'molstar/lib/mol-state/transform'; + +export const RcsbChainRepresentationPreset = StructureRepresentationPresetProvider({ + id: 'preset-superposition-representation-rcsb', + display: { + group: 'Chain', + name: 'Chain Representation', + description: 'Show representations based on the given chain asymId.' + }, + params: () => ({ + ...StructureRepresentationPresetProvider.CommonParams, + asymId: PD.Value("A"), + colorTheme: PD.Value('sequence-id'), + }), + async apply(ref, params, plugin) { + const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); + if (!structureCell) return {}; + const structure = structureCell.obj!.data; + const cartoonProps = { sizeFactor: structure.isCoarseGrained ? 0.8 : 0.2 }; + const components = Object.create(null); + const representations = Object.create(null); + const chain = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, chainSelection(params.asymId), `Chain ${params.asymId}`); + Object.assign(components, { [`Chain ${params.asymId}`]: chain }); + const { update, builder, typeParams } = reprBuilder(plugin, params); + const typeProps = { typeParams,...cartoonProps }; + const reprProps = { + typeParams: typeProps, + color: params.colorTheme as any + }; + Object.assign(representations, { + [`Chain ${params.asymId}`]: builder.buildRepresentation(update, chain, reprProps, { + tag: `Chain ${params.asymId}`, + // this only hides the visuals but the state UI will still indicate them as visible + initialState: { isHidden: false } + }) + }); + // make sure UI state is consistent + if (chain?.cell?.state) { + StateTransform.assignState(chain?.cell?.state, { isHidden: false }); + } + await update.commit({ revertOnError: false }); + // needed to apply same coloring scheme to focus representation + await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params); + return representations; + } +}); + +function chainSelection(auth_asym_id: string) { + return MS.struct.generator.atomGroups({ + 'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id]) + }); +} \ No newline at end of file diff --git a/src/viewer/helpers/preset.ts b/src/viewer/helpers/preset.ts index 33cbbe1..056e34f 100644 --- a/src/viewer/helpers/preset.ts +++ b/src/viewer/helpers/preset.ts @@ -47,11 +47,13 @@ import { } from 'molstar/lib/extensions/rcsb/assembly-symmetry/prop'; import { Task } from 'molstar/lib/mol-task'; import { PLDDTConfidenceColorThemeProvider } from 'molstar/lib/extensions/model-archive/quality-assessment/color/plddt'; +import { RcsbChainRepresentationPreset } from './chain/preset'; type BaseProps = { assemblyId?: string modelIndex?: number plddt?: 'off' | 'single-chain' | 'on' + colorTheme?: string } export { Mat4 } from 'molstar/lib/mol-math/linear-algebra'; @@ -117,8 +119,15 @@ export type NakbProps = { kind: 'nakb' } & BaseProps +type ChainProps = { + kind: 'chain', + asymId: string, +} & BaseProps + export type PresetProps = ValidationProps | StandardProps | SymmetryProps | FeatureProps | DensityProps | AlignmentProps | -MembraneProps | FeatureDensityProps | MotifProps | NakbProps | EmptyProps; +MembraneProps | FeatureDensityProps | MotifProps | NakbProps | ChainProps | EmptyProps; + + const RcsbParams = () => ({ preset: PD.Value({ kind: 'standard', assemblyId: '' }, { isHidden: true }) @@ -240,6 +249,8 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({ representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, RcsbSuperpositionRepresentationPreset, { ...presetParams, ...additions }); } else if (p.kind === 'validation') { representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, ValidationReportGeometryQualityPreset, presetParams); + } else if (p.kind === 'chain'){ + representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, RcsbChainRepresentationPreset, { asymId: p.asymId, colorTheme: p.colorTheme }); } else if (p.kind === 'symmetry' && structure?.obj) { const data = structure!.obj.data; if (!AssemblySymmetryDataProvider.get(data).value) { @@ -275,7 +286,7 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({ } else if (p.kind === 'nakb') { representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, 'auto', { ...presetParams, theme: { globalName: 'nakb', focus: { name: 'nakb' } } }); } else { - representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, 'auto', presetParams); + representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, 'auto', {...presetParams, theme: { globalName: p.colorTheme, focus: { name: p.colorTheme } } }); } // TODO align with 'motif'? diff --git a/src/viewer/index.ts b/src/viewer/index.ts index db47ad2..1fa2141 100644 --- a/src/viewer/index.ts +++ b/src/viewer/index.ts @@ -53,6 +53,7 @@ import { AssemblySymmetry } from 'molstar/lib/extensions/rcsb/assembly-symmetry/ import { wwPDBChemicalComponentDictionary } from 'molstar/lib/extensions/wwpdb/ccd/behavior'; import { ChemicalCompontentTrajectoryHierarchyPreset } from 'molstar/lib/extensions/wwpdb/ccd/representation'; import { StateTransforms } from 'molstar/lib/mol-plugin-state/transforms'; +import { Camera } from 'molstar/lib/mol-canvas3d/camera'; /** package version, filled in at bundle build time */ declare const __RCSB_MOLSTAR_VERSION__: string; @@ -355,6 +356,22 @@ export class Viewer { this._plugin.layout.events.updated.next(void 0); } + async loadMolrenderStateFromUrl(url: string) { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + this.loadPdbId(data.id, { props: buildProps(data) }).then(_ => { + this._plugin.canvas3d?.camera.setState(data.cameraState); + this.resetCamera(0); + }); + } catch (error) { + console.error('There was a problem fetching the JSON file:', error); + } + } + exportLoadedStructures(options?: { format?: 'cif' | 'bcif' }) { return exportHierarchy(this.plugin, options); } @@ -563,3 +580,43 @@ export class LigandViewer { } } } + +type MolRenderStateType = { + id: string; + colorTheme: 'plddt-confidence' | 'sequence-id' | 'polymer-index' | 'polymer-id'; + cameraState: Camera.Snapshot; +} & ({ + case: 'chain'; + asymId: string; +} | { + case: 'model'; + modelIndex: number; +} | { + case: 'assembly'; + assemblyId: string; +}); + +function buildProps(molrenderState: MolRenderStateType): PresetProps { + switch (molrenderState.case) { + case 'model': + return { + kind: 'standard', + modelIndex: molrenderState.modelIndex, + colorTheme: molrenderState.colorTheme, + }; + case 'assembly': + return { + kind: 'standard' as const, + assemblyId: molrenderState.assemblyId, + colorTheme: molrenderState.colorTheme, + }; + case 'chain': + return { + kind: 'chain' as const, + asymId: molrenderState.asymId, + colorTheme: molrenderState.colorTheme, + }; + default: + throw new Error(`Unknown case '${molrenderState}'`); + } +}