Browse Source

Assembly interfaces

bioinsilico 3 years ago
parent
commit
80b430fb1c

+ 5 - 0
CHANGELOG.md

@@ -2,6 +2,11 @@
 
 [Semantic Versioning](https://semver.org/)
 
+## [1.3.0-interfaces] - 2021-12-15
+### Improvements
+- New class `AssemblyModelSate` to handle the assembly selection state in `RcsbFvSequence.SequenceViews.AssemblyView.AssemblyView` class
+- New callback `operatorChangeCallback` function attached to operator dropdown menu changes
+
 ## [1.2.0] - 2021-12-07
 ### Improvements
 - Support for assembly instance operators

File diff suppressed because it is too large
+ 197 - 11720
package-lock.json


+ 3 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "1.2.0-interfaces",
+  "version": "1.3.0-interfaces.1",
   "description": "RCSB Molstar/Saguaro Web App",
   "main": "build/dist/app.js",
   "files": [
@@ -85,10 +85,10 @@
     "webpack-cli": "^4.9.1"
   },
   "dependencies": {
-    "@rcsb/rcsb-api-tools": "^2.2.1-interface.12",
+    "@rcsb/rcsb-api-tools": "^2.2.1-interface.14",
     "@rcsb/rcsb-molstar": "^2.0.0-dev.10",
     "@rcsb/rcsb-saguaro": "^2.0.0",
-    "@rcsb/rcsb-saguaro-app": "file:../rcsb-saguaro-app",
+    "@rcsb/rcsb-saguaro-app": "^4.0.0-interfaces.1",
     "molstar": "^2.4.1",
     "react-select": "^3.0.8"
   },

+ 3 - 1
src/RcsbFv3D/RcsbFv3DAssembly.tsx

@@ -3,14 +3,16 @@ import {RcsbFv3DAbstract, RcsbFv3DAbstractInterface} from "./RcsbFv3DAbstract";
 import {RcsbRepresentationPreset} from "../RcsbFvStructure/StructurePlugins/StructureRepresentation";
 import {RcsbFvAdditionalConfig} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
 import {InstanceSequenceConfig} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
+import {OperatorInfo} from "../RcsbFvStructure/SaguaroPluginInterface";
 
+type RcsbFv3DAssemblyAdditionalConfig = RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void};
 export interface RcsbFv3DAssemblyInterface extends RcsbFv3DAbstractInterface {
    config: {
         entryId: string;
         title?: string;
         subtitle?: string;
     };
-    additionalConfig?: RcsbFvAdditionalConfig;
+    additionalConfig?: RcsbFv3DAssemblyAdditionalConfig;
     instanceSequenceConfig?: InstanceSequenceConfig;
     useOperatorsFlag?:boolean;
 }

+ 74 - 0
src/RcsbFvSequence/SequenceViews/AssemblyView/AssemblyModelSate.ts

