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.js CHANGED
@@ -8827,25 +8827,42 @@ class ClickMenu {
8827
8827
  }
8828
8828
 
8829
8829
  getHiddenMenusFromCache() { let me = this.icn3dui; me.icn3d;
8830
- me.htmlCls.shownMenus = {};
8830
+ // me.htmlCls.shownMenus = {};
8831
8831
 
8832
- let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
8833
-
8834
- if(idArrayStr && idArrayStr != '[]') {
8835
- let idArray = JSON.parse(idArrayStr);
8832
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
8836
8833
 
8837
- // for(let i = 0, il = idArray.length; i < il; ++i) {
8838
- // me.htmlCls.shownMenus[idArray[i]] = 1;
8839
- // }
8840
- for(let menu in me.htmlCls.allMenus) {
8841
- if(idArray.indexOf(menu) == -1) {
8842
- me.htmlCls.shownMenus[menu] = 1;
8843
- }
8834
+ let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
8835
+
8836
+ if(idArrayStr && idArrayStr != '[]') {
8837
+ me.htmlCls.shownMenus = {};
8838
+
8839
+ let idArray = JSON.parse(idArrayStr);
8840
+
8841
+ // for(let i = 0, il = idArray.length; i < il; ++i) {
8842
+ // me.htmlCls.shownMenus[idArray[i]] = 1;
8843
+ // }
8844
+ for(let menu in me.htmlCls.allMenus) {
8845
+ if(idArray.indexOf(menu) == -1) {
8846
+ me.htmlCls.shownMenus[menu] = 1;
8844
8847
  }
8845
- }
8846
- else {
8847
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
8848
- }
8848
+ }
8849
+ }
8850
+ //###
8851
+ else {
8852
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
8853
+ }
8854
+
8855
+ // else {
8856
+ // if(mode == 'all') {
8857
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
8858
+ // }
8859
+ // else if(!mode || mode == 'simple') {
8860
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
8861
+ // }
8862
+ // else {
8863
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
8864
+ // }
8865
+ // }
8849
8866
  }
8850
8867
 
8851
8868
  displayShownMenus() { let me = this.icn3dui; me.icn3d;
@@ -9341,11 +9358,14 @@ class ClickMenu {
9341
9358
  me.htmlCls.shownMenus[checkbox.value] = 1;
9342
9359
  }
9343
9360
 
9361
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
9362
+
9344
9363
  thisClass.applyShownMenus();
9345
9364
  });
9346
9365
 
9347
9366
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref", "#" + me.pre + "reset_menupref2"], "click", function(e) { me.icn3d; //e.preventDefault();
9348
9367
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
9368
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'simple');
9349
9369
 
9350
9370
  thisClass.applyShownMenus();
9351
9371
  thisClass.displayShownMenus();
@@ -9353,6 +9373,7 @@ class ClickMenu {
9353
9373
 
9354
9374
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref_all", "#" + me.pre + "reset_menupref_all2"], "click", function(e) { me.icn3d; //e.preventDefault();
9355
9375
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9376
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'all');
9356
9377
 
9357
9378
  thisClass.applyShownMenus();
9358
9379
  thisClass.displayShownMenus();
@@ -9402,15 +9423,16 @@ class ClickMenu {
9402
9423
 
9403
9424
  thisClass.applyShownMenus();
9404
9425
  thisClass.displayShownMenus();
9426
+
9427
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
9405
9428
  };
9406
9429
  reader.readAsText(file);
9407
9430
  }
9408
9431
  });
9409
9432
 
