icn3d 3.39.1 → 3.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/icn3d.js +1228 -1106
  2. package/icn3d.min.js +4 -4
  3. package/icn3d.module.js +1228 -1106
  4. package/package.json +1 -1
package/icn3d.module.js CHANGED
@@ -9728,25 +9728,42 @@ class ClickMenu {
9728
9728
  }
9729
9729
 
9730
9730
  getHiddenMenusFromCache() { let me = this.icn3dui; me.icn3d;
9731
- me.htmlCls.shownMenus = {};
9731
+ // me.htmlCls.shownMenus = {};
9732
9732
 
9733
- let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
9734
-
9735
- if(idArrayStr && idArrayStr != '[]') {
9736
- let idArray = JSON.parse(idArrayStr);
9733
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
9737
9734
 
9738
- // for(let i = 0, il = idArray.length; i < il; ++i) {
9739
- // me.htmlCls.shownMenus[idArray[i]] = 1;
9740
- // }
9741
- for(let menu in me.htmlCls.allMenus) {
9742
- if(idArray.indexOf(menu) == -1) {
9743
- me.htmlCls.shownMenus[menu] = 1;
9744
- }
9735
+ let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
9736
+
9737
+ if(idArrayStr && idArrayStr != '[]') {
9738
+ me.htmlCls.shownMenus = {};
9739
+
9740
+ let idArray = JSON.parse(idArrayStr);
9741
+
9742
+ // for(let i = 0, il = idArray.length; i < il; ++i) {
9743
+ // me.htmlCls.shownMenus[idArray[i]] = 1;
9744
+ // }
9745
+ for(let menu in me.htmlCls.allMenus) {
9746
+ if(idArray.indexOf(menu) == -1) {
9747
+ me.htmlCls.shownMenus[menu] = 1;
9745
9748
  }
9746
- }
9747
- else {
9748
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9749
- }
9749
+ }
9750
+ }
9751
+ //###
9752
+ else {
9753
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9754
+ }
9755
+
9756
+ // else {
9757
+ // if(mode == 'all') {
9758
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9759
+ // }
9760
+ // else if(!mode || mode == 'simple') {
9761
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
9762
+ // }
9763
+ // else {
9764
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
9765
+ // }
9766
+ // }
9750
9767
  }
9751
9768
 
9752
9769
  displayShownMenus() { let me = this.icn3dui; me.icn3d;
@@ -10242,11 +10259,14 @@ class ClickMenu {
10242
10259
  me.htmlCls.shownMenus[checkbox.value] = 1;
10243
10260
  }
10244
10261
 
10262
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
10263
+
10245
10264
  thisClass.applyShownMenus();
10246
10265
  });
10247
10266
 
10248
10267
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref", "#" + me.pre + "reset_menupref2"], "click", function(e) { me.icn3d; //e.preventDefault();
10249
10268
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
10269
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'simple');
10250
10270
 
10251
10271
  thisClass.applyShownMenus();
10252
10272
  thisClass.displayShownMenus();
@@ -10254,6 +10274,7 @@ class ClickMenu {
10254
10274
 
10255
10275
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref_all", "#" + me.pre + "reset_menupref_all2"], "click", function(e) { me.icn3d; //e.preventDefault();
10256
10276
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
10277
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'all');
10257
10278
 
10258
10279
  thisClass.applyShownMenus();
10259
10280
  thisClass.displayShownMenus();
@@ -10303,15 +10324,16 @@ class ClickMenu {
10303
10324
 
10304
10325
  thisClass.applyShownMenus();
10305
10326
  thisClass.displayShownMenus();
10327
+
10328
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
10306
10329
  };
10307
10330
  reader.readAsText(file);
10308
10331
  }
10309
10332
  });
10310
10333
 
