Browse Source

uniprot 3D toggle display components

bioinsilico 2 năm trước cách đây
mục cha
commit
dbf0c1cb5f

+ 2 - 1
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/UniprotPfvManagerFactory.ts

@@ -71,7 +71,8 @@ class UniprotPfvManager<R> extends AbstractPfvManager<{upAcc:string},R,{context:
                                 rowTitleComponent:UniprotRowTitleComponent,
                                 rowTitleAdditionalProps:{
                                     alignmentContext,
-                                    targetAlignment
+                                    targetAlignment,
+                                    stateManager: this.stateManager
                                 }
                             }
                         });

+ 98 - 0
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/UniprotPfvManagerFactory/UniprotRowTitleCheckbox.tsx

@@ -0,0 +1,98 @@
+/*
+* Copyright (c) 2021 RCSB PDB and contributors, licensed under MIT, See LICENSE file for more info.
+* @author Joan Segura Mora <joan.segura@rcsb.org>
+*/
+
+import * as React from "react";
+import {RcsbFvStateManager} from "../../../../../RcsbFvState/RcsbFvStateManager";
+import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
+import {Subscription} from "rxjs";
+
+interface UniprotRowTitleCheckboxInterface {
+    disabled:boolean;
+    entryId: string;
+    entityId: string;
+    tag:"aligned"|"polymer"|"non-polymer";
+    stateManager:RcsbFvStateManager
+}
+
+interface UniprotRowTitleCheckboxState {
+    checked:boolean;
+}
+
+export class UniprotRowTitleCheckbox extends React.Component <UniprotRowTitleCheckboxInterface,UniprotRowTitleCheckboxState> {
+
+    readonly state: UniprotRowTitleCheckboxState = {
+        checked: !this.props.disabled
+    };
+
+    private subscription: Subscription;
+
+    constructor(props: UniprotRowTitleCheckboxInterface) {
+        super(props);
+        this.subscribe();
+    }
+
+
+    public render():JSX.Element {
+        return (<input type={"checkbox"} disabled={this.props.disabled} checked={this.state.checked} onClick={()=>{this.click()}}/>);
+    }
+
+    public componentDidUpdate(prevProps: Readonly<UniprotRowTitleCheckboxInterface>, prevState: Readonly<UniprotRowTitleCheckboxState>, snapshot?: any) {
+        if(prevProps.disabled != this.props.disabled)
+            this.setState({checked:!this.props.disabled});
+    }
+
+    public componentWillUnmount() {
+        this.subscription.unsubscribe();
+    }
+
+    private subscribe(): void{
+        this.subscription = this.props.stateManager.subscribe<"representation-change",{label:string;isHidden:boolean;} & {tag:UniprotRowTitleCheckboxInterface["tag"];isHidden:boolean;pdb:{entryId:string;entityId:string;};}>((o)=>{
+            if(o.type == "representation-change" && o.view == "3d-view" && o.data)
+                this.structureViewerRepresentationChange(o.data);
+            if(o.type == "representation-change" && o.view == "1d-view" && o.data)
+                this.sequenceViewerRepresentationChange(o.data);
+        })
+    }
+
+    private sequenceViewerRepresentationChange(d:{tag:UniprotRowTitleCheckboxInterface["tag"];isHidden:boolean;pdb:{entryId:string;entityId:string;};}): void {
+        if(this.props.tag == "aligned" && d.tag == "polymer" && this.props.entityId == d.pdb.entityId && this.props.entryId == d.pdb.entryId)
+            this.setState({checked:!d.isHidden})
+    }
+
+    private structureViewerRepresentationChange(d:{label:string;isHidden:boolean;}): void {
+        const row: string[] = d.label.split(TagDelimiter.entity);
+        const suffix: string = row.pop()!;
+        const entryId: string = row.join(TagDelimiter.entity);
+        const entityId: string = suffix.substring(0,1);
+        if( `${this.props.entryId}${TagDelimiter.entity}${this.props.entityId}` == `${entryId}${TagDelimiter.entity}${entityId}` ){
+            //TODO this is a one to many relationship
+            /*if( d.label.includes("polymer") && this.props.tag == "polymer" && d.isHidden == this.state.checked){
+                this.setState({checked:!this.state.checked});
+            }
+            if( !d.label.includes("polymer") && this.props.tag == "non-polymer" && d.isHidden == this.state.checked){
+                this.setState({checked:!this.state.checked});
+            }*/
+        }
+
+    }
+
+    private click(): void {
+        this.setState({checked:!this.state.checked},()=>{
+            this.props.stateManager.next<"representation-change",{tag:UniprotRowTitleCheckboxInterface["tag"];isHidden:boolean;pdb:{entryId:string;entityId:string;};}>({
+                view:"1d-view",
+                type: "representation-change",
+                data:{
+                    pdb:{
+                        entryId: this.props.entryId,
+                        entityId: this.props.entityId
+                    },
+                    isHidden:!this.state.checked,
+                    tag:this.props.tag
+                }
+            });
+        });
+
+    }
+}