9410
- me.myEventCls.onIds("#" + me.pre + "mn1_menuloadpref", "click", function(e) { me.icn3d; //e.preventDefault();
9433
+ me.myEventCls.onIds(["#" + me.pre + "mn1_menuloadpref", "#" + me.pre + "loadpref", "#" + me.pre + "loadpref2"], "click", function(e) { me.icn3d; //e.preventDefault();
9411
9434
  me.htmlCls.dialogCls.openDlg('dl_menuloadpref', 'Please input the menu preference file');
9412
9435
  });
9413
-
9414
9436
 
9415
9437
  me.myEventCls.onIds("#" + me.pre + "mn1_link_structure", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
9416
9438
  let url = ic.saveFileCls.getLinkToStructureSummary(true);
@@ -11249,6 +11271,51 @@ class SetMenu {
11249
11271
  return me.htmlCls.setHtmlCls.getRadioColor(radioid, id, text, color, bChecked, bSimpleMenu, selType);
11250
11272
  }
11251
11273
 
11274
+ resetMenu(mode) { let me = this.icn3dui;
11275
+ if(!mode || mode == 'simple') {
11276
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11277
+
11278
+ me.htmlCls.clickMenuCls.applyShownMenus();
11279
+ }
11280
+ else if(mode == 'all') {
11281
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
11282
+
11283
+ me.htmlCls.clickMenuCls.applyShownMenus();
11284
+ }
11285
+ else if(mode == 'custom') {
11286
+ me.htmlCls.dialogCls.openDlg('dl_menupref', 'Select Menus');
11287
+
11288
+ me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
11289
+
11290
+ me.htmlCls.clickMenuCls.displayShownMenus();
11291
+ }
11292
+ }
11293
+
11294
+ setMenuMode(bMobile) { let me = this.icn3dui;
11295
+ let spaceCss = (bMobile) ? "; padding-left:6px; background-color:#eee" : "; margin:3px; background-color:white";
11296
+ let spaceCss2 = (bMobile) ? "; font-size:14px!important" : "";
11297
+
11298
+ let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
11299
+
11300
+ let html = '<div class="icn3d-text" style="color:#f8b84e; font-weight:bold' + spaceCss + '">';
11301
+ html += '<select name="menumode" id="' + me.pre + 'menumode" class="icn3d-text" style="color:#f8b84e; font-weight:bold; border:0px' + spaceCss2 + '">';
11302
+ html += (mode == 'simple' || !mode) ? '<option value="simple" selected>Simple</option>' : '<option value="simple">Simple</option>';
11303
+ html += (mode == 'all') ? '<option value="all" selected>All</option>' : '<option value="all">All</option>';
11304
+ html += (mode == 'custom') ? '<option value="custom" selected>Custom</option>' : '<option value="custom">Custom</option>';
11305
+ html += '</select>';
11306
+
11307
+ if(bMobile) {
11308
+ html += '<br><span style="font-size:12px">&nbsp;Menus</span>';
11309
+ }
11310
+ else {
11311
+ html += '&nbsp;Menus';
11312
+ }
11313
+
11314
+ html += '</div>';
11315
+
11316
+ return html;
11317
+ }
11318
+
11252
11319
  //Set the HTML code for the menus shown at the top of the viewer.
11253
11320
  setTopMenusHtml(id, str1, str2) { let me = this.icn3dui;
11254
11321
  if(me.bNode) return '';
@@ -11268,6 +11335,9 @@ class SetMenu {
11268
11335
  html += "<table border='0' cellpadding='0' cellspacing='0' width='100'><tr>";
11269
11336
 
11270
11337
  let tdStr = '<td valign="top">';
11338
+
11339
+ // html += tdStr + this.setMenuMode() + '</td>';
11340
+
11271
11341
  html += tdStr + this.setMenu1() + '</td>';
11272
11342
 
11273
11343
  html += tdStr + this.setMenu2() + '</td>';
@@ -11280,7 +11350,11 @@ class SetMenu {
11280
11350
  //html += tdStr + this.setMenu5b() + '</td>';
11281
11351
  html += tdStr + this.setMenu6() + '</td>';
11282
11352
 
11283
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11353
+ // reset the menus at the end of the menus
11354
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
11355
+ // this.resetMenu(mode);
11356
+
11357
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11284
11358
 
11285
11359
  html += tdStr + "<div style='position:relative; margin-left:6px;'>" + str1;
11286
11360
  html += "<div class='icn3d-commandTitle' style='min-width:40px; margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -11376,6 +11450,7 @@ class SetMenu {
11376
11450
 
11377
11451
  //html += "<div class='icn3d-menu'>";
11378
11452
  html += "<div>";
11453
+
11379
11454
  html += "<accordion id='" + me.pre + "accordion0' class='icn3d-accordion'>";
11380
11455
  if(me.cfg.notebook) {
11381
11456
  html += "<h3 style='width:20px; height:24px; position:relative; padding: 0'><span style='position:absolute; left:3px; top:4px;'>&#9776;</span></h3>";
@@ -11385,6 +11460,8 @@ class SetMenu {
11385
11460
  }
11386
11461
  html += "<div>";
11387
11462
 
11463
+ // html += '<li>' + this.setMenuMode(true);
11464
+
11388
11465
  let liStr = "<li><span class='icn3d-menu-color'";
11389
11466
 
11390
11467
  html += "<ul class='icn3d-mn-item'>";
@@ -11403,7 +11480,11 @@ class SetMenu {
11403
11480
  html += liStr + ">Help</span>";
11404
11481
  html += this.setMenu6_base();
11405
11482
 
11406
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11483
+ // reset the menus at the end of the menus
11484
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
11485
+ // this.resetMenu(mode);
11486
+
11487
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11407
11488
 
11408
11489
  html += "<li><div style='position:relative; margin-top:-6px;'>" + str1;
11409
11490
  html += "<div class='icn3d-commandTitle' style='margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -11580,7 +11661,7 @@ class SetMenu {
11580
11661
  // Analysis menu
11581
11662
  html += tdStrBorder + this.setIcon(iconType, 'tool_selectannotations', 'Sequences & Annotations', 'grip-lines') + "</td>";
11582
11663
  html += tdStr + this.setIcon(iconType, 'hbondsYes', 'Interactions', 'users') + "</td>";
11583
- html += tdStr + this.setIcon(iconType, 'tool_delphi', 'Delphi Potentials', 'cloud-meatball') + "</td>";
11664
+ html += tdStr + this.setIcon(iconType, 'tool_delphi', 'DelPhi Potentials', 'cloud-meatball') + "</td>";
11584
11665
  html += tdStr + this.setIcon(iconType, 'removeLabels', 'Remove Labels', 'remove-format') + "</td>";
11585
11666
 
11586
11667
  // Help menu
@@ -12792,6 +12873,10 @@ class SetMenu {
12792
12873
 
12793
12874
  html += "<ul class='icn3d-mn-item'>";
12794
12875
 
12876
+ if(me.cfg.cid !== undefined || me.cfg.smiles !== undefined) {
12877
+ html += this.getLink('mn2_2ddepiction', '2D Depiction ' + me.htmlCls.wifiStr, 1, 1);
12878
+ }
12879
+
12795
12880
  if(me.cfg.cid === undefined) {
12796
12881
  html += this.getLink('mn6_selectannotations', 'Seq. & Annotations ' + me.htmlCls.wifiStr, 1, 1);
12797
12882
 
@@ -13593,7 +13678,7 @@ class Dialog {
13593
13678
  width='50%';
13594
13679
  }
13595
13680
  else if(id === me.pre + 'dl_menupref') {
13596
- width = 600;
13681
+ width = 800;
13597
13682
  height = 500;
13598
13683
  }
13599
13684
 
@@ -14254,6 +14339,10 @@ class SetDialog {
14254
14339
  html += me.htmlCls.divStr + "dl_collection_file' style=''>";
14255
14340
  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>";
14256
14341
  html += "Collection file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "collectionfile'><br/>";
14342
+ html += "<input type='radio' id='dl_collectionAppendStructureNone' name='appendStructure' value='none' checked/>";
14343
+ html += "<label for='dl_collectionAppendStructureNone'>Default</label>";
14344
+ html += "<input type='radio' id='dl_collectionAppendStructure' name='appendStructure' value='append' />";
14345
+ html += "<label for='dl_collectionAppendStructure'>Append</label><br/>";
14257
14346
  html += me.htmlCls.buttonStr + "reload_collectionfile' style='margin-top: 6px;'>Load</button>";
14258
14347
  html += "</div>";
14259
14348
  html += "</div>";
@@ -14261,7 +14350,8 @@ class SetDialog {
14261
14350
  html += me.htmlCls.divStr + "dl_collection_structures' style='display: none'>";
14262
14351
  html += "<select id='" + me.pre + "collections_menu'multiple size='6' style='min-width:300px;'></select>";
14263
14352
  html += '<br/>';
14264
- html += me.htmlCls.buttonStr + "opendl_export_collections'>Export</button>";
14353
+ html += me.htmlCls.buttonStr + "collections_clear_commands' style='margin-top: 6px;'>Clear Commands</button>";
14354
+ html += me.htmlCls.buttonStr + "opendl_export_collections'>Export JSON</button>";
14265
14355
  html += "</div>";
14266
14356
  html += '<br/>';
14267
14357
  html += "</div>";
@@ -14616,24 +14706,30 @@ class SetDialog {
14616
14706
  html += "</div>";
14617
14707
 
14618
14708
 
14619
- html += me.htmlCls.divStr + "dl_ligplot' sty2D Interaction for One Ligand/Residule='background-color:white' class='" + dialogClass + "'>";
14620
- html += this.addNotebookTitle('dl_ligplot', 'e with Atom Details');
14709
+ html += me.htmlCls.divStr + "dl_ligplot' style='background-color:white' class='" + dialogClass + "'>";
14710
+
14711
+ if(me.cfg.cid !== undefined || me.cfg.smiles !== undefined) {
14712
+ html += this.addNotebookTitle('dl_ligplot', '2D Depiction for Chemicals');
14713
+ }
14714
+ else {
14715
+ html += this.addNotebookTitle('dl_ligplot', '2D Interaction for One Ligand/Residue with Atom Details');
14621
14716
 
14622
- 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;
14717
+ 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;
14623
14718
 
14624
- html += '<div style="width:20px; margin-top:6px; display:inline-block;"><span id="'
14625
- + 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="'
14626
- + me.pre + 'dl_ligplotcolor_shrink" class="ui-icon ui-icon-minus icn3d-shrink icn3d-link" style="width:15px;" title="Shrink"></span></div></div>';
14719
+ html += '<div style="width:20px; margin-top:6px; display:inline-block;"><span id="'
14720
+ + 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="'
14721
+ + me.pre + 'dl_ligplotcolor_shrink" class="ui-icon ui-icon-minus icn3d-shrink icn3d-link" style="width:15px;" title="Shrink"></span></div></div>';
14627
14722
 
14628
- html += me.htmlCls.divStr + "dl_ligplotcolor' style='inline-block;'>";
14723
+ html += me.htmlCls.divStr + "dl_ligplotcolor' style='inline-block;'>";
14629
14724
 
14630
- // 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>";
14631
- // html += "Mouseover the dashed lines to see interaction types and distances.<br>";
14632
- html += "<b>Color legend</b> for interactions (dashed lines): <br>";
14725
+ // 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>";
14726
+ // html += "Mouseover the dashed lines to see interaction types and distances.<br>";
14727
+ html += "<b>Color legend</b> for interactions (dashed lines): <br>";
14633
14728
 
14634
- html += me.htmlCls.setHtmlCls.setColorHints();
14729
+ html += me.htmlCls.setHtmlCls.setColorHints();
14635
14730
 
14636
- html += "<br></div>";
14731
+ html += "<br></div>";
14732
+ }
14637
14733
 
14638
14734
  me.ligplotid = me.pre + 'ligplot';
14639
14735
  html += me.htmlCls.divNowrapStr + buttonStrTmp + me.ligplotid + '_svg">SVG</button>' + me.htmlCls.space2;
@@ -14960,13 +15056,15 @@ class SetDialog {
14960
15056
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref'>Apply</button></span>";
14961
15057
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref' style='margin-left:30px'>Reset to Simple Menus</button></span>";
14962
15058
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all' style='margin-left:30px'>Reset to All Menus</button></span>";
14963
- html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span><br><br>";
15059
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span>";
15060
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref' style='margin-left:30px'>Load Preferences</button></span><br><br>";
14964
15061
 
14965
15062
  html += "<div id='" + me.pre + "menulist'></div><br><br>";
14966
15063
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref2'>Apply</button></span>";
14967
15064
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref2' style='margin-left:30px'>Reset to Simple Menus</button></span>";
14968
15065
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all2' style='margin-left:30px'>Reset to All Menus</button></span>";
14969
15066
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref2' style='margin-left:30px'>Save Preferences</button></span>";
15067
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref2' style='margin-left:30px'>Load Preferences</button></span>";
14970
15068
  html += "</div>";
14971
15069
 
14972
15070
  html += me.htmlCls.divStr + "dl_addtrack' class='" + dialogClass + "'>";
@@ -15742,6 +15840,14 @@ class Events {
15742
15840
  me.htmlCls.clickMenuCls.clickMenu5();
15743
15841
  me.htmlCls.clickMenuCls.clickMenu6();
15744
15842
 
15843
+ me.myEventCls.onIds("#" + me.pre + "menumode", "change", async function(e) { me.icn3d;
15844
+ e.preventDefault();
15845
+ let mode = $("#" + me.pre + "menumode").val();
15846
+
15847
+ me.htmlCls.setHtmlCls.setCookie('menumode', mode);
15848
+ me.htmlCls.setMenuCls.resetMenu(mode);
15849
+ });
15850
+
15745
15851
  // back and forward arrows
15746
15852
  me.myEventCls.onIds(["#" + me.pre + "back", "#" + me.pre + "mn6_back"], "click", async function(e) { let ic = me.icn3d;
15747
15853
  e.preventDefault();
@@ -16019,6 +16125,11 @@ class Events {
16019
16125
  thisClass.setLogCmd("view interactions", true);
16020
16126
  });
16021
16127
 
16128
+ me.myEventCls.onIds("#" + me.pre + "mn2_2ddepiction", "click", async function(e) { let ic = me.icn3d;
16129
+ await ic.ligplotCls.drawLigplot(ic.atoms, true);
16130
+ thisClass.setLogCmd("view 2d depiction", true);
16131
+ });
16132
+
16022
16133
  me.myEventCls.onIds("#" + me.pre + "search_seq_button", "click", async function(e) { me.icn3d;
16023
16134
  e.stopImmediatePropagation();
16024
16135
  await thisClass.searchSeq();
@@ -16783,95 +16894,108 @@ class Events {
16783
16894
  } else {
16784
16895
  ic.resizeCanvasCls.closeDialogs();
16785
16896
  }
16786
- ic.bInputfile = false;
16787
- ic.pdbCollection = [];
16788
- ic.allData = {};
16789
- ic.allData['all'] = {
16790
- 'atoms': {},
16791
- 'proteins': {},
16792
- 'nucleotides': {},
16793
- 'chemicals': {},
16794
- 'ions': {},
16795
- 'water': {},
16796
- 'structures': {}, // getSSExpandedAtoms
16797
- 'ssbondpnts': {},
16798
- 'residues': {}, // getSSExpandedAtoms
16799
- 'chains': {},
16800
- 'chainsSeq': {}, //Sequences and Annotation
16801
- 'defNames2Atoms': {},
16802
- 'defNames2Residues': {}
16803
- };
16804
- ic.allData['prev'] = {};
16805
- ic.selectCollectionsCls.reset();
16806
-
16897
+
16807
16898
  ic.dAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
16808
16899
  ic.hAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
16809
16900
  me.htmlCls.setHtmlCls.fileSupport();
16810
16901
 
16811
16902
  let fileName = file.name;
16812
16903
  let fileExtension = fileName.split('.').pop().toLowerCase();
16813
-
16904
+ let collection = {};
16905
+
16814
16906
  $("#" + ic.pre + "collections_menu").empty();
16815
16907
  $("#" + ic.pre + "collections_menu").off("change");
16908
+
16909
+ if (dl_collectionAppendStructureNone.checked || ic.allData === undefined) {
16910
+ ic.bInputfile = false;
16911
+ ic.pdbCollection = {};
16912
+ ic.allData = {};
16913
+ ic.allData['all'] = {
16914
+ 'atoms': {},
16915
+ 'proteins': {},
16916
+ 'nucleotides': {},
16917
+ 'chemicals': {},
16918
+ 'ions': {},
16919
+ 'water': {},
16920
+ 'structures': {}, // getSSExpandedAtoms
16921
+ 'ssbondpnts': {},
16922
+ 'residues': {}, // getSSExpandedAtoms
16923
+ 'chains': {},
16924
+ 'chainsSeq': {}, //Sequences and Annotation
16925
+ 'defNames2Atoms': {},
16926
+ 'defNames2Residues': {}
16927
+ };
16928
+ ic.allData['prev'] = {};
16929
+ ic.selectCollectionsCls.reset();
16930
+
16931
+ } else {
16932
+ if (ic.collections) {
16933
+ collection = ic.collections;
16934
+ }
16935
+ }
16816
16936
 
16817
16937
  function parseJsonCollection(data) {
16818
16938
  let dataStr = JSON.parse(data);
16819
- return dataStr["structures"].map(({ id, title, description, commands }) => {
16939
+ let parsedCollection = {};
16940
+
16941
+ dataStr["structures"].map(({ id, title, description, commands }) => {
16820
16942
  if (id && id.includes('.pdb')) {
16821
16943
  id = id.split('.pdb')[0];
16822
16944
  }
16823
- return [id, title, description, commands, false];
16945
+ parsedCollection[id] = [id, title, description, commands, false];
16824
16946
  });
16825
- }
16826
16947
 
16948
+ return parsedCollection;
16949
+ }
16950
+
16827
16951
  function parsePdbCollection(data, description = '', commands = []) {
16828
16952
  let dataStr = data;
16829
16953
  let lines = dataStr.split('\n');
16830
-
16831
16954
  let sections = [];
16832
16955
  let currentSection = [];
16833
-
16956
+
16834
16957
  lines.forEach(line => {
16835
- if (line.startsWith('HEADER')) {
16958
+ if (line.startsWith('HEADER')) {
16836
16959
  currentSection = [];
16837
16960
  sections.push(currentSection);
16838
- }
16839
- currentSection.push(line);
16961
+ }
16962
+ currentSection.push(line);
16840
16963
  });
16841
-
16842
- let ids = [];
16843
- let titles = [];
16844
-
16964
+
16965
+
16966
+ let parsedCollection = {};
16967
+
16845
16968
  sections.forEach((section) => {
16846
- let headerLine = section[0];
16847
- headerLine = headerLine.replace(/[\n\r]/g, '').trim();
16969
+ let headerLine = section[0].replace(/[\n\r]/g, '').trim();
16848
16970
  let header = headerLine.split(' ').filter(Boolean);
16849
- let lastElement = header[header.length - 1];
16850
- ids.push(lastElement);
16851
- titles.push(section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : lastElement);
16971
+ let id = header[header.length - 1];
16972
+ let title = section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : id;
16973
+
16974
+ parsedCollection[id] = [id, title, description, commands, true];
16975
+
16976
+ const sanitizedSection = section.map(line => line.trim());
16977
+ ic.pdbCollection[id] = sanitizedSection;
16852
16978
  });
16853
-
16854
- if (sections.length > 0) {
16855
- ic.pdbCollection.push(...sections);
16856
- }
16857
16979
 
16858
- return ids.map((id, index, description, commands) => [id, titles[index], description, commands, true]);
16980
+ return parsedCollection;
16859
16981
  }
16860
16982
 
16861
- let collection = [];
16862
-
16863
16983
  if (fileExtension === 'json' || fileExtension === 'pdb') {
16864
16984
  let reader = new FileReader();
16865
16985
  reader.onload = async function (e) {
16866
16986
  if (fileExtension === 'json') {
16867
- collection = parseJsonCollection(e.target.result);
16987
+ let jsonCollection = parseJsonCollection(e.target.result);
16988
+ collection = { ...collection, ...jsonCollection };
16868
16989
  } else if (fileExtension === 'pdb') {
16869
16990
  ic.bInputfile = true;
16870
- collection = parsePdbCollection(e.target.result);
16991
+ let pdbCollection = parsePdbCollection(e.target.result);
16992
+ collection = { ...collection, ...pdbCollection };
16871
16993
  }
16872
16994
 
16873
16995
  let collectionHtml = await ic.selectCollectionsCls.setAtomMenu(collection);
16874
16996
 
16997
+ ic.collections = collection;
16998
+
16875
16999
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
16876
17000
  await ic.selectCollectionsCls.clickStructure(collection);
16877
17001
  $("#" + ic.pre + "collections_menu").trigger("change");
@@ -16889,7 +17013,7 @@ class Events {
16889
17013
  let reader2 = new FileReader();
16890
17014
  reader2.onload = async function (e) {
16891
17015
  if (fileExtension === 'zip') {
16892
- let url = './script/jszip.js';
17016
+ let url = './script/jszip.min.js';
16893
17017
  await me.getAjaxPromise(url, 'script');
16894
17018
 
16895
17019
  let jszip = new JSZip();
@@ -16923,7 +17047,8 @@ class Events {
16923
17047
  let jsonCollection = [];
16924
17048
  for (const file of jsonFiles) {
16925
17049
  let fileData = await file.async('text');
16926
- parseJsonCollection(fileData).forEach(element => {
17050
+ let parsedJson = Object.values(parseJsonCollection(fileData));
17051
+ parsedJson.forEach(element => {
16927
17052
  jsonCollection.push(element);
16928
17053
  });
16929
17054
  }
@@ -16933,8 +17058,9 @@ class Events {
16933
17058
  let matchingPdbFile = pdbFiles.find(file => file.name.toLowerCase().includes(id.toLowerCase()));
16934
17059
  if (matchingPdbFile) {
16935
17060
  let pdbFileData = await matchingPdbFile.async('text');
16936
- parsePdbCollection(pdbFileData, description, commands).forEach(element => {
16937
- collection.push(element);
17061
+ let parsedPdb = Object.values(parsePdbCollection(pdbFileData, description, commands));
17062
+ parsedPdb.forEach(element => {
17063
+ collection[id] = element;
16938
17064
  });
16939
17065
  }
16940
17066
  }
@@ -16943,27 +17069,30 @@ class Events {
16943
17069
  // Do something if only JSON files are present
16944
17070
  jsonFiles.forEach(async file => {
16945
17071
  let fileData = await file.async('text');
16946
- parseJsonCollection(fileData).forEach(element => {
16947
- collection.push(element);
17072
+ const parsedJson = Object.values(parseJsonCollection(fileData));
17073
+ parsedJson.forEach(element => {
17074
+ collection[element[0]] = element;
16948
17075
  });
16949
17076
  });
16950
17077
  } else if (hasPdb) {
16951
17078
  // Do something if only PDB files are present
16952
17079
  pdbFiles.forEach(async file => {
16953
17080
  let fileData = await file.async('text');
16954
- parsePdbCollection(fileData).forEach(element => {
16955
- collection.push(element);
17081
+ const parsedPdb = Object.values(parsedPdbCollection(fileData));
17082
+ parsedPdb.forEach(element => {
17083
+ collection[element[0]] = element;
16956
17084
  });
16957
17085
  });
16958
17086
  } else if (hasGz) {
16959
- let url = './script/pako.js';
17087
+ let url = './script/pako.min.js';
16960
17088
  await me.getAjaxPromise(url, 'script');
16961
17089
  try {
16962
17090
  for (const file of gzFiles) {
16963
17091
  let compressed = await file.async('uint8array');
16964
17092
  let decompressed = pako.inflate(compressed, { to: 'string' });
16965
- parsePdbCollection(decompressed).forEach(element => {
16966
- collection.push(element);
17093
+ const parsedPdb = Object.values(parsePdbCollection(decompressed));
17094
+ parsedPdb.forEach(element => {
17095
+ collection[element[0]] = element;
16967
17096
  });
16968
17097
  }
16969
17098
  } catch (error) {
@@ -16974,7 +17103,7 @@ class Events {
16974
17103
  console.error('Error loading ZIP file', error);
16975
17104
  }
16976
17105
  } else if (fileExtension === 'gz') {
16977
- let url = './script/pako.js';
17106
+ let url = './script/pako.min.js';
16978
17107
  await me.getAjaxPromise(url, 'script');
16979
17108
 
16980
17109
  try {
@@ -16991,6 +17120,8 @@ class Events {
16991
17120
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
16992
17121
  await ic.selectCollectionsCls.clickStructure(collection);
16993
17122
 
17123
+ ic.collections = collection;
17124
+
16994
17125
  $("#" + ic.pre + "collections_menu").trigger("change");
16995
17126
 
16996
17127
  me.htmlCls.clickMenuCls.setLogCmd(
@@ -17009,7 +17140,7 @@ class Events {
17009
17140
  throw new Error('Invalid file type');
17010
17141
  }
17011
17142
 
17012
- if (Object.keys(me.utilsCls.getStructures(ic.dAtoms))){
17143
+ if (ic.allData && Object.keys(ic.allData).length > 0) {
17013
17144
  $("#" + me.pre + "dl_collection_file").hide();
17014
17145
  $("#" + me.pre + "dl_collection_structures").show();
17015
17146
  $("#" + me.pre + "dl_collection_file_expand").show();
@@ -17030,6 +17161,17 @@ class Events {
17030
17161
  }
17031
17162
  });
17032
17163
 
17164
+ me.myEventCls.onIds("#" + me.pre + "collections_clear_commands", "click", function (e) {
17165
+ var selectedValues = $("#" + ic.pre + "collections_menu").val();
17166
+ selectedValues.forEach(function (selectedValue) {
17167
+ if (ic.allData[selectedValue]) {
17168
+ ic.allData[selectedValue]['commands'] = [];
17169
+ } else {
17170
+ console.warn("No data found for selectedValue:", selectedValue);
17171
+ }
17172
+ });
17173
+ });
17174
+
17033
17175
  me.myEventCls.onIds("#" + me.pre + "opendl_export_collections", "click", function (e) {
17034
17176
  me.htmlCls.dialogCls.openDlg("dl_export_collections", "Export Collections");
17035
17177
  });
@@ -19516,9 +19658,9 @@ class SetHtml {
19516
19658
 
19517
19659
  let pdbstr = '';
19518
19660
 
19519
- let bMergeIntoOne = true;
19520
- pdbstr += ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
19521
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
19661
+ let bMergeIntoOne = true, bOneLetterChain = true;
19662
+ 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);
19663
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
19522
19664
 
19523
19665
  let url = me.htmlCls.baseUrl + "delphi/delphi.cgi";
19524
19666
 
@@ -27222,6 +27364,8 @@ class Camera {
27222
27364
  }
27223
27365
  }
27224
27366
 
27367
+ // ic.cam.add(ic.directionalLight);
27368
+
27225
27369
  ic.cam.updateProjectionMatrix();
27226
27370
  // }
27227
27371
  }
@@ -28720,687 +28864,233 @@ class Stick {
28720
28864
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
28721
28865
  */
28722
28866
 
28723
- class Strand {
28867
+ class FirstAtomObj {
28724
28868
  constructor(icn3d) {
28725
28869
  this.icn3d = icn3d;
28726
28870
  }
28727
28871
 
28728
- // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
28729
- //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
28730
- //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
28731
- //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
28732
- //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
28733
- //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
28734
- //with bHighlight=1 and 3D objects with bHighlight=2.
28735
- createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
28736
- if(me.bNode) return;
28872
+ //Return the first atom in the atom hash, which has the atom serial number as the key.
28873
+ getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28874
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28875
+ return undefined;
28876
+ }
28737
28877
 
28738
- let bRibbon = fill ? true: false;
28878
+ let atomKeys = Object.keys(atomsHash);
28879
+ let firstIndex = atomKeys[0];
28739
28880
 
28740
- // when highlight, the input atoms may only include one rediue.
28741
- // add one extra residue to show the strand
28742
- let atomsAdjust = {};
28881
+ return ic.atoms[firstIndex];
28882
+ }
28743
28883
 
28744
- let residueHashTmp = ic.firstAtomObjCls.getResiduesFromAtoms(atoms);
28745
- if( Object.keys(residueHashTmp).length == 1) {
28746
- atomsAdjust = this.getOneExtraResidue(residueHashTmp);
28884
+ // n is the position of the selected atom
28885
+ getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
28886
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28887
+ return undefined;
28747
28888
  }
28748
- else {
28749
- atomsAdjust = atoms;
28889
+
28890
+ let atomKeys = Object.keys(atomsHash);
28891
+ let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
28892
+
28893
+ return ic.atoms[middleIndex];
28894
+ }
28895
+
28896
+ getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28897
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28898
+ return undefined;
28750
28899
  }
28751
28900
 
28752
- if(bHighlight === 2) {
28753
- if(fill) {
28754
- fill = false;
28755
- num = null;
28756
- div = null;
28757
- coilWidth = null;
28758
- helixSheetWidth = null;
28759
- thickness = undefined;
28901
+ let firstIndex;
28902
+
28903
+ for(let i in atomsHash) {
28904
+ if(ic.atoms[i].name == 'CA') {
28905
+ firstIndex = i;
28906
+ break;
28760
28907
  }
28761
- else {
28762
- fill = true;
28763
- num = 2;
28764
- div = undefined;
28765
- coilWidth = undefined;
28766
- helixSheetWidth = undefined;
28767
- thickness = ic.ribbonthickness;
28908
+ }
28909
+
28910
+ if(!firstIndex) {
28911
+ for(let i in atomsHash) {
28912
+ if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
28913
+ firstIndex = i;
28914
+ break;
28915
+ }
28768
28916
  }
28769
28917
  }
28770
28918
 
28771
- num = num || ic.strandDIV;
28772
- div = div || ic.axisDIV;
28773
- coilWidth = coilWidth || ic.coilWidth;
28774
- doNotSmoothen = doNotSmoothen || false;
28775
- helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
28776
- let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
28777
- let pntsCA = [];
28778
- let prevCOArray = [];
28779
- let bShowArray = [];
28780
- let calphaIdArray = []; // used to store one of the final positions drawn in 3D
28781
- let colors = [];
28782
- let currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
28783
- let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevAtomSelected = null, prevResi = null, calphaid = null, prevCalphaid = null;
28784
- let strandWidth, bSheetSegment = false, bHelixSegment = false;
28919
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
28920
+ }
28785
28921
 
28786
- // For each chain, test the first 30 atoms to see whether only C-alpha is available
28787
- let bCalphaOnlyHash = {};
28788
- for(let chainid in ic.chains) {
28789
- let atoms = me.hashUtilsCls.hash2Atoms(ic.chains[chainid], ic.atoms);
28790
- let bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atoms); //, 'CA');
28791
- bCalphaOnlyHash[chainid] = bCalphaOnly;
28922
+ getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
28923
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28924
+ return ic.atoms[0];
28792
28925
  }
28793
28926
 
28794
- // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
28795
- let residueHash = {};
28796
- for(let i in atomsAdjust) {
28797
- let atom = ic.atoms[i];
28927
+ let firstIndex;
28798
28928
 
28799
- let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
28800
- residueHash[residueid] = 1;
28929
+ for(let i in atomsHash) {
28930
+ if(ic.atoms[i].name == atomName) {
28931
+ firstIndex = i;
28932
+ break;
28933
+ }
28801
28934
  }
28802
- Object.keys(residueHash).length;
28803
-
28804
- let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
28805
28935
 
28806
- let caArray = []; // record all C-alpha atoms to predict the helix
28936
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
28937
+ }
28807
28938
 
28808
- let maxDist = 6.0;
28939
+ //Return the last atom in the atom hash, which has the atom serial number as the key.
28940
+ getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28941
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28942
+ return ic.atoms[0];
28943
+ }
28809
28944
 
28810
- //get the last residue
28811
- let atomArray = Object.keys(atomsAdjust);
28812
- let lastAtomSerial = atomArray[atomArray.length - 1];
28813
- let lastAtom = ic.atoms[lastAtomSerial];
28814
- let lastResid = lastAtom.structure + '_' + lastAtom.chain + '_' + lastAtom.resi;
28945
+ let atomKeys = Object.keys(atomsHash);
28946
+ let lastIndex = atomKeys[atomKeys.length - 1];
28815
28947
 
28816
- for (let i in atomsAdjust) {
28817
- let atom = ic.atoms[i];
28818
- let chainid = atom.structure + '_' + atom.chain;
28819
- let resid = atom.structure + '_' + atom.chain + '_' + atom.resi;
28820
- if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
28821
- // "CA" has to appear before "O"
28948
+ return ic.atoms[lastIndex];
28949
+ }
28822
28950
 
28823
- if (atom.name === 'CA') {
28824
- if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) ;
28951
+ //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
28952
+ getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28953
+ let residuesHash = {};
28954
+ for(let i in atomsHash) {
28955
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
28956
+ residuesHash[residueid] = 1;
28957
+ }
28825
28958
 
28826
- currentCA = atom.coord;
28827
- currentColor = atom.color;
28828
- calphaid = atom.serial;
28959
+ return residuesHash;
28960
+ }
28829
28961
 
28830
- caArray.push(atom.serial);
28962
+ getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28963
+ let residuesHash = {};
28964
+ for(let i in atomsHash) {
28965
+ if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
28966
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
28967
+ //residuesHash[residueid] = 1;
28968
+ residuesHash[residueid] = ic.atoms[i].resn;
28831
28969
  }
28970
+ }
28832
28971
 
28833
- if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA')) {
28834
- if(currentCA === null || currentCA === undefined) {
28835
- currentCA = atom.coord;
28836
- currentColor = atom.color;
28837
- calphaid = atom.serial;
28838
- }
28972
+ return residuesHash;
28973
+ }
28839
28974
 
28840
- if(atom.name === 'O') {
28841
- currentO = atom.coord;
28842
- }
28843
- // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
28844
-
28845
- // let bSameChain = true;
28846
- // if (currentChain !== atom.chain) {
28847
- // //if (currentChain !== atom.chain) {
28848
- // bSameChain = false;
28849
- // }
28975
+ //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
28976
+ getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28977
+ let chainsHash = {};
28978
+ for(let i in atomsHash) {
28979
+ let atom = ic.atoms[i];
28980
+ let chainid = atom.structure + "_" + atom.chain;
28850
28981
 
28851
- if(atom.ssend && atom.ss === 'sheet') {
28852
- bSheetSegment = true;
28853
- }
28854
- else if( (atom.ssend && atom.ss === 'helix') || resid == lastResid) { // partial sheet will draw as helix
28855
- bHelixSegment = true;
28856
- }
28982
+ chainsHash[chainid] = 1;
28983
+ }
28857
28984
 
28858
- // assign the previous residue
28859
- if(prevCoorO) {
28860
- if(bHighlight === 1 || bHighlight === 2) {
28861
- colors.push(ic.hColor);
28862
- }
28863
- else {
28864
- colors.push(prevColor);
28865
- }
28985
+ return chainsHash;
28986
+ }
28866
28987
 
28867
- if(ss !== 'coil' && atom.ss === 'coil') {
28868
- strandWidth = coilWidth;
28869
- }
28870
- else if(ssend && atom.ssbegin) { // a transition between two ss
28871
- strandWidth = coilWidth;
28872
- }
28873
- else {
28874
- strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
28875
- }
28988
+ getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
28989
+ if(ic.residues.hasOwnProperty(resid)) {
28990
+ for(let i in ic.residues[resid]) {
28991
+ if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
28992
+ return ic.atoms[i];
28993
+ }
28994
+ }
28995
+ }
28876
28996
 
28877
- let O, oldCA, resSpan = 4;
28878
- if(atom.name === 'O') {
28879
- O = prevCoorO.clone();
28880
- if(prevCoorCA !== null && prevCoorCA !== undefined) {
28881
- O.sub(prevCoorCA);
28882
- }
28883
- else {
28884
- prevCoorCA = prevCoorO.clone();
28885
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
28886
- O = prevCoorCA.clone();
28887
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
28888
- //O.sub(oldCA);
28889
- oldCA.sub(O);
28890
- }
28891
- else {
28892
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
28893
- }
28894
- }
28895
- }
28896
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
28897
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
28898
- O = prevCoorCA.clone();
28899
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
28900
- //O.sub(oldCA);
28901
- oldCA.sub(O);
28902
- }
28903
- else {
28904
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
28905
- }
28906
- }
28997
+ return undefined;
28998
+ }
28907
28999
 
28908
- O.normalize(); // can be omitted for performance
28909
- O.multiplyScalar(strandWidth);
28910
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
28911
- prevCO = O;
29000
+ getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
29001
+ let atom = this.getAtomFromResi(resid, atomName);
29002
+ if(atom !== undefined) {
29003
+ let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
28912
29004
 
28913
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
28914
- let delta = -1 + numM1Inv2 * j;
28915
- let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
28916
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
28917
- pnts[j].push(v);
28918
- }
29005
+ return coord;
29006
+ }
28919
29007
 
28920
- pntsCA.push(prevCoorCA);
28921
- prevCOArray.push(prevCO);
29008
+ return undefined;
29009
+ }
29010
+ }
28922
29011
 
28923
- if(atoms.hasOwnProperty(prevAtomid)) {
28924
- bShowArray.push(prevResi);
28925
- calphaIdArray.push(prevCalphaid);
28926
- }
28927
- else {
28928
- bShowArray.push(0);
28929
- calphaIdArray.push(0);
28930
- }
28931
- }
29012
+ /**
29013
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29014
+ */
28932
29015
 
28933
- //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);
29016
+ class Strip {
29017
+ constructor(icn3d) {
29018
+ this.icn3d = icn3d;
29019
+ }
28934
29020
 
28935
- let prevCoor = (prevAtomSelected) ? prevAtomSelected.coord : undefined;
28936
-
28937
- 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);
28938
-
28939
- // check whether the atoms are continuous
28940
- // atomsAdjusted has all atoms in the secondary structure
28941
- // atoms has all selected atoms
28942
- // let bBrokenSs = false;
28943
- // if(prevAtomSelected && prevAtomid == prevAtomSelected.serial && !atoms.hasOwnProperty(atom.serial)) {
28944
- // bBrokenSs = true;
28945
- // }
29021
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29022
+ createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
29023
+ calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
29024
+ if(me.bNode) return;
28946
29025
 
29026
+ if (p0.length < 2) return;
29027
+ div = div || ic.axisDIV;
28947
29028
 
28948
- // The following code didn't work to select one residue
28949
- // 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);
29029
+ // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
29030
+ if(pntsCA && ic.bDoublecolor) {
29031
+ let bExtendLastRes = false; //true;
28950
29032
 
28951
- // if(bBrokenSs && atom.ss === 'sheet') {
28952
- // bSheetSegment = true;
28953
- // }
28954
- // else if(bBrokenSs && atom.ss === 'helix') {
28955
- // bHelixSegment = true;
28956
- // }
29033
+ let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29034
+ pntsCA = pnts_clrs[0];
28957
29035
 
28958
- //if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
28959
- // 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
29036
+ this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
28960
29037
 
28961
- if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || bBrokenSs || (resid == lastResid && atom.ss != 'coil')) && pnts[0].length > 0) {
28962
- let atomName = 'CA';
28963
-
28964
- let prevone = [], nexttwo = [];
29038
+ for(let i = 0, il = prevCOArray.length; i < il; ++i) {
29039
+ prevCOArray[i].normalize();
29040
+ }
28965
29041
 
28966
- if(isNaN(ic.atoms[prevAtomid].resi)) {
28967
- prevone = [];
28968
- }
28969
- else {
28970
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
28971
- let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
28972
- prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
28973
- }
29042
+ let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29043
+ prevCOArray = pnts_clrs2[0];
28974
29044
 
28975
- if(!isNaN(ic.atoms[prevAtomid].resi)) {
28976
- let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
28977
- let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
28978
- if(nextoneCoord !== undefined) {
28979
- nexttwo.push(nextoneCoord);
28980
- }
29045
+ colors = pnts_clrs[2];
29046
+ }
29047
+ else {
28981
29048
 
28982
- let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
28983
- let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
28984
- if(nexttwoCoord !== undefined) {
28985
- nexttwo.push(nexttwoCoord);
28986
- }
28987
- }
29049
+ if(!bNoSmoothen) {
29050
+ //var bExtendLastRes = true;
29051
+ let bExtendLastRes = false;
29052
+ let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29053
+ let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29054
+ p0 = pnts_clrs0[0];
29055
+ p1 = pnts_clrs1[0];
29056
+ colors = pnts_clrs0[2];
29057
+ }
29058
+ if (p0.length < 2) return;
28988
29059
 
28989
- // include the current residue
28990
- if(!bBrokenSs) {
28991
- // assign the current joint residue to the previous segment
28992
- if(bHighlight === 1 || bHighlight === 2) {
28993
- colors.push(ic.hColor);
28994
- }
28995
- else {
28996
- //colors.push(atom.color);
28997
- colors.push(prevColor);
28998
- }
29060
+ this.setCalphaDrawnCoord(p0, div, calphaIdArray);
29061
+ }
28999
29062
 
29000
- if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
29001
- strandWidth = 0; // make the arrow end sharp
29002
- }
29003
- else if(ss === 'coil' && atom.ssbegin) {
29004
- strandWidth = coilWidth;
29005
- }
29006
- else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
29007
- strandWidth = coilWidth;
29008
- }
29009
- else { // use the ss from the previous residue
29010
- strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
29011
- }
29063
+ if(bHighlight === 1) {
29064
+ //mesh = new THREE.Mesh(geo, ic.matShader);
29012
29065
 
29013
- let O, oldCA, resSpan = 4;
29014
- if(atom.name === 'O') {
29015
- O = currentO.clone();
29016
- O.sub(currentCA);
29017
- }
29018
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
29019
- if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29020
- O = currentCA.clone();
29021
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
29022
- //O.sub(oldCA);
29023
- oldCA.sub(O);
29024
- }
29025
- else {
29026
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29027
- }
29028
- }
29066
+ let radius = ic.coilWidth / 2;
29067
+ //var radiusSegments = 8;
29068
+ let radiusSegments = 4; // save memory
29069
+ let closed = false;
29029
29070
 
29030
- O.normalize(); // can be omitted for performance
29031
- O.multiplyScalar(strandWidth);
29032
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
29033
- prevCO = O;
29071
+ if(positions !== undefined) {
29072
+ let currPos, prevPos;
29073
+ let currP0 = [], currP1 = [];
29034
29074
 
29035
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
29036
- let delta = -1 + numM1Inv2 * j;
29037
- let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
29038
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
29039
- pnts[j].push(v);
29040
- }
29075
+ for(let i = 0, il = p0.length; i < il; ++i) {
29076
+ currPos = positions[i];
29041
29077
 
29042
- atomid = atom.serial;
29078
+ if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
29079
+ // first tube
29080
+ let geometry0 = new THREE.TubeGeometry(
29081
+ new THREE.CatmullRomCurve3(currP0), // path
29082
+ currP0.length, // segments
29083
+ radius,
29084
+ radiusSegments,
29085
+ closed
29086
+ );
29043
29087
 
29044
- pntsCA.push(currentCA);
29045
- prevCOArray.push(prevCO);
29088
+ let mesh = new THREE.Mesh(geometry0, ic.matShader);
29089
+ mesh.renderOrder = ic.renderOrderPicking;
29090
+ //ic.mdlPicking.add(mesh);
29091
+ ic.mdl.add(mesh);
29046
29092
 
29047
- // 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.
29048
- //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
29049
- if(atoms.hasOwnProperty(atomid)) {
29050
- bShowArray.push(atom.resi);
29051
- calphaIdArray.push(calphaid);
29052
- }
29053
- else {
29054
- bShowArray.push(0);
29055
- calphaIdArray.push(0);
29056
- }
29057
- }
29058
-
29059
- // draw the current segment
29060
- for (let j = 0; !fill && j < num; ++j) {
29061
- if(bSheetSegment) {
29062
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29063
- }
29064
- else if(bHelixSegment) {
29065
- if(bFullAtom) {
29066
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
29067
- }
29068
- else {
29069
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29070
- }
29071
- }
29072
- }
29073
- if (fill) {
29074
- if(bSheetSegment) {
29075
- let start = 0, end = num - 1;
29076
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29077
- }
29078
- else if(bHelixSegment) {
29079
- if(bFullAtom) {
29080
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29081
- }
29082
- else {
29083
- let start = 0, end = num - 1;
29084
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29085
- }
29086
- }
29087
- else {
29088
- if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
29089
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29090
- }
29091
- }
29092
- }
29093
- for (let k = 0; k < num; ++k) pnts[k] = [];
29094
-
29095
- colors = [];
29096
- pntsCA = [];
29097
- prevCOArray = [];
29098
- bShowArray = [];
29099
- calphaIdArray = [];
29100
- bSheetSegment = false;
29101
- bHelixSegment = false;
29102
- } // end if (atom.ssbegin || atom.ssend)
29103
-
29104
- // end of a chain, or end of selection
29105
- if ((currentChain !== atom.chain
29106
- || ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi)
29107
- // || (drawnResidueCount === totalResidueCount - 1)
29108
- // || bBrokenSs
29109
- || (resid == lastResid && atom.ss != 'coil')
29110
- ) && pnts[0].length > 0) {
29111
- //if ((currentChain !== atom.chain) && pnts[0].length > 0) {
29112
-
29113
- let atomName = 'CA';
29114
-
29115
- let prevone = [], nexttwo = [];
29116
- if(isNaN(ic.atoms[prevAtomid].resi)) {
29117
- prevone = [];
29118
- }
29119
- else {
29120
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
29121
- ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
29122
- }
29123
-
29124
- for (let j = 0; !fill && j < num; ++j) {
29125
- if(bSheetSegment) {
29126
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29127
- }
29128
- else if(bHelixSegment) {
29129
- if(bFullAtom) {
29130
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
29131
- }
29132
- else {
29133
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29134
- }
29135
- }
29136
- }
29137
- if (fill) {
29138
- if(bSheetSegment) {
29139
- let start = 0, end = num - 1;
29140
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29141
- }
29142
- else if(bHelixSegment) {
29143
- if(bFullAtom) {
29144
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29145
- }
29146
- else {
29147
- let start = 0, end = num - 1;
29148
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29149
- }
29150
- }
29151
- }
29152
-
29153
- for (let k = 0; k < num; ++k) pnts[k] = [];
29154
- colors = [];
29155
- pntsCA = [];
29156
- prevCOArray = [];
29157
- bShowArray = [];
29158
- calphaIdArray = [];
29159
- bSheetSegment = false;
29160
- bHelixSegment = false;
29161
- }
29162
-
29163
- currentChain = atom.chain;
29164
- currentResi = atom.resi;
29165
- ss = atom.ss;
29166
- ssend = atom.ssend;
29167
- prevAtomid = atom.serial;
29168
- if(atoms.hasOwnProperty(atom.serial)) prevAtomSelected = atom;
29169
- prevResi = atom.resi;
29170
-
29171
- prevCalphaid = calphaid;
29172
-
29173
- // only update when atom.name === 'O'
29174
- prevCoorCA = currentCA;
29175
- prevCoorO = atom.coord;
29176
- prevColor = currentColor;
29177
- } // end if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA') ) {
29178
- } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
29179
- } // end for
29180
-
29181
- caArray = [];
29182
-
29183
- // ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
29184
- // draw all atoms in tubes and assign zero radius when the residue is not coil
29185
- ic.tubeCls.createTube(atomsAdjust, 'CA', coilWidth, bHighlight);
29186
- pnts = {};
29187
- }
29188
-
29189
- getOneExtraResidue(residueHash) { let ic = this.icn3d, me = ic.icn3dui;
29190
- let atomsAdjust = {};
29191
-
29192
- for(let resid in residueHash) {
29193
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[resid]);
29194
-
29195
- let residNcbi = ic.resid2ncbi[resid];
29196
- let resiNcbi = residNcbi.substr(residNcbi.lastIndexOf('_') + 1);
29197
-
29198
- let nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) + 1);
29199
- let nextResid = ic.ncbi2resid[nextResidNcbi];
29200
-
29201
- if(!nextResid) {
29202
- nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) - 1);
29203
- nextResid = ic.ncbi2resid[nextResidNcbi];
29204
- }
29205
-
29206
- if(nextResid) atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[nextResid]);
29207
- }
29208
-
29209
- return atomsAdjust;
29210
- }
29211
-
29212
- /*
29213
- getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
29214
- let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
29215
- let firstAtom, lastAtom;
29216
- let index = 0, length = Object.keys(atoms).length;
29217
-
29218
- let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
29219
- for(let serial in atoms) {
29220
- currChain = atoms[serial].structure + '_' + atoms[serial].chain;
29221
- currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
29222
- currAtom = atoms[serial];
29223
-
29224
- if(prevChain === undefined) firstAtom = atoms[serial];
29225
-
29226
- if( (currChain !== prevChain && prevChain !== undefined)
29227
- || (currResi !== prevResi && ic.resid2ncbi[currResi] !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) || index === length - 1) {
29228
- if( (currChain !== prevChain && prevChain !== undefined)
29229
- || (currResi !== prevResi && currResi !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) ) {
29230
- lastAtom = prevAtom;
29231
- }
29232
- else if(index === length - 1) {
29233
- lastAtom = currAtom;
29234
- }
29235
-
29236
- // fill the beginning
29237
- let beginResi = firstAtom.resi;
29238
- if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
29239
- for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
29240
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
29241
- if(!ic.residues.hasOwnProperty(residueid)) break;
29242
-
29243
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
29244
-
29245
- if(atom.ss === firstAtom.ss && atom.ssbegin) {
29246
- beginResi = atom.resi;
29247
- break;
29248
- }
29249
- }
29250
-
29251
- for(let i = beginResi; i < firstAtom.resi; ++i) {
29252
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
29253
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29254
- ic.atoms));
29255
- }
29256
- }
29257
-
29258
- // add one extra residue for coils between strands/helix
29259
- if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
29260
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
29261
- if(ic.residues.hasOwnProperty(residueid)) {
29262
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29263
- ic.atoms));
29264
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
29265
- }
29266
- }
29267
-
29268
- // fill the end
29269
- let endResi = lastAtom.resi;
29270
- // 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.
29271
-
29272
- if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
29273
-
29274
- let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
29275
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
29276
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
29277
- if(!ic.residues.hasOwnProperty(residueid)) break;
29278
-
29279
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
29280
-
29281
- if(atom.ss === lastAtom.ss && atom.ssend) {
29282
- endResi = atom.resi;
29283
- break;
29284
- }
29285
- }
29286
-
29287
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
29288
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
29289
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29290
- ic.atoms));
29291
- }
29292
- }
29293
-
29294
- // add one extra residue for coils between strands/helix
29295
- if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
29296
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
29297
- if(ic.residues.hasOwnProperty(residueid)) {
29298
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29299
- ic.atoms));
29300
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
29301
- }
29302
- }
29303
-
29304
- // reset notshow
29305
- if(lastAtom.notshow) lastAtom.notshow = undefined;
29306
-
29307
- firstAtom = currAtom;
29308
- }
29309
-
29310
- prevChain = currChain;
29311
- prevResi = currResi;
29312
- prevAtom = currAtom;
29313
-
29314
- ++index;
29315
- }
29316
-
29317
- return atomsAdjust;
29318
- }
29319
- */
29320
- }
29321
-
29322
- /**
29323
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29324
- */
29325
-
29326
- class Strip {
29327
- constructor(icn3d) {
29328
- this.icn3d = icn3d;
29329
- }
29330
-
29331
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29332
- createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
29333
- calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
29334
- if(me.bNode) return;
29335
-
29336
- if (p0.length < 2) return;
29337
- div = div || ic.axisDIV;
29338
-
29339
- // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
29340
- if(pntsCA && ic.bDoublecolor) {
29341
- let bExtendLastRes = false; //true;
29342
-
29343
- let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29344
- pntsCA = pnts_clrs[0];
29345
-
29346
- this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
29347
-
29348
- for(let i = 0, il = prevCOArray.length; i < il; ++i) {
29349
- prevCOArray[i].normalize();
29350
- }
29351
-
29352
- let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29353
- prevCOArray = pnts_clrs2[0];
29354
-
29355
- colors = pnts_clrs[2];
29356
- }
29357
- else {
29358
-
29359
- if(!bNoSmoothen) {
29360
- //var bExtendLastRes = true;
29361
- let bExtendLastRes = false;
29362
- let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29363
- let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29364
- p0 = pnts_clrs0[0];
29365
- p1 = pnts_clrs1[0];
29366
- colors = pnts_clrs0[2];
29367
- }
29368
- if (p0.length < 2) return;
29369
-
29370
- this.setCalphaDrawnCoord(p0, div, calphaIdArray);
29371
- }
29372
-
29373
- if(bHighlight === 1) {
29374
- //mesh = new THREE.Mesh(geo, ic.matShader);
29375
-
29376
- let radius = ic.coilWidth / 2;
29377
- //var radiusSegments = 8;
29378
- let radiusSegments = 4; // save memory
29379
- let closed = false;
29380
-
29381
- if(positions !== undefined) {
29382
- let currPos, prevPos;
29383
- let currP0 = [], currP1 = [];
29384
-
29385
- for(let i = 0, il = p0.length; i < il; ++i) {
29386
- currPos = positions[i];
29387
-
29388
- if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
29389
- // first tube
29390
- let geometry0 = new THREE.TubeGeometry(
29391
- new THREE.CatmullRomCurve3(currP0), // path
29392
- currP0.length, // segments
29393
- radius,
29394
- radiusSegments,
29395
- closed
29396
- );
29397
-
29398
- let mesh = new THREE.Mesh(geometry0, ic.matShader);
29399
- mesh.renderOrder = ic.renderOrderPicking;
29400
- //ic.mdlPicking.add(mesh);
29401
- ic.mdl.add(mesh);
29402
-
29403
- ic.prevHighlightObjects.push(mesh);
29093
+ ic.prevHighlightObjects.push(mesh);
29404
29094
 
29405
29095
  geometry0 = null;
29406
29096
 
@@ -29687,6 +29377,7 @@ class Tube {
29687
29377
  atom.structure + '_' + atom.chain + '_' + (parseInt(atom.resi) - 1).toString();
29688
29378
 
29689
29379
  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
29380
+ || (prevAtom.ssbegin) // e.g., https://www.ncbi.nlm.nih.gov/Structure/icn3d/?pdbid=7JO8 where a beta sheet has just two residues
29690
29381
  // || (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')
29691
29382
  || (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))
29692
29383
  ) ) {
@@ -29779,7 +29470,7 @@ class Tube {
29779
29470
  }
29780
29471
 
29781
29472
  // draw all atoms in tubes and assign zero radius when the residue is not coil
29782
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29473
+ // if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29783
29474
 
29784
29475
  //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29785
29476
  radii.push(radiusFinal);
@@ -29922,224 +29613,770 @@ class Tube {
29922
29613
  radiusFinal = this.getRadius(radius, atom);
29923
29614
  }
29924
29615
 
29925
- // draw all atoms in tubes and assign zero radius when the residue is not coil
29926
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29616
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29617
+ if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29618
+
29619
+ //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29620
+ radii.push(radiusFinal);
29621
+
29622
+ colors.push(atom.color);
29623
+ // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
29624
+ if(index === 1) colors[colors.length - 2] = atom.color;
29625
+
29626
+ currentChain = atom.chain;
29627
+ currentResi = atom.resi;
29628
+
29629
+ let scale = 1.2;
29630
+ if(bHighlight === 2 && !atom.ssbegin) {
29631
+ ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
29632
+ }
29633
+
29634
+ ++index;
29635
+
29636
+ prevAtom = atom;
29637
+ }
29638
+ }
29639
+
29640
+ if(bHighlight !== 2) {
29641
+ pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
29642
+ }
29643
+
29644
+ for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
29645
+ let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
29646
+ let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
29647
+ let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
29648
+ let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
29649
+ let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
29650
+
29651
+ this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
29652
+ }
29653
+
29654
+ pnts_colors_radii_prevone_nexttwo = [];
29655
+ }
29656
+ */
29657
+
29658
+ getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
29659
+ let pos = resid.lastIndexOf('_');
29660
+ let resi = resid.substr(pos + 1);
29661
+ let chainid = resid.substr(0, pos);
29662
+
29663
+ let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
29664
+
29665
+ return radiusFinal;
29666
+ };
29667
+
29668
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29669
+ createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29670
+ if(me.bNode) return;
29671
+
29672
+ if (_pnts.length < 2) return;
29673
+
29674
+ let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
29675
+ let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
29676
+ //var geo = new THREE.Geometry();
29677
+ let geo = new THREE.BufferGeometry();
29678
+ let verticeArray = [], colorArray = [],indexArray = [], color;
29679
+ let offset = 0, offset2 = 0, offset3 = 0;
29680
+
29681
+ let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
29682
+
29683
+ let pnts = pnts_clrs[0];
29684
+ colors = pnts_clrs[2];
29685
+
29686
+ let constRadiius;
29687
+ // a threshold to stop drawing the tube if it's less than this ratio of radius
29688
+ let thresholdRatio = 1; //0.9;
29689
+
29690
+ let prevAxis1 = new THREE.Vector3(), prevAxis2;
29691
+ for (let i = 0, lim = pnts.length; i < lim; ++i) {
29692
+ let r, idx = (i - 1) * axisDivInv;
29693
+
29694
+ if (i === 0) {
29695
+ r = radii[0];
29696
+ if(r > 0) constRadiius = r;
29697
+ }
29698
+ else {
29699
+ if (idx % 1 === 0) {
29700
+ r = radii[idx];
29701
+ if(r > 0) constRadiius = r;
29702
+ }
29703
+ else {
29704
+ let floored = Math.floor(idx);
29705
+ let tmp = idx - floored;
29706
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29707
+ // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
29708
+ r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
29709
+
29710
+ // a threshold to stop drawing the tube if it's less than this ratio of radius.
29711
+ // The extra bit of tube connects coil with strands or helices
29712
+ if(!bNonCoil) {
29713
+ if(r < thresholdRatio * constRadiius) {
29714
+ r = 0;
29715
+ }
29716
+ // else if(r < constRadiius) {
29717
+ // r *= 0.5; // use small radius for the connection between coild and sheets/helices
29718
+ // }
29719
+ }
29720
+ }
29721
+ }
29722
+ let delta, axis1, axis2;
29723
+ if (i < lim - 1) {
29724
+ delta = pnts[i].clone().sub(pnts[i + 1]);
29725
+ axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
29726
+ axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
29727
+ // let dir = 1, offset = 0;
29728
+ if (prevAxis1.dot(axis1) < 0) {
29729
+ axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
29730
+ }
29731
+ prevAxis1 = axis1; prevAxis2 = axis2;
29732
+ } else {
29733
+ axis1 = prevAxis1; axis2 = prevAxis2;
29734
+ }
29735
+ for (let j = 0; j < circleDiv; ++j) {
29736
+ let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
29737
+ let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
29738
+ verticeArray[offset++] = point.x;
29739
+ verticeArray[offset++] = point.y;
29740
+ verticeArray[offset++] = point.z;
29741
+
29742
+ color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
29743
+ colorArray[offset2++] = color.r;
29744
+ colorArray[offset2++] = color.g;
29745
+ colorArray[offset2++] = color.b;
29746
+ }
29747
+ }
29748
+ let offsetTmp = 0, nComp = 3;
29749
+ for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
29750
+ let reg = 0;
29751
+ //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
29752
+ //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
29753
+ let pos = offsetTmp * nComp;
29754
+ let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29755
+ pos = (offsetTmp + circleDiv) * nComp;
29756
+ let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29757
+ pos = (offsetTmp + circleDiv + 1) * nComp;
29758
+ let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29759
+
29760
+ let r1 = point1.clone().sub(point2).lengthSq();
29761
+ let r2 = point1.clone().sub(point3).lengthSq();
29762
+ if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
29763
+ //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
29764
+ //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
29765
+ //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
29766
+ indexArray[offset3++] = offsetTmp + j;
29767
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29768
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29769
+
29770
+ //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
29771
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29772
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29773
+ indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
29774
+ }
29775
+ offsetTmp += circleDiv;
29776
+ }
29777
+
29778
+ geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
29779
+ geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
29780
+
29781
+ geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
29782
+ //geo.setIndex(indexArray);
29783
+
29784
+ //geo.computeFaceNormals();
29785
+ //geo.computeVertexNormals(false);
29786
+ geo.computeVertexNormals();
29787
+
29788
+ let mesh;
29789
+ if(bHighlight === 2) {
29790
+ //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 }));
29791
+ 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 }));
29792
+
29793
+ if(ic.mdl) ic.mdl.add(mesh);
29794
+ }
29795
+ else if(bHighlight === 1) {
29796
+ mesh = new THREE.Mesh(geo, ic.matShader);
29797
+ mesh.renderOrder = ic.renderOrderPicking;
29798
+ //ic.mdlPicking.add(mesh);
29799
+ if(ic.mdl) ic.mdl.add(mesh);
29800
+ }
29801
+ else {
29802
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
29803
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
29804
+
29805
+ if(ic.mdl) ic.mdl.add(mesh);
29806
+ }
29807
+
29808
+ if(bHighlight === 1 || bHighlight === 2) {
29809
+ ic.prevHighlightObjects.push(mesh);
29810
+ }
29811
+ else {
29812
+ ic.objects.push(mesh);
29813
+ }
29814
+ }
29815
+
29816
+ getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
29817
+ let radiusFinal = radius;
29818
+ if(radius) {
29819
+ radiusFinal = radius;
29820
+ }
29821
+ else {
29822
+ if(atom.b > 0 && atom.b <= 100) {
29823
+ radiusFinal = atom.b * 0.01;
29824
+ }
29825
+ else if(atom.b > 100) {
29826
+ radiusFinal = 100 * 0.01;
29827
+ }
29828
+ else {
29829
+ radiusFinal = ic.coilWidth;
29830
+ }
29831
+ }
29832
+
29833
+ return radiusFinal;
29834
+ }
29835
+ }
29836
+
29837
+ /**
29838
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29839
+ */
29840
+
29841
+ class Strand {
29842
+ constructor(icn3d) {
29843
+ this.icn3d = icn3d;
29844
+ }
29845
+
29846
+ // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29847
+ //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
29848
+ //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
29849
+ //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
29850
+ //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
29851
+ //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
29852
+ //with bHighlight=1 and 3D objects with bHighlight=2.
29853
+ createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
29854
+ if(me.bNode) return;
29855
+
29856
+ let bRibbon = fill ? true: false;
29857
+
29858
+ // when highlight, the input atoms may only include part of sheet or helix
29859
+ // include the whole sheet or helix when highlighting
29860
+ let atomsAdjust = {};
29861
+
29862
+ //if( (bHighlight === 1 || bHighlight === 2) && !ic.bAllAtoms) {
29863
+ //if( !ic.bAllAtoms) {
29864
+ if( Object.keys(atoms).length < Object.keys(ic.atoms).length) {
29865
+ atomsAdjust = this.getSSExpandedAtoms(atoms);
29866
+ }
29867
+ else {
29868
+ atomsAdjust = atoms;
29869
+ }
29870
+
29871
+ if(bHighlight === 2) {
29872
+ if(fill) {
29873
+ fill = false;
29874
+ num = null;
29875
+ div = null;
29876
+ coilWidth = null;
29877
+ helixSheetWidth = null;
29878
+ thickness = undefined;
29879
+ }
29880
+ else {
29881
+ fill = true;
29882
+ num = 2;
29883
+ div = undefined;
29884
+ coilWidth = undefined;
29885
+ helixSheetWidth = undefined;
29886
+ thickness = ic.ribbonthickness;
29887
+ }
29888
+ }
29889
+
29890
+ num = num || ic.strandDIV;
29891
+ div = div || ic.axisDIV;
29892
+ coilWidth = coilWidth || ic.coilWidth;
29893
+ doNotSmoothen = doNotSmoothen || false;
29894
+ helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
29895
+ let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
29896
+ let pntsCA = [];
29897
+ let prevCOArray = [];
29898
+ let bShowArray = [];
29899
+ let calphaIdArray = []; // used to store one of the final positions drawn in 3D
29900
+ let colors = [];
29901
+ let currentChain, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
29902
+ let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevResi = null, calphaid = null, prevCalphaid = null;
29903
+ let strandWidth, bSheetSegment = false, bHelixSegment = false;
29904
+ let atom, tubeAtoms = {};
29905
+
29906
+ // test the first 30 atoms to see whether only C-alpha is available
29907
+ ic.bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atomsAdjust); //, 'CA');
29908
+
29909
+ // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
29910
+ let residueHash = {};
29911
+ for(let i in atomsAdjust) {
29912
+ let atom = atomsAdjust[i];
29913
+
29914
+ let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
29915
+ residueHash[residueid] = 1;
29916
+ }
29917
+ let totalResidueCount = Object.keys(residueHash).length;
29918
+
29919
+ let drawnResidueCount = 0;
29920
+
29921
+ let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
29922
+
29923
+ let caArray = []; // record all C-alpha atoms to predict the helix
29924
+
29925
+ for (let i in atomsAdjust) {
29926
+ atom = atomsAdjust[i];
29927
+ if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
29928
+ // "CA" has to appear before "O"
29929
+
29930
+ if (atom.name === 'CA') {
29931
+ if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) {
29932
+ tubeAtoms[i] = atom;
29933
+ }
29934
+
29935
+ currentCA = atom.coord;
29936
+ currentColor = atom.color;
29937
+ calphaid = atom.serial;
29938
+
29939
+ caArray.push(atom.serial);
29940
+ }
29941
+
29942
+ if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA')) {
29943
+ if(currentCA === null || currentCA === undefined) {
29944
+ currentCA = atom.coord;
29945
+ currentColor = atom.color;
29946
+ calphaid = atom.serial;
29947
+ }
29948
+
29949
+ if(atom.name === 'O') {
29950
+ currentO = atom.coord;
29951
+ }
29952
+ // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
29953
+ let bSameChain = true;
29954
+ // if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) {
29955
+ if (currentChain !== atom.chain) {
29956
+ bSameChain = false;
29957
+ }
29958
+
29959
+ if(atom.ssend && atom.ss === 'sheet') {
29960
+ bSheetSegment = true;
29961
+ }
29962
+ else if(atom.ssend && atom.ss === 'helix') {
29963
+ bHelixSegment = true;
29964
+ }
29965
+
29966
+ // assign the previous residue
29967
+ if(prevCoorO) {
29968
+ if(bHighlight === 1 || bHighlight === 2) {
29969
+ colors.push(ic.hColor);
29970
+ }
29971
+ else {
29972
+ colors.push(prevColor);
29973
+ }
29974
+
29975
+ if(ss !== 'coil' && atom.ss === 'coil') {
29976
+ strandWidth = coilWidth;
29977
+ }
29978
+ else if(ssend && atom.ssbegin) { // a transition between two ss
29979
+ strandWidth = coilWidth;
29980
+ }
29981
+ else {
29982
+ strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
29983
+ }
29984
+
29985
+ let O, oldCA, resSpan = 4;
29986
+ if(atom.name === 'O') {
29987
+ O = prevCoorO.clone();
29988
+ if(prevCoorCA !== null && prevCoorCA !== undefined) {
29989
+ O.sub(prevCoorCA);
29990
+ }
29991
+ else {
29992
+ prevCoorCA = prevCoorO.clone();
29993
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29994
+ O = prevCoorCA.clone();
29995
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
29996
+ //O.sub(oldCA);
29997
+ oldCA.sub(O);
29998
+ }
29999
+ else {
30000
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30001
+ }
30002
+ }
30003
+ }
30004
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
30005
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30006
+ O = prevCoorCA.clone();
30007
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
30008
+ //O.sub(oldCA);
30009
+ oldCA.sub(O);
30010
+ }
30011
+ else {
30012
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30013
+ }
30014
+ }
30015
+
30016
+ O.normalize(); // can be omitted for performance
30017
+ O.multiplyScalar(strandWidth);
30018
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
30019
+ prevCO = O;
30020
+
30021
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
30022
+ let delta = -1 + numM1Inv2 * j;
30023
+ let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
30024
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
30025
+ pnts[j].push(v);
30026
+ }
30027
+
30028
+ pntsCA.push(prevCoorCA);
30029
+ prevCOArray.push(prevCO);
30030
+
30031
+ if(atoms.hasOwnProperty(prevAtomid)) {
30032
+ bShowArray.push(prevResi);
30033
+ calphaIdArray.push(prevCalphaid);
30034
+ }
30035
+ else {
30036
+ bShowArray.push(0);
30037
+ calphaIdArray.push(0);
30038
+ }
30039
+
30040
+ ++drawnResidueCount;
30041
+ }
30042
+
30043
+ let maxDist = 6.0;
30044
+ 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);
30045
+ // The following code didn't work to select one residue
30046
+ // 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);
30047
+
30048
+ // if(bBrokenSs && atom.ss === 'sheet') {
30049
+ // bSheetSegment = true;
30050
+ // }
30051
+ // else if(bBrokenSs && atom.ss === 'helix') {
30052
+ // bHelixSegment = true;
30053
+ // }
30054
+
30055
+ if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
30056
+ let atomName = 'CA';
30057
+
30058
+ let prevone = [], nexttwo = [];
30059
+
30060
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
30061
+ prevone = [];
30062
+ }
30063
+ else {
30064
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30065
+ let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30066
+ prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
30067
+ }
30068
+
30069
+ if(!isNaN(ic.atoms[prevAtomid].resi)) {
30070
+ let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
30071
+ let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
30072
+ if(nextoneCoord !== undefined) {
30073
+ nexttwo.push(nextoneCoord);
30074
+ }
30075
+
30076
+ let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
30077
+ let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
30078
+ if(nexttwoCoord !== undefined) {
30079
+ nexttwo.push(nexttwoCoord);
30080
+ }
30081
+ }
30082
+
30083
+ if(!bBrokenSs) { // include the current residue
30084
+ // assign the current joint residue to the previous segment
30085
+ if(bHighlight === 1 || bHighlight === 2) {
30086
+ colors.push(ic.hColor);
30087
+ }
30088
+ else {
30089
+ //colors.push(atom.color);
30090
+ colors.push(prevColor);
30091
+ }
30092
+
30093
+ if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
30094
+ strandWidth = 0; // make the arrow end sharp
30095
+ }
30096
+ else if(ss === 'coil' && atom.ssbegin) {
30097
+ strandWidth = coilWidth;
30098
+ }
30099
+ else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
30100
+ strandWidth = coilWidth;
30101
+ }
30102
+ else { // use the ss from the previous residue
30103
+ strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
30104
+ }
30105
+
30106
+ let O, oldCA, resSpan = 4;
30107
+ if(atom.name === 'O') {
30108
+ O = currentO.clone();
30109
+ O.sub(currentCA);
30110
+ }
30111
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
30112
+ if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30113
+ O = currentCA.clone();
30114
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
30115
+ //O.sub(oldCA);
30116
+ oldCA.sub(O);
30117
+ }
30118
+ else {
30119
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30120
+ }
30121
+ }
30122
+
30123
+ O.normalize(); // can be omitted for performance
30124
+ O.multiplyScalar(strandWidth);
30125
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
30126
+ prevCO = O;
30127
+
30128
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
30129
+ let delta = -1 + numM1Inv2 * j;
30130
+ let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
30131
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
30132
+ pnts[j].push(v);
30133
+ }
30134
+
30135
+ atomid = atom.serial;
30136
+
30137
+ pntsCA.push(currentCA);
30138
+ prevCOArray.push(prevCO);
30139
+
30140
+ // 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.
30141
+ //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
30142
+ if(atoms.hasOwnProperty(atomid)) {
30143
+ bShowArray.push(atom.resi);
30144
+ calphaIdArray.push(calphaid);
30145
+ }
30146
+ else {
30147
+ bShowArray.push(0);
30148
+ calphaIdArray.push(0);
30149
+ }
30150
+ }
30151
+
30152
+ // draw the current segment
30153
+ for (let j = 0; !fill && j < num; ++j) {
30154
+ if(bSheetSegment) {
30155
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30156
+ }
30157
+ else if(bHelixSegment) {
30158
+ if(bFullAtom) {
30159
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
30160
+ }
30161
+ else {
30162
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30163
+ }
30164
+ }
30165
+ }
30166
+ if (fill) {
30167
+ if(bSheetSegment) {
30168
+ let start = 0, end = num - 1;
30169
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30170
+ }
30171
+ else if(bHelixSegment) {
30172
+ if(bFullAtom) {
30173
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30174
+ }
30175
+ else {
30176
+ let start = 0, end = num - 1;
30177
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30178
+ }
30179
+ }
30180
+ else {
30181
+ if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
30182
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30183
+ }
30184
+ }
30185
+ }
30186
+ for (let k = 0; k < num; ++k) pnts[k] = [];
30187
+
30188
+ colors = [];
30189
+ pntsCA = [];
30190
+ prevCOArray = [];
30191
+ bShowArray = [];
30192
+ calphaIdArray = [];
30193
+ bSheetSegment = false;
30194
+ bHelixSegment = false;
30195
+ } // end if (atom.ssbegin || atom.ssend)
30196
+
30197
+ // end of a chain
30198
+ // if ((currentChain !== atom.chain || currentResi + 1 !== atom.resi) && pnts[0].length > 0) {
30199
+ if ((currentChain !== atom.chain) && pnts[0].length > 0) {
29927
30200
 
29928
- //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29929
- radii.push(radiusFinal);
30201
+ let atomName = 'CA';
29930
30202
 
29931
- colors.push(atom.color);
29932
- // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
29933
- if(index === 1) colors[colors.length - 2] = atom.color;
30203
+ let prevone = [], nexttwo = [];
30204
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
30205
+ prevone = [];
30206
+ }
30207
+ else {
30208
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30209
+ ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30210
+ }
29934
30211
 