10311
- me.myEventCls.onIds("#" + me.pre + "mn1_menuloadpref", "click", function(e) { me.icn3d; //e.preventDefault();
10334
+ me.myEventCls.onIds(["#" + me.pre + "mn1_menuloadpref", "#" + me.pre + "loadpref", "#" + me.pre + "loadpref2"], "click", function(e) { me.icn3d; //e.preventDefault();
10312
10335
  me.htmlCls.dialogCls.openDlg('dl_menuloadpref', 'Please input the menu preference file');
10313
10336
  });
10314
-
10315
10337
 
10316
10338
  me.myEventCls.onIds("#" + me.pre + "mn1_link_structure", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10317
10339
  let url = ic.saveFileCls.getLinkToStructureSummary(true);
@@ -12150,6 +12172,51 @@ class SetMenu {
12150
12172
  return me.htmlCls.setHtmlCls.getRadioColor(radioid, id, text, color, bChecked, bSimpleMenu, selType);
12151
12173
  }
12152
12174
 
12175
+ resetMenu(mode) { let me = this.icn3dui;
12176
+ if(!mode || mode == 'simple') {
12177
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12178
+
12179
+ me.htmlCls.clickMenuCls.applyShownMenus();
12180
+ }
12181
+ else if(mode == 'all') {
12182
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
12183
+
12184
+ me.htmlCls.clickMenuCls.applyShownMenus();
12185
+ }
12186
+ else if(mode == 'custom') {
12187
+ me.htmlCls.dialogCls.openDlg('dl_menupref', 'Select Menus');
12188
+
12189
+ me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
12190
+
12191
+ me.htmlCls.clickMenuCls.displayShownMenus();
12192
+ }
12193
+ }
12194
+
12195
+ setMenuMode(bMobile) { let me = this.icn3dui;
12196
+ let spaceCss = (bMobile) ? "; padding-left:6px; background-color:#eee" : "; margin:3px; background-color:white";
12197
+ let spaceCss2 = (bMobile) ? "; font-size:14px!important" : "";
12198
+
12199
+ let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
12200
+
12201
+ let html = '<div class="icn3d-text" style="color:#f8b84e; font-weight:bold' + spaceCss + '">';
12202
+ html += '<select name="menumode" id="' + me.pre + 'menumode" class="icn3d-text" style="color:#f8b84e; font-weight:bold; border:0px' + spaceCss2 + '">';
12203
+ html += (mode == 'simple' || !mode) ? '<option value="simple" selected>Simple</option>' : '<option value="simple">Simple</option>';
12204
+ html += (mode == 'all') ? '<option value="all" selected>All</option>' : '<option value="all">All</option>';
12205
+ html += (mode == 'custom') ? '<option value="custom" selected>Custom</option>' : '<option value="custom">Custom</option>';
12206
+ html += '</select>';
12207
+
12208
+ if(bMobile) {
12209
+ html += '<br><span style="font-size:12px">&nbsp;Menus</span>';
12210
+ }
12211
+ else {
12212
+ html += '&nbsp;Menus';
12213
+ }
12214
+
12215
+ html += '</div>';
12216
+
12217
+ return html;
12218
+ }
12219
+
12153
12220
  //Set the HTML code for the menus shown at the top of the viewer.
12154
12221
  setTopMenusHtml(id, str1, str2) { let me = this.icn3dui;
12155
12222
  if(me.bNode) return '';
@@ -12169,6 +12236,9 @@ class SetMenu {
12169
12236
  html += "<table border='0' cellpadding='0' cellspacing='0' width='100'><tr>";
12170
12237
 
12171
12238
  let tdStr = '<td valign="top">';
12239
+
12240
+ // html += tdStr + this.setMenuMode() + '</td>';
12241
+
12172
12242
  html += tdStr + this.setMenu1() + '</td>';
12173
12243
 
12174
12244
  html += tdStr + this.setMenu2() + '</td>';
@@ -12181,7 +12251,11 @@ class SetMenu {
12181
12251
  //html += tdStr + this.setMenu5b() + '</td>';
12182
12252
  html += tdStr + this.setMenu6() + '</td>';
12183
12253
 
12184
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12254
+ // reset the menus at the end of the menus
12255
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
12256
+ // this.resetMenu(mode);
12257
+
12258
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12185
12259
 
12186
12260
  html += tdStr + "<div style='position:relative; margin-left:6px;'>" + str1;
12187
12261
  html += "<div class='icn3d-commandTitle' style='min-width:40px; margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -12277,6 +12351,7 @@ class SetMenu {
12277
12351
 
12278
12352
  //html += "<div class='icn3d-menu'>";
12279
12353
  html += "<div>";
12354
+
12280
12355
  html += "<accordion id='" + me.pre + "accordion0' class='icn3d-accordion'>";
12281
12356
  if(me.cfg.notebook) {
12282
12357
  html += "<h3 style='width:20px; height:24px; position:relative; padding: 0'><span style='position:absolute; left:3px; top:4px;'>&#9776;</span></h3>";
@@ -12286,6 +12361,8 @@ class SetMenu {
12286
12361
  }
12287
12362
  html += "<div>";
12288
12363
 
12364
+ // html += '<li>' + this.setMenuMode(true);
12365
+
12289
12366
  let liStr = "<li><span class='icn3d-menu-color'";
12290
12367
 
12291
12368
  html += "<ul class='icn3d-mn-item'>";
@@ -12304,7 +12381,11 @@ class SetMenu {
12304
12381
  html += liStr + ">Help</span>";
12305
12382
  html += this.setMenu6_base();
12306
12383
 
12307
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12384
+ // reset the menus at the end of the menus
12385
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
12386
+ // this.resetMenu(mode);
12387
+
12388
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12308
12389
 
12309
12390
  html += "<li><div style='position:relative; margin-top:-6px;'>" + str1;
12310
12391
  html += "<div class='icn3d-commandTitle' style='margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -12481,7 +12562,7 @@ class SetMenu {
12481
12562
  // Analysis menu
12482
12563
  html += tdStrBorder + this.setIcon(iconType, 'tool_selectannotations', 'Sequences & Annotations', 'grip-lines') + "</td>";
12483
12564
  html += tdStr + this.setIcon(iconType, 'hbondsYes', 'Interactions', 'users') + "</td>";
12484
- html += tdStr + this.setIcon(iconType, 'tool_delphi', 'Delphi Potentials', 'cloud-meatball') + "</td>";
12565
+ html += tdStr + this.setIcon(iconType, 'tool_delphi', 'DelPhi Potentials', 'cloud-meatball') + "</td>";
12485
12566
  html += tdStr + this.setIcon(iconType, 'removeLabels', 'Remove Labels', 'remove-format') + "</td>";
12486
12567
 
12487
12568
  // Help menu
@@ -13693,6 +13774,10 @@ class SetMenu {
13693
13774
 
13694
13775
  html += "<ul class='icn3d-mn-item'>";
13695
13776
 
13777
+ if(me.cfg.cid !== undefined || me.cfg.smiles !== undefined) {
13778
+ html += this.getLink('mn2_2ddepiction', '2D Depiction ' + me.htmlCls.wifiStr, 1, 1);
13779
+ }
13780
+
13696
13781
  if(me.cfg.cid === undefined) {
13697
13782
  html += this.getLink('mn6_selectannotations', 'Seq. & Annotations ' + me.htmlCls.wifiStr, 1, 1);
13698
13783
 
@@ -14494,7 +14579,7 @@ class Dialog {
14494
14579
  width='50%';
14495
14580
  }
14496
14581
  else if(id === me.pre + 'dl_menupref') {
14497
- width = 600;
14582
+ width = 800;
14498
14583
  height = 500;
14499
14584
  }
14500
14585
 
@@ -15155,6 +15240,10 @@ class SetDialog {
15155
15240
  html += me.htmlCls.divStr + "dl_collection_file' style=''>";
15156
15241
  html += "You can load a collection of structures via a file. Here are <a href='https://github.com/ncbi/icn3d/blob/master/example/collection/' target='_blank'>some example files</a><br><br>";
15157
15242
  html += "Collection file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "collectionfile'><br/>";
15243
+ html += "<input type='radio' id='dl_collectionAppendStructureNone' name='appendStructure' value='none' checked/>";
15244
+ html += "<label for='dl_collectionAppendStructureNone'>Default</label>";
15245
+ html += "<input type='radio' id='dl_collectionAppendStructure' name='appendStructure' value='append' />";
15246
+ html += "<label for='dl_collectionAppendStructure'>Append</label><br/>";
15158
15247
  html += me.htmlCls.buttonStr + "reload_collectionfile' style='margin-top: 6px;'>Load</button>";
15159
15248
  html += "</div>";
15160
15249
  html += "</div>";
@@ -15162,7 +15251,8 @@ class SetDialog {
15162
15251
  html += me.htmlCls.divStr + "dl_collection_structures' style='display: none'>";
15163
15252
  html += "<select id='" + me.pre + "collections_menu'multiple size='6' style='min-width:300px;'></select>";
15164
15253
  html += '<br/>';
15165
- html += me.htmlCls.buttonStr + "opendl_export_collections'>Export</button>";
15254
+ html += me.htmlCls.buttonStr + "collections_clear_commands' style='margin-top: 6px;'>Clear Commands</button>";
15255
+ html += me.htmlCls.buttonStr + "opendl_export_collections'>Export JSON</button>";
15166
15256
  html += "</div>";
15167
15257
  html += '<br/>';
15168
15258
  html += "</div>";
@@ -15517,24 +15607,30 @@ class SetDialog {
15517
15607
  html += "</div>";
15518
15608
 
15519
15609
 
15520
- html += me.htmlCls.divStr + "dl_ligplot' sty2D Interaction for One Ligand/Residule='background-color:white' class='" + dialogClass + "'>";
15521
- html += this.addNotebookTitle('dl_ligplot', 'e with Atom Details');
15610
+ html += me.htmlCls.divStr + "dl_ligplot' style='background-color:white' class='" + dialogClass + "'>";
15611
+
15612
+ if(me.cfg.cid !== undefined || me.cfg.smiles !== undefined) {
15613
+ html += this.addNotebookTitle('dl_ligplot', '2D Depiction for Chemicals');
15614
+ }
15615
+ else {
15616
+ html += this.addNotebookTitle('dl_ligplot', '2D Interaction for One Ligand/Residue with Atom Details');
15522
15617
 
15523
- html += me.htmlCls.divNowrapStr + "<b>Note</b>: Nodes/Residues can be dragged. Both nodes and dashed lines/interactions can be clicked to select residues. " + me.htmlCls.space3;
15618
+ html += me.htmlCls.divNowrapStr + "<b>Note</b>: Nodes/Residues can be dragged. Both nodes and dashed lines/interactions can be clicked to select residues. " + me.htmlCls.space3;
15524
15619
 
15525
- html += '<div style="width:20px; margin-top:6px; display:inline-block;"><span id="'
15526
- + me.pre + 'dl_ligplotcolor_expand" class="ui-icon ui-icon-plus icn3d-expand icn3d-link" style="display:none; width:15px;" title="Expand"></span><span id="'
15527
- + me.pre + 'dl_ligplotcolor_shrink" class="ui-icon ui-icon-minus icn3d-shrink icn3d-link" style="width:15px;" title="Shrink"></span></div></div>';
15620
+ html += '<div style="width:20px; margin-top:6px; display:inline-block;"><span id="'
15621
+ + me.pre + 'dl_ligplotcolor_expand" class="ui-icon ui-icon-plus icn3d-expand icn3d-link" style="display:none; width:15px;" title="Expand"></span><span id="'
15622
+ + me.pre + 'dl_ligplotcolor_shrink" class="ui-icon ui-icon-minus icn3d-shrink icn3d-link" style="width:15px;" title="Shrink"></span></div></div>';
15528
15623
 
15529
- html += me.htmlCls.divStr + "dl_ligplotcolor' style='inline-block;'>";
15624
+ html += me.htmlCls.divStr + "dl_ligplotcolor' style='inline-block;'>";
15530
15625
 
15531
- // html += "The real interaction distances are not in scale, and are about twice the distances of dashed line segments.<br>Some \"Contact\" lines are only shown partially to simplify the view.<br>";
15532
- // html += "Mouseover the dashed lines to see interaction types and distances.<br>";
15533
- html += "<b>Color legend</b> for interactions (dashed lines): <br>";
15626
+ // html += "The real interaction distances are not in scale, and are about twice the distances of dashed line segments.<br>Some \"Contact\" lines are only shown partially to simplify the view.<br>";
15627
+ // html += "Mouseover the dashed lines to see interaction types and distances.<br>";
15628
+ html += "<b>Color legend</b> for interactions (dashed lines): <br>";
15534
15629
 
15535
- html += me.htmlCls.setHtmlCls.setColorHints();
15630
+ html += me.htmlCls.setHtmlCls.setColorHints();
15536
15631
 
15537
- html += "<br></div>";
15632
+ html += "<br></div>";
15633
+ }
15538
15634
 
15539
15635
  me.ligplotid = me.pre + 'ligplot';
15540
15636
  html += me.htmlCls.divNowrapStr + buttonStrTmp + me.ligplotid + '_svg">SVG</button>' + me.htmlCls.space2;
@@ -15861,13 +15957,15 @@ class SetDialog {
15861
15957
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref'>Apply</button></span>";
15862
15958
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref' style='margin-left:30px'>Reset to Simple Menus</button></span>";
15863
15959
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all' style='margin-left:30px'>Reset to All Menus</button></span>";
15864
- html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span><br><br>";
15960
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span>";
15961
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref' style='margin-left:30px'>Load Preferences</button></span><br><br>";
15865
15962
 
15866
15963
  html += "<div id='" + me.pre + "menulist'></div><br><br>";
15867
15964
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref2'>Apply</button></span>";
15868
15965
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref2' style='margin-left:30px'>Reset to Simple Menus</button></span>";
15869
15966
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all2' style='margin-left:30px'>Reset to All Menus</button></span>";
15870
15967
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref2' style='margin-left:30px'>Save Preferences</button></span>";
15968
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref2' style='margin-left:30px'>Load Preferences</button></span>";
15871
15969
  html += "</div>";
15872
15970
 
15873
15971
  html += me.htmlCls.divStr + "dl_addtrack' class='" + dialogClass + "'>";
@@ -16643,6 +16741,14 @@ class Events {
16643
16741
  me.htmlCls.clickMenuCls.clickMenu5();
16644
16742
  me.htmlCls.clickMenuCls.clickMenu6();
16645
16743
 
16744
+ me.myEventCls.onIds("#" + me.pre + "menumode", "change", async function(e) { me.icn3d;
16745
+ e.preventDefault();
16746
+ let mode = $("#" + me.pre + "menumode").val();
16747
+
16748
+ me.htmlCls.setHtmlCls.setCookie('menumode', mode);
16749
+ me.htmlCls.setMenuCls.resetMenu(mode);
16750
+ });
16751
+
16646
16752
  // back and forward arrows
16647
16753
  me.myEventCls.onIds(["#" + me.pre + "back", "#" + me.pre + "mn6_back"], "click", async function(e) { let ic = me.icn3d;
16648
16754
  e.preventDefault();
@@ -16920,6 +17026,11 @@ class Events {
16920
17026
  thisClass.setLogCmd("view interactions", true);
16921
17027
  });
16922
17028
 
17029
+ me.myEventCls.onIds("#" + me.pre + "mn2_2ddepiction", "click", async function(e) { let ic = me.icn3d;
17030
+ await ic.ligplotCls.drawLigplot(ic.atoms, true);
17031
+ thisClass.setLogCmd("view 2d depiction", true);
17032
+ });
17033
+
16923
17034
  me.myEventCls.onIds("#" + me.pre + "search_seq_button", "click", async function(e) { me.icn3d;
16924
17035
  e.stopImmediatePropagation();
16925
17036
  await thisClass.searchSeq();
@@ -17684,95 +17795,108 @@ class Events {
17684
17795
  } else {
17685
17796
  ic.resizeCanvasCls.closeDialogs();
17686
17797
  }
17687
- ic.bInputfile = false;
17688
- ic.pdbCollection = [];
17689
- ic.allData = {};
17690
- ic.allData['all'] = {
17691
- 'atoms': {},
17692
- 'proteins': {},
17693
- 'nucleotides': {},
17694
- 'chemicals': {},
17695
- 'ions': {},
17696
- 'water': {},
17697
- 'structures': {}, // getSSExpandedAtoms
17698
- 'ssbondpnts': {},
17699
- 'residues': {}, // getSSExpandedAtoms
17700
- 'chains': {},
17701
- 'chainsSeq': {}, //Sequences and Annotation
17702
- 'defNames2Atoms': {},
17703
- 'defNames2Residues': {}
17704
- };
17705
- ic.allData['prev'] = {};
17706
- ic.selectCollectionsCls.reset();
17707
-
17798
+
17708
17799
  ic.dAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
17709
17800
  ic.hAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
17710
17801
  me.htmlCls.setHtmlCls.fileSupport();
17711
17802
 
17712
17803
  let fileName = file.name;
17713
17804
  let fileExtension = fileName.split('.').pop().toLowerCase();
17714
-
17805
+ let collection = {};
17806
+
17715
17807
  $("#" + ic.pre + "collections_menu").empty();
17716
17808
  $("#" + ic.pre + "collections_menu").off("change");
17809
+
17810
+ if (dl_collectionAppendStructureNone.checked || ic.allData === undefined) {
17811
+ ic.bInputfile = false;
17812
+ ic.pdbCollection = {};
17813
+ ic.allData = {};
17814
+ ic.allData['all'] = {
17815
+ 'atoms': {},
17816
+ 'proteins': {},
17817
+ 'nucleotides': {},
17818
+ 'chemicals': {},
17819
+ 'ions': {},
17820
+ 'water': {},
17821
+ 'structures': {}, // getSSExpandedAtoms
17822
+ 'ssbondpnts': {},
17823
+ 'residues': {}, // getSSExpandedAtoms
17824
+ 'chains': {},
17825
+ 'chainsSeq': {}, //Sequences and Annotation
17826
+ 'defNames2Atoms': {},
17827
+ 'defNames2Residues': {}
17828
+ };
17829
+ ic.allData['prev'] = {};
17830
+ ic.selectCollectionsCls.reset();
17831
+
17832
+ } else {
17833
+ if (ic.collections) {
17834
+ collection = ic.collections;
17835
+ }
17836
+ }
17717
17837
 
17718
17838
  function parseJsonCollection(data) {
17719
17839
  let dataStr = JSON.parse(data);
17720
- return dataStr["structures"].map(({ id, title, description, commands }) => {
17840
+ let parsedCollection = {};
17841
+
17842
+ dataStr["structures"].map(({ id, title, description, commands }) => {
17721
17843
  if (id && id.includes('.pdb')) {
17722
17844
  id = id.split('.pdb')[0];
17723
17845
  }
17724
- return [id, title, description, commands, false];
17846
+ parsedCollection[id] = [id, title, description, commands, false];
17725
17847
  });
17726
- }
17727
17848
 
17849
+ return parsedCollection;
17850
+ }
17851
+
17728
17852
  function parsePdbCollection(data, description = '', commands = []) {
17729
17853
  let dataStr = data;
17730
17854
  let lines = dataStr.split('\n');
17731
-
17732
17855
  let sections = [];
17733
17856
  let currentSection = [];
17734
-
17857
+
17735
17858
  lines.forEach(line => {
17736
- if (line.startsWith('HEADER')) {
17859
+ if (line.startsWith('HEADER')) {
17737
17860
  currentSection = [];
17738
17861
  sections.push(currentSection);
17739
- }
17740
- currentSection.push(line);
17862
+ }
17863
+ currentSection.push(line);
17741
17864
  });
17742
-
17743
- let ids = [];
17744
- let titles = [];
17745
-
17865
+
17866
+
17867
+ let parsedCollection = {};
17868
+
17746
17869
  sections.forEach((section) => {
17747
- let headerLine = section[0];
17748
- headerLine = headerLine.replace(/[\n\r]/g, '').trim();
17870
+ let headerLine = section[0].replace(/[\n\r]/g, '').trim();
17749
17871
  let header = headerLine.split(' ').filter(Boolean);
17750
- let lastElement = header[header.length - 1];
17751
- ids.push(lastElement);
17752
- titles.push(section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : lastElement);
17872
+ let id = header[header.length - 1];
17873
+ let title = section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : id;
17874
+
17875
+ parsedCollection[id] = [id, title, description, commands, true];
17876
+
17877
+ const sanitizedSection = section.map(line => line.trim());
17878
+ ic.pdbCollection[id] = sanitizedSection;
17753
17879
  });
17754
-
17755
- if (sections.length > 0) {
17756
- ic.pdbCollection.push(...sections);
17757
- }
17758
17880
 
17759
- return ids.map((id, index, description, commands) => [id, titles[index], description, commands, true]);
17881
+ return parsedCollection;
17760
17882
  }
17761
17883
 
17762
- let collection = [];
17763
-
17764
17884
  if (fileExtension === 'json' || fileExtension === 'pdb') {
17765
17885
  let reader = new FileReader();
17766
17886
  reader.onload = async function (e) {
17767
17887
  if (fileExtension === 'json') {
17768
- collection = parseJsonCollection(e.target.result);
17888
+ let jsonCollection = parseJsonCollection(e.target.result);
17889
+ collection = { ...collection, ...jsonCollection };
17769
17890
  } else if (fileExtension === 'pdb') {
17770
17891
  ic.bInputfile = true;
17771
- collection = parsePdbCollection(e.target.result);
17892
+ let pdbCollection = parsePdbCollection(e.target.result);
17893
+ collection = { ...collection, ...pdbCollection };
17772
17894
  }
17773
17895
 
17774
17896
  let collectionHtml = await ic.selectCollectionsCls.setAtomMenu(collection);
17775
17897
 
17898
+ ic.collections = collection;
17899
+
17776
17900
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
17777
17901
  await ic.selectCollectionsCls.clickStructure(collection);
17778
17902
  $("#" + ic.pre + "collections_menu").trigger("change");
@@ -17790,7 +17914,7 @@ class Events {
17790
17914
  let reader2 = new FileReader();
17791
17915
  reader2.onload = async function (e) {
17792
17916
  if (fileExtension === 'zip') {
17793
- let url = './script/jszip.js';
17917
+ let url = './script/jszip.min.js';
17794
17918
  await me.getAjaxPromise(url, 'script');
17795
17919
 
17796
17920
  let jszip = new JSZip();
@@ -17824,7 +17948,8 @@ class Events {
17824
17948
  let jsonCollection = [];
17825
17949
  for (const file of jsonFiles) {
17826
17950
  let fileData = await file.async('text');
17827
- parseJsonCollection(fileData).forEach(element => {
17951
+ let parsedJson = Object.values(parseJsonCollection(fileData));
17952
+ parsedJson.forEach(element => {
17828
17953
  jsonCollection.push(element);
17829
17954
  });
17830
17955
  }
@@ -17834,8 +17959,9 @@ class Events {
17834
17959
  let matchingPdbFile = pdbFiles.find(file => file.name.toLowerCase().includes(id.toLowerCase()));
17835
17960
  if (matchingPdbFile) {
17836
17961
  let pdbFileData = await matchingPdbFile.async('text');
17837
- parsePdbCollection(pdbFileData, description, commands).forEach(element => {
17838
- collection.push(element);
17962
+ let parsedPdb = Object.values(parsePdbCollection(pdbFileData, description, commands));
17963
+ parsedPdb.forEach(element => {
17964
+ collection[id] = element;
17839
17965
  });
17840
17966
  }
17841
17967
  }
@@ -17844,27 +17970,30 @@ class Events {
17844
17970
  // Do something if only JSON files are present
17845
17971
  jsonFiles.forEach(async file => {
17846
17972
  let fileData = await file.async('text');
17847
- parseJsonCollection(fileData).forEach(element => {
17848
- collection.push(element);
17973
+ const parsedJson = Object.values(parseJsonCollection(fileData));
17974
+ parsedJson.forEach(element => {
17975
+ collection[element[0]] = element;
17849
17976
  });
17850
17977
  });
17851
17978
  } else if (hasPdb) {
17852
17979
  // Do something if only PDB files are present
17853
17980
  pdbFiles.forEach(async file => {
17854
17981
  let fileData = await file.async('text');
17855
- parsePdbCollection(fileData).forEach(element => {
17856
- collection.push(element);
17982
+ const parsedPdb = Object.values(parsedPdbCollection(fileData));
17983
+ parsedPdb.forEach(element => {
17984
+ collection[element[0]] = element;
17857
17985
  });
17858
17986
  });
17859
17987
  } else if (hasGz) {
17860
- let url = './script/pako.js';
17988
+ let url = './script/pako.min.js';
17861
17989
  await me.getAjaxPromise(url, 'script');
17862
17990
  try {
17863
17991
  for (const file of gzFiles) {
17864
17992
  let compressed = await file.async('uint8array');
17865
17993
  let decompressed = pako.inflate(compressed, { to: 'string' });
17866
- parsePdbCollection(decompressed).forEach(element => {
17867
- collection.push(element);
17994
+ const parsedPdb = Object.values(parsePdbCollection(decompressed));
17995
+ parsedPdb.forEach(element => {
17996
+ collection[element[0]] = element;
17868
17997
  });
17869
17998
  }
17870
17999
  } catch (error) {
@@ -17875,7 +18004,7 @@ class Events {
17875
18004
  console.error('Error loading ZIP file', error);
17876
18005
  }
17877
18006
  } else if (fileExtension === 'gz') {
17878
- let url = './script/pako.js';
18007
+ let url = './script/pako.min.js';
17879
18008
  await me.getAjaxPromise(url, 'script');
17880
18009
 
17881
18010
  try {
@@ -17892,6 +18021,8 @@ class Events {
17892
18021
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
17893
18022
  await ic.selectCollectionsCls.clickStructure(collection);
17894
18023
 
18024
+ ic.collections = collection;
18025
+
17895
18026
  $("#" + ic.pre + "collections_menu").trigger("change");
17896
18027
 
17897
18028
  me.htmlCls.clickMenuCls.setLogCmd(
@@ -17910,7 +18041,7 @@ class Events {
17910
18041
  throw new Error('Invalid file type');
17911
18042
  }
17912
18043
 
17913
- if (Object.keys(me.utilsCls.getStructures(ic.dAtoms))){
18044
+ if (ic.allData && Object.keys(ic.allData).length > 0) {
17914
18045
  $("#" + me.pre + "dl_collection_file").hide();
17915
18046
  $("#" + me.pre + "dl_collection_structures").show();
17916
18047
  $("#" + me.pre + "dl_collection_file_expand").show();
@@ -17931,6 +18062,17 @@ class Events {
17931
18062
  }
17932
18063
  });
17933
18064
 
18065
+ me.myEventCls.onIds("#" + me.pre + "collections_clear_commands", "click", function (e) {
18066
+ var selectedValues = $("#" + ic.pre + "collections_menu").val();
18067
+ selectedValues.forEach(function (selectedValue) {
18068
+ if (ic.allData[selectedValue]) {
18069
+ ic.allData[selectedValue]['commands'] = [];
18070
+ } else {
18071
+ console.warn("No data found for selectedValue:", selectedValue);
18072
+ }
18073
+ });
18074
+ });
18075
+
17934
18076
  me.myEventCls.onIds("#" + me.pre + "opendl_export_collections", "click", function (e) {
17935
18077
  me.htmlCls.dialogCls.openDlg("dl_export_collections", "Export Collections");
17936
18078
  });
@@ -20417,9 +20559,9 @@ class SetHtml {
20417
20559
 
20418
20560
  let pdbstr = '';
20419
20561
 
20420
- let bMergeIntoOne = true;
20421
- pdbstr += ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
20422
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
20562
+ let bMergeIntoOne = true, bOneLetterChain = true;
20563
+ pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain);
20564
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
20423
20565
 
20424
20566
  let url = me.htmlCls.baseUrl + "delphi/delphi.cgi";
20425
20567
 
@@ -28123,6 +28265,8 @@ class Camera {
28123
28265
  }
28124
28266
  }
28125
28267
 
28268
+ // ic.cam.add(ic.directionalLight);
28269
+
28126
28270
  ic.cam.updateProjectionMatrix();
28127
28271
  // }
28128
28272
  }
@@ -29621,687 +29765,233 @@ class Stick {
29621
29765
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29622
29766
  */
29623
29767
 
29624
- class Strand {
29768
+ class FirstAtomObj {
29625
29769
  constructor(icn3d) {
29626
29770
  this.icn3d = icn3d;
29627
29771
  }
29628
29772
 
29629
- // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29630
- //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
29631
- //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
29632
- //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
29633
- //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
29634
- //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
29635
- //with bHighlight=1 and 3D objects with bHighlight=2.
29636
- createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
29637
- if(me.bNode) return;
29773
+ //Return the first atom in the atom hash, which has the atom serial number as the key.
29774
+ getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29775
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29776
+ return undefined;
29777
+ }
29638
29778
 
29639
- let bRibbon = fill ? true: false;
29779
+ let atomKeys = Object.keys(atomsHash);
29780
+ let firstIndex = atomKeys[0];
29640
29781
 
29641
- // when highlight, the input atoms may only include one rediue.
29642
- // add one extra residue to show the strand
29643
- let atomsAdjust = {};
29782
+ return ic.atoms[firstIndex];
29783
+ }
29644
29784
 
29645
- let residueHashTmp = ic.firstAtomObjCls.getResiduesFromAtoms(atoms);
29646
- if( Object.keys(residueHashTmp).length == 1) {
29647
- atomsAdjust = this.getOneExtraResidue(residueHashTmp);
29785
+ // n is the position of the selected atom
29786
+ getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
29787
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29788
+ return undefined;
29648
29789
  }
29649
- else {
29650
- atomsAdjust = atoms;
29790
+
29791
+ let atomKeys = Object.keys(atomsHash);
29792
+ let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
29793
+
29794
+ return ic.atoms[middleIndex];
29795
+ }
29796
+
29797
+ getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29798
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29799
+ return undefined;
29651
29800
  }
29652
29801
 
29653
- if(bHighlight === 2) {
29654
- if(fill) {
29655
- fill = false;
29656
- num = null;
29657
- div = null;
29658
- coilWidth = null;
29659
- helixSheetWidth = null;
29660
- thickness = undefined;
29802
+ let firstIndex;
29803
+
29804
+ for(let i in atomsHash) {
29805
+ if(ic.atoms[i].name == 'CA') {
29806
+ firstIndex = i;
29807
+ break;
29661
29808
  }
29662
- else {
29663
- fill = true;
29664
- num = 2;
29665
- div = undefined;
29666
- coilWidth = undefined;
29667
- helixSheetWidth = undefined;
29668
- thickness = ic.ribbonthickness;
29809
+ }
29810
+
29811
+ if(!firstIndex) {
29812
+ for(let i in atomsHash) {
29813
+ if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
29814
+ firstIndex = i;
29815
+ break;
29816
+ }
29669
29817
  }
29670
29818
  }
29671
29819
 
29672
- num = num || ic.strandDIV;
29673
- div = div || ic.axisDIV;
29674
- coilWidth = coilWidth || ic.coilWidth;
29675
- doNotSmoothen = doNotSmoothen || false;
29676
- helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
29677
- let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
29678
- let pntsCA = [];
29679
- let prevCOArray = [];
29680
- let bShowArray = [];
29681
- let calphaIdArray = []; // used to store one of the final positions drawn in 3D
29682
- let colors = [];
29683
- let currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
29684
- let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevAtomSelected = null, prevResi = null, calphaid = null, prevCalphaid = null;
29685
- let strandWidth, bSheetSegment = false, bHelixSegment = false;
29820
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
29821
+ }
29686
29822
 
29687
- // For each chain, test the first 30 atoms to see whether only C-alpha is available
29688
- let bCalphaOnlyHash = {};
29689
- for(let chainid in ic.chains) {
29690
- let atoms = me.hashUtilsCls.hash2Atoms(ic.chains[chainid], ic.atoms);
29691
- let bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atoms); //, 'CA');
29692
- bCalphaOnlyHash[chainid] = bCalphaOnly;
29823
+ getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
29824
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29825
+ return ic.atoms[0];
29693
29826
  }
29694
29827
 
29695
- // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
29696
- let residueHash = {};
29697
- for(let i in atomsAdjust) {
29698
- let atom = ic.atoms[i];
29828
+ let firstIndex;
29699
29829
 
29700
- let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
29701
- residueHash[residueid] = 1;
29830
+ for(let i in atomsHash) {
29831
+ if(ic.atoms[i].name == atomName) {
29832
+ firstIndex = i;
29833
+ break;
29834
+ }
29702
29835
  }
29703
- Object.keys(residueHash).length;
29704
-
29705
- let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
29706
29836
 
29707
- let caArray = []; // record all C-alpha atoms to predict the helix
29837
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
29838
+ }
29708
29839
 
29709
- let maxDist = 6.0;
29840
+ //Return the last atom in the atom hash, which has the atom serial number as the key.
29841
+ getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29842
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29843
+ return ic.atoms[0];
29844
+ }
29710
29845
 
29711
- //get the last residue
29712
- let atomArray = Object.keys(atomsAdjust);
29713
- let lastAtomSerial = atomArray[atomArray.length - 1];
29714
- let lastAtom = ic.atoms[lastAtomSerial];
29715
- let lastResid = lastAtom.structure + '_' + lastAtom.chain + '_' + lastAtom.resi;
29846
+ let atomKeys = Object.keys(atomsHash);
29847
+ let lastIndex = atomKeys[atomKeys.length - 1];
29716
29848
 
29717
- for (let i in atomsAdjust) {
29718
- let atom = ic.atoms[i];
29719
- let chainid = atom.structure + '_' + atom.chain;
29720
- let resid = atom.structure + '_' + atom.chain + '_' + atom.resi;
29721
- if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
29722
- // "CA" has to appear before "O"
29849
+ return ic.atoms[lastIndex];
29850
+ }
29723
29851
 
29724
- if (atom.name === 'CA') {
29725
- if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) ;
29852
+ //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
29853
+ getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29854
+ let residuesHash = {};
29855
+ for(let i in atomsHash) {
29856
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
29857
+ residuesHash[residueid] = 1;
29858
+ }
29726
29859
 
29727
- currentCA = atom.coord;
29728
- currentColor = atom.color;
29729
- calphaid = atom.serial;
29860
+ return residuesHash;
29861
+ }
29730
29862
 
29731
- caArray.push(atom.serial);
29863
+ getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29864
+ let residuesHash = {};
29865
+ for(let i in atomsHash) {
29866
+ if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
29867
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
29868
+ //residuesHash[residueid] = 1;
29869
+ residuesHash[residueid] = ic.atoms[i].resn;
29732
29870
  }
29871
+ }
29733
29872
 
29734
- if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA')) {
29735
- if(currentCA === null || currentCA === undefined) {
29736
- currentCA = atom.coord;
29737
- currentColor = atom.color;
29738
- calphaid = atom.serial;
29739
- }
29873
+ return residuesHash;
29874
+ }
29740
29875
 
29741
- if(atom.name === 'O') {
29742
- currentO = atom.coord;
29743
- }
29744
- // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
29745
-
29746
- // let bSameChain = true;
29747
- // if (currentChain !== atom.chain) {
29748
- // //if (currentChain !== atom.chain) {
29749
- // bSameChain = false;
29750
- // }
29876
+ //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
29877
+ getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29878
+ let chainsHash = {};
29879
+ for(let i in atomsHash) {
29880
+ let atom = ic.atoms[i];
29881
+ let chainid = atom.structure + "_" + atom.chain;
29751
29882
 
29752
- if(atom.ssend && atom.ss === 'sheet') {
29753
- bSheetSegment = true;
29754
- }
29755
- else if( (atom.ssend && atom.ss === 'helix') || resid == lastResid) { // partial sheet will draw as helix
29756
- bHelixSegment = true;
29757
- }
29883
+ chainsHash[chainid] = 1;
29884
+ }
29758
29885
 
29759
- // assign the previous residue
29760
- if(prevCoorO) {
29761
- if(bHighlight === 1 || bHighlight === 2) {
29762
- colors.push(ic.hColor);
29763
- }
29764
- else {
29765
- colors.push(prevColor);
29766
- }
29886
+ return chainsHash;
29887
+ }
29767
29888
 
29768
- if(ss !== 'coil' && atom.ss === 'coil') {
29769
- strandWidth = coilWidth;
29770
- }
29771
- else if(ssend && atom.ssbegin) { // a transition between two ss
29772
- strandWidth = coilWidth;
29773
- }
29774
- else {
29775
- strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
29776
- }
29889
+ getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
29890
+ if(ic.residues.hasOwnProperty(resid)) {
29891
+ for(let i in ic.residues[resid]) {
29892
+ if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
29893
+ return ic.atoms[i];
29894
+ }
29895
+ }
29896
+ }
29777
29897
 
29778
- let O, oldCA, resSpan = 4;
29779
- if(atom.name === 'O') {
29780
- O = prevCoorO.clone();
29781
- if(prevCoorCA !== null && prevCoorCA !== undefined) {
29782
- O.sub(prevCoorCA);
29783
- }
29784
- else {
29785
- prevCoorCA = prevCoorO.clone();
29786
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29787
- O = prevCoorCA.clone();
29788
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
29789
- //O.sub(oldCA);
29790
- oldCA.sub(O);
29791
- }
29792
- else {
29793
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29794
- }
29795
- }
29796
- }
29797
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
29798
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29799
- O = prevCoorCA.clone();
29800
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
29801
- //O.sub(oldCA);
29802
- oldCA.sub(O);
29803
- }
29804
- else {
29805
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29806
- }
29807
- }
29898
+ return undefined;
29899
+ }
29808
29900
 
29809
- O.normalize(); // can be omitted for performance
29810
- O.multiplyScalar(strandWidth);
29811
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
29812
- prevCO = O;
29901
+ getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
29902
+ let atom = this.getAtomFromResi(resid, atomName);
29903
+ if(atom !== undefined) {
29904
+ let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
29813
29905
 
29814
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
29815
- let delta = -1 + numM1Inv2 * j;
29816
- let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
29817
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
29818
- pnts[j].push(v);
29819
- }
29906
+ return coord;
29907
+ }
29820
29908
 
29821
- pntsCA.push(prevCoorCA);
29822
- prevCOArray.push(prevCO);
29909
+ return undefined;
29910
+ }
29911
+ }
29823
29912
 
29824
- if(atoms.hasOwnProperty(prevAtomid)) {
29825
- bShowArray.push(prevResi);
29826
- calphaIdArray.push(prevCalphaid);
29827
- }
29828
- else {
29829
- bShowArray.push(0);
29830
- calphaIdArray.push(0);
29831
- }
29832
- }
29913
+ /**
29914
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29915
+ */
29833
29916
 
29834
- //let bBrokenSs = ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi) || (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
29917
+ class Strip {
29918
+ constructor(icn3d) {
29919
+ this.icn3d = icn3d;
29920
+ }
29835
29921
 
29836
- let prevCoor = (prevAtomSelected) ? prevAtomSelected.coord : undefined;
29837
-
29838
- let bBrokenSs = ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi) || (prevCoor && Math.abs(currentCA.x - prevCoor.x) > maxDist) || (prevCoor && Math.abs(currentCA.y - prevCoor.y) > maxDist) || (prevCoor && Math.abs(currentCA.z - prevCoor.z) > maxDist);
29839
-
29840
- // check whether the atoms are continuous
29841
- // atomsAdjusted has all atoms in the secondary structure
29842
- // atoms has all selected atoms
29843
- // let bBrokenSs = false;
29844
- // if(prevAtomSelected && prevAtomid == prevAtomSelected.serial && !atoms.hasOwnProperty(atom.serial)) {
29845
- // bBrokenSs = true;
29846
- // }
29922
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29923
+ createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
29924
+ calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
29925
+ if(me.bNode) return;
29847
29926
 
29927
+ if (p0.length < 2) return;
29928
+ div = div || ic.axisDIV;
29848
29929
 
29849
- // The following code didn't work to select one residue
29850
- // let bBrokenSs = !atoms.hasOwnProperty(atom.serial) || (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
29930
+ // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
29931
+ if(pntsCA && ic.bDoublecolor) {
29932
+ let bExtendLastRes = false; //true;
29851
29933
 
29852
- // if(bBrokenSs && atom.ss === 'sheet') {
29853
- // bSheetSegment = true;
29854
- // }
29855
- // else if(bBrokenSs && atom.ss === 'helix') {
29856
- // bHelixSegment = true;
29857
- // }
29934
+ let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29935
+ pntsCA = pnts_clrs[0];
29858
29936
 
29859
- //if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
29860
- // if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs || resid == lastResid) && pnts[0].length > 0) { // last coil was not drawn correctly, e.g., in 1TOP
29937
+ this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
29861
29938
 
29862
- if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || bBrokenSs || (resid == lastResid && atom.ss != 'coil')) && pnts[0].length > 0) {
29863
- let atomName = 'CA';
29864
-
29865
- let prevone = [], nexttwo = [];
29939
+ for(let i = 0, il = prevCOArray.length; i < il; ++i) {
29940
+ prevCOArray[i].normalize();
29941
+ }
29866
29942
 
29867
- if(isNaN(ic.atoms[prevAtomid].resi)) {
29868
- prevone = [];
29869
- }
29870
- else {
29871
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
29872
- let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
29873
- prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
29874
- }
29943
+ let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29944
+ prevCOArray = pnts_clrs2[0];
29875
29945
 
29876
- if(!isNaN(ic.atoms[prevAtomid].resi)) {
29877
- let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
29878
- let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
29879
- if(nextoneCoord !== undefined) {
29880
- nexttwo.push(nextoneCoord);
29881
- }
29946
+ colors = pnts_clrs[2];
29947
+ }
29948
+ else {
29882
29949
 
29883
- let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
29884
- let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
29885
- if(nexttwoCoord !== undefined) {
29886
- nexttwo.push(nexttwoCoord);
29887
- }
29888
- }
29950
+ if(!bNoSmoothen) {
29951
+ //var bExtendLastRes = true;
29952
+ let bExtendLastRes = false;
29953
+ let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29954
+ let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29955
+ p0 = pnts_clrs0[0];
29956
+ p1 = pnts_clrs1[0];
29957
+ colors = pnts_clrs0[2];
29958
+ }
29959
+ if (p0.length < 2) return;
29889
29960
 
29890
- // include the current residue
29891
- if(!bBrokenSs) {
29892
- // assign the current joint residue to the previous segment
29893
- if(bHighlight === 1 || bHighlight === 2) {
29894
- colors.push(ic.hColor);
29895
- }
29896
- else {
29897
- //colors.push(atom.color);
29898
- colors.push(prevColor);
29899
- }
29961
+ this.setCalphaDrawnCoord(p0, div, calphaIdArray);
29962
+ }
29900
29963
 
29901
- if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
29902
- strandWidth = 0; // make the arrow end sharp
29903
- }
29904
- else if(ss === 'coil' && atom.ssbegin) {
29905
- strandWidth = coilWidth;
29906
- }
29907
- else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
29908
- strandWidth = coilWidth;
29909
- }
29910
- else { // use the ss from the previous residue
29911
- strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
29912
- }
29964
+ if(bHighlight === 1) {
29965
+ //mesh = new THREE.Mesh(geo, ic.matShader);
29913
29966
 
29914
- let O, oldCA, resSpan = 4;
29915
- if(atom.name === 'O') {
29916
- O = currentO.clone();
29917
- O.sub(currentCA);
29918
- }
29919
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
29920
- if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29921
- O = currentCA.clone();
29922
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
29923
- //O.sub(oldCA);
29924
- oldCA.sub(O);
29925
- }
29926
- else {
29927
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29928
- }
29929
- }
29967
+ let radius = ic.coilWidth / 2;
29968
+ //var radiusSegments = 8;
29969
+ let radiusSegments = 4; // save memory
29970
+ let closed = false;
29930
29971
 
29931
- O.normalize(); // can be omitted for performance
29932
- O.multiplyScalar(strandWidth);
29933
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
29934
- prevCO = O;
29972
+ if(positions !== undefined) {
29973
+ let currPos, prevPos;
29974
+ let currP0 = [], currP1 = [];
29935
29975
 
29936
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
29937
- let delta = -1 + numM1Inv2 * j;
29938
- let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
29939
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
29940
- pnts[j].push(v);
29941
- }
29976
+ for(let i = 0, il = p0.length; i < il; ++i) {
29977
+ currPos = positions[i];
29942
29978
 
29943
- atomid = atom.serial;
29979
+ if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
29980
+ // first tube
29981
+ let geometry0 = new THREE.TubeGeometry(
29982
+ new THREE.CatmullRomCurve3(currP0), // path
29983
+ currP0.length, // segments
29984
+ radius,
29985
+ radiusSegments,
29986
+ closed
29987
+ );
29944
29988
 
29945
- pntsCA.push(currentCA);
29946
- prevCOArray.push(prevCO);
29989
+ let mesh = new THREE.Mesh(geometry0, ic.matShader);
29990
+ mesh.renderOrder = ic.renderOrderPicking;
29991
+ //ic.mdlPicking.add(mesh);
29992
+ ic.mdl.add(mesh);
29947
29993
 
29948
- // when a coil connects to a sheet and the last residue of coild is highlighted, the first sheet residue is set as atom.highlightStyle. This residue should not be shown.
29949
- //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
29950
- if(atoms.hasOwnProperty(atomid)) {
29951
- bShowArray.push(atom.resi);
29952
- calphaIdArray.push(calphaid);
29953
- }
29954
- else {
29955
- bShowArray.push(0);
29956
- calphaIdArray.push(0);
29957
- }
29958
- }
29959
-
29960
- // draw the current segment
29961
- for (let j = 0; !fill && j < num; ++j) {
29962
- if(bSheetSegment) {
29963
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29964
- }
29965
- else if(bHelixSegment) {
29966
- if(bFullAtom) {
29967
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
29968
- }
29969
- else {
29970
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29971
- }
29972
- }
29973
- }
29974
- if (fill) {
29975
- if(bSheetSegment) {
29976
- let start = 0, end = num - 1;
29977
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29978
- }
29979
- else if(bHelixSegment) {
29980
- if(bFullAtom) {
29981
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29982
- }
29983
- else {
29984
- let start = 0, end = num - 1;
29985
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29986
- }
29987
- }
29988
- else {
29989
- if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
29990
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29991
- }
29992
- }
29993
- }
29994
- for (let k = 0; k < num; ++k) pnts[k] = [];
29995
-
29996
- colors = [];
29997
- pntsCA = [];
29998
- prevCOArray = [];
29999
- bShowArray = [];
30000
- calphaIdArray = [];
30001
- bSheetSegment = false;
30002
- bHelixSegment = false;
30003
- } // end if (atom.ssbegin || atom.ssend)
30004
-
30005
- // end of a chain, or end of selection
30006
- if ((currentChain !== atom.chain
30007
- || ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi)
30008
- // || (drawnResidueCount === totalResidueCount - 1)
30009
- // || bBrokenSs
30010
- || (resid == lastResid && atom.ss != 'coil')
30011
- ) && pnts[0].length > 0) {
30012
- //if ((currentChain !== atom.chain) && pnts[0].length > 0) {
30013
-
30014
- let atomName = 'CA';
30015
-
30016
- let prevone = [], nexttwo = [];
30017
- if(isNaN(ic.atoms[prevAtomid].resi)) {
30018
- prevone = [];
30019
- }
30020
- else {
30021
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30022
- ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30023
- }
30024
-
30025
- for (let j = 0; !fill && j < num; ++j) {
30026
- if(bSheetSegment) {
30027
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30028
- }
30029
- else if(bHelixSegment) {
30030
- if(bFullAtom) {
30031
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
30032
- }
30033
- else {
30034
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30035
- }
30036
- }
30037
- }
30038
- if (fill) {
30039
- if(bSheetSegment) {
30040
- let start = 0, end = num - 1;
30041
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30042
- }
30043
- else if(bHelixSegment) {
30044
- if(bFullAtom) {
30045
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30046
- }
30047
- else {
30048
- let start = 0, end = num - 1;
30049
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30050
- }
30051
- }
30052
- }
30053
-
30054
- for (let k = 0; k < num; ++k) pnts[k] = [];
30055
- colors = [];
30056
- pntsCA = [];
30057
- prevCOArray = [];
30058
- bShowArray = [];
30059
- calphaIdArray = [];
30060
- bSheetSegment = false;
30061
- bHelixSegment = false;
30062
- }
30063
-
30064
- currentChain = atom.chain;
30065
- currentResi = atom.resi;
30066
- ss = atom.ss;
30067
- ssend = atom.ssend;
30068
- prevAtomid = atom.serial;
30069
- if(atoms.hasOwnProperty(atom.serial)) prevAtomSelected = atom;
30070
- prevResi = atom.resi;
30071
-
30072
- prevCalphaid = calphaid;
30073
-
30074
- // only update when atom.name === 'O'
30075
- prevCoorCA = currentCA;
30076
- prevCoorO = atom.coord;
30077
- prevColor = currentColor;
30078
- } // end if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA') ) {
30079
- } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
30080
- } // end for
30081
-
30082
- caArray = [];
30083
-
30084
- // ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
30085
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30086
- ic.tubeCls.createTube(atomsAdjust, 'CA', coilWidth, bHighlight);
30087
- pnts = {};
30088
- }
30089
-
30090
- getOneExtraResidue(residueHash) { let ic = this.icn3d, me = ic.icn3dui;
30091
- let atomsAdjust = {};
30092
-
30093
- for(let resid in residueHash) {
30094
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[resid]);
30095
-
30096
- let residNcbi = ic.resid2ncbi[resid];
30097
- let resiNcbi = residNcbi.substr(residNcbi.lastIndexOf('_') + 1);
30098
-
30099
- let nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) + 1);
30100
- let nextResid = ic.ncbi2resid[nextResidNcbi];
30101
-
30102
- if(!nextResid) {
30103
- nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) - 1);
30104
- nextResid = ic.ncbi2resid[nextResidNcbi];
30105
- }
30106
-
30107
- if(nextResid) atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[nextResid]);
30108
- }
30109
-
30110
- return atomsAdjust;
30111
- }
30112
-
30113
- /*
30114
- getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
30115
- let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
30116
- let firstAtom, lastAtom;
30117
- let index = 0, length = Object.keys(atoms).length;
30118
-
30119
- let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
30120
- for(let serial in atoms) {
30121
- currChain = atoms[serial].structure + '_' + atoms[serial].chain;
30122
- currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
30123
- currAtom = atoms[serial];
30124
-
30125
- if(prevChain === undefined) firstAtom = atoms[serial];
30126
-
30127
- if( (currChain !== prevChain && prevChain !== undefined)
30128
- || (currResi !== prevResi && ic.resid2ncbi[currResi] !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) || index === length - 1) {
30129
- if( (currChain !== prevChain && prevChain !== undefined)
30130
- || (currResi !== prevResi && currResi !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) ) {
30131
- lastAtom = prevAtom;
30132
- }
30133
- else if(index === length - 1) {
30134
- lastAtom = currAtom;
30135
- }
30136
-
30137
- // fill the beginning
30138
- let beginResi = firstAtom.resi;
30139
- if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
30140
- for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
30141
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30142
- if(!ic.residues.hasOwnProperty(residueid)) break;
30143
-
30144
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30145
-
30146
- if(atom.ss === firstAtom.ss && atom.ssbegin) {
30147
- beginResi = atom.resi;
30148
- break;
30149
- }
30150
- }
30151
-
30152
- for(let i = beginResi; i < firstAtom.resi; ++i) {
30153
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30154
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30155
- ic.atoms));
30156
- }
30157
- }
30158
-
30159
- // add one extra residue for coils between strands/helix
30160
- if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
30161
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
30162
- if(ic.residues.hasOwnProperty(residueid)) {
30163
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30164
- ic.atoms));
30165
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30166
- }
30167
- }
30168
-
30169
- // fill the end
30170
- let endResi = lastAtom.resi;
30171
- // when a coil connects to a sheet and the last residue of coil is highlighted, the first sheet residue is set as atom.notshow. This residue should not be shown.
30172
-
30173
- if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
30174
-
30175
- let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
30176
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
30177
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30178
- if(!ic.residues.hasOwnProperty(residueid)) break;
30179
-
30180
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30181
-
30182
- if(atom.ss === lastAtom.ss && atom.ssend) {
30183
- endResi = atom.resi;
30184
- break;
30185
- }
30186
- }
30187
-
30188
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
30189
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30190
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30191
- ic.atoms));
30192
- }
30193
- }
30194
-
30195
- // add one extra residue for coils between strands/helix
30196
- if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
30197
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
30198
- if(ic.residues.hasOwnProperty(residueid)) {
30199
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30200
- ic.atoms));
30201
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30202
- }
30203
- }
30204
-
30205
- // reset notshow
30206
- if(lastAtom.notshow) lastAtom.notshow = undefined;
30207
-
30208
- firstAtom = currAtom;
30209
- }
30210
-
30211
- prevChain = currChain;
30212
- prevResi = currResi;
30213
- prevAtom = currAtom;
30214
-
30215
- ++index;
30216
- }
30217
-
30218
- return atomsAdjust;
30219
- }
30220
- */
30221
- }
30222
-
30223
- /**
30224
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
30225
- */
30226
-
30227
- class Strip {
30228
- constructor(icn3d) {
30229
- this.icn3d = icn3d;
30230
- }
30231
-
30232
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30233
- createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
30234
- calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
30235
- if(me.bNode) return;
30236
-
30237
- if (p0.length < 2) return;
30238
- div = div || ic.axisDIV;
30239
-
30240
- // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
30241
- if(pntsCA && ic.bDoublecolor) {
30242
- let bExtendLastRes = false; //true;
30243
-
30244
- let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30245
- pntsCA = pnts_clrs[0];
30246
-
30247
- this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
30248
-
30249
- for(let i = 0, il = prevCOArray.length; i < il; ++i) {
30250
- prevCOArray[i].normalize();
30251
- }
30252
-
30253
- let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30254
- prevCOArray = pnts_clrs2[0];
30255
-
30256
- colors = pnts_clrs[2];
30257
- }
30258
- else {
30259
-
30260
- if(!bNoSmoothen) {
30261
- //var bExtendLastRes = true;
30262
- let bExtendLastRes = false;
30263
- let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30264
- let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30265
- p0 = pnts_clrs0[0];
30266
- p1 = pnts_clrs1[0];
30267
- colors = pnts_clrs0[2];
30268
- }
30269
- if (p0.length < 2) return;
30270
-
30271
- this.setCalphaDrawnCoord(p0, div, calphaIdArray);
30272
- }
30273
-
30274
- if(bHighlight === 1) {
30275
- //mesh = new THREE.Mesh(geo, ic.matShader);
30276
-
30277
- let radius = ic.coilWidth / 2;
30278
- //var radiusSegments = 8;
30279
- let radiusSegments = 4; // save memory
30280
- let closed = false;
30281
-
30282
- if(positions !== undefined) {
30283
- let currPos, prevPos;
30284
- let currP0 = [], currP1 = [];
30285
-
30286
- for(let i = 0, il = p0.length; i < il; ++i) {
30287
- currPos = positions[i];
30288
-
30289
- if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
30290
- // first tube
30291
- let geometry0 = new THREE.TubeGeometry(
30292
- new THREE.CatmullRomCurve3(currP0), // path
30293
- currP0.length, // segments
30294
- radius,
30295
- radiusSegments,
30296
- closed
30297
- );
30298
-
30299
- let mesh = new THREE.Mesh(geometry0, ic.matShader);
30300
- mesh.renderOrder = ic.renderOrderPicking;
30301
- //ic.mdlPicking.add(mesh);
30302
- ic.mdl.add(mesh);
30303
-
30304
- ic.prevHighlightObjects.push(mesh);
29994
+ ic.prevHighlightObjects.push(mesh);
30305
29995
 
30306
29996
  geometry0 = null;
30307
29997
 
@@ -30588,6 +30278,7 @@ class Tube {
30588
30278
  atom.structure + '_' + atom.chain + '_' + (parseInt(atom.resi) - 1).toString();
30589
30279
 
30590
30280
  if (index > 0 && (currentChain !== atom.chain || Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist
30281
+ || (prevAtom.ssbegin) // e.g., https://www.ncbi.nlm.nih.gov/Structure/icn3d/?pdbid=7JO8 where a beta sheet has just two residues
30591
30282
  // || (parseInt(currentResi) + 1 < parseInt(atom.resi) && (Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist2 || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist2 || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist2) && ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]) && ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]).ss == 'helix')
30592
30283
  || (ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 < ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + atom.chain, atom.resi) && (Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist2 || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist2 || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist2))
30593
30284
  ) ) {
@@ -30680,7 +30371,7 @@ class Tube {
30680
30371
  }
30681
30372
 
30682
30373
  // draw all atoms in tubes and assign zero radius when the residue is not coil
30683
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30374
+ // if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30684
30375
 
30685
30376
  //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
30686
30377
  radii.push(radiusFinal);
@@ -30823,224 +30514,770 @@ class Tube {
30823
30514
  radiusFinal = this.getRadius(radius, atom);
30824
30515
  }
30825
30516
 
30826
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30827
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30517
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
30518
+ if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30519
+
30520
+ //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
30521
+ radii.push(radiusFinal);
30522
+
30523
+ colors.push(atom.color);
30524
+ // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
30525
+ if(index === 1) colors[colors.length - 2] = atom.color;
30526
+
30527
+ currentChain = atom.chain;
30528
+ currentResi = atom.resi;
30529
+
30530
+ let scale = 1.2;
30531
+ if(bHighlight === 2 && !atom.ssbegin) {
30532
+ ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
30533
+ }
30534
+
30535
+ ++index;
30536
+
30537
+ prevAtom = atom;
30538
+ }
30539
+ }
30540
+
30541
+ if(bHighlight !== 2) {
30542
+ pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
30543
+ }
30544
+
30545
+ for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
30546
+ let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
30547
+ let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
30548
+ let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
30549
+ let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
30550
+ let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
30551
+
30552
+ this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
30553
+ }
30554
+
30555
+ pnts_colors_radii_prevone_nexttwo = [];
30556
+ }
30557
+ */
30558
+
30559
+ getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
30560
+ let pos = resid.lastIndexOf('_');
30561
+ let resi = resid.substr(pos + 1);
30562
+ let chainid = resid.substr(0, pos);
30563
+
30564
+ let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
30565
+
30566
+ return radiusFinal;
30567
+ };
30568
+
30569
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30570
+ createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
30571
+ if(me.bNode) return;
30572
+
30573
+ if (_pnts.length < 2) return;
30574
+
30575
+ let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
30576
+ let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
30577
+ //var geo = new THREE.Geometry();
30578
+ let geo = new THREE.BufferGeometry();
30579
+ let verticeArray = [], colorArray = [],indexArray = [], color;
30580
+ let offset = 0, offset2 = 0, offset3 = 0;
30581
+
30582
+ let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
30583
+
30584
+ let pnts = pnts_clrs[0];
30585
+ colors = pnts_clrs[2];
30586
+
30587
+ let constRadiius;
30588
+ // a threshold to stop drawing the tube if it's less than this ratio of radius
30589
+ let thresholdRatio = 1; //0.9;
30590
+
30591
+ let prevAxis1 = new THREE.Vector3(), prevAxis2;
30592
+ for (let i = 0, lim = pnts.length; i < lim; ++i) {
30593
+ let r, idx = (i - 1) * axisDivInv;
30594
+
30595
+ if (i === 0) {
30596
+ r = radii[0];
30597
+ if(r > 0) constRadiius = r;
30598
+ }
30599
+ else {
30600
+ if (idx % 1 === 0) {
30601
+ r = radii[idx];
30602
+ if(r > 0) constRadiius = r;
30603
+ }
30604
+ else {
30605
+ let floored = Math.floor(idx);
30606
+ let tmp = idx - floored;
30607
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
30608
+ // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
30609
+ r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
30610
+
30611
+ // a threshold to stop drawing the tube if it's less than this ratio of radius.
30612
+ // The extra bit of tube connects coil with strands or helices
30613
+ if(!bNonCoil) {
30614
+ if(r < thresholdRatio * constRadiius) {
30615
+ r = 0;
30616
+ }
30617
+ // else if(r < constRadiius) {
30618
+ // r *= 0.5; // use small radius for the connection between coild and sheets/helices
30619
+ // }
30620
+ }
30621
+ }
30622
+ }
30623
+ let delta, axis1, axis2;
30624
+ if (i < lim - 1) {
30625
+ delta = pnts[i].clone().sub(pnts[i + 1]);
30626
+ axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
30627
+ axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
30628
+ // let dir = 1, offset = 0;
30629
+ if (prevAxis1.dot(axis1) < 0) {
30630
+ axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
30631
+ }
30632
+ prevAxis1 = axis1; prevAxis2 = axis2;
30633
+ } else {
30634
+ axis1 = prevAxis1; axis2 = prevAxis2;
30635
+ }
30636
+ for (let j = 0; j < circleDiv; ++j) {
30637
+ let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
30638
+ let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
30639
+ verticeArray[offset++] = point.x;
30640
+ verticeArray[offset++] = point.y;
30641
+ verticeArray[offset++] = point.z;
30642
+
30643
+ color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
30644
+ colorArray[offset2++] = color.r;
30645
+ colorArray[offset2++] = color.g;
30646
+ colorArray[offset2++] = color.b;
30647
+ }
30648
+ }
30649
+ let offsetTmp = 0, nComp = 3;
30650
+ for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
30651
+ let reg = 0;
30652
+ //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
30653
+ //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
30654
+ let pos = offsetTmp * nComp;
30655
+ let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30656
+ pos = (offsetTmp + circleDiv) * nComp;
30657
+ let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30658
+ pos = (offsetTmp + circleDiv + 1) * nComp;
30659
+ let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30660
+
30661
+ let r1 = point1.clone().sub(point2).lengthSq();
30662
+ let r2 = point1.clone().sub(point3).lengthSq();
30663
+ if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
30664
+ //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
30665
+ //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
30666
+ //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
30667
+ indexArray[offset3++] = offsetTmp + j;
30668
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30669
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30670
+
30671
+ //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
30672
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30673
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30674
+ indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
30675
+ }
30676
+ offsetTmp += circleDiv;
30677
+ }
30678
+
30679
+ geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
30680
+ geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
30681
+
30682
+ geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
30683
+ //geo.setIndex(indexArray);
30684
+
30685
+ //geo.computeFaceNormals();
30686
+ //geo.computeVertexNormals(false);
30687
+ geo.computeVertexNormals();
30688
+
30689
+ let mesh;
30690
+ if(bHighlight === 2) {
30691
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
30692
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30693
+
30694
+ if(ic.mdl) ic.mdl.add(mesh);
30695
+ }
30696
+ else if(bHighlight === 1) {
30697
+ mesh = new THREE.Mesh(geo, ic.matShader);
30698
+ mesh.renderOrder = ic.renderOrderPicking;
30699
+ //ic.mdlPicking.add(mesh);
30700
+ if(ic.mdl) ic.mdl.add(mesh);
30701
+ }
30702
+ else {
30703
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
30704
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30705
+
30706
+ if(ic.mdl) ic.mdl.add(mesh);
30707
+ }
30708
+
30709
+ if(bHighlight === 1 || bHighlight === 2) {
30710
+ ic.prevHighlightObjects.push(mesh);
30711
+ }
30712
+ else {
30713
+ ic.objects.push(mesh);
30714
+ }
30715
+ }
30716
+
30717
+ getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
30718
+ let radiusFinal = radius;
30719
+ if(radius) {
30720
+ radiusFinal = radius;
30721
+ }
30722
+ else {
30723
+ if(atom.b > 0 && atom.b <= 100) {
30724
+ radiusFinal = atom.b * 0.01;
30725
+ }
30726
+ else if(atom.b > 100) {
30727
+ radiusFinal = 100 * 0.01;
30728
+ }
30729
+ else {
30730
+ radiusFinal = ic.coilWidth;
30731
+ }
30732
+ }
30733
+
30734
+ return radiusFinal;
30735
+ }
30736
+ }
30737
+
30738
+ /**
30739
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
30740
+ */
30741
+
30742
+ class Strand {
30743
+ constructor(icn3d) {
30744
+ this.icn3d = icn3d;
30745
+ }
30746
+
30747
+ // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30748
+ //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
30749
+ //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
30750
+ //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
30751
+ //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
30752
+ //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
30753
+ //with bHighlight=1 and 3D objects with bHighlight=2.
30754
+ createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
30755
+ if(me.bNode) return;
30756
+
30757
+ let bRibbon = fill ? true: false;
30758
+
30759
+ // when highlight, the input atoms may only include part of sheet or helix
30760
+ // include the whole sheet or helix when highlighting
30761
+ let atomsAdjust = {};
30762
+
30763
+ //if( (bHighlight === 1 || bHighlight === 2) && !ic.bAllAtoms) {
30764
+ //if( !ic.bAllAtoms) {
30765
+ if( Object.keys(atoms).length < Object.keys(ic.atoms).length) {
30766
+ atomsAdjust = this.getSSExpandedAtoms(atoms);
30767
+ }
30768
+ else {
30769
+ atomsAdjust = atoms;
30770
+ }
30771
+
30772
+ if(bHighlight === 2) {
30773
+ if(fill) {
30774
+ fill = false;
30775
+ num = null;
30776
+ div = null;
30777
+ coilWidth = null;
30778
+ helixSheetWidth = null;
30779
+ thickness = undefined;
30780
+ }
30781
+ else {
30782
+ fill = true;
30783
+ num = 2;
30784
+ div = undefined;
30785
+ coilWidth = undefined;
30786
+ helixSheetWidth = undefined;
30787
+ thickness = ic.ribbonthickness;
30788
+ }
30789
+ }
30790
+
30791
+ num = num || ic.strandDIV;
30792
+ div = div || ic.axisDIV;
30793
+ coilWidth = coilWidth || ic.coilWidth;
30794
+ doNotSmoothen = doNotSmoothen || false;
30795
+ helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
30796
+ let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
30797
+ let pntsCA = [];
30798
+ let prevCOArray = [];
30799
+ let bShowArray = [];
30800
+ let calphaIdArray = []; // used to store one of the final positions drawn in 3D
30801
+ let colors = [];
30802
+ let currentChain, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
30803
+ let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevResi = null, calphaid = null, prevCalphaid = null;
30804
+ let strandWidth, bSheetSegment = false, bHelixSegment = false;
30805
+ let atom, tubeAtoms = {};
30806
+
30807
+ // test the first 30 atoms to see whether only C-alpha is available
30808
+ ic.bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atomsAdjust); //, 'CA');
30809
+
30810
+ // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
30811
+ let residueHash = {};
30812
+ for(let i in atomsAdjust) {
30813
+ let atom = atomsAdjust[i];
30814
+
30815
+ let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
30816
+ residueHash[residueid] = 1;
30817
+ }
30818
+ let totalResidueCount = Object.keys(residueHash).length;
30819
+
30820
+ let drawnResidueCount = 0;
30821
+
30822
+ let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
30823
+
30824
+ let caArray = []; // record all C-alpha atoms to predict the helix
30825
+
30826
+ for (let i in atomsAdjust) {
30827
+ atom = atomsAdjust[i];
30828
+ if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
30829
+ // "CA" has to appear before "O"
30830
+
30831
+ if (atom.name === 'CA') {
30832
+ if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) {
30833
+ tubeAtoms[i] = atom;
30834
+ }
30835
+
30836
+ currentCA = atom.coord;
30837
+ currentColor = atom.color;
30838
+ calphaid = atom.serial;
30839
+
30840
+ caArray.push(atom.serial);
30841
+ }
30842
+
30843
+ if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA')) {
30844
+ if(currentCA === null || currentCA === undefined) {
30845
+ currentCA = atom.coord;
30846
+ currentColor = atom.color;
30847
+ calphaid = atom.serial;
30848
+ }
30849
+
30850
+ if(atom.name === 'O') {
30851
+ currentO = atom.coord;
30852
+ }
30853
+ // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
30854
+ let bSameChain = true;
30855
+ // if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) {
30856
+ if (currentChain !== atom.chain) {
30857
+ bSameChain = false;
30858
+ }
30859
+
30860
+ if(atom.ssend && atom.ss === 'sheet') {
30861
+ bSheetSegment = true;
30862
+ }
30863
+ else if(atom.ssend && atom.ss === 'helix') {
30864
+ bHelixSegment = true;
30865
+ }
30866
+
30867
+ // assign the previous residue
30868
+ if(prevCoorO) {
30869
+ if(bHighlight === 1 || bHighlight === 2) {
30870
+ colors.push(ic.hColor);
30871
+ }
30872
+ else {
30873
+ colors.push(prevColor);
30874
+ }
30875
+
30876
+ if(ss !== 'coil' && atom.ss === 'coil') {
30877
+ strandWidth = coilWidth;
30878
+ }
30879
+ else if(ssend && atom.ssbegin) { // a transition between two ss
30880
+ strandWidth = coilWidth;
30881
+ }
30882
+ else {
30883
+ strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
30884
+ }
30885
+
30886
+ let O, oldCA, resSpan = 4;
30887
+ if(atom.name === 'O') {
30888
+ O = prevCoorO.clone();
30889
+ if(prevCoorCA !== null && prevCoorCA !== undefined) {
30890
+ O.sub(prevCoorCA);
30891
+ }
30892
+ else {
30893
+ prevCoorCA = prevCoorO.clone();
30894
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30895
+ O = prevCoorCA.clone();
30896
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
30897
+ //O.sub(oldCA);
30898
+ oldCA.sub(O);
30899
+ }
30900
+ else {
30901
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30902
+ }
30903
+ }
30904
+ }
30905
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
30906
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30907
+ O = prevCoorCA.clone();
30908
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
30909
+ //O.sub(oldCA);
30910
+ oldCA.sub(O);
30911
+ }
30912
+ else {
30913
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30914
+ }
30915
+ }
30916
+
30917
+ O.normalize(); // can be omitted for performance
30918
+ O.multiplyScalar(strandWidth);
30919
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
30920
+ prevCO = O;
30921
+
30922
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
30923
+ let delta = -1 + numM1Inv2 * j;
30924
+ let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
30925
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
30926
+ pnts[j].push(v);
30927
+ }
30928
+
30929
+ pntsCA.push(prevCoorCA);
30930
+ prevCOArray.push(prevCO);
30931
+
30932
+ if(atoms.hasOwnProperty(prevAtomid)) {
30933
+ bShowArray.push(prevResi);
30934
+ calphaIdArray.push(prevCalphaid);
30935
+ }
30936
+ else {
30937
+ bShowArray.push(0);
30938
+ calphaIdArray.push(0);
30939
+ }
30940
+
30941
+ ++drawnResidueCount;
30942
+ }
30943
+
30944
+ let maxDist = 6.0;
30945
+ let bBrokenSs = (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
30946
+ // The following code didn't work to select one residue
30947
+ // let bBrokenSs = !atoms.hasOwnProperty(atom.serial) || (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
30948
+
30949
+ // if(bBrokenSs && atom.ss === 'sheet') {
30950
+ // bSheetSegment = true;
30951
+ // }
30952
+ // else if(bBrokenSs && atom.ss === 'helix') {
30953
+ // bHelixSegment = true;
30954
+ // }
30955
+
30956
+ if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
30957
+ let atomName = 'CA';
30958
+
30959
+ let prevone = [], nexttwo = [];
30960
+
30961
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
30962
+ prevone = [];
30963
+ }
30964
+ else {
30965
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30966
+ let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30967
+ prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
30968
+ }
30969
+
30970
+ if(!isNaN(ic.atoms[prevAtomid].resi)) {
30971
+ let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
30972
+ let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
30973
+ if(nextoneCoord !== undefined) {
30974
+ nexttwo.push(nextoneCoord);
30975
+ }
30976
+
30977
+ let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
30978
+ let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
30979
+ if(nexttwoCoord !== undefined) {
30980
+ nexttwo.push(nexttwoCoord);
30981
+ }
30982
+ }
30983
+
30984
+ if(!bBrokenSs) { // include the current residue
30985
+ // assign the current joint residue to the previous segment
30986
+ if(bHighlight === 1 || bHighlight === 2) {
30987
+ colors.push(ic.hColor);
30988
+ }
30989
+ else {
30990
+ //colors.push(atom.color);
30991
+ colors.push(prevColor);
30992
+ }
30993
+
30994
+ if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
30995
+ strandWidth = 0; // make the arrow end sharp
30996
+ }
30997
+ else if(ss === 'coil' && atom.ssbegin) {
30998
+ strandWidth = coilWidth;
30999
+ }
31000
+ else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
31001
+ strandWidth = coilWidth;
31002
+ }
31003
+ else { // use the ss from the previous residue
31004
+ strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
31005
+ }
31006
+
31007
+ let O, oldCA, resSpan = 4;
31008
+ if(atom.name === 'O') {
31009
+ O = currentO.clone();
31010
+ O.sub(currentCA);
31011
+ }
31012
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
31013
+ if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
31014
+ O = currentCA.clone();
31015
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
31016
+ //O.sub(oldCA);
31017
+ oldCA.sub(O);
31018
+ }
31019
+ else {
31020
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
31021
+ }
31022
+ }
31023
+
31024
+ O.normalize(); // can be omitted for performance
31025
+ O.multiplyScalar(strandWidth);
31026
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
31027
+ prevCO = O;
31028
+
31029
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
31030
+ let delta = -1 + numM1Inv2 * j;
31031
+ let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
31032
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
31033
+ pnts[j].push(v);
31034
+ }
31035
+
31036
+ atomid = atom.serial;
31037
+
31038
+ pntsCA.push(currentCA);
31039
+ prevCOArray.push(prevCO);
31040
+
31041
+ // when a coil connects to a sheet and the last residue of coild is highlighted, the first sheet residue is set as atom.highlightStyle. This residue should not be shown.
31042
+ //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
31043
+ if(atoms.hasOwnProperty(atomid)) {
31044
+ bShowArray.push(atom.resi);
31045
+ calphaIdArray.push(calphaid);
31046
+ }
31047
+ else {
31048
+ bShowArray.push(0);
31049
+ calphaIdArray.push(0);
31050
+ }
31051
+ }
31052
+
31053
+ // draw the current segment
31054
+ for (let j = 0; !fill && j < num; ++j) {
31055
+ if(bSheetSegment) {
31056
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31057
+ }
31058
+ else if(bHelixSegment) {
31059
+ if(bFullAtom) {
31060
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
31061
+ }
31062
+ else {
31063
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31064
+ }
31065
+ }
31066
+ }
31067
+ if (fill) {
31068
+ if(bSheetSegment) {
31069
+ let start = 0, end = num - 1;
31070
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31071
+ }
31072
+ else if(bHelixSegment) {
31073
+ if(bFullAtom) {
31074
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
31075
+ }
31076
+ else {
31077
+ let start = 0, end = num - 1;
31078
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31079
+ }
31080
+ }
31081
+ else {
31082
+ if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
31083
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
31084
+ }
31085
+ }
31086
+ }
31087
+ for (let k = 0; k < num; ++k) pnts[k] = [];
31088
+
31089
+ colors = [];
31090
+ pntsCA = [];
31091
+ prevCOArray = [];
31092
+ bShowArray = [];
31093
+ calphaIdArray = [];
31094
+ bSheetSegment = false;
31095
+ bHelixSegment = false;
31096
+ } // end if (atom.ssbegin || atom.ssend)
31097
+
31098
+ // end of a chain
31099
+ // if ((currentChain !== atom.chain || currentResi + 1 !== atom.resi) && pnts[0].length > 0) {
31100
+ if ((currentChain !== atom.chain) && pnts[0].length > 0) {
30828
31101
 
30829
- //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
30830
- radii.push(radiusFinal);
31102
+ let atomName = 'CA';
30831
31103
 
30832
- colors.push(atom.color);
30833
- // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
30834
- if(index === 1) colors[colors.length - 2] = atom.color;
31104
+ let prevone = [], nexttwo = [];
31105
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
31106
+ prevone = [];
31107
+ }
31108
+ else {
31109
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
31110
+ ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
31111
+ }
30835
31112
 
