icn3d 3.40.0 → 3.40.2

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 +1183 -1102
  2. package/icn3d.min.js +4 -4
  3. package/icn3d.module.js +1183 -1102
  4. package/package.json +1 -1
package/icn3d.js CHANGED
@@ -8827,25 +8827,42 @@ class ClickMenu {
8827
8827
  }
8828
8828
 
8829
8829
  getHiddenMenusFromCache() { let me = this.icn3dui; me.icn3d;
8830
- me.htmlCls.shownMenus = {};
8830
+ // me.htmlCls.shownMenus = {};
8831
8831
 
8832
- let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
8833
-
8834
- if(idArrayStr && idArrayStr != '[]') {
8835
- let idArray = JSON.parse(idArrayStr);
8832
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
8836
8833
 
8837
- // for(let i = 0, il = idArray.length; i < il; ++i) {
8838
- // me.htmlCls.shownMenus[idArray[i]] = 1;
8839
- // }
8840
- for(let menu in me.htmlCls.allMenus) {
8841
- if(idArray.indexOf(menu) == -1) {
8842
- me.htmlCls.shownMenus[menu] = 1;
8843
- }
8834
+ let idArrayStr = (localStorage) ? localStorage.getItem('hiddenmenus') : '';
8835
+
8836
+ if(idArrayStr && idArrayStr != '[]') {
8837
+ me.htmlCls.shownMenus = {};
8838
+
8839
+ let idArray = JSON.parse(idArrayStr);
8840
+
8841
+ // for(let i = 0, il = idArray.length; i < il; ++i) {
8842
+ // me.htmlCls.shownMenus[idArray[i]] = 1;
8843
+ // }
8844
+ for(let menu in me.htmlCls.allMenus) {
8845
+ if(idArray.indexOf(menu) == -1) {
8846
+ me.htmlCls.shownMenus[menu] = 1;
8844
8847
  }
8845
- }
8846
- else {
8847
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
8848
- }
8848
+ }
8849
+ }
8850
+ //###
8851
+ else {
8852
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
8853
+ }
8854
+
8855
+ // else {
8856
+ // if(mode == 'all') {
8857
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
8858
+ // }
8859
+ // else if(!mode || mode == 'simple') {
8860
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
8861
+ // }
8862
+ // else {
8863
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
8864
+ // }
8865
+ // }
8849
8866
  }
8850
8867
 
8851
8868
  displayShownMenus() { let me = this.icn3dui; me.icn3d;
@@ -9341,11 +9358,14 @@ class ClickMenu {
9341
9358
  me.htmlCls.shownMenus[checkbox.value] = 1;
9342
9359
  }
9343
9360
 
9361
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
9362
+
9344
9363
  thisClass.applyShownMenus();
9345
9364
  });
9346
9365
 
9347
9366
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref", "#" + me.pre + "reset_menupref2"], "click", function(e) { me.icn3d; //e.preventDefault();
9348
9367
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
9368
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'simple');
9349
9369
 
9350
9370
  thisClass.applyShownMenus();
9351
9371
  thisClass.displayShownMenus();
@@ -9353,6 +9373,7 @@ class ClickMenu {
9353
9373
 
9354
9374
  me.myEventCls.onIds(["#" + me.pre + "reset_menupref_all", "#" + me.pre + "reset_menupref_all2"], "click", function(e) { me.icn3d; //e.preventDefault();
9355
9375
  me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
9376
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'all');
9356
9377
 
9357
9378
  thisClass.applyShownMenus();
9358
9379
  thisClass.displayShownMenus();
@@ -9402,15 +9423,16 @@ class ClickMenu {
9402
9423
 
9403
9424
  thisClass.applyShownMenus();
9404
9425
  thisClass.displayShownMenus();
9426
+
9427
+ me.htmlCls.setHtmlCls.setCookie('menumode', 'custom');
9405
9428
  };
9406
9429
  reader.readAsText(file);
9407
9430
  }
9408
9431
  });
9409
9432
 