29935
- currentChain = atom.chain;
29936
- currentResi = atom.resi;
30212
+ for (let j = 0; !fill && j < num; ++j) {
30213
+ if(bSheetSegment) {
30214
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30215
+ }
30216
+ else if(bHelixSegment) {
30217
+ if(bFullAtom) {
30218
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
30219
+ }
30220
+ else {
30221
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30222
+ }
30223
+ }
30224
+ }
30225
+ if (fill) {
30226
+ if(bSheetSegment) {
30227
+ let start = 0, end = num - 1;
30228
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30229
+ }
30230
+ else if(bHelixSegment) {
30231
+ if(bFullAtom) {
30232
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30233
+ }
30234
+ else {
30235
+ let start = 0, end = num - 1;
30236
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30237
+ }
30238
+ }
30239
+ }
29937
30240
 
29938
- let scale = 1.2;
29939
- if(bHighlight === 2 && !atom.ssbegin) {
29940
- ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
30241
+ for (let k = 0; k < num; ++k) pnts[k] = [];
30242
+ colors = [];
30243
+ pntsCA = [];
30244
+ prevCOArray = [];
30245
+ bShowArray = [];
30246
+ calphaIdArray = [];
30247
+ bSheetSegment = false;
30248
+ bHelixSegment = false;
29941
30249
  }