+ 48 - 6
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/UniprotPfvManagerFactory/UniprotRowTitleComponent.tsx

@@ -10,15 +10,33 @@ import {
     AlignmentRequestContextType
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvFactories/RcsbFvTrackFactory/TrackFactoryImpl/AlignmentTrackFactory";
 import {TargetAlignment} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
+import {RcsbFvStateManager} from "../../../../../RcsbFvState/RcsbFvStateManager";
+import {Subscription} from "rxjs";
+import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
+import {UniprotRowTitleCheckbox} from "./UniprotRowTitleCheckbox";
 
-export class UniprotRowTitleComponent extends React.Component <RcsbFvRowTitleInterface & {alignmentContext: AlignmentRequestContextType, targetAlignment: TargetAlignment}, {}> {
+interface UniprotRowTitleInterface extends RcsbFvRowTitleInterface {
+    alignmentContext: AlignmentRequestContextType;
+    targetAlignment: TargetAlignment;
+    stateManager:RcsbFvStateManager;
+
+}
+
+interface UniprotRowTitleState {
+    expandTitle: boolean;
+    disabled: boolean;
+}
+
+export class UniprotRowTitleComponent extends React.Component <UniprotRowTitleInterface, UniprotRowTitleState> {
 
     private readonly configData : RcsbFvRowConfigInterface;
+    private subscription: Subscription;
     readonly state = {
-        expandTitle: false
+        expandTitle: false,
+        disabled: true
     };
 
-    constructor(props: RcsbFvRowTitleInterface & {alignmentContext: AlignmentRequestContextType, targetAlignment: TargetAlignment}) {
+    constructor(props: UniprotRowTitleInterface) {
         super(props);
         this.configData = this.props.data;
     }
@@ -26,10 +44,34 @@ export class UniprotRowTitleComponent extends React.Component <RcsbFvRowTitleInt
     public render(): JSX.Element{
        return <div style={{textAlign:"right"}}>
            {this.props.targetAlignment.target_id}
-           <input type={"checkbox"}/>
-           <input type={"checkbox"}/>
-           <input type={"checkbox"}/>
+           <UniprotRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"aligned"} stateManager={this.props.stateManager}/>
+           <UniprotRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"polymer"} stateManager={this.props.stateManager}/>
+           <UniprotRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"non-polymer"} stateManager={this.props.stateManager}/>
        </div>;
     }
 
+    public componentDidMount(): void {
+        this.subscribe();
+    }
+
+    public componentWillUnmount() {
+        this.subscription.unsubscribe();
+    }
+
+    private subscribe(): void{
+        this.subscription = this.props.stateManager.subscribe<"representation-change",{label:string;isHidden:boolean;}>((o)=>{
+            if(o.type == "model-change" && o.view == "3d-view")
+                this.modelChange();
+        })
+    }
+
+    private modelChange(): void {
+        if(this.props.targetAlignment.target_id && this.props.stateManager.assemblyModelSate.getMap().has(this.props.targetAlignment.target_id)){
+            if(this.state.disabled)
+                this.setState({disabled:false})
+        }else if(!this.state.disabled){
+            this.setState({disabled:true})
+        }
+    }
+
 }

+ 4 - 0
src/RcsbFvState/AssemblyModelSate.ts

@@ -58,6 +58,10 @@ export class AssemblyModelSate {
         this.state.operator = opName ? currentChainInfo?.operators.filter(op=>(op.name === opName))[0] : currentChainInfo?.operators[0];
     }
 
+    public getModelChainInfo(modelId: string): {entryId: string; assemblyId: string, chains:Array<ChainInfo>;} | undefined {
+        return this.modelMap.get(modelId);
+    }
+
     public getChainInfo(asymId?:string): ChainInfo | undefined{
         if(!this.state.modelId)
             throw "modelId not define";

+ 3 - 0
src/RcsbFvStructure/StructureViewerBehaviour/AssemblyBehaviour.ts

@@ -106,6 +106,9 @@ class AssemblyBehaviour<R> implements StructureViewerBehaviourInterface {
         }
     }
 
