AssemblyView.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. import {asyncScheduler} from "rxjs";
  2. import * as React from "react";
  3. import {RcsbFvDOMConstants} from "../../../RcsbFvConstants/RcsbFvConstants";
  4. import {buildInstanceSequenceFv, FeatureType, RcsbFvContextManager, RcsbFvUI, unmount} from "@rcsb/rcsb-saguaro-app";
  5. import {AbstractView, AbstractViewInterface} from "../AbstractView";
  6. import {
  7. InstanceSequenceConfig,
  8. InstanceSequenceOnchangeInterface
  9. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
  10. import {RcsbFvBoardConfigInterface, RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
  11. import {
  12. ChainInfo,
  13. OperatorInfo,
  14. SaguaroPluginInterface,
  15. SaguaroPluginModelMapType,
  16. SaguaroRange,
  17. SaguaroRegionList
  18. } from "../../../RcsbFvStructure/SaguaroPluginInterface";
  19. import {ChainDisplay} from "./ChainDisplay";
  20. import {
  21. RcsbFvAdditionalConfig,
  22. RcsbFvModulePublicInterface
  23. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
  24. import {AnnotationFeatures, Source, Type} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
  25. import {PolymerEntityInstanceInterface} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/Translators/PolymerEntityInstancesCollector";
  26. import {InterfaceInstanceTranslate} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbUtils/Translators/InterfaceInstanceTranslate";
  27. import {AssemblyModelSate} from "./AssemblyModelSate";
  28. import {SelectOptionProps} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/WebTools/SelectButton";
  29. export interface AssemblyViewInterface {
  30. entryId: string;
  31. additionalConfig?: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void};
  32. instanceSequenceConfig?: InstanceSequenceConfig;
  33. useOperatorsFlag?:boolean;
  34. }
  35. export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractViewInterface, {}>{
  36. private readonly assemblyModelSate: AssemblyModelSate = new AssemblyModelSate();
  37. private createComponentThreshold: number = 3;
  38. private innerSelectionFlag: boolean = false;
  39. private currentSelectedComponentId: string;
  40. private boardConfig: Partial<RcsbFvBoardConfigInterface>;
  41. private rcsbFvModule: RcsbFvModulePublicInterface | null;
  42. private OPERATOR_DROPDOWN_TITLE: string = "Symmetry Partner";
  43. //private readonly componentSet = new Map<string, {current: Set<string>, previous: Set<string>}>();
  44. additionalContent(): JSX.Element {
  45. return (
  46. <div style={{marginTop:10}}>
  47. <div>
  48. <div id={RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID} style={{display:"inline-block"}}/>
  49. <div style={{display:"inline-block", marginLeft:25}}>
  50. <a href={"/docs/sequence-viewers/protein-feature-view"} target={"_blank"}>Help</a>
  51. </div>
  52. </div>
  53. <div style={{position:"absolute", top:5, right:5}} >
  54. <a style={{textDecoration:"none", color:"#337ab7", cursor:"pointer", marginRight:15}} target={"_blank"} href={"/docs/sequence-viewers/3d-protein-feature-view"}>
  55. Help
  56. </a>
  57. <a style={{textDecoration:"none", color: "#337ab7", cursor:"pointer"}} onClick={()=>{this.props.unmount(true)}}>
  58. Back
  59. </a>
  60. </div>
  61. </div>
  62. );
  63. }
  64. componentDidMount (): void {
  65. super.componentDidMount();
  66. const width: number | undefined = document.getElementById(this.componentDivId)?.getBoundingClientRect().width;
  67. if(width == null)
  68. return;
  69. const trackWidth: number = width - 190 - 55;
  70. this.boardConfig = {
  71. ...this.props.additionalConfig?.boardConfig,
  72. trackWidth: trackWidth,
  73. highlightHoverPosition:true,
  74. highlightHoverElement:true,
  75. elementClickCallBack:(e:RcsbFvTrackDataElementInterface)=>{
  76. this.elementClickCallback(e);
  77. if(typeof this.props.additionalConfig?.boardConfig?.elementClickCallBack === "function")
  78. this.props.additionalConfig?.boardConfig.elementClickCallBack(e);
  79. },
  80. selectionChangeCallBack:(selection: Array<RcsbFvTrackDataElementInterface>)=>{
  81. this.selectionChangeCallback(selection);
  82. if(typeof this.props.additionalConfig?.boardConfig?.selectionChangeCallBack === "function")
  83. this.props.additionalConfig?.boardConfig.selectionChangeCallBack(selection);
  84. },
  85. highlightHoverCallback:(selection: RcsbFvTrackDataElementInterface[])=>{
  86. this.highlightHoverCallback(selection);
  87. if(typeof this.props.additionalConfig?.boardConfig?.highlightHoverCallback === "function")
  88. this.props.additionalConfig?.boardConfig.highlightHoverCallback(selection);
  89. },
  90. };
  91. }
  92. componentWillUnmount() {
  93. super.componentWillUnmount();
  94. unmount(this.rcsbFvDivId);
  95. }
  96. async structureSelectionCallback(): Promise<void> {
  97. await this.pluginSelectCallback('select');
  98. }
  99. async structureHoverCallback(): Promise<void> {
  100. await this.pluginSelectCallback('hover');
  101. }
  102. representationChangeCallback(): void{
  103. //TODO
  104. }
  105. async updateDimensions(): Promise<void> {
  106. const width: number = window.document.getElementById(this.componentDivId)?.getBoundingClientRect().width ?? 0;
  107. const trackWidth: number = width - 190 - 55;
  108. this.boardConfig.trackWidth = trackWidth;
  109. await this.rcsbFvModule?.getFv().updateBoardConfig({boardConfigData:{trackWidth:trackWidth}})
  110. await this.structureSelectionCallback();
  111. return void 0;
  112. }
  113. private resetPluginView(): void {
  114. this.props.plugin.clearFocus();
  115. this.props.plugin.resetCamera();
  116. }
  117. private async pluginSelectCallback(mode:'select'|'hover'): Promise<void> {
  118. if(this.rcsbFvModule == null)
  119. return;
  120. this.innerSelectionFlag = true;
  121. if(mode === 'select' && this.currentSelectedComponentId != null){
  122. this.props.plugin.removeComponent(this.currentSelectedComponentId);
  123. }
  124. const allSel: Array<SaguaroRegionList> | undefined = this.props.selectorManager.getSelection(mode);
  125. if(allSel == null || allSel.length ===0) {
  126. this.rcsbFvModule?.getFv().clearSelection(mode);
  127. if(mode === 'select')
  128. this.resetPluginView();
  129. }else if(
  130. mode === 'select' &&
  131. this.props.selectorManager.getLastSelection('select')?.labelAsymId != null &&
  132. this.props.selectorManager.getLastSelection('select')?.labelAsymId != this.assemblyModelSate.getString("labelAsymId")
  133. ){
  134. const authId: string | undefined = this.assemblyModelSate.getChainInfo(this.props.selectorManager.getLastSelection('select')?.labelAsymId!)?.auth;
  135. await this.modelChangeCallback(this.assemblyModelSate.getMap(), authId, this.props.selectorManager.getLastSelection('select')?.operatorName);
  136. }else if(
  137. mode === 'select' &&
  138. this.props.selectorManager.getLastSelection('select')?.labelAsymId != null &&
  139. this.props.selectorManager.getLastSelection('select')?.operatorName != null &&
  140. this.props.selectorManager.getLastSelection('select')?.operatorName != this.assemblyModelSate.getOperator()?.name
  141. ){
  142. const authId: string | undefined = this.assemblyModelSate.getChainInfo(this.props.selectorManager.getLastSelection('select')?.labelAsymId!)?.auth;
  143. await this.modelChangeCallback(this.assemblyModelSate.getMap(), authId, this.props.selectorManager.getLastSelection('select')?.operatorName);
  144. }else{
  145. if(mode === 'select' && this.props.selectorManager.getLastSelection('select')?.operatorName && this.props.selectorManager.getLastSelection('select')?.operatorName != this.assemblyModelSate.getOperator()?.name)
  146. this.addOperatorButton(this.props.selectorManager.getLastSelection('select')?.operatorName);
  147. const sel: SaguaroRegionList | undefined = this.props.selectorManager.getSelectionWithCondition(
  148. this.assemblyModelSate.getString("modelId"),
  149. this.assemblyModelSate.getString("labelAsymId"),
  150. mode,
  151. this.assemblyModelSate.getOperator()?.name
  152. );
  153. if (sel == null) {
  154. this.rcsbFvModule?.getFv().clearSelection(mode);
  155. if(mode === 'select')
  156. this.resetPluginView();
  157. } else {
  158. this.rcsbFvModule?.getFv().setSelection({elements: sel.regions, mode: mode});
  159. }
  160. }
  161. this.innerSelectionFlag = false;
  162. }
  163. async modelChangeCallback(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<void> {
  164. this.assemblyModelSate.setMap(modelMap);
  165. this.props.plugin.clearFocus();
  166. const onChangeCallback: Map<string, (x: InstanceSequenceOnchangeInterface)=>void> = new Map<string, (x: InstanceSequenceOnchangeInterface) => {}>();
  167. const assemblyInstances: Map<string, Set<string>> = new Map<string, Set<string>>();
  168. this.assemblyModelSate.forEach((v,k)=>{
  169. assemblyInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
  170. onChangeCallback.set(v.entryId,(x)=>{
  171. this.assemblyModelSate.set({entryId: v.entryId, labelAsymId: x.asymId, modelId: k});
  172. asyncScheduler.schedule(()=>{
  173. this.props.selectorManager.setLastSelection('select', null);
  174. this.instanceChangeCallback();
  175. },1000);
  176. });
  177. });
  178. this.unmountRcsbFv();
  179. const operatorNameContainer: {operatorName?:string} = {operatorName: defaultOperatorName};
  180. if(this.assemblyModelSate.get("entryId") != null) {
  181. this.rcsbFvModule = await buildInstanceSequenceFv(
  182. this.rcsbFvDivId,
  183. RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
  184. this.assemblyModelSate.getString("entryId"),
  185. {
  186. ...this.props.instanceSequenceConfig,
  187. defaultValue: defaultAuthId,
  188. onChangeCallback: onChangeCallback.get(this.assemblyModelSate.getString("entryId")),
  189. beforeChangeCallback: (x: InstanceSequenceOnchangeInterface)=>{
  190. this.assemblyModelSate.set({entryId:x.pdbId, labelAsymId: x.asymId});
  191. const entryMap:[string, {entryId: string, assemblyId: string, chains: ChainInfo[]}] | undefined = Array.from(this.assemblyModelSate.entries()).find((e)=>(e[1].entryId === x.pdbId));
  192. if(!entryMap){
  193. throw `Error: no modelId was found for ${x.pdbId}`;
  194. }
  195. const operator: OperatorInfo|undefined = getOperator(this.assemblyModelSate.getMap().get(entryMap[0])!, defaultAuthId, operatorNameContainer.operatorName);
  196. this.addOperatorButton(operator?.name);
  197. this.assemblyModelSate.setOperator(x.asymId,operator?.name);
  198. operatorNameContainer.operatorName = undefined;
  199. if(typeof this.props.additionalConfig?.operatorChangeCallback === "function" && this.assemblyModelSate.getOperator()){
  200. this.props.additionalConfig.operatorChangeCallback(this.assemblyModelSate.getOperator()!);
  201. }
  202. if((this.assemblyModelSate.getChainInfo()?.operators?.length ?? 0) > 1)
  203. return {
  204. operatorIds: operator?.ids
  205. }
  206. },
  207. filterInstances: assemblyInstances.get(this.assemblyModelSate.getString("entryId")),
  208. selectButtonOptionProps: (props: SelectOptionProps) => (
  209. <div style={{display: 'flex'}}>
  210. <ChainDisplay plugin={this.props.plugin} label={props.data.label}/>
  211. {props.children}
  212. </div>)
  213. },
  214. {
  215. ...this.props.additionalConfig,
  216. boardConfig: this.boardConfig,
  217. externalTrackBuilder:{
  218. filterFeatures: this.filterFeatures.bind(this)
  219. }
  220. }
  221. );
  222. }
  223. if(!defaultAuthId)
  224. await createComponents(this.props.plugin, this.assemblyModelSate.getMap());
  225. }
  226. private async instanceChangeCallback(): Promise<void>{
  227. this.resetPluginView();
  228. await this.pluginSelectCallback('select');
  229. }
  230. private addOperatorButton(operatorName?: string): void{
  231. const currentChainInfo: ChainInfo|undefined = this.assemblyModelSate.getChainInfo();
  232. if(this.props.useOperatorsFlag && currentChainInfo && currentChainInfo.operators.length >1 ){
  233. this.assemblyModelSate.setOperator(undefined,operatorName);
  234. RcsbFvUI.addSelectButton(
  235. this.rcsbFvDivId,
  236. RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
  237. currentChainInfo.operators.map(op=>({
  238. label:`${op.ids.join("-")} (${op.name})`,
  239. optId:op.name,
  240. onChange: async ()=>{
  241. this.assemblyModelSate.set({operator:op});
  242. await this.modelChangeCallback(
  243. this.assemblyModelSate.getMap(),
  244. this.assemblyModelSate.getChainInfo()?.auth,
  245. op.name
  246. )
  247. }
  248. })),
  249. {
  250. defaultValue: this.assemblyModelSate.getOperator()?.name,
  251. dropdownTitle:this.OPERATOR_DROPDOWN_TITLE
  252. }
  253. );
  254. }
  255. }
  256. private unmountRcsbFv(): void {
  257. this.rcsbFvModule = null;
  258. unmount(this.rcsbFvDivId);
  259. }
  260. private highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): void {
  261. if(selection != null && selection.length > 0) {
  262. if(selection[0].isEmpty){
  263. const selectionList = [{
  264. modelId: this.assemblyModelSate.getString("modelId"),
  265. labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
  266. position: selection[0].begin,
  267. operatorName: this.assemblyModelSate.getOperator()?.name
  268. }];
  269. if(selection[0].end != null)
  270. selectionList.push({
  271. modelId: this.assemblyModelSate.getString("modelId"),
  272. labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
  273. position: selection[0].end,
  274. operatorName: this.assemblyModelSate.getOperator()?.name
  275. })
  276. this.props.plugin.select(
  277. selectionList,
  278. 'hover',
  279. 'set'
  280. );
  281. }else {
  282. this.props.plugin.select(processMultipleGaps(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), selection, this.assemblyModelSate.getOperator()?.name), 'hover', 'set');
  283. }
  284. }else{
  285. this.props.plugin.clearSelection('hover');
  286. }
  287. }
  288. private selectionChangeCallback(selection: Array<RcsbFvTrackDataElementInterface>): void {
  289. if(this.innerSelectionFlag)
  290. return;
  291. this.props.plugin.clearSelection('select', {modelId: this.assemblyModelSate.getString("modelId"), labelAsymId: this.assemblyModelSate.getString("labelAsymId"), operatorName: this.assemblyModelSate.getOperator()?.name});
  292. this.props.selectorManager.clearSelection('select', {labelAsymId: this.assemblyModelSate.getString("labelAsymId"), operatorName: this.assemblyModelSate.getOperator()?.name});
  293. if(selection == null || selection.length === 0) {
  294. this.resetPluginView();
  295. }else{
  296. this.select(selection);
  297. }
  298. }
  299. private select(selection: Array<RcsbFvTrackDataElementInterface>): void{
  300. selection.forEach(e=>{
  301. const x = e.begin;
  302. const y = e.end ?? e.begin;
  303. if(e.isEmpty){
  304. this.props.plugin.select([{
  305. modelId: this.assemblyModelSate.getString("modelId"),
  306. labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
  307. position: x,
  308. operatorName: this.assemblyModelSate.getOperator()?.name},
  309. {
  310. modelId: this.assemblyModelSate.getString("modelId"),
  311. labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
  312. position: y,
  313. operatorName: this.assemblyModelSate.getOperator()?.name
  314. }],
  315. 'select',
  316. 'add'
  317. );
  318. this.props.selectorManager.addSelectionFromRegion(
  319. this.assemblyModelSate.getString("modelId"),
  320. this.assemblyModelSate.getString("labelAsymId"),
  321. {begin:x, end:y, isEmpty: true, source: 'sequence'},
  322. 'select', this.assemblyModelSate.getOperator()?.name);
  323. }else{
  324. this.props.plugin.select(processGaps(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), e, this.assemblyModelSate.getOperator()?.name), 'select', 'add');
  325. this.props.selectorManager.addSelectionFromRegion(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), {begin:x, end:y, source: 'sequence'}, 'select', this.assemblyModelSate.getOperator()?.name);
  326. }
  327. });
  328. }
  329. private elementClickCallback(e:RcsbFvTrackDataElementInterface): void {
  330. this.props.plugin.clearFocus();
  331. if(this.currentSelectedComponentId != null)
  332. this.props.plugin.removeComponent(this.currentSelectedComponentId);
  333. if(e == null)
  334. return;
  335. const x = e.begin;
  336. const y = e.end ?? e.begin;
  337. if(e.isEmpty){
  338. this.props.plugin.cameraFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), [x,y], this.assemblyModelSate.getOperator()?.name);
  339. this.currentSelectedComponentId = this.assemblyModelSate.getString("labelAsymId") +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
  340. asyncScheduler.schedule(async ()=>{
  341. await this.props.plugin.createComponent(
  342. this.currentSelectedComponentId,
  343. [
  344. {modelId: this.assemblyModelSate.getString("modelId"), labelAsymId: this.assemblyModelSate.getString("labelAsymId"), position: x, operatorName: this.assemblyModelSate.getOperator()?.name},
  345. {modelId: this.assemblyModelSate.getString("modelId"), labelAsymId: this.assemblyModelSate.getString("labelAsymId"), position: y, operatorName: this.assemblyModelSate.getOperator()?.name}
  346. ],
  347. 'ball-and-stick'
  348. )
  349. if(x === y)
  350. asyncScheduler.schedule(()=>{
  351. this.props.plugin.setFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), x, y, this.assemblyModelSate.getOperator()?.name);
  352. },200);
  353. },100);
  354. }else{
  355. this.props.plugin.cameraFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), x, y, this.assemblyModelSate.getOperator()?.name);
  356. if((y-x)<this.createComponentThreshold){
  357. this.currentSelectedComponentId = this.assemblyModelSate.getString("labelAsymId") +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
  358. asyncScheduler.schedule(async ()=>{
  359. await this.props.plugin.createComponent(
  360. this.currentSelectedComponentId,
  361. processGaps(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), e, this.assemblyModelSate.getOperator()?.name),
  362. 'ball-and-stick'
  363. )
  364. if(x === y)
  365. asyncScheduler.schedule(()=>{
  366. this.props.plugin.setFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), x, y, this.assemblyModelSate.getOperator()?.name);
  367. },200);
  368. },100);
  369. }
  370. }
  371. }
  372. private filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
  373. return new Promise<Array<AnnotationFeatures>>(async resolve => {
  374. let annotations: Array<AnnotationFeatures> = [];
  375. (await Promise.all(data.annotations.map(async ann=>{
  376. if(ann.source == Source.PdbInterface && ann.target_id && data.rcsbContext?.asymId) {
  377. const interfaceToInstance: InterfaceInstanceTranslate = await RcsbFvContextManager.getInterfaceToInstance(ann.target_id);
  378. if(typeof ann.target_identifiers?.interface_partner_index === "number" && ann.target_identifiers.assembly_id === this.assemblyModelSate.getString("assemblyId")) {
  379. const operatorIds:string[][] = interfaceToInstance.getOperatorIds(ann.target_id)[ann.target_identifiers.interface_partner_index];
  380. if(ann.features && this.assemblyModelSate.getOperator() && operatorIds.map(o=>o.join("|")).includes( this.assemblyModelSate.getOperator()!.ids.join("|") )){
  381. ann.features = ann.features.filter(f=>(f && f.type == FeatureType.BurialFraction));
  382. if(ann.features.length > 0)
  383. return ann;
  384. }
  385. }
  386. }else if(ann.source == Source.PdbInstance && ann.features){
  387. ann.features = ann.features?.filter(f=>(f?.type!==Type.Asa));
  388. return ann;
  389. }else if(ann.source != Source.PdbInterface){
  390. return ann;
  391. }
  392. }))).forEach((value,index,array)=>{
  393. if(value)
  394. annotations = annotations.concat(value);
  395. });
  396. resolve(annotations);
  397. });
  398. }
  399. }
  400. function processGaps(modelId: string, labelAsymId: string, e: RcsbFvTrackDataElementInterface, operatorName?:string): Array<SaguaroRange>{
  401. const regions: Array<SaguaroRange> = new Array<SaguaroRange>();
  402. let lastIndex: number = e.begin;
  403. e.gaps?.forEach((g)=>{
  404. regions.push({
  405. modelId: modelId,
  406. labelAsymId: labelAsymId,
  407. begin: lastIndex,
  408. end: g.begin,
  409. operatorName: operatorName
  410. });
  411. lastIndex = g.end;
  412. });
  413. regions.push({
  414. modelId: modelId,
  415. labelAsymId: labelAsymId,
  416. begin: lastIndex,
  417. end: e.end ?? e.begin,
  418. operatorName: operatorName
  419. });
  420. return regions;
  421. }
  422. function processMultipleGaps(modelId: string, labelAsymId: string, list: Array<RcsbFvTrackDataElementInterface>, operatorName?:string): Array<SaguaroRange>{
  423. let regions: Array<SaguaroRange> = new Array<SaguaroRange>();
  424. list.forEach(e=>{
  425. regions = regions.concat(processGaps(modelId, labelAsymId, e, operatorName));
  426. });
  427. return regions;
  428. }
  429. async function createComponents(plugin: SaguaroPluginInterface, modelMap:SaguaroPluginModelMapType): Promise<void> {
  430. plugin.displayComponent("Water", false);
  431. await plugin.colorComponent("Polymer", 'chain-id');
  432. const chains: Array<{modelId: string; auth: string; label: string;}> = new Array<{modelId: string; auth: string; label: string;}>();
  433. modelMap.forEach((entry, modelId)=>{
  434. entry.chains.forEach(ch=>{
  435. if(ch.type === "polymer") {
  436. chains.push({modelId: modelId, auth: ch.auth, label: ch.label});
  437. }
  438. });
  439. });
  440. plugin.removeComponent();
  441. plugin.clearFocus();
  442. for(const ch of chains) {
  443. const label: string = ch.auth === ch.label ? ch.label : `${ch.label} [auth ${ch.auth}]`;
  444. await plugin.createComponent(label, ch.modelId, ch.label, 'cartoon');
  445. await plugin.colorComponent(label, 'chain-id');
  446. }
  447. await plugin.removeComponent("Polymer");
  448. }
  449. function getOperator(entryInfo: {entryId: string; assemblyId: string, chains:Array<ChainInfo>;}, defaultAuthId?: string, defaultOperatorName?:string): OperatorInfo | undefined{
  450. const chainInfo: ChainInfo | undefined = defaultAuthId ? entryInfo.chains.find(ch=>ch.auth === defaultAuthId) : entryInfo.chains[0];
  451. if(chainInfo){
  452. const operatorInfo: OperatorInfo | undefined = defaultOperatorName ? chainInfo.operators.find(op=>op.name === defaultOperatorName) : chainInfo.operators[0];
  453. if(operatorInfo)
  454. return operatorInfo;
  455. }
  456. return undefined;
  457. }