29942
30250
 
29943
- ++index;
30251
+ currentChain = atom.chain;
30252
+ ss = atom.ss;
30253
+ ssend = atom.ssend;
30254
+ prevAtomid = atom.serial;
30255
+ prevResi = atom.resi;
29944
30256
 
29945
- prevAtom = atom;
29946
- }
29947
- }
30257
+ prevCalphaid = calphaid;
29948
30258
 
29949
- if(bHighlight !== 2) {
29950
- pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
29951
- }
30259
+ // only update when atom.name === 'O'
30260
+ prevCoorCA = currentCA;
30261
+ prevCoorO = atom.coord;
30262
+ prevColor = currentColor;
30263
+ } // end if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA') ) {
30264
+ } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
30265
+ } // end for
29952
30266
 
29953
- for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
29954
- let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
29955
- let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
29956
- let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
29957
- let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
29958
- let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
30267
+ caArray = [];
29959
30268
 
29960
- this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
29961
- }
30269
+ ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
29962
30270
 
29963
- pnts_colors_radii_prevone_nexttwo = [];
30271
+ tubeAtoms = {};
30272
+ pnts = {};
29964
30273
  }
29965
- */
29966
-
29967
- getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
29968
- let pos = resid.lastIndexOf('_');
29969
- let resi = resid.substr(pos + 1);
29970
- let chainid = resid.substr(0, pos);
29971
-
29972
- let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
29973
-
29974
- return radiusFinal;
29975
- };
29976
-
29977
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29978
- createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29979
- if(me.bNode) return;
29980
-
29981
- if (_pnts.length < 2) return;
29982
30274
 