9410
- me.myEventCls.onIds("#" + me.pre + "mn1_menuloadpref", "click", function(e) { me.icn3d; //e.preventDefault();
9433
+ me.myEventCls.onIds(["#" + me.pre + "mn1_menuloadpref", "#" + me.pre + "loadpref", "#" + me.pre + "loadpref2"], "click", function(e) { me.icn3d; //e.preventDefault();
9411
9434
  me.htmlCls.dialogCls.openDlg('dl_menuloadpref', 'Please input the menu preference file');
9412
9435
  });
9413
-
9414
9436
 
9415
9437
  me.myEventCls.onIds("#" + me.pre + "mn1_link_structure", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
9416
9438
  let url = ic.saveFileCls.getLinkToStructureSummary(true);
@@ -11249,6 +11271,51 @@ class SetMenu {
11249
11271
  return me.htmlCls.setHtmlCls.getRadioColor(radioid, id, text, color, bChecked, bSimpleMenu, selType);
11250
11272
  }
11251
11273
 
11274
+ resetMenu(mode) { let me = this.icn3dui;
11275
+ if(!mode || mode == 'simple') {
11276
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11277
+
11278
+ me.htmlCls.clickMenuCls.applyShownMenus();
11279
+ }
11280
+ else if(mode == 'all') {
11281
+ me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.allMenus);
11282
+
11283
+ me.htmlCls.clickMenuCls.applyShownMenus();
11284
+ }
11285
+ else if(mode == 'custom') {
11286
+ me.htmlCls.dialogCls.openDlg('dl_menupref', 'Select Menus');
11287
+
11288
+ me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
11289
+
11290
+ me.htmlCls.clickMenuCls.displayShownMenus();
11291
+ }
11292
+ }
11293
+
11294
+ setMenuMode(bMobile) { let me = this.icn3dui;
11295
+ let spaceCss = (bMobile) ? "; padding-left:6px; background-color:#eee" : "; margin:3px; background-color:white";
11296
+ let spaceCss2 = (bMobile) ? "; font-size:14px!important" : "";
11297
+
11298
+ let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
11299
+
11300
+ let html = '<div class="icn3d-text" style="color:#f8b84e; font-weight:bold' + spaceCss + '">';
11301
+ html += '<select name="menumode" id="' + me.pre + 'menumode" class="icn3d-text" style="color:#f8b84e; font-weight:bold; border:0px' + spaceCss2 + '">';
11302
+ html += (mode == 'simple' || !mode) ? '<option value="simple" selected>Simple</option>' : '<option value="simple">Simple</option>';
11303
+ html += (mode == 'all') ? '<option value="all" selected>All</option>' : '<option value="all">All</option>';
11304
+ html += (mode == 'custom') ? '<option value="custom" selected>Custom</option>' : '<option value="custom">Custom</option>';
11305
+ html += '</select>';
11306
+
11307
+ if(bMobile) {
11308
+ html += '<br><span style="font-size:12px">&nbsp;Menus</span>';
11309
+ }
11310
+ else {
11311
+ html += '&nbsp;Menus';
11312
+ }
11313
+
11314
+ html += '</div>';
11315
+
11316
+ return html;
11317
+ }
11318
+
11252
11319
  //Set the HTML code for the menus shown at the top of the viewer.
11253
11320
  setTopMenusHtml(id, str1, str2) { let me = this.icn3dui;
11254
11321
  if(me.bNode) return '';
@@ -11268,6 +11335,9 @@ class SetMenu {
11268
11335
  html += "<table border='0' cellpadding='0' cellspacing='0' width='100'><tr>";
11269
11336
 
11270
11337
  let tdStr = '<td valign="top">';
11338
+
11339
+ // html += tdStr + this.setMenuMode() + '</td>';
11340
+
11271
11341
  html += tdStr + this.setMenu1() + '</td>';
11272
11342
 
11273
11343
  html += tdStr + this.setMenu2() + '</td>';
@@ -11280,7 +11350,11 @@ class SetMenu {
11280
11350
  //html += tdStr + this.setMenu5b() + '</td>';
11281
11351
  html += tdStr + this.setMenu6() + '</td>';
11282
11352
 
11283
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11353
+ // reset the menus at the end of the menus
11354
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
11355
+ // this.resetMenu(mode);
11356
+
11357
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11284
11358
 
11285
11359
  html += tdStr + "<div style='position:relative; margin-left:6px;'>" + str1;
11286
11360
  html += "<div class='icn3d-commandTitle' style='min-width:40px; margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -11376,6 +11450,7 @@ class SetMenu {
11376
11450
 
11377
11451
  //html += "<div class='icn3d-menu'>";
11378
11452
  html += "<div>";
11453
+
11379
11454
  html += "<accordion id='" + me.pre + "accordion0' class='icn3d-accordion'>";
11380
11455
  if(me.cfg.notebook) {
11381
11456
  html += "<h3 style='width:20px; height:24px; position:relative; padding: 0'><span style='position:absolute; left:3px; top:4px;'>&#9776;</span></h3>";
@@ -11385,6 +11460,8 @@ class SetMenu {
11385
11460
  }
11386
11461
  html += "<div>";
11387
11462
 
11463
+ // html += '<li>' + this.setMenuMode(true);
11464
+
11388
11465
  let liStr = "<li><span class='icn3d-menu-color'";
11389
11466
 
11390
11467
  html += "<ul class='icn3d-mn-item'>";
@@ -11403,7 +11480,11 @@ class SetMenu {
11403
11480
  html += liStr + ">Help</span>";
11404
11481
  html += this.setMenu6_base();
11405
11482
 
11406
- me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11483
+ // reset the menus at the end of the menus
11484
+ // let mode = me.htmlCls.setHtmlCls.getCookie('menumode');
11485
+ // this.resetMenu(mode);
11486
+
11487
+ // me.htmlCls.shownMenus = me.hashUtilsCls.cloneHash(me.htmlCls.simpleMenus);
11407
11488
 
11408
11489
  html += "<li><div style='position:relative; margin-top:-6px;'>" + str1;
11409
11490
  html += "<div class='icn3d-commandTitle' style='margin-top: 3px; white-space: nowrap;'>" + str2;
@@ -11580,7 +11661,7 @@ class SetMenu {
11580
11661
  // Analysis menu
11581
11662
  html += tdStrBorder + this.setIcon(iconType, 'tool_selectannotations', 'Sequences & Annotations', 'grip-lines') + "</td>";
11582
11663
  html += tdStr + this.setIcon(iconType, 'hbondsYes', 'Interactions', 'users') + "</td>";
11583
- html += tdStr + this.setIcon(iconType, 'tool_delphi', 'Delphi Potentials', 'cloud-meatball') + "</td>";
11664
+ html += tdStr + this.setIcon(iconType, 'tool_delphi', 'DelPhi Potentials', 'cloud-meatball') + "</td>";
11584
11665
  html += tdStr + this.setIcon(iconType, 'removeLabels', 'Remove Labels', 'remove-format') + "</td>";
11585
11666
 
11586
11667
  // Help menu
@@ -13597,7 +13678,7 @@ class Dialog {
13597
13678
  width='50%';
13598
13679
  }
13599
13680
  else if(id === me.pre + 'dl_menupref') {
13600
- width = 600;
13681
+ width = 800;
13601
13682
  height = 500;
13602
13683
  }
13603
13684
 
@@ -14258,6 +14339,10 @@ class SetDialog {
14258
14339
  html += me.htmlCls.divStr + "dl_collection_file' style=''>";
14259
14340
  html += "You can load a collection of structures via a file. Here are <a href='https://github.com/ncbi/icn3d/blob/master/example/collection/' target='_blank'>some example files</a><br><br>";
14260
14341
  html += "Collection file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "collectionfile'><br/>";
14342
+ html += "<input type='radio' id='dl_collectionAppendStructureNone' name='appendStructure' value='none' checked/>";
14343
+ html += "<label for='dl_collectionAppendStructureNone'>Default</label>";
14344
+ html += "<input type='radio' id='dl_collectionAppendStructure' name='appendStructure' value='append' />";
14345
+ html += "<label for='dl_collectionAppendStructure'>Append</label><br/>";
14261
14346
  html += me.htmlCls.buttonStr + "reload_collectionfile' style='margin-top: 6px;'>Load</button>";
14262
14347
  html += "</div>";
14263
14348
  html += "</div>";
@@ -14265,7 +14350,8 @@ class SetDialog {
14265
14350
  html += me.htmlCls.divStr + "dl_collection_structures' style='display: none'>";
14266
14351
  html += "<select id='" + me.pre + "collections_menu'multiple size='6' style='min-width:300px;'></select>";
14267
14352
  html += '<br/>';
14268
- html += me.htmlCls.buttonStr + "opendl_export_collections'>Export</button>";
14353
+ html += me.htmlCls.buttonStr + "collections_clear_commands' style='margin-top: 6px;'>Clear Commands</button>";
14354
+ html += me.htmlCls.buttonStr + "opendl_export_collections'>Export JSON</button>";
14269
14355
  html += "</div>";
14270
14356
  html += '<br/>';
14271
14357
  html += "</div>";
@@ -14970,13 +15056,15 @@ class SetDialog {
14970
15056
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref'>Apply</button></span>";
14971
15057
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref' style='margin-left:30px'>Reset to Simple Menus</button></span>";
14972
15058
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all' style='margin-left:30px'>Reset to All Menus</button></span>";
14973
- html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span><br><br>";
15059
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref' style='margin-left:30px'>Save Preferences</button></span>";
15060
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref' style='margin-left:30px'>Load Preferences</button></span><br><br>";
14974
15061
 
14975
15062
  html += "<div id='" + me.pre + "menulist'></div><br><br>";
14976
15063
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "apply_menupref2'>Apply</button></span>";
14977
15064
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref2' style='margin-left:30px'>Reset to Simple Menus</button></span>";
14978
15065
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "reset_menupref_all2' style='margin-left:30px'>Reset to All Menus</button></span>";
14979
15066
  html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "savepref2' style='margin-left:30px'>Save Preferences</button></span>";
15067
+ html += me.htmlCls.spanNowrapStr + "" + me.htmlCls.buttonStr + "loadpref2' style='margin-left:30px'>Load Preferences</button></span>";
14980
15068
  html += "</div>";
14981
15069
 
14982
15070
  html += me.htmlCls.divStr + "dl_addtrack' class='" + dialogClass + "'>";
@@ -15752,6 +15840,14 @@ class Events {
15752
15840
  me.htmlCls.clickMenuCls.clickMenu5();
15753
15841
  me.htmlCls.clickMenuCls.clickMenu6();
15754
15842
 
15843
+ me.myEventCls.onIds("#" + me.pre + "menumode", "change", async function(e) { me.icn3d;
15844
+ e.preventDefault();
15845
+ let mode = $("#" + me.pre + "menumode").val();
15846
+
15847
+ me.htmlCls.setHtmlCls.setCookie('menumode', mode);
15848
+ me.htmlCls.setMenuCls.resetMenu(mode);
15849
+ });
15850
+
15755
15851
  // back and forward arrows
15756
15852
  me.myEventCls.onIds(["#" + me.pre + "back", "#" + me.pre + "mn6_back"], "click", async function(e) { let ic = me.icn3d;
15757
15853
  e.preventDefault();
@@ -16798,95 +16894,108 @@ class Events {
16798
16894
  } else {
16799
16895
  ic.resizeCanvasCls.closeDialogs();
16800
16896
  }
16801
- ic.bInputfile = false;
16802
- ic.pdbCollection = [];
16803
- ic.allData = {};
16804
- ic.allData['all'] = {
16805
- 'atoms': {},
16806
- 'proteins': {},
16807
- 'nucleotides': {},
16808
- 'chemicals': {},
16809
- 'ions': {},
16810
- 'water': {},
16811
- 'structures': {}, // getSSExpandedAtoms
16812
- 'ssbondpnts': {},
16813
- 'residues': {}, // getSSExpandedAtoms
16814
- 'chains': {},
16815
- 'chainsSeq': {}, //Sequences and Annotation
16816
- 'defNames2Atoms': {},
16817
- 'defNames2Residues': {}
16818
- };
16819
- ic.allData['prev'] = {};
16820
- ic.selectCollectionsCls.reset();
16821
-
16897
+
16822
16898
  ic.dAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
16823
16899
  ic.hAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
16824
16900
  me.htmlCls.setHtmlCls.fileSupport();
16825
16901
 
16826
16902
  let fileName = file.name;
16827
16903
  let fileExtension = fileName.split('.').pop().toLowerCase();
16828
-
16904
+ let collection = {};
16905
+
16829
16906
  $("#" + ic.pre + "collections_menu").empty();
16830
16907
  $("#" + ic.pre + "collections_menu").off("change");
16908
+
16909
+ if (dl_collectionAppendStructureNone.checked || ic.allData === undefined) {
16910
+ ic.bInputfile = false;
16911
+ ic.pdbCollection = {};
16912
+ ic.allData = {};
16913
+ ic.allData['all'] = {
16914
+ 'atoms': {},
16915
+ 'proteins': {},
16916
+ 'nucleotides': {},
16917
+ 'chemicals': {},
16918
+ 'ions': {},
16919
+ 'water': {},
16920
+ 'structures': {}, // getSSExpandedAtoms
16921
+ 'ssbondpnts': {},
16922
+ 'residues': {}, // getSSExpandedAtoms
16923
+ 'chains': {},
16924
+ 'chainsSeq': {}, //Sequences and Annotation
16925
+ 'defNames2Atoms': {},
16926
+ 'defNames2Residues': {}
16927
+ };
16928
+ ic.allData['prev'] = {};
16929
+ ic.selectCollectionsCls.reset();
16930
+
16931
+ } else {
16932
+ if (ic.collections) {
16933
+ collection = ic.collections;
16934
+ }
16935
+ }
16831
16936
 
16832
16937
  function parseJsonCollection(data) {
16833
16938
  let dataStr = JSON.parse(data);
16834
- return dataStr["structures"].map(({ id, title, description, commands }) => {
16939
+ let parsedCollection = {};
16940
+
16941
+ dataStr["structures"].map(({ id, title, description, commands }) => {
16835
16942
  if (id && id.includes('.pdb')) {
16836
16943
  id = id.split('.pdb')[0];
16837
16944
  }
16838
- return [id, title, description, commands, false];
16945
+ parsedCollection[id] = [id, title, description, commands, false];
16839
16946
  });
16840
- }
16841
16947
 
16948
+ return parsedCollection;
16949
+ }
16950
+
16842
16951
  function parsePdbCollection(data, description = '', commands = []) {
16843
16952
  let dataStr = data;
16844
16953
  let lines = dataStr.split('\n');
16845
-
16846
16954
  let sections = [];
16847
16955
  let currentSection = [];
16848
-
16956
+
16849
16957
  lines.forEach(line => {
16850
- if (line.startsWith('HEADER')) {
16958
+ if (line.startsWith('HEADER')) {
16851
16959
  currentSection = [];
16852
16960
  sections.push(currentSection);
16853
- }
16854
- currentSection.push(line);
16961
+ }
16962
+ currentSection.push(line);
16855
16963
  });
16856
-
16857
- let ids = [];
16858
- let titles = [];
16859
-
16964
+
16965
+
16966
+ let parsedCollection = {};
16967
+
16860
16968
  sections.forEach((section) => {
16861
- let headerLine = section[0];
16862
- headerLine = headerLine.replace(/[\n\r]/g, '').trim();
16969
+ let headerLine = section[0].replace(/[\n\r]/g, '').trim();
16863
16970
  let header = headerLine.split(' ').filter(Boolean);
16864
- let lastElement = header[header.length - 1];
16865
- ids.push(lastElement);
16866
- titles.push(section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : lastElement);
16971
+ let id = header[header.length - 1];
16972
+ let title = section[1].startsWith('TITLE') ? section[1].split('TITLE').pop().trim() : id;
16973
+
16974
+ parsedCollection[id] = [id, title, description, commands, true];
16975
+
16976
+ const sanitizedSection = section.map(line => line.trim());
16977
+ ic.pdbCollection[id] = sanitizedSection;
16867
16978
  });
16868
-
16869
- if (sections.length > 0) {
16870
- ic.pdbCollection.push(...sections);
16871
- }
16872
16979
 
16873
- return ids.map((id, index, description, commands) => [id, titles[index], description, commands, true]);
16980
+ return parsedCollection;
16874
16981
  }
16875
16982
 
16876
- let collection = [];
16877
-
16878
16983
  if (fileExtension === 'json' || fileExtension === 'pdb') {
16879
16984
  let reader = new FileReader();
16880
16985
  reader.onload = async function (e) {
16881
16986
  if (fileExtension === 'json') {
16882
- collection = parseJsonCollection(e.target.result);
16987
+ let jsonCollection = parseJsonCollection(e.target.result);
16988
+ collection = { ...collection, ...jsonCollection };
16883
16989
  } else if (fileExtension === 'pdb') {
16884
16990
  ic.bInputfile = true;
16885
- collection = parsePdbCollection(e.target.result);
16991
+ let pdbCollection = parsePdbCollection(e.target.result);
16992
+ collection = { ...collection, ...pdbCollection };
16886
16993
  }
16887
16994
 
16888
16995
  let collectionHtml = await ic.selectCollectionsCls.setAtomMenu(collection);
16889
16996
 
16997
+ ic.collections = collection;
16998
+
16890
16999
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
16891
17000
  await ic.selectCollectionsCls.clickStructure(collection);
16892
17001
  $("#" + ic.pre + "collections_menu").trigger("change");
@@ -16904,7 +17013,7 @@ class Events {
16904
17013
  let reader2 = new FileReader();
16905
17014
  reader2.onload = async function (e) {
16906
17015
  if (fileExtension === 'zip') {
16907
- let url = './script/jszip.js';
17016
+ let url = './script/jszip.min.js';
16908
17017
  await me.getAjaxPromise(url, 'script');
16909
17018
 
16910
17019
  let jszip = new JSZip();
@@ -16938,7 +17047,8 @@ class Events {
16938
17047
  let jsonCollection = [];
16939
17048
  for (const file of jsonFiles) {
16940
17049
  let fileData = await file.async('text');
16941
- parseJsonCollection(fileData).forEach(element => {
17050
+ let parsedJson = Object.values(parseJsonCollection(fileData));
17051
+ parsedJson.forEach(element => {
16942
17052
  jsonCollection.push(element);
16943
17053
  });
16944
17054
  }
@@ -16948,8 +17058,9 @@ class Events {
16948
17058
  let matchingPdbFile = pdbFiles.find(file => file.name.toLowerCase().includes(id.toLowerCase()));
16949
17059
  if (matchingPdbFile) {
16950
17060
  let pdbFileData = await matchingPdbFile.async('text');
16951
- parsePdbCollection(pdbFileData, description, commands).forEach(element => {
16952
- collection.push(element);
17061
+ let parsedPdb = Object.values(parsePdbCollection(pdbFileData, description, commands));
17062
+ parsedPdb.forEach(element => {
17063
+ collection[id] = element;
16953
17064
  });
16954
17065
  }
16955
17066
  }
@@ -16958,27 +17069,30 @@ class Events {
16958
17069
  // Do something if only JSON files are present
16959
17070
  jsonFiles.forEach(async file => {
16960
17071
  let fileData = await file.async('text');
16961
- parseJsonCollection(fileData).forEach(element => {
16962
- collection.push(element);
17072
+ const parsedJson = Object.values(parseJsonCollection(fileData));
17073
+ parsedJson.forEach(element => {
17074
+ collection[element[0]] = element;
16963
17075
  });
16964
17076
  });
16965
17077
  } else if (hasPdb) {
16966
17078
  // Do something if only PDB files are present
16967
17079
  pdbFiles.forEach(async file => {
16968
17080
  let fileData = await file.async('text');
16969
- parsePdbCollection(fileData).forEach(element => {
16970
- collection.push(element);
17081
+ const parsedPdb = Object.values(parsedPdbCollection(fileData));
17082
+ parsedPdb.forEach(element => {
17083
+ collection[element[0]] = element;
16971
17084
  });
16972
17085
  });
16973
17086
  } else if (hasGz) {
16974
- let url = './script/pako.js';
17087
+ let url = './script/pako.min.js';
16975
17088
  await me.getAjaxPromise(url, 'script');
16976
17089
  try {
16977
17090
  for (const file of gzFiles) {
16978
17091
  let compressed = await file.async('uint8array');
16979
17092
  let decompressed = pako.inflate(compressed, { to: 'string' });
16980
- parsePdbCollection(decompressed).forEach(element => {
16981
- collection.push(element);
17093
+ const parsedPdb = Object.values(parsePdbCollection(decompressed));
17094
+ parsedPdb.forEach(element => {
17095
+ collection[element[0]] = element;
16982
17096
  });
16983
17097
  }
16984
17098
  } catch (error) {
@@ -16989,7 +17103,7 @@ class Events {
16989
17103
  console.error('Error loading ZIP file', error);
16990
17104
  }
16991
17105
  } else if (fileExtension === 'gz') {
16992
- let url = './script/pako.js';
17106
+ let url = './script/pako.min.js';
16993
17107
  await me.getAjaxPromise(url, 'script');
16994
17108
 
16995
17109
  try {
@@ -17006,6 +17120,8 @@ class Events {
17006
17120
  $("#" + ic.pre + "collections_menu").html(collectionHtml);
17007
17121
  await ic.selectCollectionsCls.clickStructure(collection);
17008
17122
 
17123
+ ic.collections = collection;
17124
+
17009
17125
  $("#" + ic.pre + "collections_menu").trigger("change");
17010
17126
 
17011
17127
  me.htmlCls.clickMenuCls.setLogCmd(
@@ -17024,7 +17140,7 @@ class Events {
17024
17140
  throw new Error('Invalid file type');
17025
17141
  }
17026
17142
 
17027
- if (Object.keys(me.utilsCls.getStructures(ic.dAtoms))){
17143
+ if (ic.allData && Object.keys(ic.allData).length > 0) {
17028
17144
  $("#" + me.pre + "dl_collection_file").hide();
17029
17145
  $("#" + me.pre + "dl_collection_structures").show();
17030
17146
  $("#" + me.pre + "dl_collection_file_expand").show();
@@ -17045,6 +17161,17 @@ class Events {
17045
17161
  }
17046
17162
  });
17047
17163
 
17164
+ me.myEventCls.onIds("#" + me.pre + "collections_clear_commands", "click", function (e) {
17165
+ var selectedValues = $("#" + ic.pre + "collections_menu").val();
17166
+ selectedValues.forEach(function (selectedValue) {
17167
+ if (ic.allData[selectedValue]) {
17168
+ ic.allData[selectedValue]['commands'] = [];
17169
+ } else {
17170
+ console.warn("No data found for selectedValue:", selectedValue);
17171
+ }
17172
+ });
17173
+ });
17174
+
17048
17175
  me.myEventCls.onIds("#" + me.pre + "opendl_export_collections", "click", function (e) {
17049
17176
  me.htmlCls.dialogCls.openDlg("dl_export_collections", "Export Collections");
17050
17177
  });
@@ -19531,9 +19658,9 @@ class SetHtml {
19531
19658
 
19532
19659
  let pdbstr = '';
19533
19660
 
19534
- let bMergeIntoOne = true;
19535
- pdbstr += ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
19536
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
19661
+ let bMergeIntoOne = true, bOneLetterChain = true;
19662
+ pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain);
19663
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
19537
19664
 
19538
19665
  let url = me.htmlCls.baseUrl + "delphi/delphi.cgi";
19539
19666
 
@@ -28737,698 +28864,244 @@ class Stick {
28737
28864
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
28738
28865
  */
28739
28866
 
28740
- class Strand {
28867
+ class FirstAtomObj {
28741
28868
  constructor(icn3d) {
28742
28869
  this.icn3d = icn3d;
28743
28870
  }
28744
28871
 
28745
- // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
28746
- //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
28747
- //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
28748
- //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
28749
- //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
28750
- //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
28751
- //with bHighlight=1 and 3D objects with bHighlight=2.
28752
- createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
28753
- if(me.bNode) return;
28872
+ //Return the first atom in the atom hash, which has the atom serial number as the key.
28873
+ getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28874
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28875
+ return undefined;
28876
+ }
28754
28877
 
28755
- let bRibbon = fill ? true: false;
28878
+ let atomKeys = Object.keys(atomsHash);
28879
+ let firstIndex = atomKeys[0];
28756
28880
 
28757
- // when highlight, the input atoms may only include one rediue.
28758
- // add one extra residue to show the strand
28759
- let atomsAdjust = {};
28881
+ return ic.atoms[firstIndex];
28882
+ }
28760
28883
 
28761
- let residueHashTmp = ic.firstAtomObjCls.getResiduesFromAtoms(atoms);
28762
- if( Object.keys(residueHashTmp).length == 1) {
28763
- atomsAdjust = this.getOneExtraResidue(residueHashTmp);
28884
+ // n is the position of the selected atom
28885
+ getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
28886
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28887
+ return undefined;
28764
28888
  }
28765
- else {
28766
- atomsAdjust = atoms;
28889
+
28890
+ let atomKeys = Object.keys(atomsHash);
28891
+ let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
28892
+
28893
+ return ic.atoms[middleIndex];
28894
+ }
28895
+
28896
+ getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28897
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28898
+ return undefined;
28767
28899
  }
28768
28900
 
28769
- if(bHighlight === 2) {
28770
- if(fill) {
28771
- fill = false;
28772
- num = null;
28773
- div = null;
28774
- coilWidth = null;
28775
- helixSheetWidth = null;
28776
- thickness = undefined;
28901
+ let firstIndex;
28902
+
28903
+ for(let i in atomsHash) {
28904
+ if(ic.atoms[i].name == 'CA') {
28905
+ firstIndex = i;
28906
+ break;
28777
28907
  }
28778
- else {
28779
- fill = true;
28780
- num = 2;
28781
- div = undefined;
28782
- coilWidth = undefined;
28783
- helixSheetWidth = undefined;
28784
- thickness = ic.ribbonthickness;
28908
+ }
28909
+
28910
+ if(!firstIndex) {
28911
+ for(let i in atomsHash) {
28912
+ if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
28913
+ firstIndex = i;
28914
+ break;
28915
+ }
28785
28916
  }
28786
28917
  }
28787
28918
 
28788
- num = num || ic.strandDIV;
28789
- div = div || ic.axisDIV;
28790
- coilWidth = coilWidth || ic.coilWidth;
28791
- doNotSmoothen = doNotSmoothen || false;
28792
- helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
28793
- let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
28794
- let pntsCA = [];
28795
- let prevCOArray = [];
28796
- let bShowArray = [];
28797
- let calphaIdArray = []; // used to store one of the final positions drawn in 3D
28798
- let colors = [];
28799
- let currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
28800
- let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevAtomSelected = null, prevResi = null, calphaid = null, prevCalphaid = null;
28801
- let strandWidth, bSheetSegment = false, bHelixSegment = false;
28919
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
28920
+ }
28802
28921
 
28803
- // For each chain, test the first 30 atoms to see whether only C-alpha is available
28804
- let bCalphaOnlyHash = {};
28805
- for(let chainid in ic.chains) {
28806
- let atoms = me.hashUtilsCls.hash2Atoms(ic.chains[chainid], ic.atoms);
28807
- let bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atoms); //, 'CA');
28808
- bCalphaOnlyHash[chainid] = bCalphaOnly;
28922
+ getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
28923
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28924
+ return ic.atoms[0];
28809
28925
  }
28810
28926
 
28811
- // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
28812
- let residueHash = {};
28813
- for(let i in atomsAdjust) {
28814
- let atom = ic.atoms[i];
28927
+ let firstIndex;
28815
28928
 
28816
- let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
28817
- residueHash[residueid] = 1;
28929
+ for(let i in atomsHash) {
28930
+ if(ic.atoms[i].name == atomName) {
28931
+ firstIndex = i;
28932
+ break;
28933
+ }
28818
28934
  }
28819
- Object.keys(residueHash).length;
28820
-
28821
- let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
28822
28935
 
28823
- let caArray = []; // record all C-alpha atoms to predict the helix
28936
+ return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
28937
+ }
28824
28938
 
28825
- let maxDist = 6.0;
28939
+ //Return the last atom in the atom hash, which has the atom serial number as the key.
28940
+ getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28941
+ if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
28942
+ return ic.atoms[0];
28943
+ }
28826
28944
 
28827
- //get the last residue
28828
- let atomArray = Object.keys(atomsAdjust);
28829
- let lastAtomSerial = atomArray[atomArray.length - 1];
28830
- let lastAtom = ic.atoms[lastAtomSerial];
28831
- let lastResid = lastAtom.structure + '_' + lastAtom.chain + '_' + lastAtom.resi;
28945
+ let atomKeys = Object.keys(atomsHash);
28946
+ let lastIndex = atomKeys[atomKeys.length - 1];
28832
28947
 
28833
- for (let i in atomsAdjust) {
28834
- let atom = ic.atoms[i];
28835
- let chainid = atom.structure + '_' + atom.chain;
28836
- let resid = atom.structure + '_' + atom.chain + '_' + atom.resi;
28837
- if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
28838
- // "CA" has to appear before "O"
28948
+ return ic.atoms[lastIndex];
28949
+ }
28839
28950
 
28840
- if (atom.name === 'CA') {
28841
- if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) ;
28951
+ //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
28952
+ getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28953
+ let residuesHash = {};
28954
+ for(let i in atomsHash) {
28955
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
28956
+ residuesHash[residueid] = 1;
28957
+ }
28842
28958
 
28843
- currentCA = atom.coord;
28844
- currentColor = atom.color;
28845
- calphaid = atom.serial;
28959
+ return residuesHash;
28960
+ }
28846
28961
 
28847
- caArray.push(atom.serial);
28962
+ getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28963
+ let residuesHash = {};
28964
+ for(let i in atomsHash) {
28965
+ if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
28966
+ let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
28967
+ //residuesHash[residueid] = 1;
28968
+ residuesHash[residueid] = ic.atoms[i].resn;
28848
28969
  }
28970
+ }
28849
28971
 
28850
- if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA')) {
28851
- if(currentCA === null || currentCA === undefined) {
28852
- currentCA = atom.coord;
28853
- currentColor = atom.color;
28854
- calphaid = atom.serial;
28855
- }
28972
+ return residuesHash;
28973
+ }
28856
28974
 
28857
- if(atom.name === 'O') {
28858
- currentO = atom.coord;
28859
- }
28860
- // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
28861
-
28862
- // let bSameChain = true;
28863
- // if (currentChain !== atom.chain) {
28864
- // //if (currentChain !== atom.chain) {
28865
- // bSameChain = false;
28866
- // }
28975
+ //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
28976
+ getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
28977
+ let chainsHash = {};
28978
+ for(let i in atomsHash) {
28979
+ let atom = ic.atoms[i];
28980
+ let chainid = atom.structure + "_" + atom.chain;
28867
28981
 
28868
- if(atom.ssend && atom.ss === 'sheet') {
28869
- bSheetSegment = true;
28870
- }
28871
- else if( (atom.ssend && atom.ss === 'helix') || resid == lastResid) { // partial sheet will draw as helix
28872
- bHelixSegment = true;
28982
+ chainsHash[chainid] = 1;
28983
+ }
28984
+
28985
+ return chainsHash;
28986
+ }
28987
+
28988
+ getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
28989
+ if(ic.residues.hasOwnProperty(resid)) {
28990
+ for(let i in ic.residues[resid]) {
28991
+ if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
28992
+ return ic.atoms[i];
28873
28993
  }
28994
+ }
28995
+ }
28874
28996
 
28875
- // assign the previous residue
28876
- if(prevCoorO) {
28877
- if(bHighlight === 1 || bHighlight === 2) {
28878
- colors.push(ic.hColor);
28879
- }
28880
- else {
28881
- colors.push(prevColor);
28882
- }
28997
+ return undefined;
28998
+ }
28883
28999
 
28884
- if(ss !== 'coil' && atom.ss === 'coil') {
28885
- strandWidth = coilWidth;
28886
- }
28887
- else if(ssend && atom.ssbegin) { // a transition between two ss
28888
- strandWidth = coilWidth;
28889
- }
28890
- else {
28891
- strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
28892
- }
29000
+ getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
29001
+ let atom = this.getAtomFromResi(resid, atomName);
29002
+ if(atom !== undefined) {
29003
+ let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
28893
29004
 
28894
- let O, oldCA, resSpan = 4;
28895
- if(atom.name === 'O') {
28896
- O = prevCoorO.clone();
28897
- if(prevCoorCA !== null && prevCoorCA !== undefined) {
28898
- O.sub(prevCoorCA);
28899
- }
28900
- else {
28901
- prevCoorCA = prevCoorO.clone();
28902
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
28903
- O = prevCoorCA.clone();
28904
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
28905
- //O.sub(oldCA);
28906
- oldCA.sub(O);
28907
- }
28908
- else {
28909
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
28910
- }
28911
- }
28912
- }
28913
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
28914
- if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
28915
- O = prevCoorCA.clone();
28916
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
28917
- //O.sub(oldCA);
28918
- oldCA.sub(O);
28919
- }
28920
- else {
28921
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
28922
- }
28923
- }
29005
+ return coord;
29006
+ }
28924
29007
 
28925
- O.normalize(); // can be omitted for performance
28926
- O.multiplyScalar(strandWidth);
28927
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
28928
- prevCO = O;
29008
+ return undefined;
29009
+ }
29010
+ }
28929
29011
 
28930
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
28931
- let delta = -1 + numM1Inv2 * j;
28932
- let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
28933
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
28934
- pnts[j].push(v);
28935
- }
29012
+ /**
29013
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29014
+ */
28936
29015
 
28937
- pntsCA.push(prevCoorCA);
28938
- prevCOArray.push(prevCO);
29016
+ class Strip {
29017
+ constructor(icn3d) {
29018
+ this.icn3d = icn3d;
29019
+ }
28939
29020
 
28940
- if(atoms.hasOwnProperty(prevAtomid)) {
28941
- bShowArray.push(prevResi);
28942
- calphaIdArray.push(prevCalphaid);
28943
- }
28944
- else {
28945
- bShowArray.push(0);
28946
- calphaIdArray.push(0);
28947
- }
28948
- }
29021
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29022
+ createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
29023
+ calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
29024
+ if(me.bNode) return;
28949
29025
 
28950
- //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);
29026
+ if (p0.length < 2) return;
29027
+ div = div || ic.axisDIV;
28951
29028
 
28952
- let prevCoor = (prevAtomSelected) ? prevAtomSelected.coord : undefined;
28953
-
28954
- 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);
28955
-
28956
- // check whether the atoms are continuous
28957
- // atomsAdjusted has all atoms in the secondary structure
28958
- // atoms has all selected atoms
28959
- // let bBrokenSs = false;
28960
- // if(prevAtomSelected && prevAtomid == prevAtomSelected.serial && !atoms.hasOwnProperty(atom.serial)) {
28961
- // bBrokenSs = true;
28962
- // }
29029
+ // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
29030
+ if(pntsCA && ic.bDoublecolor) {
29031
+ let bExtendLastRes = false; //true;
28963
29032
 
29033
+ let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29034
+ pntsCA = pnts_clrs[0];
28964
29035
 
28965
- // The following code didn't work to select one residue
28966
- // 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);
29036
+ this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
28967
29037
 
28968
- // if(bBrokenSs && atom.ss === 'sheet') {
28969
- // bSheetSegment = true;
28970
- // }
28971
- // else if(bBrokenSs && atom.ss === 'helix') {
28972
- // bHelixSegment = true;
28973
- // }
29038
+ for(let i = 0, il = prevCOArray.length; i < il; ++i) {
29039
+ prevCOArray[i].normalize();
29040
+ }
28974
29041
 
28975
- //if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
28976
- // 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
29042
+ let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29043
+ prevCOArray = pnts_clrs2[0];
28977
29044
 
28978
- if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || bBrokenSs || (resid == lastResid && atom.ss != 'coil')) && pnts[0].length > 0) {
28979
- let atomName = 'CA';
28980
-
28981
- let prevone = [], nexttwo = [];
29045
+ colors = pnts_clrs[2];
29046
+ }
29047
+ else {
28982
29048
 
28983
- if(isNaN(ic.atoms[prevAtomid].resi)) {
28984
- prevone = [];
28985
- }
28986
- else {
28987
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
28988
- let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
28989
- prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
28990
- }
29049
+ if(!bNoSmoothen) {
29050
+ //var bExtendLastRes = true;
29051
+ let bExtendLastRes = false;
29052
+ let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29053
+ let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29054
+ p0 = pnts_clrs0[0];
29055
+ p1 = pnts_clrs1[0];
29056
+ colors = pnts_clrs0[2];
29057
+ }
29058
+ if (p0.length < 2) return;
28991
29059
 
28992
- if(!isNaN(ic.atoms[prevAtomid].resi)) {
28993
- let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
28994
- let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
28995
- if(nextoneCoord !== undefined) {
28996
- nexttwo.push(nextoneCoord);
28997
- }
29060
+ this.setCalphaDrawnCoord(p0, div, calphaIdArray);
29061
+ }
28998
29062
 
28999
- let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
29000
- let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
29001
- if(nexttwoCoord !== undefined) {
29002
- nexttwo.push(nexttwoCoord);
29003
- }
29004
- }
29063
+ if(bHighlight === 1) {
29064
+ //mesh = new THREE.Mesh(geo, ic.matShader);
29005
29065
 
29006
- // include the current residue
29007
- if(!bBrokenSs) {
29008
- // assign the current joint residue to the previous segment
29009
- if(bHighlight === 1 || bHighlight === 2) {
29010
- colors.push(ic.hColor);
29011
- }
29012
- else {
29013
- //colors.push(atom.color);
29014
- colors.push(prevColor);
29015
- }
29066
+ let radius = ic.coilWidth / 2;
29067
+ //var radiusSegments = 8;
29068
+ let radiusSegments = 4; // save memory
29069
+ let closed = false;
29016
29070
 
29017
- if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
29018
- strandWidth = 0; // make the arrow end sharp
29019
- }
29020
- else if(ss === 'coil' && atom.ssbegin) {
29021
- strandWidth = coilWidth;
29022
- }
29023
- else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
29024
- strandWidth = coilWidth;
29025
- }
29026
- else { // use the ss from the previous residue
29027
- strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
29028
- }
29071
+ if(positions !== undefined) {
29072
+ let currPos, prevPos;
29073
+ let currP0 = [], currP1 = [];
29029
29074
 
29030
- let O, oldCA, resSpan = 4;
29031
- if(atom.name === 'O') {
29032
- O = currentO.clone();
29033
- O.sub(currentCA);
29034
- }
29035
- else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
29036
- if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29037
- O = currentCA.clone();
29038
- oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
29039
- //O.sub(oldCA);
29040
- oldCA.sub(O);
29041
- }
29042
- else {
29043
- O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
29044
- }
29045
- }
29075
+ for(let i = 0, il = p0.length; i < il; ++i) {
29076
+ currPos = positions[i];
29046
29077
 
29047
- O.normalize(); // can be omitted for performance
29048
- O.multiplyScalar(strandWidth);
29049
- if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
29050
- prevCO = O;
29078
+ if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
29079
+ // first tube
29080
+ let geometry0 = new THREE.TubeGeometry(
29081
+ new THREE.CatmullRomCurve3(currP0), // path
29082
+ currP0.length, // segments
29083
+ radius,
29084
+ radiusSegments,
29085
+ closed
29086
+ );
29051
29087
 
29052
- for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
29053
- let delta = -1 + numM1Inv2 * j;
29054
- let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
29055
- if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
29056
- pnts[j].push(v);
29057
- }
29088
+ let mesh = new THREE.Mesh(geometry0, ic.matShader);
29089
+ mesh.renderOrder = ic.renderOrderPicking;
29090
+ //ic.mdlPicking.add(mesh);
29091
+ ic.mdl.add(mesh);
29058
29092
 
29059
- atomid = atom.serial;
29093
+ ic.prevHighlightObjects.push(mesh);
29060
29094
 
29061
- pntsCA.push(currentCA);
29062
- prevCOArray.push(prevCO);
29095
+ geometry0 = null;
29063
29096
 
29064
- // 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.
29065
- //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
29066
- if(atoms.hasOwnProperty(atomid)) {
29067
- bShowArray.push(atom.resi);
29068
- calphaIdArray.push(calphaid);
29069
- }
29070
- else {
29071
- bShowArray.push(0);
29072
- calphaIdArray.push(0);
29073
- }
29074
- }
29075
-
29076
- // draw the current segment
29077
- for (let j = 0; !fill && j < num; ++j) {
29078
- if(bSheetSegment) {
29079
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29080
- }
29081
- else if(bHelixSegment) {
29082
- if(bFullAtom) {
29083
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
29084
- }
29085
- else {
29086
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29087
- }
29088
- }
29089
- }
29090
- if (fill) {
29091
- if(bSheetSegment) {
29092
- let start = 0, end = num - 1;
29093
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29094
- }
29095
- else if(bHelixSegment) {
29096
- if(bFullAtom) {
29097
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29098
- }
29099
- else {
29100
- let start = 0, end = num - 1;
29101
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29102
- }
29103
- }
29104
- else {
29105
- if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
29106
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29107
- }
29108
- }
29109
- }
29110
- for (let k = 0; k < num; ++k) pnts[k] = [];
29111
-
29112
- colors = [];
29113
- pntsCA = [];
29114
- prevCOArray = [];
29115
- bShowArray = [];
29116
- calphaIdArray = [];
29117
- bSheetSegment = false;
29118
- bHelixSegment = false;
29119
- } // end if (atom.ssbegin || atom.ssend)
29120
-
29121
- // end of a chain, or end of selection
29122
- if ((currentChain !== atom.chain
29123
- || ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi)
29124
- // || (drawnResidueCount === totalResidueCount - 1)
29125
- // || bBrokenSs
29126
- || (resid == lastResid && atom.ss != 'coil')
29127
- ) && pnts[0].length > 0) {
29128
- //if ((currentChain !== atom.chain) && pnts[0].length > 0) {
29129
-
29130
- let atomName = 'CA';
29131
-
29132
- let prevone = [], nexttwo = [];
29133
- if(isNaN(ic.atoms[prevAtomid].resi)) {
29134
- prevone = [];
29135
- }
29136
- else {
29137
- let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
29138
- ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
29139
- }
29140
-
29141
- for (let j = 0; !fill && j < num; ++j) {
29142
- if(bSheetSegment) {
29143
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29144
- }
29145
- else if(bHelixSegment) {
29146
- if(bFullAtom) {
29147
- ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
29148
- }
29149
- else {
29150
- ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29151
- }
29152
- }
29153
- }
29154
- if (fill) {
29155
- if(bSheetSegment) {
29156
- let start = 0, end = num - 1;
29157
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
29158
- }
29159
- else if(bHelixSegment) {
29160
- if(bFullAtom) {
29161
- ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
29162
- }
29163
- else {
29164
- let start = 0, end = num - 1;
29165
- ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
29166
- }
29167
- }
29168
- }
29169
-
29170
- for (let k = 0; k < num; ++k) pnts[k] = [];
29171
- colors = [];
29172
- pntsCA = [];
29173
- prevCOArray = [];
29174
- bShowArray = [];
29175
- calphaIdArray = [];
29176
- bSheetSegment = false;
29177
- bHelixSegment = false;
29178
- }
29179
-
29180
- currentChain = atom.chain;
29181
- currentResi = atom.resi;
29182
- ss = atom.ss;
29183
- ssend = atom.ssend;
29184
- prevAtomid = atom.serial;
29185
- if(atoms.hasOwnProperty(atom.serial)) prevAtomSelected = atom;
29186
- prevResi = atom.resi;
29187
-
29188
- prevCalphaid = calphaid;
29189
-
29190
- // only update when atom.name === 'O'
29191
- prevCoorCA = currentCA;
29192
- prevCoorO = atom.coord;
29193
- prevColor = currentColor;
29194
- } // end if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA') ) {
29195
- } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
29196
- } // end for
29197
-
29198
- caArray = [];
29199
-
29200
- // ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
29201
- // draw all atoms in tubes and assign zero radius when the residue is not coil
29202
- ic.tubeCls.createTube(atomsAdjust, 'CA', coilWidth, bHighlight);
29203
- pnts = {};
29204
- }
29205
-
29206
- getOneExtraResidue(residueHash) { let ic = this.icn3d, me = ic.icn3dui;
29207
- let atomsAdjust = {};
29208
-
29209
- for(let resid in residueHash) {
29210
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[resid]);
29211
-
29212
- let residNcbi = ic.resid2ncbi[resid];
29213
- let resiNcbi = residNcbi.substr(residNcbi.lastIndexOf('_') + 1);
29214
-
29215
- let nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) + 1);
29216
- let nextResid = ic.ncbi2resid[nextResidNcbi];
29217
-
29218
- if(!nextResid) {
29219
- nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) - 1);
29220
- nextResid = ic.ncbi2resid[nextResidNcbi];
29221
- }
29222
-
29223
- if(nextResid) atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[nextResid]);
29224
- }
29225
-
29226
- return atomsAdjust;
29227
- }
29228
-
29229
- /*
29230
- getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
29231
- let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
29232
- let firstAtom, lastAtom;
29233
- let index = 0, length = Object.keys(atoms).length;
29234
-
29235
- let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
29236
- for(let serial in atoms) {
29237
- currChain = atoms[serial].structure + '_' + atoms[serial].chain;
29238
- currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
29239
- currAtom = atoms[serial];
29240
-
29241
- if(prevChain === undefined) firstAtom = atoms[serial];
29242
-
29243
- if( (currChain !== prevChain && prevChain !== undefined)
29244
- || (currResi !== prevResi && ic.resid2ncbi[currResi] !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) || index === length - 1) {
29245
- if( (currChain !== prevChain && prevChain !== undefined)
29246
- || (currResi !== prevResi && currResi !== ic.resid2ncbi[prevResi] + 1 && prevResi !== undefined) ) {
29247
- lastAtom = prevAtom;
29248
- }
29249
- else if(index === length - 1) {
29250
- lastAtom = currAtom;
29251
- }
29252
-
29253
- // fill the beginning
29254
- let beginResi = firstAtom.resi;
29255
- if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
29256
- for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
29257
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
29258
- if(!ic.residues.hasOwnProperty(residueid)) break;
29259
-
29260
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
29261
-
29262
- if(atom.ss === firstAtom.ss && atom.ssbegin) {
29263
- beginResi = atom.resi;
29264
- break;
29265
- }
29266
- }
29267
-
29268
- for(let i = beginResi; i < firstAtom.resi; ++i) {
29269
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
29270
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29271
- ic.atoms));
29272
- }
29273
- }
29274
-
29275
- // add one extra residue for coils between strands/helix
29276
- if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
29277
- let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
29278
- if(ic.residues.hasOwnProperty(residueid)) {
29279
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29280
- ic.atoms));
29281
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
29282
- }
29283
- }
29284
-
29285
- // fill the end
29286
- let endResi = lastAtom.resi;
29287
- // 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.
29288
-
29289
- if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
29290
-
29291
- let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
29292
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
29293
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
29294
- if(!ic.residues.hasOwnProperty(residueid)) break;
29295
-
29296
- let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
29297
-
29298
- if(atom.ss === lastAtom.ss && atom.ssend) {
29299
- endResi = atom.resi;
29300
- break;
29301
- }
29302
- }
29303
-
29304
- for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
29305
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
29306
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29307
- ic.atoms));
29308
- }
29309
- }
29310
-
29311
- // add one extra residue for coils between strands/helix
29312
- if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
29313
- let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
29314
- if(ic.residues.hasOwnProperty(residueid)) {
29315
- atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
29316
- ic.atoms));
29317
- atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
29318
- }
29319
- }
29320
-
29321
- // reset notshow
29322
- if(lastAtom.notshow) lastAtom.notshow = undefined;
29323
-
29324
- firstAtom = currAtom;
29325
- }
29326
-
29327
- prevChain = currChain;
29328
- prevResi = currResi;
29329
- prevAtom = currAtom;
29330
-
29331
- ++index;
29332
- }
29333
-
29334
- return atomsAdjust;
29335
- }
29336
- */
29337
- }
29338
-
29339
- /**
29340
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29341
- */
29342
-
29343
- class Strip {
29344
- constructor(icn3d) {
29345
- this.icn3d = icn3d;
29346
- }
29347
-
29348
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29349
- createStrip(p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray,
29350
- calphaIdArray, positions, prevone, nexttwo, pntsCA, prevCOArray) { let ic = this.icn3d, me = ic.icn3dui;
29351
- if(me.bNode) return;
29352
-
29353
- if (p0.length < 2) return;
29354
- div = div || ic.axisDIV;
29355
-
29356
- // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
29357
- if(pntsCA && ic.bDoublecolor) {
29358
- let bExtendLastRes = false; //true;
29359
-
29360
- let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29361
- pntsCA = pnts_clrs[0];
29362
-
29363
- this.setCalphaDrawnCoord(pntsCA, div, calphaIdArray);
29364
-
29365
- for(let i = 0, il = prevCOArray.length; i < il; ++i) {
29366
- prevCOArray[i].normalize();
29367
- }
29368
-
29369
- let pnts_clrs2 = me.subdivideCls.subdivide(prevCOArray, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29370
- prevCOArray = pnts_clrs2[0];
29371
-
29372
- colors = pnts_clrs[2];
29373
- }
29374
- else {
29375
-
29376
- if(!bNoSmoothen) {
29377
- //var bExtendLastRes = true;
29378
- let bExtendLastRes = false;
29379
- let pnts_clrs0 = me.subdivideCls.subdivide(p0, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29380
- let pnts_clrs1 = me.subdivideCls.subdivide(p1, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
29381
- p0 = pnts_clrs0[0];
29382
- p1 = pnts_clrs1[0];
29383
- colors = pnts_clrs0[2];
29384
- }
29385
- if (p0.length < 2) return;
29386
-
29387
- this.setCalphaDrawnCoord(p0, div, calphaIdArray);
29388
- }
29389
-
29390
- if(bHighlight === 1) {
29391
- //mesh = new THREE.Mesh(geo, ic.matShader);
29392
-
29393
- let radius = ic.coilWidth / 2;
29394
- //var radiusSegments = 8;
29395
- let radiusSegments = 4; // save memory
29396
- let closed = false;
29397
-
29398
- if(positions !== undefined) {
29399
- let currPos, prevPos;
29400
- let currP0 = [], currP1 = [];
29401
-
29402
- for(let i = 0, il = p0.length; i < il; ++i) {
29403
- currPos = positions[i];
29404
-
29405
- if((currPos !== prevPos && parseInt(currPos) !== parseInt(prevPos) + 1 && prevPos !== undefined) || (i === il -1) ) {
29406
- // first tube
29407
- let geometry0 = new THREE.TubeGeometry(
29408
- new THREE.CatmullRomCurve3(currP0), // path
29409
- currP0.length, // segments
29410
- radius,
29411
- radiusSegments,
29412
- closed
29413
- );
29414
-
29415
- let mesh = new THREE.Mesh(geometry0, ic.matShader);
29416
- mesh.renderOrder = ic.renderOrderPicking;
29417
- //ic.mdlPicking.add(mesh);
29418
- ic.mdl.add(mesh);
29419
-
29420
- ic.prevHighlightObjects.push(mesh);
29421
-
29422
- geometry0 = null;
29423
-
29424
- // second tube
29425
- let geometry1 = new THREE.TubeGeometry(
29426
- new THREE.CatmullRomCurve3(currP1), // path
29427
- currP1.length, // segments
29428
- radius,
29429
- radiusSegments,
29430
- closed
29431
- );
29097
+ // second tube
29098
+ let geometry1 = new THREE.TubeGeometry(
29099
+ new THREE.CatmullRomCurve3(currP1), // path
29100
+ currP1.length, // segments
29101
+ radius,
29102
+ radiusSegments,
29103
+ closed
29104
+ );
29432
29105
 
29433
29106
  mesh = new THREE.Mesh(geometry1, ic.matShader);
29434
29107
  mesh.renderOrder = ic.renderOrderPicking;
@@ -29704,6 +29377,7 @@ class Tube {
29704
29377
  atom.structure + '_' + atom.chain + '_' + (parseInt(atom.resi) - 1).toString();
29705
29378
 
29706
29379
  if (index > 0 && (currentChain !== atom.chain || Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist
29380
+ || (prevAtom.ssbegin) // e.g., https://www.ncbi.nlm.nih.gov/Structure/icn3d/?pdbid=7JO8 where a beta sheet has just two residues
29707
29381
  // || (parseInt(currentResi) + 1 < parseInt(atom.resi) && (Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist2 || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist2 || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist2) && ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]) && ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]).ss == 'helix')
29708
29382
  || (ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 < ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + atom.chain, atom.resi) && (Math.abs(atom.coord.x - prevAtom.coord.x) > maxDist2 || Math.abs(atom.coord.y - prevAtom.coord.y) > maxDist2 || Math.abs(atom.coord.z - prevAtom.coord.z) > maxDist2))
29709
29383
  ) ) {
@@ -29796,7 +29470,7 @@ class Tube {
29796
29470
  }
29797
29471
 
29798
29472
  // draw all atoms in tubes and assign zero radius when the residue is not coil
29799
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29473
+ // if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29800
29474
 
29801
29475
  //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29802
29476
  radii.push(radiusFinal);
@@ -29931,232 +29605,778 @@ class Tube {
29931
29605
 
29932
29606
  pnts.push(atom.coord);
29933
29607
 
29934
- let radiusFinal;
29935
- if(bCustom) {
29936
- radiusFinal = this.getCustomtubesize(atom.structure + '_' + atom.chain + '_' + atom.resi);
29937
- }
29938
- else {
29939
- radiusFinal = this.getRadius(radius, atom);
29940
- }
29608
+ let radiusFinal;
29609
+ if(bCustom) {
29610
+ radiusFinal = this.getCustomtubesize(atom.structure + '_' + atom.chain + '_' + atom.resi);
29611
+ }
29612
+ else {
29613
+ radiusFinal = this.getRadius(radius, atom);
29614
+ }
29615
+
29616
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29617
+ if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29618
+
29619
+ //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29620
+ radii.push(radiusFinal);
29621
+
29622
+ colors.push(atom.color);
29623
+ // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
29624
+ if(index === 1) colors[colors.length - 2] = atom.color;
29625
+
29626
+ currentChain = atom.chain;
29627
+ currentResi = atom.resi;
29628
+
29629
+ let scale = 1.2;
29630
+ if(bHighlight === 2 && !atom.ssbegin) {
29631
+ ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
29632
+ }
29633
+
29634
+ ++index;
29635
+
29636
+ prevAtom = atom;
29637
+ }
29638
+ }
29639
+
29640
+ if(bHighlight !== 2) {
29641
+ pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
29642
+ }
29643
+
29644
+ for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
29645
+ let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
29646
+ let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
29647
+ let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
29648
+ let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
29649
+ let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
29650
+
29651
+ this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
29652
+ }
29653
+
29654
+ pnts_colors_radii_prevone_nexttwo = [];
29655
+ }
29656
+ */
29657
+
29658
+ getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
29659
+ let pos = resid.lastIndexOf('_');
29660
+ let resi = resid.substr(pos + 1);
29661
+ let chainid = resid.substr(0, pos);
29662
+
29663
+ let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
29664
+
29665
+ return radiusFinal;
29666
+ };
29667
+
29668
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29669
+ createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29670
+ if(me.bNode) return;
29671
+
29672
+ if (_pnts.length < 2) return;
29673
+
29674
+ let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
29675
+ let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
29676
+ //var geo = new THREE.Geometry();
29677
+ let geo = new THREE.BufferGeometry();
29678
+ let verticeArray = [], colorArray = [],indexArray = [], color;
29679
+ let offset = 0, offset2 = 0, offset3 = 0;
29680
+
29681
+ let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
29682
+
29683
+ let pnts = pnts_clrs[0];
29684
+ colors = pnts_clrs[2];
29685
+
29686
+ let constRadiius;
29687
+ // a threshold to stop drawing the tube if it's less than this ratio of radius
29688
+ let thresholdRatio = 1; //0.9;
29689
+
29690
+ let prevAxis1 = new THREE.Vector3(), prevAxis2;
29691
+ for (let i = 0, lim = pnts.length; i < lim; ++i) {
29692
+ let r, idx = (i - 1) * axisDivInv;
29693
+
29694
+ if (i === 0) {
29695
+ r = radii[0];
29696
+ if(r > 0) constRadiius = r;
29697
+ }
29698
+ else {
29699
+ if (idx % 1 === 0) {
29700
+ r = radii[idx];
29701
+ if(r > 0) constRadiius = r;
29702
+ }
29703
+ else {
29704
+ let floored = Math.floor(idx);
29705
+ let tmp = idx - floored;
29706
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29707
+ // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
29708
+ r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
29709
+
29710
+ // a threshold to stop drawing the tube if it's less than this ratio of radius.
29711
+ // The extra bit of tube connects coil with strands or helices
29712
+ if(!bNonCoil) {
29713
+ if(r < thresholdRatio * constRadiius) {
29714
+ r = 0;
29715
+ }
29716
+ // else if(r < constRadiius) {
29717
+ // r *= 0.5; // use small radius for the connection between coild and sheets/helices
29718
+ // }
29719
+ }
29720
+ }
29721
+ }
29722
+ let delta, axis1, axis2;
29723
+ if (i < lim - 1) {
29724
+ delta = pnts[i].clone().sub(pnts[i + 1]);
29725
+ axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
29726
+ axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
29727
+ // let dir = 1, offset = 0;
29728
+ if (prevAxis1.dot(axis1) < 0) {
29729
+ axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
29730
+ }
29731
+ prevAxis1 = axis1; prevAxis2 = axis2;
29732
+ } else {
29733
+ axis1 = prevAxis1; axis2 = prevAxis2;
29734
+ }
29735
+ for (let j = 0; j < circleDiv; ++j) {
29736
+ let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
29737
+ let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
29738
+ verticeArray[offset++] = point.x;
29739
+ verticeArray[offset++] = point.y;
29740
+ verticeArray[offset++] = point.z;
29741
+
29742
+ color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
29743
+ colorArray[offset2++] = color.r;
29744
+ colorArray[offset2++] = color.g;
29745
+ colorArray[offset2++] = color.b;
29746
+ }
29747
+ }
29748
+ let offsetTmp = 0, nComp = 3;
29749
+ for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
29750
+ let reg = 0;
29751
+ //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
29752
+ //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
29753
+ let pos = offsetTmp * nComp;
29754
+ let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29755
+ pos = (offsetTmp + circleDiv) * nComp;
29756
+ let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29757
+ pos = (offsetTmp + circleDiv + 1) * nComp;
29758
+ let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29759
+
29760
+ let r1 = point1.clone().sub(point2).lengthSq();
29761
+ let r2 = point1.clone().sub(point3).lengthSq();
29762
+ if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
29763
+ //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
29764
+ //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
29765
+ //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
29766
+ indexArray[offset3++] = offsetTmp + j;
29767
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29768
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29769
+
29770
+ //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
29771
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29772
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29773
+ indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
29774
+ }
29775
+ offsetTmp += circleDiv;
29776
+ }
29777
+
29778
+ geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
29779
+ geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
29780
+
29781
+ geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
29782
+ //geo.setIndex(indexArray);
29783
+
29784
+ //geo.computeFaceNormals();
29785
+ //geo.computeVertexNormals(false);
29786
+ geo.computeVertexNormals();
29787
+
29788
+ let mesh;
29789
+ if(bHighlight === 2) {
29790
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
29791
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
29792
+
29793
+ if(ic.mdl) ic.mdl.add(mesh);
29794
+ }
29795
+ else if(bHighlight === 1) {
29796
+ mesh = new THREE.Mesh(geo, ic.matShader);
29797
+ mesh.renderOrder = ic.renderOrderPicking;
29798
+ //ic.mdlPicking.add(mesh);
29799
+ if(ic.mdl) ic.mdl.add(mesh);
29800
+ }
29801
+ else {
29802
+ //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
29803
+ mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
29804
+
29805
+ if(ic.mdl) ic.mdl.add(mesh);
29806
+ }
29807
+
29808
+ if(bHighlight === 1 || bHighlight === 2) {
29809
+ ic.prevHighlightObjects.push(mesh);
29810
+ }
29811
+ else {
29812
+ ic.objects.push(mesh);
29813
+ }
29814
+ }
29815
+
29816
+ getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
29817
+ let radiusFinal = radius;
29818
+ if(radius) {
29819
+ radiusFinal = radius;
29820
+ }
29821
+ else {
29822
+ if(atom.b > 0 && atom.b <= 100) {
29823
+ radiusFinal = atom.b * 0.01;
29824
+ }
29825
+ else if(atom.b > 100) {
29826
+ radiusFinal = 100 * 0.01;
29827
+ }
29828
+ else {
29829
+ radiusFinal = ic.coilWidth;
29830
+ }
29831
+ }
29832
+
29833
+ return radiusFinal;
29834
+ }
29835
+ }
29836
+
29837
+ /**
29838
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
29839
+ */
29840
+
29841
+ class Strand {
29842
+ constructor(icn3d) {
29843
+ this.icn3d = icn3d;
29844
+ }
29845
+
29846
+ // significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29847
+ //Create the style of ribbon or strand for "atoms". "num" means how many lines define the curve.
29848
+ //"num" is 2 for ribbon and 6 for strand. "div" means how many pnts are used to smooth the curve.
29849
+ //It's typically 5. "coilWidth" is the width of curve for coil. "helixSheetWidth" is the width of curve for helix or sheet.
29850
+ //"doNotSmoothen" is a flag to smooth the curve or not. "thickness" is the thickness of the curve.
29851
+ //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be outlines
29852
+ //with bHighlight=1 and 3D objects with bHighlight=2.
29853
+ createStrand(atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
29854
+ if(me.bNode) return;
29855
+
29856
+ let bRibbon = fill ? true: false;
29857
+
29858
+ // when highlight, the input atoms may only include part of sheet or helix
29859
+ // include the whole sheet or helix when highlighting
29860
+ let atomsAdjust = {};
29861
+
29862
+ //if( (bHighlight === 1 || bHighlight === 2) && !ic.bAllAtoms) {
29863
+ //if( !ic.bAllAtoms) {
29864
+ if( Object.keys(atoms).length < Object.keys(ic.atoms).length) {
29865
+ atomsAdjust = this.getSSExpandedAtoms(atoms);
29866
+ }
29867
+ else {
29868
+ atomsAdjust = atoms;
29869
+ }
29870
+
29871
+ if(bHighlight === 2) {
29872
+ if(fill) {
29873
+ fill = false;
29874
+ num = null;
29875
+ div = null;
29876
+ coilWidth = null;
29877
+ helixSheetWidth = null;
29878
+ thickness = undefined;
29879
+ }
29880
+ else {
29881
+ fill = true;
29882
+ num = 2;
29883
+ div = undefined;
29884
+ coilWidth = undefined;
29885
+ helixSheetWidth = undefined;
29886
+ thickness = ic.ribbonthickness;
29887
+ }
29888
+ }
29889
+
29890
+ num = num || ic.strandDIV;
29891
+ div = div || ic.axisDIV;
29892
+ coilWidth = coilWidth || ic.coilWidth;
29893
+ doNotSmoothen = doNotSmoothen || false;
29894
+ helixSheetWidth = helixSheetWidth || ic.helixSheetWidth;
29895
+ let pnts = {}; for (let k = 0; k < num; ++k) pnts[k] = [];
29896
+ let pntsCA = [];
29897
+ let prevCOArray = [];
29898
+ let bShowArray = [];
29899
+ let calphaIdArray = []; // used to store one of the final positions drawn in 3D
29900
+ let colors = [];
29901
+ let currentChain, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
29902
+ let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevResi = null, calphaid = null, prevCalphaid = null;
29903
+ let strandWidth, bSheetSegment = false, bHelixSegment = false;
29904
+ let atom, tubeAtoms = {};
29905
+
29906
+ // test the first 30 atoms to see whether only C-alpha is available
29907
+ ic.bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atomsAdjust); //, 'CA');
29908
+
29909
+ // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
29910
+ let residueHash = {};
29911
+ for(let i in atomsAdjust) {
29912
+ let atom = atomsAdjust[i];
29913
+
29914
+ let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
29915
+ residueHash[residueid] = 1;
29916
+ }
29917
+ let totalResidueCount = Object.keys(residueHash).length;
29918
+
29919
+ let drawnResidueCount = 0;
29920
+
29921
+ let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
29922
+
29923
+ let caArray = []; // record all C-alpha atoms to predict the helix
29924
+
29925
+ for (let i in atomsAdjust) {
29926
+ atom = atomsAdjust[i];
29927
+ if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
29928
+ // "CA" has to appear before "O"
29929
+
29930
+ if (atom.name === 'CA') {
29931
+ if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) {
29932
+ tubeAtoms[i] = atom;
29933
+ }
29934
+
29935
+ currentCA = atom.coord;
29936
+ currentColor = atom.color;
29937
+ calphaid = atom.serial;
29938
+
29939
+ caArray.push(atom.serial);
29940
+ }
29941
+
29942
+ if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA')) {
29943
+ if(currentCA === null || currentCA === undefined) {
29944
+ currentCA = atom.coord;
29945
+ currentColor = atom.color;
29946
+ calphaid = atom.serial;
29947
+ }
29948
+
29949
+ if(atom.name === 'O') {
29950
+ currentO = atom.coord;
29951
+ }
29952
+ // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment
29953
+ let bSameChain = true;
29954
+ // if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) {
29955
+ if (currentChain !== atom.chain) {
29956
+ bSameChain = false;
29957
+ }
29958
+
29959
+ if(atom.ssend && atom.ss === 'sheet') {
29960
+ bSheetSegment = true;
29961
+ }
29962
+ else if(atom.ssend && atom.ss === 'helix') {
29963
+ bHelixSegment = true;
29964
+ }
29965
+
29966
+ // assign the previous residue
29967
+ if(prevCoorO) {
29968
+ if(bHighlight === 1 || bHighlight === 2) {
29969
+ colors.push(ic.hColor);
29970
+ }
29971
+ else {
29972
+ colors.push(prevColor);
29973
+ }
29974
+
29975
+ if(ss !== 'coil' && atom.ss === 'coil') {
29976
+ strandWidth = coilWidth;
29977
+ }
29978
+ else if(ssend && atom.ssbegin) { // a transition between two ss
29979
+ strandWidth = coilWidth;
29980
+ }
29981
+ else {
29982
+ strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth;
29983
+ }
29984
+
29985
+ let O, oldCA, resSpan = 4;
29986
+ if(atom.name === 'O') {
29987
+ O = prevCoorO.clone();
29988
+ if(prevCoorCA !== null && prevCoorCA !== undefined) {
29989
+ O.sub(prevCoorCA);
29990
+ }
29991
+ else {
29992
+ prevCoorCA = prevCoorO.clone();
29993
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
29994
+ O = prevCoorCA.clone();
29995
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
29996
+ //O.sub(oldCA);
29997
+ oldCA.sub(O);
29998
+ }
29999
+ else {
30000
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30001
+ }
30002
+ }
30003
+ }
30004
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
30005
+ if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30006
+ O = prevCoorCA.clone();
30007
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
30008
+ //O.sub(oldCA);
30009
+ oldCA.sub(O);
30010
+ }
30011
+ else {
30012
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30013
+ }
30014
+ }
30015
+
30016
+ O.normalize(); // can be omitted for performance
30017
+ O.multiplyScalar(strandWidth);
30018
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
30019
+ prevCO = O;
30020
+
30021
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
30022
+ let delta = -1 + numM1Inv2 * j;
30023
+ let v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta);
30024
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
30025
+ pnts[j].push(v);
30026
+ }
30027
+
30028
+ pntsCA.push(prevCoorCA);
30029
+ prevCOArray.push(prevCO);
30030
+
30031
+ if(atoms.hasOwnProperty(prevAtomid)) {
30032
+ bShowArray.push(prevResi);
30033
+ calphaIdArray.push(prevCalphaid);
30034
+ }
30035
+ else {
30036
+ bShowArray.push(0);
30037
+ calphaIdArray.push(0);
30038
+ }
30039
+
30040
+ ++drawnResidueCount;
30041
+ }
30042
+
30043
+ let maxDist = 6.0;
30044
+ let bBrokenSs = (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
30045
+ // The following code didn't work to select one residue
30046
+ // let bBrokenSs = !atoms.hasOwnProperty(atom.serial) || (prevCoorCA && Math.abs(currentCA.x - prevCoorCA.x) > maxDist) || (prevCoorCA && Math.abs(currentCA.y - prevCoorCA.y) > maxDist) || (prevCoorCA && Math.abs(currentCA.z - prevCoorCA.z) > maxDist);
30047
+
30048
+ // if(bBrokenSs && atom.ss === 'sheet') {
30049
+ // bSheetSegment = true;
30050
+ // }
30051
+ // else if(bBrokenSs && atom.ss === 'helix') {
30052
+ // bHelixSegment = true;
30053
+ // }
30054
+
30055
+ if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
30056
+ let atomName = 'CA';
30057
+
30058
+ let prevone = [], nexttwo = [];
30059
+
30060
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
30061
+ prevone = [];
30062
+ }
30063
+ else {
30064
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30065
+ let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30066
+ prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
30067
+ }
30068
+
30069
+ if(!isNaN(ic.atoms[prevAtomid].resi)) {
30070
+ let nextoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 1).toString();
30071
+ let nextoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nextoneResid, atomName);
30072
+ if(nextoneCoord !== undefined) {
30073
+ nexttwo.push(nextoneCoord);
30074
+ }
30075
+
30076
+ let nexttwoResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) + 2).toString();
30077
+ let nexttwoCoord = ic.firstAtomObjCls.getAtomCoordFromResi(nexttwoResid, atomName);
30078
+ if(nexttwoCoord !== undefined) {
30079
+ nexttwo.push(nexttwoCoord);
30080
+ }
30081
+ }
30082
+
30083
+ if(!bBrokenSs) { // include the current residue
30084
+ // assign the current joint residue to the previous segment
30085
+ if(bHighlight === 1 || bHighlight === 2) {
30086
+ colors.push(ic.hColor);
30087
+ }
30088
+ else {
30089
+ //colors.push(atom.color);
30090
+ colors.push(prevColor);
30091
+ }
30092
+
30093
+ if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow
30094
+ strandWidth = 0; // make the arrow end sharp
30095
+ }
30096
+ else if(ss === 'coil' && atom.ssbegin) {
30097
+ strandWidth = coilWidth;
30098
+ }
30099
+ else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil
30100
+ strandWidth = coilWidth;
30101
+ }
30102
+ else { // use the ss from the previous residue
30103
+ strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth;
30104
+ }
30105
+
30106
+ let O, oldCA, resSpan = 4;
30107
+ if(atom.name === 'O') {
30108
+ O = currentO.clone();
30109
+ O.sub(currentCA);
30110
+ }
30111
+ else if(ic.bCalphaOnly && atom.name === 'CA') {
30112
+ if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
30113
+ O = currentCA.clone();
30114
+ oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
30115
+ //O.sub(oldCA);
30116
+ oldCA.sub(O);
30117
+ }
30118
+ else {
30119
+ O = new THREE.Vector3(Math.random(),Math.random(),Math.random());
30120
+ }
30121
+ }
30122
+
30123
+ O.normalize(); // can be omitted for performance
30124
+ O.multiplyScalar(strandWidth);
30125
+ if (prevCO !== null && O.dot(prevCO) < 0) O.negate();
30126
+ prevCO = O;
30127
+
30128
+ for (let j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) {
30129
+ let delta = -1 + numM1Inv2 * j;
30130
+ let v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
30131
+ if (!doNotSmoothen && ss === 'sheet') v.smoothen = true;
30132
+ pnts[j].push(v);
30133
+ }
30134
+
30135
+ atomid = atom.serial;
30136
+
30137
+ pntsCA.push(currentCA);
30138
+ prevCOArray.push(prevCO);
30139
+
30140
+ // when a coil connects to a sheet and the last residue of coild is highlighted, the first sheet residue is set as atom.highlightStyle. This residue should not be shown.
30141
+ //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) {
30142
+ if(atoms.hasOwnProperty(atomid)) {
30143
+ bShowArray.push(atom.resi);
30144
+ calphaIdArray.push(calphaid);
30145
+ }
30146
+ else {
30147
+ bShowArray.push(0);
30148
+ calphaIdArray.push(0);
30149
+ }
30150
+ }
30151
+
30152
+ // draw the current segment
30153
+ for (let j = 0; !fill && j < num; ++j) {
30154
+ if(bSheetSegment) {
30155
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30156
+ }
30157
+ else if(bHelixSegment) {
30158
+ if(bFullAtom) {
30159
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
30160
+ }
30161
+ else {
30162
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30163
+ }
30164
+ }
30165
+ }
30166
+ if (fill) {
30167
+ if(bSheetSegment) {
30168
+ let start = 0, end = num - 1;
30169
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30170
+ }
30171
+ else if(bHelixSegment) {
30172
+ if(bFullAtom) {
30173
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30174
+ }
30175
+ else {
30176
+ let start = 0, end = num - 1;
30177
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30178
+ }
30179
+ }
30180
+ else {
30181
+ if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately
30182
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30183
+ }
30184
+ }
30185
+ }
30186
+ for (let k = 0; k < num; ++k) pnts[k] = [];
30187
+
30188
+ colors = [];
30189
+ pntsCA = [];
30190
+ prevCOArray = [];
30191
+ bShowArray = [];
30192
+ calphaIdArray = [];
30193
+ bSheetSegment = false;
30194
+ bHelixSegment = false;
30195
+ } // end if (atom.ssbegin || atom.ssend)
29941
30196
 