+    public reprChange(): void {
+    }
+
     public unsubscribe(): void {
         this.subscription.unsubscribe();
     }

+ 31 - 2
src/RcsbFvStructure/StructureViewerBehaviour/UniprotBehaviour.ts

@@ -11,6 +11,8 @@ import {ViewerActionManagerInterface, ViewerCallbackManagerInterface} from "../S
 import {RcsbFvStateInterface} from "../../RcsbFvState/RcsbFvStateInterface";
 import {Subscription} from "rxjs";
 import {StructureLoaderInterface} from "../StructureUtils/StructureLoaderInterface";
+import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
+import {createSelectionExpressions} from "@rcsb/rcsb-molstar/build/src/viewer/helpers/selection";
 
 export class UniprotBehaviourObserver<R> implements StructureViewerBehaviourObserverInterface<R> {
 
@@ -52,9 +54,11 @@ class UniprotBehaviour<R> implements StructureViewerBehaviourInterface {
     }
 
     private subscribe(): Subscription {
-        return this.stateManager.subscribe<"model-change",{pdb:{entryId:string;entityId:string;}}>(async o=>{
+        return this.stateManager.subscribe<"model-change" | "representation-change",{pdb:{entryId:string;entityId:string;}} & {tag:"polymer"|"non-polymer";isHidden:boolean;}>(async o=>{
             if(o.type == "model-change" && o.view == "1d-view" && o.data)
                 await this.modelChange(o.data);
+            if(o.type == "representation-change" && o.view == "1d-view" && o.data)
+                this.reprChange(o.data);
         });
     }
 
@@ -70,10 +74,35 @@ class UniprotBehaviour<R> implements StructureViewerBehaviourInterface {
     unsubscribe(): void {
     }
 
+    reprChange(data?:{pdb:{entryId:string;entityId:string;}} & {tag:"aligned"|"polymer"|"non-polymer";isHidden:boolean;}): void {
+        if(data){
+            switch (data.tag){
+                case "aligned":
+                    const asymId: string|undefined = this.stateManager.assemblyModelSate.getModelChainInfo(`${data.pdb.entryId}${TagDelimiter.entity}${data.pdb.entityId}`)?.chains[0].label;
+                    const componentId: string = `${data.pdb.entryId}${TagDelimiter.entity}${data.pdb.entityId}${TagDelimiter.instance}${asymId}${TagDelimiter.assembly}${"polymer"}`;
+                    this.structureViewer.displayComponent(componentId, !data.isHidden);
+                    break;
+                case "polymer":
+                    this.stateManager.assemblyModelSate.getModelChainInfo(`${data.pdb.entryId}${TagDelimiter.entity}${data.pdb.entityId}`)?.chains.map(ch=>ch.label).forEach(asymId=>{
+                        const componentId: string = `${data.pdb.entryId}${TagDelimiter.entity}${data.pdb.entityId}${TagDelimiter.instance}${asymId}${TagDelimiter.assembly}${data.tag}`;
+                        this.structureViewer.displayComponent(componentId, !data.isHidden);
+                    });
+                    break;
+                case "non-polymer":
+                    createSelectionExpressions(data.pdb.entryId).map(expression=>expression.tag).filter(tag=>tag!="water").forEach(tag=>{
+                        const componentId: string = `${data.pdb.entryId}${TagDelimiter.entity}${data.pdb.entityId}${TagDelimiter.assembly}${tag}`;
+                        this.structureViewer.displayComponent(componentId, !data.isHidden);
+                    });
+                    break;
+            }
+        }
+    }
+
     async modelChange(data?:{pdb:{entryId:string;entityId:string;}}): Promise<void> {
         if(data)
             await this.structureLoader.load(this.structureViewer, data.pdb);
-        console.log(this.stateManager.assemblyModelSate.getMap());
     }
 
+
+
 }

+ 1 - 0
src/RcsbFvStructure/StructureViewerBehaviourInterface.ts

@@ -16,5 +16,6 @@ export interface StructureViewerBehaviourInterface {
     hoverChange(): void;
     featureClick(): void;
     modelChange(): void;
+    reprChange(): void;
     unsubscribe(): void;
 }

+ 5 - 1
src/RcsbFvStructure/StructureViewers/MolstarViewer/MolstarActionManager.ts

@@ -55,14 +55,16 @@ export class MolstarActionManager implements ViewerActionManagerInterface<LoadMo
     private readonly viewer: Viewer;
 
     private readonly innerSelectionFlag: DataContainer<boolean>;
+    private readonly innerReprChangeFlag: DataContainer<boolean>;
     private readonly modelMapManager: ViewerModelMapManagerInterface<LoadMolstarInterface>;
     private readonly componentMap: Map<string, StructureComponentRef> = new Map<string, StructureComponentRef>();
     private readonly loadingFlag: DataContainer<boolean>;
 
-    constructor(config:{viewer: Viewer;modelMapManager: ViewerModelMapManagerInterface<LoadMolstarInterface>;innerSelectionFlag: DataContainer<boolean>; loadingFlag: DataContainer<boolean>;}) {
+    constructor(config:{viewer: Viewer;modelMapManager: ViewerModelMapManagerInterface<LoadMolstarInterface>;innerSelectionFlag: DataContainer<boolean>;  innerReprChangeFlag: DataContainer<boolean>; loadingFlag: DataContainer<boolean>;}) {
         this.viewer = config.viewer;
         this.modelMapManager = config.modelMapManager;
         this.innerSelectionFlag = config.innerSelectionFlag;
+        this.innerReprChangeFlag = config.innerReprChangeFlag;
         this.loadingFlag = config.loadingFlag;
     }
 
@@ -281,6 +283,7 @@ export class MolstarActionManager implements ViewerActionManagerInterface<LoadMo
             return this.getComponentDisplay(componentLabel);
     }
     private changeComponentDisplay(componentLabel: string, visibilityFlag: boolean): void{
+        this.innerReprChangeFlag.set(true);
         if(this.componentMap.has(componentLabel) && this.getComponentDisplay(componentLabel) != visibilityFlag) {
             this.viewer.plugin.managers.structure.component.toggleVisibility([this.componentMap.get(componentLabel)!]);
         }else if(!this.componentMap.has(componentLabel)){
@@ -295,6 +298,7 @@ export class MolstarActionManager implements ViewerActionManagerInterface<LoadMo
                 }
             }
         }
+        this.innerReprChangeFlag.set(false);
     }
     private getComponentDisplay(componentLabel: string): boolean | undefined{
         if(this.componentMap.has(componentLabel)) {

+ 12 - 4
src/RcsbFvStructure/StructureViewers/MolstarViewer/MolstarCallbackManager.ts

@@ -30,21 +30,30 @@ export class MolstarCallbackManager implements ViewerCallbackManagerInterface{
     private readonly loadingFlag: DataContainerReader<boolean>;
     private readonly modelMapManager: ModelMapType;
     private readonly innerSelectionFlag: DataContainer<boolean>;
+    private readonly innerReprChangeFlag: DataContainer<boolean>;
 
     private selectSubs: Subscription;
     private hoverSubs: Subscription;
     private modelChangeSubs: Subscription;
+    private reprChangeSubs: Subscription;
 
-    constructor(config:{viewer: Viewer; stateManager: RcsbFvStateManager;loadingFlag: DataContainerReader<boolean>;modelMapManager: ModelMapType;innerSelectionFlag: DataContainer<boolean>;}) {
+    constructor(config:{viewer: Viewer; stateManager: RcsbFvStateManager;loadingFlag: DataContainerReader<boolean>;modelMapManager: ModelMapType;innerSelectionFlag: DataContainer<boolean>; innerReprChangeFlag: DataContainer<boolean>;}) {
         this.viewer = config.viewer;
         this.stateManager = config.stateManager;
         this.loadingFlag = config.loadingFlag;
         this.modelMapManager = config.modelMapManager;
         this.innerSelectionFlag = config.innerSelectionFlag;
+        this.innerReprChangeFlag = config.innerReprChangeFlag;
     }
 
     public subscribeRepresentationChange(): Subscription{
-        return new Subscription();
+        this.reprChangeSubs = this.viewer.plugin.state.data.events.cell.stateUpdated.subscribe((s)=>{
+            if(this.innerReprChangeFlag.get())
+                return;
+            if(s.cell.obj?.tags?.find(t => t.indexOf('structure-component-') === 0))
+                this.stateManager.next<"representation-change",{label:string;isHidden:boolean;}>({type:"representation-change", view:"3d-view", data:{label:s.cell.obj?.label, isHidden:!!s.cell.state.isHidden}});
+        });
+        return this.reprChangeSubs;
     }
 
     public subscribeHover(): Subscription{
@@ -77,9 +86,8 @@ export class MolstarCallbackManager implements ViewerCallbackManagerInterface{
 
     public subscribeSelection(): Subscription {
         this.selectSubs = this.viewer.plugin.managers.structure.selection.events.changed.subscribe(()=>{
-            if(this.innerSelectionFlag.get()) {
+            if(this.innerSelectionFlag.get())
                 return;
-            }
             if(this.viewer.plugin.managers.structure.selection.additionsHistory.length > 0) {
                 const currentLoci: Loci = this.viewer.plugin.managers.structure.selection.additionsHistory[0].loci;
                 const loc: StructureElement.Location = StructureElement.Location.create(currentLoci.structure);

+ 4 - 1
src/RcsbFvStructure/StructureViewers/MolstarViewer/MolstarManagerFactory.ts

@@ -12,6 +12,7 @@ export class MolstarManagerFactory implements ViewerManagerFactoryInterface<Load
     public getViewerManagerFactory(stateManager: RcsbFvStateManager, viewerParams: {viewerElement: string | HTMLElement, viewerProps: Partial<ViewerProps>}) {
         const loadingFlag: DataContainer<boolean> = new DataContainer(false);
         const innerSelectionFlag: DataContainer<boolean> = new DataContainer(false);
+        const innerReprChangeFlag: DataContainer<boolean> = new DataContainer(false);
         const viewer = new Viewer(viewerParams.viewerElement, {
             ...viewerParams,
             layoutShowControls:false,
@@ -29,12 +30,14 @@ export class MolstarManagerFactory implements ViewerManagerFactoryInterface<Load
             stateManager: stateManager,
             loadingFlag: loadingFlag,
             modelMapManager: modelMapManager,
-            innerSelectionFlag: innerSelectionFlag
+            innerSelectionFlag: innerSelectionFlag,
+            innerReprChangeFlag: innerReprChangeFlag
         });
         const actionManager = new MolstarActionManager({
             viewer: viewer,
             modelMapManager: modelMapManager,
             innerSelectionFlag: innerSelectionFlag,
+            innerReprChangeFlag: innerReprChangeFlag,
             loadingFlag: loadingFlag
         });
         return {

+ 8 - 4
src/RcsbFvStructure/StructureViewers/MolstarViewer/TrajectoryPresetProvider/AlignmentRepresentationPresetProvider.ts

@@ -63,9 +63,9 @@ export const AlignmentRepresentationPresetProvider = StructureRepresentationPres
                         MS.struct.generator.atomGroups({
                             'chain-test': MS.core.rel.eq([MS.ammp('label_asym_id'), asymId])
                         }),
-                        uniqid(`${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.instance}${asymId}_${operators.join(",")}`),
+                        uniqid(`${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.instance}${asymId}${TagDelimiter.entity}${operators.join(",")}`),
                         {
-                            label: `${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.instance}${asymId}-${operators.join(",")}`
+                            label: `${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.instance}${asymId}${TagDelimiter.assembly}${type}`
                         }
                     );
                     //TODO This needs to be called after tryCreateComponentFromExpression
@@ -77,7 +77,7 @@ export const AlignmentRepresentationPresetProvider = StructureRepresentationPres
                     builder.buildRepresentation(update, comp, {
                         color: PLDDTConfidenceColorThemeProvider.isApplicable({ structure }) ? PLDDTConfidenceColorThemeProvider.name as ColorTheme.BuiltIn : "chain-id",
                         type: "cartoon"
-                    })
+                    });
                     await update.commit({ revertOnError: false });
                 }
             }
@@ -99,7 +99,11 @@ export const AlignmentRepresentationPresetProvider = StructureRepresentationPres
                 });
                 builder.buildRepresentation(update, comp, {
                     type: expression.type
-                });
+                },expression.tag == "water" ? {
+                    initialState:{
+                        isHidden:true
+                    }
+                } : undefined);
                 await update.commit({ revertOnError: false });
             }
 

+ 1 - 0
src/RcsbFvStructure/StructureViewers/StructureViewer.ts

@@ -35,6 +35,7 @@ export class StructureViewer<R,S> implements StructureViewerInterface<R,S> {
 
         this.subscribeSelection();
         this.subscribeHover();
+        this.subscribeRepresentationChange();
         this.subscribeModelChange();
     }