icn3d 3.40.0 → 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 +1181 -1100
  2. package/icn3d.min.js +4 -4
  3. package/icn3d.module.js +1181 -1100
  4. package/package.json +1 -1
package/icn3d.module.js CHANGED
@@ -9728,25 +9728,42 @@ class ClickMenu {
9728
9728
  }
9729
9729
 
9730
9730
  getHiddenMenusFromCache() { let me = this.icn3dui; me.icn3d;
9731
- me.htmlCls.shownMenus = {};
9731
+ // me.htmlCls.shownMenus = {};
9732
9732
 
9733
- let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
9734
-
9735
- if(idArrayStr && idArrayStr != '[]') {
9736
- let idArray = JSON.parse(idArrayStr);
9733
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
9737
9734
 
9738
- // for(let i = 0, il = idArray.length; i < il; ++i) {
9739
- // me.htmlCls.shownMenus[idArray[i]] = 1;
9740
- // }
9741
- for(let menu in me.htmlCls.allMenus) {
9742
- if(idArray.indexOf(menu) == -1) {
9743
- me.htmlCls.shownMenus[menu] = 1;
9744
- }
9735
+ let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
9736
+
9737
+ if(idArrayStr && idArrayStr != '[]') {
9738
+ me.htmlCls.shownMenus = {};
9739
+
9740
+ let idArray = JSON.parse(idArrayStr);
9741
+
9742
+ // for(let i = 0, il = idArray.length; i < il; ++i) {
9743
+ // me.htmlCls.shownMenus[idArray[i]] = 1;
9744
+ // }
9745
+ for(let menu in me.htmlCls.allMenus) {
9746
+ if(idArray.indexOf(menu) == -1) {
9747
+ me.htmlCls.shownMenus[menu] = 1;
9745
9748
  }
9746
- }
9747
- else {
9748
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9749
- }
9749
+ }
9750
+ }
9751
+ //###
9752
+ else {
9753
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9754
+ }
9755
+
9756
+ // else {
9757
+ // if(mode == 'all') {
9758
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9759
+ // }
9760
+ // else if(!mode || mode == 'simple') {
9761
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
9762
+ // }
9763
+ // else {
9764
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
9765
+ // }
9766
+ // }
9750
9767
  }
9751
9768
 
9752
9769
  displayShownMenus() { let me = this.icn3dui; me.icn3d;
@@ -10242,11 +10259,14 @@ class ClickMenu {
10242
10259
  me.htmlCls.shownMenus[checkbox.value] = 1;
10243
10260
  }
10244
10261
 
10262
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
10263
+
10245
10264
  thisClass.applyShownMenus();
10246
10265
  });
10247
10266
 
10248
10267
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref", "#" + me.pre + "reset_menupref2"], "click", function(e) { me.icn3d; //e.preventDefault();
10249
10268
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
10269
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'simple');
10250
10270
 
10251
10271
  thisClass.applyShownMenus();
10252
10272
  thisClass.displayShownMenus();
@@ -10254,6 +10274,7 @@ class ClickMenu {
10254
10274
 
10255
10275
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref_all", "#" + me.pre + "reset_menupref_all2"], "click", function(e) { me.icn3d; //e.preventDefault();
10256
10276
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
10277
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'all');
10257
10278
 
10258
10279
  thisClass.applyShownMenus();
10259
10280
  thisClass.displayShownMenus();
@@ -10303,15 +10324,16 @@ class ClickMenu {
10303
10324
 
10304
10325
  thisClass.applyShownMenus();
10305
10326
  thisClass.displayShownMenus();
10327
+
10328
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
10306
10329
  };
10307
10330
  reader.readAsText(file);
10308
10331
  }
10309
10332
  });
10310
10333
 