29942
- // draw all atoms in tubes and assign zero radius when the residue is not coil
29943
- if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
30197
+ // end of a chain
30198
+ // if ((currentChain !== atom.chain || currentResi + 1 !== atom.resi) && pnts[0].length > 0) {
30199
+ if ((currentChain !== atom.chain) && pnts[0].length > 0) {
29944
30200
 
29945
- //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29946
- radii.push(radiusFinal);
30201
+ let atomName = 'CA';
29947
30202
 
29948
- colors.push(atom.color);
29949
- // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
29950
- if(index === 1) colors[colors.length - 2] = atom.color;
30203
+ let prevone = [], nexttwo = [];
30204
+ if(isNaN(ic.atoms[prevAtomid].resi)) {
30205
+ prevone = [];
30206
+ }
30207
+ else {
30208
+ let prevoneResid = ic.atoms[prevAtomid].structure + '_' + ic.atoms[prevAtomid].chain + '_' + (parseInt(ic.atoms[prevAtomid].resi) - 1).toString();
30209
+ ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
30210
+ }
29951
30211
 
29952
- currentChain = atom.chain;
29953
- currentResi = atom.resi;
30212
+ for (let j = 0; !fill && j < num; ++j) {
30213
+ if(bSheetSegment) {
30214
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30215
+ }
30216
+ else if(bHelixSegment) {
30217
+ if(bFullAtom) {
30218
+ ic.curveCls.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo);
30219
+ }
30220
+ else {
30221
+ ic.curveStripArrowCls.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30222
+ }
30223
+ }
30224
+ }
30225
+ if (fill) {
30226
+ if(bSheetSegment) {
30227
+ let start = 0, end = num - 1;
30228
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, true, prevone, nexttwo);
30229
+ }
30230
+ else if(bHelixSegment) {
30231
+ if(bFullAtom) {
30232
+ ic.stripCls.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray, undefined, prevone, nexttwo, pntsCA, prevCOArray);
30233
+ }
30234
+ else {
30235
+ let start = 0, end = num - 1;
30236
+ ic.curveStripArrowCls.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false, prevone, nexttwo);
30237
+ }
30238
+ }
30239
+ }
29954
30240
 