@@ -0,0 +1,74 @@
+import {ChainInfo, OperatorInfo, SaguaroPluginModelMapType} from "../../../RcsbFvStructure/SaguaroPluginInterface";
+
+interface AssemblyModelStateInterface {
+    modelId: string;
+    entryId:string;
+    assemblyId:string;
+    labelAsymId:string;
+    operator: OperatorInfo
+}
+
+export class AssemblyModelSate {
+
+    private modelMap:SaguaroPluginModelMapType;
+    private state: Partial<AssemblyModelStateInterface> = {};
+
+    constructor(modelMap?:SaguaroPluginModelMapType) {
+        if(modelMap)
+            this.modelMap = modelMap;
+    }
+
+    public setMap(modelMap:SaguaroPluginModelMapType): void{
+        this.modelMap = modelMap;
+        this.setFirstModel();
+    }
+
+    public getMap(): SaguaroPluginModelMapType{
+        return this.modelMap;
+    }
+
+    public set(state: Partial<AssemblyModelStateInterface>): void{
+        this.state = {...this.state,...state};
+    }
+
+    public get(key: keyof AssemblyModelStateInterface): string|OperatorInfo|undefined {
+        return this.state[key];
+    }
+
+    public getString(key: keyof Omit<AssemblyModelStateInterface,"operator">): string {
+        if(!this.state[key])
+            throw `${key} is undefined`;
+        return this.state[key]!;
+    }
+
+    public getOperator(): OperatorInfo | undefined {
+        return this.state.operator;
+    }
+
+    public forEach(f: (v:{entryId: string; assemblyId: string, chains:Array<ChainInfo>;},k:string)=>void): void{
+        this.modelMap.forEach((v,k)=>f(v,k));
+    }
+
+
+    public setOperator(asymId?:string, opName?:string) {
+        const currentChainInfo: ChainInfo|undefined = this.getChainInfo(asymId??this.state.labelAsymId);
+        this.state.operator = opName ? currentChainInfo?.operators.filter(op=>(op.name === opName))[0] : currentChainInfo?.operators[0];
+    }
+
+    public getChainInfo(asymId?:string): ChainInfo | undefined{
+        if(!this.state.modelId)
+            throw "modelId not define";
+        if(asymId)
+            return this.modelMap.get(this.state.modelId)?.chains.find(ch=>ch.label===asymId);
+        else
+            return this.modelMap.get(this.state.modelId)?.chains.find(ch=>ch.label===this.state.labelAsymId);
+    }
+
+    private setFirstModel(): void{
+        this.state.modelId = Array.from(this.modelMap.keys())[0];
+        this.state.entryId = this.modelMap.get(this.state.modelId)!.entryId;
+        this.state.assemblyId = this.modelMap.get(this.state.modelId)!.assemblyId;
+        this.state.operator = undefined;
+    }
+
+}

+ 117 - 58
src/RcsbFvSequence/SequenceViews/AssemblyView/AssemblyView.tsx

@@ -3,7 +3,7 @@ import * as React from "react";
 
 import {RcsbFvDOMConstants} from "../../../RcsbFvConstants/RcsbFvConstants";
 import {
-    buildInstanceSequenceFv,
+    buildInstanceSequenceFv, RcsbFvContextManager,
     unmount
 } from "@rcsb/rcsb-saguaro-app";
 import {AbstractView, AbstractViewInterface} from "../AbstractView";