10311
- me.myEventCls.onIds("#" + me.pre + "mn1_menuloadpref", "click", function(e) { me.icn3d; //e.preventDefault();
10334
+ me.myEventCls.onIds(["#" + me.pre + "mn1_menuloadpref", "#" + me.pre + "loadpref", "#" + me.pre + "loadpref2"], "click", function(e) { me.icn3d; //e.preventDefault();
10312
10335
  me.htmlCls.dialogCls.openDlg('dl_menuloadpref', 'Please input the menu preference file');
10313
10336
  });
10314
-
10315
10337
 
10316
10338
  me.myEventCls.onIds("#" + me.pre + "mn1_link_structure", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10317
10339
  let url = ic.saveFileCls.getLinkToStructureSummary(true);
@@ -12150,6 +12172,51 @@ class SetMenu {
12150
12172
  return me.htmlCls.setHtmlCls.getRadioColor(radioid, id, text, color, bChecked, bSimpleMenu, selType);
12151
12173
  }
12152
12174
 
12175
+ resetMenu(mode) { let me = this.icn3dui;
12176
+ if(!mode || mode == 'simple') {
12177
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12178
+
12179
+ me.htmlCls.clickMenuCls.applyShownMenus();
12180
+ }
12181
+ else if(mode == 'all') {
12182
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
12183
+
12184
+ me.htmlCls.clickMenuCls.applyShownMenus();
12185
+ }
12186
+ else if(mode == 'custom') {
12187
+ me.htmlCls.dialogCls.openDlg('dl_menupref', 'Select Menus');
12188
+
12189
+ me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
12190
+
12191
+ me.htmlCls.clickMenuCls.displayShownMenus();
12192
+ }
12193
+ }
12194
+
12195
+ setMenuMode(bMobile) { let me = this.icn3dui;
12196
+ let spaceCss = (bMobile) ? "; padding-left:6px; background-color:#eee" : "; margin:3px; background-color:white";
12197
+ let spaceCss2 = (bMobile) ? "; font-size:14px!important" : "";
12198
+
12199
+ let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
12200
+
12201
+ let html = '<div class="icn3d-text" style="color:#f8b84e; font-weight:bold' + spaceCss + '">';
12202
+ html += '<select name="menumode" id="' + me.pre + 'menumode" class="icn3d-text" style="color:#f8b84e; font-weight:bold; border:0px' + spaceCss2 + '">';
12203
+ html += (mode == 'simple' || !mode) ? '<option value="simple" selected>Simple</option>' : '<option value="simple">Simple</option>';
12204
+ html += (mode == 'all') ? '<option value="all" selected>All</option>' : '<option value="all">All</option>';
12205
+ html += (mode == 'custom') ? '<option value="custom" selected>Custom</option>' : '<option value="custom">Custom</option>';
12206
+ html += '</select>';
12207
+
12208
+ if(bMobile) {
12209
+ html += '<br><span style="font-size:12px">&nbsp;Menus</span>';
12210
+ }
12211
+ else {
12212
+ html += '&nbsp;Menus';
12213
+ }
12214
+
12215
+ html += '</div>';
12216
+
12217
+ return html;
12218
+ }
12219
+
12153
12220
  //Set the HTML code for the menus shown at the top of the viewer.
12154
12221
  setTopMenusHtml(id, str1, str2) { let me = this.icn3dui;
12155
12222
  if(me.bNode) return '';
@@ -12169,6 +12236,9 @@ class SetMenu {
12169
12236
  html += "<table border='0' cellpadding='0' cellspacing='0' width='100'><tr>";
12170
12237
 
12171
12238
  let tdStr = '<td valign="top">';
12239
+
12240
+ // html += tdStr + this.setMenuMode() + '</td>';
12241
+
12172
12242
  html += tdStr + this.setMenu1() + '</td>';
12173
12243
 
12174
12244
  html += tdStr + this.setMenu2() + '</td>';
@@ -12181,7 +12251,11 @@ class SetMenu {
12181
12251
  //html += tdStr + this.setMenu5b() + '</td>';
12182
12252
  html += tdStr + this.setMenu6() + '</td>';
12183
12253
 
12184
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12254
+ // reset the menus at the end of the menus
12255
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
12256
+ // this.resetMenu(mode);
12257
+
12258
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12185
12259
 
12186
12260
  html += tdStr + "<div style='position:relative; margin-left:6px;'>" + str1;
12187
12261
  html += "<div class='icn3d-commandTitle' style='min-width:40px; margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -12277,6 +12351,7 @@ class SetMenu {
12277
12351
 
12278
12352
  //html += "<div class='icn3d-menu'>";
12279
12353
  html += "<div>";
12354
+
12280
12355
  html += "<accordion id='" + me.pre + "accordion0' class='icn3d-accordion'>";
12281
12356
  if(me.cfg.notebook) {
12282
12357
  html += "<h3 style='width:20px; height:24px; position:relative; padding: 0'><span style='position:absolute; left:3px; top:4px;'>&#9776;</span></h3>";
@@ -12286,6 +12361,8 @@ class SetMenu {
12286
12361
  }
12287
12362
  html += "<div>";
12288
12363
 
12364
+ // html += '<li>' + this.setMenuMode(true);
12365
+
12289
12366
  let liStr = "<li><span class='icn3d-menu-color'";
12290
12367
 
12291
12368
  html += "<ul class='icn3d-mn-item'>";
@@ -12304,7 +12381,11 @@ class SetMenu {
12304
12381
  html += liStr + ">Help</span>";
12305
12382
  html += this.setMenu6_base();
12306
12383
 
12307
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12384
+ // reset the menus at the end of the menus
12385
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
12386
+ // this.resetMenu(mode);
12387
+
12388
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
12308
12389
 
12309
12390
  html += "<li><div style='position:relative; margin-top:-6px;'>" + str1;
12310
12391
  html += "<div class='icn3d-commandTitle' style='margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -12481,7 +12562,7 @@ class SetMenu {
12481
12562
  // Analysis menu
12482
12563
  html += tdStrBorder + this.setIcon(iconType, 'tool_selectannotations', 'Sequences & Annotations', 'grip-lines') + "</td>";
12483
12564
  html += tdStr + this.setIcon(iconType, 'hbondsYes', 'Interactions', 'users') + "</td>";
12484
- html += tdStr + this.setIcon(iconType, 'tool_delphi', 'Delphi Potentials', 'cloud-meatball') + "</td>";
12565
+ html += tdStr + this.setIcon(iconType, 'tool_delphi', 'DelPhi Potentials', 'cloud-meatball') + "</td>";
12485
12566
  html += tdStr + this.setIcon(iconType, 'removeLabels', 'Remove Labels', 'remove-format') + "</td>";
12486
12567
 
12487
12568
  // Help menu
@@ -14498,7 +14579,7 @@ class Dialog {
14498
14579
  width='50%';
14499
14580
  }
14500
14581
  else if(id === me.pre + 'dl_menupref') {
14501
- width = 600;
14582
+ width = 800;
14502
14583
  height = 500;
14503
14584
  }
14504
14585
 
@@ -15159,6 +15240,10 @@ class SetDialog {
15159
15240
  html += me.htmlCls.divStr + "dl_collection_file' style=''>";
15160
15241
  html += "You can load a collection of structures via a file. Here are <a href='https://github.com/ncbi/icn3d/blob/master/example/collection/' target='_blank'>some example files</a><br><br>";
15161
15242
  html += "Collection file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "collectionfile'><br/>";
15243
+ html += "<input type='radio' id='dl_collectionAppendStructureNone' name='appendStructure' value='none' checked/>";
15244
+ html += "<label for='dl_collectionAppendStructureNone'>Default</label>";
15245
+ html += "<input type='radio' id='dl_collectionAppendStructure' name='appendStructure' value='append' />";
15246
+ html += "<label for='dl_collectionAppendStructure'>Append</label><br/>";
15162
15247
  html += me.htmlCls.buttonStr + "reload_collectionfile' style='margin-top: 6px;'>Load</button>";
15163
15248
  html += "</div>";
15164
15249
  html += "</div>";
@@ -15166,7 +15251,8 @@ class SetDialog {
15166
15251
  html += me.htmlCls.divStr + "dl_collection_structures' style='display: none'>";
15167
15252
  html += "<select id='" + me.pre + "collections_menu'multiple size='6' style='min-width:300px;'></select>";
15168
15253
  html += '<br/>';
15169
- html += me.htmlCls.buttonStr + "opendl_export_collections'>Export</button>";
15254
+ html += me.htmlCls.buttonStr + "collections_clear_commands' style='margin-top: 6px;'>Clear Commands</button>";
15255
+ html += me.htmlCls.buttonStr + "opendl_export_collections'>Export JSON</button>";
15170
15256
  html += "</div>";
15171
15257
  html += '<br/>';
15172
15258
  html += "</div>";
@@ -15871,13 +15957,15 @@ class SetDialog {
15871
15957
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref'>Apply</button></span>";
15872
15958
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref' style='margin-left:30px'>Reset to Simple Menus</button></span>";
15873
15959
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all' style='margin-left:30px'>Reset to All Menus</button></span>";
15874
- html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span><br><br>";
15960
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span>";
15961
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref' style='margin-left:30px'>Load Preferences</button></span><br><br>";
15875
15962
 
15876
15963
  html += "<div id='" + me.pre + "menulist'></div><br><br>";
15877
15964
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref2'>Apply</button></span>";
15878
15965
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref2' style='margin-left:30px'>Reset to Simple Menus</button></span>";
15879
15966
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all2' style='margin-left:30px'>Reset to All Menus</button></span>";
15880
15967
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref2' style='margin-left:30px'>Save Preferences</button></span>";
15968
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref2' style='margin-left:30px'>Load Preferences</button></span>";
15881
15969
  html += "</div>";
15882
15970
 
15883
15971
  html += me.htmlCls.divStr + "dl_addtrack' class='" + dialogClass + "'>";
@@ -16653,6 +16741,14 @@ class Events {
16653
16741
  me.htmlCls.clickMenuCls.clickMenu5();
16654
16742
  me.htmlCls.clickMenuCls.clickMenu6();
16655
16743
 
16744
+ me.myEventCls.onIds("#" + me.pre + "menumode", "change", async function(e) { me.icn3d;
16745
+ e.preventDefault();
16746
+ let mode = $("#" + me.pre + "menumode").val();
16747
+
16748
+ me.htmlCls.setHtmlCls.setCookie('menumode', mode);
16749
+ me.htmlCls.setMenuCls.resetMenu(mode);
16750
+ });
16751
+
16656
16752
  // back and forward arrows
16657
16753
  me.myEventCls.onIds(["#" + me.pre + "back", "#" + me.pre + "mn6_back"], "click", async function(e) { let ic = me.icn3d;
16658
16754
  e.preventDefault();
@@ -17699,95 +17795,108 @@ class Events {
17699
17795
  } else {
17700
17796
  ic.resizeCanvasCls.closeDialogs();
17701
17797
  }
17702
- ic.bInputfile = false;
17703
- ic.pdbCollection = [];
17704
- ic.allData = {};
17705
- ic.allData['all'] = {
17706
- 'atoms': {},
17707
- 'proteins': {},
17708
- 'nucleotides': {},
17709
- 'chemicals': {},
17710
- 'ions': {},
17711
- 'water': {},
17712
- 'structures': {}, // getSSExpandedAtoms
17713
- 'ssbondpnts': {},
17714
- 'residues': {}, // getSSExpandedAtoms
17715
- 'chains': {},
17716
- 'chainsSeq': {}, //Sequences and Annotation
17717
- 'defNames2Atoms': {},
17718
- 'defNames2Residues': {}
17719
- };
17720
- ic.allData['prev'] = {};
17721
- ic.selectCollectionsCls.reset();
17722
-
17798
+
17723
17799
  ic.dAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
17724
17800
  ic.hAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
17725
17801
  me.htmlCls.setHtmlCls.fileSupport();
17726
17802
 
17727
17803
  let fileName = file.name;
17728
17804
  let fileExtension = fileName.split('.').pop().toLowerCase();
17729
-
17805
+ let collection = {};
17806
+
17730
17807
  $("#" + ic.pre + "collections_menu").empty();
17731
17808
  $("#" + ic.pre + "collections_menu").off("change");
17809
+
17810
+ if (dl_collectionAppendStructureNone.checked || ic.allData === undefined) {
17811
+ ic.bInputfile = false;
17812
+ ic.pdbCollection = {};
17813
+ ic.allData = {};
17814
+ ic.allData['all'] = {
17815
+ 'atoms': {},
17816
+ 'proteins': {},
17817
+ 'nucleotides': {},
17818
+ 'chemicals': {},
17819
+ 'ions': {},
17820
+ 'water': {},
17821
+ 'structures': {}, // getSSExpandedAtoms
17822
+ 'ssbondpnts': {},
17823
+ 'residues': {}, // getSSExpandedAtoms
17824
+ 'chains': {},
17825
+ 'chainsSeq': {}, //Sequences and Annotation
17826
+ 'defNames2Atoms': {},
17827
+ 'defNames2Residues': {}
17828
+ };
17829
+ ic.allData['prev'] = {};
17830
+ ic.selectCollectionsCls.reset();
17831
+
17832
+ } else {
17833
+ if (ic.collections) {
17834
+ collection = ic.collections;
17835
+ }
17836
+ }
17732
17837
 
17733
17838
  function parseJsonCollection(data) {
17734
17839
  let dataStr = JSON.parse(data);
17735
- return dataStr["structures"].map(({ id, title, description, commands }) => {
17840
+ let parsedCollection = {};
17841
+
17842
+ dataStr["structures"].map(({ id, title, description, commands }) => {
17736
17843
  if (id && id.includes('.pdb')) {
17737
17844
  id = id.split('.pdb')[0];
17738
17845
  }
17739
- return [id, title, description, commands, false];
17846
+ parsedCollection[id] = [id, title, description, commands, false];
17740
17847
  });
17741
- }
17742
17848
 
17849
+ return parsedCollection;
17850
+ }
17851
+
17743
17852
  function parsePdbCollection(data, description = '', commands = []) {
17744
17853
  let dataStr = data;
17745
17854
  let lines = dataStr.split('\n');
17746
-
17747
17855
  let sections = [];
17748
17856
  let currentSection = [];
17749
-
17857
+
17750
17858
  lines.forEach(line => {
17751
- if (line.startsWith('HEADER')) {
17859
+ if (line.startsWith('HEADER')) {
17752
17860
  currentSection = [];
17753
17861
  sections.push(currentSection);
17754
- }
17755
- currentSection.push(line);
17862
+ }
17863
+ currentSection.push(line);
17756
17864
  });
17757
-
17758
- let ids = [];
17759
- let titles = [];
17760
-
17865
+
17866
+
17867
+ let parsedCollection = {};
17868
+
17761
17869
  sections.forEach((section) => {
17762
- let headerLine = section[0];
17763
- headerLine = headerLine.replace(/[\n\r]/g, '').trim();
17870
+ let headerLine = section[0].replace(/[\n\r]/g, '').trim();
17764
17871
  let header = headerLine.split(' ').filter(Boolean);
17765
- let lastElement = header[header.length - 1];
17766
- ids.push(lastElement);
17767
- titles.push(section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : lastElement);
17872
+ let id = header[header.length - 1];
17873
+ let title = section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : id;
17874
+
17875
+ parsedCollection[id] = [id, title, description, commands, true];
17876
+
17877
+ const sanitizedSection = section.map(line => line.trim());
17878
+ ic.pdbCollection[id] = sanitizedSection;
17768
17879
  });
17769
-
17770
- if (sections.length > 0) {
17771
- ic.pdbCollection.push(...sections);
17772
- }
17773
17880
 
17774
- return ids.map((id, index, description, commands) => [id, titles[index], description, commands, true]);
17881
+ return parsedCollection;
17775
17882
  }
17776
17883
 
17777
- let collection = [];
17778
-
17779
17884
  if (fileExtension === 'json' || fileExtension === 'pdb') {
17780
17885
  let reader = new FileReader();
17781
17886
  reader.onload = async function (e) {
17782
17887
  if (fileExtension === 'json') {
17783
- collection = parseJsonCollection(e.target.result);
17888
+ let jsonCollection = parseJsonCollection(e.target.result);
17889
+ collection = { ...collection, ...jsonCollection };
17784
17890
  } else if (fileExtension === 'pdb') {
17785
17891
  ic.bInputfile = true;
17786
- collection = parsePdbCollection(e.target.result);
17892
+ let pdbCollection = parsePdbCollection(e.target.result);
17893
+ collection = { ...collection, ...pdbCollection };
17787
17894
  }
17788
17895
 
17789
17896
  let collectionHtml = await ic.selectCollectionsCls.setAtomMenu(collection);
17790
17897
 
17898
+ ic.collections = collection;
17899
+
17791
17900
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
17792
17901
  await ic.selectCollectionsCls.clickStructure(collection);
17793
17902
  $("#" + ic.pre + "collections_menu").trigger("change");
@@ -17805,7 +17914,7 @@ class Events {
17805
17914
  let reader2 = new FileReader();
17806
17915
  reader2.onload = async function (e) {
17807
17916
  if (fileExtension === 'zip') {
17808
- let url = './script/jszip.js';
17917
+ let url = './script/jszip.min.js';
17809
17918
  await me.getAjaxPromise(url, 'script');
17810
17919
 
17811
17920
  let jszip = new JSZip();
@@ -17839,7 +17948,8 @@ class Events {
17839
17948
  let jsonCollection = [];
17840
17949
  for (const file of jsonFiles) {
17841
17950
  let fileData = await file.async('text');
17842
- parseJsonCollection(fileData).forEach(element => {
17951
+ let parsedJson = Object.values(parseJsonCollection(fileData));
17952
+ parsedJson.forEach(element => {
17843
17953
  jsonCollection.push(element);
17844
17954
  });
17845
17955
  }
@@ -17849,8 +17959,9 @@ class Events {
17849
17959
  let matchingPdbFile = pdbFiles.find(file => file.name.toLowerCase().includes(id.toLowerCase()));
17850
17960
  if (matchingPdbFile) {
17851
17961
  let pdbFileData = await matchingPdbFile.async('text');
17852
- parsePdbCollection(pdbFileData, description, commands).forEach(element => {
17853
- collection.push(element);
17962
+ let parsedPdb = Object.values(parsePdbCollection(pdbFileData, description, commands));
17963
+ parsedPdb.forEach(element => {
17964
+ collection[id] = element;
17854
17965
  });
17855
17966
  }
17856
17967
  }
@@ -17859,27 +17970,30 @@ class Events {
17859
17970
  // Do something if only JSON files are present
17860
17971
  jsonFiles.forEach(async file => {
17861
17972
  let fileData = await file.async('text');
17862
- parseJsonCollection(fileData).forEach(element => {
17863
- collection.push(element);
17973
+ const parsedJson = Object.values(parseJsonCollection(fileData));
17974
+ parsedJson.forEach(element => {
17975
+ collection[element[0]] = element;
17864
17976
  });
17865
17977
  });
17866
17978
  } else if (hasPdb) {
17867
17979
  // Do something if only PDB files are present
17868
17980
  pdbFiles.forEach(async file => {
17869
17981
  let fileData = await file.async('text');
17870
- parsePdbCollection(fileData).forEach(element => {
17871
- collection.push(element);
17982
+ const parsedPdb = Object.values(parsedPdbCollection(fileData));
17983
+ parsedPdb.forEach(element => {
17984
+ collection[element[0]] = element;
17872
17985
  });
17873
17986
  });
17874
17987
  } else if (hasGz) {
17875
- let url = './script/pako.js';
17988
+ let url = './script/pako.min.js';
17876
17989
  await me.getAjaxPromise(url, 'script');
17877
17990
  try {
17878
17991
  for (const file of gzFiles) {
17879
17992
  let compressed = await file.async('uint8array');
17880
17993
  let decompressed = pako.inflate(compressed, { to: 'string' });
17881
- parsePdbCollection(decompressed).forEach(element => {
17882
- collection.push(element);
17994
+ const parsedPdb = Object.values(parsePdbCollection(decompressed));
17995
+ parsedPdb.forEach(element => {
17996
+ collection[element[0]] = element;
17883
17997
  });
17884
17998
  }
17885
17999
  } catch (error) {
@@ -17890,7 +18004,7 @@ class Events {
17890
18004
  console.error('Error loading ZIP file', error);
17891
18005
  }
17892
18006
  } else if (fileExtension === 'gz') {
17893
- let url = './script/pako.js';
18007
+ let url = './script/pako.min.js';
17894
18008
  await me.getAjaxPromise(url, 'script');
17895
18009
 
17896
18010
  try {
@@ -17907,6 +18021,8 @@ class Events {
17907
18021
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
17908
18022
  await ic.selectCollectionsCls.clickStructure(collection);
17909
18023
 
18024
+ ic.collections = collection;
18025
+
17910
18026
  $("#" + ic.pre + "collections_menu").trigger("change");
17911
18027
 
17912
18028
  me.htmlCls.clickMenuCls.setLogCmd(
@@ -17925,7 +18041,7 @@ class Events {
17925
18041
  throw new Error('Invalid file type');
17926
18042
  }
17927
18043
 
17928
- if (Object.keys(me.utilsCls.getStructures(ic.dAtoms))){
18044
+ if (ic.allData && Object.keys(ic.allData).length > 0) {
17929
18045
  $("#" + me.pre + "dl_collection_file").hide();
17930
18046
  $("#" + me.pre + "dl_collection_structures").show();
17931
18047
  $("#" + me.pre + "dl_collection_file_expand").show();
@@ -17946,6 +18062,17 @@ class Events {
17946
18062
  }
17947
18063
  });
17948
18064
 
18065
+ me.myEventCls.onIds("#" + me.pre + "collections_clear_commands", "click", function (e) {
18066
+ var selectedValues = $("#" + ic.pre + "collections_menu").val();
18067
+ selectedValues.forEach(function (selectedValue) {
18068
+ if (ic.allData[selectedValue]) {
18069
+ ic.allData[selectedValue]['commands'] = [];
18070
+ } else {
18071
+ console.warn("No data found for selectedValue:", selectedValue);
18072
+ }
18073
+ });
18074
+ });
18075
+
17949
18076
  me.myEventCls.onIds("#" + me.pre + "opendl_export_collections", "click", function (e) {
17950
18077
  me.htmlCls.dialogCls.openDlg("dl_export_collections", "Export Collections");
17951
18078
  });
@@ -20432,9 +20559,9 @@ class SetHtml {
20432
20559
 
20433
20560
  let pdbstr = '';
20434
20561
 
20435
- let bMergeIntoOne = true;
20436
- pdbstr += ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
20437
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
20562
+ let bMergeIntoOne = true, bOneLetterChain = true;
20563
+ pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain);
20564
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
20438
20565
 
20439
20566
  let url = me.htmlCls.baseUrl + "delphi/delphi.cgi";
20440
20567
 
@@ -29638,698 +29765,244 @@ class Stick {
29638
29765
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29639
29766
  */
29640
29767
 
29641
- class Strand {
29768
+ class FirstAtomObj {
29642
29769
  constructor(icn3d) {
29643
29770
  this.icn3d = icn3d;
29644
29771
  }
29645
29772
 
29646
- // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29647
- //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
29648
- //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
29649
- //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
29650
- //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
29651
- //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
29652
- //with bHighlight=1 and 3D objects with bHighlight=2.
29653
- createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
29654
- if(me.bNode) return;
29773
+ //Return the first atom in the atom hash, which has the atom serial number as the key.
29774
+ getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29775
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29776
+ return undefined;
29777
+ }
29655
29778
 
29656
- let bRibbon = fill ? true: false;
29779
+ let atomKeys = Object.keys(atomsHash);
29780
+ let firstIndex = atomKeys[0];
29657
29781
 
29658
- // when highlight, the input atoms may only include one rediue.
29659
- // add one extra residue to show the strand
29660
- let atomsAdjust = {};
29782
+ return ic.atoms[firstIndex];
29783
+ }
29661
29784
 
29662
- let residueHashTmp = ic.firstAtomObjCls.getResiduesFromAtoms(atoms);
29663
- if( Object.keys(residueHashTmp).length == 1) {
29664
- atomsAdjust = this.getOneExtraResidue(residueHashTmp);
29785
+ // n is the position of the selected atom
29786
+ getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
29787
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29788
+ return undefined;
29665
29789
  }
29666
- else {
29667
- atomsAdjust = atoms;
29790
+
29791
+ let atomKeys = Object.keys(atomsHash);
29792
+ let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
29793
+
29794
+ return ic.atoms[middleIndex];
29795
+ }
29796
+
29797
+ getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29798
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29799
+ return undefined;
29668
29800
  }
29669
29801
 
29670
- if(bHighlight === 2) {
29671
- if(fill) {
29672
- fill = false;
29673
- num = null;
29674
- div = null;
29675
- coilWidth = null;
29676
- helixSheetWidth = null;
29677
- thickness = undefined;
29802
+ let firstIndex;
29803
+
29804
+ for(let i in atomsHash) {
29805
+ if(ic.atoms[i].name == 'CA') {
29806
+ firstIndex = i;
29807
+ break;
29678
29808
  }
29679
- else {
29680
- fill = true;
29681
- num = 2;
29682
- div = undefined;
29683
- coilWidth = undefined;
29684
- helixSheetWidth = undefined;
29685
- thickness = ic.ribbonthickness;
29809
+ }
29810
+
29811
+ if(!firstIndex) {
29812
+ for(let i in atomsHash) {
29813
+ if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
29814
+ firstIndex = i;
29815
+ break;
29816
+ }
29686
29817
  }
29687
29818
  }
29688
29819
 
29689
- num = num || ic.strandDIV;
29690
- div = div || ic.axisDIV;
29691
- coilWidth = coilWidth || ic.coilWidth;
29692
- doNotSmoothen = doNotSmoothen || false;
29693
- helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
29694
- let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
29695
- let pntsCA = [];
29696
- let prevCOArray = [];
29697
- let bShowArray = [];
29698
- let calphaIdArray = []; // used to store one of the final positions drawn in 3D
29699
- let colors = [];
29700
- let currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
29701
- let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevAtomSelected = null, prevResi = null, calphaid = null, prevCalphaid = null;
29702
- let strandWidth, bSheetSegment = false, bHelixSegment = false;
29820
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
29821
+ }
29703
29822
 
29704
- // For each chain, test the first 30 atoms to see whether only C-alpha is available
29705
- let bCalphaOnlyHash = {};
29706
- for(let chainid in ic.chains) {
29707
- let atoms = me.hashUtilsCls.hash2Atoms(ic.chains[chainid], ic.atoms);
29708
- let bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atoms); //, 'CA');
29709
- bCalphaOnlyHash[chainid] = bCalphaOnly;
29823
+ getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
29824
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29825
+ return ic.atoms[0];
29710
29826
  }
29711
29827
 
29712
- // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
29713
- let residueHash = {};
29714
- for(let i in atomsAdjust) {
29715
- let atom = ic.atoms[i];
29828
+ let firstIndex;
29716
29829
 
29717
- let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
29718
- residueHash[residueid] = 1;
29830
+ for(let i in atomsHash) {
29831
+ if(ic.atoms[i].name == atomName) {
29832
+ firstIndex = i;
29833
+ break;
29834
+ }
29719
29835
  }
29720
- Object.keys(residueHash).length;
29721
-
29722
- let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
29723
29836
 
29724
- let caArray = []; // record all C-alpha atoms to predict the helix
29837
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
29838
+ }
29725
29839
 
29726
- let maxDist = 6.0;
29840
+ //Return the last atom in the atom hash, which has the atom serial number as the key.
29841
+ getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29842
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
29843
+ return ic.atoms[0];
29844
+ }
29727
29845
 
29728
- //get the last residue
29729
- let atomArray = Object.keys(atomsAdjust);
29730
- let lastAtomSerial = atomArray[atomArray.length - 1];
29731
- let lastAtom = ic.atoms[lastAtomSerial];
29732
- let lastResid = lastAtom.structure + '_' + lastAtom.chain + '_' + lastAtom.resi;
29846
+ let atomKeys = Object.keys(atomsHash);
29847
+ let lastIndex = atomKeys[atomKeys.length - 1];
29733
29848
 
29734
- for (let i in atomsAdjust) {
29735
- let atom = ic.atoms[i];
29736
- let chainid = atom.structure + '_' + atom.chain;
29737
- let resid = atom.structure + '_' + atom.chain + '_' + atom.resi;
29738
- if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
29739
- // "CA" has to appear before "O"
29849
+ return ic.atoms[lastIndex];
29850
+ }
29740
29851
 
29741
- if (atom.name === 'CA') {
29742
- if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) ;
29852
+ //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
29853
+ getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29854
+ let residuesHash = {};
29855
+ for(let i in atomsHash) {
29856
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
29857
+ residuesHash[residueid] = 1;
29858
+ }
29743
29859
 
29744
- currentCA = atom.coord;
29745
- currentColor = atom.color;
29746
- calphaid = atom.serial;
29860
+ return residuesHash;
29861
+ }
29747
29862
 
29748
- caArray.push(atom.serial);
29863
+ getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29864
+ let residuesHash = {};
29865
+ for(let i in atomsHash) {
29866
+ if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
29867
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
29868
+ //residuesHash[residueid] = 1;
29869
+ residuesHash[residueid] = ic.atoms[i].resn;
29749
29870
  }
29871
+ }
29750
29872
 
29751
- if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA')) {
29752
- if(currentCA === null || currentCA === undefined) {
29753
- currentCA = atom.coord;
29754
- currentColor = atom.color;
29755
- calphaid = atom.serial;
29756
- }
29873
+ return residuesHash;
29874
+ }
29757
29875
 
29758
- if(atom.name === 'O') {
29759
- currentO = atom.coord;
29760
- }
29761
- // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
29762
-
29763
- // let bSameChain = true;
29764
- // if (currentChain !== atom.chain) {
29765
- // //if (currentChain !== atom.chain) {
29766
- // bSameChain = false;
29767
- // }
29876
+ //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
29877
+ getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
29878
+ let chainsHash = {};
29879
+ for(let i in atomsHash) {
29880
+ let atom = ic.atoms[i];
29881
+ let chainid = atom.structure + "_" + atom.chain;
29768
29882
 
29769
- if(atom.ssend && atom.ss === 'sheet') {
29770
- bSheetSegment = true;
29771
- }
29772
- else if( (atom.ssend && atom.ss === 'helix') || resid == lastResid) { // partial sheet will draw as helix
29773
- bHelixSegment = true;
29883
+ chainsHash[chainid] = 1;
29884
+ }
29885
+
29886
+ return chainsHash;
29887
+ }
29888
+
29889
+ getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
29890
+ if(ic.residues.hasOwnProperty(resid)) {
29891
+ for(let i in ic.residues[resid]) {
29892
+ if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
29893
+ return ic.atoms[i];
29774
29894
  }
29895
+ }
29896
+ }
29775
29897
 
29776
- // assign the previous residue
29777
- if(prevCoorO) {
29778
- if(bHighlight === 1 || bHighlight === 2) {
29779
- colors.push(ic.hColor);
29780
- }
29781
- else {
29782
- colors.push(prevColor);
29783
- }
29898
+ return undefined;
29899
+ }
29784
29900
 
29785
- if(ss !== 'coil' && atom.ss === 'coil') {
29786
- strandWidth = coilWidth;
29787
- }
29788
- else if(ssend && atom.ssbegin) { // a transition between two ss
29789
- strandWidth = coilWidth;
29790
- }
29791
- else {
29792
- strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
29793
- }
29901
+ getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
29902
+ let atom = this.getAtomFromResi(resid, atomName);
29903
+ if(atom !== undefined) {
29904
+ let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
29794
29905
 
29795
- let O, oldCA, resSpan = 4;
29796
- if(atom.name === 'O') {
29797
- O = prevCoorO.clone();
29798
- if(prevCoorCA !== null && prevCoorCA !== undefined) {
29799
- O.sub(prevCoorCA);
29800
- }
29801
- else {
29802
- prevCoorCA = prevCoorO.clone();
29803
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29804
- O = prevCoorCA.clone();
29805
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
29806
- //O.sub(oldCA);
29807
- oldCA.sub(O);
29808
- }
29809
- else {
29810
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29811
- }
29812
- }
29813
- }
29814
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
29815
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29816
- O = prevCoorCA.clone();
29817
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
29818
- //O.sub(oldCA);
29819
- oldCA.sub(O);
29820
- }
29821
- else {
29822
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29823
- }
29824
- }
29906
+ return coord;
29907
+ }
29825
29908
 
29826
- O.normalize(); // can be omitted for performance
29827
- O.multiplyScalar(strandWidth);
29828
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
29829
- prevCO = O;
29909
+ return undefined;
29910
+ }
29911
+ }
29830
29912
 
29831
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
29832
- let delta = -1 + numM1Inv2 * j;
29833
- let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
29834
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
29835
- pnts[j].push(v);
29836
- }
29913
+ /**
29914
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29915
+ */
29837
29916
 
29838
- pntsCA.push(prevCoorCA);
29839
- prevCOArray.push(prevCO);
29917
+ class Strip {
29918
+ constructor(icn3d) {
29919
+ this.icn3d = icn3d;
29920
+ }
29840
29921
 
29841
- if(atoms.hasOwnProperty(prevAtomid)) {
29842
- bShowArray.push(prevResi);
29843
- calphaIdArray.push(prevCalphaid);
29844
- }
29845
- else {
29846
- bShowArray.push(0);
29847
- calphaIdArray.push(0);
29848
- }
29849
- }
29922
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29923
+ createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
29924
+ calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
29925
+ if(me.bNode) return;
29850
29926
 
29851
- //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);
29927
+ if (p0.length < 2) return;
29928
+ div = div || ic.axisDIV;
29852
29929
 
29853
- let prevCoor = (prevAtomSelected) ? prevAtomSelected.coord : undefined;
29854
-
29855
- 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);
29856
-
29857
- // check whether the atoms are continuous
29858
- // atomsAdjusted has all atoms in the secondary structure
29859
- // atoms has all selected atoms
29860
- // let bBrokenSs = false;
29861
- // if(prevAtomSelected && prevAtomid == prevAtomSelected.serial && !atoms.hasOwnProperty(atom.serial)) {
29862
- // bBrokenSs = true;
29863
- // }
29930
+ // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
29931
+ if(pntsCA && ic.bDoublecolor) {
29932
+ let bExtendLastRes = false; //true;
29864
29933
 
29934
+ let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29935
+ pntsCA = pnts_clrs[0];
29865
29936
 
29866
- // The following code didn't work to select one residue
29867
- // 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);
29937
+ this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
29868
29938
 
29869
- // if(bBrokenSs && atom.ss === 'sheet') {
29870
- // bSheetSegment = true;
29871
- // }
29872
- // else if(bBrokenSs && atom.ss === 'helix') {
29873
- // bHelixSegment = true;
29874
- // }
29939
+ for(let i = 0, il = prevCOArray.length; i < il; ++i) {
29940
+ prevCOArray[i].normalize();
29941
+ }
29875
29942
 
29876
- //if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
29877
- // 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
29943
+ let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29944
+ prevCOArray = pnts_clrs2[0];
29878
29945
 
29879
- if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || bBrokenSs || (resid == lastResid && atom.ss != 'coil')) && pnts[0].length > 0) {
29880
- let atomName = 'CA';
29881
-
29882
- let prevone = [], nexttwo = [];
29946
+ colors = pnts_clrs[2];
29947
+ }
29948
+ else {
29883
29949
 
29884
- if(isNaN(ic.atoms[prevAtomid].resi)) {
29885
- prevone = [];
29886
- }
29887
- else {
29888
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
29889
- let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
29890
- prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
29891
- }
29950
+ if(!bNoSmoothen) {
29951
+ //var bExtendLastRes = true;
29952
+ let bExtendLastRes = false;
29953
+ let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29954
+ let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29955
+ p0 = pnts_clrs0[0];
29956
+ p1 = pnts_clrs1[0];
29957
+ colors = pnts_clrs0[2];
29958
+ }
29959
+ if (p0.length < 2) return;
29892
29960
 
29893
- if(!isNaN(ic.atoms[prevAtomid].resi)) {
29894
- let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
29895
- let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
29896
- if(nextoneCoord !== undefined) {
29897
- nexttwo.push(nextoneCoord);
29898
- }
29961
+ this.setCalphaDrawnCoord(p0, div, calphaIdArray);
29962
+ }
29899
29963
 
29900
- let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
29901
- let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
29902
- if(nexttwoCoord !== undefined) {
29903
- nexttwo.push(nexttwoCoord);
29904
- }
29905
- }
29964
+ if(bHighlight === 1) {
29965
+ //mesh = new THREE.Mesh(geo, ic.matShader);
29906
29966
 
29907
- // include the current residue
29908
- if(!bBrokenSs) {
29909
- // assign the current joint residue to the previous segment
29910
- if(bHighlight === 1 || bHighlight === 2) {
29911
- colors.push(ic.hColor);
29912
- }
29913
- else {
29914
- //colors.push(atom.color);
29915
- colors.push(prevColor);
29916
- }
29967
+ let radius = ic.coilWidth / 2;
29968
+ //var radiusSegments = 8;
29969
+ let radiusSegments = 4; // save memory
29970
+ let closed = false;
29917
29971
 
29918
- if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
29919
- strandWidth = 0; // make the arrow end sharp
29920
- }
29921
- else if(ss === 'coil' && atom.ssbegin) {
29922
- strandWidth = coilWidth;
29923
- }
29924
- else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
29925
- strandWidth = coilWidth;
29926
- }
29927
- else { // use the ss from the previous residue
29928
- strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
29929
- }
29972
+ if(positions !== undefined) {
29973
+ let currPos, prevPos;
29974
+ let currP0 = [], currP1 = [];
29930
29975
 
29931
- let O, oldCA, resSpan = 4;
29932
- if(atom.name === 'O') {
29933
- O = currentO.clone();
29934
- O.sub(currentCA);
29935
- }
29936
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
29937
- if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29938
- O = currentCA.clone();
29939
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
29940
- //O.sub(oldCA);
29941
- oldCA.sub(O);
29942
- }
29943
- else {
29944
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29945
- }
29946
- }
29976
+ for(let i = 0, il = p0.length; i < il; ++i) {
29977
+ currPos = positions[i];
29947
29978
 
29948
- O.normalize(); // can be omitted for performance
29949
- O.multiplyScalar(strandWidth);
29950
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
29951
- prevCO = O;
29979
+ if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
29980
+ // first tube
29981
+ let geometry0 = new THREE.TubeGeometry(
29982
+ new THREE.CatmullRomCurve3(currP0), // path
29983
+ currP0.length, // segments
29984
+ radius,
29985
+ radiusSegments,
29986
+ closed
29987
+ );
29952
29988
 
29953
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
29954
- let delta = -1 + numM1Inv2 * j;
29955
- let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
29956
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
29957
- pnts[j].push(v);
29958
- }
29989
+ let mesh = new THREE.Mesh(geometry0, ic.matShader);
29990
+ mesh.renderOrder = ic.renderOrderPicking;
29991
+ //ic.mdlPicking.add(mesh);
29992
+ ic.mdl.add(mesh);
29959
29993
 
29960
- atomid = atom.serial;
29994
+ ic.prevHighlightObjects.push(mesh);
29961
29995
 
29962
- pntsCA.push(currentCA);
29963
- prevCOArray.push(prevCO);
29996
+ geometry0 = null;
29964
29997
 
29965
- // 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.
29966
- //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
29967
- if(atoms.hasOwnProperty(atomid)) {
29968
- bShowArray.push(atom.resi);
29969
- calphaIdArray.push(calphaid);
29970
- }
29971
- else {
29972
- bShowArray.push(0);
29973
- calphaIdArray.push(0);
29974
- }
29975
- }
29976
-
29977
- // draw the current segment
29978
- for (let j = 0; !fill && j < num; ++j) {
29979
- if(bSheetSegment) {
29980
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29981
- }
29982
- else if(bHelixSegment) {
29983
- if(bFullAtom) {
29984
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
29985
- }
29986
- else {
29987
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29988
- }
29989
- }
29990
- }
29991
- if (fill) {
29992
- if(bSheetSegment) {
29993
- let start = 0, end = num - 1;
29994
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29995
- }
29996
- else if(bHelixSegment) {
29997
- if(bFullAtom) {
29998
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29999
- }
30000
- else {
30001
- let start = 0, end = num - 1;
30002
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30003
- }
30004
- }
30005
- else {
30006
- if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
30007
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30008
- }
30009
- }
30010
- }
30011
- for (let k = 0; k < num; ++k) pnts[k] = [];
30012
-
30013
- colors = [];
30014
- pntsCA = [];
30015
- prevCOArray = [];
30016
- bShowArray = [];
30017
- calphaIdArray = [];
30018
- bSheetSegment = false;
30019
- bHelixSegment = false;
30020
- } // end if (atom.ssbegin || atom.ssend)
30021
-
30022
- // end of a chain, or end of selection
30023
- if ((currentChain !== atom.chain
30024
- || ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi)
30025
- // || (drawnResidueCount === totalResidueCount - 1)
30026
- // || bBrokenSs
30027
- || (resid == lastResid && atom.ss != 'coil')
30028
- ) && pnts[0].length > 0) {
30029
- //if ((currentChain !== atom.chain) && pnts[0].length > 0) {
30030
-
30031
- let atomName = 'CA';
30032
-
30033
- let prevone = [], nexttwo = [];
30034
- if(isNaN(ic.atoms[prevAtomid].resi)) {
30035
- prevone = [];
30036
- }
30037
- else {
30038
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30039
- ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30040
- }
30041
-
30042
- for (let j = 0; !fill && j < num; ++j) {
30043
- if(bSheetSegment) {
30044
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30045
- }
30046
- else if(bHelixSegment) {
30047
- if(bFullAtom) {
30048
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
30049
- }
30050
- else {
30051
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30052
- }
30053
- }
30054
- }
30055
- if (fill) {
30056
- if(bSheetSegment) {
30057
- let start = 0, end = num - 1;
30058
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30059
- }
30060
- else if(bHelixSegment) {
30061
- if(bFullAtom) {
30062
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30063
- }
30064
- else {
30065
- let start = 0, end = num - 1;
30066
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30067
- }
30068
- }
30069
- }
30070
-
30071
- for (let k = 0; k < num; ++k) pnts[k] = [];
30072
- colors = [];
30073
- pntsCA = [];
30074
- prevCOArray = [];
30075
- bShowArray = [];
30076
- calphaIdArray = [];
30077
- bSheetSegment = false;
30078
- bHelixSegment = false;
30079
- }
30080
-
30081
- currentChain = atom.chain;
30082
- currentResi = atom.resi;
30083
- ss = atom.ss;
30084
- ssend = atom.ssend;
30085
- prevAtomid = atom.serial;
30086
- if(atoms.hasOwnProperty(atom.serial)) prevAtomSelected = atom;
30087
- prevResi = atom.resi;
30088
-
30089
- prevCalphaid = calphaid;
30090
-
30091
- // only update when atom.name === 'O'
30092
- prevCoorCA = currentCA;
30093
- prevCoorO = atom.coord;
30094
- prevColor = currentColor;
30095
- } // end if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA') ) {
30096
- } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
30097
- } // end for
30098
-
30099
- caArray = [];
30100
-
30101
- // ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
30102
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30103
- ic.tubeCls.createTube(atomsAdjust, 'CA', coilWidth, bHighlight);
30104
- pnts = {};
30105
- }
30106
-
30107
- getOneExtraResidue(residueHash) { let ic = this.icn3d, me = ic.icn3dui;
30108
- let atomsAdjust = {};
30109
-
30110
- for(let resid in residueHash) {
30111
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[resid]);
30112
-
30113
- let residNcbi = ic.resid2ncbi[resid];
30114
- let resiNcbi = residNcbi.substr(residNcbi.lastIndexOf('_') + 1);
30115
-
30116
- let nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) + 1);
30117
- let nextResid = ic.ncbi2resid[nextResidNcbi];
30118
-
30119
- if(!nextResid) {
30120
- nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) - 1);
30121
- nextResid = ic.ncbi2resid[nextResidNcbi];
30122
- }
30123
-
30124
- if(nextResid) atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[nextResid]);
30125
- }
30126
-
30127
- return atomsAdjust;
30128
- }
30129
-
30130
- /*
30131
- getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
30132
- let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
30133
- let firstAtom, lastAtom;
30134
- let index = 0, length = Object.keys(atoms).length;
30135
-
30136
- let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
30137
- for(let serial in atoms) {
30138
- currChain = atoms[serial].structure + '_' + atoms[serial].chain;
30139
- currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
30140
- currAtom = atoms[serial];
30141
-
30142
- if(prevChain === undefined) firstAtom = atoms[serial];
30143
-
30144
- if( (currChain !== prevChain && prevChain !== undefined)
30145
- || (currResi !== prevResi && ic.resid2ncbi[currResi] !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) || index === length - 1) {
30146
- if( (currChain !== prevChain && prevChain !== undefined)
30147
- || (currResi !== prevResi && currResi !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) ) {
30148
- lastAtom = prevAtom;
30149
- }
30150
- else if(index === length - 1) {
30151
- lastAtom = currAtom;
30152
- }
30153
-
30154
- // fill the beginning
30155
- let beginResi = firstAtom.resi;
30156
- if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
30157
- for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
30158
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30159
- if(!ic.residues.hasOwnProperty(residueid)) break;
30160
-
30161
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30162
-
30163
- if(atom.ss === firstAtom.ss && atom.ssbegin) {
30164
- beginResi = atom.resi;
30165
- break;
30166
- }
30167
- }
30168
-
30169
- for(let i = beginResi; i < firstAtom.resi; ++i) {
30170
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30171
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30172
- ic.atoms));
30173
- }
30174
- }
30175
-
30176
- // add one extra residue for coils between strands/helix
30177
- if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
30178
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
30179
- if(ic.residues.hasOwnProperty(residueid)) {
30180
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30181
- ic.atoms));
30182
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30183
- }
30184
- }
30185
-
30186
- // fill the end
30187
- let endResi = lastAtom.resi;
30188
- // 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.
30189
-
30190
- if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
30191
-
30192
- let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
30193
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
30194
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30195
- if(!ic.residues.hasOwnProperty(residueid)) break;
30196
-
30197
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30198
-
30199
- if(atom.ss === lastAtom.ss && atom.ssend) {
30200
- endResi = atom.resi;
30201
- break;
30202
- }
30203
- }
30204
-
30205
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
30206
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30207
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30208
- ic.atoms));
30209
- }
30210
- }
30211
-
30212
- // add one extra residue for coils between strands/helix
30213
- if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
30214
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
30215
- if(ic.residues.hasOwnProperty(residueid)) {
30216
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30217
- ic.atoms));
30218
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30219
- }
30220
- }
30221
-
30222
- // reset notshow
30223
- if(lastAtom.notshow) lastAtom.notshow = undefined;
30224
-
30225
- firstAtom = currAtom;
30226
- }
30227
-
30228
- prevChain = currChain;
30229
- prevResi = currResi;
30230
- prevAtom = currAtom;
30231
-
30232
- ++index;
30233
- }
30234
-
30235
- return atomsAdjust;
30236
- }
30237
- */
30238
- }
30239
-
30240
- /**
30241
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
30242
- */
30243
-
30244
- class Strip {
30245
- constructor(icn3d) {
30246
- this.icn3d = icn3d;
30247
- }
30248
-
30249
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30250
- createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
30251
- calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
30252
- if(me.bNode) return;
30253
-
30254
- if (p0.length < 2) return;
30255
- div = div || ic.axisDIV;
30256
-
30257
- // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
30258
- if(pntsCA && ic.bDoublecolor) {
30259
- let bExtendLastRes = false; //true;
30260
-
30261
- let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30262
- pntsCA = pnts_clrs[0];
30263
-
30264
- this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
30265
-
30266
- for(let i = 0, il = prevCOArray.length; i < il; ++i) {
30267
- prevCOArray[i].normalize();
30268
- }
30269
-
30270
- let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30271
- prevCOArray = pnts_clrs2[0];
30272
-
30273
- colors = pnts_clrs[2];
30274
- }
30275
- else {
30276
-
30277
- if(!bNoSmoothen) {
30278
- //var bExtendLastRes = true;
30279
- let bExtendLastRes = false;
30280
- let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30281
- let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
30282
- p0 = pnts_clrs0[0];
30283
- p1 = pnts_clrs1[0];
30284
- colors = pnts_clrs0[2];
30285
- }
30286
- if (p0.length < 2) return;
30287
-
30288
- this.setCalphaDrawnCoord(p0, div, calphaIdArray);
30289
- }
30290
-
30291
- if(bHighlight === 1) {
30292
- //mesh = new THREE.Mesh(geo, ic.matShader);
30293
-
30294
- let radius = ic.coilWidth / 2;
30295
- //var radiusSegments = 8;
30296
- let radiusSegments = 4; // save memory
30297
- let closed = false;
30298
-
30299
- if(positions !== undefined) {
30300
- let currPos, prevPos;
30301
- let currP0 = [], currP1 = [];
30302
-
30303
- for(let i = 0, il = p0.length; i < il; ++i) {
30304
- currPos = positions[i];
30305
-
30306
- if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
30307
- // first tube
30308
- let geometry0 = new THREE.TubeGeometry(
30309
- new THREE.CatmullRomCurve3(currP0), // path
30310
- currP0.length, // segments
30311
- radius,
30312
- radiusSegments,
30313
- closed
30314
- );
30315
-
30316
- let mesh = new THREE.Mesh(geometry0, ic.matShader);
30317
- mesh.renderOrder = ic.renderOrderPicking;
30318
- //ic.mdlPicking.add(mesh);
30319
- ic.mdl.add(mesh);
30320
-
30321
- ic.prevHighlightObjects.push(mesh);
30322
-
30323
- geometry0 = null;
30324
-
30325
- // second tube
30326
- let geometry1 = new THREE.TubeGeometry(
30327
- new THREE.CatmullRomCurve3(currP1), // path
30328
- currP1.length, // segments
30329
- radius,
30330
- radiusSegments,
30331
- closed
30332
- );
29998
+ // second tube
29999
+ let geometry1 = new THREE.TubeGeometry(
30000
+ new THREE.CatmullRomCurve3(currP1), // path
30001
+ currP1.length, // segments
30002
+ radius,
30003
+ radiusSegments,
30004
+ closed
30005
+ );
30333
30006
 
30334
30007
  mesh = new THREE.Mesh(geometry1, ic.matShader);
30335
30008
  mesh.renderOrder = ic.renderOrderPicking;
@@ -30605,6 +30278,7 @@ class Tube {
30605
30278
  atom.structure + '_' + atom.chain + '_' + (parseInt(atom.resi) - 1).toString();
30606
30279
 
30607
30280
  if (index > 0 && (currentChain !== atom.chain || Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist
30281
+ || (prevAtom.ssbegin) // e.g., https://www.ncbi.nlm.nih.gov/Structure/icn3d/?pdbid=7JO8 where a beta sheet has just two residues
30608
30282
  // || (parseInt(currentResi) + 1 < parseInt(atom.resi) && (Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist2 || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist2 || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist2) && ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]) && ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]).ss == 'helix')
30609
30283
  || (ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 < ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + atom.chain, atom.resi) && (Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist2 || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist2 || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist2))
30610
30284
  ) ) {
@@ -30697,7 +30371,7 @@ class Tube {
30697
30371
  }
30698
30372
 
30699
30373
  // draw all atoms in tubes and assign zero radius when the residue is not coil
30700
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30374
+ // if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30701
30375
 
30702
30376
  //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
30703
30377
  radii.push(radiusFinal);
@@ -30832,232 +30506,778 @@ class Tube {
30832
30506
 
30833
30507
  pnts.push(atom.coord);
30834
30508
 
30835
- let radiusFinal;
30836
- if(bCustom) {
30837
- radiusFinal = this.getCustomtubesize(atom.structure + '_' + atom.chain + '_' + atom.resi);
30838
- }
30839
- else {
30840
- radiusFinal = this.getRadius(radius, atom);
30841
- }
30509
+ let radiusFinal;
30510
+ if(bCustom) {
30511
+ radiusFinal = this.getCustomtubesize(atom.structure + '_' + atom.chain + '_' + atom.resi);
30512
+ }
30513
+ else {
30514
+ radiusFinal = this.getRadius(radius, atom);
30515
+ }
30516
+
30517
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
30518
+ if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30519
+
30520
+ //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
30521
+ radii.push(radiusFinal);
30522
+
30523
+ colors.push(atom.color);
30524
+ // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
30525
+ if(index === 1) colors[colors.length - 2] = atom.color;
30526
+
30527
+ currentChain = atom.chain;
30528
+ currentResi = atom.resi;
30529
+
30530
+ let scale = 1.2;
30531
+ if(bHighlight === 2 && !atom.ssbegin) {
30532
+ ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
30533
+ }
30534
+
30535
+ ++index;
30536
+
30537
+ prevAtom = atom;
30538
+ }
30539
+ }
30540
+
30541
+ if(bHighlight !== 2) {
30542
+ pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
30543
+ }
30544
+
30545
+ for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
30546
+ let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
30547
+ let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
30548
+ let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
30549
+ let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
30550
+ let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
30551
+
30552
+ this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
30553
+ }
30554
+
30555
+ pnts_colors_radii_prevone_nexttwo = [];
30556
+ }
30557
+ */
30558
+
30559
+ getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
30560
+ let pos = resid.lastIndexOf('_');
30561
+ let resi = resid.substr(pos + 1);
30562
+ let chainid = resid.substr(0, pos);
30563
+
30564
+ let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
30565
+
30566
+ return radiusFinal;
30567
+ };
30568
+
30569
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30570
+ createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
30571
+ if(me.bNode) return;
30572
+
30573
+ if (_pnts.length < 2) return;
30574
+
30575
+ let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
30576
+ let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
30577
+ //var geo = new THREE.Geometry();
30578
+ let geo = new THREE.BufferGeometry();
30579
+ let verticeArray = [], colorArray = [],indexArray = [], color;
30580
+ let offset = 0, offset2 = 0, offset3 = 0;
30581
+
30582
+ let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
30583
+
30584
+ let pnts = pnts_clrs[0];
30585
+ colors = pnts_clrs[2];
30586
+
30587
+ let constRadiius;
30588
+ // a threshold to stop drawing the tube if it's less than this ratio of radius
30589
+ let thresholdRatio = 1; //0.9;
30590
+
30591
+ let prevAxis1 = new THREE.Vector3(), prevAxis2;
30592
+ for (let i = 0, lim = pnts.length; i < lim; ++i) {
30593
+ let r, idx = (i - 1) * axisDivInv;
30594
+
30595
+ if (i === 0) {
30596
+ r = radii[0];
30597
+ if(r > 0) constRadiius = r;
30598
+ }
30599
+ else {
30600
+ if (idx % 1 === 0) {
30601
+ r = radii[idx];
30602
+ if(r > 0) constRadiius = r;
30603
+ }
30604
+ else {
30605
+ let floored = Math.floor(idx);
30606
+ let tmp = idx - floored;
30607
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
30608
+ // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
30609
+ r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
30610
+
30611
+ // a threshold to stop drawing the tube if it's less than this ratio of radius.
30612
+ // The extra bit of tube connects coil with strands or helices
30613
+ if(!bNonCoil) {
30614
+ if(r < thresholdRatio * constRadiius) {
30615
+ r = 0;
30616
+ }
30617
+ // else if(r < constRadiius) {
30618
+ // r *= 0.5; // use small radius for the connection between coild and sheets/helices
30619
+ // }
30620
+ }
30621
+ }
30622
+ }
30623
+ let delta, axis1, axis2;
30624
+ if (i < lim - 1) {
30625
+ delta = pnts[i].clone().sub(pnts[i + 1]);
30626
+ axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
30627
+ axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
30628
+ // let dir = 1, offset = 0;
30629
+ if (prevAxis1.dot(axis1) < 0) {
30630
+ axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
30631
+ }
30632
+ prevAxis1 = axis1; prevAxis2 = axis2;
30633
+ } else {
30634
+ axis1 = prevAxis1; axis2 = prevAxis2;
30635
+ }
30636
+ for (let j = 0; j < circleDiv; ++j) {
30637
+ let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
30638
+ let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
30639
+ verticeArray[offset++] = point.x;
30640
+ verticeArray[offset++] = point.y;
30641
+ verticeArray[offset++] = point.z;
30642
+
30643
+ color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
30644
+ colorArray[offset2++] = color.r;
30645
+ colorArray[offset2++] = color.g;
30646
+ colorArray[offset2++] = color.b;
30647
+ }
30648
+ }
30649
+ let offsetTmp = 0, nComp = 3;
30650
+ for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
30651
+ let reg = 0;
30652
+ //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
30653
+ //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
30654
+ let pos = offsetTmp * nComp;
30655
+ let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30656
+ pos = (offsetTmp + circleDiv) * nComp;
30657
+ let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30658
+ pos = (offsetTmp + circleDiv + 1) * nComp;
30659
+ let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30660
+
30661
+ let r1 = point1.clone().sub(point2).lengthSq();
30662
+ let r2 = point1.clone().sub(point3).lengthSq();
30663
+ if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
30664
+ //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
30665
+ //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
30666
+ //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
30667
+ indexArray[offset3++] = offsetTmp + j;
30668
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30669
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30670
+
30671
+ //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
30672
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30673
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30674
+ indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
30675
+ }
30676
+ offsetTmp += circleDiv;
30677
+ }
30678
+
30679
+ geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
30680
+ geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
30681
+
30682
+ geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
30683
+ //geo.setIndex(indexArray);
30684
+
30685
+ //geo.computeFaceNormals();
30686
+ //geo.computeVertexNormals(false);
30687
+ geo.computeVertexNormals();
30688
+
30689
+ let mesh;
30690
+ if(bHighlight === 2) {
30691
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
30692
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30693
+
30694
+ if(ic.mdl) ic.mdl.add(mesh);
30695
+ }
30696
+ else if(bHighlight === 1) {
30697
+ mesh = new THREE.Mesh(geo, ic.matShader);
30698
+ mesh.renderOrder = ic.renderOrderPicking;
30699
+ //ic.mdlPicking.add(mesh);
30700
+ if(ic.mdl) ic.mdl.add(mesh);
30701
+ }
30702
+ else {
30703
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
30704
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30705
+
30706
+ if(ic.mdl) ic.mdl.add(mesh);
30707
+ }
30708
+
30709
+ if(bHighlight === 1 || bHighlight === 2) {
30710
+ ic.prevHighlightObjects.push(mesh);
30711
+ }
30712
+ else {
30713
+ ic.objects.push(mesh);
30714
+ }
30715
+ }
30716
+
30717
+ getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
30718
+ let radiusFinal = radius;
30719
+ if(radius) {
30720
+ radiusFinal = radius;
30721
+ }
30722
+ else {
30723
+ if(atom.b > 0 && atom.b <= 100) {
30724
+ radiusFinal = atom.b * 0.01;
30725
+ }
30726
+ else if(atom.b > 100) {
30727
+ radiusFinal = 100 * 0.01;
30728
+ }
30729
+ else {
30730
+ radiusFinal = ic.coilWidth;
30731
+ }
30732
+ }
30733
+
30734
+ return radiusFinal;
30735
+ }
30736
+ }
30737
+
30738
+ /**
30739
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
30740
+ */
30741
+
30742
+ class Strand {
30743
+ constructor(icn3d) {
30744
+ this.icn3d = icn3d;
30745
+ }
30746
+
30747
+ // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30748
+ //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
30749
+ //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
30750
+ //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
30751
+ //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
30752
+ //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
30753
+ //with bHighlight=1 and 3D objects with bHighlight=2.
30754
+ createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
30755
+ if(me.bNode) return;
30756
+
30757
+ let bRibbon = fill ? true: false;
30758
+
30759
+ // when highlight, the input atoms may only include part of sheet or helix
30760
+ // include the whole sheet or helix when highlighting
30761
+ let atomsAdjust = {};
30762
+
30763
+ //if( (bHighlight === 1 || bHighlight === 2) && !ic.bAllAtoms) {
30764
+ //if( !ic.bAllAtoms) {
30765
+ if( Object.keys(atoms).length < Object.keys(ic.atoms).length) {
30766
+ atomsAdjust = this.getSSExpandedAtoms(atoms);
30767
+ }
30768
+ else {
30769
+ atomsAdjust = atoms;
30770
+ }
30771
+
30772
+ if(bHighlight === 2) {
30773
+ if(fill) {
30774
+ fill = false;
30775
+ num = null;
30776
+ div = null;
30777
+ coilWidth = null;
30778
+ helixSheetWidth = null;
30779
+ thickness = undefined;
30780
+ }
30781
+ else {
30782
+ fill = true;
30783
+ num = 2;
30784
+ div = undefined;
30785
+ coilWidth = undefined;
30786
+ helixSheetWidth = undefined;
30787
+ thickness = ic.ribbonthickness;
30788
+ }
30789
+ }
30790
+
30791
+ num = num || ic.strandDIV;
30792
+ div = div || ic.axisDIV;
30793
+ coilWidth = coilWidth || ic.coilWidth;
30794
+ doNotSmoothen = doNotSmoothen || false;
30795
+ helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
30796
+ let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
30797
+ let pntsCA = [];
30798
+ let prevCOArray = [];
30799
+ let bShowArray = [];
30800
+ let calphaIdArray = []; // used to store one of the final positions drawn in 3D
30801
+ let colors = [];
30802
+ let currentChain, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
30803
+ let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevResi = null, calphaid = null, prevCalphaid = null;
30804
+ let strandWidth, bSheetSegment = false, bHelixSegment = false;
30805
+ let atom, tubeAtoms = {};
30806
+
30807
+ // test the first 30 atoms to see whether only C-alpha is available
30808
+ ic.bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atomsAdjust); //, 'CA');
30809
+
30810
+ // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
30811
+ let residueHash = {};
30812
+ for(let i in atomsAdjust) {
30813
+ let atom = atomsAdjust[i];
30814
+
30815
+ let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
30816
+ residueHash[residueid] = 1;
30817
+ }
30818
+ let totalResidueCount = Object.keys(residueHash).length;
30819
+
30820
+ let drawnResidueCount = 0;
30821
+
30822
+ let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
30823
+
30824
+ let caArray = []; // record all C-alpha atoms to predict the helix
30825
+
30826
+ for (let i in atomsAdjust) {
30827
+ atom = atomsAdjust[i];
30828
+ if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
30829
+ // "CA" has to appear before "O"
30830
+
30831
+ if (atom.name === 'CA') {
30832
+ if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) {
30833
+ tubeAtoms[i] = atom;
30834
+ }
30835
+
30836
+ currentCA = atom.coord;
30837
+ currentColor = atom.color;
30838
+ calphaid = atom.serial;
30839
+
30840
+ caArray.push(atom.serial);
30841
+ }
30842
+
30843
+ if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA')) {
30844
+ if(currentCA === null || currentCA === undefined) {
30845
+ currentCA = atom.coord;
30846
+ currentColor = atom.color;
30847
+ calphaid = atom.serial;
30848
+ }
30849
+
30850
+ if(atom.name === 'O') {
30851
+ currentO = atom.coord;
30852
+ }
30853
+ // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
30854
+ let bSameChain = true;
30855
+ // if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) {
30856
+ if (currentChain !== atom.chain) {
30857
+ bSameChain = false;
30858
+ }
30859
+
30860
+ if(atom.ssend && atom.ss === 'sheet') {
30861
+ bSheetSegment = true;
30862
+ }
30863
+ else if(atom.ssend && atom.ss === 'helix') {
30864
+ bHelixSegment = true;
30865
+ }
30866
+
30867
+ // assign the previous residue
30868
+ if(prevCoorO) {
30869
+ if(bHighlight === 1 || bHighlight === 2) {
30870
+ colors.push(ic.hColor);
30871
+ }
30872
+ else {
30873
+ colors.push(prevColor);
30874
+ }
30875
+
30876
+ if(ss !== 'coil' && atom.ss === 'coil') {
30877
+ strandWidth = coilWidth;
30878
+ }
30879
+ else if(ssend && atom.ssbegin) { // a transition between two ss
30880
+ strandWidth = coilWidth;
30881
+ }
30882
+ else {
30883
+ strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
30884
+ }
30885
+
30886
+ let O, oldCA, resSpan = 4;
30887
+ if(atom.name === 'O') {
30888
+ O = prevCoorO.clone();
30889
+ if(prevCoorCA !== null && prevCoorCA !== undefined) {
30890
+ O.sub(prevCoorCA);
30891
+ }
30892
+ else {
30893
+ prevCoorCA = prevCoorO.clone();
30894
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30895
+ O = prevCoorCA.clone();
30896
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
30897
+ //O.sub(oldCA);
30898
+ oldCA.sub(O);
30899
+ }
30900
+ else {
30901
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30902
+ }
30903
+ }
30904
+ }
30905
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
30906
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30907
+ O = prevCoorCA.clone();
30908
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
30909
+ //O.sub(oldCA);
30910
+ oldCA.sub(O);
30911
+ }
30912
+ else {
30913
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30914
+ }
30915
+ }
30916
+
30917
+ O.normalize(); // can be omitted for performance
30918
+ O.multiplyScalar(strandWidth);
30919
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
30920
+ prevCO = O;
30921
+
30922
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
30923
+ let delta = -1 + numM1Inv2 * j;
30924
+ let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
30925
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
30926
+ pnts[j].push(v);
30927
+ }
30928
+
30929
+ pntsCA.push(prevCoorCA);
30930
+ prevCOArray.push(prevCO);
30931
+
30932
+ if(atoms.hasOwnProperty(prevAtomid)) {
30933
+ bShowArray.push(prevResi);
30934
+ calphaIdArray.push(prevCalphaid);
30935
+ }
30936
+ else {
30937
+ bShowArray.push(0);
30938
+ calphaIdArray.push(0);
30939
+ }
30940
+
30941
+ ++drawnResidueCount;
30942
+ }
30943
+
30944
+ let maxDist = 6.0;
30945
+ let bBrokenSs = (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
30946
+ // The following code didn't work to select one residue
30947
+ // let bBrokenSs = !atoms.hasOwnProperty(atom.serial) || (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
30948
+
30949
+ // if(bBrokenSs && atom.ss === 'sheet') {
30950
+ // bSheetSegment = true;
30951
+ // }
30952
+ // else if(bBrokenSs && atom.ss === 'helix') {
30953
+ // bHelixSegment = true;
30954
+ // }
30955
+
30956
+ if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
30957
+ let atomName = 'CA';
30958
+
30959
+ let prevone = [], nexttwo = [];
30960
+
30961
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
30962
+ prevone = [];
30963
+ }
30964
+ else {
30965
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30966
+ let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30967
+ prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
30968
+ }
30969
+
30970
+ if(!isNaN(ic.atoms[prevAtomid].resi)) {
30971
+ let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
30972
+ let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
30973
+ if(nextoneCoord !== undefined) {
30974
+ nexttwo.push(nextoneCoord);
30975
+ }
30976
+
30977
+ let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
30978
+ let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
30979
+ if(nexttwoCoord !== undefined) {
30980
+ nexttwo.push(nexttwoCoord);
30981
+ }
30982
+ }
30983
+
30984
+ if(!bBrokenSs) { // include the current residue
30985
+ // assign the current joint residue to the previous segment
30986
+ if(bHighlight === 1 || bHighlight === 2) {
30987
+ colors.push(ic.hColor);
30988
+ }
30989
+ else {
30990
+ //colors.push(atom.color);
30991
+ colors.push(prevColor);
30992
+ }
30993
+
30994
+ if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
30995
+ strandWidth = 0; // make the arrow end sharp
30996
+ }
30997
+ else if(ss === 'coil' && atom.ssbegin) {
30998
+ strandWidth = coilWidth;
30999
+ }
31000
+ else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
31001
+ strandWidth = coilWidth;
31002
+ }
31003
+ else { // use the ss from the previous residue
31004
+ strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
31005
+ }
31006
+
31007
+ let O, oldCA, resSpan = 4;
31008
+ if(atom.name === 'O') {
31009
+ O = currentO.clone();
31010
+ O.sub(currentCA);
31011
+ }
31012
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
31013
+ if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
31014
+ O = currentCA.clone();
31015
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
31016
+ //O.sub(oldCA);
31017
+ oldCA.sub(O);
31018
+ }
31019
+ else {
31020
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
31021
+ }
31022
+ }
31023
+
31024
+ O.normalize(); // can be omitted for performance
31025
+ O.multiplyScalar(strandWidth);
31026
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
31027
+ prevCO = O;
31028
+
31029
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
31030
+ let delta = -1 + numM1Inv2 * j;
31031
+ let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
31032
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
31033
+ pnts[j].push(v);
31034
+ }
31035
+
31036
+ atomid = atom.serial;
31037
+
31038
+ pntsCA.push(currentCA);
31039
+ prevCOArray.push(prevCO);
31040
+
31041
+ // when a coil connects to a sheet and the last residue of coild is highlighted, the first sheet residue is set as atom.highlightStyle. This residue should not be shown.
31042
+ //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
31043
+ if(atoms.hasOwnProperty(atomid)) {
31044
+ bShowArray.push(atom.resi);
31045
+ calphaIdArray.push(calphaid);
31046
+ }
31047
+ else {
31048
+ bShowArray.push(0);
31049
+ calphaIdArray.push(0);
31050
+ }
31051
+ }
31052
+
31053
+ // draw the current segment
31054
+ for (let j = 0; !fill && j < num; ++j) {
31055
+ if(bSheetSegment) {
31056
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31057
+ }
31058
+ else if(bHelixSegment) {
31059
+ if(bFullAtom) {
31060
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
31061
+ }
31062
+ else {
31063
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31064
+ }
31065
+ }
31066
+ }
31067
+ if (fill) {
31068
+ if(bSheetSegment) {
31069
+ let start = 0, end = num - 1;
31070
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31071
+ }
31072
+ else if(bHelixSegment) {
31073
+ if(bFullAtom) {
31074
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
31075
+ }
31076
+ else {
31077
+ let start = 0, end = num - 1;
31078
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31079
+ }
31080
+ }
31081
+ else {
31082
+ if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
31083
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
31084
+ }
31085
+ }
31086
+ }
31087
+ for (let k = 0; k < num; ++k) pnts[k] = [];
31088
+
31089
+ colors = [];
31090
+ pntsCA = [];
31091
+ prevCOArray = [];
31092
+ bShowArray = [];
31093
+ calphaIdArray = [];
31094
+ bSheetSegment = false;
31095
+ bHelixSegment = false;
31096
+ } // end if (atom.ssbegin || atom.ssend)
30842
31097
 
