icn3d 3.32.0 → 3.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/icn3d.js +694 -334
  2. package/icn3d.min.js +3 -11
  3. package/icn3d.module.js +694 -334
  4. package/package.json +1 -1
package/icn3d.js CHANGED
@@ -6893,7 +6893,10 @@ class UtilsCls {
6893
6893
  let nOtherAtoms = 0;
6894
6894
  for(let i in atomlist) {
6895
6895
  if(index < testLength) {
6896
- let atomName = atomlist[i].name.trim();
6896
+ let atomName = atomlist[i].name;
6897
+ if(!atomName) continue;
6898
+ atomName = atomName.trim();
6899
+
6897
6900
  if(atomName !== "CA" && atomName !== "P" && atomName !== "O3'" && atomName !== "O3*") {
6898
6901
  //bOtherAtoms = true;
6899
6902
  //break;
@@ -9046,7 +9049,7 @@ class ClickMenu {
9046
9049
  });
9047
9050
 
9048
9051
  me.myEventCls.onIds("#" + me.pre + "mn1_cid", "click", function(e) { me.icn3d; //e.preventDefault();
9049
- me.htmlCls.dialogCls.openDlg('dl_cid', 'Please input PubChem CID');
9052
+ me.htmlCls.dialogCls.openDlg('dl_cid', 'Please input PubChem Compound');
9050
9053
  });
9051
9054
 
9052
9055
  me.myEventCls.onIds("#" + me.pre + "mn1_pngimage", "click", function(e) { me.icn3d; //e.preventDefault();
@@ -10588,6 +10591,13 @@ class ClickMenu {
10588
10591
  ic.drawCls.draw();
10589
10592
  });
10590
10593
 
10594
+ me.myEventCls.onIds("#" + me.pre + "mn6_addlabelIg", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10595
+ ic.residueLabelsCls.addIgLabels(ic.hAtoms);
10596
+ ic.selectionCls.saveSelectionIfSelected();
10597
+ thisClass.setLogCmd('add ig labels', true);
10598
+ ic.drawCls.draw();
10599
+ });
10600
+
10591
10601
  me.myEventCls.onIds("#" + me.pre + "mn6_addlabelChains", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10592
10602
  ic.analysisCls.addChainLabels(ic.hAtoms);
10593
10603
  ic.selectionCls.saveSelectionIfSelected();
@@ -10663,13 +10673,21 @@ class ClickMenu {
10663
10673
  });
10664
10674
 
10665
10675
  me.myEventCls.onIds("#" + me.pre + "mn6_distManySets", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10666
- me.htmlCls.dialogCls.openDlg('dl_distmanysets', 'Measure the pairwise distance among many sets');
10676
+ me.htmlCls.dialogCls.openDlg('dl_distmanysets', 'Measure the pairwise distances among many sets');
10667
10677
 
10668
10678
  thisClass.setSetsMenus('atomsCustomDistTable');
10669
10679
 
10670
10680
  ic.bMeasureDistance = true;
10671
10681
  });
10672
10682
 
10683
+ me.myEventCls.onIds("#" + me.pre + "mn6_angleManySets", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10684
+ me.htmlCls.dialogCls.openDlg('dl_anglemanysets', 'Measure the pairwise angles among many sets');
10685
+
10686
+ thisClass.setSetsMenus('atomsCustomAngleTable');
10687
+
10688
+ ic.bMeasureAngle = true;
10689
+ });
10690
+
10673
10691
  me.myEventCls.onIds("#" + me.pre + "mn6_distanceNo", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
10674
10692
  ic.pickpair = false;
10675
10693
  let select = "set lines off";
@@ -10754,6 +10772,10 @@ class ClickMenu {
10754
10772
  me.htmlCls.dialogCls.openDlg('dl_translate', 'Translate the X,Y,Z coordinates of the structure');
10755
10773
  });
10756
10774
 
10775
+ $(document).on("click", "#" + me.pre + "mn6_angleTwoSets", function(e) { me.icn3d; //e.preventDefault();
10776
+ me.htmlCls.dialogCls.openDlg('dl_angle', 'Measure the angle between two vectors');
10777
+ });
10778
+
10757
10779
  $(document).on("click", "#" + me.pre + "mn2_matrix", function(e) { me.icn3d; //e.preventDefault();
10758
10780
  me.htmlCls.dialogCls.openDlg('dl_matrix', 'Apply matrix to the X,Y,Z coordinates of the structure');
10759
10781
  });
@@ -11674,7 +11696,7 @@ class SetMenu {
11674
11696
  html += this.getLink('mn1_mmcifid', 'RCSB mmCIF ID ' + me.htmlCls.wifiStr, undefined, 2);
11675
11697
  //html += this.getLink('mn1_gi', 'NCBI gi ' + me.htmlCls.wifiStr, undefined, 2);
11676
11698
 
11677
- html += this.getLink('mn1_cid', 'PubChem CID ' + me.htmlCls.wifiStr, 1, 2);
11699
+ html += this.getLink('mn1_cid', 'PubChem CID/Name/InchI ' + me.htmlCls.wifiStr, 1, 2);
11678
11700
 
11679
11701
  html += "</ul>";
11680
11702
  html += "</li>";
@@ -11684,7 +11706,7 @@ class SetMenu {
11684
11706
  // html += this.getLink('mn1_pdbfile', 'PDB File');
11685
11707
  // html += this.getLink('mn1_pdbfile_app', 'PDB File (append)');
11686
11708
  html += this.getLink('mn1_pdbfile_app', 'PDB Files (appendable)', 1, 2);
11687
- html += this.getLink('mn1_mmciffile', 'mmCIF File (appendable)', undefined, 2);
11709
+ html += this.getLink('mn1_mmciffile', 'mmCIF Files (appendable)', undefined, 2);
11688
11710
  html += this.getLink('mn1_mol2file', 'Mol2 File', undefined, 2);
11689
11711
  html += this.getLink('mn1_sdffile', 'SDF File', undefined, 2);
11690
11712
  html += this.getLink('mn1_xyzfile', 'XYZ File', undefined, 2);
@@ -11812,15 +11834,12 @@ class SetMenu {
11812
11834
  html += this.getLink('mn1_exportSecondary', 'Secondary Structure', undefined, 2);
11813
11835
  }
11814
11836
 
11815
- //!!!
11816
- /*
11817
11837
  html += this.getMenuText('m1_exportrefnum', 'Reference Numbers', undefined, undefined, 2);
11818
11838
  html += "<ul>";
11819
11839
  html += this.getLink('mn1_exportIgstrand', 'Ig Strand', undefined, 3);
11820
11840
  html += this.getLink('mn1_exportKabat', 'Kabat', undefined, 3);
11821
11841
  html += this.getLink('mn1_exportImgt', 'IMGT', undefined, 3);
11822
11842
  html += "</ul>";
11823
- */
11824
11843
 
11825
11844
  html += "<li><br/></li>";
11826
11845
 
@@ -12663,9 +12682,8 @@ class SetMenu {
12663
12682
  html += this.getRadio('mn4_clr', 'mn4_clrConfidence', 'pLDDT', undefined, 1, 1);
12664
12683
  //}
12665
12684
 
12666
- //!!!
12667
- // html += this.getRadio('mn4_clr', 'mn4_clrIgstrand', 'Ig Strand', undefined, undefined, 2);
12668
- // html += this.getRadio('mn4_clr', 'mn4_clrIgproto', 'Ig Protodomain', undefined, undefined, 2);
12685
+ html += this.getRadio('mn4_clr', 'mn4_clrIgstrand', 'Ig Strand', undefined, undefined, 2);
12686
+ html += this.getRadio('mn4_clr', 'mn4_clrIgproto', 'Ig Protodomain', undefined, undefined, 2);
12669
12687
  }
12670
12688
  else {
12671
12689
  //if(!me.cfg.hidelicense) html += this.getRadio('mn4_clr', 'mn1_delphi2', 'DelPhi<br><span style="padding-left:1.5em;">Potential ' + me.htmlCls.licenseStr + '</span>');
@@ -12779,11 +12797,18 @@ class SetMenu {
12779
12797
  html += "<ul>";
12780
12798
  html += this.getRadio('mn6_distance', 'mn6_distanceYes', 'between Two Atoms', undefined, 1, 2);
12781
12799
  html += this.getRadio('mn6_distance', 'mn6_distTwoSets', 'between Two Sets', undefined, undefined, 2);
12782
- html += this.getRadio('mn6_distance', 'mn6_distManySets', 'Among Many Sets', undefined, undefined, 2);
12800
+ html += this.getRadio('mn6_distance', 'mn6_distManySets', 'among Many Sets', undefined, undefined, 2);
12783
12801
  html += this.getRadio('mn6_distance', 'mn6_distanceNo', 'Hide', true, 1, 2);
12784
12802
  html += "</ul>";
12785
12803
  html += "</li>";
12786
12804
 
12805
+ html += this.getMenuText('mn6_anglewrap', 'Angle', undefined, 1, 1);
12806
+ html += "<ul>";
12807
+ html += this.getRadio('mn6_angle', 'mn6_angleManySets', 'among Many Sets', undefined, 1, 2);
12808
+ html += this.getRadio('mn6_angle', 'mn6_angleTwoSets', 'b/w Two Vectors', undefined, undefined, 2);
12809
+ html += "</ul>";
12810
+ html += "</li>";
12811
+
12787
12812
  html += this.getLink('mn6_area', 'Surface Area', 1, 1);
12788
12813
 
12789
12814
  html += this.getMenuText('mn6_addlabelwrap', 'Label', undefined, 1, 1);
@@ -12795,8 +12820,10 @@ class SetMenu {
12795
12820
  if(me.cfg.cid === undefined) {
12796
12821
  html += this.getRadio('mn6_addlabel', 'mn6_addlabelResidues', 'per Residue', undefined, 1, 2);
12797
12822
  html += this.getRadio('mn6_addlabel', 'mn6_addlabelResnum', 'per Residue & Number', undefined, 1, 2);
12798
- //!!!
12799
- // html += this.getRadio('mn6_addlabel', 'mn6_addlabelRefnum', 'per Reference Number', undefined, 1, 2);
12823
+
12824
+ html += this.getRadio('mn6_addlabel', 'mn6_addlabelRefnum', 'per Reference Number', undefined, 1, 2);
12825
+ html += this.getRadio('mn6_addlabel', 'mn6_addlabelIg', 'per Ig Domain', undefined, 1, 2);
12826
+
12800
12827
  html += this.getRadio('mn6_addlabel', 'mn6_addlabelChains', 'per Chain', undefined, undefined, 2);
12801
12828
  html += this.getRadio('mn6_addlabel', 'mn6_addlabelTermini', 'N- & C-Termini', undefined, 1, 2);
12802
12829
  }
@@ -12889,17 +12916,13 @@ class SetMenu {
12889
12916
  html += this.getMenuText('mn6_igrefwrap', 'Ref. Number', undefined, undefined, 1);
12890
12917
 
12891
12918
  html += "<ul>";
12892
- //!!!
12893
- /*
12919
+
12894
12920
  html += this.getLink('mn6_igrefYes', 'Show Ig for Selection', undefined, 2);
12895
12921
  html += this.getLink('mn6_igrefTpl', 'Ig w/ Specified Template', undefined, 2);
12896
12922
  html += this.getLink('mn6_alignrefTpl', 'Align w/ Specified Template', undefined, 2);
12897
12923
  html += this.getLink('mn6_igrefNo', 'Reset Ig Ref. Number', undefined, 2);
12898
12924
 
12899
12925
  html += this.getMenuSep();
12900
- */
12901
-
12902
-
12903
12926
 
12904
12927
  html += this.getLink('mn6_customref', 'Custom Ref. Number', undefined, 2);
12905
12928
  html += "</ul>";
@@ -12989,6 +13012,7 @@ class SetMenu {
12989
13012
  html += this.getMenuUrl('faq_simialphapdb', me.htmlCls.baseUrl + "icn3d/icn3d.html#simifoldseek", "Similar AlphaFold/PDB", 1, 2);
12990
13013
  html += this.getMenuUrl('faq_alnstru', me.htmlCls.baseUrl + "icn3d/icn3d.html#alignmul", "Align Multiple Structures", 1, 2);
12991
13014
  html += this.getMenuUrl('faq_batchanal', me.htmlCls.baseUrl + "icn3d/icn3d.html#batchanalysis", "Batch Analysis", 1, 2);
13015
+ html += this.getMenuUrl('faq_batchanal', me.htmlCls.baseUrl + "icn3d/icn3d.html#igrefnum", "Assign Ig Ref. Numbers", 1, 2);
12992
13016
  html += this.getMenuUrl('faq_embedicn3d', me.htmlCls.baseUrl + "icn3d/icn3d.html#embedicn3d", "Embed iCn3D", 1, 2);
12993
13017
  html += "</ul>";
12994
13018
  html += "</li>";
@@ -14100,8 +14124,8 @@ class SetDialog {
14100
14124
  html += "</div>";
14101
14125
 
14102
14126
  html += me.htmlCls.divStr + "dl_cid' class='" + dialogClass + "'>";
14103
- html += this.addNotebookTitle('dl_cid', 'Please input a PubChem CID');
14104
- html += "PubChem CID: " + me.htmlCls.inputTextStr + "id='" + me.pre + "cid' value='2244' size=8> ";
14127
+ html += this.addNotebookTitle('dl_cid', 'Please input a PubChem Compound');
14128
+ html += "PubChem CID/Name/InchI: " + me.htmlCls.inputTextStr + "id='" + me.pre + "cid' value='2244' size=8> ";
14105
14129
  html += me.htmlCls.buttonStr + "reload_cid'>Load</button>";
14106
14130
  html += "</div>";
14107
14131
 
@@ -14732,6 +14756,28 @@ class SetDialog {
14732
14756
  html += me.htmlCls.spanNowrapStr + "2. " + me.htmlCls.buttonStr + "applydisttable'>Distances in Table</button></span>";
14733
14757
  html += "</div>";
14734
14758
 
14759
+
14760
+ html += me.htmlCls.divStr + "dl_anglemanysets' class='" + dialogClass + "'>";
14761
+ html += this.addNotebookTitle('dl_anglemanysets', 'Measure angles among many sets');
14762
+ html += me.htmlCls.spanNowrapStr + "1. Select sets for pairwise angles</span><br/>";
14763
+ html += "<table border=0 width=400 cellspacing=10><tr><td>";
14764
+
14765
+ html += me.htmlCls.divNowrapStr + "First sets:</div>";
14766
+ html += "<div style='text-indent:1.1em'><select style='max-width:200px' id='" + me.pre + "atomsCustomAngleTable2' multiple size='5' style='min-width:130px;'>";
14767
+ html += "</select></div>";
14768
+
14769
+ html += "</td><td>";
14770
+
14771
+ html += me.htmlCls.divNowrapStr + "Second sets:</div>";
14772
+ html += "<div style='text-indent:1.1em'><select style='max-width:200px' id='" + me.pre + "atomsCustomAngleTable' multiple size='5' style='min-width:130px;'>";
14773
+ html += "</select></div>";
14774
+
14775
+ html += "</td></tr></table>";
14776
+
14777
+ html += me.htmlCls.spanNowrapStr + "2. " + me.htmlCls.buttonStr + "applyangletable'>Angles in Table</button></span>";
14778
+ html += "</div>";
14779
+
14780
+
14735
14781
  html += me.htmlCls.divStr + "dl_stabilizer_rm' class='" + dialogClass + "'>";
14736
14782
  html += this.addNotebookTitle('dl_stabilizer_rm', 'Remove a stabilizer');
14737
14783
  if(me.utilsCls.isMobile()) {
@@ -15011,6 +15057,11 @@ class SetDialog {
15011
15057
  html += this.addNotebookTitle('dl_disttable', 'Distance Table', true);
15012
15058
  html += "</div>";
15013
15059
 
15060
+
15061
+ html += me.htmlCls.divStr + "dl_angletable' class='" + dialogClass + "'>";
15062
+ html += this.addNotebookTitle('dl_angletable', 'Angle Table', true);
15063
+ html += "</div>";
15064
+
15014
15065
  html += me.htmlCls.divStr + "dl_translate' class='" + dialogClass + "'>";
15015
15066
  html += this.addNotebookTitle('dl_translate', 'Translate the X,Y,Z coordinates of the structure');
15016
15067
  html += "X: " + me.htmlCls.inputTextStr + "id='" + me.pre + "translateX' value='' size=4> ";
@@ -15019,6 +15070,20 @@ class SetDialog {
15019
15070
  html += me.htmlCls.buttonStr + "translate_pdb'>Translate</button>";
15020
15071
  html += "</div>";
15021
15072
 
15073
+ html += me.htmlCls.divStr + "dl_angle' class='" + dialogClass + "'>";
15074
+ html += this.addNotebookTitle('dl_angle', 'Measure the angle between two vectors');
15075
+ html += "<b>Vector 1</b>, X: " + me.htmlCls.inputTextStr + "id='" + me.pre + "v1X' value='' size=6> ";
15076
+ html += "Y: " + me.htmlCls.inputTextStr + "id='" + me.pre + "v1Y' value='' size=6> ";
15077
+ html += "Z: " + me.htmlCls.inputTextStr + "id='" + me.pre + "v1Z' value='' size=6><br>";
15078
+ html += "<b>Vector 2</b>, X: " + me.htmlCls.inputTextStr + "id='" + me.pre + "v2X' value='' size=6> ";
15079
+ html += "Y: " + me.htmlCls.inputTextStr + "id='" + me.pre + "v2Y' value='' size=6> ";
15080
+ html += "Z: " + me.htmlCls.inputTextStr + "id='" + me.pre + "v2Z' value='' size=6><br>";
15081
+ html += "<br>";
15082
+
15083
+ html += me.htmlCls.buttonStr + "measure_angle'>Measure Angle</button>";
15084
+ html += "The angle is: " + me.htmlCls.inputTextStr + "id='" + me.pre + "angle_value' value='' size=6> degree.";
15085
+ html += "</div>";
15086
+
15022
15087
  html += me.htmlCls.divStr + "dl_matrix' class='" + dialogClass + "'>";
15023
15088
  html += this.addNotebookTitle('dl_matrix', 'Apply matrix to the X,Y,Z coordinates of the structure');
15024
15089
  html += "0: " + me.htmlCls.inputTextStr + "id='" + me.pre + "matrix0' value='1' size=2> ";
@@ -15177,13 +15242,10 @@ class SetDialog {
15177
15242
  html += tmpStr1 + me.htmlCls.inputCheckStr + "id='" + me.pre + "anno_interact'>Interactions" + me.htmlCls.space2 + "</span></td>";
15178
15243
  html += tmpStr1 + me.htmlCls.inputCheckStr + "id='" + me.pre + "anno_crosslink'>Cross-Linkages" + me.htmlCls.space2 + "</span></td>";
15179
15244
  html += tmpStr1 + me.htmlCls.inputCheckStr + "id='" + me.pre + "anno_transmem'>Transmembrane" + me.htmlCls.space2 + "</span></td>";
15180
- //!!!
15181
- /*
15245
+
15182
15246
  html += "<td></td>";
15183
15247
  html += "</tr><tr>";
15184
15248
  html += tmpStr1 + me.htmlCls.inputCheckStr + "id='" + me.pre + "anno_ig'>Ig Domains" + me.htmlCls.space2 + "</span></td>";
15185
- */
15186
-
15187
15249
 
15188
15250
  html += "<td></td>";
15189
15251
  html += "</tr></table></div></div>";
@@ -15865,6 +15927,27 @@ class Events {
15865
15927
  thisClass.setLogCmd("translate pdb " + dx + " " + dy + " " + dz, true);
15866
15928
  });
15867
15929
 
15930
+ me.myEventCls.onIds("#" + me.pre + "measure_angle", "click", function(e) { me.icn3d;
15931
+ e.preventDefault();
15932
+ if(!me.cfg.notebook) dialog.dialog( "close" );
15933
+ let v1X = $("#" + me.pre + "v1X").val();
15934
+ let v1Y = $("#" + me.pre + "v1Y").val();
15935
+ let v1Z= $("#" + me.pre + "v1Z").val();
15936
+
15937
+ let v2X = $("#" + me.pre + "v2X").val();
15938
+ let v2Y = $("#" + me.pre + "v2Y").val();
15939
+ let v2Z = $("#" + me.pre + "v2Z").val();
15940
+
15941
+ let angleRad = new THREE.Vector3(parseFloat(v1X), parseFloat(v1Y), parseFloat(v1Z)).angleTo(new THREE.Vector3(parseFloat(v2X), parseFloat(v2Y), parseFloat(v2Z)));
15942
+ let angle = angleRad / 3.1416 * 180;
15943
+ angle = Math.abs(angle).toFixed(0);
15944
+ if(angle > 180) angle -= 180;
15945
+ if(angle > 90) angle = 180 - angle;
15946
+
15947
+ thisClass.setLogCmd("The angle is " + angle + " degree", false);
15948
+ $("#" + me.pre + "angle_value").val(angle);
15949
+ });
15950
+
15868
15951
  me.myEventCls.onIds("#" + me.pre + "matrix_pdb", "click", function(e) { let ic = me.icn3d;
15869
15952
  e.preventDefault();
15870
15953
  if(!me.cfg.notebook) dialog.dialog( "close" );
@@ -17521,6 +17604,20 @@ class Events {
17521
17604
  thisClass.setLogCmd("disttable | " + nameArray2 + " " + nameArray, true);
17522
17605
  });
17523
17606
 
17607
+ me.myEventCls.onIds("#" + me.pre + "applyangletable", "click", function(e) { let ic = me.icn3d;
17608
+ e.preventDefault();
17609
+ if(!me.cfg.notebook) dialog.dialog( "close" );
17610
+ ic.bMeasureAngle = false;
17611
+
17612
+ let nameArray = $("#" + me.pre + "atomsCustomAngleTable").val();
17613
+ let nameArray2 = $("#" + me.pre + "atomsCustomAngleTable2").val();
17614
+
17615
+ ic.analysisCls.measureAngleManySets(nameArray, nameArray2);
17616
+ me.htmlCls.dialogCls.openDlg('dl_angletable', 'Angles among the sets');
17617
+
17618
+ thisClass.setLogCmd("angletable | " + nameArray2 + " " + nameArray, true);
17619
+ });
17620
+
17524
17621
  me.myEventCls.onIds("#" + me.pre + "applylinebtwsets", "click", function(e) { let ic = me.icn3d;
17525
17622
  e.preventDefault();
17526
17623
 
@@ -28110,18 +28207,17 @@ class Strand {
28110
28207
 
28111
28208
  let bRibbon = fill ? true: false;
28112
28209
 
28113
- // when highlight, the input atoms may only include part of sheet or helix
28114
- // include the whole sheet or helix when highlighting
28210
+ // when highlight, the input atoms may only include one rediue.
28211
+ // add one extra residue to show the strand
28115
28212
  let atomsAdjust = {};
28116
28213
 
28117
- // if( Object.keys(atoms).length < Object.keys(ic.atoms).length) {
28118
- // atomsAdjust = this.getSSExpandedAtoms(atoms);
28119
- // }
28120
- // else {
28121
- // atomsAdjust = atoms;
28122
- // }
28123
-
28124
- atomsAdjust = atoms;
28214
+ let residueHashTmp = ic.firstAtomObjCls.getResiduesFromAtoms(atoms);
28215
+ if( Object.keys(residueHashTmp).length == 1) {
28216
+ atomsAdjust = this.getOneExtraResidue(residueHashTmp);
28217
+ }
28218
+ else {
28219
+ atomsAdjust = atoms;
28220
+ }
28125
28221
 
28126
28222
  if(bHighlight === 2) {
28127
28223
  if(fill) {
@@ -28156,22 +28252,24 @@ class Strand {
28156
28252
  let currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null;
28157
28253
  let prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevAtomSelected = null, prevResi = null, calphaid = null, prevCalphaid = null;
28158
28254
  let strandWidth, bSheetSegment = false, bHelixSegment = false;
28159
- let atom, tubeAtoms = {};
28160
28255
 
28161
- // test the first 30 atoms to see whether only C-alpha is available
28162
- ic.bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atomsAdjust); //, 'CA');
28256
+ // For each chain, test the first 30 atoms to see whether only C-alpha is available
28257
+ let bCalphaOnlyHash = {};
28258
+ for(let chainid in ic.chains) {
28259
+ let atoms = me.hashUtilsCls.hash2Atoms(ic.chains[chainid], ic.atoms);
28260
+ let bCalphaOnly = me.utilsCls.isCalphaPhosOnly(atoms); //, 'CA');
28261
+ bCalphaOnlyHash[chainid] = bCalphaOnly;
28262
+ }
28163
28263
 
28164
28264
  // when highlight, draw whole beta sheet and use bShowArray to show the highlight part
28165
28265
  let residueHash = {};
28166
28266
  for(let i in atomsAdjust) {
28167
- let atom = atomsAdjust[i];
28267
+ let atom = ic.atoms[i];
28168
28268
 
28169
28269
  let residueid = atom.structure + '_' + atom.chain + '_' + atom.resi;
28170
28270
  residueHash[residueid] = 1;
28171
28271
  }
28172
- let totalResidueCount = Object.keys(residueHash).length;
28173
-
28174
- let drawnResidueCount = 0;
28272
+ Object.keys(residueHash).length;
28175
28273
 
28176
28274
  let bFullAtom = (Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) ? true : false;
28177
28275
 
@@ -28180,22 +28278,20 @@ class Strand {
28180
28278
  let maxDist = 6.0;
28181
28279
 
28182
28280
  //get the last residue
28183
- let atomArray = Object.keys(atoms);
28281
+ let atomArray = Object.keys(atomsAdjust);
28184
28282
  let lastAtomSerial = atomArray[atomArray.length - 1];
28185
- let lastAtom = atoms[lastAtomSerial];
28283
+ let lastAtom = ic.atoms[lastAtomSerial];
28186
28284
  let lastResid = lastAtom.structure + '_' + lastAtom.chain + '_' + lastAtom.resi;
28187
28285
 
28188
28286
  for (let i in atomsAdjust) {
28189
- atom = atomsAdjust[i];
28287
+ let atom = ic.atoms[i];
28190
28288
  let chainid = atom.structure + '_' + atom.chain;
28191
28289
  let resid = atom.structure + '_' + atom.chain + '_' + atom.resi;
28192
28290
  if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
28193
28291
  // "CA" has to appear before "O"
28194
28292
 
28195
28293
  if (atom.name === 'CA') {
28196
- if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) {
28197
- tubeAtoms[i] = atom;
28198
- }
28294
+ if ( atoms.hasOwnProperty(i) && ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) ) ;
28199
28295
 
28200
28296
  currentCA = atom.coord;
28201
28297
  currentColor = atom.color;
@@ -28204,7 +28300,7 @@ class Strand {
28204
28300
  caArray.push(atom.serial);
28205
28301
  }
28206
28302
 
28207
- if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA')) {
28303
+ if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA')) {
28208
28304
  if(currentCA === null || currentCA === undefined) {
28209
28305
  currentCA = atom.coord;
28210
28306
  currentColor = atom.color;
@@ -28267,7 +28363,7 @@ class Strand {
28267
28363
  }
28268
28364
  }
28269
28365
  }
28270
- else if(ic.bCalphaOnly && atom.name === 'CA') {
28366
+ else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
28271
28367
  if(caArray.length > resSpan + 1) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
28272
28368
  O = prevCoorCA.clone();
28273
28369
  oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan - 1]].coord.clone();
@@ -28302,8 +28398,6 @@ class Strand {
28302
28398
  bShowArray.push(0);
28303
28399
  calphaIdArray.push(0);
28304
28400
  }
28305
-
28306
- ++drawnResidueCount;
28307
28401
  }
28308
28402
 
28309
28403
  //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);
@@ -28332,7 +28426,9 @@ class Strand {
28332
28426
  // }
28333
28427
 
28334
28428
  //if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
28335
- if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs || resid == lastResid) && pnts[0].length > 0) {
28429
+ // 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
28430
+
28431
+ if ((currentChain !== atom.chain || atom.ssbegin || atom.ssend || bBrokenSs || (resid == lastResid && atom.ss != 'coil')) && pnts[0].length > 0) {
28336
28432
  let atomName = 'CA';
28337
28433
 
28338
28434
  let prevone = [], nexttwo = [];
@@ -28389,7 +28485,7 @@ class Strand {
28389
28485
  O = currentO.clone();
28390
28486
  O.sub(currentCA);
28391
28487
  }
28392
- else if(ic.bCalphaOnly && atom.name === 'CA') {
28488
+ else if(bCalphaOnlyHash[chainid] && atom.name === 'CA') {
28393
28489
  if(caArray.length > resSpan) { // use the calpha and the previous 4th c-alpha to calculate the helix direction
28394
28490
  O = currentCA.clone();
28395
28491
  oldCA = ic.atoms[caArray[caArray.length - 1 - resSpan]].coord.clone();
@@ -28478,7 +28574,9 @@ class Strand {
28478
28574
  // end of a chain, or end of selection
28479
28575
  if ((currentChain !== atom.chain
28480
28576
  || ic.ParserUtilsCls.getResiNCBI(atom.structure + '_' + currentChain, currentResi) + 1 !== ic.ParserUtilsCls.getResiNCBI(chainid, atom.resi)
28481
- || resid == lastResid
28577
+ // || (drawnResidueCount === totalResidueCount - 1)
28578
+ // || bBrokenSs
28579
+ || (resid == lastResid && atom.ss != 'coil')
28482
28580
  ) && pnts[0].length > 0) {
28483
28581
  //if ((currentChain !== atom.chain) && pnts[0].length > 0) {
28484
28582
 
@@ -28546,18 +28644,42 @@ class Strand {
28546
28644
  prevCoorCA = currentCA;
28547
28645
  prevCoorO = atom.coord;
28548
28646
  prevColor = currentColor;
28549
- } // end if (atom.name === 'O' || (ic.bCalphaOnly && atom.name === 'CA') ) {
28647
+ } // end if (atom.name === 'O' || (bCalphaOnlyHash[chainid] && atom.name === 'CA') ) {
28550
28648
  } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) {
28551
28649
  } // end for
28552
28650
 
28553
28651
  caArray = [];
28554
28652
 
28555
- ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
28556
-
28557
- tubeAtoms = {};
28653
+ // ic.tubeCls.createTube(tubeAtoms, 'CA', coilWidth, bHighlight);
28654
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
28655
+ ic.tubeCls.createTube(atomsAdjust, 'CA', coilWidth, bHighlight);
28558
28656
  pnts = {};
28559
28657
  }
28560
28658
 
28659
+ getOneExtraResidue(residueHash) { let ic = this.icn3d, me = ic.icn3dui;
28660
+ let atomsAdjust = {};
28661
+
28662
+ for(let resid in residueHash) {
28663
+ atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[resid]);
28664
+
28665
+ let residNcbi = ic.resid2ncbi[resid];
28666
+ let resiNcbi = residNcbi.substr(residNcbi.lastIndexOf('_') + 1);
28667
+
28668
+ let nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) + 1);
28669
+ let nextResid = ic.ncbi2resid[nextResidNcbi];
28670
+
28671
+ if(!nextResid) {
28672
+ nextResidNcbi = residNcbi.substr(0, residNcbi.lastIndexOf('_')) + '_' + (parseInt(resiNcbi) - 1);
28673
+ nextResid = ic.ncbi2resid[nextResidNcbi];
28674
+ }
28675
+
28676
+ if(nextResid) atomsAdjust = me.hashUtilsCls.unionHash(atomsAdjust, ic.residues[nextResid]);
28677
+ }
28678
+
28679
+ return atomsAdjust;
28680
+ }
28681
+
28682
+ /*
28561
28683
  getSSExpandedAtoms(atoms, bHighlight) { let ic = this.icn3d, me = ic.icn3dui;
28562
28684
  let currChain, currResi, currAtom, prevChain, prevResi, prevAtom;
28563
28685
  let firstAtom, lastAtom;
@@ -28664,6 +28786,7 @@ class Strand {
28664
28786
 
28665
28787
  return atomsAdjust;
28666
28788
  }
28789
+ */
28667
28790
  }
28668
28791
 
28669
28792
  /**
@@ -28683,7 +28806,8 @@ class Strip {
28683
28806
  if (p0.length < 2) return;
28684
28807
  div = div || ic.axisDIV;
28685
28808
 
28686
- if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
28809
+ // if(pntsCA && ic.bDoublecolor && !ic.bCalphaOnly) {
28810
+ if(pntsCA && ic.bDoublecolor) {
28687
28811
  let bExtendLastRes = false; //true;
28688
28812
 
28689
28813
  let pnts_clrs = me.subdivideCls.subdivide(pntsCA, colors, div, bShowArray, bHighlight, prevone, nexttwo, bExtendLastRes);
@@ -29011,7 +29135,7 @@ class Tube {
29011
29135
  //Create tubes for "atoms" with certain "atomName". "radius" is the radius of the tubes.
29012
29136
  //"bHighlight" is an option to draw the highlight for these atoms. The highlight could be
29013
29137
  //outlines with bHighlight=1 and 3D objects with bHighlight=2.
29014
- createTube(atoms, atomName, radius, bHighlight, bCustom, bRadiusArray) { let ic = this.icn3d, me = ic.icn3dui;
29138
+ createTube(atoms, atomName, radius, bHighlight, bCustom, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29015
29139
  if(me.bNode) return;
29016
29140
 
29017
29141
  let pnts = [], colors = [], radii = [], prevone = [], nexttwo = [];
@@ -29123,6 +29247,9 @@ class Tube {
29123
29247
  else {
29124
29248
  radiusFinal = this.getRadius(radius, atom);
29125
29249
  }
29250
+
29251
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29252
+ if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29126
29253
 
29127
29254
  //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29128
29255
  radii.push(radiusFinal);
@@ -29196,183 +29323,247 @@ class Tube {
29196
29323
  let prevone = pnts_colors_radii_prevone_nexttwo[i].prevone;
29197
29324
  let nexttwo = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
29198
29325
 
29199
- this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bRadiusArray);
29326
+ this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
29200
29327
  }
29201
29328
 
29202
- pnts_colors_radii_prevone_nexttwo = [];
29329
+ pnts_colors_radii_prevone_nexttwo = [];
29203
29330
  }
29204
29331
 
29205
- getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
29206
- let pos = resid.lastIndexOf('_');
29207
- let resi = resid.substr(pos + 1);
29208
- let chainid = resid.substr(0, pos);
29332
+ /*
29333
+ createTube(atoms, atomName, radius, bHighlight, bCustom, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29334
+ if(me.bNode) return;
29209
29335
 
29210
- let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
29336
+ let pnts = [], colors = [], radii = [], prevone = [], nexttwo = [];
29337
+ let currentChain, currentResi;
29338
+ let index = 0;
29339
+ let maxDist = 6.0;
29340
+ let maxDist2 = 3.0; // avoid tube between the residues in 3 residue helix
29211
29341
 
29212
- return radiusFinal;
29213
- };
29342
+ let pnts_colors_radii_prevone_nexttwo = [];
29343
+ let firstAtom, atom, prevAtom;
29214
29344
 
29215
- // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29216
- createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bRadiusArray) { let ic = this.icn3d, me = ic.icn3dui;
29217
- if(me.bNode) return;
29345
+ for (let i in atoms) {
29346
+ atom = atoms[i];
29347
+ if ((atom.name === atomName) && !atom.het) {
29348
+ if(index == 0) {
29349
+ firstAtom = atom;
29350
+ }
29218
29351
 
29219
- if (_pnts.length < 2) return;
29352
+ 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
29353
+ || (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))
29354
+ ) ) {
29355
+ if(bHighlight !== 2) {
29356
+ if(!isNaN(firstAtom.resi) && !isNaN(prevAtom.resi)) {
29357
+ let prevoneResid = firstAtom.structure + '_' + firstAtom.chain + '_' + (parseInt(firstAtom.resi) - 1).toString();
29358
+ let prevoneCoord = ic.firstAtomObjCls.getAtomCoordFromResi(prevoneResid, atomName);
29359
+ prevone = (prevoneCoord !== undefined) ? [prevoneCoord] : [];
29220
29360
 
29221
- // if(bRadiusArray) {
29222
- let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
29223
- let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
29224
- //var geo = new THREE.Geometry();
29225
- let geo = new THREE.BufferGeometry();
29226
- let verticeArray = [], colorArray = [],indexArray = [], color;
29227
- let offset = 0, offset2 = 0, offset3 = 0;
29361
+ let nextoneResid = prevAtom.structure + '_' + prevAtom.chain + '_' + (parseInt(prevAtom.resi) + 1).toString();
29228
29362
 
29229
- let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
29363
+ // add one more residue if only one residue is available
29364
+ if(pnts.length == 1 && ic.residues.hasOwnProperty(nextoneResid)) {
29365
+ let nextAtom = ic.firstAtomObjCls.getAtomFromResi(nextoneResid, atomName);
29230
29366
 
29231
- let pnts = pnts_clrs[0];
29232
- colors = pnts_clrs[2];
29367
+ if(nextAtom) {
29368
+ pnts.push(nextAtom.coord);
29369
+ colors.push(nextAtom.color);
29233
29370
 
29234
- let prevAxis1 = new THREE.Vector3(), prevAxis2;
29235
- for (let i = 0, lim = pnts.length; i < lim; ++i) {
29236
- let r, idx = (i - 1) * axisDivInv;
29237
- if (i === 0) r = radii[0];
29238
- else {
29239
- if (idx % 1 === 0) r = radii[idx];
29240
- else {
29241
- let floored = Math.floor(idx);
29242
- let tmp = idx - floored;
29243
- r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
29371
+ let radiusFinal = this.getRadius(radius, atom);
29372
+ radii.push(radiusFinal);
29373
+ }
29374
+ }
29375
+
29376
+ }
29377
+
29378
+ pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
29244
29379
  }
29380
+ pnts = []; colors = []; radii = []; prevone = []; nexttwo = [];
29381
+ firstAtom = atom;
29382
+ index = 0;
29245
29383
  }
29246
- let delta, axis1, axis2;
29247
- if (i < lim - 1) {
29248
- delta = pnts[i].clone().sub(pnts[i + 1]);
29249
- axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
29250
- axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
29251
- // let dir = 1, offset = 0;
29252
- if (prevAxis1.dot(axis1) < 0) {
29253
- axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
29254
- }
29255
- prevAxis1 = axis1; prevAxis2 = axis2;
29256
- } else {
29257
- axis1 = prevAxis1; axis2 = prevAxis2;
29384
+
29385
+ pnts.push(atom.coord);
29386
+
29387
+ let radiusFinal;
29388
+ if(bCustom) {
29389
+ radiusFinal = this.getCustomtubesize(atom.structure + '_' + atom.chain + '_' + atom.resi);
29390
+ }
29391
+ else {
29392
+ radiusFinal = this.getRadius(radius, atom);
29258
29393
  }
29259
- for (let j = 0; j < circleDiv; ++j) {
29260
- let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
29261
- let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
29262
- verticeArray[offset++] = point.x;
29263
- verticeArray[offset++] = point.y;
29264
- verticeArray[offset++] = point.z;
29265
29394
 
29266
- color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
29267
- colorArray[offset2++] = color.r;
29268
- colorArray[offset2++] = color.g;
29269
- colorArray[offset2++] = color.b;
29395
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29396
+ if(!bNonCoil && atom.ss != 'coil' && !atom.ssbegin && !atom.ssend ) radiusFinal = 0;
29397
+
29398
+ //radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : ic.coilWidth));
29399
+ radii.push(radiusFinal);
29400
+
29401
+ colors.push(atom.color);
29402
+ // the starting residue of a coil uses the color from the next residue to avoid using the color of the last helix/sheet residue
29403
+ if(index === 1) colors[colors.length - 2] = atom.color;
29404
+
29405
+ currentChain = atom.chain;
29406
+ currentResi = atom.resi;
29407
+
29408
+ let scale = 1.2;
29409
+ if(bHighlight === 2 && !atom.ssbegin) {
29410
+ ic.boxCls.createBox(atom, undefined, undefined, scale, undefined, bHighlight);
29270
29411
  }
29412
+
29413
+ ++index;
29414
+
29415
+ prevAtom = atom;
29271
29416
  }
29272
- let offsetTmp = 0, nComp = 3;
29273
- for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
29274
- let reg = 0;
29275
- //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
29276
- //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
29277
- let pos = offsetTmp * nComp;
29278
- let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29279
- pos = (offsetTmp + circleDiv) * nComp;
29280
- let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29281
- pos = (offsetTmp + circleDiv + 1) * nComp;
29282
- let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29283
-
29284
- let r1 = point1.clone().sub(point2).lengthSq();
29285
- let r2 = point1.clone().sub(point3).lengthSq();
29286
- if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
29287
- //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
29288
- //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
29289
- //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
29290
- indexArray[offset3++] = offsetTmp + j;
29291
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29292
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29293
-
29294
- //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
29295
- indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29296
- indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29297
- indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
29298
- }
29299
- offsetTmp += circleDiv;
29300
- }
29417
+ }
29301
29418
 
29302
- geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
29303
- geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
29419
+ if(bHighlight !== 2) {
29420
+ pnts_colors_radii_prevone_nexttwo.push({'pnts':pnts, 'colors':colors, 'radii':radii, 'prevone':prevone, 'nexttwo':nexttwo});
29421
+ }
29304
29422
 
29305
- geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
29306
- //geo.setIndex(indexArray);
29423
+ for(let i = 0, il = pnts_colors_radii_prevone_nexttwo.length; i < il; ++i) {
29424
+ let pnts = pnts_colors_radii_prevone_nexttwo[i].pnts;
29425
+ let colors = pnts_colors_radii_prevone_nexttwo[i].colors;
29426
+ let radii = pnts_colors_radii_prevone_nexttwo[i].radii;
29427
+ let prevone = []; // = pnts_colors_radii_prevone_nexttwo[i].prevone;
29428
+ let nexttwo = []; // = pnts_colors_radii_prevone_nexttwo[i].nexttwo;
29307
29429
 
29308
- //geo.computeFaceNormals();
29309
- //geo.computeVertexNormals(false);
29310
- geo.computeVertexNormals();
29311
- /*
29430
+ this.createTubeSub(pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil);
29312
29431
  }
29313
- else {
29314
- let axisDiv = ic.axisDIV;
29315
29432
 
29316
- let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
29317
- // extend one residue
29318
- //var pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo, true);
29433
+ pnts_colors_radii_prevone_nexttwo = [];
29434
+ }
29435
+ */
29319
29436
 
29320
- _pnts = pnts_clrs[0];
29321
- colors = pnts_clrs[2];
29437
+ getCustomtubesize(resid) { let ic = this.icn3d; ic.icn3dui;
29438
+ let pos = resid.lastIndexOf('_');
29439
+ let resi = resid.substr(pos + 1);
29440
+ let chainid = resid.substr(0, pos);
29322
29441
 
29323
- let radius = ic.coilWidth;
29324
- segments = _pnts.length;
29325
- //radiusSegments = 8;
29326
- radiusSegments = 4; // save memory
29327
- let closed = false;
29442
+ let radiusFinal = (ic.queryresi2score[chainid] && ic.queryresi2score[chainid].hasOwnProperty(resi)) ? ic.queryresi2score[chainid][resi] * 0.01 : ic.coilWidth;
29328
29443
 
29329
- // when using radiusArray with modified three.js, the tube didn't work in picking
29330
- let geo = new THREE.TubeGeometry(
29331
- new THREE.CatmullRomCurve3(_pnts), // path
29332
- segments,
29333
- radius, //radiusArray, //radius,
29334
- radiusSegments,
29335
- closed
29336
- );
29444
+ return radiusFinal;
29445
+ };
29446
+
29447
+ // modified from iview (http://istar.cse.cuhk.edu.hk/iview/)
29448
+ createTubeSub(_pnts, colors, radii, bHighlight, prevone, nexttwo, bNonCoil) { let ic = this.icn3d, me = ic.icn3dui;
29449
+ if(me.bNode) return;
29450
+
29451
+ if (_pnts.length < 2) return;
29337
29452
 
29338
- //https://stemkoski.github.io/Three.js/Graphulus-Curve.html
29339
- let color, face, numberOfSides, vertexIndex;
29340
- // faces are indexed using characters
29341
- let faceIndices = [ 'a', 'b', 'c', 'd' ];
29453
+ let circleDiv = ic.tubeDIV, axisDiv = ic.axisDIV;
29454
+ let circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv;
29455
+ //var geo = new THREE.Geometry();
29456
+ let geo = new THREE.BufferGeometry();
29457
+ let verticeArray = [], colorArray = [],indexArray = [], color;
29458
+ let offset = 0, offset2 = 0, offset3 = 0;
29342
29459
 
29343
- // first, assign colors to vertices as desired
29344
- let prevColor, geoColors = {};
29345
- for ( let s = 0; s <= segments; s++ ) {
29346
- for ( let r = 0; r < radiusSegments; r++ )
29347
- {
29348
- vertexIndex = r + s * radiusSegments;
29349
- color = colors[s];
29350
- if(!color) color = prevColor;
29460
+ let pnts_clrs = me.subdivideCls.subdivide(_pnts, colors, axisDiv, undefined, undefined, prevone, nexttwo);
29351
29461
 
29352
- geoColors[vertexIndex] = color; // use this array for convenience
29462
+ let pnts = pnts_clrs[0];
29463
+ colors = pnts_clrs[2];
29353
29464
 
29354
- prevColor = color;
29355
- }
29356
- }
29465
+ let constRadiius;
29466
+ // a threshold to stop drawing the tube if it's less than this ratio of radius
29467
+ let thresholdRatio = 1; //0.9;
29357
29468
 
29358
- // copy the colors as necessary to the face's vertexColors array.
29359
- // after version r125, geo.faces is undefined for TubeGeometry
29360
- for ( let i = 0; geo.faces && i < geo.faces.length; i++ )
29361
- {
29362
- face = geo.faces[ i ];
29469
+ let prevAxis1 = new THREE.Vector3(), prevAxis2;
29470
+ for (let i = 0, lim = pnts.length; i < lim; ++i) {
29471
+ let r, idx = (i - 1) * axisDivInv;
29363
29472
 
29364
- numberOfSides = ( face instanceof THREE.Face3 ) ? 3 : 4;
29365
- for( let j = 0; j < numberOfSides; j++ )
29366
- {
29367
- vertexIndex = face[ faceIndices[ j ] ];
29368
- face.vertexColors[ j ] = geoColors[ vertexIndex ];
29473
+ if (i === 0) {
29474
+ r = radii[0];
29475
+ if(r > 0) constRadiius = r;
29476
+ }
29477
+ else {
29478
+ if (idx % 1 === 0) {
29479
+ r = radii[idx];
29480
+ if(r > 0) constRadiius = r;
29481
+ }
29482
+ else {
29483
+ let floored = Math.floor(idx);
29484
+ let tmp = idx - floored;
29485
+ // draw all atoms in tubes and assign zero radius when the residue is not coil
29486
+ // r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
29487
+ r = radii[floored] * (1 - tmp) + radii[floored + 1] * tmp;
29488
+
29489
+ // a threshold to stop drawing the tube if it's less than this ratio of radius.
29490
+ // The extra bit of tube connects coil with strands or helices
29491
+ if(!bNonCoil) {
29492
+ if(r < thresholdRatio * constRadiius) {
29493
+ r = 0;
29494
+ }
29495
+ // else if(r < constRadiius) {
29496
+ // r *= 0.5; // use small radius for the connection between coild and sheets/helices
29497
+ // }
29498
+ }
29369
29499
  }
29370
29500
  }
29371
-
29372
- geo.computeFaceNormals();
29373
- geo.computeVertexNormals(false);
29501
+ let delta, axis1, axis2;
29502
+ if (i < lim - 1) {
29503
+ delta = pnts[i].clone().sub(pnts[i + 1]);
29504
+ axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r);
29505
+ axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r);
29506
+ // let dir = 1, offset = 0;
29507
+ if (prevAxis1.dot(axis1) < 0) {
29508
+ axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
29509
+ }
29510
+ prevAxis1 = axis1; prevAxis2 = axis2;
29511
+ } else {
29512
+ axis1 = prevAxis1; axis2 = prevAxis2;
29513
+ }
29514
+ for (let j = 0; j < circleDiv; ++j) {
29515
+ let angle = 2 * Math.PI * circleDivInv * j; //* dir + offset;
29516
+ let point = pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)));
29517
+ verticeArray[offset++] = point.x;
29518
+ verticeArray[offset++] = point.y;
29519
+ verticeArray[offset++] = point.z;
29520
+
29521
+ color = (i == colors.length - 1 && colors.length > 1) ? me.parasCls.thr(colors[colors.length - 2]) : me.parasCls.thr(colors[i]);
29522
+ colorArray[offset2++] = color.r;
29523
+ colorArray[offset2++] = color.g;
29524
+ colorArray[offset2++] = color.b;
29525
+ }
29526
+ }
29527
+ let offsetTmp = 0, nComp = 3;
29528
+ for (let i = 0, lim = pnts.length - 1; i < lim; ++i) {
29529
+ let reg = 0;
29530
+ //var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq();
29531
+ //var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq();
29532
+ let pos = offsetTmp * nComp;
29533
+ let point1 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29534
+ pos = (offsetTmp + circleDiv) * nComp;
29535
+ let point2 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29536
+ pos = (offsetTmp + circleDiv + 1) * nComp;
29537
+ let point3 = new THREE.Vector3(verticeArray[pos], verticeArray[pos + 1], verticeArray[pos + 2]);
29538
+
29539
+ let r1 = point1.clone().sub(point2).lengthSq();
29540
+ let r2 = point1.clone().sub(point3).lengthSq();
29541
+ if (r1 > r2) { r1 = r2; reg = 1; } for (let j = 0; j < circleDiv; ++j) {
29542
+ //geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c));
29543
+ //geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c));
29544
+ //indexArray = indexArray.concat([offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv]);
29545
+ indexArray[offset3++] = offsetTmp + j;
29546
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29547
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29548
+
29549
+ //indexArray = indexArray.concat([offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv]);
29550
+ indexArray[offset3++] = offsetTmp + (j + 1) % circleDiv;
29551
+ indexArray[offset3++] = offsetTmp + (j + reg) % circleDiv + circleDiv;
29552
+ indexArray[offset3++] = offsetTmp + (j + reg + 1) % circleDiv + circleDiv;
29553
+ }
29554
+ offsetTmp += circleDiv;
29374
29555
  }
29375
- */
29556
+
29557
+ geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticeArray), nComp));
29558
+ geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), nComp));
29559
+
29560
+ geo.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1));
29561
+ //geo.setIndex(indexArray);
29562
+
29563
+ //geo.computeFaceNormals();
29564
+ //geo.computeVertexNormals(false);
29565
+ geo.computeVertexNormals();
29566
+
29376
29567
  let mesh;
29377
29568
  if(bHighlight === 2) {
29378
29569
  //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 }));
@@ -29955,7 +30146,7 @@ class Axes {
29955
30146
  return cone;
29956
30147
  }
29957
30148
 
29958
- setPc1Axes() { let ic = this.icn3d, me = ic.icn3dui;
30149
+ setPc1Axes(bXAxis) { let ic = this.icn3d, me = ic.icn3dui;
29959
30150
  if(me.bNode) return;
29960
30151
 
29961
30152
  let atomHash = me.hashUtilsCls.intHash(ic.hAtoms, ic.dAtoms);
@@ -30036,6 +30227,11 @@ class Axes {
30036
30227
 
30037
30228
  let positionX = center.clone().add(vecX.normalize().multiplyScalar(maxD * 0.4));
30038
30229
 
30230
+ let prinXaxis = vecX.normalize();
30231
+ me.htmlCls.clickMenuCls.setLogCmd('Principle X-Axis: ' + prinXaxis.x.toFixed(3) + " " + prinXaxis.y.toFixed(3) + " " + prinXaxis.z.toFixed(3), false);
30232
+
30233
+ if(bXAxis) return prinXaxis;
30234
+
30039
30235
  let vecY = new THREE.Vector3(eigenRet.h2[0], eigenRet.h2[1], eigenRet.h2[2]);
30040
30236
  let positionY = center.clone().add(vecY.normalize().multiplyScalar(maxD * 0.3));
30041
30237
 
@@ -34254,6 +34450,9 @@ class ApplySymd {
34254
34450
 
34255
34451
  ic.cylinderCls.createCylinder(start, end, axisRadius, colorAxis, 0);
34256
34452
 
34453
+ let SymAxis = end.clone().sub(start).normalize();
34454
+ me.htmlCls.clickMenuCls.setLogCmd('Symmetry Axis: ' + SymAxis.x.toFixed(3) + " " + SymAxis.y.toFixed(3) + " " + SymAxis.z.toFixed(3), false);
34455
+
34257
34456
  if(ic.bAxisOnly) continue;
34258
34457
 
34259
34458
  if(symmetryType == 'C' || (symmetryType == 'D' && order == nSide) ) {
@@ -34410,87 +34609,87 @@ class ApplySymd {
34410
34609
  pointArray.push(end);
34411
34610
  }
34412
34611
  else ;
34413
- }
34414
34612
 
34415
- if(symmetryType == 'T') {
34416
- let pos1 = pointArray[0]; // pointArray: start, end, start, end, ...
34417
- ic.sphereCls.createSphereBase(pos1, colorPolygon, polygonRadius, 1.0, 0);
34613
+ if(symmetryType == 'T') {
34614
+ let pos1 = pointArray[0]; // pointArray: start, end, start, end, ...
34615
+ ic.sphereCls.createSphereBase(pos1, colorPolygon, polygonRadius, 1.0, 0);
34418
34616
 
34419
- let dist2 = pos1.distanceTo(pointArray[2]);
34420
- let dist3 = pos1.distanceTo(pointArray[3]);
34617
+ let dist2 = pos1.distanceTo(pointArray[2]);
34618
+ let dist3 = pos1.distanceTo(pointArray[3]);
34421
34619
 
34422
- let distSmall, posSel;
34423
- if(dist2 < dist3) {
34424
- distSmall = dist2;
34425
- posSel = pointArray[3];
34426
- }
34427
- else {
34428
- distSmall = dist3;
34429
- posSel = pointArray[2];
34430
- }
34620
+ let distSmall, posSel;
34621
+ if(dist2 < dist3) {
34622
+ distSmall = dist2;
34623
+ posSel = pointArray[3];
34624
+ }
34625
+ else {
34626
+ distSmall = dist3;
34627
+ posSel = pointArray[2];
34628
+ }
34431
34629
 
34432
- ic.sphereCls.createSphereBase(posSel, colorPolygon, polygonRadius, 1.0, 0);
34433
- ic.cylinderCls.createCylinder(pos1, posSel, polygonRadius, colorPolygon, 0);
34630
+ ic.sphereCls.createSphereBase(posSel, colorPolygon, polygonRadius, 1.0, 0);
34631
+ ic.cylinderCls.createCylinder(pos1, posSel, polygonRadius, colorPolygon, 0);
34434
34632
 
34435
- let iPrev;
34436
- for(let i = 4, il = pointArray.length; i < il; ++i) {
34437
- let pos2 = pointArray[i];
34633
+ let iPrev;
34634
+ for(let i = 4, il = pointArray.length; i < il; ++i) {
34635
+ let pos2 = pointArray[i];
34438
34636
 
34439
- let dist = pos1.distanceTo(pos2);
34440
- if(dist > distSmall) {
34441
- ic.sphereCls.createSphereBase(pos2, colorPolygon, polygonRadius, 1.0, 0);
34442
- ic.cylinderCls.createCylinder(pos1, pos2, polygonRadius, colorPolygon, 0);
34637
+ let dist = pos1.distanceTo(pos2);
34638
+ if(dist > distSmall) {
34639
+ ic.sphereCls.createSphereBase(pos2, colorPolygon, polygonRadius, 1.0, 0);
34640
+ ic.cylinderCls.createCylinder(pos1, pos2, polygonRadius, colorPolygon, 0);
34443
34641
 
34444
- ic.cylinderCls.createCylinder(posSel, pos2, polygonRadius, colorPolygon, 0);
34445
- if(iPrev !== undefined) {
34446
- ic.cylinderCls.createCylinder(pointArray[iPrev], pos2, polygonRadius, colorPolygon, 0);
34447
- }
34642
+ ic.cylinderCls.createCylinder(posSel, pos2, polygonRadius, colorPolygon, 0);
34643
+ if(iPrev !== undefined) {
34644
+ ic.cylinderCls.createCylinder(pointArray[iPrev], pos2, polygonRadius, colorPolygon, 0);
34645
+ }
34448
34646
 
34449
- iPrev = i;
34647
+ iPrev = i;
34648
+ }
34450
34649
  }
34451
34650
  }
34452
- }
34453
- else if(symmetryType == 'O') {
34454
- for(let i = 0, il = pointArray.length; i < il; i += 2) {
34455
- let pos1 = pointArray[i];
34456
- let pos2 = pointArray[i+1];
34457
- ic.sphereCls.createSphereBase(pos1, colorPolygon, polygonRadius, 1.0, 0);
34458
- ic.sphereCls.createSphereBase(pos2, colorPolygon, polygonRadius, 1.0, 0);
34459
- for(let j = i + 2, jl = pointArray.length; j < jl; ++j) {
34460
- let pos3 = pointArray[j];
34461
- ic.sphereCls.createSphereBase(pos3, colorPolygon, polygonRadius, 1.0, 0);
34462
- ic.cylinderCls.createCylinder(pos1, pos3, polygonRadius, colorPolygon, 0);
34463
- ic.cylinderCls.createCylinder(pos2, pos3, polygonRadius, colorPolygon, 0);
34651
+ else if(symmetryType == 'O') {
34652
+ for(let i = 0, il = pointArray.length; i < il; i += 2) {
34653
+ let pos1 = pointArray[i];
34654
+ let pos2 = pointArray[i+1];
34655
+ ic.sphereCls.createSphereBase(pos1, colorPolygon, polygonRadius, 1.0, 0);
34656
+ ic.sphereCls.createSphereBase(pos2, colorPolygon, polygonRadius, 1.0, 0);
34657
+ for(let j = i + 2, jl = pointArray.length; j < jl; ++j) {
34658
+ let pos3 = pointArray[j];
34659
+ ic.sphereCls.createSphereBase(pos3, colorPolygon, polygonRadius, 1.0, 0);
34660
+ ic.cylinderCls.createCylinder(pos1, pos3, polygonRadius, colorPolygon, 0);
34661
+ ic.cylinderCls.createCylinder(pos2, pos3, polygonRadius, colorPolygon, 0);
34662
+ }
34464
34663
  }
34465
34664
  }
34466
- }
34467
- else if(symmetryType == 'I') {
34468
- for(let i = 0, il = pointArray.length; i < il; i += 2) {
34469
- let pos1 = pointArray[i];
34470
- let pos2 = pointArray[i+1];
34471
- ic.sphereCls.createSphereBase(pos1, colorPolygon, polygonRadius, 1.0, 0);
34472
- ic.sphereCls.createSphereBase(pos2, colorPolygon, polygonRadius, 1.0, 0);
34473
- for(let j = i + 2, jl = pointArray.length; j < jl; j += 2) {
34474
- let pos3 = pointArray[j];
34475
- let pos4 = pointArray[j+1];
34665
+ else if(symmetryType == 'I') {
34666
+ for(let i = 0, il = pointArray.length; i < il; i += 2) {
34667
+ let pos1 = pointArray[i];
34668
+ let pos2 = pointArray[i+1];
34669
+ ic.sphereCls.createSphereBase(pos1, colorPolygon, polygonRadius, 1.0, 0);
34670
+ ic.sphereCls.createSphereBase(pos2, colorPolygon, polygonRadius, 1.0, 0);
34671
+ for(let j = i + 2, jl = pointArray.length; j < jl; j += 2) {
34672
+ let pos3 = pointArray[j];
34673
+ let pos4 = pointArray[j+1];
34476
34674
 
34477
- let dist3 = pos1.distanceTo(pos3);
34478
- let dist4 = pos1.distanceTo(pos4);
34675
+ let dist3 = pos1.distanceTo(pos3);
34676
+ let dist4 = pos1.distanceTo(pos4);
34479
34677
 
34480
- let pos1Sel, pos2Sel;
34481
- if(dist3 < dist4) {
34482
- pos1Sel = pos3;
34483
- pos2Sel = pos4;
34484
- }
34485
- else {
34486
- pos1Sel = pos4;
34487
- pos2Sel = pos3;
34488
- }
34678
+ let pos1Sel, pos2Sel;
34679
+ if(dist3 < dist4) {
34680
+ pos1Sel = pos3;
34681
+ pos2Sel = pos4;
34682
+ }
34683
+ else {
34684
+ pos1Sel = pos4;
34685
+ pos2Sel = pos3;
34686
+ }
34489
34687
 
34490
- ic.sphereCls.createSphereBase(pos1Sel, colorPolygon, polygonRadius, 1.0, 0);
34491
- ic.sphereCls.createSphereBase(pos2Sel, colorPolygon, polygonRadius, 1.0, 0);
34492
- ic.cylinderCls.createCylinder(pos1, pos1Sel, polygonRadius, colorPolygon, 0);
34493
- ic.cylinderCls.createCylinder(pos2, pos2Sel, polygonRadius, colorPolygon, 0);
34688
+ ic.sphereCls.createSphereBase(pos1Sel, colorPolygon, polygonRadius, 1.0, 0);
34689
+ ic.sphereCls.createSphereBase(pos2Sel, colorPolygon, polygonRadius, 1.0, 0);
34690
+ ic.cylinderCls.createCylinder(pos1, pos1Sel, polygonRadius, colorPolygon, 0);
34691
+ ic.cylinderCls.createCylinder(pos2, pos2Sel, polygonRadius, colorPolygon, 0);
34692
+ }
34494
34693
  }
34495
34694
  }
34496
34695
  }
@@ -34854,6 +35053,33 @@ class ResidueLabels {
34854
35053
  ic.hlObjectsCls.removeHlObjects();
34855
35054
  }
34856
35055
 
35056
+ //Add labels for each Ig domain
35057
+ addIgLabels(atoms) { let ic = this.icn3d, me = ic.icn3dui;
35058
+ if(me.bNode) return;
35059
+
35060
+ let size = 60; //18;
35061
+
35062
+ ic.labels['ig'] = [];
35063
+ let chainidHash = ic.firstAtomObjCls.getChainsFromAtoms(atoms);
35064
+
35065
+ for(let chainid in ic.igLabel2Pos) {
35066
+ if(!chainidHash.hasOwnProperty(chainid)) continue;
35067
+
35068
+ for(let text in ic.igLabel2Pos[chainid]) {
35069
+ let label = {}; // Each label contains 'position', 'text', 'color', 'background'
35070
+ label.position = ic.igLabel2Pos[chainid][text];
35071
+ label.text = text;
35072
+
35073
+ label.size = size;
35074
+ label.color = '#00FFFF';
35075
+
35076
+ ic.labels['ig'].push(label);
35077
+ }
35078
+ }
35079
+
35080
+ ic.hlObjectsCls.removeHlObjects();
35081
+ }
35082
+
34857
35083
  addNonCarbonAtomLabels(atoms) { let ic = this.icn3d, me = ic.icn3dui;
34858
35084
  if(me.bNode) return;
34859
35085
 
@@ -41124,6 +41350,8 @@ class AnnoIg {
41124
41350
  let igCnt = ic.chain2igArray[chnid].length;
41125
41351
  let fromArray = [], toArray = [];
41126
41352
  let posindex2domainindex = {};
41353
+ if(!ic.igLabel2Pos) ic.igLabel2Pos = {};
41354
+ ic.igLabel2Pos[chnid] = {};
41127
41355
  for(let i = 0; i < igCnt; ++i) {
41128
41356
  let igElem = ic.chain2igArray[chnid][i];
41129
41357
  fromArray = fromArray.concat(igElem.startPosArray);
@@ -41133,6 +41361,18 @@ class AnnoIg {
41133
41361
  let pos = igElem.startPosArray[j];
41134
41362
  posindex2domainindex[pos] = i;
41135
41363
  }
41364
+
41365
+ let resi1 = ic.ParserUtilsCls.getResi(chnid, igElem.startPosArray[0]);
41366
+ let resid1 = chnid + "_" + resi1;
41367
+ let calpha1 = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid1]);
41368
+
41369
+ let resi2 = ic.ParserUtilsCls.getResi(chnid, igElem.endPosArray[igElem.endPosArray.length - 1]);
41370
+ let resid2 = chnid + "_" + resi2;
41371
+ let calpha2 = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid2]);
41372
+
41373
+ let label = chnid.substr(chnid.lastIndexOf('_') + 1) + '-Ig' + (i+1).toString();
41374
+
41375
+ ic.igLabel2Pos[chnid][label] = calpha1.coord.clone().add(calpha2.coord).multiplyScalar(0.5);
41136
41376
  }
41137
41377
 
41138
41378
  // let htmlCnt = '<span class="icn3d-residueNum" title="Ig domain count">' + igCnt.toString() + ' Igs</span>';
@@ -41183,6 +41423,7 @@ class AnnoIg {
41183
41423
  if(igArray.length == 0) return {html: html, html2: html2, html3: html3}
41184
41424
  let rangeArray = [], titleArray = [], fullTitleArray = [], domainArray = [];
41185
41425
 
41426
+ let chain = chnid.substr(chnid.lastIndexOf('_') + 1);
41186
41427
  for(let i = 0, il = igArray.length; i < il; ++i) {
41187
41428
  let domainid = igArray[i].domainid;
41188
41429
  if(!ic.domainid2info) continue;
@@ -41194,7 +41435,7 @@ class AnnoIg {
41194
41435
 
41195
41436
  let igType = (parseFloat(tmscore) < ic.refnumCls.TMThresholdIgType ) ? 'Ig' : ic.ref2igtype[info.refpdbname];
41196
41437
  titleArray.push(igType + ' (TM:' + parseFloat(tmscore).toFixed(2) + ')');
41197
- fullTitleArray.push(igType + ' (TM:' + parseFloat(tmscore).toFixed(2) + '), template: ' + info.refpdbname + ', type: ' + ic.ref2igtype[info.refpdbname] + ', Seq. identity: ' + parseFloat(info.seqid).toFixed(2) + ', aligned residues: ' + info.nresAlign);
41438
+ fullTitleArray.push(igType + ' (TM:' + parseFloat(tmscore).toFixed(2) + '), template: ' + info.refpdbname + ', type: ' + ic.ref2igtype[info.refpdbname] + ', Seq. identity: ' + parseFloat(info.seqid).toFixed(2) + ', aligned residues: ' + info.nresAlign + ', label in 3D: ' + chain + '-Ig' + (i+1).toString());
41198
41439
 
41199
41440
  domainArray.push(igType);
41200
41441
 
@@ -44626,7 +44867,6 @@ class AddTrack {
44626
44867
  }
44627
44868
 
44628
44869
  getExonHtml(exonIndex, colorGradient, from, to, genomeRange, chainid, simpTitle) { let ic = this.icn3d; ic.icn3dui;
44629
- // return '<div style="display:inline-block; color:white!important; width:' + Math.round(ic.seqAnnWidth *(to - from + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" domain="' + (exonIndex + 1) + '" from="' + from + '" to="' + to + '" setname="' + simpTitle + ', Exon ' + (exonIndex + 1) + '" title="Exon ' + (exonIndex + 1) + ': ' + genomeRange + ' genomic interval" anno="sequence" chain="' + chainid + '"><div style="height: 12px; border: 1px solid #000; background: linear-gradient(to right, ' + colorGradient + ');"></div></div>';
44630
44870
  return '<div style="display:inline-block; color:white!important; width:' + Math.round(ic.seqAnnWidth *(to - from + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" domain="' + (exonIndex + 1) + '" from="' + from + '" to="' + to + '" setname="' + simpTitle + ', ' + (exonIndex + 1) + '" title="Exon: ' + genomeRange + ' genomic interval" anno="sequence" chain="' + chainid + '"><div style="height: 12px; border: 1px solid #000; background: linear-gradient(to right, ' + colorGradient + ');"></div></div>';
44631
44871
  }
44632
44872
 
@@ -45368,7 +45608,7 @@ class AddTrack {
45368
45608
  let targetId = 'genomeRes';
45369
45609
 
45370
45610
  let acc2index = {};
45371
-
45611
+
45372
45612
  for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
45373
45613
  let acc = accArray[index];
45374
45614
 
@@ -45453,22 +45693,24 @@ class AddTrack {
45453
45693
  trackSeqArrayFinal[i] = '';
45454
45694
  }
45455
45695
 
45456
- for(let j = 0, jl = trackSeqArray[maxIndex].length; j < jl; ++j) {
45457
- let seq = trackSeqArray[maxIndex][j];
45696
+ if(trackSeqArray[maxIndex]) {
45697
+ for(let j = 0, jl = trackSeqArray[maxIndex].length; j < jl; ++j) {
45698
+ let seq = trackSeqArray[maxIndex][j];
45458
45699
 
45459
- let bExon = (seq != '-') ? true : false;
45460
- if(!bExon) {
45461
- for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
45462
- if(trackSeqArray[i][j] != '-') {
45463
- bExon = true;
45464
- break;
45700
+ let bExon = (seq != '-') ? true : false;
45701
+ if(!bExon) {
45702
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
45703
+ if(trackSeqArray[i][j] != '-') {
45704
+ bExon = true;
45705
+ break;
45706
+ }
45465
45707
  }
45466
45708
  }
45467
- }
45468
-
45469
- if(bExon) {
45470
- for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
45471
- trackSeqArrayFinal[i] += trackSeqArray[i][j];
45709
+
45710
+ if(bExon) {
45711
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
45712
+ trackSeqArrayFinal[i] += trackSeqArray[i][j];
45713
+ }
45472
45714
  }
45473
45715
  }
45474
45716
  }
@@ -45539,7 +45781,9 @@ class AddTrack {
45539
45781
  ic.maxAnnoLength = ic.maxAnnoLengthOri + ic.seqStartLen[chainid] + ic.seqEndLen[chainid];
45540
45782
  }
45541
45783
 
45542
- await ic.annotationCls.resetAnnoAll();
45784
+ // do not remove other tracks
45785
+ // await ic.annotationCls.resetAnnoAll();
45786
+ await ic.showAnnoCls.processSeqData(ic.chainid_seq);
45543
45787
 
45544
45788
  let targetGapHashStr = '';
45545
45789
  let cntTmp = 0;
@@ -45706,7 +45950,7 @@ class AddTrack {
45706
45950
 
45707
45951
  let type = 'identity';
45708
45952
 
45709
- await thisClass.addExonTracks(chainid, geneid, startpos, type);
45953
+ await this.addExonTracks(chainid, geneid, startpos, type);
45710
45954
  }
45711
45955
 
45712
45956
  async addExonTracks(chainid, geneid, startpos, type) { let ic = this.icn3d, me = ic.icn3dui;
@@ -45741,13 +45985,12 @@ class AddTrack {
45741
45985
  let cnt = (j == jl - 1) ? itemArray[2] - 3 : itemArray[2]; // The last one is stop codeon
45742
45986
  cntTotal += cnt;
45743
45987
 
45744
- let resStart = parseInt(prevCntTotal/3.0 + 0.5); // 0-based
45745
- let resEnd = parseInt(cntTotal/3.0 + 0.5) - 1; // 0-based
45988
+ let resStart = parseInt((prevCntTotal+2)/3.0); // 0-based
45989
+ let resEnd = parseInt((cntTotal+2)/3.0) - 1; // 0-based
45746
45990
 
45747
- let genResStart = parseInt(itemArray[0] / 3.0 + 0.5);
45748
-
45749
- //let genResEnd = parseInt(itemArray[1] / 3.0 + 0.5); // some difference due to round
45750
- let genResEnd = genResStart + ic.exonOrder * (resEnd - resStart);
45991
+ let genResEnd = parseInt((itemArray[1]+2) / 3.0);
45992
+ // let genResStart = parseInt((itemArray[0]+2) / 3.0); // some difference due to round
45993
+ let genResStart = genResEnd - ic.exonOrder * (resEnd - resStart);
45751
45994
 
45752
45995
  rangeArray.push({genomeRange: genomeRange, genResStart: genResStart, genResEnd: genResEnd, resStart: resStart, resEnd: resEnd});
45753
45996
 
@@ -45801,7 +46044,7 @@ class AddTrack {
45801
46044
 
45802
46045
  let ALen = trackSeqArray.length;
45803
46046
 
45804
- while (i < A.length && j < B.length) {
46047
+ while (A && B && i < A.length && j < B.length) {
45805
46048
  if(A[i] != B[j]) {
45806
46049
  if(A[i] == '-') {
45807
46050
  // insert "-" in B
@@ -48464,7 +48707,7 @@ class HlUpdate {
48464
48707
  }
48465
48708
 
48466
48709
  //Remove the highlight in the 2D interaction diagram.
48467
- removeHl2D() { let ic = this.icn3d; ic.icn3dui;
48710
+ removeHl2D(bRemoveChainOnly) { let ic = this.icn3d; ic.icn3dui;
48468
48711
  // clear nodes in 2d dgm
48469
48712
  $("#" + ic.pre + "dl_2ddgm rect").attr('stroke', '#000000');
48470
48713
  $("#" + ic.pre + "dl_2ddgm circle").attr('stroke', '#000000');
@@ -48478,6 +48721,22 @@ class HlUpdate {
48478
48721
  $("#" + ic.pre + "dl_2ddgm svg line").attr('stroke', '#000000');
48479
48722
  $("#" + ic.pre + "dl_2ddgm line").attr('stroke-width', 1);
48480
48723
  }
48724
+
48725
+ if(!bRemoveChainOnly) {
48726
+ // clear nodes in 2d interaction network
48727
+ // $("#" + ic.pre + "dl_linegraph rect").attr('stroke', '#000000');
48728
+ $("#" + ic.pre + "dl_linegraph circle").attr('stroke', '#000000');
48729
+
48730
+ // $("#" + ic.pre + "dl_linegraph rect").attr('stroke-width', 1);
48731
+ $("#" + ic.pre + "dl_linegraph circle").attr('stroke-width', 1);
48732
+
48733
+ // clear nodes in 2d interaction graph
48734
+ $("#" + ic.pre + "dl_scatterplot rect").attr('stroke', '#000000');
48735
+ $("#" + ic.pre + "dl_scatterplot circle").attr('stroke', '#000000');
48736
+
48737
+ $("#" + ic.pre + "dl_scatterplot rect").attr('stroke-width', 1);
48738
+ $("#" + ic.pre + "dl_scatterplot circle").attr('stroke-width', 1);
48739
+ }
48481
48740
  }
48482
48741
 
48483
48742
  //Remove the selection in the menu of defined sets.
@@ -48575,7 +48834,7 @@ class HlUpdate {
48575
48834
  // update highlight in 2D window
48576
48835
  //Update the highlight of 2D interaction diagram according to the current highlighted atoms.
48577
48836
  updateHl2D(chainArray2d) { let ic = this.icn3d, me = ic.icn3dui;
48578
- this.removeHl2D();
48837
+ this.removeHl2D(true);
48579
48838
 
48580
48839
  if(ic.hAtoms && ic.atoms && Object.keys(ic.hAtoms).length == Object.keys(ic.atoms).length) return;
48581
48840
 
@@ -49002,6 +49261,8 @@ class LineGraph {
49002
49261
  && ic.chainsMapping[chainid2] && ic.chainsMapping[chainid2][resid2]) {
49003
49262
  mapping1 = (nodeA.s == "a") ? ic.chainsMapping[chainid1][resid1] : ic.chainsMapping[chainid2][resid2];
49004
49263
  mapping2 = (nodeA.s == "a") ? ic.chainsMapping[chainid2][resid2] : ic.chainsMapping[chainid1][resid1];
49264
+
49265
+ let bIgRef = (mapping1.length > 4 && !isNaN(parseInt(mapping1.substr(-4, 4)))) || (mapping2.length > 4 && !isNaN(parseInt(mapping2.substr(-4, 4))));
49005
49266
 
49006
49267
  let mappingid = mapping1 + '_' + mapping2 + '_' + link.c; // link.c determines the interaction type
49007
49268
 
@@ -49013,7 +49274,7 @@ class LineGraph {
49013
49274
  linkDiff.source += separatorDiff + ic.chainsMapping[chainid1][resid1];
49014
49275
  linkDiff.target += separatorDiff + ic.chainsMapping[chainid2][resid2];
49015
49276
 
49016
- if(linkedNodeCnt[mappingid] == structureArray.length && linkedNodeInterDiffBool[mappingid] == 0) {
49277
+ if(linkedNodeCnt[mappingid] == structureArray.length && (bIgRef || linkedNodeInterDiffBool[mappingid] == 0)) {
49017
49278
  linkArraySplitCommon[index].push(linkCommon);
49018
49279
  }
49019
49280
  else {
@@ -57592,13 +57853,7 @@ class RealignParser {
57592
57853
 
57593
57854
  let allPromise = Promise.allSettled(ajaxArray);
57594
57855
  // try {
57595
- // let dataArray = await allPromise;
57596
-
57597
- let startDate = new Date();
57598
57856
  let dataArray = await allPromise;
57599
- let endDate = new Date();
57600
- let miliseconds = (endDate.getTime() - startDate.getTime());
57601
- console.log("vastdyn time: " + miliseconds + " miliseconds");
57602
57857
 
57603
57858
  ic.qt_start_end = []; // reset the alignment
57604
57859
  await ic.chainalignParserCls.downloadChainalignmentPart2bRealign(dataArray, chainidPairArray, bReverse);
@@ -57690,6 +57945,8 @@ class RealignParser {
57690
57945
  async realignChainOnSeqAlign(chainresiCalphaHash2, chainidArray, bRealign, bPredefined) { let ic = this.icn3d, me = ic.icn3dui;
57691
57946
  let thisClass = this;
57692
57947
 
57948
+ me.cfg.aligntool = 'seqalign';
57949
+
57693
57950
  //bRealign: realign based on seq alignment
57694
57951
  //bPredefined: chain alignment with predefined matching residues
57695
57952
 
@@ -59887,9 +60144,11 @@ class LoadAtomData {
59887
60144
  serial2structure[j] = pdbidTmp.toString();
59888
60145
  mmdbid2pdbid[mmdbidTmp] = pdbidTmp;
59889
60146
  }
59890
-
60147
+
59891
60148
  for(let j = 0, jl = structure.molecules.length; j < jl; ++j) {
59892
60149
  let chain = structure.molecules[j].chain;
60150
+ chain = chain.replace(/_/g, ''); // change "A_1" to "A1"
60151
+
59893
60152
  let kind = structure.molecules[j].kind;
59894
60153
  let title = structure.molecules[j].name;
59895
60154
  //var seq = structure.molecules[j].sequence;
@@ -60882,7 +61141,9 @@ class SetSeqAlign {
60882
61141
 
60883
61142
  let resid = chainid + '_' + resi;
60884
61143
  let resn = ic.residueId2Name[resid];
60885
- if(!resn) resn = '?';
61144
+ if(!resn) {
61145
+ resn = '?';
61146
+ }
60886
61147
 
60887
61148
  return resn;
60888
61149
  }
@@ -63936,7 +64197,7 @@ class Vastplus {
63936
64197
  let chainid = chainidArray[i];
63937
64198
  let atoms = me.hashUtilsCls.intHash(ic.hAtoms, ic.chains[chainid]);
63938
64199
  let firstAtom = ic.firstAtomObjCls.getFirstAtomObj(atoms);
63939
- structHash[firstAtom.structure] = 1;
64200
+ if(firstAtom) structHash[firstAtom.structure] = 1;
63940
64201
  }
63941
64202
  }
63942
64203
 
@@ -64958,6 +65219,11 @@ class ApplyCommand {
64958
65219
 
64959
65220
  ic.drawCls.draw();
64960
65221
  }
65222
+ else if(command == 'add ig labels') {
65223
+ ic.residueLabelsCls.addIgLabels(ic.hAtoms);
65224
+
65225
+ ic.drawCls.draw();
65226
+ }
64961
65227
  else if(command == 'add atom labels') {
64962
65228
  ic.residueLabelsCls.addAtomLabels(ic.hAtoms);
64963
65229
 
@@ -65589,7 +65855,21 @@ class ApplyCommand {
65589
65855
  let nameArray2 = setNameArray[1].split(',');
65590
65856
 
65591
65857
  ic.analysisCls.measureDistManySets(nameArray, nameArray2);
65592
- me.htmlCls.dialogCls.openDlg('dl_disttable', 'Distance among the sets');
65858
+ me.htmlCls.dialogCls.openDlg('dl_disttable', 'Distances among the sets');
65859
+ }
65860
+ }
65861
+ }
65862
+ else if(commandOri.indexOf('angletable') == 0) {
65863
+ let paraArray = commandOri.split(' | ');
65864
+ if(paraArray.length == 2) {
65865
+ let setNameArray = paraArray[1].split(' ');
65866
+
65867
+ if(setNameArray.length == 2) {
65868
+ let nameArray = setNameArray[0].split(',');
65869
+ let nameArray2 = setNameArray[1].split(',');
65870
+
65871
+ ic.analysisCls.measureAngleManySets(nameArray, nameArray2);
65872
+ me.htmlCls.dialogCls.openDlg('dl_angletable', 'Angles among the sets');
65593
65873
  }
65594
65874
  }
65595
65875
  }
@@ -66284,6 +66564,7 @@ class ApplyCommand {
66284
66564
  else if(cmd.indexOf('hide annotation') == 0) return seqAnnoStr + ': checkboxes off';
66285
66565
  else if(cmd == 'add residue labels') return labelStr + 'per Residue';
66286
66566
  else if(cmd == 'add residue number labels') return labelStr + 'per Residue & Number';
66567
+ else if(cmd == 'add Ig domain labels') return labelStr + 'per Ig Domain';
66287
66568
  else if(cmd == 'add atom labels') return labelStr + 'per Atom';
66288
66569
  else if(cmd == 'add chain labels') return labelStr + 'per Chain';
66289
66570
  else if(cmd == 'add terminal labels') return labelStr + 'N- & C- Termini';
@@ -67196,9 +67477,9 @@ class SelectCollections {
67196
67477
  'chemicals': {},
67197
67478
  'ions': {},
67198
67479
  'water': {},
67199
- 'structures': {}, // getSSExpandedAtoms
67480
+ 'structures': {},
67200
67481
  'ssbondpnts': {},
67201
- 'residues': {}, // getSSExpandedAtoms
67482
+ 'residues': {},
67202
67483
  'chains': {},
67203
67484
  'chainsSeq': {}, //Sequences and Annotation
67204
67485
  'defNames2Atoms': {},
@@ -67247,9 +67528,9 @@ class SelectCollections {
67247
67528
  'chemicals': ic.chemicals,
67248
67529
  'ions': ic.ions,
67249
67530
  'water': ic.water,
67250
- 'structures': ic.structures, // getSSExpandedAtoms
67531
+ 'structures': ic.structures,
67251
67532
  'ssbondpnts': ic.ssbondpnts,
67252
- 'residues': ic.residues, // getSSExpandedAtoms
67533
+ 'residues': ic.residues,
67253
67534
  'chains': ic.chains,
67254
67535
  'chainsSeq': ic.chainsSeq, //Sequences and Annotation
67255
67536
  'defNames2Atoms': ic.defNames2Atoms,
@@ -67263,9 +67544,9 @@ class SelectCollections {
67263
67544
  'chemicals': thisClass.dictionaryDifference(ic.allData['prev']['chemicals'], ic.chemicals),
67264
67545
  'ions': thisClass.dictionaryDifference(ic.allData['prev']['ions'], ic.ions),
67265
67546
  'water': thisClass.dictionaryDifference(ic.allData['prev']['water'], ic.water),
67266
- 'structures': thisClass.dictionaryDifference(ic.allData['prev']['structures'], ic.structures), // getSSExpandedAtoms
67547
+ 'structures': thisClass.dictionaryDifference(ic.allData['prev']['structures'], ic.structures),
67267
67548
  'ssbondpnts': thisClass.dictionaryDifference(ic.allData['prev']['ssbondpnts'], ic.ssbondpnts),
67268
- 'residues': thisClass.dictionaryDifference(ic.allData['prev']['residues'], ic.residues), // getSSExpandedAtoms
67549
+ 'residues': thisClass.dictionaryDifference(ic.allData['prev']['residues'], ic.residues),
67269
67550
  'chains': thisClass.dictionaryDifference(ic.allData['prev']['chains'], ic.chains),
67270
67551
  'chainsSeq': thisClass.dictionaryDifference(ic.allData['prev']['chainsSeq'], ic.chainsSeq), //Sequences and Annotation
67271
67552
  'defNames2Atoms': thisClass.dictionaryDifference(ic.allData['prev']['defNames2Atoms'], ic.defNames2Atoms),
@@ -74080,7 +74361,73 @@ class Analysis {
74080
74361
 
74081
74362
  $("#" + me.pre + "dl_disttable_html").html(tableHtml);
74082
74363
  }
74083
- }
74364
+ }
74365
+
74366
+ measureAngleManySets(nameArray, nameArray2) {var ic = this.icn3d, me = ic.icn3dui;
74367
+ if(nameArray.length == 0 || nameArray2.length == 0) {
74368
+ var aaa = 1; //alert("Please select sets for angleance calculation...");
74369
+ }
74370
+ else {
74371
+ let angleHash = {};
74372
+
74373
+ for(let i = 0, il = nameArray.length; i < il; ++i) {
74374
+ let set1 = nameArray[i];
74375
+ let array1 = [set1];
74376
+ angleHash[set1] = {};
74377
+
74378
+ ic.hAtoms = ic.definedSetsCls.getAtomsFromNameArray(array1);
74379
+ let axis1 = ic.axesCls.setPc1Axes(true);
74380
+
74381
+ for(let j = 0, jl = nameArray2.length; j < jl; ++j) {
74382
+ let set2 = nameArray2[j];
74383
+ let array2 = [set2];
74384
+
74385
+ if(set1 == set2) continue;
74386
+
74387
+ ic.hAtoms = ic.definedSetsCls.getAtomsFromNameArray(array2);
74388
+ let axis2 = ic.axesCls.setPc1Axes(true);
74389
+
74390
+ let angleRad = new THREE.Vector3(parseFloat(axis1.x), parseFloat(axis1.y), parseFloat(axis1.z)).angleTo(new THREE.Vector3(parseFloat(axis2.x), parseFloat(axis2.y), parseFloat(axis2.z)));
74391
+
74392
+ let angle = angleRad / 3.1416 * 180;
74393
+ angle = Math.abs(angle).toFixed(0);
74394
+ if(angle > 180) angle -= 180;
74395
+ if(angle > 90) angle = 180 - angle;
74396
+
74397
+ angleHash[set1][set2] = angle;
74398
+ }
74399
+ }
74400
+
74401
+ let tableHtml = '<table align=center border=1 cellpadding=10 cellspacing=0><tr><th></th>';
74402
+ for(let j = 0, jl = nameArray2.length; j < jl; ++j) {
74403
+ let set2 = nameArray2[j];
74404
+ tableHtml += '<th><b>' + set2 + '</b> (&deg;)</th>';
74405
+ }
74406
+ tableHtml += '</tr>';
74407
+
74408
+ for(let i = 0, il = nameArray.length; i < il; ++i) {
74409
+ let set1 = nameArray[i];
74410
+ tableHtml += '<tr><th><b>' + set1 + '</b> (&deg;)</th>';
74411
+
74412
+ for(let j = 0, jl = nameArray2.length; j < jl; ++j) {
74413
+ let set2 = nameArray2[j];
74414
+
74415
+ if(angleHash[set1] && angleHash[set1][set2]) {
74416
+ tableHtml += '<td><span>' + angleHash[set1][set2] + '</span></td>';
74417
+ }
74418
+ else {
74419
+ tableHtml += '<td>0</td>';
74420
+ }
74421
+ }
74422
+
74423
+ tableHtml += '</tr>';
74424
+ }
74425
+
74426
+ tableHtml += '</table><br><br>';
74427
+
74428
+ $("#" + me.pre + "dl_angletable_html").html(tableHtml);
74429
+ }
74430
+ }
74084
74431
 
74085
74432
  //Add a line between the position (x1, y1, z1) and the position (x2, y2, z2) with the input "color".
74086
74433
  //The line can be dashed if "dashed" is set true.
@@ -77923,7 +78270,8 @@ class ShareLink {
77923
78270
  }
77924
78271
  else {
77925
78272
  text += "\nStart of type file======\n";
77926
- text += ic.InputfileType + "\n";
78273
+ // text += ic.InputfileType + "\n";
78274
+ text += "pdb\n";
77927
78275
  text += "End of type file======\n";
77928
78276
 
77929
78277
  text += "Start of data file======\n";
@@ -80788,7 +81136,7 @@ class iCn3DUI {
80788
81136
  //even when multiple iCn3D viewers are shown together.
80789
81137
  this.pre = this.cfg.divid + "_";
80790
81138
 
80791
- this.REVISION = '3.32.0';
81139
+ this.REVISION = '3.33.2';
80792
81140
 
80793
81141
  // In nodejs, iCn3D defines "window = {navigator: {}}"
80794
81142
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -80932,7 +81280,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
80932
81280
  ic.setStyleCls.handleContextLost();
80933
81281
  ic.applyCenterCls.setWidthHeight(width, height);
80934
81282
  ic.ori_chemicalbinding = ic.opts['chemicalbinding'];
80935
- if(me.cfg.bCalphaOnly !== undefined) ic.bCalphaOnly = me.cfg.bCalphaOnly;
81283
+ // if(me.cfg.bCalphaOnly !== undefined) ic.bCalphaOnly = me.cfg.bCalphaOnly;
80936
81284
  ic.opts = me.hashUtilsCls.cloneHash(ic.opts);
80937
81285
  ic.STATENUMBER = ic.commands.length;
80938
81286
  // If previously crashed, recover it
@@ -80960,7 +81308,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
80960
81308
  ic.bInputfile = true;
80961
81309
  ic.InputfileType = 'pdb';
80962
81310
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + pdbStr : pdbStr;
80963
-
81311
+
80964
81312
  await ic.pdbParserCls.loadPdbData(pdbStr);
80965
81313
 
80966
81314
  if(me.cfg.resdef !== undefined && me.cfg.chains !== undefined) {
@@ -80971,9 +81319,9 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
80971
81319
  for(let i = 0, il = structureArray.length; i < il; ++i) {
80972
81320
  chainidArray.push(structureArray[i] + '_' + chainArray[i]);
80973
81321
  }
80974
-
81322
+
80975
81323
  chainidArray = ic.chainalignParserCls.addPostfixForChainids(chainidArray);
80976
-
81324
+
80977
81325
  let bRealign = true, bPredefined = true;
80978
81326
  await ic.realignParserCls.realignChainOnSeqAlign(undefined, chainidArray, bRealign, bPredefined);
80979
81327
  }
@@ -80994,7 +81342,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
80994
81342
  chainidArray.push(domainidArray[i]);
80995
81343
  }
80996
81344
  }
80997
-
81345
+
80998
81346
  // get the matched structures, do not include the template
80999
81347
  let mmdbafid = '';
81000
81348
  for(let i = 0, il = chainidArray.length; i < il; ++i) {
@@ -81005,7 +81353,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81005
81353
  // realign, include the template
81006
81354
  ic.chainidArray = [chain_t].concat(chainidArray);
81007
81355
  ic.chainidArray = ic.chainalignParserCls.addPostfixForChainids(ic.chainidArray);
81008
-
81356
+
81009
81357
  me.htmlCls.clickMenuCls.setLogCmd('resdef ' + me.cfg.resdef, true);
81010
81358
 
81011
81359
  ic.loadCmd = 'vast_search_chainid ' + ic.chainidArray;
@@ -81082,7 +81430,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81082
81430
  }
81083
81431
  else if(me.cfg.refseqid !== undefined) {
81084
81432
  ic.inputid = me.cfg.refseqid;
81085
-
81433
+
81086
81434
  // ic.bNCBI = true;
81087
81435
  ic.loadCmd = 'load refseq ' + me.cfg.refseqid;
81088
81436
  me.htmlCls.clickMenuCls.setLogCmd(ic.loadCmd, true);
@@ -81090,7 +81438,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81090
81438
  }
81091
81439
  else if(me.cfg.protein !== undefined) {
81092
81440
  ic.inputid = me.cfg.protein;
81093
-
81441
+
81094
81442
  // ic.bNCBI = true;
81095
81443
  ic.loadCmd = 'load protein ' + me.cfg.protein;
81096
81444
  me.htmlCls.clickMenuCls.setLogCmd(ic.loadCmd, true);
@@ -81099,7 +81447,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81099
81447
  else if(me.cfg.blast_rep_id !== undefined) {
81100
81448
  // ic.bNCBI = true;
81101
81449
  ic.inputid = me.cfg.query_id + ',' + me.cfg.blast_rep_id;
81102
-
81450
+
81103
81451
  me.cfg.oriQuery_id = me.cfg.query_id;
81104
81452
  me.cfg.oriBlast_rep_id = me.cfg.blast_rep_id;
81105
81453
 
@@ -81123,7 +81471,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81123
81471
  ic.bSmithwm = false;
81124
81472
  ic.bLocalSmithwm = false;
81125
81473
  }
81126
-
81474
+
81127
81475
  me.htmlCls.clickMenuCls.setLogCmd(ic.loadCmd, true);
81128
81476
  await ic.mmdbParserCls.downloadBlast_rep_id(me.cfg.query_id + ',' + me.cfg.blast_rep_id);
81129
81477
  }
@@ -81132,7 +81480,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81132
81480
  let data = await me.getAjaxPromise(url, 'json', false, 'The RID ' + me.cfg.rid + ' may have expired...');
81133
81481
 
81134
81482
  for(let q = 0, ql = data.BlastOutput2.length; q < ql; ++q) {
81135
-
81483
+
81136
81484
  let hitArray;
81137
81485
  if(data.BlastOutput2[q].report.results.iterations) { // psi-blast may have "iterations". Use the last iteration.
81138
81486
  let nIterations = data.BlastOutput2[q].report.results.iterations.length;
@@ -81143,7 +81491,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81143
81491
  if(data.BlastOutput2[q].report.results.search.query_id != me.cfg.query_id) continue;
81144
81492
  hitArray = data.BlastOutput2[q].report.results.search.hits;
81145
81493
  }
81146
-
81494
+
81147
81495
  let qseq = undefined;
81148
81496
  for(let i = 0, il = hitArray.length; i < il; ++i) {
81149
81497
  let hit = hitArray[i];
@@ -81175,6 +81523,18 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81175
81523
  }
81176
81524
  }
81177
81525
  else if(me.cfg.cid !== undefined) {
81526
+ if(isNaN(me.cfg.cid)) {
81527
+ let urlCid = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?compound2cid=" + me.cfg.cid;
81528
+ let cidJson = await me.getAjaxPromise(urlCid, 'jsonp');
81529
+ if(cidJson.cid && cidJson.cid[0]) {
81530
+ me.cfg.cid = cidJson.cid[0];
81531
+ }
81532
+ else {
81533
+ var aaa = 1; //alert("Please input an valid PubChem CID...");
81534
+ return;
81535
+ }
81536
+ }
81537
+
81178
81538
  ic.inputid = me.cfg.cid;
81179
81539
 
81180
81540
  let url = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/" + ic.inputid + "/description/jsonp";
@@ -81239,7 +81599,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81239
81599
  }
81240
81600
  me.htmlCls.clickMenuCls.setLogCmd(ic.loadCmd, true);
81241
81601
 
81242
- await ic.chainalignParserCls.downloadMmdbAf(me.cfg.mmdbafid);
81602
+ await ic.chainalignParserCls.downloadMmdbAf(me.cfg.mmdbafid);
81243
81603
  //await ic.loadScriptCls.loadScript(me.cfg.command, undefined, true);
81244
81604
  }
81245
81605
  else if(me.cfg.command !== undefined && me.cfg.command !== '') {
@@ -81253,7 +81613,7 @@ iCn3DUI.prototype.show3DStructure = async function(pdbStr) { let me = this;
81253
81613
 
81254
81614
  return;
81255
81615
  }
81256
-
81616
+
81257
81617
  await ic.loadScriptCls.loadScript(me.cfg.command, undefined, true);
81258
81618
  // });
81259
81619
  // return me.deferred.promise();
@@ -81321,7 +81681,7 @@ iCn3DUI.prototype.getXMLHttpRqstPromise = function(url, dataType, responseType,
81321
81681
  let oReq = new XMLHttpRequest();
81322
81682
  oReq.open(dataType, url, true);
81323
81683
  oReq.responseType = responseType;
81324
-
81684
+
81325
81685
  oReq.onreadystatechange = function() {
81326
81686
  if (this.readyState == 4) {
81327
81687
  if(this.status == 200) {
@@ -81373,7 +81733,7 @@ iCn3DUI.prototype.getAjaxPromise = function(url, dataType, beforeSend, alertMess
81373
81733
  error : function() {
81374
81734
  if(alertMess) var aaa = 1; //alert(alertMess);
81375
81735
  if(logMess) console.log(logMess);
81376
-
81736
+
81377
81737
  reject('error');
81378
81738
  }
81379
81739
  });
@@ -81416,7 +81776,7 @@ iCn3DUI.prototype.getAjaxPostPromise = async function(url, data, beforeSend, ale
81416
81776
  //if(alertMess) var aaa = 1; //alert(alertMess);
81417
81777
  if(!me.bNode && alertMess) console.log(alertMess);
81418
81778
  if(!me.bNode && logMess) console.log(logMess);
81419
-
81779
+
81420
81780
  // reject('error');
81421
81781
  // keep running the program
81422
81782
  resolve('error');