29983
- let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
29984
- let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
29985
- //var geo = new THREE.Geometry();
29986
- let geo = new THREE.BufferGeometry();
29987
- let verticeArray = [], colorArray = [],indexArray = [], color;
29988
- let offset = 0, offset2 = 0, offset3 = 0;
30275
+ getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
30276
+ let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
30277
+ let firstAtom, lastAtom;
30278
+ let index = 0, length = Object.keys(atoms).length;
29989
30279
 
29990
- let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
30280
+ let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
30281
+ for(let serial in atoms) {
30282
+ currChain = atoms[serial].structure + '_' + atoms[serial].chain;
30283
+ currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
30284
+ currAtom = atoms[serial];
29991
30285
 
29992
- let pnts = pnts_clrs[0];
29993
- colors = pnts_clrs[2];
30286
+ if(prevChain === undefined) firstAtom = atoms[serial];
29994
30287
 
29995
- let constRadiius;
29996
- // a threshold to stop drawing the tube if it's less than this ratio of radius
29997
- let thresholdRatio = 1; //0.9;
30288
+ if( (currChain !== prevChain && prevChain !== undefined)
30289
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) || index === length - 1) {
30290
+ if( (currChain !== prevChain && prevChain !== undefined)
30291
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) ) {
30292
+ lastAtom = prevAtom;
30293
+ }
30294
+ else if(index === length - 1) {
30295
+ lastAtom = currAtom;
30296
+ }
29998
30297
 