30843
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30844
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
31098
+ // end of a chain
31099
+ // if ((currentChain !== atom.chain || currentResi + 1 !== atom.resi) && pnts[0].length > 0) {
31100
+ if ((currentChain !== atom.chain) && pnts[0].length > 0) {
30845
31101
 
30846
- //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
30847
- radii.push(radiusFinal);
31102
+ let atomName = 'CA';
30848
31103
 
30849
- colors.push(atom.color);
30850
- // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
30851
- if(index === 1) colors[colors.length - 2] = atom.color;
31104
+ let prevone = [], nexttwo = [];
31105
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
31106
+ prevone = [];
31107
+ }
31108
+ else {
31109
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
31110
+ ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
31111
+ }
30852
31112
 
30853
- currentChain = atom.chain;
30854
- currentResi = atom.resi;
31113
+ for (let j = 0; !fill && j < num; ++j) {
31114
+ if(bSheetSegment) {
31115
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31116
+ }
31117
+ else if(bHelixSegment) {
31118
+ if(bFullAtom) {
31119
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
31120
+ }
31121
+ else {
31122
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31123
+ }
31124
+ }
31125
+ }
31126
+ if (fill) {
31127
+ if(bSheetSegment) {
31128
+ let start = 0, end = num - 1;
31129
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
31130
+ }
31131
+ else if(bHelixSegment) {
31132
+ if(bFullAtom) {
31133
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
31134
+ }
31135
+ else {
31136
+ let start = 0, end = num - 1;
31137
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
31138
+ }
31139
+ }
31140
+ }
30855
31141
 