29955
- let scale = 1.2;
29956
- if(bHighlight === 2 && !atom.ssbegin) {
29957
- ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
30241
+ for (let k = 0; k < num; ++k) pnts[k] = [];
30242
+ colors = [];
30243
+ pntsCA = [];
30244
+ prevCOArray = [];
30245
+ bShowArray = [];
30246
+ calphaIdArray = [];
30247
+ bSheetSegment = false;
30248
+ bHelixSegment = false;
29958
30249
  }
29959
30250
 
29960
- ++index;
30251
+ currentChain = atom.chain;
30252
+ ss = atom.ss;
30253
+ ssend = atom.ssend;
30254
+ prevAtomid = atom.serial;
30255
+ prevResi = atom.resi;
29961
30256
 
29962
- prevAtom = atom;
29963
- }
29964
- }
30257
+ prevCalphaid = calphaid;
29965
30258
 
29966
- if(bHighlight !== 2) {
29967
- pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
29968
- }
30259
+ // only update when atom.name === 'O'
30260
+ prevCoorCA = currentCA;
30261
+ prevCoorO = atom.coord;
30262
+ prevColor = currentColor;
30263
+ } // end if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA') ) {
30264
+ } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
30265
+ } // end for
29969
30266
 
29970
- for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
29971
- let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
29972
- let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
29973
- let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
29974
- let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
29975
- let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
30267
+ caArray = [];
29976
30268
 