30836
- currentChain = atom.chain;
30837
- currentResi = atom.resi;
31113
+ for (let j = 0; !fill && j < num; ++j) {
31114
+ if(bSheetSegment) {
31115
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31116
+ }
31117
+ else if(bHelixSegment) {
31118
+ if(bFullAtom) {
31119
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
31120
+ }
31121
+ else {
31122
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31123
+ }
31124
+ }
31125
+ }
31126
+ if (fill) {
31127
+ if(bSheetSegment) {
31128
+ let start = 0, end = num - 1;
31129
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31130
+ }
31131
+ else if(bHelixSegment) {
31132
+ if(bFullAtom) {
31133
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
31134
+ }
31135
+ else {
31136
+ let start = 0, end = num - 1;
31137
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31138
+ }
31139
+ }
31140
+ }
30838
31141
 
30839
- let scale = 1.2;
30840
- if(bHighlight === 2 && !atom.ssbegin) {
30841
- ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
31142
+ for (let k = 0; k < num; ++k) pnts[k] = [];
31143
+ colors = [];
31144
+ pntsCA = [];
31145
+ prevCOArray = [];
31146
+ bShowArray = [];
31147
+ calphaIdArray = [];
31148
+ bSheetSegment = false;
31149
+ bHelixSegment = false;
30842
31150
  }