@@ -13,7 +13,7 @@ import {
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
 import {RcsbFvBoardConfigInterface, RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
 import {
-    ChainInfo,
+    ChainInfo, OperatorInfo,
     SaguaroPluginInterface,
     SaguaroPluginModelMapType, SaguaroRange, SaguaroRegionList
 } from "../../../RcsbFvStructure/SaguaroPluginInterface";
@@ -28,25 +28,24 @@ import {
     RcsbFvModulePublicInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
 import {RcsbFvUI} from "@rcsb/rcsb-saguaro-app";
+import {AnnotationFeatures, Source, Type} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
+import {PolymerEntityInstanceInterface} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/Translators/PolymerEntityInstancesCollector";
+import {InterfaceInstanceTranslate} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbUtils/InterfaceInstanceTranslate";
+import {AssemblyModelSate} from "./AssemblyModelSate";
 
 export interface AssemblyViewInterface {
     entryId: string;
-    additionalConfig?: RcsbFvAdditionalConfig;
+    additionalConfig?: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void};
     instanceSequenceConfig?: InstanceSequenceConfig;
     useOperatorsFlag?:boolean;
 }
 
 export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractViewInterface, {}>{
 
-    private currentLabelAsymId: string;
-    private currentEntryId: string;
-    private currentModelId: string;
-    private currentOpName: string | undefined = undefined;
-    private currentModelNumber: string;
+    private readonly ams: AssemblyModelSate = new AssemblyModelSate();
     private createComponentThreshold: number = 3;
     private innerSelectionFlag: boolean = false;
     private currentSelectedComponentId: string;
-    private currentModelMap:SaguaroPluginModelMapType;
     private boardConfig: Partial<RcsbFvBoardConfigInterface>;
     private rcsbFvModule: RcsbFvModulePublicInterface | null;
     //private readonly componentSet = new Map<string, {current: Set<string>, previous: Set<string>}>();
@@ -142,15 +141,18 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
         const allSel: Array<SaguaroRegionList> | undefined = this.props.selectorManager.getSelection(mode);
         if(allSel == null || allSel.length ===0) {
             this.rcsbFvModule?.getFv().clearSelection(mode);
-        }else if(mode === 'select' && this.props.selectorManager.getLastSelection('select')?.labelAsymId != null && this.props.selectorManager.getLastSelection('select')?.labelAsymId != this.currentLabelAsymId){
-            const authId: string | undefined = this.currentModelMap
-                .get(this.currentModelId)?.chains
-                .filter(ch=>(ch.label===this.props.selectorManager.getLastSelection('select')?.labelAsymId))[0]?.auth;
-            await this.modelChangeCallback(this.currentModelMap, authId, this.props.selectorManager.getLastSelection('select')?.operatorName);
+        }else if(mode === 'select' && this.props.selectorManager.getLastSelection('select')?.labelAsymId != null && this.props.selectorManager.getLastSelection('select')?.labelAsymId != this.ams.getString("labelAsymId")){
+            const authId: string | undefined = this.ams.getChainInfo(this.props.selectorManager.getLastSelection('select')?.labelAsymId!)?.auth;
+            await this.modelChangeCallback(this.ams.getMap(), authId, this.props.selectorManager.getLastSelection('select')?.operatorName);
         }else{
-            if(mode === 'select' && this.props.selectorManager.getLastSelection('select')?.operatorName && this.props.selectorManager.getLastSelection('select')?.operatorName != this.currentOpName)
+            if(mode === 'select' && this.props.selectorManager.getLastSelection('select')?.operatorName && this.props.selectorManager.getLastSelection('select')?.operatorName != this.ams.getOperator()?.name)
                 this.addOperatorButton(this.props.selectorManager.getLastSelection('select')?.operatorName);
-            const sel: SaguaroRegionList | undefined = this.props.selectorManager.getSelectionWithCondition(this.currentModelId, this.currentLabelAsymId, mode, this.currentOpName);
+            const sel: SaguaroRegionList | undefined = this.props.selectorManager.getSelectionWithCondition(
+                this.ams.getString("modelId"),
+                this.ams.getString("labelAsymId"),
+                mode,
+                this.ams.getOperator()?.name
+            );
             if (sel == null) {
                 this.rcsbFvModule?.getFv().clearSelection(mode);
             } else {
@@ -161,36 +163,38 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
     }
 
     async modelChangeCallback(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<void> {
-        this.currentModelMap = modelMap;
-        this.currentOpName = undefined;
+        this.ams.setMap(modelMap);
         this.props.plugin.clearFocus();
         const onChangeCallback: Map<string, (x: InstanceSequenceOnchangeInterface)=>void> = new Map<string, (x: InstanceSequenceOnchangeInterface) => {}>();
         const filterInstances: Map<string, Set<string>> = new Map<string, Set<string>>();
-        modelMap.forEach((v,k)=>{
+        this.ams.forEach((v,k)=>{
+            filterInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
             onChangeCallback.set(v.entryId,(x)=>{
-                this.currentEntryId = v.entryId;
-                this.currentLabelAsymId = x.asymId;
-                this.currentModelId = k;
+                this.ams.set({entryId: v.entryId, labelAsymId: x.asymId, modelId: k});
                 asyncScheduler.schedule(()=>{
                     this.props.selectorManager.setLastSelection('select', null);
                     this.instanceChangeCallback();
                 },1000);
                 this.addOperatorButton(defaultOperatorName);
             });
-            filterInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
         });
         this.unmountRcsbFv();
-        const entryId: string = Array.from(modelMap.values()).map(d=>d.entryId)[0];
-        if(entryId != null) {
+        if(this.ams.get("entryId") != null) {
             this.rcsbFvModule = await buildInstanceSequenceFv(
                 this.rcsbFvDivId,
                 RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
-                entryId,
+                this.ams.getString("entryId"),
                 {
                     ...this.props.instanceSequenceConfig,
                     defaultValue: defaultAuthId,
-                    onChangeCallback: onChangeCallback.get(entryId),
-                    filterInstances: filterInstances.get(entryId),
+                    onChangeCallback: onChangeCallback.get(this.ams.getString("entryId")),
+                    beforeRenderCallback: (x: InstanceSequenceOnchangeInterface)=>{
+                        this.ams.setOperator(x.asymId,defaultOperatorName);
+                        if(typeof this.props.additionalConfig?.operatorChangeCallback === "function" && this.ams.getOperator()){
+                                this.props.additionalConfig.operatorChangeCallback(this.ams.getOperator()!);
+                        }
+                    },
+                    filterInstances: filterInstances.get(this.ams.getString("entryId")),
                     selectButtonOptionProps: (props: OptionProps<OptionPropsInterface>) => (components.Option &&
                         <div style={{display: 'flex'}}>
                             <ChainDisplay plugin={this.props.plugin} label={props.data.label}/>
@@ -199,12 +203,16 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
                 },
                 {
                     ...this.props.additionalConfig,
-                    boardConfig: this.boardConfig
+                    boardConfig: this.boardConfig,
+                    externalTrackBuilder:{
+                        filterFeatures: this.filterFeatures.bind(this)
+                    }
+
                 }
             );
         }
         if(!defaultAuthId)
-            await createComponents(this.props.plugin, modelMap);
+            await createComponents(this.props.plugin, this.ams.getMap());
     }
 
     private async instanceChangeCallback(): Promise<void>{
@@ -213,9 +221,9 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
     }
 
     private addOperatorButton(operatorName?:string): void{
-        const currentChainInfo: ChainInfo|undefined = this.currentModelMap.get(this.currentModelId)?.chains.find(ch=>ch.label===this.currentLabelAsymId);
+        const currentChainInfo: ChainInfo|undefined = this.ams.getChainInfo();
         if(this.props.useOperatorsFlag && currentChainInfo && currentChainInfo.operators.length >1 ){
-            this.currentOpName = operatorName ?? currentChainInfo.operators[0].name;
+            this.ams.setOperator(undefined,operatorName);
             RcsbFvUI.addSelectButton(
                 this.rcsbFvDivId,
                 RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
@@ -223,15 +231,16 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
                     label:op.name,
                     optId:op.name,
                     onChange:()=>{
-                        this.currentOpName = op.name;
-                        asyncScheduler.schedule(()=>{
-                            this.props.selectorManager.setLastSelection('select', null);
-                            this.structureSelectionCallback();
-                        },300);
+                        this.ams.set({operator:op});
+                        this.modelChangeCallback(
+                            this.ams.getMap(),
+                            this.ams.getChainInfo()?.auth,
+                            op.name
+                        )
                     }
                 })),
                 {
-                    defaultValue: this.currentOpName
+                    defaultValue: this.ams.getOperator()?.name
                 }
             );
         }
@@ -245,15 +254,26 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
     private highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): void {
         if(selection != null && selection.length > 0) {
             if(selection[0].isEmpty){
-                const selectionList = [{modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, position: selection[0].begin, operatorName: this.currentOpName}];
-                if(selection[0].end != null) selectionList.push({modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, position: selection[0].end, operatorName: this.currentOpName})
+                const selectionList = [{
+                    modelId: this.ams.getString("modelId"),
+                    labelAsymId: this.ams.getString("labelAsymId"),
+                    position: selection[0].begin,
+                    operatorName: this.ams.getOperator()?.name
+                }];
+                if(selection[0].end != null)
+                    selectionList.push({
+                        modelId: this.ams.getString("modelId"),
+                        labelAsymId: this.ams.getString("labelAsymId"),
+                        position: selection[0].end,
+                        operatorName: this.ams.getOperator()?.name
+                    })
                 this.props.plugin.select(
                     selectionList,
                     'hover',
                     'set'
                 );
             }else {
-                this.props.plugin.select(processMultipleGaps(this.currentModelId, this.currentLabelAsymId, selection, this.currentOpName), 'hover', 'set');
+                this.props.plugin.select(processMultipleGaps(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), selection, this.ams.getOperator()?.name), 'hover', 'set');
             }
         }else{
             this.props.plugin.clearSelection('hover');
@@ -263,8 +283,8 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
     private selectionChangeCallback(selection: Array<RcsbFvTrackDataElementInterface>): void {
         if(this.innerSelectionFlag)
             return;
-        this.props.plugin.clearSelection('select', {modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, operatorName: this.currentOpName});
-        this.props.selectorManager.clearSelection('select', {labelAsymId: this.currentLabelAsymId, operatorName: this.currentOpName});
+        this.props.plugin.clearSelection('select', {modelId: this.ams.getString("modelId"), labelAsymId: this.ams.getString("labelAsymId"), operatorName: this.ams.getOperator()?.name});
+        this.props.selectorManager.clearSelection('select', {labelAsymId: this.ams.getString("labelAsymId"), operatorName: this.ams.getOperator()?.name});
         if(selection == null || selection.length === 0) {
             this.resetPluginView();
         }else{
@@ -277,14 +297,28 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
             const x = e.begin;
             const y = e.end ?? e.begin;
             if(e.isEmpty){
-                this.props.plugin.select(
-                    [{modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, position: x, operatorName: this.currentOpName},{modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, position: y, operatorName: this.currentOpName}], 'select',
-                    'add'
+                this.props.plugin.select([{
+                        modelId: this.ams.getString("modelId"),
+                        labelAsymId: this.ams.getString("labelAsymId"),
+                        position: x,
+                        operatorName: this.ams.getOperator()?.name},
+                    {
+                        modelId: this.ams.getString("modelId"),
+                        labelAsymId: this.ams.getString("labelAsymId"),
+                        position: y,
+                        operatorName: this.ams.getOperator()?.name
+                    }],
+                    'select',
+                 'add'
                 );
-                this.props.selectorManager.addSelectionFromRegion(this.currentModelId, this.currentLabelAsymId, {begin:x, end:y, isEmpty: true, source: 'sequence'}, 'select', this.currentOpName);
+                this.props.selectorManager.addSelectionFromRegion(
+                    this.ams.getString("modelId"),
+                    this.ams.getString("labelAsymId"),
+                    {begin:x, end:y, isEmpty: true, source: 'sequence'},
+                    'select', this.ams.getOperator()?.name);
             }else{
-                this.props.plugin.select(processGaps(this.currentModelId, this.currentLabelAsymId, e, this.currentOpName), 'select', 'add');
-                this.props.selectorManager.addSelectionFromRegion(this.currentModelId, this.currentLabelAsymId, {begin:x, end:y, source: 'sequence'}, 'select', this.currentOpName);
+                this.props.plugin.select(processGaps(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), e, this.ams.getOperator()?.name), 'select', 'add');
+                this.props.selectorManager.addSelectionFromRegion(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), {begin:x, end:y, source: 'sequence'}, 'select', this.ams.getOperator()?.name);
             }
         });
     }
@@ -298,42 +332,67 @@ export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractV
         const x = e.begin;
         const y = e.end ?? e.begin;
         if(e.isEmpty){
-            this.props.plugin.cameraFocus(this.currentModelId, this.currentLabelAsymId, [x,y], this.currentOpName);
-            this.currentSelectedComponentId = this.currentLabelAsymId +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
+            this.props.plugin.cameraFocus(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), [x,y], this.ams.getOperator()?.name);
+            this.currentSelectedComponentId = this.ams.getString("labelAsymId") +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
             asyncScheduler.schedule(async ()=>{
                 await this.props.plugin.createComponent(
                     this.currentSelectedComponentId,
                     [
-                        {modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, position: x, operatorName: this.currentOpName},
-                        {modelId: this.currentModelId, labelAsymId: this.currentLabelAsymId, position: y, operatorName: this.currentOpName}
+                        {modelId: this.ams.getString("modelId"), labelAsymId: this.ams.getString("labelAsymId"), position: x, operatorName: this.ams.getOperator()?.name},
+                        {modelId: this.ams.getString("modelId"), labelAsymId: this.ams.getString("labelAsymId"), position: y, operatorName: this.ams.getOperator()?.name}
                         ],
                     'ball-and-stick'
                 )
                 if(x === y)
                     asyncScheduler.schedule(()=>{
-                        this.props.plugin.setFocus(this.currentModelId, this.currentLabelAsymId, x, y, this.currentOpName);
+                        this.props.plugin.setFocus(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), x, y, this.ams.getOperator()?.name);
                     },200);
             },100);
 
         }else{
-            this.props.plugin.cameraFocus(this.currentModelId, this.currentLabelAsymId, x, y, this.currentOpName);
+            this.props.plugin.cameraFocus(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), x, y, this.ams.getOperator()?.name);
             if((y-x)<this.createComponentThreshold){
-                this.currentSelectedComponentId = this.currentLabelAsymId +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
+                this.currentSelectedComponentId = this.ams.getString("labelAsymId") +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
                 asyncScheduler.schedule(async ()=>{
                     await this.props.plugin.createComponent(
                         this.currentSelectedComponentId,
-                        processGaps(this.currentModelId, this.currentLabelAsymId, e, this.currentOpName),
+                        processGaps(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), e, this.ams.getOperator()?.name),
                         'ball-and-stick'
                     )
                     if(x === y)
                         asyncScheduler.schedule(()=>{
-                            this.props.plugin.setFocus(this.currentModelId, this.currentLabelAsymId, x, y, this.currentOpName);
+                            this.props.plugin.setFocus(this.ams.getString("modelId"), this.ams.getString("labelAsymId"), x, y, this.ams.getOperator()?.name);
                         },200);
                 },100);
             }
         }
     }
 