29977
- this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
29978
- }
30269
+ ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
29979
30270
 
29980
- pnts_colors_radii_prevone_nexttwo = [];
30271
+ tubeAtoms = {};
30272
+ pnts = {};
29981
30273
  }
29982
- */
29983
-
29984
- getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
29985
- let pos = resid.lastIndexOf('_');
29986
- let resi = resid.substr(pos + 1);
29987
- let chainid = resid.substr(0, pos);
29988
30274
 
29989
- let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
29990
-
29991
- return radiusFinal;
29992
- };
29993
-
29994
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29995
- createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29996
- if(me.bNode) return;
29997
-
29998
- if (_pnts.length < 2) return;
29999
-
30000
- let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
30001
- let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
30002
- //var geo = new THREE.Geometry();
30003
- let geo = new THREE.BufferGeometry();
30004
- let verticeArray = [], colorArray = [],indexArray = [], color;
30005
- let offset = 0, offset2 = 0, offset3 = 0;
30275
+ getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
30276
+ let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
30277
+ let firstAtom, lastAtom;
30278
+ let index = 0, length = Object.keys(atoms).length;
30006
30279
 
30007
- let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
30280
+ let atomsAdjust = me.hashUtilsCls.cloneHash(atoms);
30281
+ for(let serial in atoms) {
30282
+ currChain = atoms[serial].structure + '_' + atoms[serial].chain;
30283
+ currResi = atoms[serial].resi; //parseInt(atoms[serial].resi);
30284
+ currAtom = atoms[serial];
30008
30285
 
30009
- let pnts = pnts_clrs[0];
30010
- colors = pnts_clrs[2];
30286
+ if(prevChain === undefined) firstAtom = atoms[serial];
30011
30287
 
30012
- let constRadiius;
30013
- // a threshold to stop drawing the tube if it's less than this ratio of radius
30014
- let thresholdRatio = 1; //0.9;
30288
+ if( (currChain !== prevChain && prevChain !== undefined)
30289
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) || index === length - 1) {
30290
+ if( (currChain !== prevChain && prevChain !== undefined)
30291
+ || (currResi !== prevResi && currResi !== parseInt(prevResi) + 1 && prevResi !== undefined) ) {
30292
+ lastAtom = prevAtom;
30293
+ }
30294
+ else if(index === length - 1) {
30295
+ lastAtom = currAtom;
30296
+ }
30015
30297
 
30016
- let prevAxis1 = new THREE.Vector3(), prevAxis2;
30017
- for (let i = 0, lim = pnts.length; i < lim; ++i) {
30018
- let r, idx = (i - 1) * axisDivInv;
30298
+ // fill the beginning
30299
+ let beginResi = firstAtom.resi;
30300
+ if(!isNaN(firstAtom.resi) && firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) {
30301
+ for(let i = parseInt(firstAtom.resi) - 1; i > 0; --i) {
30302
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30303
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30019
30304
 
30020
- if (i === 0) {
30021
- r = radii[0];
30022
- if(r > 0) constRadiius = r;
30023
- }
30024
- else {
30025
- if (idx % 1 === 0) {
30026
- r = radii[idx];
30027
- if(r > 0) constRadiius = r;
30028
- }
30029
- else {
30030
- let floored = Math.floor(idx);
30031
- let tmp = idx - floored;
30032
- // draw all atoms in tubes and assign zero radius when the residue is not coil
30033
- // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
30034
- r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
30305
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30035
30306
 
30036
- // a threshold to stop drawing the tube if it's less than this ratio of radius.
30037
- // The extra bit of tube connects coil with strands or helices
30038
- if(!bNonCoil) {
30039
- if(r < thresholdRatio * constRadiius) {
30040
- r = 0;
30041
- }
30042
- // else if(r < constRadiius) {
30043
- // r *= 0.5; // use small radius for the connection between coild and sheets/helices
30044
- // }
30307
+ if(atom.ss === firstAtom.ss && atom.ssbegin) {
30308
+ beginResi = atom.resi;
30309
+ break;
30045
30310
  }
30046
30311
  }
30047
- }
30048
- let delta, axis1, axis2;
30049
- if (i < lim - 1) {
30050
- delta = pnts[i].clone().sub(pnts[i + 1]);
30051
- axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
30052
- axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
30053
- // let dir = 1, offset = 0;
30054
- if (prevAxis1.dot(axis1) < 0) {
30055
- axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
30312
+
30313
+ for(let i = beginResi; i < firstAtom.resi; ++i) {
30314
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i;
30315
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30316
+ ic.atoms));
30056
30317
  }
30057
- prevAxis1 = axis1; prevAxis2 = axis2;
30058
- } else {
30059
- axis1 = prevAxis1; axis2 = prevAxis2;
30060
30318
  }
30061
- for (let j = 0; j < circleDiv; ++j) {
30062
- let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
30063
- let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
30064
- verticeArray[offset++] = point.x;
30065
- verticeArray[offset++] = point.y;
30066
- verticeArray[offset++] = point.z;
30067
30319
 
30068
- color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
30069
- colorArray[offset2++] = color.r;
30070
- colorArray[offset2++] = color.g;
30071
- colorArray[offset2++] = color.b;
30320
+ // add one extra residue for coils between strands/helix
30321
+ if(!isNaN(firstAtom.resi) && ic.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') {
30322
+ let residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
30323
+ if(ic.residues.hasOwnProperty(residueid)) {
30324
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30325
+ ic.atoms));
30326
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30327
+ }
30072
30328
  }
30073
- }
30074
- let offsetTmp = 0, nComp = 3;
30075
- for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
30076
- let reg = 0;
30077
- //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
30078
- //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
30079
- let pos = offsetTmp * nComp;
30080
- let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30081
- pos = (offsetTmp + circleDiv) * nComp;
30082
- let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30083
- pos = (offsetTmp + circleDiv + 1) * nComp;
30084
- let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
30085
30329
 
30086
- let r1 = point1.clone().sub(point2).lengthSq();
30087
- let r2 = point1.clone().sub(point3).lengthSq();
30088
- if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
30089
- //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
30090
- //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
30091
- //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
30092
- indexArray[offset3++] = offsetTmp + j;
30093
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30094
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30330
+ // fill the end
30331
+ let endResi = lastAtom.resi;
30332
+ // when a coil connects to a sheet and the last residue of coil is highlighted, the first sheet residue is set as atom.notshow. This residue should not be shown.
30095
30333
 
30096
- //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
30097
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
30098
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
30099
- indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
30100
- }
30101
- offsetTmp += circleDiv;
30102
- }
30334
+ if(lastAtom.ss !== undefined && lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) {
30103
30335
 
30104
- geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
30105
- geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
30336
+ let endChainResi = ic.firstAtomObjCls.getLastAtomObj(ic.chains[lastAtom.structure + '_' + lastAtom.chain]).resi;
30337
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endChainResi); ++i) {
30338
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30339
+ if(!ic.residues.hasOwnProperty(residueid)) break;
30106
30340
 
30107
- geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
30108
- //geo.setIndex(indexArray);
30341
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[residueid]);
30109
30342
 