30856
- let scale = 1.2;
30857
- if(bHighlight === 2 && !atom.ssbegin) {
30858
- ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
31142
+ for (let k = 0; k < num; ++k) pnts[k] = [];
31143
+ colors = [];
31144
+ pntsCA = [];
31145
+ prevCOArray = [];
31146
+ bShowArray = [];
31147
+ calphaIdArray = [];
31148
+ bSheetSegment = false;
31149
+ bHelixSegment = false;
30859
31150
  }
30860
31151
 
30861
- ++index;
31152
+ currentChain = atom.chain;
31153
+ ss = atom.ss;
31154
+ ssend = atom.ssend;
31155
+ prevAtomid = atom.serial;
31156
+ prevResi = atom.resi;
30862
31157
 
30863
- prevAtom = atom;
30864
- }
30865
- }
31158
+ prevCalphaid = calphaid;
30866
31159
 
30867
- if(bHighlight !== 2) {
30868
- pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
30869
- }
31160
+ // only update when atom.name === 'O'
31161
+ prevCoorCA = currentCA;
31162
+ prevCoorO = atom.coord;
31163
+ prevColor = currentColor;
31164
+ } // end if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA') ) {
31165
+ } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
31166
+ } // end for
30870
31167
 
30871
- for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
30872
- let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
30873
- let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
30874
- let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
30875
- let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
30876
- let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
31168
+ caArray = [];
30877
31169
 