+    private filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
+        return new Promise<Array<AnnotationFeatures>>(async resolve => {
+            let annotations: Array<AnnotationFeatures> = [];
+            (await Promise.all(data.annotations.map(async ann=>{
+                if(ann.source == Source.PdbInterface && ann.target_id && data.rcsbContext?.asymId) {
+                    const interfaceToInstance: InterfaceInstanceTranslate = await RcsbFvContextManager.getInterfaceToInstance(ann.target_id);
+                    if(typeof ann.target_identifiers?.interface_partner_index === "number" && ann.target_identifiers.assembly_id === this.ams.getString("assemblyId")) {
+                        const operatorIds:string[][] = interfaceToInstance.getOperatorIds(ann.target_id)[ann.target_identifiers.interface_partner_index];
+                        if(ann.features && this.ams.getOperator() && operatorIds.map(o=>o.join("|")).includes( this.ams.getOperator()!.ids.join("|") )){
+                            ann.features = ann.features.filter(f=>(f && f.type == Type.BurialFraction));
+                            if(ann.features.length > 0)
+                                return ann;
+                        }
+                    }
+                }else if(ann.source != Source.PdbInterface){
+                    return ann;
+                }
+            }))).forEach((value,index,array)=>{
+                if(value)
+                    annotations = annotations.concat(value);
+            });
+            resolve(annotations);
+        });
+    }
+
 }
 
 function processGaps(modelId: string, labelAsymId: string, e: RcsbFvTrackDataElementInterface, operatorName?:string): Array<SaguaroRange>{

+ 1 - 1
src/RcsbFvStructure/SaguaroPluginInterface.ts

@@ -7,7 +7,7 @@ import {RegionSelectionInterface} from "../RcsbFvSelection/RcsbFvSelectorManager
 export type ChainType = "polymer"|"water"|"branched"|"non-polymer"|"macrolide";
 export type OperatorInfo = {ids:string[], name: string};
 export type ChainInfo = {auth:string;label:string;entityId:string;title:string;type:ChainType;operators:OperatorInfo[]};
-export type SaguaroPluginModelMapType = Map<string,{entryId: string; chains:Array<ChainInfo>;}>;
+export type SaguaroPluginModelMapType = Map<string,{entryId: string; assemblyId: string, chains:Array<ChainInfo>;}>;
 
 export interface SaguaroChain {
     modelId: string;

+ 8 - 6
src/RcsbFvStructure/StructurePlugins/MolstarPlugin.ts

@@ -463,12 +463,12 @@ export class MolstarPlugin extends AbstractPlugin implements SaguaroPluginInterf
 
     private getChains(): SaguaroPluginModelMapType{
         const structureRefList = getStructureOptions(this.viewer.plugin);
-        const out: SaguaroPluginModelMapType = new Map<string, {entryId: string; chains: Array<ChainInfo>}>();
+        const out: SaguaroPluginModelMapType = new Map<string, {entryId: string; chains: Array<ChainInfo>; assemblyId:string;}>();
         structureRefList.forEach((structureRef,i)=>{
-            const structure = getStructure(structureRef[0], this.viewer.plugin.state.data);
+            const structure: Structure = getStructure(structureRef[0], this.viewer.plugin.state.data);
             let modelEntityId = getModelEntityOptions(structure)[0][0];
-            const chains: [{modelId:string;entryId:string},ChainInfo[]] = getChainValues(structure, modelEntityId);
-            out.set(this.getModelId(chains[0].modelId),{entryId:chains[0].entryId, chains: chains[1]});
+            const chains: [{modelId:string;entryId:string;assemblyId:string;},ChainInfo[]] = getChainValues(structure, modelEntityId);
+            out.set(this.getModelId(chains[0].modelId),{entryId:chains[0].entryId, assemblyId:chains[0].assemblyId, chains: chains[1]});
         });
         return out;
     }
@@ -509,12 +509,14 @@ export function getStructureOptions(plugin: PluginContext): [string,string][] {
     return options;
 }
 
-function getChainValues(structure: Structure, modelEntityId: string): [{modelId:string, entryId:string},ChainInfo[]] {
+function getChainValues(structure: Structure, modelEntityId: string): [{modelId:string; entryId:string; assemblyId:string;},ChainInfo[]] {
     const chains: Map<number, ChainInfo> = new Map<number, ChainInfo>();
     const l = StructureElement.Location.create(structure);
+    let assemblyId:string = "-";
     const [modelIdx, entityId] = splitModelEntityId(modelEntityId);
     for (const unit of structure.units) {
         StructureElement.Location.set(l, structure, unit, unit.elements[0]);
+        assemblyId = SP.unit.pdbx_struct_assembly_id(l);
         if (structure.getModelIndex(unit.model) !== modelIdx) continue;
         const chId: number = unit.chainGroupId;
         if(chains.has(chId)){
@@ -523,7 +525,7 @@ function getChainValues(structure: Structure, modelEntityId: string): [{modelId:
             chains.set(chId, {label:SP.chain.label_asym_id(l), auth:SP.chain.auth_asym_id(l), entityId: SP.entity.id(l), title: SP.entity.pdbx_description(l).join("|"), type: SP.entity.type(l), operators:[opKey(l)]});
         }
     }
-    const id: {modelId:string, entryId:string} = {modelId:l.unit?.model?.id, entryId: l.unit?.model?.entryId};
+    const id: {modelId:string; entryId:string; assemblyId:string;} = {modelId:l.unit?.model?.id, entryId: l.unit?.model?.entryId, assemblyId: assemblyId};
     return [id,Array.from(chains.values())];
 }
 

+ 2 - 68
src/examples/assembly-interface/index.ts

@@ -1,16 +1,6 @@
 
 import './index.html';
 import {RcsbFv3DAssembly} from "../../RcsbFv3D/RcsbFv3DAssembly";
-import {
-    AlignmentResponse,
-    AnnotationFeatures,
-    Type
-} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
-import {SequenceCollectorDataInterface} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/SequenceCollector/SequenceCollector";
-import {RcsbFvDisplayTypes, RcsbFvRowConfigInterface} from "@rcsb/rcsb-saguaro";
-import {PolymerEntityInstanceInterface} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/Translators/PolymerEntityInstancesCollector";
-import {RcsbFvContextManager} from "@rcsb/rcsb-saguaro-app";
-import {InterfaceInstanceTranslate} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbUtils/InterfaceInstanceTranslate";
 
 document.addEventListener("DOMContentLoaded", function(event) {
 
@@ -45,66 +35,10 @@ document.addEventListener("DOMContentLoaded", function(event) {
                 elementClickCallBack: (e) => {
                     console.log(`Element clicked ${e?.type}`)
                 }
-            },
-            externalTrackBuilder:externalTrackBuilder()
+            }
         },
         useOperatorsFlag: true
     });
     panel3d.render();
 
-});
-
-function externalTrackBuilder(){
-    let myComputedTrack: RcsbFvRowConfigInterface = {
-        trackId: "blockTrack",
-        trackHeight: 20,
-        trackColor: "#F9F9F9",
-        titleFlagColor: "#48a1b3",
-        displayType: RcsbFvDisplayTypes.BLOCK,
-        displayColor: "#56e0f5",
-        rowTitle: "COMPUTED",
-        trackData: []
-    };
-    return {
-        processAlignmentAndFeatures(data: { annotations?: Array<AnnotationFeatures>; alignments?: AlignmentResponse }): Promise<void> {
-            return new Promise<void>(resolve => {
-                myComputedTrack.trackData = [];
-                data.annotations?.forEach(a=>{
-                    a.features?.forEach(f=>{
-                        if(f!=null && f.type === Type.Site){
-                            if(f.feature_positions)
-                                myComputedTrack.trackData?.push( ...f.feature_positions?.map(p=>({
-                                    begin:p?.beg_seq_id ?? 0,
-                                    end:p?.end_seq_id ?? undefined
-                                })))
-                        }
-                    })
-                });
-                resolve(void 0);
-            })
-
-        },
-        addTo(tracks: { alignmentTracks?: SequenceCollectorDataInterface; annotationTracks?: Array<RcsbFvRowConfigInterface>; rcsbContext?: Partial<PolymerEntityInstanceInterface>; }): Promise<void> {
-            return new Promise<void>(resolve => {
-                if (tracks.rcsbContext?.asymId === "A" && myComputedTrack?.trackData && myComputedTrack.trackData.length > 0) {
-                    tracks.annotationTracks?.push(myComputedTrack);
-                }
-                resolve(void 0);
-            })
-        },
-        filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
-            return new Promise<Array<AnnotationFeatures>>(async resolve => {
-                (await Promise.all(data.annotations.map(async ann=>{
-                    if(ann.target_id && data.rcsbContext?.asymId) {
-                        const interfaceToInstance: InterfaceInstanceTranslate = await RcsbFvContextManager.getInterfaceToInstance(ann.target_id);
-                        return interfaceToInstance.getOperatorIds(ann.target_id, data.rcsbContext.asymId);
-                    }
-                }))).forEach((value,index,array)=>{
-                    if(value)
-                        console.log(value);
-                });
-                resolve(data.annotations);
-            });
-        }
-    }
-}
+});

+ 1 - 2
webpack.examples.config.js

@@ -56,7 +56,6 @@ examples.push({
     }
 });
 
-/*
 examples.push({
     ...commonConfig,
     entry: {
@@ -110,6 +109,6 @@ examples.push({
         filename: '[name].js',
         path: path.resolve(__dirname, out_path+'/css-config/')
     }
-});*/
+});
 
 module.exports = examples;

Some files were not shown because too many files changed in this diff