30110
- //geo.computeFaceNormals();
30111
- //geo.computeVertexNormals(false);
30112
- geo.computeVertexNormals();
30343
+ if(atom.ss === lastAtom.ss && atom.ssend) {
30344
+ endResi = atom.resi;
30345
+ break;
30346
+ }
30347
+ }
30113
30348
 
30114
- let mesh;
30115
- if(bHighlight === 2) {
30116
- //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 }));
30117
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30349
+ for(let i = parseInt(lastAtom.resi) + 1; i <= parseInt(endResi); ++i) {
30350
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i;
30351
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30352
+ ic.atoms));
30353
+ }
30354
+ }
30118
30355
 
30119
- if(ic.mdl) ic.mdl.add(mesh);
30120
- }
30121
- else if(bHighlight === 1) {
30122
- mesh = new THREE.Mesh(geo, ic.matShader);
30123
- mesh.renderOrder = ic.renderOrderPicking;
30124
- //ic.mdlPicking.add(mesh);
30125
- if(ic.mdl) ic.mdl.add(mesh);
30126
- }
30127
- else {
30128
- //mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: THREE.FaceColors, side: THREE.DoubleSide }));
30129
- mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ specular: ic.frac, shininess: ic.shininess, emissive: ic.emissive, vertexColors: true, side: THREE.DoubleSide }));
30356
+ // add one extra residue for coils between strands/helix
30357
+ if(ic.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') {
30358
+ let residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (parseInt(lastAtom.resi) + 1).toString();
30359
+ if(ic.residues.hasOwnProperty(residueid)) {
30360
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, me.hashUtilsCls.hash2Atoms(ic.residues[residueid],
30361
+ ic.atoms));
30362
+ atoms = me.hashUtilsCls.unionHash(atoms, me.hashUtilsCls.hash2Atoms(ic.residues[residueid], ic.atoms));
30363
+ }
30364
+ }
30130
30365
 