29999
- let prevAxis1 = new THREE.Vector3(), prevAxis2;
30000
- for (let i = 0, lim = pnts.length; i < lim; ++i) {
30001
- let r, idx = (i - 1) * axisDivInv;
30298
+ // fill the beginning
30299
+ let beginResi = firstAtom.resi;
30300
+ if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
30301
+ for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
30302
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30303
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30002
30304
 
30003
- if (i === 0) {
30004
- r = radii[0];
30005
- if(r > 0) constRadiius = r;
30006
- }
30007
- else {
30008
- if (idx % 1 === 0) {
30009
- r = radii[idx];
30010
- if(r > 0) constRadiius = r;
30011
- }
30012
- else {
30013
- let floored = Math.floor(idx);
30014
- let tmp = idx - floored;
30015
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30016
- // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
30017
- r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
30305
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30018
30306
 
30019
- // a threshold to stop drawing the tube if it's less than this ratio of radius.
30020
- // The extra bit of tube connects coil with strands or helices
30021
- if(!bNonCoil) {
30022
- if(r < thresholdRatio * constRadiius) {
30023
- r = 0;
30024
- }
30025
- // else if(r < constRadiius) {
30026
- // r *= 0.5; // use small radius for the connection between coild and sheets/helices
30027
- // }
30307
+ if(atom.ss === firstAtom.ss && atom.ssbegin) {
30308
+ beginResi = atom.resi;
30309
+ break;
30028
30310
  }
30029
30311
  }
30030
- }
30031
- let delta, axis1, axis2;
30032
- if (i < lim - 1) {
30033
- delta = pnts[i].clone().sub(pnts[i + 1]);
30034
- axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
30035
- axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
30036
- // let dir = 1, offset = 0;
30037
- if (prevAxis1.dot(axis1) < 0) {
30038
- axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
30312
+
30313
+ for(let i = beginResi; i < firstAtom.resi; ++i) {
30314
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30315
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30316
+ ic.atoms));
30039
30317
  }
30040
- prevAxis1 = axis1; prevAxis2 = axis2;
30041
- } else {
30042
- axis1 = prevAxis1; axis2 = prevAxis2;
30043
30318
  }
30044
- for (let j = 0; j < circleDiv; ++j) {
30045
- let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
30046
- let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
30047
- verticeArray[offset++] = point.x;
30048
- verticeArray[offset++] = point.y;
30049
- verticeArray[offset++] = point.z;
30050
30319
 
30051
- color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
30052
- colorArray[offset2++] = color.r;
30053
- colorArray[offset2++] = color.g;
30054
- colorArray[offset2++] = color.b;
30320
+ // add one extra residue for coils between strands/helix
30321
+ if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
30322
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
30323
+ if(ic.residues.hasOwnProperty(residueid)) {
30324
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30325
+ ic.atoms));
30326
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30327
+ }
30055
30328
  }
