Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom sequence component #14

Open
wants to merge 37 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fcc0c6c
Add SequenceView component
p3rcypj Dec 4, 2024
e18e3a9
Fire sequenceComplete event
p3rcypj Dec 4, 2024
8b393e9
Add chain on props
p3rcypj Dec 5, 2024
4bd77a7
PluginContext
p3rcypj Dec 10, 2024
54619ad
Fix last commit
p3rcypj Dec 10, 2024
e6834fc
Chain changed
p3rcypj Dec 10, 2024
8f6e656
Chain update
p3rcypj Dec 10, 2024
0a7efd2
Sync from viewer to molstar
p3rcypj Dec 10, 2024
6ce87b4
On dependency change
p3rcypj Dec 16, 2024
acfe984
Update with entity and on ligand changed
p3rcypj Dec 16, 2024
d90b85d
Revert protvista on previous commit
p3rcypj Dec 16, 2024
a149c5f
Fix protvista events
p3rcypj Dec 16, 2024
8a513e4
Fix protvista
p3rcypj Dec 17, 2024
2c8e8ae
Await on load
p3rcypj Dec 17, 2024
0db82c4
Ignore events for ligand view. Sync events for init states
p3rcypj Dec 19, 2024
57fa85b
Debug as errors
p3rcypj Dec 19, 2024
ef94513
Hide toasts
p3rcypj Dec 19, 2024
82178f1
Remove comment
p3rcypj Dec 20, 2024
50dc732
Bump beta
p3rcypj Dec 20, 2024
e9ca110
beta.4 package-lock.json
p3rcypj Dec 20, 2024
6c79661
Change error message
p3rcypj Dec 20, 2024
90be531
beta.5
p3rcypj Dec 20, 2024
d5ffc61
lock
p3rcypj Dec 20, 2024
baea2ee
Bump beta
p3rcypj Jan 19, 2025
5eccfc5
Formatting and only polymers option
p3rcypj Jan 19, 2025
c313e0f
Give name to class
p3rcypj Jan 19, 2025
3411f72
Added comment
p3rcypj Jan 19, 2025
262784c
Missplacement of whitespaces
p3rcypj Jan 19, 2025
13a857f
Select taking into account uniprotId
p3rcypj Jan 27, 2025
3310b31
.lock
p3rcypj Jan 27, 2025
531eabf
Enforce smaller line length
p3rcypj Feb 3, 2025
afb5227
Abstract code into private methods
p3rcypj Feb 3, 2025
d27ab2c
EOF
p3rcypj Feb 3, 2025
5b3d7ba
Fix giving entity instead of chain
p3rcypj Feb 3, 2025
6b2b638
Add proteinId through pdbePlugin class
p3rcypj Feb 3, 2025
ca0eff2
Bump beta
p3rcypj Feb 3, 2025
2cc8e21
bump version
p3rcypj Feb 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@3dbionotes/pdbe-molstar",
"version": "3.1.0-est-2",
"version": "3.1.0-est-2-beta.5",
"description": "Molstar implementation for PDBe",
"main": "index.js",
"scripts": {
Expand Down
74 changes: 49 additions & 25 deletions src/app/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { createPluginUI, DefaultPluginUISpec, InitParams, DefaultParams } from './spec';
import { PluginContext } from 'Molstar/mol-plugin/context';
import { PluginCommands } from 'Molstar/mol-plugin/commands';
import { SequenceView } from "Molstar/mol-plugin-ui/sequence"
import { PluginStateObject } from 'Molstar/mol-plugin-state/objects';
import { StateTransform } from 'Molstar/mol-state';
import { Loci, EmptyLoci } from 'Molstar/mol-model/loci';
Expand Down Expand Up @@ -46,6 +44,9 @@ import { AnimateAssemblyUnwind } from 'Molstar/mol-plugin-state/animation/built-
import { DownloadDensity, EmdbDownloadProvider } from 'molstar/lib/mol-plugin-state/actions/volume';
import { ControlsWrapper } from 'molstar/lib/mol-plugin-ui/plugin';
import { PluginToast } from 'molstar/lib/mol-plugin/util/toast';
import { getEntityChainPairs } from './ui/sequence';
import { initSequenceView } from './ui/sequence-wrapper';
import { PluginContext } from 'molstar/lib/mol-plugin/context';

require("Molstar/mol-plugin-ui/skin/dark.scss");

Expand All @@ -64,6 +65,13 @@ class PDBeMolstarPlugin {
readonly events = {
loadComplete: this._ev<boolean>(),
updateComplete: this._ev<boolean>(),
sequenceComplete: this._ev<any>(),
chainUpdate: this._ev<string>(), // chainId
ligandUpdate: this._ev<{ ligandId: string, chainId: string }>(),
dependencyChanged: {
onChainUpdate: this._ev<(chainId: string) => void>(),
isLigandView: this._ev<() => boolean>()
},
};

plugin: PluginContext;
Expand All @@ -76,6 +84,8 @@ class PDBeMolstarPlugin {
isSelectedColorUpdated = false;
toasts: string[] = [];

chainSelected: string | undefined = undefined;

async render(target: string | HTMLElement, options: InitParams) {
if (!options) return;
this.initParams = { ...DefaultParams };
Expand Down Expand Up @@ -158,7 +168,7 @@ class PDBeMolstarPlugin {
left: showDebugPanels ? LeftPanelControls : "none",
right: showDebugPanels ? ControlsWrapper : "none",
top: "none",
bottom: SequenceView,
bottom: this.initParams.onChainUpdate && this.initParams.isLigandView ? initSequenceView(this, this.initParams.onChainUpdate, this.initParams.isLigandView).component : "none",
},
viewport: {
controls: PDBeViewportControls,
Expand All @@ -170,8 +180,8 @@ class PDBeMolstarPlugin {
structureTools: this.initParams.superposition
? PDBeSuperpositionStructureTools
: this.initParams.ligandView
? PDBeLigandViewStructureTools
: PDBeStructureTools,
? PDBeLigandViewStructureTools
: PDBeStructureTools,
};

if (this.initParams.alphafoldView) {
Expand All @@ -189,8 +199,8 @@ class PDBeMolstarPlugin {
};
}

if(this.initParams.sequencePanel) {
if(pdbePluginSpec.components.controls?.top) delete pdbePluginSpec.components.controls.top;
if (this.initParams.sequencePanel) {
if (pdbePluginSpec.components.controls?.top) delete pdbePluginSpec.components.controls.top;
}

pdbePluginSpec.config = [
Expand Down Expand Up @@ -343,7 +353,7 @@ class PDBeMolstarPlugin {
// Load Molecule CIF or coordQuery and Parse
let dataSource = this.getMoleculeSrcUrl();
if (dataSource) {
this.load({
await this.load({
url: dataSource.url,
label: this.initParams.moleculeId,
format: dataSource.format as BuiltInTrajectoryFormat,
Expand All @@ -360,7 +370,7 @@ class PDBeMolstarPlugin {
// Event handling
CustomEvents.add(this.plugin, this.targetElement);

if(!dataSource) { //allow plugin to load without pdbId
if (!dataSource) { //allow plugin to load without pdbId
this.events.loadComplete.next(true);
}
}
Expand All @@ -384,7 +394,7 @@ class PDBeMolstarPlugin {
if (this.initParams.ligandView.label_comp_id) {
queryParams.push(
"label_comp_id=" +
this.initParams.ligandView.label_comp_id
this.initParams.ligandView.label_comp_id
);
} else if (this.initParams.ligandView.auth_seq_id) {
queryParams.push(
Expand All @@ -394,17 +404,15 @@ class PDBeMolstarPlugin {
if (this.initParams.ligandView.auth_asym_id)
queryParams.push(
"auth_asym_id=" +
this.initParams.ligandView.auth_asym_id
this.initParams.ligandView.auth_asym_id
);
}
query = "residueSurroundings?" + queryParams.join("&");
sep = "&";
}
let url = `${
this.initParams.pdbeUrl
}model-server/v1/${id}/${query}${sep}encoding=${
this.initParams.encoding
}${this.initParams.lowPrecisionCoords ? "&lowPrecisionCoords=1" : ""}`;
let url = `${this.initParams.pdbeUrl
}model-server/v1/${id}/${query}${sep}encoding=${this.initParams.encoding
}${this.initParams.lowPrecisionCoords ? "&lowPrecisionCoords=1" : ""}`;
let isBinary = this.initParams.encoding === "bcif" ? true : false;
let format = "mmcif";

Expand Down Expand Up @@ -591,7 +599,7 @@ class PDBeMolstarPlugin {
{ url: Asset.Url(url, downloadOptions), label, isBinary },
{ state: { isGhost: true } }
);

const trajectory = await this.plugin.builders.structure
.parseTrajectory(data, format)
.then((trajectory) => {
Expand Down Expand Up @@ -641,7 +649,7 @@ class PDBeMolstarPlugin {
.length - 1;
const pivot =
this.plugin.managers.structure.hierarchy.selection.structures[
pivotIndex
pivotIndex
];
if (pivot && pivot.cell.parent)
this.assemblyRef = pivot.cell.transform.ref;
Expand Down Expand Up @@ -680,9 +688,19 @@ class PDBeMolstarPlugin {
await this.createLigandStructure(isBranchedView);
}

// Sequence Viewer
try {
const ONLY_POLYMERS = true;
const sequenceOptions = getEntityChainPairs(this.plugin.state.data, ONLY_POLYMERS);
this.events.sequenceComplete.next(sequenceOptions);
} catch (error) {
console.error(error);
this.events.sequenceComplete.error(error);
}

this.events.loadComplete.next(true);
}

applyVisualParams = () => {
const TagRefs: any = {
"structure-component-static-polymer": "polymer",
Expand Down Expand Up @@ -812,22 +830,22 @@ class PDBeMolstarPlugin {

getLociByPLDDT(score: number, structureNumber?: number) {
let assemblyRef = this.assemblyRef;
if(structureNumber) {
if (structureNumber) {
assemblyRef = this.plugin.managers.structure.hierarchy.current.structures[structureNumber - 1].cell.transform.ref;
}

if(assemblyRef === '') return EmptyLoci;
if (assemblyRef === '') return EmptyLoci;
const structure = this.plugin.state.data.select(assemblyRef)[0]?.obj as
| PluginStateObject.Molecule.Structure
| undefined;
const data = structure?.data;
if(!data) return EmptyLoci;
if (!data) return EmptyLoci;
return AlphafoldView.getLociByPLDDT(score, data);
}



normalizeColor(colorVal: any, defaultColor?: Color){
normalizeColor(colorVal: any, defaultColor?: Color) {
let color = Color.fromRgb(170, 170, 170);
try {
if (typeof colorVal.r !== "undefined") {
Expand Down Expand Up @@ -887,7 +905,7 @@ class PDBeMolstarPlugin {
if (params.structureNumber) {
structureData = [
this.plugin.managers.structure.hierarchy.current.structures[
params.structureNumber - 1
params.structureNumber - 1
],
];
}
Expand Down Expand Up @@ -1090,7 +1108,7 @@ class PDBeMolstarPlugin {
// Load Molecule CIF or coordQuery and Parse
let dataSource = this.getMoleculeSrcUrl();
if (dataSource) {
this.load(
await this.load(
{
url: dataSource.url,
label: this.initParams.moleculeId,
Expand All @@ -1104,6 +1122,12 @@ class PDBeMolstarPlugin {

this.events.updateComplete.next(true);
},
updateChain: (chainId: string) => this.events.chainUpdate.next(chainId),
updateLigand: (options: { chainId: string; ligandId: string }) => this.events.ligandUpdate.next(options),
updateDependency: {
onChainUpdate: (callback: (chainId: string) => void) => this.events.dependencyChanged.onChainUpdate.next(callback),
isLigandView: (callback: () => boolean) => this.events.dependencyChanged.isLigandView.next(callback),
},
visibility: (data: {
polymer?: boolean;
het?: boolean;
Expand Down
6 changes: 5 additions & 1 deletion src/app/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export type InitParams = {
selectColor?: {r: number, g: number, b: number}, highlightColor?: {r: number, g: number, b: number}, superpositionParams?: {matrixAccession?: string, segment?: number, cluster?: number[], superposeCompleteCluster?: boolean, ligandView?: boolean},
hideStructure?: ['polymer', 'het', 'water', 'carbs', 'nonStandard', 'coarse'], visualStyle?: 'cartoon' | 'ball-and-stick', encoding: 'cif' | 'bcif'
granularity?: Loci.Granularity, selection?: { data: QueryParam[], nonSelectedColor?: any, clearPrevious?: boolean }, mapSettings: any, [key: string]: any;
onChainUpdate?: (chainId: string) => void;
isLigandView?: () => boolean;
}

export const DefaultParams: InitParams = {
Expand Down Expand Up @@ -105,5 +107,7 @@ export const DefaultParams: InitParams = {
landscape: false,
subscribeEvents: false,
alphafoldView: false,
sequencePanel: false
sequencePanel: false,
onChainUpdate: undefined,
isLigandView: undefined,
};
22 changes: 11 additions & 11 deletions src/app/subscribe-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ export function subscribeToComponentEvents(wrapperCtx: any) {
// Create query object from event data
if(e.detail.start && e.detail.end){
highlightQuery = {
start_residue_number: parseInt(e.detail.start),
end_residue_number: parseInt(e.detail.end)
start_auth_residue_number: parseInt(e.detail.start),
end_auth_residue_number: parseInt(e.detail.end)
};
}

if(e.detail.feature && e.detail.feature.entityId) highlightQuery['entity_id'] = e.detail.feature.entityId + '';
if(e.detail.feature && e.detail.feature.bestChainId) highlightQuery['struct_asym_id'] = e.detail.feature.bestChainId;
if(e.detail.feature && e.detail.feature.chainId) highlightQuery['struct_asym_id'] = e.detail.feature.chainId;
if(e.detail.feature && e.detail.feature.bestChainId) highlightQuery['auth_asym_id'] = e.detail.feature.bestChainId;
if(e.detail.feature && e.detail.feature.chainId) highlightQuery['auth_asym_id'] = e.detail.feature.chainId;

if(highlightQuery) wrapperCtx.visual.highlight({data: [highlightQuery]});
}
Expand All @@ -84,11 +84,11 @@ export function subscribeToComponentEvents(wrapperCtx: any) {

const params = (detail.fragments || []).map((fragment): QueryParam => {
return {
start_residue_number: (fragment.start),
end_residue_number: (fragment.end),
start_auth_residue_number: (fragment.start),
end_auth_residue_number: (fragment.end),
color: fragment.color,
entity_id: fragment.feature?.entityId,
struct_asym_id: fragment.feature?.bestChainId,
auth_asym_id: fragment.feature?.bestChainId,
};
});

Expand All @@ -115,14 +115,14 @@ export function subscribeToComponentEvents(wrapperCtx: any) {
// Create query object from event data
if(e.detail.start && e.detail.end){
highlightQuery = {
start_residue_number: parseInt(e.detail.start),
end_residue_number: parseInt(e.detail.end)
start_auth_residue_number: parseInt(e.detail.start),
end_auth_residue_number: parseInt(e.detail.end)
};
}

if(e.detail.feature && e.detail.feature.entityId) highlightQuery['entity_id'] = e.detail.feature.entityId + '';
if(e.detail.feature && e.detail.feature.bestChainId) highlightQuery['struct_asym_id'] = e.detail.feature.bestChainId;
if(e.detail.feature && e.detail.feature.chainId) highlightQuery['struct_asym_id'] = e.detail.feature.chainId;
if(e.detail.feature && e.detail.feature.bestChainId) highlightQuery['auth_asym_id'] = e.detail.feature.bestChainId;
if(e.detail.feature && e.detail.feature.chainId) highlightQuery['auth_asym_id'] = e.detail.feature.chainId;

if(e.detail.feature && e.detail.feature.accession && e.detail.feature.accession.split(' ')[0] === 'Chain' || e.detail.feature.tooltipContent === 'Ligand binding site') {
showInteraction = true;
Expand Down
24 changes: 24 additions & 0 deletions src/app/ui/sequence-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import { SequenceView } from "./sequence";
import { PDBeMolstarPlugin } from "..";

export function initSequenceView(
plugin: PDBeMolstarPlugin,
onChainUpdate: (chainId: string) => void,
isLigandView: () => boolean
) {
return {
component: class extends React.Component<{}> {
render() {
return (
<SequenceView
{...this.props}
plugin={plugin}
onChainUpdate={onChainUpdate}
isLigandView={isLigandView}
/>
);
}
},
};
}
Loading