30131
- if(ic.mdl) ic.mdl.add(mesh);
30132
- }
30366
+ // reset notshow
30367
+ if(lastAtom.notshow) lastAtom.notshow = undefined;
30133
30368
 
30134
- if(bHighlight === 1 || bHighlight === 2) {
30135
- ic.prevHighlightObjects.push(mesh);
30136
- }
30137
- else {
30138
- ic.objects.push(mesh);
30139
- }
30140
- }
30369
+ firstAtom = currAtom;
30370
+ }
30141
30371
 
30142
- getRadius(radius, atom) { let ic = this.icn3d; ic.icn3dui;
30143
- let radiusFinal = radius;
30144
- if(radius) {
30145
- radiusFinal = radius;
30146
- }
30147
- else {
30148
- if(atom.b > 0 && atom.b <= 100) {
30149
- radiusFinal = atom.b * 0.01;
30150
- }
30151
- else if(atom.b > 100) {
30152
- radiusFinal = 100 * 0.01;
30153
- }
30154
- else {
30155
- radiusFinal = ic.coilWidth;
30156
- }
30372
+ prevChain = currChain;
30373
+ prevResi = currResi;
30374
+ prevAtom = currAtom;
30375
+
30376
+ ++index;
30157
30377
  }
30158
30378
 
30159
- return radiusFinal;
30379
+ return atomsAdjust;
30160
30380
  }
30161
30381
  }
30162
30382
 
@@ -47810,6 +48030,13 @@ class ShowAnno {
47810
48030
  } // align seq to structure
47811
48031
  }
47812
48032
  //ic.bAnnoShown = true;
48033
+
48034
+ if($("#" + ic.pre + "anno_ig").length && $("#" + ic.pre + "anno_ig")[0].checked) {
48035
+ ic.bRunRefnumAgain = true;
48036
+ await ic.annotationCls.setAnnoTabIg();
48037
+
48038
+ ic.bRunRefnumAgain = false;
48039
+ }
47813
48040
  }
47814
48041
 