30056
- }
30057
- let offsetTmp = 0, nComp = 3;
30058
- for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
30059
- let reg = 0;
30060
- //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
30061
- //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
30062
- let pos = offsetTmp * nComp;
30063
- let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30064
- pos = (offsetTmp + circleDiv) * nComp;
30065
- let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30066
- pos = (offsetTmp + circleDiv + 1) * nComp;
30067
- let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30068
30329
 
30069
- let r1 = point1.clone().sub(point2).lengthSq();
30070
- let r2 = point1.clone().sub(point3).lengthSq();
30071
- if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
30072
- //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
30073
- //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
30074
- //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
30075
- indexArray[offset3++] = offsetTmp + j;
30076
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30077
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30330
+ // fill the end
30331
+ let endResi = lastAtom.resi;
30332
+ // 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.
30078
30333
 
30079
- //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
30080
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30081
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30082
- indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
30083
- }
30084
- offsetTmp += circleDiv;
30085
- }
30334
+ if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
30086
30335
 
30087
- geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
30088
- geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
30336
+ let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
30337
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
30338
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30339
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30089
30340
 
30090
- geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
30091
- //geo.setIndex(indexArray);
30341
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30092
30342
 
30093
- //geo.computeFaceNormals();
30094
- //geo.computeVertexNormals(false);
30095
- geo.computeVertexNormals();
30343
+ if(atom.ss === lastAtom.ss && atom.ssend) {
30344
+ endResi = atom.resi;
30345
+ break;
30346
+ }
30347
+ }
30096
30348
 
30097
- let mesh;
30098
- if(bHighlight === 2) {
30099
- //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 }));
30100
- 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 }));
30349
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
30350
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30351
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30352
+ ic.atoms));
30353
+ }
30354
+ }
30101
30355
 
30102
- if(ic.mdl) ic.mdl.add(mesh);
30103
- }
30104
- else if(bHighlight === 1) {
30105
- mesh = new THREE.Mesh(geo, ic.matShader);
30106
- mesh.renderOrder = ic.renderOrderPicking;
30107
- //ic.mdlPicking.add(mesh);
30108
- if(ic.mdl) ic.mdl.add(mesh);
30109
- }
30110
- else {
30111
- //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
30112
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30356
+ // add one extra residue for coils between strands/helix
30357
+ if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
30358
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
30359
+ if(ic.residues.hasOwnProperty(residueid)) {
30360
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30361
+ ic.atoms));
30362
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30363
+ }
30364
+ }
30113
30365
 
30114
- if(ic.mdl) ic.mdl.add(mesh);
30115
- }
30366
+ // reset notshow
30367
+ if(lastAtom.notshow) lastAtom.notshow = undefined;
30116
30368
 
30117
- if(bHighlight === 1 || bHighlight === 2) {
30118
- ic.prevHighlightObjects.push(mesh);
30119
- }
30120
- else {
30121
- ic.objects.push(mesh);
30122
- }
30123
- }
30369
+ firstAtom = currAtom;
30370
+ }
30124
30371
 
30125
- getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
30126
- let radiusFinal = radius;
30127
- if(radius) {
30128
- radiusFinal = radius;
30129
- }
30130
- else {
30131
- if(atom.b > 0 && atom.b <= 100) {
30132
- radiusFinal = atom.b * 0.01;
30133
- }
30134
- else if(atom.b > 100) {
30135
- radiusFinal = 100 * 0.01;
30136
- }
30137
- else {
30138
- radiusFinal = ic.coilWidth;
30139
- }
30372
+ prevChain = currChain;
30373
+ prevResi = currResi;
30374
+ prevAtom = currAtom;
30375
+
30376
+ ++index;
30140
30377
  }
30141
30378
 
30142
- return radiusFinal;
30379
+ return atomsAdjust;
30143
30380
  }
30144
30381
  }
30145
30382
 
@@ -37157,6 +37394,11 @@ class Alternate {
37157
37394
  ic.directionalLight.position.copy(ic.lightPos.clone().applyQuaternion( quaternion ).normalize());
37158
37395
  ic.directionalLight2.position.copy(ic.lightPos2.clone().applyQuaternion( quaternion ).normalize());
37159
37396
  ic.directionalLight3.position.copy(ic.lightPos3.clone().applyQuaternion( quaternion ).normalize());
37397
+
37398
+ // adjust the light according to the position of camera
37399
+ ic.directionalLight.applyMatrix4(cam.matrixWorld);
37400
+ ic.directionalLight2.applyMatrix4(cam.matrixWorld);
37401
+ ic.directionalLight3.applyMatrix4(cam.matrixWorld);
37160
37402
  }
37161
37403
 
37162
37404
  if(!ic.bVr) ic.renderer.setPixelRatio( window.devicePixelRatio ); // r71
@@ -47788,6 +48030,13 @@ class ShowAnno {
47788
48030
  } // align seq to structure
47789
48031
  }
47790
48032
  //ic.bAnnoShown = true;
48033
+
48034
+ if($("#" + ic.pre + "anno_ig").length && $("#" + ic.pre + "anno_ig")[0].checked) {
48035
+ ic.bRunRefnumAgain = true;
48036
+ await ic.annotationCls.setAnnoTabIg();
48037
+
48038
+ ic.bRunRefnumAgain = false;
48039
+ }
47791
48040
  }
47792
48041
 
