MsaCallbackManager.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import {
  2. AbstractCallbackManager,
  3. CallbackConfigInterface,
  4. CallbackManagerFactoryInterface, CallbackManagerInterface
  5. } from "../CallbackManagerFactoryInterface";
  6. import {RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
  7. import {
  8. AlignedRegion,
  9. AlignmentResponse,
  10. TargetAlignment
  11. } from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
  12. import {RegionSelectionInterface} from "../../../../RcsbFvState/RcsbFvSelectorManager";
  13. import {ChainInfo, SaguaroRegionList} from "../../../../RcsbFvStructure/StructureViewerInterface";
  14. import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
  15. import {AlignmentMapper as AM} from "../../../../Utils/AlignmentMapper";
  16. export class MsaCallbackManagerFactory<R,U> implements CallbackManagerFactoryInterface<R,U> {
  17. private readonly pluginLoadParamsDefinition:(id: string)=>R;
  18. constructor(config: {pluginLoadParamsDefinition:(id: string)=>R}) {
  19. this.pluginLoadParamsDefinition = config.pluginLoadParamsDefinition;
  20. }
  21. getCallbackManager(config: CallbackConfigInterface<R>): CallbackManagerInterface<U> {
  22. return new MsaCallbackManager( {...config, loadParamRequest:this.pluginLoadParamsDefinition});
  23. }
  24. }
  25. type SelectedRegion = {modelId: string, labelAsymId: string, region: RegionSelectionInterface, operatorName?: string};
  26. class MsaCallbackManager<R,U> extends AbstractCallbackManager<R,U>{
  27. private readonly loadParamRequest:(id: string)=>R;
  28. private readonly targetIds: {[key:string]:boolean} = {};
  29. private alignmentResponse: AlignmentResponse;
  30. constructor(config: CallbackConfigInterface<R> & {loadParamRequest:(id: string)=>R}) {
  31. super(config);
  32. this.loadParamRequest = config.loadParamRequest;
  33. }
  34. async featureClickCallback(e: RcsbFvTrackDataElementInterface): Promise<void> {
  35. const alignment: AlignmentResponse|undefined = await this.rcsbFvContainer.get()?.getAlignmentResponse();
  36. if(alignment){
  37. const regions: SelectedRegion[] = this.getModelRegions( e? [e] : [], alignment, Array.from(this.stateManager.assemblyModelSate.getMap().keys()),"query");
  38. this.stateManager.next<"feature-click",SelectedRegion[]>({type:"feature-click", view:"1d-view", data: regions})
  39. }
  40. }
  41. async highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): Promise<void> {
  42. await this.select(selection, "hover");
  43. }
  44. modelChangeCallback(defaultAuthId?: string, defaultOperatorName?: string): Promise<void> {
  45. return Promise.resolve(undefined);
  46. }
  47. async pfvChangeCallback(params:U): Promise<void> {
  48. if(typeof this.rcsbFvContainer.get() === "undefined")
  49. return;
  50. const alignmentResponse: AlignmentResponse|undefined = await this.rcsbFvContainer.get()?.getAlignmentResponse();
  51. if(!this.alignmentResponse && alignmentResponse) {
  52. this.alignmentResponse = alignmentResponse;
  53. alignmentResponse.target_alignment?.forEach(ta=> {if(ta?.target_id) this.targetIds[ta.target_id]=true})
  54. }else if(alignmentResponse) {
  55. const newTargetAlignments = alignmentResponse.target_alignment?.filter(ta=>{
  56. if(ta && ta.target_id && !this.targetIds[ta.target_id]){
  57. this.targetIds[ta.target_id] = true;
  58. return true;
  59. }
  60. });
  61. if(newTargetAlignments)
  62. this.alignmentResponse.target_alignment = this.alignmentResponse.target_alignment?.concat(
  63. newTargetAlignments
  64. );
  65. }
  66. }
  67. protected async innerStructureViewerSelectionChange(mode: "select" | "hover"): Promise<void> {
  68. const allSel: Array<SaguaroRegionList> | undefined = this.stateManager.selectionState.getSelection(mode);
  69. const alignment: AlignmentResponse|undefined = await this.rcsbFvContainer.get()?.getAlignmentResponse();
  70. let regions: SelectedRegion[] = [];
  71. if(alignment) {
  72. allSel.forEach(sel => {
  73. const chain: ChainInfo | undefined = this.stateManager.assemblyModelSate.getModelChainInfo(sel.modelId)?.chains.find(ch => ch.entityId == TagDelimiter.parseEntity(sel.modelId).entityId && ch.label == sel.labelAsymId);
  74. if (chain) {
  75. regions = regions.concat(this.getModelRegions(sel.regions.map(r => ({
  76. begin: r.begin,
  77. end: r.end
  78. })), alignment, [sel.modelId], "target"));
  79. }
  80. });
  81. }
  82. this.rcsbFvContainer.get()?.getFv().setSelection({mode, elements: regions.map(r => r.region)})
  83. }
  84. protected async innerPfvSelectionChange(selection: Array<RcsbFvTrackDataElementInterface>): Promise<void> {
  85. await this.select(selection, "select");
  86. }
  87. private async select(selection: Array<RcsbFvTrackDataElementInterface>, mode:"select"|"hover"): Promise<void> {
  88. const alignment: AlignmentResponse|undefined = await this.rcsbFvContainer.get()?.getAlignmentResponse();
  89. if(alignment){
  90. const regions = this.getModelRegions(selection, alignment, Array.from(this.stateManager.assemblyModelSate.getMap().keys()), "query");
  91. if(regions.length == 0)
  92. this.stateManager.selectionState.clearSelection(mode);
  93. else
  94. this.stateManager.selectionState.selectFromMultipleRegions("set", regions, mode);
  95. this.stateManager.next({type: mode == "select" ? "selection-change" : "hover-change", view:"1d-view"});
  96. }
  97. }
  98. private getModelRegions(selection: Array<RcsbFvTrackDataElementInterface>, alignment: AlignmentResponse, modelList: string[], pointer:"query"|"target"): SelectedRegion[] {
  99. const regions: SelectedRegion[] = [];
  100. modelList.forEach(modelId=>{
  101. const chain: ChainInfo|undefined = this.stateManager.assemblyModelSate.getModelChainInfo(modelId)?.chains.find(ch=>ch.entityId==TagDelimiter.parseEntity(modelId).entityId);
  102. if(!chain)
  103. return;
  104. const labelAsymId: string | undefined = chain.label;
  105. const operatorName: string | undefined = chain.operators[0].name;
  106. if(!labelAsymId || ! operatorName)
  107. return;
  108. selection.forEach(s=>{
  109. const alignedRegions = (alignment.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []).concat(
  110. this.alignmentResponse?.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []
  111. );
  112. if(!alignedRegions)
  113. return;
  114. AM.mapRangeToRegionList({begin:s.begin, end: s.end ?? s.begin}, alignedRegions, pointer)?.forEach(region=>{
  115. regions.push({
  116. modelId,
  117. labelAsymId,
  118. operatorName,
  119. region:{
  120. ...region,
  121. source:"sequence"
  122. }
  123. });
  124. });
  125. });
  126. });
  127. return regions;
  128. }
  129. }