30843
31151
 
30844
- ++index;
31152
+ currentChain = atom.chain;
31153
+ ss = atom.ss;
31154
+ ssend = atom.ssend;
31155
+ prevAtomid = atom.serial;
31156
+ prevResi = atom.resi;
30845
31157
 
30846
- prevAtom = atom;
30847
- }
30848
- }
31158
+ prevCalphaid = calphaid;
30849
31159
 
30850
- if(bHighlight !== 2) {
30851
- pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
30852
- }
31160
+ // only update when atom.name === 'O'
31161
+ prevCoorCA = currentCA;
31162
+ prevCoorO = atom.coord;
31163
+ prevColor = currentColor;
31164
+ } // end if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA') ) {
31165
+ } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
31166
+ } // end for
30853
31167
 
30854
- for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
30855
- let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
30856
- let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
30857
- let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
30858
- let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
30859
- let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
31168
+ caArray = [];
30860
31169
 
30861
- this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
30862
- }
31170
+ ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
30863
31171
 
30864
- pnts_colors_radii_prevone_nexttwo = [];
31172
+ tubeAtoms = {};
31173
+ pnts = {};
30865
31174
  }
30866
- */
30867
-
30868
- getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
30869
- let pos = resid.lastIndexOf('_');
30870
- let resi = resid.substr(pos + 1);
30871
- let chainid = resid.substr(0, pos);
30872
-
30873
- let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
30874
-
30875
- return radiusFinal;
30876
- };
30877
-
30878
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30879
- createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
30880
- if(me.bNode) return;
30881
-
30882
- if (_pnts.length < 2) return;
30883
31175
 