30878
- this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
30879
- }
31170
+ ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
30880
31171
 
30881
- pnts_colors_radii_prevone_nexttwo = [];
31172
+ tubeAtoms = {};
31173
+ pnts = {};
30882
31174
  }
30883
- */
30884
-
30885
- getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
30886
- let pos = resid.lastIndexOf('_');
30887
- let resi = resid.substr(pos + 1);
30888
- let chainid = resid.substr(0, pos);
30889
-
30890
- let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
30891
-
30892
- return radiusFinal;
30893
- };
30894
-
30895
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
30896
- createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
30897
- if(me.bNode) return;
30898
31175
 
30899
- if (_pnts.length < 2) return;
30900
-
30901
- let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
30902
- let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
30903
- //var geo = new THREE.Geometry();
30904
- let geo = new THREE.BufferGeometry();
30905
- let verticeArray = [], colorArray = [],indexArray = [], color;
30906
- let offset = 0, offset2 = 0, offset3 = 0;
31176
+ getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
31177
+ let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
31178
+ let firstAtom, lastAtom;
31179
+ let index = 0, length = Object.keys(atoms).length;
30907
31180
 
30908
- let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
31181
+ let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
31182
+ for(let serial in atoms) {
31183
+ currChain = atoms[serial].structure + '_' + atoms[serial].chain;
31184
+ currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
31185
+ currAtom = atoms[serial];
30909
31186
 
30910
- let pnts = pnts_clrs[0];
30911
- colors = pnts_clrs[2];
31187
+ if(prevChain === undefined) firstAtom = atoms[serial];
30912
31188
 
30913
- let constRadiius;
30914
- // a threshold to stop drawing the tube if it's less than this ratio of radius
30915
- let thresholdRatio = 1; //0.9;
31189
+ if( (currChain !== prevChain && prevChain !== undefined)
31190
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) || index === length - 1) {
31191
+ if( (currChain !== prevChain && prevChain !== undefined)
31192
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) ) {
31193
+ lastAtom = prevAtom;
31194
+ }
31195
+ else if(index === length - 1) {
31196
+ lastAtom = currAtom;
31197
+ }
30916
31198
 
