AssemblyCallbackManager.ts 10 KB

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