47815
48042
  async showAnnoSeqData(nucleotide_chainid, chemical_chainid, chemical_set) { let ic = this.icn3d, me = ic.icn3dui;
@@ -53544,6 +53771,7 @@ class AlignParser {
53544
53771
 
53545
53772
  async downloadAlignmentPart2(data, seqalign, chainresiCalphaHash2) { let ic = this.icn3d, me = ic.icn3dui;
53546
53773
  //ic.init();
53774
+
53547
53775
  ic.loadAtomDataCls.loadAtomDataIn(data, undefined, 'align', seqalign);
53548
53776
 
53549
53777
  if(me.cfg.align === undefined && Object.keys(ic.structures).length == 1) {
@@ -53587,20 +53815,21 @@ class AlignParser {
53587
53815
  async loadOpmDataForAlign(data, seqalign, mmdbidArray) { let ic = this.icn3d, me = ic.icn3dui;
53588
53816
  let thisClass = this;
53589
53817
 
53590
- let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
53591
- let prms1 = me.getAjaxPromise(url, 'text');
53592
- let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
53593
- let prms2 = me.getAjaxPromise(url2, 'text');
53594
-
53595
- let allPromise = Promise.allSettled([prms1, prms2]);
53596
53818
  try {
53819
+ let url = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[0].toLowerCase()+ ".pdb";
53820
+ let prms1 = me.getAjaxPromise(url, 'text');
53821
+ let url2 = "https://opm-assets.storage.googleapis.com/pdb/" + mmdbidArray[1].toLowerCase()+ ".pdb";
53822
+ let prms2 = me.getAjaxPromise(url2, 'text');
53823
+
53824
+ let allPromise = Promise.allSettled([prms1, prms2]);
53825
+
53597
53826
  let dataArray = await allPromise;
53598
-
53827
+
53599
53828
  let bFound = false;
53600
53829
  for(let i = 0, il = dataArray.length; i < il; ++i) {
53601
- //let opmdata = (me.bNode) ? dataArray[i] : dataArray[i].value;
53602
- let opmdata = dataArray[i].value;
53830
+ // if(dataArray[i].status == 'rejected') continue;
53603
53831
 
53832
+ let opmdata = dataArray[i].value;
53604
53833
  if(!opmdata) continue;
53605
53834
 
53606
53835
  ic.selectedPdbid = mmdbidArray[i];
@@ -53616,6 +53845,7 @@ class AlignParser {
53616
53845
  $("#" + ic.pre + "intra_mem_z").val(-ic.halfBilayerSize);
53617
53846
 
53618
53847
  ic.init(); // remove all previously loaded data
53848
+
53619
53849
  await thisClass.downloadAlignmentPart2(data, seqalign, chainresiCalphaHash);
53620
53850
 
53621
53851
  bFound = true;
@@ -58191,7 +58421,7 @@ class SdfParser {
58191
58421
  let sdfStr = await me.getAjaxPromise(urlSmiles, 'text');
58192
58422
 
58193
58423
  ic.init();
58194
- ic.bInputfile = true;
58424
+ //ic.bInputfile = true;
58195
58425
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + sdfStr : sdfStr;
58196
58426
  ic.InputfileType = 'sdf';
58197
58427
  await ic.sdfParserCls.loadSdfData(sdfStr);
@@ -61000,6 +61230,7 @@ class ParserUtils {
61000
61230
  ic.selectionCls.oneStructurePerWindow(); // for alignment
61001
61231
  ic.drawCls.draw();
61002
61232
  }
61233
+
61003
61234
  if(ic.bOpm) {
61004
61235
  let axis = new THREE.Vector3(1,0,0);
61005
61236
  let angle = -0.5 * Math.PI;
@@ -63537,7 +63768,8 @@ class LoadPDB {
63537
63768
 
63538
63769
  // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
63539
63770
  //This PDB parser feeds the viewer with the content of a PDB file, pdbData.
63540
- async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
63771
+ // async loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
63772
+ loadPDB(src, pdbid, bOpm, bVector, bMutation, bAppend, type, bEsmfold) { let ic = this.icn3d, me = ic.icn3dui;
63541
63773
  let hAtoms = {};
63542
63774
 
63543
63775
  let bNMR = false;
@@ -68658,17 +68890,15 @@ class SelectCollections {
68658
68890
  }
68659
68891
 
68660
68892
  //Set the menu of defined sets with an array of defined names "commandnameArray".
68661
- setAtomMenu(nameArray) {
68893
+ setAtomMenu(collection) {
68662
68894
  let ic = this.icn3d;
68663
68895
  ic.icn3dui;
68664
68896
  let html = "";
68665
- //for(let i in ic.defNames2Atoms) {
68666
- for (let i = 0, il = nameArray.length; i < il; ++i) {
68667
- let name = nameArray[i][0];
68668
- let title = nameArray[i][1];
68669
- let description = nameArray[i][2];
68670
-
68897
+
68898
+ Object.entries(collection).forEach(([name, structure], index) => {
68671
68899
  let atomHash;
68900
+ let [id, title, description, commands, pdb] = structure;
68901
+
68672
68902
  if (
68673
68903
  ic.defNames2Atoms !== undefined &&
68674
68904
  ic.defNames2Atoms.hasOwnProperty(name)
@@ -68689,12 +68919,12 @@ class SelectCollections {
68689
68919
  }
68690
68920
  }
68691
68921
 
68692
- if (i == 0) {
68693
- html += "<option value='" + nameArray[0][0] + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
68694
- } else {
68922
+ if (index === 0) {
68923
+ html += "<option value='" + name + "' selected='selected' data-description='" + description + "'>" + title + "</option>";
68924
+ } else {
68695
68925
  html += "<option value='" + name + "' data-description='" + description + "'>" + title + "</option>";
68696
- }
68697
- }
68926
+ }
68927
+ });
68698
68928
 
68699
68929
  return html;
68700
68930
  }
@@ -68748,7 +68978,7 @@ class SelectCollections {
68748
68978
  let nameArray = $(this).val();
68749
68979
  let nameStructure = $(this).find("option:selected").text();
68750
68980
  let selectedIndices = Array.from(this.selectedOptions).map(option => option.index);
68751
- let selectedIndicesMap = nameArray.reduce((map, name, i) => {
68981
+ nameArray.reduce((map, name, i) => {
68752
68982
  map[name] = selectedIndices[i];
68753
68983
  return map;
68754
68984
  }, {});
@@ -68785,13 +69015,13 @@ class SelectCollections {
68785
69015
  if (Object.keys(ic.structures).length == 0) {
68786
69016
  bAppend = false;
68787
69017
  }
68788
- await ic.pdbParserCls.loadPdbData(ic.pdbCollection[selectedIndicesMap[name]].join('\n'), undefined, undefined, bAppend);
69018
+ await ic.pdbParserCls.loadPdbData(ic.pdbCollection[name].join('\n'), undefined, undefined, bAppend);
68789
69019
  } else {
68790
69020
  await ic.chainalignParserCls.downloadMmdbAf(name, undefined, undefined, bNoDuplicate);
68791
69021
  }
68792
69022
  }
68793
69023
 
68794
- await loadStructure(collection[selectedIndicesMap[name]][4]).then(() => {
69024
+ await loadStructure(collection[name][4]).then(() => {
68795
69025
  ic.allData['all'] = {
68796
69026
  'atoms': ic.atoms,
68797
69027
  'proteins': ic.proteins,
@@ -68851,9 +69081,9 @@ class SelectCollections {
68851
69081
 
68852
69082
  ic.molTitle = ic.allData[name]['title'];
68853
69083
 
68854
- if (collection[selectedIndicesMap[name]][3] !== undefined && collection[selectedIndicesMap[name]][3].length > 0) {
69084
+ if (collection[name][3] !== undefined && collection[name][3].length > 0) {
68855
69085
  if (ic.allData[name]['commands'] == undefined) {
68856
- let commands = collection[selectedIndicesMap[name]][3];
69086
+ let commands = collection[name][3];
68857
69087
  ic.allData[name]['commands'] = commands;
68858
69088
  }
68859
69089
  }
@@ -71495,155 +71725,6 @@ class Resid2spec {
71495
71725
  }
71496
71726
  }
71497
71727
 
71498
- /**
71499
- * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
71500
- */
71501
-
71502
- class FirstAtomObj {
71503
- constructor(icn3d) {
71504
- this.icn3d = icn3d;
71505
- }
71506
-
71507
- //Return the first atom in the atom hash, which has the atom serial number as the key.
71508
- getFirstAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71509
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71510
- return undefined;
71511
- }
71512
-
71513
- let atomKeys = Object.keys(atomsHash);
71514
- let firstIndex = atomKeys[0];
71515
-
71516
- return ic.atoms[firstIndex];
71517
- }
71518
-
71519
- // n is the position of the selected atom
71520
- getMiddleAtomObj(atomsHash, n) { let ic = this.icn3d; ic.icn3dui;
71521
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71522
- return undefined;
71523
- }
71524
-
71525
- let atomKeys = Object.keys(atomsHash);
71526
- let middleIndex = (n && n < atomKeys.length) ? atomKeys[n] : atomKeys[parseInt(atomKeys.length / 2)];
71527
-
71528
- return ic.atoms[middleIndex];
71529
- }
71530
-
71531
- getFirstCalphaAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71532
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71533
- return undefined;
71534
- }
71535
-
71536
- let firstIndex;
71537
-
71538
- for(let i in atomsHash) {
71539
- if(ic.atoms[i].name == 'CA') {
71540
- firstIndex = i;
71541
- break;
71542
- }
71543
- }
71544
-
71545
- if(!firstIndex) {
71546
- for(let i in atomsHash) {
71547
- if(ic.atoms[i].name == "O3'" || ic.atoms[i].name == "O3*") {
71548
- firstIndex = i;
71549
- break;
71550
- }
71551
- }
71552
- }
71553
-
71554
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : this.getFirstAtomObj(atomsHash);
71555
- }
71556
-
71557
- getFirstAtomObjByName(atomsHash, atomName) { let ic = this.icn3d; ic.icn3dui;
71558
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71559
- return ic.atoms[0];
71560
- }
71561
-
71562
- let firstIndex;
71563
-
71564
- for(let i in atomsHash) {
71565
- if(ic.atoms[i].name == atomName) {
71566
- firstIndex = i;
71567
- break;
71568
- }
71569
- }
71570
-
71571
- return (firstIndex !== undefined) ? ic.atoms[firstIndex] : undefined;
71572
- }
71573
-
71574
- //Return the last atom in the atom hash, which has the atom serial number as the key.
71575
- getLastAtomObj(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71576
- if(atomsHash === undefined || Object.keys(atomsHash).length === 0) {
71577
- return ic.atoms[0];
71578
- }
71579
-
71580
- let atomKeys = Object.keys(atomsHash);
71581
- let lastIndex = atomKeys[atomKeys.length - 1];
71582
-
71583
- return ic.atoms[lastIndex];
71584
- }
71585
-
71586
- //Return the residue hash from the atom hash. The residue hash has the resid as the key and 1 as the value.
71587
- getResiduesFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71588
- let residuesHash = {};
71589
- for(let i in atomsHash) {
71590
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
71591
- residuesHash[residueid] = 1;
71592
- }
71593
-
71594
- return residuesHash;
71595
- }
71596
-
71597
- getResiduesFromCalphaAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71598
- let residuesHash = {};
71599
- for(let i in atomsHash) {
71600
- if((ic.atoms[i].name == 'CA' && ic.proteins.hasOwnProperty(i)) || !ic.proteins.hasOwnProperty(i)) {
71601
- let residueid = ic.atoms[i].structure + '_' + ic.atoms[i].chain + '_' + ic.atoms[i].resi;
71602
- //residuesHash[residueid] = 1;
71603
- residuesHash[residueid] = ic.atoms[i].resn;
71604
- }
71605
- }
71606
-
71607
- return residuesHash;
71608
- }
71609
-
71610
- //Return the chain hash from the atom hash. The chain hash has the chainid as the key and 1 as the value.
71611
- getChainsFromAtoms(atomsHash) { let ic = this.icn3d; ic.icn3dui;
71612
- let chainsHash = {};
71613
- for(let i in atomsHash) {
71614
- let atom = ic.atoms[i];
71615
- let chainid = atom.structure + "_" + atom.chain;
71616
-
71617
- chainsHash[chainid] = 1;
71618
- }
71619
-
71620
- return chainsHash;
71621
- }
71622
-
71623
- getAtomFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
71624
- if(ic.residues.hasOwnProperty(resid)) {
71625
- for(let i in ic.residues[resid]) {
71626
- if(ic.atoms[i].name === atomName && !ic.atoms[i].het) {
71627
- return ic.atoms[i];
71628
- }
71629
- }
71630
- }
71631
-
71632
- return undefined;
71633
- }
71634
-
71635
- getAtomCoordFromResi(resid, atomName) { let ic = this.icn3d; ic.icn3dui;
71636
- let atom = this.getAtomFromResi(resid, atomName);
71637
- if(atom !== undefined) {
71638
- let coord = (atom.coord2 !== undefined) ? atom.coord2 : atom.coord;
71639
-
71640
- return coord;
71641
- }
71642
-
71643
- return undefined;
71644
- }
71645
- }
71646
-
71647
71728
  /**
71648
71729
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
71649
71730
  */
@@ -71704,9 +71785,9 @@ class Delphi {
71704
71785
  let pdbstr = '';
71705
71786
  /// pdbstr += ic.saveFileCls.getPDBHeader();
71706
71787
 
71707
- let bMergeIntoOne = true;
71708
- pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne);
71709
- pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true);
71788
+ let bMergeIntoOne = true, bOneLetterChain = true;
71789
+ pdbstr +=(me.cfg.cid) ? ic.saveFileCls.getAtomPDB(atomHash, true, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain) : ic.saveFileCls.getAtomPDB(atomHash, undefined, undefined, undefined, undefined, undefined, bMergeIntoOne, bOneLetterChain);
71790
+ pdbstr += ic.saveFileCls.getAtomPDB(ionHash, true, undefined, true, undefined, undefined, bMergeIntoOne, bOneLetterChain);
71710
71791
 
71711
71792
  return pdbstr;
71712
71793
  }
@@ -78976,7 +79057,7 @@ class SaveFile {
78976
79057
  }
78977
79058
 
78978
79059
  //getAtomPDB: function(atomHash, bPqr, bPdb, bNoChem) { let ic = this.icn3d, me = ic.icn3dui;
78979
- getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bVastSearch) { let ic = this.icn3d, me = ic.icn3dui;
79060
+ getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb, pdbid, bMergeIntoOne, bOneLetterChain) { let ic = this.icn3d, me = ic.icn3dui;
78980
79061
  let pdbStr = '';
78981
79062
 
78982
79063
  // get all phosphate groups in lipids
@@ -79251,7 +79332,7 @@ class SaveFile {
79251
79332
  //line +=(atom.chain.length <= 1) ? atom.chain.padStart(1, ' ') : atom.chain.substr(0, 1);
79252
79333
  if(atom.chain.length >= 2) {
79253
79334
  let chainTmp = atom.chain.replace(/_/gi, '').substr(0, 2);
79254
- if(bVastSearch) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
79335
+ if(bOneLetterChain) chainTmp = ' ' + chainTmp.substr(0,1); // VAST search only support one lettter chain ID
79255
79336
  line += chainTmp;
79256
79337
  }
79257
79338
  else if(atom.chain.length == 1) {
@@ -79330,8 +79411,8 @@ class SaveFile {
79330
79411
  }
79331
79412
  else {
79332
79413
  line += "1.00".padStart(6, ' ');
79333
- // line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : ' '.padStart(6, ' ');
79334
- let defaultBFactor = (bVastSearch) ? "1.0" : " ";
79414
+ // let defaultBFactor = (bOneLetterChain) ? "1.0" : " ";
79415
+ let defaultBFactor = " ";
79335
79416
  line +=(atom.b) ? parseFloat(atom.b).toFixed(2).toString().padStart(6, ' ') : defaultBFactor.padStart(6, ' ');
79336
79417
  line += ' '.padStart(10, ' ');
79337
79418
  line += atom.elem.padStart(2, ' ');
@@ -79379,7 +79460,7 @@ class SaveFile {
79379
79460
  let resn = me.utilsCls.residueName2Abbr(atom.resn);
79380
79461
  let ss = this.secondary2Abbr(atom.ss);
79381
79462
 
79382
- if(chainid != prevChainid) {
79463
+ if(chainid != prevChainid && !data[chainid]) {
79383
79464
  data[chainid] = {"resi": [], "resn": [], "secondary": []};
79384
79465
  }
79385
79466
 
@@ -79400,8 +79481,8 @@ class SaveFile {
79400
79481
  json += '{"chain": "' + chainid + '",\n';
79401
79482
 
79402
79483
  json += '"resi": "' + data[chainid]["resi"].join(',') + '",\n';
79403
- json += '"resn": "' + data[chainid]["resn"].join('') + '",\n';
79404
- json += '"secondary": "' + data[chainid]["secondary"].join('') + '"';
79484
+ json += '"resn": "' + data[chainid]["resn"].join(',') + '",\n';
79485
+ json += '"secondary": "' + data[chainid]["secondary"].join(',') + '"';
79405
79486
 
79406
79487
  if(i < cnt - 1) {
79407
79488
  json += '},\n';
@@ -82903,7 +82984,7 @@ class iCn3DUI {
82903
82984
  //even when multiple iCn3D viewers are shown together.
82904
82985
  this.pre = this.cfg.divid + "_";
82905
82986
 
82906
- this.REVISION = '3.40.0';
82987
+ this.REVISION = '3.40.3';
82907
82988
 
82908
82989
  // In nodejs, iCn3D defines "window = {navigator: {}}"
82909
82990
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -83065,7 +83146,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
83065
83146
  ic.molTitle = '';
83066
83147
  ic.loadCmd;
83067
83148
 
83068
- // set menus
83149
+ // set menus
83069
83150
  me.htmlCls.clickMenuCls.getHiddenMenusFromCache();
83070
83151
  me.htmlCls.clickMenuCls.applyShownMenus();
83071
83152