30917
- let prevAxis1 = new THREE.Vector3(), prevAxis2;
30918
- for (let i = 0, lim = pnts.length; i < lim; ++i) {
30919
- let r, idx = (i - 1) * axisDivInv;
31199
+ // fill the beginning
31200
+ let beginResi = firstAtom.resi;
31201
+ if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
31202
+ for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
31203
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
31204
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30920
31205
 
30921
- if (i === 0) {
30922
- r = radii[0];
30923
- if(r > 0) constRadiius = r;
30924
- }
30925
- else {
30926
- if (idx % 1 === 0) {
30927
- r = radii[idx];
30928
- if(r > 0) constRadiius = r;
30929
- }
30930
- else {
30931
- let floored = Math.floor(idx);
30932
- let tmp = idx - floored;
30933
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30934
- // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
30935
- r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
31206
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30936
31207
 
30937
- // a threshold to stop drawing the tube if it's less than this ratio of radius.
30938
- // The extra bit of tube connects coil with strands or helices
30939
- if(!bNonCoil) {
30940
- if(r < thresholdRatio * constRadiius) {
30941
- r = 0;
30942
- }
30943
- // else if(r < constRadiius) {
30944
- // r *= 0.5; // use small radius for the connection between coild and sheets/helices
30945
- // }
31208
+ if(atom.ss === firstAtom.ss && atom.ssbegin) {
31209
+ beginResi = atom.resi;
31210
+ break;
30946
31211
  }
30947
31212
  }
30948
- }
30949
- let delta, axis1, axis2;
30950
- if (i < lim - 1) {
30951
- delta = pnts[i].clone().sub(pnts[i + 1]);
30952
- axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
30953
- axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
30954
- // let dir = 1, offset = 0;
30955
- if (prevAxis1.dot(axis1) < 0) {
30956
- axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
31213
+
31214
+ for(let i = beginResi; i < firstAtom.resi; ++i) {
31215
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
31216
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31217
+ ic.atoms));
30957
31218
  }
30958
- prevAxis1 = axis1; prevAxis2 = axis2;
30959
- } else {
30960
- axis1 = prevAxis1; axis2 = prevAxis2;
30961
31219
  }
30962
- for (let j = 0; j < circleDiv; ++j) {
30963
- let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
30964
- let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
30965
- verticeArray[offset++] = point.x;
30966
- verticeArray[offset++] = point.y;
30967
- verticeArray[offset++] = point.z;
30968
31220
 
30969
- color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
30970
- colorArray[offset2++] = color.r;
30971
- colorArray[offset2++] = color.g;
30972
- colorArray[offset2++] = color.b;
31221
+ // add one extra residue for coils between strands/helix
31222
+ if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
31223
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
31224
+ if(ic.residues.hasOwnProperty(residueid)) {
31225
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31226
+ ic.atoms));
31227
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
31228
+ }
30973
31229
  }