30884
- let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
30885
- let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
30886
- //var geo = new THREE.Geometry();
30887
- let geo = new THREE.BufferGeometry();
30888
- let verticeArray = [], colorArray = [],indexArray = [], color;
30889
- let offset = 0, offset2 = 0, offset3 = 0;
31176
+ getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
31177
+ let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
31178
+ let firstAtom, lastAtom;
31179
+ let index = 0, length = Object.keys(atoms).length;
30890
31180
 
30891
- let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
31181
+ let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
31182
+ for(let serial in atoms) {
31183
+ currChain = atoms[serial].structure + '_' + atoms[serial].chain;
31184
+ currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
31185
+ currAtom = atoms[serial];
30892
31186
 
30893
- let pnts = pnts_clrs[0];
30894
- colors = pnts_clrs[2];
31187
+ if(prevChain === undefined) firstAtom = atoms[serial];
30895
31188
 
30896
- let constRadiius;
30897
- // a threshold to stop drawing the tube if it's less than this ratio of radius
30898
- let thresholdRatio = 1; //0.9;
31189
+ if( (currChain !== prevChain && prevChain !== undefined)
31190
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) || index === length - 1) {
31191
+ if( (currChain !== prevChain && prevChain !== undefined)
31192
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) ) {
31193
+ lastAtom = prevAtom;
31194
+ }
31195
+ else if(index === length - 1) {
31196
+ lastAtom = currAtom;
31197
+ }
30899
31198
 