47793
48042
  async showAnnoSeqData(nucleotide_chainid, chemical_chainid, chemical_set) { let ic = this.icn3d, me = ic.icn3dui;
@@ -53522,6 +53771,7 @@ class AlignParser {
53522
53771
 
53523
53772
  async downloadAlignmentPart2(data, seqalign, chainresiCalphaHash2) { let ic = this.icn3d, me = ic.icn3dui;
53524
53773
  //ic.init();
53774
+
53525
53775
  ic.loadAtomDataCls.loadAtomDataIn(data, undefined, 'align', seqalign);
53526
53776
 
53527
53777
  if(me.cfg.align === undefined && Object.keys(ic.structures).length == 1) {
@@ -53565,20 +53815,21 @@ class AlignParser {
53565
53815
  async loadOpmDataForAlign(data, seqalign, mmdbidArray) { let ic = this.icn3d, me = ic.icn3dui;
53566
53816
  let thisClass = this;
53567
53817
 
53568
- let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
53569
- let prms1 = me.getAjaxPromise(url, 'text');
53570
- let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
53571
- let prms2 = me.getAjaxPromise(url2, 'text');
53572
-
53573
- let allPromise = Promise.allSettled([prms1, prms2]);
53574
53818
  try {
53819
+ let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
53820
+ let prms1 = me.getAjaxPromise(url, 'text');
53821
+ let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
53822
+ let prms2 = me.getAjaxPromise(url2, 'text');
53823
+
53824
+ let allPromise = Promise.allSettled([prms1, prms2]);
53825
+
53575
53826
  let dataArray = await allPromise;
53576
-
53827
+
53577
53828
  let bFound = false;
53578
53829
  for(let i = 0, il = dataArray.length; i < il; ++i) {
53579
- //let opmdata = (me.bNode) ? dataArray[i] : dataArray[i].value;
53580
- let opmdata = dataArray[i].value;
53830
+ // if(dataArray[i].status == 'rejected') continue;
53581
53831
 
53832
+ let opmdata = dataArray[i].value;
53582
53833
  if(!opmdata) continue;
53583
53834
 
53584
53835
  ic.selectedPdbid = mmdbidArray[i];
@@ -53594,6 +53845,7 @@ class AlignParser {
53594
53845
  $("#" + ic.pre + "intra_mem_z").val(-ic.halfBilayerSize);
53595
53846
 
53596
53847
  ic.init(); // remove all previously loaded data
53848
+
53597
53849
  await thisClass.downloadAlignmentPart2(data, seqalign, chainresiCalphaHash);
53598
53850
 
53599
53851
  bFound = true;
@@ -58169,7 +58421,7 @@ class SdfParser {
58169
58421
  let sdfStr = await me.getAjaxPromise(urlSmiles, 'text');
58170
58422
 
58171
58423
  ic.init();
58172
- ic.bInputfile = true;
58424
+ //ic.bInputfile = true;
58173
58425
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + sdfStr : sdfStr;
58174
58426
  ic.InputfileType = 'sdf';
58175
58427
  await ic.sdfParserCls.loadSdfData(sdfStr);
@@ -60978,6 +61230,7 @@ class ParserUtils {
60978
61230
  ic.selectionCls.oneStructurePerWindow(); // for alignment
60979
61231
  ic.drawCls.draw();
60980
61232
  }
61233
+
60981
61234
  if(ic.bOpm) {
60982
61235
  let axis = new THREE.Vector3(1,0,0);
60983
61236
  let angle = -0.5 * Math.PI;
@@ -63515,7 +63768,8 @@ class LoadPDB {
63515
63768
 
63516
63769
  // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
63517
63770
  //This PDB parser feeds the viewer with the content of a PDB file, pdbData.
63518
- async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
63771
+ // async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
63772
+ loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
63519
63773
  let hAtoms = {};
63520
63774
 
63521
63775
  let bNMR = false;
@@ -68636,17 +68890,15 @@ class SelectCollections {
68636
68890
  }
68637
68891
 
68638
68892
  //Set the menu of defined sets with an array of defined names "commandnameArray".
68639
- setAtomMenu(nameArray) {
68893
+ setAtomMenu(collection) {
68640
68894
  let ic = this.icn3d;
68641
68895
  ic.icn3dui;
68642
68896
  let html = "";
68643
- //for(let i in ic.defNames2Atoms) {
68644
- for (let i = 0, il = nameArray.length; i < il; ++i) {
68645
- let name = nameArray[i][0];
68646
- let title = nameArray[i][1];
68647
- let description = nameArray[i][2];
68648
-
68897
+
68898
+ Object.entries(collection).forEach(([name, structure], index) => {
68649
68899
  let atomHash;
68900
+ let [id, title, description, commands, pdb] = structure;
68901
+
68650
68902
  if (
68651
68903
  ic.defNames2Atoms !== undefined &&
68652
68904
  ic.defNames2Atoms.hasOwnProperty(name)
@@ -68667,12 +68919,12 @@ class SelectCollections {
68667
68919
  }
68668
68920
  }
68669
68921
 
68670
- if (i == 0) {
68671
- html += "<option value='" + nameArray[0][0] + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
68672
- } else {
68922
+ if (index === 0) {
68923
+ html += "<option value='" + name + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
68924
+ } else {
68673
68925
  html += "<option value='" + name + "' data-description='" + description + "'>" + title + "</option>";
68674
- }
68675
- }
68926
+ }
68927
+ });
68676
68928
 
68677
68929
  return html;
68678
68930
  }
@@ -68726,7 +68978,7 @@ class SelectCollections {
68726
68978
  let nameArray = $(this).val();
68727
68979
  let nameStructure = $(this).find("option:selected").text();
68728
68980
  let selectedIndices = Array.from(this.selectedOptions).map(option => option.index);
68729
- let selectedIndicesMap = nameArray.reduce((map, name, i) => {
68981
+ nameArray.reduce((map, name, i) => {
68730
68982
  map[name] = selectedIndices[i];
68731
68983
  return map;
68732
68984
  }, {});
@@ -68763,13 +69015,13 @@ class SelectCollections {
68763
69015
  if (Object.keys(ic.structures).length == 0) {
68764
69016
  bAppend = false;
68765
69017
  }
68766
- await ic.pdbParserCls.loadPdbData(ic.pdbCollection[selectedIndicesMap[name]].join('\n'), undefined, undefined, bAppend);
69018
+ await ic.pdbParserCls.loadPdbData(ic.pdbCollection[name].join('\n'), undefined, undefined, bAppend);
68767
69019
  } else {
68768
69020
  await ic.chainalignParserCls.downloadMmdbAf(name, undefined, undefined, bNoDuplicate);
68769
69021
  }
68770
69022
  }
68771
69023
 
68772
- await loadStructure(collection[selectedIndicesMap[name]][4]).then(() => {
69024
+ await loadStructure(collection[name][4]).then(() => {
68773
69025
  ic.allData['all'] = {
68774
69026
  'atoms': ic.atoms,
68775
69027
  'proteins': ic.proteins,
@@ -68829,9 +69081,9 @@ class SelectCollections {
68829
69081
 
68830
69082
  ic.molTitle = ic.allData[name]['title'];
68831
69083
 
68832
- if (collection[selectedIndicesMap[name]][3] !== undefined && collection[selectedIndicesMap[name]][3].length > 0) {
69084
+ if (collection[name][3] !== undefined && collection[name][3].length > 0) {
68833
69085
  if (ic.allData[name]['commands'] == undefined) {
68834
- let commands = collection[selectedIndicesMap[name]][3];
69086
+ let commands = collection[name][3];
68835
69087
  ic.allData[name]['commands'] = commands;
68836
69088
  }
68837
69089
  }
@@ -69100,7 +69352,9 @@ class LoadScript {
69100
69352
  }
69101
69353
  else if(command.indexOf('view interactions') == 0 && me.cfg.align !== undefined) { // the command may have "|||{"factor"...
69102
69354
  await thisClass.applyCommandViewinteraction(strArray[0].trim());
69103
-
69355
+ }
69356
+ else if(command.indexOf('view 2d depiction') == 0) { // the command may have "|||{"factor"...
69357
+ await ic.ligplotCls.drawLigplot(ic.atoms, true);
69104
69358
  }
69105
69359
  else if(command.indexOf('symmetry') == 0) {
69106
69360
  ic.bAxisOnly = false;
@@ -69310,6 +69564,9 @@ class LoadScript {
69310
69564
  else if(lastCommand.indexOf('view interactions') == 0 && me.cfg.align !== undefined) {
69311
69565
  await thisClass.applyCommandViewinteraction(lastCommand);
69312
69566
  }
69567
+ else if(lastCommand.indexOf('view 2d depiction') == 0) {
69568
+ await ic.ligplotCls.drawLigplot(ic.atoms, true);
69569
+ }
69313
69570
  else if(lastCommand.indexOf('symmetry') == 0) {
69314
69571
  let title = lastCommand.substr(lastCommand.indexOf(' ') + 1);
69315
69572
  ic.symmetrytitle =(title === 'none') ? undefined : title;
@@ -71468,155 +71725,6 @@ class Resid2spec {
71468
71725
  }
71469
71726
  }
71470
71727
 
71471
- /**
71472
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
71473
- */
71474
-
71475
- class FirstAtomObj {
71476
- constructor(icn3d) {
71477
- this.icn3d = icn3d;
71478
- }
71479
-
71480
- //Return the first atom in the atom hash, which has the atom serial number as the key.
71481
- getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71482
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71483
- return undefined;
71484
- }
71485
-
71486
- let atomKeys = Object.keys(atomsHash);
71487
- let firstIndex = atomKeys[0];
71488
-
71489
- return ic.atoms[firstIndex];
71490
- }
71491
-
71492
- // n is the position of the selected atom
71493
- getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
71494
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71495
- return undefined;
71496
- }
71497
-
71498
- let atomKeys = Object.keys(atomsHash);
71499
- let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
71500
-
71501
- return ic.atoms[middleIndex];
71502
- }
71503
-
71504
- getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71505
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71506
- return undefined;
71507
- }
71508
-
71509
- let firstIndex;
71510
-
71511
- for(let i in atomsHash) {
71512
- if(ic.atoms[i].name == 'CA') {
71513
- firstIndex = i;
71514
- break;
71515
- }
71516
- }
71517
-
71518
- if(!firstIndex) {
71519
- for(let i in atomsHash) {
71520
- if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
71521
- firstIndex = i;
71522
- break;
71523
- }
71524
- }
71525
- }
71526
-
71527
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
71528
- }
71529
-
71530
- getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
71531
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71532
- return ic.atoms[0];
71533
- }
71534
-
71535
- let firstIndex;
71536
-
71537
- for(let i in atomsHash) {
71538
- if(ic.atoms[i].name == atomName) {
71539
- firstIndex = i;
71540
- break;
71541
- }
71542
- }
71543
-
71544
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
71545
- }
71546
-
71547
- //Return the last atom in the atom hash, which has the atom serial number as the key.
71548
- getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71549
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71550
- return ic.atoms[0];
71551
- }
71552
-
71553
- let atomKeys = Object.keys(atomsHash);
71554
- let lastIndex = atomKeys[atomKeys.length - 1];
71555
-
71556
- return ic.atoms[lastIndex];
71557
- }
71558
-
71559
- //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
71560
- getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71561
- let residuesHash = {};
71562
- for(let i in atomsHash) {
71563
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
71564
- residuesHash[residueid] = 1;
71565
- }
71566
-
71567
- return residuesHash;
71568
- }
71569
-
71570
- getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71571
- let residuesHash = {};
71572
- for(let i in atomsHash) {
71573
- if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
71574
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
71575
- //residuesHash[residueid] = 1;
71576
- residuesHash[residueid] = ic.atoms[i].resn;
71577
- }
71578
- }
71579
-
71580
- return residuesHash;
71581
- }
71582
-
71583
- //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
71584
- getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71585
- let chainsHash = {};
71586
- for(let i in atomsHash) {
71587
- let atom = ic.atoms[i];
71588
- let chainid = atom.structure + "_" + atom.chain;
71589
-
71590
- chainsHash[chainid] = 1;
71591
- }
71592
-
71593
- return chainsHash;
71594
- }
71595
-
71596
- getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
71597
- if(ic.residues.hasOwnProperty(resid)) {
71598
- for(let i in ic.residues[resid]) {
71599
- if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
71600
- return ic.atoms[i];
71601
- }
71602
- }
71603
- }
71604
-
71605
- return undefined;
71606
- }
71607
-
71608
- getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
71609
- let atom = this.getAtomFromResi(resid, atomName);
71610
- if(atom !== undefined) {
71611
- let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
71612
-
71613
- return coord;
71614
- }
71615
-
71616
- return undefined;
71617
- }
71618
- }
71619
-
71620
71728
  /**
71621
71729
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
71622
71730
  */
@@ -71677,9 +71785,9 @@ class Delphi {
71677
71785
  let pdbstr = '';
71678
71786
  /// pdbstr += ic.saveFileCls.getPDBHeader();
71679
71787
 
71680
- let bMergeIntoOne = true;
71681
- pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
71682
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
71788
+ let bMergeIntoOne = true, bOneLetterChain = true;
71789
+ 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);
71790
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
71683
71791
 
71684
71792
  return pdbstr;
71685
71793
  }
@@ -77693,8 +77801,13 @@ class Ligplot {
77693
77801
  this.icn3d = icn3d;
77694
77802
  }
77695
77803
 
77696
- async drawLigplot(atomSet1) { let ic = this.icn3d, me = ic.icn3dui;
77697
- me.htmlCls.dialogCls.openDlg('dl_ligplot', 'Show ligand interactions with atom details');
77804
+ async drawLigplot(atomSet1, bDepiction) { let ic = this.icn3d, me = ic.icn3dui;
77805
+ if(bDepiction) {
77806
+ me.htmlCls.dialogCls.openDlg('dl_ligplot', '2D Depiction');
77807
+ }
77808
+ else {
77809
+ me.htmlCls.dialogCls.openDlg('dl_ligplot', 'Show ligand interactions with atom details');
77810
+ }
77698
77811
 
77699
77812
  let widthOri, heightOri, width = 100, height = 100;
77700
77813
  ic.len4ang = 80;
@@ -77776,19 +77889,28 @@ class Ligplot {
77776
77889
  let offset = - ic.len4ang;
77777
77890
  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'>";
77778
77891
 
77779
- let xlen = parseInt(widthOri / ic.svgGridSize), ylen = parseInt(heightOri / ic.svgGridSize);
77780
- let result = ic.viewInterPairsCls.getAllInteractionTable("save1", index2xy, xlen, ylen, xcenter, ycenter); // sort on the ligand/set1
77781
- ic.bLigplot = true;
77892
+ if(bDepiction) {
77893
+ svgHtml += lineSvg + nodeSvg;
77894
+ }
77895
+ else {
77896
+ let xlen = parseInt(widthOri / ic.svgGridSize), ylen = parseInt(heightOri / ic.svgGridSize);
77897
+ let result = ic.viewInterPairsCls.getAllInteractionTable("save1", index2xy, xlen, ylen, xcenter, ycenter); // sort on the ligand/set1
77898
+ // ic.bLigplot = true;
77782
77899
 
77783
- svgHtml += lineSvg + result.svgHtmlLine;
77900
+ svgHtml += lineSvg + result.svgHtmlLine;
77784
77901
 
77785
- svgHtml += nodeSvg + result.svgHtmlNode;
77902
+ svgHtml += nodeSvg + result.svgHtmlNode;
77903
+ }
77786
77904
 
77787
77905
  svgHtml += "</svg>";
77788
77906
 
77789
- $("#" + ic.pre + "ligplotDiv").html(svgHtml);
77790
-
77791
- this.setEventsForLigplot();
77907
+ if(bDepiction) {
77908
+ $("#" + ic.pre + "ligplotDiv").html(svgHtml);
77909
+ }
77910
+ else {
77911
+ $("#" + ic.pre + "ligplotDiv").html(svgHtml);
77912
+ this.setEventsForLigplot();
77913
+ }
77792
77914
  }
77793
77915
 
77794
77916
 
@@ -78935,7 +79057,7 @@ class SaveFile {
78935
79057
  }
78936
79058
 
78937
79059
  //getAtomPDB: function(atomHash, bPqr, bPdb, bNoChem) { let ic = this.icn3d, me = ic.icn3dui;
78938
- getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bVastSearch) { let ic = this.icn3d, me = ic.icn3dui;
79060
+ getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bOneLetterChain) { let ic = this.icn3d, me = ic.icn3dui;
78939
79061
  let pdbStr = '';
78940
79062
 
78941
79063
  // get all phosphate groups in lipids
@@ -79210,7 +79332,7 @@ class SaveFile {
79210
79332
  //line +=(atom.chain.length <= 1) ? atom.chain.padStart(1, ' ') : atom.chain.substr(0, 1);
79211
79333
  if(atom.chain.length >= 2) {
79212
79334
  let chainTmp = atom.chain.replace(/_/gi, '').substr(0, 2);
79213
- if(bVastSearch) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
79335
+ if(bOneLetterChain) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
79214
79336
  line += chainTmp;
79215
79337
  }
79216
79338
  else if(atom.chain.length == 1) {
@@ -79289,8 +79411,8 @@ class SaveFile {
79289
79411
  }
79290
79412
  else {
79291
79413
  line += "1.00".padStart(6, ' ');
79292
- // line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : ' '.padStart(6, ' ');
79293
- let defaultBFactor = (bVastSearch) ? "1.0" : " ";
79414
+ // let defaultBFactor = (bOneLetterChain) ? "1.0" : " ";
79415
+ let defaultBFactor = " ";
79294
79416
  line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : defaultBFactor.padStart(6, ' ');
79295
79417
  line += ' '.padStart(10, ' ');
79296
79418
  line += atom.elem.padStart(2, ' ');
@@ -79338,7 +79460,7 @@ class SaveFile {
79338
79460
  let resn = me.utilsCls.residueName2Abbr(atom.resn);
79339
79461
  let ss = this.secondary2Abbr(atom.ss);
79340
79462
 
79341
- if(chainid != prevChainid) {
79463
+ if(chainid != prevChainid && !data[chainid]) {
79342
79464
  data[chainid] = {"resi": [], "resn": [], "secondary": []};
79343
79465
  }
79344
79466
 
@@ -82862,7 +82984,7 @@ class iCn3DUI {
82862
82984
  //even when multiple iCn3D viewers are shown together.
82863
82985
  this.pre = this.cfg.divid + "_";
82864
82986
 
82865
- this.REVISION = '3.39.0';
82987
+ this.REVISION = '3.40.3';
82866
82988
 
82867
82989
  // In nodejs, iCn3D defines "window = {navigator: {}}"
82868
82990
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -83024,7 +83146,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
83024
83146
  ic.molTitle = '';
83025
83147
  ic.loadCmd;
83026
83148
 
83027
- // set menus
83149
+ // set menus
83028
83150
  me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
83029
83151
  me.htmlCls.clickMenuCls.applyShownMenus();
83030
83152