30974
- }
30975
- let offsetTmp = 0, nComp = 3;
30976
- for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
30977
- let reg = 0;
30978
- //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
30979
- //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
30980
- let pos = offsetTmp * nComp;
30981
- let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30982
- pos = (offsetTmp + circleDiv) * nComp;
30983
- let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30984
- pos = (offsetTmp + circleDiv + 1) * nComp;
30985
- let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30986
31230
 
30987
- let r1 = point1.clone().sub(point2).lengthSq();
30988
- let r2 = point1.clone().sub(point3).lengthSq();
30989
- if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
30990
- //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
30991
- //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
30992
- //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
30993
- indexArray[offset3++] = offsetTmp + j;
30994
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30995
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
31231
+ // fill the end
31232
+ let endResi = lastAtom.resi;
31233
+ // when a coil connects to a sheet and the last residue of coil is highlighted, the first sheet residue is set as atom.notshow. This residue should not be shown.
30996
31234
 
30997
- //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
30998
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30999
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
31000
- indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
31001
- }
31002
- offsetTmp += circleDiv;
31003
- }
31235
+ if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
31004
31236
 
31005
- geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
31006
- geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
31237
+ let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
31238
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
31239
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
31240
+ if(!ic.residues.hasOwnProperty(residueid)) break;
31007
31241
 
31008
- geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
31009
- //geo.setIndex(indexArray);
31242
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
31010
31243
 
31011
- //geo.computeFaceNormals();
31012
- //geo.computeVertexNormals(false);
31013
- geo.computeVertexNormals();
31244
+ if(atom.ss === lastAtom.ss && atom.ssend) {
31245
+ endResi = atom.resi;
31246
+ break;
31247
+ }
31248
+ }
31014
31249
 
31015
- let mesh;
31016
- if(bHighlight === 2) {
31017
- //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 }));
31018
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
31250
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
31251
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
31252
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31253
+ ic.atoms));
31254
+ }
31255
+ }
31019
31256
 
31020
- if(ic.mdl) ic.mdl.add(mesh);
31021
- }
31022
- else if(bHighlight === 1) {
31023
- mesh = new THREE.Mesh(geo, ic.matShader);
31024
- mesh.renderOrder = ic.renderOrderPicking;
31025
- //ic.mdlPicking.add(mesh);
31026
- if(ic.mdl) ic.mdl.add(mesh);
31027
- }
31028
- else {
31029
- //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
31030
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
31257
+ // add one extra residue for coils between strands/helix
31258
+ if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
31259
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
31260
+ if(ic.residues.hasOwnProperty(residueid)) {
31261
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
31262
+ ic.atoms));
31263
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
31264
+ }
31265
+ }
31031
31266
 
31032
- if(ic.mdl) ic.mdl.add(mesh);
31033
- }
31267
+ // reset notshow
31268
+ if(lastAtom.notshow) lastAtom.notshow = undefined;
31034
31269
 
31035
- if(bHighlight === 1 || bHighlight === 2) {
31036
- ic.prevHighlightObjects.push(mesh);
31037
- }
31038
- else {
31039
- ic.objects.push(mesh);
31040
- }
31041
- }
31270
+ firstAtom = currAtom;
31271
+ }
31042
31272
 
31043
- getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
31044
- let radiusFinal = radius;
31045
- if(radius) {
31046
- radiusFinal = radius;
31047
- }
31048
- else {
31049
- if(atom.b > 0 && atom.b <= 100) {
31050
- radiusFinal = atom.b * 0.01;
31051
- }
31052
- else if(atom.b > 100) {
31053
- radiusFinal = 100 * 0.01;
31054
- }
31055
- else {
31056
- radiusFinal = ic.coilWidth;
31057
- }
31273
+ prevChain = currChain;
31274
+ prevResi = currResi;
31275
+ prevAtom = currAtom;
31276
+
31277
+ ++index;
31058
31278
  }
31059
31279
 
31060
- return radiusFinal;
31280
+ return atomsAdjust;
31061
31281
  }
31062
31282
  }
31063
31283
 
@@ -48711,6 +48931,13 @@ class ShowAnno {
48711
48931
  } // align seq to structure
48712
48932
  }
48713
48933
  //ic.bAnnoShown = true;
48934
+
48935
+ if($("#" + ic.pre + "anno_ig").length && $("#" + ic.pre + "anno_ig")[0].checked) {
48936
+ ic.bRunRefnumAgain = true;
48937
+ await ic.annotationCls.setAnnoTabIg();
48938
+
48939
+ ic.bRunRefnumAgain = false;
48940
+ }
48714
48941
  }
48715
48942
 