30900
- let prevAxis1 = new THREE.Vector3(), prevAxis2;
30901
- for (let i = 0, lim = pnts.length; i < lim; ++i) {
30902
- let r, idx = (i - 1) * axisDivInv;
31199
+ // fill the beginning
31200
+ let beginResi = firstAtom.resi;
31201
+ if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
31202
+ for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
31203
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
31204
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30903
31205
 
30904
- if (i === 0) {
30905
- r = radii[0];
30906
- if(r > 0) constRadiius = r;
30907
- }
30908
- else {
30909
- if (idx % 1 === 0) {
30910
- r = radii[idx];
30911
- if(r > 0) constRadiius = r;
30912
- }
30913
- else {
30914
- let floored = Math.floor(idx);
30915
- let tmp = idx - floored;
30916
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30917
- // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
30918
- r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
31206
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30919
31207
 
30920
- // a threshold to stop drawing the tube if it's less than this ratio of radius.
30921
- // The extra bit of tube connects coil with strands or helices
30922
- if(!bNonCoil) {
30923
- if(r < thresholdRatio * constRadiius) {
30924
- r = 0;
30925
- }
30926
- // else if(r < constRadiius) {
30927
- // r *= 0.5; // use small radius for the connection between coild and sheets/helices
30928
- // }
31208
+ if(atom.ss === firstAtom.ss && atom.ssbegin) {
31209
+ beginResi = atom.resi;
31210
+ break;
30929
31211
  }
30930
31212
  }
30931
- }
30932
- let delta, axis1, axis2;
30933
- if (i < lim - 1) {
30934
- delta = pnts[i].clone().sub(pnts[i + 1]);
30935
- axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
30936
- axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
30937
- // let dir = 1, offset = 0;
30938
- if (prevAxis1.dot(axis1) < 0) {
30939
- axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
31213
+
31214
+ for(let i = beginResi; i < firstAtom.resi; ++i) {
31215
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
31216
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31217
+ ic.atoms));
30940
31218
  }
30941
- prevAxis1 = axis1; prevAxis2 = axis2;
30942
- } else {
30943
- axis1 = prevAxis1; axis2 = prevAxis2;
30944
31219
  }
30945
- for (let j = 0; j < circleDiv; ++j) {
30946
- let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
30947
- let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
30948
- verticeArray[offset++] = point.x;
30949
- verticeArray[offset++] = point.y;
30950
- verticeArray[offset++] = point.z;
30951
31220
 
30952
- color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
30953
- colorArray[offset2++] = color.r;
30954
- colorArray[offset2++] = color.g;
30955
- colorArray[offset2++] = color.b;
31221
+ // add one extra residue for coils between strands/helix
31222
+ if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
31223
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
31224
+ if(ic.residues.hasOwnProperty(residueid)) {
31225
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31226
+ ic.atoms));
31227
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
31228
+ }
30956
31229
  }
