CallbackHelper.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import {
  2. RcsbFvModulePublicInterface
  3. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
  4. import {RcsbFvSelectorManager} from "../../../RcsbFvSelection/RcsbFvSelectorManager";
  5. import {AssemblyModelSate} from "./AssemblyModelSate";
  6. import {
  7. SaguaroPluginInterface,
  8. SaguaroPluginModelMapType, SaguaroRange,
  9. SaguaroRegionList
  10. } from "../../../RcsbFvStructure/SaguaroPluginInterface";
  11. import {RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
  12. import {asyncScheduler} from "rxjs";
  13. import {DataContainer} from "../../../Utils/DataContainer";
  14. interface CallbackHelperInterface {
  15. rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
  16. selectorManager: RcsbFvSelectorManager;
  17. assemblyModelSate: AssemblyModelSate;
  18. plugin: SaguaroPluginInterface;
  19. modelChangeCallback(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<void>;
  20. }
  21. export class CallbackHelper {
  22. private readonly rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
  23. private readonly selectorManager: RcsbFvSelectorManager;
  24. private readonly assemblyModelSate: AssemblyModelSate;
  25. private selectedComponentId: string|undefined;
  26. private readonly plugin: SaguaroPluginInterface;
  27. private readonly modelChangeCallback: (modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string)=> Promise<void>;
  28. private readonly CREATE_COMPONENT_THR: number = 3;
  29. constructor(config: CallbackHelperInterface) {
  30. this.rcsbFvContainer = config.rcsbFvContainer;
  31. this.selectorManager = config.selectorManager;
  32. this.assemblyModelSate = config.assemblyModelSate;
  33. this.plugin = config.plugin;
  34. this.modelChangeCallback = config.modelChangeCallback;
  35. }
  36. public async pluginSelectCallback(mode:'select'|'hover'): Promise<void> {
  37. const allSel: Array<SaguaroRegionList> | undefined = this.selectorManager.getSelection(mode);
  38. const lastSel: SaguaroRegionList|null = this.selectorManager.getLastSelection('select');
  39. const modelId: string = this.assemblyModelSate.getString("modelId");
  40. const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
  41. const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
  42. if(mode === 'select') this.removeComponent();
  43. if(allSel == null || allSel.length ===0) {
  44. this.rcsbFvContainer.get()?.getFv().clearSelection(mode);
  45. if(mode === 'select') this.resetPluginView();
  46. }else if( mode === 'select' && lastSel?.labelAsymId && (lastSel?.labelAsymId != labelAsymId || lastSel?.operatorName != operatorName) ){
  47. const authId: string | undefined = this.assemblyModelSate.getChainInfo(lastSel?.labelAsymId!)?.auth;
  48. await this.modelChangeCallback(this.assemblyModelSate.getMap(), authId, lastSel?.operatorName);
  49. }else{
  50. const sel: SaguaroRegionList | undefined = this.selectorManager.getSelectionWithCondition(
  51. modelId,
  52. labelAsymId,
  53. mode,
  54. operatorName
  55. );
  56. if (sel == null) {
  57. this.rcsbFvContainer.get()?.getFv().clearSelection(mode);
  58. if(mode === 'select') this.resetPluginView();
  59. } else {
  60. this.rcsbFvContainer.get()?.getFv().setSelection({elements: sel.regions, mode: mode});
  61. }
  62. }
  63. }
  64. public elementClickCallback(e:RcsbFvTrackDataElementInterface): void {
  65. this.plugin.clearFocus();
  66. this.removeComponent();
  67. if(e == null)
  68. return;
  69. const x = e.begin;
  70. const y = e.end ?? e.begin;
  71. const modelId: string = this.assemblyModelSate.getString("modelId");
  72. const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
  73. const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
  74. if(e.isEmpty){
  75. this.plugin.cameraFocus(modelId, labelAsymId, [x,y], operatorName);
  76. this.selectedComponentId = labelAsymId +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
  77. asyncScheduler.schedule(async ()=>{
  78. await this.plugin.createComponent(
  79. this.selectedComponentId!,
  80. [
  81. {modelId, labelAsymId, operatorName, position: x},
  82. {modelId, labelAsymId, operatorName, position: y}
  83. ],
  84. 'ball-and-stick'
  85. )
  86. if(x === y)
  87. asyncScheduler.schedule(()=>{
  88. this.plugin.setFocus(modelId, labelAsymId, x, y, operatorName);
  89. },60);
  90. },30);
  91. }else{
  92. this.plugin.cameraFocus(modelId, labelAsymId, x, y, operatorName);
  93. if((y-x)<this.CREATE_COMPONENT_THR){
  94. this.selectedComponentId = labelAsymId +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
  95. asyncScheduler.schedule(async ()=>{
  96. await this.plugin.createComponent(
  97. this.selectedComponentId!,
  98. processGaps(modelId, labelAsymId, e, operatorName),
  99. 'ball-and-stick'
  100. )
  101. if(x === y)
  102. asyncScheduler.schedule(()=>{
  103. this.plugin.setFocus(modelId, labelAsymId, x, y, operatorName);
  104. },60);
  105. },30);
  106. }
  107. }
  108. }
  109. public highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): void {
  110. const modelId: string = this.assemblyModelSate.getString("modelId");
  111. const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
  112. const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
  113. if(selection != null && selection.length > 0) {
  114. if(selection[0].isEmpty){
  115. const selectionList = [{
  116. modelId,
  117. labelAsymId,
  118. position: selection[0].begin,
  119. operatorName
  120. }];
  121. if(selection[0].end != null)
  122. selectionList.push({
  123. modelId,
  124. labelAsymId,
  125. position: selection[0].end,
  126. operatorName
  127. })
  128. this.plugin.select(
  129. selectionList,
  130. 'hover',
  131. 'set'
  132. );
  133. }else {
  134. this.plugin.select(
  135. processMultipleGaps(modelId, labelAsymId, selection, operatorName),
  136. 'hover',
  137. 'set'
  138. );
  139. }
  140. }else{
  141. this.plugin.clearSelection('hover');
  142. }
  143. }
  144. public selectionChangeCallback(selection: Array<RcsbFvTrackDataElementInterface>): void {
  145. const modelId: string = this.assemblyModelSate.getString("modelId");
  146. const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
  147. const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
  148. this.plugin.clearSelection('select', {modelId, labelAsymId, operatorName});
  149. this.selectorManager.clearSelection('select', {labelAsymId, operatorName});
  150. if(selection == null || selection.length === 0) {
  151. this.resetPluginView();
  152. }else{
  153. this.select(selection);
  154. }
  155. }
  156. private select(selection: Array<RcsbFvTrackDataElementInterface>): void{
  157. const modelId: string = this.assemblyModelSate.getString("modelId");
  158. const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
  159. const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
  160. selection.forEach(e=>{
  161. const x = e.begin;
  162. const y = e.end ?? e.begin;
  163. if(e.isEmpty){
  164. this.plugin.select([{
  165. modelId,
  166. labelAsymId,
  167. operatorName,
  168. position: x
  169. }, {
  170. modelId,
  171. labelAsymId,
  172. operatorName,
  173. position: y
  174. }],
  175. 'select',
  176. 'add'
  177. );
  178. this.selectorManager.addSelectionFromRegion(
  179. modelId,
  180. labelAsymId,
  181. {begin:x, end:y, isEmpty: true, source: 'sequence'},
  182. 'select',
  183. operatorName
  184. );
  185. }else{
  186. this.plugin.select(processGaps(modelId, labelAsymId, e, operatorName), 'select', 'add');
  187. this.selectorManager.addSelectionFromRegion(modelId, labelAsymId, {begin:x, end:y, source: 'sequence'}, 'select', operatorName);
  188. }
  189. });
  190. }
  191. private removeComponent(): void {
  192. if(this.selectedComponentId != null) {
  193. this.plugin.removeComponent(this.selectedComponentId);
  194. this.selectedComponentId = undefined;
  195. }
  196. }
  197. private resetPluginView(): void {
  198. this.plugin.clearFocus();
  199. this.plugin.resetCamera();
  200. }
  201. }
  202. function processMultipleGaps(modelId: string, labelAsymId: string, list: Array<RcsbFvTrackDataElementInterface>, operatorName?:string): Array<SaguaroRange>{
  203. let regions: Array<SaguaroRange> = new Array<SaguaroRange>();
  204. list.forEach(e=>{
  205. regions = regions.concat(processGaps(modelId, labelAsymId, e, operatorName));
  206. });
  207. return regions;
  208. }
  209. function processGaps(modelId: string, labelAsymId: string, e: RcsbFvTrackDataElementInterface, operatorName?:string): Array<SaguaroRange>{
  210. const regions: Array<SaguaroRange> = new Array<SaguaroRange>();
  211. let lastIndex: number = e.begin;
  212. e.gaps?.forEach((g)=>{
  213. regions.push({
  214. modelId: modelId,
  215. labelAsymId: labelAsymId,
  216. begin: lastIndex,
  217. end: g.begin,
  218. operatorName: operatorName
  219. });
  220. lastIndex = g.end;
  221. });
  222. regions.push({
  223. modelId: modelId,
  224. labelAsymId: labelAsymId,
  225. begin: lastIndex,
  226. end: e.end ?? e.begin,
  227. operatorName: operatorName
  228. });
  229. return regions;
  230. }