48716
48943
  async showAnnoSeqData(nucleotide_chainid, chemical_chainid, chemical_set) { let ic = this.icn3d, me = ic.icn3dui;
@@ -54445,6 +54672,7 @@ class AlignParser {
54445
54672
 
54446
54673
  async downloadAlignmentPart2(data, seqalign, chainresiCalphaHash2) { let ic = this.icn3d, me = ic.icn3dui;
54447
54674
  //ic.init();
54675
+
54448
54676
  ic.loadAtomDataCls.loadAtomDataIn(data, undefined, 'align', seqalign);
54449
54677
 
54450
54678
  if(me.cfg.align === undefined && Object.keys(ic.structures).length == 1) {
@@ -54488,20 +54716,21 @@ class AlignParser {
54488
54716
  async loadOpmDataForAlign(data, seqalign, mmdbidArray) { let ic = this.icn3d, me = ic.icn3dui;
54489
54717
  let thisClass = this;
54490
54718
 
54491
- let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
54492
- let prms1 = me.getAjaxPromise(url, 'text');
54493
- let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
54494
- let prms2 = me.getAjaxPromise(url2, 'text');
54495
-
54496
- let allPromise = Promise.allSettled([prms1, prms2]);
54497
54719
  try {
54720
+ let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
54721
+ let prms1 = me.getAjaxPromise(url, 'text');
54722
+ let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
54723
+ let prms2 = me.getAjaxPromise(url2, 'text');
54724
+
54725
+ let allPromise = Promise.allSettled([prms1, prms2]);
54726
+
54498
54727
  let dataArray = await allPromise;
54499
-
54728
+
54500
54729
  let bFound = false;
54501
54730
  for(let i = 0, il = dataArray.length; i < il; ++i) {
54502
- //let opmdata = (me.bNode) ? dataArray[i] : dataArray[i].value;
54503
- let opmdata = dataArray[i].value;
54731
+ // if(dataArray[i].status == 'rejected') continue;
54504
54732
 
54733
+ let opmdata = dataArray[i].value;
54505
54734
  if(!opmdata) continue;
54506
54735
 
54507
54736
  ic.selectedPdbid = mmdbidArray[i];
@@ -54517,6 +54746,7 @@ class AlignParser {
54517
54746
  $("#" + ic.pre + "intra_mem_z").val(-ic.halfBilayerSize);
54518
54747
 
54519
54748
  ic.init(); // remove all previously loaded data
54749
+
54520
54750
  await thisClass.downloadAlignmentPart2(data, seqalign, chainresiCalphaHash);
54521
54751
 
54522
54752
  bFound = true;
@@ -59092,7 +59322,7 @@ class SdfParser {
59092
59322
  let sdfStr = await me.getAjaxPromise(urlSmiles, 'text');
59093
59323
 
59094
59324
  ic.init();
59095
- ic.bInputfile = true;
59325
+ //ic.bInputfile = true;
59096
59326
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + sdfStr : sdfStr;
59097
59327
  ic.InputfileType = 'sdf';
59098
59328
  await ic.sdfParserCls.loadSdfData(sdfStr);
@@ -61901,6 +62131,7 @@ class ParserUtils {
61901
62131
  ic.selectionCls.oneStructurePerWindow(); // for alignment
61902
62132
  ic.drawCls.draw();
61903
62133
  }
62134
+
61904
62135
  if(ic.bOpm) {
61905
62136
  let axis = new THREE.Vector3(1,0,0);
61906
62137
  let angle = -0.5 * Math.PI;
@@ -64438,7 +64669,8 @@ class LoadPDB {
64438
64669
 
64439
64670
  // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
64440
64671
  //This PDB parser feeds the viewer with the content of a PDB file, pdbData.
64441
- async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
64672
+ // async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
64673
+ loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
64442
64674
  let hAtoms = {};
64443
64675
 
64444
64676
  let bNMR = false;
@@ -69559,17 +69791,15 @@ class SelectCollections {
69559
69791
  }
69560
69792
 
69561
69793
  //Set the menu of defined sets with an array of defined names "commandnameArray".
69562
- setAtomMenu(nameArray) {
69794
+ setAtomMenu(collection) {
69563
69795
  let ic = this.icn3d;
69564
69796
  ic.icn3dui;
69565
69797
  let html = "";
69566
- //for(let i in ic.defNames2Atoms) {
69567
- for (let i = 0, il = nameArray.length; i < il; ++i) {
69568
- let name = nameArray[i][0];
69569
- let title = nameArray[i][1];
69570
- let description = nameArray[i][2];
69571
-
69798
+
69799
+ Object.entries(collection).forEach(([name, structure], index) => {
69572
69800
  let atomHash;
69801
+ let [id, title, description, commands, pdb] = structure;
69802
+
69573
69803
  if (
69574
69804
  ic.defNames2Atoms !== undefined &&
69575
69805
  ic.defNames2Atoms.hasOwnProperty(name)
@@ -69590,12 +69820,12 @@ class SelectCollections {
69590
69820
  }
69591
69821
  }
69592
69822
 
69593
- if (i == 0) {
69594
- html += "<option value='" + nameArray[0][0] + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
69595
- } else {
69823
+ if (index === 0) {
69824
+ html += "<option value='" + name + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
69825
+ } else {
69596
69826
  html += "<option value='" + name + "' data-description='" + description + "'>" + title + "</option>";
69597
- }
69598
- }
69827
+ }
69828
+ });
69599
69829
 
69600
69830
  return html;
69601
69831
  }
@@ -69649,7 +69879,7 @@ class SelectCollections {
69649
69879
  let nameArray = $(this).val();
69650
69880
  let nameStructure = $(this).find("option:selected").text();
69651
69881
  let selectedIndices = Array.from(this.selectedOptions).map(option => option.index);
69652
- let selectedIndicesMap = nameArray.reduce((map, name, i) => {
69882
+ nameArray.reduce((map, name, i) => {
69653
69883
  map[name] = selectedIndices[i];
69654
69884
  return map;
69655
69885
  }, {});
@@ -69686,13 +69916,13 @@ class SelectCollections {
69686
69916
  if (Object.keys(ic.structures).length == 0) {
69687
69917
  bAppend = false;
69688
69918
  }
69689
- await ic.pdbParserCls.loadPdbData(ic.pdbCollection[selectedIndicesMap[name]].join('\n'), undefined, undefined, bAppend);
69919
+ await ic.pdbParserCls.loadPdbData(ic.pdbCollection[name].join('\n'), undefined, undefined, bAppend);
69690
69920
  } else {
69691
69921
  await ic.chainalignParserCls.downloadMmdbAf(name, undefined, undefined, bNoDuplicate);
69692
69922
  }
69693
69923
  }
69694
69924
 
69695
- await loadStructure(collection[selectedIndicesMap[name]][4]).then(() => {
69925
+ await loadStructure(collection[name][4]).then(() => {
69696
69926
  ic.allData['all'] = {
69697
69927
  'atoms': ic.atoms,
69698
69928
  'proteins': ic.proteins,
@@ -69752,9 +69982,9 @@ class SelectCollections {
69752
69982
 
69753
69983
  ic.molTitle = ic.allData[name]['title'];
69754
69984
 
69755
- if (collection[selectedIndicesMap[name]][3] !== undefined && collection[selectedIndicesMap[name]][3].length > 0) {
69985
+ if (collection[name][3] !== undefined && collection[name][3].length > 0) {
69756
69986
  if (ic.allData[name]['commands'] == undefined) {
69757
- let commands = collection[selectedIndicesMap[name]][3];
69987
+ let commands = collection[name][3];
69758
69988
  ic.allData[name]['commands'] = commands;
69759
69989
  }
69760
69990
  }
@@ -72396,155 +72626,6 @@ class Resid2spec {
72396
72626
  }
72397
72627
  }
72398
72628
 
72399
- /**
72400
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
72401
- */
72402
-
72403
- class FirstAtomObj {
72404
- constructor(icn3d) {
72405
- this.icn3d = icn3d;
72406
- }
72407
-
72408
- //Return the first atom in the atom hash, which has the atom serial number as the key.
72409
- getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72410
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72411
- return undefined;
72412
- }
72413
-
72414
- let atomKeys = Object.keys(atomsHash);
72415
- let firstIndex = atomKeys[0];
72416
-
72417
- return ic.atoms[firstIndex];
72418
- }
72419
-
72420
- // n is the position of the selected atom
72421
- getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
72422
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72423
- return undefined;
72424
- }
72425
-
72426
- let atomKeys = Object.keys(atomsHash);
72427
- let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
72428
-
72429
- return ic.atoms[middleIndex];
72430
- }
72431
-
72432
- getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72433
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72434
- return undefined;
72435
- }
72436
-
72437
- let firstIndex;
72438
-
72439
- for(let i in atomsHash) {
72440
- if(ic.atoms[i].name == 'CA') {
72441
- firstIndex = i;
72442
- break;
72443
- }
72444
- }
72445
-
72446
- if(!firstIndex) {
72447
- for(let i in atomsHash) {
72448
- if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
72449
- firstIndex = i;
72450
- break;
72451
- }
72452
- }
72453
- }
72454
-
72455
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
72456
- }
72457
-
72458
- getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
72459
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72460
- return ic.atoms[0];
72461
- }
72462
-
72463
- let firstIndex;
72464
-
72465
- for(let i in atomsHash) {
72466
- if(ic.atoms[i].name == atomName) {
72467
- firstIndex = i;
72468
- break;
72469
- }
72470
- }
72471
-
72472
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
72473
- }
72474
-
72475
- //Return the last atom in the atom hash, which has the atom serial number as the key.
72476
- getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72477
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
72478
- return ic.atoms[0];
72479
- }
72480
-
72481
- let atomKeys = Object.keys(atomsHash);
72482
- let lastIndex = atomKeys[atomKeys.length - 1];
72483
-
72484
- return ic.atoms[lastIndex];
72485
- }
72486
-
72487
- //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
72488
- getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72489
- let residuesHash = {};
72490
- for(let i in atomsHash) {
72491
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
72492
- residuesHash[residueid] = 1;
72493
- }
72494
-
72495
- return residuesHash;
72496
- }
72497
-
72498
- getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72499
- let residuesHash = {};
72500
- for(let i in atomsHash) {
72501
- if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
72502
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
72503
- //residuesHash[residueid] = 1;
72504
- residuesHash[residueid] = ic.atoms[i].resn;
72505
- }
72506
- }
72507
-
72508
- return residuesHash;
72509
- }
72510
-
72511
- //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
72512
- getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
72513
- let chainsHash = {};
72514
- for(let i in atomsHash) {
72515
- let atom = ic.atoms[i];
72516
- let chainid = atom.structure + "_" + atom.chain;
72517
-
72518
- chainsHash[chainid] = 1;
72519
- }
72520
-
72521
- return chainsHash;
72522
- }
72523
-
72524
- getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
72525
- if(ic.residues.hasOwnProperty(resid)) {
72526
- for(let i in ic.residues[resid]) {
72527
- if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
72528
- return ic.atoms[i];
72529
- }
72530
- }
72531
- }
72532
-
72533
- return undefined;
72534
- }
72535
-
72536
- getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
72537
- let atom = this.getAtomFromResi(resid, atomName);
72538
- if(atom !== undefined) {
72539
- let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
72540
-
72541
- return coord;
72542
- }
72543
-
72544
- return undefined;
72545
- }
72546
- }
72547
-
72548
72629
  /**
72549
72630
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
72550
72631
  */
@@ -72605,9 +72686,9 @@ class Delphi {
72605
72686
  let pdbstr = '';
72606
72687
  /// pdbstr += ic.saveFileCls.getPDBHeader();
72607
72688
 
72608
- let bMergeIntoOne = true;
72609
- pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
72610
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
72689
+ let bMergeIntoOne = true, bOneLetterChain = true;
72690
+ pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain);
72691
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
72611
72692
 
72612
72693
  return pdbstr;
72613
72694
  }
@@ -79877,7 +79958,7 @@ class SaveFile {
79877
79958
  }
79878
79959
 
79879
79960
  //getAtomPDB: function(atomHash, bPqr, bPdb, bNoChem) { let ic = this.icn3d, me = ic.icn3dui;
79880
- getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bVastSearch) { let ic = this.icn3d, me = ic.icn3dui;
79961
+ getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bOneLetterChain) { let ic = this.icn3d, me = ic.icn3dui;
79881
79962
  let pdbStr = '';
79882
79963
 
79883
79964
  // get all phosphate groups in lipids
@@ -80152,7 +80233,7 @@ class SaveFile {
80152
80233
  //line +=(atom.chain.length <= 1) ? atom.chain.padStart(1, ' ') : atom.chain.substr(0, 1);
80153
80234
  if(atom.chain.length >= 2) {
80154
80235
  let chainTmp = atom.chain.replace(/_/gi, '').substr(0, 2);
80155
- if(bVastSearch) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
80236
+ if(bOneLetterChain) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
80156
80237
  line += chainTmp;
80157
80238
  }
80158
80239
  else if(atom.chain.length == 1) {
@@ -80231,8 +80312,8 @@ class SaveFile {
80231
80312
  }
80232
80313
  else {
80233
80314
  line += "1.00".padStart(6, ' ');
80234
- // line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : ' '.padStart(6, ' ');
80235
- let defaultBFactor = (bVastSearch) ? "1.0" : " ";
80315
+ // let defaultBFactor = (bOneLetterChain) ? "1.0" : " ";
80316
+ let defaultBFactor = " ";
80236
80317
  line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : defaultBFactor.padStart(6, ' ');
80237
80318
  line += ' '.padStart(10, ' ');
80238
80319
  line += atom.elem.padStart(2, ' ');
@@ -80280,7 +80361,7 @@ class SaveFile {
80280
80361
  let resn = me.utilsCls.residueName2Abbr(atom.resn);
80281
80362
  let ss = this.secondary2Abbr(atom.ss);
80282
80363
 
80283
- if(chainid != prevChainid) {
80364
+ if(chainid != prevChainid && !data[chainid]) {
80284
80365
  data[chainid] = {"resi": [], "resn": [], "secondary": []};
80285
80366
  }
80286
80367
 
@@ -83804,7 +83885,7 @@ class iCn3DUI {
83804
83885
  //even when multiple iCn3D viewers are shown together.
83805
83886
  this.pre = this.cfg.divid + "_";
83806
83887
 
83807
- this.REVISION = '3.40.0';
83888
+ this.REVISION = '3.40.3';
83808
83889
 
83809
83890
  // In nodejs, iCn3D defines "window = {navigator: {}}"
83810
83891
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -83966,7 +84047,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
83966
84047
  ic.molTitle = '';
83967
84048
  ic.loadCmd;
83968
84049
 
83969
- // set menus
84050
+ // set menus
83970
84051
  me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
83971
84052
  me.htmlCls.clickMenuCls.applyShownMenus();
83972
84053