30957
- }
30958
- let offsetTmp = 0, nComp = 3;
30959
- for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
30960
- let reg = 0;
30961
- //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
30962
- //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
30963
- let pos = offsetTmp * nComp;
30964
- let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30965
- pos = (offsetTmp + circleDiv) * nComp;
30966
- let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30967
- pos = (offsetTmp + circleDiv + 1) * nComp;
30968
- let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30969
31230
 
30970
- let r1 = point1.clone().sub(point2).lengthSq();
30971
- let r2 = point1.clone().sub(point3).lengthSq();
30972
- if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
30973
- //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
30974
- //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
30975
- //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
30976
- indexArray[offset3++] = offsetTmp + j;
30977
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30978
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
31231
+ // fill the end
31232
+ let endResi = lastAtom.resi;
31233
+ // when a coil connects to a sheet and the last residue of coil is highlighted, the first sheet residue is set as atom.notshow. This residue should not be shown.
30979
31234
 
30980
- //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
30981
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30982
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30983
- indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
30984
- }
30985
- offsetTmp += circleDiv;
30986
- }
31235
+ if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
30987
31236
 
30988
- geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
30989
- geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
31237
+ let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
31238
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
31239
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
31240
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30990
31241
 
30991
- geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
30992
- //geo.setIndex(indexArray);
31242
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30993
31243
 
30994
- //geo.computeFaceNormals();
30995
- //geo.computeVertexNormals(false);
30996
- geo.computeVertexNormals();
31244
+ if(atom.ss === lastAtom.ss && atom.ssend) {
31245
+ endResi = atom.resi;
31246
+ break;
31247
+ }
31248
+ }
30997
31249
 
30998
- let mesh;
30999
- if(bHighlight === 2) {
31000
- //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
31001
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
31250
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
31251
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
31252
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31253
+ ic.atoms));
31254
+ }
31255
+ }
31002
31256
 
31003
- if(ic.mdl) ic.mdl.add(mesh);
31004
- }
31005
- else if(bHighlight === 1) {
31006
- mesh = new THREE.Mesh(geo, ic.matShader);
31007
- mesh.renderOrder = ic.renderOrderPicking;
31008
- //ic.mdlPicking.add(mesh);
31009
- if(ic.mdl) ic.mdl.add(mesh);
31010
- }
31011
- else {
31012
- //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
31013
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
31257
+ // add one extra residue for coils between strands/helix
31258
+ if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
31259
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
31260
+ if(ic.residues.hasOwnProperty(residueid)) {
31261
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31262
+ ic.atoms));
31263
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
31264
+ }
31265
+ }
31014
31266
 
31015
- if(ic.mdl) ic.mdl.add(mesh);
31016
- }
31267
+ // reset notshow
31268
+ if(lastAtom.notshow) lastAtom.notshow = undefined;
31017
31269
 
31018
- if(bHighlight === 1 || bHighlight === 2) {
31019
- ic.prevHighlightObjects.push(mesh);
31020
- }
31021
- else {
31022
- ic.objects.push(mesh);
31023
- }
31024
- }
31270
+ firstAtom = currAtom;
31271
+ }
31025
31272
 
31026
- getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
31027
- let radiusFinal = radius;
31028
- if(radius) {
31029
- radiusFinal = radius;
31030
- }
31031
- else {
31032
- if(atom.b > 0 && atom.b <= 100) {
31033
- radiusFinal = atom.b * 0.01;
31034
- }
31035
- else if(atom.b > 100) {
31036
- radiusFinal = 100 * 0.01;
31037
- }
31038
- else {
31039
- radiusFinal = ic.coilWidth;
31040
- }
31273
+ prevChain = currChain;
31274
+ prevResi = currResi;
31275
+ prevAtom = currAtom;
31276
+
31277
+ ++index;
31041
31278
  }
31042
31279
 
31043
- return radiusFinal;
31280
+ return atomsAdjust;
31044
31281
  }
31045
31282
  }
31046
31283
 
@@ -38058,6 +38295,11 @@ class Alternate {
38058
38295
  ic.directionalLight.position.copy(ic.lightPos.clone().applyQuaternion( quaternion ).normalize());
38059
38296
  ic.directionalLight2.position.copy(ic.lightPos2.clone().applyQuaternion( quaternion ).normalize());
38060
38297
  ic.directionalLight3.position.copy(ic.lightPos3.clone().applyQuaternion( quaternion ).normalize());
38298
+
38299
+ // adjust the light according to the position of camera
38300
+ ic.directionalLight.applyMatrix4(cam.matrixWorld);
38301
+ ic.directionalLight2.applyMatrix4(cam.matrixWorld);
38302
+ ic.directionalLight3.applyMatrix4(cam.matrixWorld);
38061
38303
  }
38062
38304
 
38063
38305
  if(!ic.bVr) ic.renderer.setPixelRatio( window.devicePixelRatio ); // r71
@@ -48689,6 +48931,13 @@ class ShowAnno {
48689
48931
  } // align seq to structure
48690
48932
  }
48691
48933
  //ic.bAnnoShown = true;
48934
+
48935
+ if($("#" + ic.pre + "anno_ig").length && $("#" + ic.pre + "anno_ig")[0].checked) {
48936
+ ic.bRunRefnumAgain = true;
48937
+ await ic.annotationCls.setAnnoTabIg();
48938
+
48939
+ ic.bRunRefnumAgain = false;
48940
+ }
48692
48941
  }
48693
48942
 
48694
48943
  async showAnnoSeqData(nucleotide_chainid, chemical_chainid, chemical_set) { let ic = this.icn3d, me = ic.icn3dui;
@@ -54423,6 +54672,7 @@ class AlignParser {
54423
54672
 
54424
54673
  async downloadAlignmentPart2(data, seqalign, chainresiCalphaHash2) { let ic = this.icn3d, me = ic.icn3dui;
54425
54674
  //ic.init();
54675
+
54426
54676
  ic.loadAtomDataCls.loadAtomDataIn(data, undefined, 'align', seqalign);
54427
54677
 
54428
54678
  if(me.cfg.align === undefined && Object.keys(ic.structures).length == 1) {
@@ -54466,20 +54716,21 @@ class AlignParser {
54466
54716
  async loadOpmDataForAlign(data, seqalign, mmdbidArray) { let ic = this.icn3d, me = ic.icn3dui;
54467
54717
  let thisClass = this;
54468
54718
 
54469
- let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
54470
- let prms1 = me.getAjaxPromise(url, 'text');
54471
- let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
54472
- let prms2 = me.getAjaxPromise(url2, 'text');
54473
-
54474
- let allPromise = Promise.allSettled([prms1, prms2]);
54475
54719
  try {
54720
+ let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
54721
+ let prms1 = me.getAjaxPromise(url, 'text');
54722
+ let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
54723
+ let prms2 = me.getAjaxPromise(url2, 'text');
54724
+
54725
+ let allPromise = Promise.allSettled([prms1, prms2]);
54726
+
54476
54727
  let dataArray = await allPromise;
54477
-
54728
+
54478
54729
  let bFound = false;
54479
54730
  for(let i = 0, il = dataArray.length; i < il; ++i) {
54480
- //let opmdata = (me.bNode) ? dataArray[i] : dataArray[i].value;
54481
- let opmdata = dataArray[i].value;
54731
+ // if(dataArray[i].status == 'rejected') continue;
54482
54732
 
54733
+ let opmdata = dataArray[i].value;
54483
54734
  if(!opmdata) continue;
54484
54735
 
54485
54736
  ic.selectedPdbid = mmdbidArray[i];
@@ -54495,6 +54746,7 @@ class AlignParser {
54495
54746
  $("#" + ic.pre + "intra_mem_z").val(-ic.halfBilayerSize);
54496
54747
 
54497
54748
  ic.init(); // remove all previously loaded data
54749
+
54498
54750
  await thisClass.downloadAlignmentPart2(data, seqalign, chainresiCalphaHash);
54499
54751
 
54500
54752
  bFound = true;
@@ -59070,7 +59322,7 @@ class SdfParser {
59070
59322
  let sdfStr = await me.getAjaxPromise(urlSmiles, 'text');
59071
59323
 
59072
59324
  ic.init();
59073
- ic.bInputfile = true;
59325
+ //ic.bInputfile = true;
59074
59326
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + sdfStr : sdfStr;
59075
59327
  ic.InputfileType = 'sdf';
59076
59328
  await ic.sdfParserCls.loadSdfData(sdfStr);
@@ -61879,6 +62131,7 @@ class ParserUtils {
61879
62131
  ic.selectionCls.oneStructurePerWindow(); // for alignment
61880
62132
  ic.drawCls.draw();
61881
62133
  }
62134
+
61882
62135
  if(ic.bOpm) {
61883
62136
  let axis = new THREE.Vector3(1,0,0);
61884
62137
  let angle = -0.5 * Math.PI;
@@ -64416,7 +64669,8 @@ class LoadPDB {
64416
64669
 
64417
64670
  // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
64418
64671
  //This PDB parser feeds the viewer with the content of a PDB file, pdbData.
64419
- async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
64672
+ // async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
64673
+ loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
64420
64674
  let hAtoms = {};
64421
64675
 
64422
64676
  let bNMR = false;
@@ -69537,17 +69791,15 @@ class SelectCollections {
69537
69791
  }
69538
69792
 
69539
69793
  //Set the menu of defined sets with an array of defined names "commandnameArray".
69540
- setAtomMenu(nameArray) {
69794
+ setAtomMenu(collection) {
69541
69795
  let ic = this.icn3d;
69542
69796
  ic.icn3dui;
69543
69797
  let html = "";
69544
- //for(let i in ic.defNames2Atoms) {
69545
- for (let i = 0, il = nameArray.length; i < il; ++i) {
69546
- let name = nameArray[i][0];
69547
- let title = nameArray[i][1];
69548
- let description = nameArray[i][2];
69549
-
69798
+
69799
+ Object.entries(collection).forEach(([name, structure], index) => {
69550
69800
  let atomHash;
69801
+ let [id, title, description, commands, pdb] = structure;
69802
+
69551
69803
  if (
69552
69804
  ic.defNames2Atoms !== undefined &&
69553
69805
  ic.defNames2Atoms.hasOwnProperty(name)
@@ -69568,12 +69820,12 @@ class SelectCollections {
69568
69820
  }
69569
69821
  }
69570
69822
 
69571
- if (i == 0) {
69572
- html += "<option value='" + nameArray[0][0] + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
69573
- } else {
69823
+ if (index === 0) {
69824
+ html += "<option value='" + name + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
69825
+ } else {
69574
69826
  html += "<option value='" + name + "' data-description='" + description + "'>" + title + "</option>";
69575
- }
69576
- }
69827
+ }
69828
+ });
69577
69829
 
69578
69830
  return html;
69579
69831
  }
@@ -69627,7 +69879,7 @@ class SelectCollections {
69627
69879
  let nameArray = $(this).val();
69628
69880
  let nameStructure = $(this).find("option:selected").text();
69629
69881
  let selectedIndices = Array.from(this.selectedOptions).map(option => option.index);
69630
- let selectedIndicesMap = nameArray.reduce((map, name, i) => {
69882
+ nameArray.reduce((map, name, i) => {
69631
69883
  map[name] = selectedIndices[i];
69632
69884
  return map;
69633
69885
  }, {});
@@ -69664,13 +69916,13 @@ class SelectCollections {
69664
69916
  if (Object.keys(ic.structures).length == 0) {
69665
69917
  bAppend = false;
69666
69918
  }
69667
- await ic.pdbParserCls.loadPdbData(ic.pdbCollection[selectedIndicesMap[name]].join('\n'), undefined, undefined, bAppend);
69919
+ await ic.pdbParserCls.loadPdbData(ic.pdbCollection[name].join('\n'), undefined, undefined, bAppend);
69668
69920
  } else {
69669
69921
  await ic.chainalignParserCls.downloadMmdbAf(name, undefined, undefined, bNoDuplicate);
69670
69922
  }
69671
69923
  }
69672
69924
 
69673
- await loadStructure(collection[selectedIndicesMap[name]][4]).then(() => {
69925
+ await loadStructure(collection[name][4]).then(() => {
69674
69926
  ic.allData['all'] = {
69675
69927
  'atoms': ic.atoms,
69676
69928
  'proteins': ic.proteins,
@@ -69730,9 +69982,9 @@ class SelectCollections {
69730
69982
 
69731
69983
  ic.molTitle = ic.allData[name]['title'];
69732
69984
 
69733
- if (collection[selectedIndicesMap[name]][3] !== undefined && collection[selectedIndicesMap[name]][3].length > 0) {
69985
+ if (collection[name][3] !== undefined && collection[name][3].length > 0) {
69734
69986
  if (ic.allData[name]['commands'] == undefined) {
69735
- let commands = collection[selectedIndicesMap[name]][3];
69987
+ let commands = collection[name][3];
69736
69988
  ic.allData[name]['commands'] = commands;
69737
69989
  }
69738
69990
  }
@@ -70001,7 +70253,9 @@ class LoadScript {
70001
70253
  }
70002
70254
  else if(command.indexOf('view interactions') == 0 && me.cfg.align !== undefined) { // the command may have "|||{"factor"...
70003
70255
  await thisClass.applyCommandViewinteraction(strArray[0].trim());
70004
-
70256
+ }
70257
+ else if(command.indexOf('view 2d depiction') == 0) { // the command may have "|||{"factor"...
70258
+ await ic.ligplotCls.drawLigplot(ic.atoms, true);
70005
70259
  }
70006
70260
  else if(command.indexOf('symmetry') == 0) {
70007
70261
  ic.bAxisOnly = false;
@@ -70211,6 +70465,9 @@ class LoadScript {
70211
70465
  else if(lastCommand.indexOf('view interactions') == 0 && me.cfg.align !== undefined) {
70212
70466
  await thisClass.applyCommandViewinteraction(lastCommand);
70213
70467
  }
70468
+ else if(lastCommand.indexOf('view 2d depiction') == 0) {
70469
+ await ic.ligplotCls.drawLigplot(ic.atoms, true);
70470
+ }
70214
70471
  else if(lastCommand.indexOf('symmetry') == 0) {
70215
70472
  let title = lastCommand.substr(lastCommand.indexOf(' ') + 1);
70216
70473
  ic.symmetrytitle =(title === 'none') ? undefined : title;
@@ -72369,155 +72626,6 @@ class Resid2spec {
72369
72626
  }
72370
72627
  }
72371
72628
 
72372
- /**
72373
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
72374
- */
72375
-
72376
- class FirstAtomObj {
72377
- constructor(icn3d) {
72378
- this.icn3d = icn3d;
72379
- }
72380
-
72381
- //Return the first atom in the atom hash, which has the atom serial number as the key.
72382
- getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72383
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72384
- return undefined;
72385
- }
72386
-
72387
- let atomKeys = Object.keys(atomsHash);
72388
- let firstIndex = atomKeys[0];
72389
-
72390
- return ic.atoms[firstIndex];
72391
- }
72392
-
72393
- // n is the position of the selected atom
72394
- getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
72395
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72396
- return undefined;
72397
- }
72398
-
72399
- let atomKeys = Object.keys(atomsHash);
72400
- let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
72401
-
72402
- return ic.atoms[middleIndex];
72403
- }
72404
-
72405
- getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72406
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72407
- return undefined;
72408
- }
72409
-
72410
- let firstIndex;
72411
-
72412
- for(let i in atomsHash) {
72413
- if(ic.atoms[i].name == 'CA') {
72414
- firstIndex = i;
72415
- break;
72416
- }
72417
- }
72418
-
72419
- if(!firstIndex) {
72420
- for(let i in atomsHash) {
72421
- if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
72422
- firstIndex = i;
72423
- break;
72424
- }
72425
- }
72426
- }
72427
-
72428
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
72429
- }
72430
-
72431
- getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
72432
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72433
- return ic.atoms[0];
72434
- }
72435
-
72436
- let firstIndex;
72437
-
72438
- for(let i in atomsHash) {
72439
- if(ic.atoms[i].name == atomName) {
72440
- firstIndex = i;
72441
- break;
72442
- }
72443
- }
72444
-
72445
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
72446
- }
72447
-
72448
- //Return the last atom in the atom hash, which has the atom serial number as the key.
72449
- getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72450
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72451
- return ic.atoms[0];
72452
- }
72453
-
72454
- let atomKeys = Object.keys(atomsHash);
72455
- let lastIndex = atomKeys[atomKeys.length - 1];
72456
-
72457
- return ic.atoms[lastIndex];
72458
- }
72459
-
72460
- //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
72461
- getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72462
- let residuesHash = {};
72463
- for(let i in atomsHash) {
72464
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
72465
- residuesHash[residueid] = 1;
72466
- }
72467
-
72468
- return residuesHash;
72469
- }
72470
-
72471
- getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72472
- let residuesHash = {};
72473
- for(let i in atomsHash) {
72474
- if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
72475
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
72476
- //residuesHash[residueid] = 1;
72477
- residuesHash[residueid] = ic.atoms[i].resn;
72478
- }
72479
- }
72480
-
72481
- return residuesHash;
72482
- }
72483
-
72484
- //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
72485
- getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72486
- let chainsHash = {};
72487
- for(let i in atomsHash) {
72488
- let atom = ic.atoms[i];
72489
- let chainid = atom.structure + "_" + atom.chain;
72490
-
72491
- chainsHash[chainid] = 1;
72492
- }
72493
-
72494
- return chainsHash;
72495
- }
72496
-
72497
- getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
72498
- if(ic.residues.hasOwnProperty(resid)) {
72499
- for(let i in ic.residues[resid]) {
72500
- if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
72501
- return ic.atoms[i];
72502
- }
72503
- }
72504
- }
72505
-
72506
- return undefined;
72507
- }
72508
-
72509
- getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
72510
- let atom = this.getAtomFromResi(resid, atomName);
72511
- if(atom !== undefined) {
72512
- let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
72513
-
72514
- return coord;
72515
- }
72516
-
72517
- return undefined;
72518
- }
72519
- }
72520
-
72521
72629
  /**
72522
72630
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
72523
72631
  */
@@ -72578,9 +72686,9 @@ class Delphi {
72578
72686
  let pdbstr = '';
72579
72687
  /// pdbstr += ic.saveFileCls.getPDBHeader();
72580
72688
 
72581
- let bMergeIntoOne = true;
72582
- pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
72583
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
72689
+ let bMergeIntoOne = true, bOneLetterChain = true;
72690
+ pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain);
72691
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
72584
72692
 
72585
72693
  return pdbstr;
72586
72694
  }
@@ -78594,8 +78702,13 @@ class Ligplot {
78594
78702
  this.icn3d = icn3d;
78595
78703
  }
78596
78704
 
78597
- async drawLigplot(atomSet1) { let ic = this.icn3d, me = ic.icn3dui;
78598
- me.htmlCls.dialogCls.openDlg('dl_ligplot', 'Show ligand interactions with atom details');
78705
+ async drawLigplot(atomSet1, bDepiction) { let ic = this.icn3d, me = ic.icn3dui;
78706
+ if(bDepiction) {
78707
+ me.htmlCls.dialogCls.openDlg('dl_ligplot', '2D Depiction');
78708
+ }
78709
+ else {
78710
+ me.htmlCls.dialogCls.openDlg('dl_ligplot', 'Show ligand interactions with atom details');
78711
+ }
78599
78712
 
78600
78713
  let widthOri, heightOri, width = 100, height = 100;
78601
78714
  ic.len4ang = 80;
@@ -78677,19 +78790,28 @@ class Ligplot {
78677
78790
  let offset = - ic.len4ang;
78678
78791
  let svgHtml = "<svg id='" + id + "' viewBox='" + offset + "," + offset + "," + width + "," + heightAll + "' width='" + graphWidth + "px' font-family='sans-serif' stroke='rgb(0,0,0)' stroke-width='2' stroke-linecap='round'>";
78679
78792
 
78680
- let xlen = parseInt(widthOri / ic.svgGridSize), ylen = parseInt(heightOri / ic.svgGridSize);
78681
- let result = ic.viewInterPairsCls.getAllInteractionTable("save1", index2xy, xlen, ylen, xcenter, ycenter); // sort on the ligand/set1
78682
- ic.bLigplot = true;
78793
+ if(bDepiction) {
78794
+ svgHtml += lineSvg + nodeSvg;
78795
+ }
78796
+ else {
78797
+ let xlen = parseInt(widthOri / ic.svgGridSize), ylen = parseInt(heightOri / ic.svgGridSize);
78798
+ let result = ic.viewInterPairsCls.getAllInteractionTable("save1", index2xy, xlen, ylen, xcenter, ycenter); // sort on the ligand/set1
78799
+ // ic.bLigplot = true;
78683
78800
 
78684
- svgHtml += lineSvg + result.svgHtmlLine;
78801
+ svgHtml += lineSvg + result.svgHtmlLine;
78685
78802
 
78686
- svgHtml += nodeSvg + result.svgHtmlNode;
78803
+ svgHtml += nodeSvg + result.svgHtmlNode;
78804
+ }
78687
78805
 
78688
78806
  svgHtml += "</svg>";
78689
78807
 
78690
- $("#" + ic.pre + "ligplotDiv").html(svgHtml);
78691
-
78692
- this.setEventsForLigplot();
78808
+ if(bDepiction) {
78809
+ $("#" + ic.pre + "ligplotDiv").html(svgHtml);
78810
+ }
78811
+ else {
78812
+ $("#" + ic.pre + "ligplotDiv").html(svgHtml);
78813
+ this.setEventsForLigplot();
78814
+ }
78693
78815
  }
78694
78816
 
78695
78817
 
@@ -79836,7 +79958,7 @@ class SaveFile {
79836
79958
  }
79837
79959
 
79838
79960
  //getAtomPDB: function(atomHash, bPqr, bPdb, bNoChem) { let ic = this.icn3d, me = ic.icn3dui;
79839
- getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bVastSearch) { let ic = this.icn3d, me = ic.icn3dui;
79961
+ getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bOneLetterChain) { let ic = this.icn3d, me = ic.icn3dui;
79840
79962
  let pdbStr = '';
79841
79963
 
79842
79964
  // get all phosphate groups in lipids
@@ -80111,7 +80233,7 @@ class SaveFile {
80111
80233
  //line +=(atom.chain.length <= 1) ? atom.chain.padStart(1, ' ') : atom.chain.substr(0, 1);
80112
80234
  if(atom.chain.length >= 2) {
80113
80235
  let chainTmp = atom.chain.replace(/_/gi, '').substr(0, 2);
80114
- if(bVastSearch) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
80236
+ if(bOneLetterChain) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
80115
80237
  line += chainTmp;
80116
80238
  }
80117
80239
  else if(atom.chain.length == 1) {
@@ -80190,8 +80312,8 @@ class SaveFile {
80190
80312
  }
80191
80313
  else {
80192
80314
  line += "1.00".padStart(6, ' ');
80193
- // line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : ' '.padStart(6, ' ');
80194
- let defaultBFactor = (bVastSearch) ? "1.0" : " ";
80315
+ // let defaultBFactor = (bOneLetterChain) ? "1.0" : " ";
80316
+ let defaultBFactor = " ";
80195
80317
  line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : defaultBFactor.padStart(6, ' ');
80196
80318
  line += ' '.padStart(10, ' ');
80197
80319
  line += atom.elem.padStart(2, ' ');
@@ -80239,7 +80361,7 @@ class SaveFile {
80239
80361
  let resn = me.utilsCls.residueName2Abbr(atom.resn);
80240
80362
  let ss = this.secondary2Abbr(atom.ss);
80241
80363
 
80242
- if(chainid != prevChainid) {
80364
+ if(chainid != prevChainid && !data[chainid]) {
80243
80365
  data[chainid] = {"resi": [], "resn": [], "secondary": []};
80244
80366
  }
80245
80367
 
@@ -83763,7 +83885,7 @@ class iCn3DUI {
83763
83885
  //even when multiple iCn3D viewers are shown together.
83764
83886
  this.pre = this.cfg.divid + "_";
83765
83887
 
83766
- this.REVISION = '3.39.0';
83888
+ this.REVISION = '3.40.3';
83767
83889
 
83768
83890
  // In nodejs, iCn3D defines "window = {navigator: {}}"
83769
83891
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -83925,7 +84047,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
83925
84047
  ic.molTitle = '';
83926
84048
  ic.loadCmd;
83927
84049
 
83928
- // set menus
84050
+ // set menus
83929
84051
  me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
83930
84052
  me.htmlCls.clickMenuCls.applyShownMenus();
83931
84053