icn3d 3.8.3 → 3.9.0

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 (2) hide show
  1. package/icn3d.js +1246 -80
  2. package/package.json +1 -1
package/icn3d.js CHANGED
@@ -7460,7 +7460,15 @@ class Strand {
7460
7460
  }
7461
7461
 
7462
7462
  let maxDist = 6.0;
7463
- 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);
7463
+ //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);
7464
+ 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);
7465
+
7466
+ if(bBrokenSs && atom.ss === 'sheet') {
7467
+ bSheetSegment = true;
7468
+ }
7469
+ else if(bBrokenSs && atom.ss === 'helix') {
7470
+ bHelixSegment = true;
7471
+ }
7464
7472
 
7465
7473
  if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) || bBrokenSs) && pnts[0].length > 0 && bSameChain) {
7466
7474
  let atomName = 'CA';
@@ -11461,17 +11469,32 @@ class Surface {
11461
11469
  //vertexColors.push(ic.atoms[verts[vb].atomid].color);
11462
11470
  //vertexColors.push(ic.atoms[verts[vc].atomid].color);
11463
11471
 
11464
- colorArray[offset2++] = ic.atoms[verts[va].atomid].color.r;
11465
- colorArray[offset2++] = ic.atoms[verts[va].atomid].color.g;
11466
- colorArray[offset2++] = ic.atoms[verts[va].atomid].color.b;
11472
+ if(type == 21 || type == 22 || type == 23) { // potential on surface
11473
+ colorArray[offset2++] = verts[va].color.r;
11474
+ colorArray[offset2++] = verts[va].color.g;
11475
+ colorArray[offset2++] = verts[va].color.b;
11467
11476
 
11468
- colorArray[offset2++] = ic.atoms[verts[vb].atomid].color.r;
11469
- colorArray[offset2++] = ic.atoms[verts[vb].atomid].color.g;
11470
- colorArray[offset2++] = ic.atoms[verts[vb].atomid].color.b;
11477
+ colorArray[offset2++] = verts[vb].color.r;
11478
+ colorArray[offset2++] = verts[vb].color.g;
11479
+ colorArray[offset2++] = verts[vb].color.b;
11471
11480
 
11472
- colorArray[offset2++] = ic.atoms[verts[vc].atomid].color.r;
11473
- colorArray[offset2++] = ic.atoms[verts[vc].atomid].color.g;
11474
- colorArray[offset2++] = ic.atoms[verts[vc].atomid].color.b;
11481
+ colorArray[offset2++] = verts[vc].color.r;
11482
+ colorArray[offset2++] = verts[vc].color.g;
11483
+ colorArray[offset2++] = verts[vc].color.b;
11484
+ }
11485
+ else {
11486
+ colorArray[offset2++] = ic.atoms[verts[va].atomid].color.r;
11487
+ colorArray[offset2++] = ic.atoms[verts[va].atomid].color.g;
11488
+ colorArray[offset2++] = ic.atoms[verts[va].atomid].color.b;
11489
+
11490
+ colorArray[offset2++] = ic.atoms[verts[vb].atomid].color.r;
11491
+ colorArray[offset2++] = ic.atoms[verts[vb].atomid].color.g;
11492
+ colorArray[offset2++] = ic.atoms[verts[vb].atomid].color.b;
11493
+
11494
+ colorArray[offset2++] = ic.atoms[verts[vc].atomid].color.r;
11495
+ colorArray[offset2++] = ic.atoms[verts[vc].atomid].color.g;
11496
+ colorArray[offset2++] = ic.atoms[verts[vc].atomid].color.b;
11497
+ }
11475
11498
 
11476
11499
  //var normals = [];
11477
11500
  //normals.push(normalArrayIn[va]);
@@ -11723,7 +11746,7 @@ class ShareLink {
11723
11746
 
11724
11747
  if(!bPngHtml) {
11725
11748
  if(ic.bInputfile && !ic.bInputUrlfile) {
11726
- var aaa = 1; //alert("Share Link does NOT work when the data is from custom files. Please save 'iCn3D PNG Image' in the File menu and open it in iCn3D.");
11749
+ var aaa = 1; //alert("Share Link does NOT work when the data are from custom files. Please save 'iCn3D PNG Image' in the File menu and open it in iCn3D.");
11727
11750
  return;
11728
11751
  }
11729
11752
  if(bTooLong) {
@@ -15589,6 +15612,7 @@ class GetGraph {
15589
15612
  let atom = ic.firstAtomObjCls.getFirstAtomObj(ic.residues[resid]);
15590
15613
  resid2color[resid] = atom.color.getHexString().toUpperCase();
15591
15614
  }
15615
+
15592
15616
  let target2resid = {};
15593
15617
  for(let i = 0, il = graphJson.nodes.length; i < il; ++i) {
15594
15618
  let node = graphJson.nodes[i];
@@ -15615,11 +15639,9 @@ class GetGraph {
15615
15639
  ic.graphStr = JSON.stringify(graphJson);
15616
15640
  }
15617
15641
 
15618
- /*
15619
15642
  if(ic.bGraph) ic.drawGraphCls.drawGraph(ic.graphStr, ic.pre + 'dl_graph');
15620
15643
  if(ic.bLinegraph) ic.lineGraphCls.drawLineGraph(ic.graphStr);
15621
15644
  if(ic.bScatterplot) ic.lineGraphCls.drawLineGraph(ic.graphStr, true);
15622
- */
15623
15645
  }
15624
15646
 
15625
15647
  handleForce() { let ic = this.icn3d, me = ic.icn3dui;
@@ -26491,9 +26513,10 @@ class LoadScript {
26491
26513
  let strArray = ic.commands[i].split("|||");
26492
26514
 
26493
26515
  if(Object.keys(ic.proteins).length > 0 && ic.mmdb_data === undefined &&(ic.bAjax3ddomain === undefined || !ic.bAjax3ddomain)) {
26494
- $.when(thisClass.applyCommand3ddomain(strArray[0].trim())).then(function() {
26495
- thisClass.execCommandsBase(i + 1, end, steps);
26496
- });
26516
+ //$.when(thisClass.applyCommand3ddomain(strArray[0].trim())).then(function() {
26517
+ thisClass.applyCommand3ddomain(strArray[0].trim());
26518
+ thisClass.execCommandsBase(i + 1, end, steps);
26519
+ //});
26497
26520
  }
26498
26521
  else {
26499
26522
  if(Object.keys(ic.proteins).length > 0) {
@@ -26515,8 +26538,9 @@ class LoadScript {
26515
26538
  &&(ic.bAjax3ddomain === undefined || !ic.bAjax3ddomain || ic.mmdb_data === undefined) ) {
26516
26539
  $.when(thisClass.applyCommandClinvar(strArray[0].trim()))
26517
26540
  .then(thisClass.applyCommandSnp(strArray[0].trim()))
26518
- .then(thisClass.applyCommand3ddomain(strArray[0].trim()))
26541
+ //.then(thisClass.applyCommand3ddomain(strArray[0].trim()))
26519
26542
  .then(function() {
26543
+ thisClass.applyCommand3ddomain(strArray[0].trim());
26520
26544
  ic.annotationCls.setAnnoTabAll();
26521
26545
 
26522
26546
  thisClass.execCommandsBase(i + 1, end, steps);
@@ -26535,8 +26559,9 @@ class LoadScript {
26535
26559
  else if(Object.keys(ic.proteins).length > 0 &&(ic.bAjaxClinvar === undefined || !ic.bAjaxClinvar)
26536
26560
  &&(ic.bAjax3ddomain === undefined || !ic.bAjax3ddomain || ic.mmdb_data === undefined)) {
26537
26561
  $.when(thisClass.applyCommandClinvar(strArray[0].trim()))
26538
- .then(thisClass.applyCommand3ddomain(strArray[0].trim()))
26562
+ //.then(thisClass.applyCommand3ddomain(strArray[0].trim()))
26539
26563
  .then(function() {
26564
+ thisClass.applyCommand3ddomain(strArray[0].trim());
26540
26565
  ic.annotationCls.setAnnoTabAll();
26541
26566
 
26542
26567
  thisClass.execCommandsBase(i + 1, end, steps);
@@ -26544,10 +26569,11 @@ class LoadScript {
26544
26569
  }
26545
26570
  else if(Object.keys(ic.proteins).length > 0 &&(ic.bAjax3ddomain === undefined || !ic.bAjax3ddomain || ic.mmdb_data === undefined)
26546
26571
  &&(ic.bAjaxSnp === undefined || !ic.bAjaxSnp)) {
26547
- $.when(thisClass.applyCommand3ddomain(strArray[0].trim()))
26548
- .then(thisClass.applyCommandSnp(strArray[0].trim()))
26572
+ //$.when(thisClass.applyCommand3ddomain(strArray[0].trim()))
26573
+ $.when(thisClass.applyCommandSnp(strArray[0].trim()))
26549
26574
  .then(function() {
26550
- ic.annotationCls.setAnnoTabAll();
26575
+ thisClass.applyCommand3ddomain(strArray[0].trim());
26576
+ ic.annotationCls.setAnnoTabAll();
26551
26577
 
26552
26578
  thisClass.execCommandsBase(i + 1, end, steps);
26553
26579
  });
@@ -26569,12 +26595,14 @@ class LoadScript {
26569
26595
  });
26570
26596
  }
26571
26597
  else if(Object.keys(ic.proteins).length > 0 &&(ic.bAjax3ddomain === undefined || !ic.bAjax3ddomain || ic.mmdb_data === undefined) ) {
26572
- $.when(thisClass.applyCommand3ddomain(strArray[0].trim()))
26573
- .then(function() {
26598
+ //$.when(thisClass.applyCommand3ddomain(strArray[0].trim()))
26599
+ // .then(function() {
26600
+ thisClass.applyCommand3ddomain(strArray[0].trim());
26601
+
26574
26602
  ic.annotationCls.setAnnoTabAll();
26575
26603
 
26576
26604
  thisClass.execCommandsBase(i + 1, end, steps);
26577
- });
26605
+ //});
26578
26606
  }
26579
26607
  else {
26580
26608
  if(Object.keys(ic.proteins).length > 0) {
@@ -26841,12 +26869,13 @@ class LoadScript {
26841
26869
  thisClass.applyCommand3ddomain(lastCommand);
26842
26870
  }
26843
26871
  else if(lastCommand.indexOf('set annotation all') == 0) {
26844
- //$.when(thisClass.applyCommandAnnotationsAndCddSite(lastCommand))
26845
- // .then(thisClass.applyCommandSnpClinvar(lastCommand))
26846
26872
  $.when(thisClass.applyCommandClinvar(lastCommand))
26847
26873
  .then(thisClass.applyCommandSnp(lastCommand))
26848
- .then(thisClass.applyCommand3ddomain(lastCommand));
26849
- ic.annotationCls.setAnnoTabAll();
26874
+ //.then(thisClass.applyCommand3ddomain(lastCommand));
26875
+ .then(function() {
26876
+ thisClass.applyCommand3ddomain(lastCommand);
26877
+ ic.annotationCls.setAnnoTabAll();
26878
+ });
26850
26879
  }
26851
26880
  else if(lastCommand.indexOf('view interactions') == 0 && me.cfg.align !== undefined) {
26852
26881
  thisClass.applyCommandViewinteraction(lastCommand);
@@ -27238,11 +27267,11 @@ class LoadScript {
27238
27267
  let thisClass = this;
27239
27268
 
27240
27269
  // chain functions together
27241
- ic.deferred3ddomain = $.Deferred(function() {
27270
+ //ic.deferred3ddomain = $.Deferred(function() {
27242
27271
  thisClass.applyCommand3ddomainBase(command);
27243
- }); // end of me.deferred = $.Deferred(function() {
27272
+ //}); // end of me.deferred = $.Deferred(function() {
27244
27273
 
27245
- return ic.deferred3ddomain.promise();
27274
+ //return ic.deferred3ddomain.promise();
27246
27275
  }
27247
27276
 
27248
27277
  applyCommandViewinteractionBase(command) { let ic = this.icn3d, me = ic.icn3dui;
@@ -27581,6 +27610,1037 @@ class ResizeCanvas {
27581
27610
  }
27582
27611
  }
27583
27612
 
27613
+ /*
27614
+ * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
27615
+ * Modified from Tom Madej's C++ code
27616
+ */
27617
+
27618
+ //import * as THREE from 'three';
27619
+
27620
+ class Domain3d {
27621
+ constructor(icn3d) {
27622
+ this.icn3d = icn3d;
27623
+
27624
+ this.dcut = 8; // threshold for C-alpha interactions
27625
+
27626
+ this.MAX_SSE = 512;
27627
+
27628
+ //let this.ctc_cnt[this.MAX_SSE][this.MAX_SSE]; // contact count matrix
27629
+ this.ctc_cnt = [];
27630
+ for(let i = 0; i < this.MAX_SSE; ++i) {
27631
+ this.ctc_cnt[i] = [];
27632
+ }
27633
+
27634
+ //let this.elt_size[this.MAX_SSE]; // element sizes in residues
27635
+ this.elt_size = [];
27636
+ this.elt_size.length = this.MAX_SSE;
27637
+
27638
+ //let this.group_num[this.MAX_SSE]; // indicates required element groupings
27639
+ this.group_num = [];
27640
+ this.group_num.length = this.MAX_SSE;
27641
+
27642
+ this.split_ratio = 0.0; //let // splitting ratio
27643
+ this.min_size = 0; // min required size of a domain
27644
+ this.min_sse = 0; // min number of SSEs required in a domain
27645
+ this.max_csz = 0; // max size of a cut, i.e. number of points
27646
+ this.mean_cts = 0.0; // mean number of contacts in a domain
27647
+ this.c_delta = 0; // cut set parameter
27648
+ this.nc_fact = 0.0; // size factor for internal contacts
27649
+ //let this.elements[2*this.MAX_SSE]; // sets of this.elements to be split
27650
+ this.elements = [];
27651
+ this.elements.length = 2*this.MAX_SSE;
27652
+
27653
+ //let this.stack[this.MAX_SSE]; // this.stack of sets (subdomains) to split
27654
+ this.stack = [];
27655
+ this.stack.length = this.MAX_SSE;
27656
+
27657
+ this.top = 0; // this.top of this.stack
27658
+ //let this.curr_prt0[this.MAX_SSE]; // current part 0 this.elements
27659
+ this.curr_prt0 = [];
27660
+ this.curr_prt0.length = this.MAX_SSE;
27661
+
27662
+ //let this.curr_prt1[this.MAX_SSE]; // current part 1 this.elements
27663
+ this.curr_prt1 = [];
27664
+ this.curr_prt1.length = this.MAX_SSE;
27665
+
27666
+ this.curr_ne0 = 0; // no. of this.elements in current part 0
27667
+ this.curr_ne1 = 0; // no. of this.elements in current part 1
27668
+ this.curr_ratio = 0.0; // current splitting ratio
27669
+ this.curr_msize = 0; // min of current part sizes
27670
+ //let this.parts[2*this.MAX_SSE]; // final partition into domains
27671
+ this.parts = [];
27672
+ this.parts.length = 2*this.MAX_SSE;
27673
+
27674
+ this.np = 0; // next free location in this.parts[]
27675
+ this.n_doms = 0; // number of domains
27676
+ //let this.save_ratios[this.MAX_SSE]; // this.saved splitting ratios
27677
+ this.save_ratios = [];
27678
+ this.save_ratios.length = this.MAX_SSE;
27679
+
27680
+ this.saved = 0; // number of this.saved ratios
27681
+ }
27682
+
27683
+ // Partition the set of this.elements on this.top of the this.stack based on the input cut.
27684
+ // If the partition is valid and the ratio is smaller than the current one, then
27685
+ // save it as the best partition so far encountered. Various criteria are
27686
+ // employed for valid partitions, as described below.
27687
+ //
27688
+
27689
+ //update_partition(int* cut, let k, let n) { let ic = this.icn3d, me = ic.icn3dui;
27690
+ update_partition(cut, k, n) { let ic = this.icn3d; ic.icn3dui;
27691
+ let i, il, j, t, nc0, nc1, ncx, ne, ne0, ne1, elts = [], prt = []; //int
27692
+ let size0, size1, prt0 = [], prt1 = []; // int
27693
+ prt0.length = this.MAX_SSE;
27694
+ prt1.length = this.MAX_SSE;
27695
+ let f, r0; //let
27696
+
27697
+ // this.elements from the this.top of the this.stack
27698
+ //elts = &this.elements[this.stack[this.top - 1]];
27699
+ for(i = this.stack[this.top - 1], il = this.elements.length; i < il; ++i) {
27700
+ elts.push(this.elements[i]);
27701
+ }
27702
+
27703
+ // generate the partition based on the cut //
27704
+ for (i = ne = ne0 = ne1 = 0, prt = prt0, t = -1; i < k; i++) {
27705
+ // write the this.elements into prt //
27706
+ for (j = t + 1; j <= cut[i]; j++)
27707
+ prt[ne++] = elts[j];
27708
+
27709
+ t = cut[i];
27710
+
27711
+ // switch the partition //
27712
+ if (prt == prt0) {
27713
+ ne0 = ne;
27714
+ prt = prt1;
27715
+ ne = ne1;
27716
+ }
27717
+ else {
27718
+ ne1 = ne;
27719
+ prt = prt0;
27720
+ ne = ne0;
27721
+ }
27722
+ }
27723
+
27724
+ // finish with the last part //
27725
+ for (j = t + 1; j < n; j++)
27726
+ prt[ne++] = elts[j];
27727
+
27728
+ if (prt == prt0)
27729
+ ne0 = ne;
27730
+ else
27731
+ ne1 = ne;
27732
+
27733
+ // don't split into two teeny this.parts! //
27734
+ if ((ne0 < this.min_sse) && (ne1 < this.min_sse))
27735
+ return cut;
27736
+
27737
+ // check to see if the partition splits any required groups //
27738
+ for (i = 0; i < ne0; i++) {
27739
+ t = this.group_num[prt0[i]];
27740
+
27741
+ for (j = 0; j < ne1; j++) {
27742
+ if (t == this.group_num[prt1[j]])
27743
+ return cut;
27744
+ }
27745
+ }
27746
+
27747
+ // compute the sizes of the this.parts //
27748
+ for (i = size0 = 0; i < ne0; i++)
27749
+ size0 += this.elt_size[prt0[i]];
27750
+
27751
+ for (i = size1 = 0; i < ne1; i++)
27752
+ size1 += this.elt_size[prt1[i]];
27753
+
27754
+ // count internal contacts for part 0 //
27755
+ for (i = nc0 = 0; i < ne0; i++) {
27756
+ for (j = i; j < ne0; j++)
27757
+ nc0 += this.ctc_cnt[prt0[i]][prt0[j]];
27758
+ }
27759
+
27760
+ // count internal contacts for part 1 //
27761
+ for (i = nc1 = 0; i < ne1; i++) {
27762
+ for (j = i; j < ne1; j++)
27763
+ nc1 += this.ctc_cnt[prt1[i]][prt1[j]];
27764
+ }
27765
+
27766
+ // check globularity condition //
27767
+ if ((1.0 * nc0 / size0 < this.mean_cts) ||
27768
+ (1.0 * nc1 / size1 < this.mean_cts))
27769
+ return cut;
27770
+
27771
+ // to handle non-globular pieces make sure nc0, nc1, are large enough //
27772
+ nc0 = Math.max(nc0, this.nc_fact*size0);
27773
+ nc1 = Math.max(nc1, this.nc_fact*size1);
27774
+
27775
+ // count inter-part contacts //
27776
+ for (i = ncx = 0; i < ne0; i++) {
27777
+ t = prt0[i];
27778
+
27779
+ for (j = 0; j < ne1; j++)
27780
+ ncx += this.ctc_cnt[t][prt1[j]];
27781
+ }
27782
+
27783
+ // compute the splitting ratio //
27784
+ f = Math.min(nc0, nc1);
27785
+ r0 = 1.0 * ncx / (f + 1.0);
27786
+
27787
+ if ((r0 >= this.curr_ratio + 0.01) || (r0 > this.split_ratio))
27788
+ return cut;
27789
+
27790
+ // If the difference in the ratios is insignificant then take the split
27791
+ // that most evenly partitions the domain.
27792
+
27793
+ if ((r0 > this.curr_ratio - 0.01) && (Math.min(size0, size1) < this.curr_msize))
27794
+ return cut;
27795
+
27796
+ // if we get to here then keep this split //
27797
+ for (i = 0; i < ne0; i++)
27798
+ this.curr_prt0[i] = prt0[i];
27799
+
27800
+ for (i = 0; i < ne1; i++)
27801
+ this.curr_prt1[i] = prt1[i];
27802
+
27803
+ this.curr_ne0 = ne0;
27804
+ this.curr_ne1 = ne1;
27805
+ this.curr_ratio = r0;
27806
+ this.curr_msize = Math.min(size0, size1);
27807
+
27808
+ return cut;
27809
+
27810
+ } // end update_partition //
27811
+
27812
+
27813
+
27814
+ // // Run through the possible cuts of size k for a set of this.elements of size n.
27815
+ // *
27816
+ // * To avoid small protrusions, no blocks of consecutive this.elements of length <= this.c_delta
27817
+ // * are allowed. An example where this is desirable is as follows. Let's say you
27818
+ // * have a protein with 2 subdomains, one of them an alpha-beta-alpha sandwich. It
27819
+ // * could then happen that one of the helices in the sandwich domain might make more
27820
+ // * contacts with the other subdomain than with the sandwich. The correct thing to
27821
+ // * do is to keep the helix with the rest of the sandwich, and the "this.c_delta rule"
27822
+ // * enforces this.
27823
+ // //
27824
+
27825
+ cut_size(k, n) { let ic = this.icn3d; ic.icn3dui;
27826
+ let i, j, cok, cut0 = []; //int
27827
+ cut0.length = this.MAX_SSE;
27828
+
27829
+ for (i = 0; i < k; i++)
27830
+ cut0[i] = i;
27831
+
27832
+ // enumerate cuts of length k //
27833
+ while (1) {
27834
+ // check block sizes in the cut //
27835
+ for (i = cok = 1; i < k; i++) {
27836
+ if (cut0[i] - cut0[i - 1] <= this.c_delta) {
27837
+ cok = 0;
27838
+ break;
27839
+ }
27840
+ }
27841
+ if (cok && (cut0[k - 1] < n - 1))
27842
+ cut0 = this.update_partition(cut0, k, n);
27843
+
27844
+ // generate the next k-tuple of positions //
27845
+ for (j = k - 1; (j >= 0) && (cut0[j] == n - k + j); j--);
27846
+
27847
+ if (j < 0) break;
27848
+
27849
+ cut0[j]++;
27850
+
27851
+ for (i = j + 1; i < k; i++)
27852
+ cut0[i] = cut0[i - 1] + 1;
27853
+ }
27854
+
27855
+ } // end cut_size //
27856
+
27857
+
27858
+
27859
+ // // Process the set of this.elements on this.top of the this.stack. We generate cut sets in
27860
+ // * a limited size range, generally from 1 to 5. For each cut the induced
27861
+ // * partition is considered and its splitting parameters computed. The cut
27862
+ // * that yields the smallest splitting ratio is chosen as the correct one, if
27863
+ // * the ratio is low enough. The subdomains are then placed on the this.stack for
27864
+ // * further consideration.
27865
+ // *
27866
+ // * Subdomains with < this.min_sse SSEs are not allowed to split further, however,
27867
+ // * it is possible to trim fewer than this.min_sse SSEs from a larger domain. E.g.
27868
+ // * a chain with 7 SSEs can be split into a subdomain with 5 SSEs and another
27869
+ // * with 2 SSEs, but the one with 2 SSEs cannot be split further.
27870
+ // *
27871
+ // * Note that the invariant is, that this.stack[top] always points to the next free
27872
+ // * location in this.elements[].
27873
+ // //
27874
+
27875
+ process_set() { let ic = this.icn3d; ic.icn3dui;
27876
+ let i, il, k, n, t, k0, elts = []; //int
27877
+
27878
+ // count the this.elements //
27879
+ //elts = &this.elements[this.stack[this.top - 1]];
27880
+ for(i = this.stack[this.top - 1], il = this.elements.length; i < il; ++i) {
27881
+ elts.push(this.elements[i]);
27882
+ }
27883
+
27884
+ //for (n = 0; *elts > -1; n++, elts++);
27885
+ for (n = 0; n < elts.length && elts[n] > -1; n++);
27886
+
27887
+ // try various cut sizes //
27888
+ k0 = Math.min(n - 1, this.max_csz);
27889
+ this.curr_ne0 = this.curr_ne1 = 0;
27890
+ this.curr_ratio = 100.0;
27891
+
27892
+ for (k = 1; k <= k0; k++)
27893
+ this.cut_size(k, n);
27894
+
27895
+ // pop this.stack //
27896
+ this.top--;
27897
+
27898
+ if (this.curr_ne0 == 0) {
27899
+ // no split took place, save part //
27900
+ t = this.stack[this.top];
27901
+
27902
+ //for (elts = &this.elements[t]; *elts > -1; elts++)
27903
+ // parts[np++] = *elts;
27904
+
27905
+ for (i = t; i < this.elements.length && this.elements[i] > -1; i++)
27906
+ this.parts[this.np++] = this.elements[i];
27907
+
27908
+ this.parts[this.np++] = -1;
27909
+ this.n_doms++;
27910
+ }
27911
+ else {
27912
+ this.save_ratios[this.saved++] = this.curr_ratio;
27913
+
27914
+ if (this.curr_ne0 > this.min_sse) {
27915
+ // push on part 0 //
27916
+ t = this.stack[this.top];
27917
+
27918
+ for (i = 0; i < this.curr_ne0; i++)
27919
+ this.elements[t++] = this.curr_prt0[i];
27920
+
27921
+ this.elements[t++] = -1;
27922
+ this.stack[++this.top] = t;
27923
+ }
27924
+ else {
27925
+ // save part 0 //
27926
+ for (i = 0; i < this.curr_ne0; i++)
27927
+ this.parts[this.np++] = this.curr_prt0[i];
27928
+
27929
+ this.parts[this.np++] = -1;
27930
+ this.n_doms++;
27931
+ }
27932
+
27933
+ if (this.curr_ne1 > this.min_sse) {
27934
+ // push on part 1 //
27935
+ t = this.stack[this.top];
27936
+
27937
+ for (i = 0; i < this.curr_ne1; i++)
27938
+ this.elements[t++] = this.curr_prt1[i];
27939
+
27940
+ this.elements[t++] = -1;
27941
+ this.stack[++this.top] = t;
27942
+ }
27943
+ else {
27944
+ // save part 1 //
27945
+ for (i = 0; i < this.curr_ne1; i++)
27946
+ this.parts[this.np++] = this.curr_prt1[i];
27947
+
27948
+ this.parts[this.np++] = -1;
27949
+ this.n_doms++;
27950
+ }
27951
+ }
27952
+ } // end process_set //
27953
+
27954
+
27955
+
27956
+ // Main driver for chain splitting. //
27957
+ //process_all(let n) { let ic = this.icn3d, me = ic.icn3dui;
27958
+ process_all(n) { let ic = this.icn3d; ic.icn3dui;
27959
+ let i; //int
27960
+
27961
+ // initialize the this.stack //
27962
+ this.top = 1;
27963
+ this.stack[0] = this.np = this.n_doms = 0;
27964
+ this.saved = 0;
27965
+
27966
+ for (i = 0; i < n; i++)
27967
+ this.elements[i] = i;
27968
+
27969
+ this.elements[n] = -1;
27970
+
27971
+ // recursively split the chain into domains //
27972
+ while (this.top > 0) {
27973
+ this.process_set();
27974
+ }
27975
+ } // end process_all //
27976
+
27977
+ // Output the domains. For S we number the this.elements 1, 2, ..., n. //
27978
+ //output(let n, int* prts) { let ic = this.icn3d, me = ic.icn3dui;
27979
+ output(n) { let ic = this.icn3d; ic.icn3dui;
27980
+ let i, k; //int
27981
+
27982
+ let prts = [];
27983
+
27984
+ // zap the output array //
27985
+ for (i = 0; i < 2*n; i++)
27986
+ prts.push(0);
27987
+
27988
+ // now write out the subdomains //
27989
+ for (i = k = 0; k < this.n_doms; i++) {
27990
+ prts[i] = this.parts[i] + 1;
27991
+
27992
+ if (this.parts[i] < 0)
27993
+ k++;
27994
+ }
27995
+
27996
+ return prts;
27997
+ } // end output //
27998
+
27999
+
28000
+
28001
+ // // S-interface to the chain-splitting program.
28002
+ // *
28003
+ // * Explanation of parameters:
28004
+ // *
28005
+ // * ne - number of secondary structure this.elements (SSEs)
28006
+ // * cts - contact count matrix
28007
+ // * elt_sz - sizes of SSEs
28008
+ // * grps - element group indicators
28009
+ // * sratio - splitting ratio
28010
+ // * msize - min size of a split domain
28011
+ // * m_sse - min number of SSEs required in a split part
28012
+ // * mcsz - max cut size, i.e. max number of split points
28013
+ // * avg_cts - mean number of internal contacts for a domain
28014
+ // * c_delt - cut set parameter
28015
+ // * ncf0 - size factor for number of internal contacts
28016
+ // * prts - output listing of domains
28017
+ // * n_saved - number of this.saved splitting ratios
28018
+ // * ratios - splitting ratios
28019
+ // * ret - success/failure indicator
28020
+ // * verb - flag to turn off/on splitting information
28021
+ // //
28022
+
28023
+ //new_split_chain(let ne, let sratio, let msize, let m_sse, let mcsz, let avg_cts,
28024
+ // let c_delt, let ncf0, int* prts, int* n_saved, let* ratios) { let ic = this.icn3d, me = ic.icn3dui;
28025
+ new_split_chain(ne, sratio, msize, m_sse, mcsz, avg_cts,
28026
+ c_delt, ncf0, prts, n_saved, ratios) { let ic = this.icn3d; ic.icn3dui;
28027
+ let i; //int
28028
+
28029
+ this.split_ratio = sratio;
28030
+ this.min_size = msize;
28031
+ this.min_sse = m_sse;
28032
+ this.max_csz = mcsz;
28033
+ this.mean_cts = avg_cts;
28034
+ this.c_delta = c_delt;
28035
+ this.nc_fact = ncf0;
28036
+
28037
+ this.process_all(ne);
28038
+ //this.output(ne, prts);
28039
+ this.parts = this.output(ne);
28040
+ n_saved = this.saved;
28041
+ for (i = 0; i < this.saved; i++)
28042
+ ratios[i] = this.save_ratios[i];
28043
+
28044
+ return n_saved;
28045
+
28046
+ } // end new_split_chain //
28047
+
28048
+ //
28049
+ // Actually, here is a better method that is also simple!
28050
+ //
28051
+ // If there are N atoms (residues) this algorithm should usually run in
28052
+ // time O(N^4/3), and usually even much faster! In very unusual cases
28053
+ // it could take quadratic time. The key idea is that atoms are not
28054
+ // infinitely compressible, i.e. only a fixed number will fit in a given
28055
+ // region of space. So if the protein is roughly spherical, there will
28056
+ // only be O(N^1/3) atoms close to any given diameter. Therefore, a
28057
+ // bound on the number of iterations of the inner loop is O(N^1/3).
28058
+ //
28059
+ // For an elongated protein that happens to have the x-axis normal to
28060
+ // the long axis, then it is possible for the inner loop to take time
28061
+ // O(N), in which case the whole takes O(N^2). But this should rarely,
28062
+ // if ever, occur in practice. It would also be possible beforehand to
28063
+ // choose the axis with the largest variance.
28064
+ //
28065
+
28066
+ // typedef struct res_struct {
28067
+ // let rnum;
28068
+ // let x, y, z;
28069
+ // } ResRec;
28070
+
28071
+ //list< pair< pair< int, let >, let > >
28072
+ //c2b_AlphaContacts(let n0, let* x0, let* y0, let* z0,
28073
+ // const let incr = 4, const let dcut = 8.0) { let ic = this.icn3d, me = ic.icn3dui;
28074
+ c2b_AlphaContacts(n0, x0, y0, z0, dcut) { let ic = this.icn3d; ic.icn3dui;
28075
+ //if(!incr) incr = 4;
28076
+ if(!dcut) dcut = this.dcut;
28077
+
28078
+ let list_cts = [], list_rr = [];
28079
+
28080
+ for (let i = 0; i < n0; i++) {
28081
+ // don't include residues with missing coordinates
28082
+ //if ((x0[i] == MissingCoord) || (y0[i] == MissingCoord) || (z0[i] == MissingCoord))
28083
+ if (!x0[i]|| !y0[i] || !z0[i])
28084
+ continue;
28085
+
28086
+ //ResRec rr0;
28087
+ let rr0 = {};
28088
+ rr0.rnum = i + 1;
28089
+ rr0.x = x0[i];
28090
+ rr0.y = y0[i];
28091
+ rr0.z = z0[i];
28092
+ list_rr.push(rr0);
28093
+ }
28094
+
28095
+ list_rr.sort(function(rr1, rr2) {
28096
+ return rr1.x - rr2.x;
28097
+ });
28098
+
28099
+ //let rrit1, rrit2, rrbeg;
28100
+ let i, j, len = list_rr.length;
28101
+
28102
+ //for (rrit1 = list_rr.begin(); rrit1 != list_rr.end(); rrit1++) {
28103
+ for (i = 0; i < len; ++i) {
28104
+ //ResRec rr1 = *rrit1;
28105
+ let rr1 = list_rr[i];
28106
+ let x1 = rr1.x;
28107
+ let y1 = rr1.y;
28108
+ let z1 = rr1.z;
28109
+ //rrbeg = rrit1;
28110
+ //rrbeg++;
28111
+
28112
+ //for (rrit2 = rrbeg; rrit2 != list_rr.end(); rrit2++) {
28113
+ for (j = i + 1; j < len; ++j) {
28114
+ //ResRec rr2 = *rrit2;
28115
+ let rr2 = list_rr[j];
28116
+ if ((rr1.rnum - rr2.rnum <= 3) && (rr2.rnum - rr1.rnum <= 3)) continue;
28117
+ let x2 = rr2.x;
28118
+ let y2 = rr2.y;
28119
+ let z2 = rr2.z;
28120
+
28121
+ if (x2 > x1 + dcut)
28122
+ break;
28123
+
28124
+ // x1 <= x2 <= x1 + dcut so compare
28125
+ let sum = (x1 - x2)*(x1 - x2);
28126
+ sum += (y1 - y2)*(y1 - y2);
28127
+ sum += (z1 - z2)*(z1 - z2);
28128
+ let d0 = Math.sqrt(sum);
28129
+ if (d0 > dcut) continue;
28130
+ //pair< pair< int, let >, let > lpair;
28131
+ //pair< int, let > rpair;
28132
+ let lpair = {}, rpair = {};
28133
+
28134
+ if (rr1.rnum < rr2.rnum) {
28135
+ rpair.first = rr1.rnum;
28136
+ rpair.second = rr2.rnum;
28137
+ }
28138
+ else {
28139
+ rpair.first = rr2.rnum;
28140
+ rpair.second = rr1.rnum;
28141
+ }
28142
+
28143
+ lpair.first = rpair;
28144
+ lpair.second = d0;
28145
+ list_cts.push(lpair);
28146
+ }
28147
+ }
28148
+
28149
+ return list_cts;
28150
+
28151
+ } // end c2b_AlphaContacts
28152
+
28153
+
28154
+
28155
+ //
28156
+ // Creates a table, actually a graph, of the contacts between SSEs.
28157
+ //
28158
+
28159
+ //static map< pair< int, let >, let >
28160
+ //c2b_ContactTable(vector<int>& v1, vector<int>& v2) { let ic = this.icn3d, me = ic.icn3dui;
28161
+ c2b_ContactTable(v1, v2) { let ic = this.icn3d; ic.icn3dui;
28162
+ let cmap = {};
28163
+ let n0 = v1.length; //unsigned int
28164
+
28165
+ if (n0 != v2.length) {
28166
+ // problem!
28167
+
28168
+ return cmap;
28169
+ }
28170
+
28171
+ for (let i = 0; i < n0; i++) {
28172
+ let e1 = v1[i];
28173
+ let e2 = v2[i];
28174
+ //pair<int, int> epr;
28175
+ //let epr = {};
28176
+ //epr.first = e1;
28177
+ //epr.second = e2;
28178
+ let epr = e1 + '_' + e2;
28179
+
28180
+ //if (cmap.count(epr) == 0) {
28181
+ if (!cmap[epr]) {
28182
+ cmap[epr] = 1;
28183
+ }
28184
+ else
28185
+ cmap[epr]++;
28186
+ }
28187
+
28188
+ return cmap;
28189
+
28190
+ } // end c2b_ContactTable
28191
+
28192
+
28193
+ //https://www.geeksforgeeks.org/number-groups-formed-graph-friends/
28194
+ countUtil(ss1, sheetNeighbor, existing_groups) {
28195
+ this.visited[ss1] = true;
28196
+ if(!this.groupnum2sheet[existing_groups]) this.groupnum2sheet[existing_groups] = [];
28197
+ this.groupnum2sheet[existing_groups].push(parseInt(ss1));
28198
+
28199
+ for(let ss2 in sheetNeighbor[ss1]) {
28200
+ if (!this.visited[ss2]) {
28201
+ this.countUtil(ss2, sheetNeighbor, existing_groups);
28202
+ }
28203
+ }
28204
+ }
28205
+
28206
+ //
28207
+ // Residue ranges of the Vast domains, per protein chain.
28208
+ //
28209
+
28210
+ //
28211
+ // Subdomain definition rules are as follows; let m0 = minSSE:
28212
+ //
28213
+ // 1. A subdomain with <= m0 SSEs cannot be split.
28214
+ //
28215
+ // 2. A subdomain cannot be split into two this.parts, both with < m0 SSEs.
28216
+ //
28217
+ // 3. However, a subdomain can be trimmed, i.e. split into two this.parts,
28218
+ // one with < m0 SSEs.
28219
+ //
28220
+ //c2b_NewSplitChain(string asymId, let seqLen, let* x0, let* y0, let* z0) { let ic = this.icn3d, me = ic.icn3dui;
28221
+ // x0, y0, z0: array of x,y,z coordinates of C-alpha atoms
28222
+ c2b_NewSplitChain(chnid, dcut) { let ic = this.icn3d; ic.icn3dui;
28223
+ let x0 = [], y0 = [], z0 = [];
28224
+
28225
+ //substruct: array of secondary structures, each of which has the keys: From (1-based), To (1-based), Sheet (0 or 1)
28226
+ let substruct = [];
28227
+ // sheets: array of sheets, each of which has the key: sheet_num (beta sandwich has two sheets, e.g., 0 and 1), adj_strand1 (not used), adj_strand2
28228
+ let sheets = [];
28229
+
28230
+ let substructItem = {};
28231
+ let resiOffset = 0;
28232
+ for(let i = 0; i < ic.chainsSeq[chnid].length; ++i) {
28233
+ let resi = ic.chainsSeq[chnid][i].resi;
28234
+ if(i == 0) {
28235
+ resiOffset = resi - 1;
28236
+
28237
+ for(let j = 0; j < resiOffset; ++j) {
28238
+ x0.push(undefined);
28239
+ y0.push(undefined);
28240
+ z0.push(undefined);
28241
+ }
28242
+ }
28243
+
28244
+ let resid = chnid + "_" + resi;
28245
+ let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.residues[resid]);
28246
+
28247
+ if(atom) {
28248
+ x0.push(atom.coord.x);
28249
+ y0.push(atom.coord.y);
28250
+ z0.push(atom.coord.z);
28251
+ }
28252
+ else {
28253
+ x0.push(undefined);
28254
+ y0.push(undefined);
28255
+ z0.push(undefined);
28256
+ }
28257
+
28258
+ if(!atom) continue;
28259
+
28260
+ if(atom.ssend) {
28261
+ substructItem.To = parseInt(resi);
28262
+ substructItem.Sheet = (atom.ss == 'sheet') ? true : false;
28263
+ substruct.push(substructItem);
28264
+ substructItem = {};
28265
+ }
28266
+
28267
+ // a residue could be both start and end. check ssend first, then check ssbegin
28268
+ if(atom.ssbegin) {
28269
+ substructItem.From = parseInt(resi);
28270
+ }
28271
+ }
28272
+
28273
+ let nsse = substruct.length;
28274
+
28275
+ if (nsse <= 3)
28276
+ // too small, can't split or trim
28277
+ return;
28278
+
28279
+ if (nsse > this.MAX_SSE) {
28280
+ // we have a problem...
28281
+
28282
+ return;
28283
+ }
28284
+
28285
+ let seqLen = ic.chainsSeq[chnid].length + resiOffset;
28286
+
28287
+ // get a list of Calpha-Calpha contacts
28288
+ ///list< pair< pair< int, let >, let > >
28289
+ let cts = this.c2b_AlphaContacts(seqLen, x0, y0, z0, dcut);
28290
+
28291
+ //
28292
+ // Produce a "map" of the SSEs, i.e. vec_sse[i] = 0 means residue i + 1
28293
+ // is in a loop, and vec_sse[i] = k means residue i + 1 belongs to SSE
28294
+ // number k.
28295
+ //
28296
+ let vec_sse = []; //vector<int>
28297
+
28298
+ for (let i = 0; i < seqLen; i++)
28299
+ vec_sse.push(0);
28300
+
28301
+ let hasSheets = false;
28302
+
28303
+ //substruct: array of secondary structures, each of which has the keys: From, To, Sheet (0, 1)
28304
+ for (let i = 0; i < substruct.length; i++) {
28305
+ //SSE_Rec sserec = substruct[i];
28306
+ let sserec = substruct[i];
28307
+ let From = sserec.From;
28308
+ let To = sserec.To;
28309
+ this.elt_size[i] = To - From + 1;
28310
+
28311
+ // double-check indexing OK???
28312
+ for (let j = From; j <= To; j++)
28313
+ vec_sse[j - 1] = i + 1;
28314
+
28315
+ //if (sserec.Sheet > 0)
28316
+ if (sserec.Sheet)
28317
+ hasSheets = true;
28318
+ }
28319
+
28320
+ // produce the SSE contact lists
28321
+ let vec_cts1 = [], vec_cts2 = [], vec_cts1a = [], vec_cts2a = [];
28322
+
28323
+ //for (ctsit = cts.begin(); ctsit != cts.end(); ctsit++) {
28324
+ for (let i = 0, il = cts.length; i < il; ++i) {
28325
+ //pair< pair< int, let >, let > epr = *ctsit;
28326
+ //pair< int, let > respair = epr.first;
28327
+ let epr = cts[i];
28328
+ let respair = epr.first;
28329
+ let sse1 = vec_sse[respair.first - 1];
28330
+ let sse2 = vec_sse[respair.second - 1];
28331
+ // could be 0 or null
28332
+ if ((sse1 <= 0) || (sse2 <= 0) || !sse1 || !sse2) continue;
28333
+ vec_cts1.push(sse1);
28334
+ vec_cts2.push(sse2);
28335
+ if (sse1 == sse2) continue;
28336
+ vec_cts1a.push(sse1);
28337
+ vec_cts2a.push(sse2);
28338
+ }
28339
+
28340
+ // this symmetrizes the contact data
28341
+ for (let i = 0; i < vec_cts1a.length; i++) {
28342
+ vec_cts1.push(vec_cts2a[i]);
28343
+ vec_cts2.push(vec_cts1a[i]);
28344
+ }
28345
+
28346
+ // add dummy contacts
28347
+ for (let i = 0; i < nsse; i++) {
28348
+ vec_cts1.push(i + 1);
28349
+ vec_cts2.push(i + 1);
28350
+ }
28351
+
28352
+ // create contact counts from the contacts/interactions
28353
+ //map< pair< int, let >, let > ctable = this.c2b_ContactTable(vec_cts1, vec_cts2);
28354
+ let ctable = this.c2b_ContactTable(vec_cts1, vec_cts2);
28355
+
28356
+ // neighbor list of each sheet
28357
+ let sheetNeighbor = {};
28358
+ for(let pair in ctable) {
28359
+ let ssPair = pair.split('_'); // 1-based
28360
+ let ss1 = parseInt(ssPair[0]);
28361
+ let ss2 = parseInt(ssPair[1]);
28362
+
28363
+ // both are sheets
28364
+ if(substruct[ss1 - 1].Sheet && substruct[ss2 - 1].Sheet) {
28365
+ if(!sheetNeighbor[ss1]) sheetNeighbor[ss1] = {};
28366
+ if(!sheetNeighbor[ss2]) sheetNeighbor[ss2] = {};
28367
+
28368
+ sheetNeighbor[ss1][ss2] = 1;
28369
+ sheetNeighbor[ss2][ss1] = 1;
28370
+ }
28371
+ }
28372
+
28373
+ //https://www.geeksforgeeks.org/number-groups-formed-graph-friends/
28374
+ let existing_groups = 0;
28375
+ let sheet2sheetnum = {};
28376
+ this.groupnum2sheet = {};
28377
+ this.visited = {};
28378
+ for (let ss1 in sheetNeighbor) {
28379
+ this.visited[ss1] = false;
28380
+ }
28381
+
28382
+ // get this.groupnum2sheet
28383
+ for (let ss1 in sheetNeighbor) {
28384
+ // If not in any group.
28385
+ if (this.visited[ss1] == false) {
28386
+ existing_groups++;
28387
+
28388
+ this.countUtil(ss1, sheetNeighbor, existing_groups);
28389
+ }
28390
+ }
28391
+
28392
+ // get sheet2sheetnum
28393
+ // each neighboring sheet willbe represented by the sheet with the smallest sse
28394
+ for(let groupnum in this.groupnum2sheet) {
28395
+ let ssArray = this.groupnum2sheet[groupnum].sort();
28396
+ for(let i = 0, il = ssArray.length; i < il; ++i) {
28397
+ sheet2sheetnum[ssArray[i]] = ssArray[0];
28398
+ }
28399
+ }
28400
+
28401
+ for (let i = 0; i < nsse; i++) {
28402
+ if(substruct[i].Sheet) {
28403
+ let sheetsItem = {};
28404
+ if(sheet2sheetnum[i+1]) {
28405
+ sheetsItem.sheet_num = sheet2sheetnum[i+1];
28406
+ sheetsItem.adj_strand2 = 1;
28407
+ sheetsItem.sse = i + 1;
28408
+ }
28409
+ else {
28410
+ sheetsItem.sheet_num = 0;
28411
+ sheetsItem.adj_strand2 = 0;
28412
+ sheetsItem.sse = i + 1;
28413
+ }
28414
+
28415
+ sheets.push(sheetsItem);
28416
+ }
28417
+ }
28418
+
28419
+ //
28420
+ // Correct for dummy contacts; they're present to ensure that the
28421
+ // table gives the right result in the possible case there is an
28422
+ // element with no contacts.
28423
+ //
28424
+ for (let i = 0; i < nsse; i++) {
28425
+ for (let j = 0; j < nsse; j++) {
28426
+ //pair<int, int> epr;
28427
+ //let epr = {};
28428
+ //epr.first = i + 1;
28429
+ //epr.second = j + 1;
28430
+ let epr = (i+1).toString() + '_' + (j+1).toString();
28431
+
28432
+ //if (ctable.count(epr) == 0)
28433
+ if (!ctable[epr])
28434
+ this.ctc_cnt[i][j] = 0;
28435
+ else {
28436
+ let cnt = ctable[epr];
28437
+ if (i == j) cnt--; // subtract dummy contact
28438
+ this.ctc_cnt[i][j] = cnt;
28439
+ this.ctc_cnt[j][i] = cnt;
28440
+ }
28441
+ }
28442
+ }
28443
+
28444
+ let minStrand = 6;
28445
+
28446
+ if (hasSheets) {
28447
+ //sheets: array of sheets, each of which has the key: sheet_num (number of strands), adj_strand1, adj_strand2
28448
+
28449
+ let cnt = 0;
28450
+
28451
+ for (let i = 0; i < sheets.length; i++) {
28452
+ //BetaSheet_Rec bsrec = sheets[i];
28453
+ let bsrec = sheets[i];
28454
+
28455
+ //if ((bsrec.sheet_num > 0) && (this.elt_size[i] >= minStrand) && (bsrec.adj_strand2 != 0))
28456
+ if ((bsrec.sheet_num > 0) && (this.elt_size[bsrec.sse - 1] >= minStrand) && (bsrec.adj_strand2 != 0))
28457
+ cnt++;
28458
+ }
28459
+
28460
+ for (let i = 0; i < nsse; i++) {
28461
+ //this.group_num[i] = (cnt == 0) ? i + 1 : 0;
28462
+ this.group_num[i] = i + 1;
28463
+ }
28464
+
28465
+ if (cnt> 0) {
28466
+ for (let i = 0; i < sheets.length; i++) {
28467
+ let bsrec = sheets[i];
28468
+ this.group_num[bsrec.sse - 1] = bsrec.sheet_num;
28469
+ }
28470
+ }
28471
+ }
28472
+ else {
28473
+ for (let i = 0; i < nsse; i++)
28474
+ this.group_num[i] = i + 1;
28475
+ }
28476
+
28477
+ let sratio = 0.25;
28478
+ let minSize = 25;
28479
+ let maxCsz = 4;
28480
+ let avgCts = 0.0;
28481
+ let ncFact = 0.0;
28482
+ let cDelta = 3;
28483
+ let minSSE = 3;
28484
+
28485
+ // call the domain splitter
28486
+ this.parts = [];
28487
+ this.parts.length = 2*this.MAX_SSE;
28488
+ let ratios = [];
28489
+ ratios.length = this.MAX_SSE;
28490
+ let n_saved = 0;
28491
+
28492
+ for (let i = 0; i < nsse; i++) {
28493
+ this.parts[2*i] = this.parts[2*i + 1] = 0;
28494
+ ratios[i] = 0.0;
28495
+ }
28496
+
28497
+ n_saved = this.new_split_chain(nsse, sratio, minSize, minSSE, maxCsz, avgCts, cDelta, ncFact, this.parts, n_saved, ratios);
28498
+
28499
+ // save domain data
28500
+ //list< vector< let > > list_parts;
28501
+ let list_parts = [];
28502
+
28503
+ if (n_saved > 0) {
28504
+ // splits occurred...
28505
+ let j = 0;
28506
+
28507
+ for (let i = 0; i <= n_saved; i++) {
28508
+ //vector<int> sselst;
28509
+ let sselst = [];
28510
+ //sselst.clear();
28511
+
28512
+ while (j < 2*nsse) {
28513
+ let sse0 = this.parts[j++];
28514
+
28515
+ if (sse0 == 0) {
28516
+ list_parts.push(sselst);
28517
+ break;
28518
+ }
28519
+ else
28520
+ sselst.push(sse0);
28521
+ }
28522
+ }
28523
+ }
28524
+
28525
+ list_parts.sort(function(v1, v2) {
28526
+ return v1[0] - v2[0];
28527
+ });
28528
+
28529
+ // determine residue ranges for each subdomain
28530
+ let subdomains = [];
28531
+
28532
+ //for (lplet = list_parts.begin(); lplet != list_parts.end(); lpint++) {
28533
+ for (let index = 0, indexl = list_parts.length; index < indexl; ++index) {
28534
+ //vector<int> prts = *lpint;
28535
+ let prts = list_parts[index];
28536
+ //vector<int> resflags;
28537
+ //resflags.clear();
28538
+ let resflags = [];
28539
+
28540
+ // a domain must have at least 3 SSEs...
28541
+ if (prts.length <= 2) continue;
28542
+
28543
+ for (let i = 0; i < seqLen; i++)
28544
+ resflags.push(0);
28545
+
28546
+ for (let i = 0; i < prts.length; i++) {
28547
+ let k = prts[i] - 1;
28548
+
28549
+ if ((k < 0) || (k >= substruct.length)) {
28550
+ return;
28551
+ }
28552
+
28553
+ //SSE_Rec sserec = substruct[k];
28554
+ let sserec = substruct[k];
28555
+ let From = sserec.From;
28556
+ let To = sserec.To;
28557
+
28558
+ for (let j = From; j <= To; j++)
28559
+ resflags[j - 1] = 1;
28560
+
28561
+ if ((k == 0) && (From > 1)) {
28562
+ // residues with negative residue numbers will not be included
28563
+ for (let j = 1; j < From; j++)
28564
+ resflags[j - 1] = 1;
28565
+ }
28566
+
28567
+ if ((k == substruct.length - 1) && (To < seqLen)) {
28568
+ for (let j = To + 1; j <= seqLen; j++)
28569
+ resflags[j - 1] = 1;
28570
+ }
28571
+
28572
+ // left side
28573
+ if (k > 0) {
28574
+ //SSE_Rec sserec1 = substruct[k - 1];
28575
+ let sserec1 = substruct[k - 1];
28576
+ let To1 = sserec1.To;
28577
+ //let ll = (int) floor(0.5*((let) (From - To1 - 1)));
28578
+ let ll = parseInt(0.5 * (From - To1 - 1));
28579
+
28580
+ if (ll > 0) {
28581
+ for (let j = From - ll; j <= From - 1; j++)
28582
+ resflags[j - 1] = 1;
28583
+ }
28584
+ }
28585
+
28586
+ // right side
28587
+ if (k < substruct.length - 1) {
28588
+ //SSE_Rec sserec1 = substruct[k + 1];
28589
+ let sserec1 = substruct[k + 1];
28590
+ let From1 = sserec1.From;
28591
+ //let ll = (int) ceil(0.5*((let) (From1 - To - 1)));
28592
+ // let ft = From1 - To - 1;
28593
+ // let ll = parseInt(ft/2);
28594
+ // if (ft % 2 == 1) ll++;
28595
+ let ll = parseInt(0.5 * (From1 - To - 1) + 0.5);
28596
+
28597
+ if (ll > 0) {
28598
+ for (let j = To + 1; j <= To + ll; j++)
28599
+ resflags[j - 1] = 1;
28600
+ }
28601
+ }
28602
+ }
28603
+
28604
+ // extract the continguous segments
28605
+ let inseg = false;
28606
+ let startseg;
28607
+ //vector<int> segments;
28608
+ //segments.clear();
28609
+ let segments = [];
28610
+
28611
+ for (let i = 0; i < seqLen; i++) {
28612
+ let rf = resflags[i];
28613
+
28614
+ if (!inseg && (rf == 1)) {
28615
+ // new segment starts here
28616
+ startseg = i + 1;
28617
+ inseg = true;
28618
+ continue;
28619
+ }
28620
+
28621
+ if (inseg && (rf == 0)) {
28622
+ // segment ends
28623
+ segments.push(startseg);
28624
+ segments.push(i);
28625
+ inseg = false;
28626
+ }
28627
+ }
28628
+
28629
+ // check for the last segment
28630
+ if (inseg) {
28631
+ segments.push(startseg);
28632
+ segments.push(seqLen);
28633
+ }
28634
+
28635
+ subdomains.push(segments);
28636
+ }
28637
+
28638
+ //console.log("subdomains: " + JSON.stringify(subdomains));
28639
+
28640
+ return subdomains;
28641
+ } // end c2b_NewSplitChain
28642
+ }
28643
+
27584
28644
  /**
27585
28645
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
27586
28646
  */
@@ -29902,6 +30962,8 @@ class Picking {
29902
30962
  transformation.quaternion._y = parseFloat(ic.quaternion._y).toPrecision(5);
29903
30963
  transformation.quaternion._z = parseFloat(ic.quaternion._z).toPrecision(5);
29904
30964
  transformation.quaternion._w = parseFloat(ic.quaternion._w).toPrecision(5);
30965
+
30966
+ /*
29905
30967
  if(ic.bAddCommands) {
29906
30968
  ic.commands.push('pickatom ' + atom.serial + '|||' + ic.transformCls.getTransformationStr(transformation));
29907
30969
  ic.optsHistory.push(me.hashUtilsCls.cloneHash(ic.opts));
@@ -29913,6 +30975,9 @@ class Picking {
29913
30975
  if( $( "#" + ic.pre + "logtext" ).length ) {
29914
30976
  $("#" + ic.pre + "logtext").val("> " + ic.logs.join("\n> ") + "\n> ").scrollTop($("#" + ic.pre + "logtext")[0].scrollHeight);
29915
30977
  }
30978
+ */
30979
+ me.htmlCls.clickMenuCls.setLogCmd('pickatom ' + atom.serial, true);
30980
+
29916
30981
  // update the interaction flag
29917
30982
  ic.bSphereCalc = false;
29918
30983
  //me.htmlCls.clickMenuCls.setLogCmd('set calculate sphere false', true);
@@ -31289,11 +32354,16 @@ class ApplyCommand {
31289
32354
  ic.bGlycansCartoon = false;
31290
32355
  }
31291
32356
  }
32357
+ else if(command.indexOf('save html') == 0) {
32358
+ let id = command.substr(command.lastIndexOf(' ') + 1);
32359
+ me.htmlCls.eventsCls.saveHtml(id);
32360
+ }
31292
32361
 
31293
32362
  // special, select ==========
31294
32363
 
31295
32364
  else if(command.indexOf('select displayed set') !== -1) {
31296
- ic.hAtoms = me.hashUtilsCls.cloneHash(ic.dAtoms);
32365
+ //ic.hAtoms = me.hashUtilsCls.cloneHash(ic.dAtoms);
32366
+ ic.hAtoms = me.hashUtilsCls.cloneHash(ic.viewSelectionAtoms);
31297
32367
  ic.hlUpdateCls.updateHlAll();
31298
32368
  }
31299
32369
  else if(command.indexOf('select prop') !== -1) {
@@ -32140,11 +33210,12 @@ class Resid2spec {
32140
33210
 
32141
33211
  atoms2spec(atomHash) {var ic = this.icn3d; ic.icn3dui;
32142
33212
  let spec = "";
32143
-
32144
33213
  let i = 0;
32145
33214
  let structureHash = {}, chainHash = {}, resiHash = {};
33215
+
33216
+ let atom;
32146
33217
  for(let serial in atomHash) {
32147
- let atom = ic.atoms[serial];
33218
+ atom = ic.atoms[serial];
32148
33219
  if(i > 0) {
32149
33220
  spec += ' or ';
32150
33221
  }
@@ -35203,7 +36274,7 @@ class ShowAnno {
35203
36274
  // the missing residues at the end of the seq will be filled up in the API showNewTrack()
35204
36275
  let nGap = 0;
35205
36276
  ic.alnChainsSeq[chnid] = [];
35206
- let offset =(ic.chainid2offset[chnid]) ? ic.chainid2offset[chnid] : 0;
36277
+ let offset =(ic.chainid2offset[chnid]) ? ic.chainid2offset[chnid] : 0;
35207
36278
  for(let i = 0, il = targetSeq.length; i < il; ++i) {
35208
36279
  //text += ic.showSeqCls.insertGap(chnid, i, '-', true);
35209
36280
  if(ic.targetGapHash.hasOwnProperty(i)) {
@@ -35576,7 +36647,8 @@ class AnnoDomain {
35576
36647
  let pdbArray = Object.keys(ic.structures);
35577
36648
  // show 3D domains
35578
36649
  let pdbid = pdbArray[index];
35579
- let url = me.htmlCls.baseUrl + "mmdb/mmdb_strview.cgi?v=2&program=icn3d&domain&molinfor&uid=" + pdbid;
36650
+ me.htmlCls.baseUrl + "mmdb/mmdb_strview.cgi?v=2&program=icn3d&domain&molinfor&uid=" + pdbid;
36651
+
35580
36652
  if(index == 0 && ic.mmdb_data !== undefined) {
35581
36653
  for(let chnid in ic.protein_chainid) {
35582
36654
  if(chnid.indexOf(pdbid) !== -1) {
@@ -35592,6 +36664,43 @@ class AnnoDomain {
35592
36664
  }
35593
36665
  }
35594
36666
  else {
36667
+ // calculate 3D domains on-the-fly
36668
+ //ic.protein_chainid[chainArray[i]]
36669
+ let data = {};
36670
+ data.domains = {};
36671
+ for(let chainid in ic.chains) {
36672
+ let structure = chainid.substr(0, chainid.indexOf('_'));
36673
+ if(pdbid == structure && ic.protein_chainid.hasOwnProperty(chainid)) {
36674
+ data.domains[chainid] = {};
36675
+ data.domains[chainid].domains = [];
36676
+
36677
+ let subdomains = ic.domain3dCls.c2b_NewSplitChain(chainid);
36678
+ for(let i = 0, il = subdomains.length; i < il; ++i) {
36679
+ // domain item: {"sdid":1722375,"intervals":[[1,104],[269,323]]}
36680
+ let domain = {};
36681
+ domain.intervals = [];
36682
+
36683
+ for(let j = 0, jl = subdomains[i].length; j < jl; j += 2) {
36684
+ domain.intervals.push([subdomains[i][j], subdomains[i][j+1]]);
36685
+ }
36686
+
36687
+ data.domains[chainid].domains.push(domain);
36688
+ }
36689
+ }
36690
+ }
36691
+
36692
+ ic.mmdb_dataArray[index] = data;
36693
+ let bCalcDirect = true;
36694
+ for(let chnid in ic.protein_chainid) {
36695
+ if(chnid.indexOf(pdbid) !== -1) {
36696
+ thisClass.showDomainWithData(chnid, ic.mmdb_dataArray[index], bCalcDirect);
36697
+ }
36698
+ }
36699
+
36700
+ ic.bAjax3ddomain = true;
36701
+ ic.bAjaxDoneArray[index] = true;
36702
+
36703
+ /*
35595
36704
  $.ajax({
35596
36705
  url: url,
35597
36706
  dataType: 'json',
@@ -35655,6 +36764,7 @@ class AnnoDomain {
35655
36764
  return;
35656
36765
  }
35657
36766
  });
36767
+ */
35658
36768
  }
35659
36769
  }
35660
36770
 
@@ -35673,28 +36783,36 @@ class AnnoDomain {
35673
36783
  this.showDomainPerStructure(i);
35674
36784
  }
35675
36785
  }
35676
- showDomainWithData(chnid, data) { let ic = this.icn3d, me = ic.icn3dui;
36786
+ showDomainWithData(chnid, data, bCalcDirect) { let ic = this.icn3d, me = ic.icn3dui;
35677
36787
  let html = '<div id="' + ic.pre + chnid + '_domainseq_sequence" class="icn3d-dl_sequence">';
35678
36788
  let html2 = html;
35679
36789
  let html3 = html;
35680
36790
  let domainArray, proteinname;
35681
36791
  let pos = chnid.indexOf('_');
35682
36792
  let chain = chnid.substr(pos + 1);
35683
- let molinfo = data.moleculeInfor;
35684
- let currMolid;
35685
- for(let molid in molinfo) {
35686
- if(molinfo[molid].chain === chain) {
35687
- currMolid = molid;
35688
- proteinname = molinfo[molid].name;
35689
- break;
35690
- }
35691
- }
35692
- if(currMolid !== undefined && data.domains[currMolid] !== undefined) {
35693
- domainArray = data.domains[currMolid].domains;
36793
+
36794
+ if(bCalcDirect) {
36795
+ proteinname = chnid;
36796
+ domainArray = data.domains[chnid].domains;
35694
36797
  }
35695
- if(domainArray === undefined) {
35696
- domainArray = [];
36798
+ else {
36799
+ let molinfo = data.moleculeInfor;
36800
+ let currMolid;
36801
+ for(let molid in molinfo) {
36802
+ if(molinfo[molid].chain === chain) {
36803
+ currMolid = molid;
36804
+ proteinname = molinfo[molid].name;
36805
+ break;
36806
+ }
36807
+ }
36808
+ if(currMolid !== undefined && data.domains[currMolid] !== undefined) {
36809
+ domainArray = data.domains[currMolid].domains;
36810
+ }
36811
+ if(domainArray === undefined) {
36812
+ domainArray = [];
36813
+ }
35697
36814
  }
36815
+
35698
36816
  for(let index = 0, indexl = domainArray.length; index < indexl; ++index) {
35699
36817
  //var fulltitle = '3D domain ' +(index+1).toString() + ' of ' + proteinname + '(PDB ID: ' + data.pdbId + ')';
35700
36818
  let fulltitle = '3D domain ' +(index+1).toString() + ' of ' + proteinname;
@@ -35717,7 +36835,7 @@ class AnnoDomain {
35717
36835
  }
35718
36836
 
35719
36837
  // use the NCBI residue number, and convert to PDB residue number during selection
35720
- if(ic.bNCBI) {
36838
+ if(ic.bNCBI || bCalcDirect) {
35721
36839
  fromArray.push(domainFrom);
35722
36840
  toArray.push(domainTo);
35723
36841
  }
@@ -38507,8 +39625,11 @@ class Selection {
38507
39625
 
38508
39626
  for(let i in ic.chains) {
38509
39627
  ic.hAtoms = me.hashUtilsCls.unionHash(ic.hAtoms, ic.chains[i]);
38510
- ic.dAtoms = me.hashUtilsCls.unionHash(ic.dAtoms, ic.chains[i]);
39628
+ //ic.dAtoms = me.hashUtilsCls.unionHash(ic.dAtoms, ic.chains[i]);
38511
39629
  }
39630
+
39631
+ ic.dAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
39632
+ ic.viewSelectionAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
38512
39633
  }
38513
39634
 
38514
39635
  //Select a chain with the chain id "chainid" in the sequence dialog and save it as a custom selection with the name "commandname".
@@ -38743,6 +39864,7 @@ class Selection {
38743
39864
  if(Object.keys(ic.hAtoms).length == 0) this.selectAll_base();
38744
39865
 
38745
39866
  ic.dAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
39867
+ ic.viewSelectionAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
38746
39868
 
38747
39869
  let centerAtomsResults = ic.applyCenterCls.centerAtoms(me.hashUtilsCls.hash2Atoms(ic.dAtoms, ic.atoms));
38748
39870
  ic.maxD = centerAtomsResults.maxD;
@@ -40999,8 +42121,9 @@ class ApplySsbonds {
40999
42121
  let start, end;
41000
42122
 
41001
42123
  if(ic.bAlternate) {
41002
- start = ic.ALTERNATE_STRUCTURE;
41003
- end = ic.ALTERNATE_STRUCTURE + 1;
42124
+ let nStructures = structureArray.length;
42125
+ start = ic.ALTERNATE_STRUCTURE % nStructures;
42126
+ end = ic.ALTERNATE_STRUCTURE % nStructures + 1;
41004
42127
  }
41005
42128
  else {
41006
42129
  start = 0;
@@ -44291,7 +45414,8 @@ class ClickMenu {
44291
45414
  // clkMn2_selectdisplayed: function() {
44292
45415
  me.myEventCls.onIds("#" + me.pre + "mn2_selectdisplayed", "click", function(e) { let ic = me.icn3d;
44293
45416
  thisClass.setLogCmd("select displayed set", true);
44294
- ic.hAtoms = me.hashUtilsCls.cloneHash(ic.dAtoms);
45417
+ //ic.hAtoms = me.hashUtilsCls.cloneHash(ic.dAtoms);
45418
+ ic.hAtoms = me.hashUtilsCls.cloneHash(ic.viewSelectionAtoms);
44295
45419
  ic.hlUpdateCls.updateHlAll();
44296
45420
  //ic.drawCls.draw();
44297
45421
  });
@@ -45412,6 +46536,10 @@ class ClickMenu {
45412
46536
  // },
45413
46537
  // clkMn6_sidebyside: function() {
45414
46538
  me.myEventCls.onIds("#" + me.pre + "mn6_sidebyside", "click", function(e) { let ic = me.icn3d;
46539
+ if(ic.bInputfile) {
46540
+ var aaa = 1; //alert("Side-by-Side does NOT work when the input is from a local file.");
46541
+ return;
46542
+ }
45415
46543
  let url = ic.shareLinkCls.shareLinkUrl(undefined);
45416
46544
  //if(url.indexOf('http') !== 0) {
45417
46545
  // var aaa = 1; //alert("The url is more than 4000 characters and may not work.");
@@ -46019,6 +47147,8 @@ class SetMenu {
46019
47147
  // show title at the top left corner
46020
47148
  html += me.htmlCls.divStr + "title' class='icn3d-commandTitle' style='font-size:1.2em; font-weight:normal; position:absolute; z-index:1; float:left; display:block; margin: 12px 0px 0px 40px; color:" + titleColor + "; width:" +(me.htmlCls.WIDTH - 40).toString() + "px'></div>";
46021
47149
  html += me.htmlCls.divStr + "viewer' style='position:relative; width:100%; height:100%; background-color: " + me.htmlCls.GREYD + ";'>";
47150
+ // don't show legend in mobile
47151
+ //html += me.htmlCls.divStr + "legend' class='icn3d-text icn3d-legend'></div>";
46022
47152
  html += me.htmlCls.divStr + "mnLogSection'>";
46023
47153
  html += "<div style='height: " + me.htmlCls.MENU_HEIGHT + "px;'></div>";
46024
47154
  html += "</div>";
@@ -49176,6 +50306,18 @@ class Events {
49176
50306
  }
49177
50307
  }
49178
50308
 
50309
+ saveHtml(id) { let me = this.icn3dui, ic = me.icn3d;
50310
+ let html = '';
50311
+ html += '<link rel="stylesheet" href="https:///structure.ncbi.nlm.nih.gov/icn3d/lib/jquery-ui-1.12.1.min.css">\n';
50312
+ html += '<link rel="stylesheet" href="https:///structure.ncbi.nlm.nih.gov/icn3d/icn3d_full_ui.css">\n';
50313
+ html += $("#" + id).html();
50314
+ let idArray = id.split('_');
50315
+ let idStr =(idArray.length > 2) ? idArray[2] : id;
50316
+ let structureStr = Object.keys(ic.structures)[0];
50317
+ if(Object.keys(ic.structures).length > 1) structureStr += '-' + Object.keys(ic.structures)[1];
50318
+ ic.saveFileCls.saveFile(structureStr + '-' + idStr + '.html', 'html', encodeURIComponent(html));
50319
+ }
50320
+
49179
50321
  //Hold all functions related to click events.
49180
50322
  allEventFunctions() { let me = this.icn3dui, ic = me.icn3d;
49181
50323
  let thisClass = this;
@@ -50837,18 +51979,12 @@ class Events {
50837
51979
  });
50838
51980
  // },
50839
51981
  // clickSaveDialog: function() {
50840
- $(document).on("click", ".icn3d-saveicon", function(e) { let ic = me.icn3d;
51982
+ $(document).on("click", ".icn3d-saveicon", function(e) { me.icn3d;
50841
51983
  e.stopImmediatePropagation();
50842
51984
  let id = $(this).attr('pid');
50843
- let html = '';
50844
- html += '<link rel="stylesheet" href="https:///structure.ncbi.nlm.nih.gov/icn3d/lib/jquery-ui-1.12.1.min.css">\n';
50845
- html += '<link rel="stylesheet" href="https:///structure.ncbi.nlm.nih.gov/icn3d/icn3d_full_ui.css">\n';
50846
- html += $("#" + id).html();
50847
- let idArray = id.split('_');
50848
- let idStr =(idArray.length > 2) ? idArray[2] : id;
50849
- let structureStr = Object.keys(ic.structures)[0];
50850
- if(Object.keys(ic.structures).length > 1) structureStr += '-' + Object.keys(ic.structures)[1];
50851
- ic.saveFileCls.saveFile(structureStr + '-' + idStr + '.html', 'html', encodeURIComponent(html));
51985
+
51986
+ thisClass.saveHtml(id);
51987
+ me.htmlCls.clickMenuCls.setLogCmd("save html " + id, true);
50852
51988
  });
50853
51989
  // },
50854
51990
  // clickHideDialog: function() {
@@ -52363,38 +53499,66 @@ class Alternate {
52363
53499
  // change the display atom when alternating
52364
53500
  //Show structures one by one.
52365
53501
  alternateStructures() { let ic = this.icn3d, me = ic.icn3dui;
52366
- let hAtomsCount = Object.keys(ic.hAtoms).length;
53502
+ if(!ic.viewSelectionAtoms) {
53503
+ ic.viewSelectionAtoms = me.hashUtilsCls.cloneHash(ic.dAtoms);
53504
+ }
53505
+
53506
+ let viewSelectionAtomsCount = Object.keys(ic.viewSelectionAtoms).length;
52367
53507
  let allAtomsCount = Object.keys(ic.atoms).length;
52368
53508
 
52369
53509
  ic.dAtoms = {};
52370
53510
 
53511
+ // alternate all displayed structures
53512
+ let moleculeArray = Object.keys(ic.structures);
52371
53513
  // only alternate selected structures
52372
- //let moleculeArray = Object.keys(ic.structures);
52373
- let structureHash = {};
52374
- for(let i in ic.hAtoms) {
52375
- let structure = ic.atoms[i].structure;
52376
- structureHash[structure] = 1;
52377
- }
52378
- let moleculeArray = Object.keys(structureHash);
53514
+ // let structureHash = {};
53515
+ // for(let i in ic.hAtoms) {
53516
+ // let structure = ic.atoms[i].structure;
53517
+ // structureHash[structure] = 1;
53518
+ // }
53519
+ // let moleculeArray = Object.keys(structureHash);
52379
53520
 
52380
53521
  for(let i = 0, il = moleculeArray.length; i < il; ++i) {
52381
53522
  let structure = moleculeArray[i];
52382
- if(i > ic.ALTERNATE_STRUCTURE || (ic.ALTERNATE_STRUCTURE === il - 1 && i === 0) ) {
53523
+ //if(i > ic.ALTERNATE_STRUCTURE || (ic.ALTERNATE_STRUCTURE === il - 1 && i === 0) ) {
53524
+ let bChoose;
53525
+ if(ic.bShift) {
53526
+ // default ic.ALTERNATE_STRUCTURE = -1
53527
+ if(ic.ALTERNATE_STRUCTURE < 0) ic.ALTERNATE_STRUCTURE = 1;
53528
+
53529
+ bChoose = (i == ic.ALTERNATE_STRUCTURE % il - 1)
53530
+ || (ic.ALTERNATE_STRUCTURE % il === 0 && i === il - 1);
53531
+ }
53532
+ else {
53533
+ bChoose = (i == ic.ALTERNATE_STRUCTURE % il + 1)
53534
+ || (ic.ALTERNATE_STRUCTURE % il === il - 1 && i === 0);
53535
+ }
53536
+
53537
+ if(bChoose) {
52383
53538
  for(let k in ic.structures[structure]) {
52384
53539
  let chain = ic.structures[structure][k];
52385
53540
  ic.dAtoms = me.hashUtilsCls.unionHash(ic.dAtoms, ic.chains[chain]);
52386
53541
  }
52387
53542
 
52388
- ic.ALTERNATE_STRUCTURE = i;
53543
+ //ic.ALTERNATE_STRUCTURE = i;
53544
+ if(ic.bShift) {
53545
+ --ic.ALTERNATE_STRUCTURE;
53546
+ }
53547
+ else {
53548
+ ++ic.ALTERNATE_STRUCTURE;
53549
+ }
53550
+
53551
+ if(ic.ALTERNATE_STRUCTURE < 0) ic.ALTERNATE_STRUCTURE += il;
52389
53552
 
52390
53553
  $("#" + ic.pre + "title").html(structure);
52391
53554
 
52392
53555
  break;
52393
53556
  }
52394
- }
53557
+ }
52395
53558
 
52396
- if(hAtomsCount < allAtomsCount) {
52397
- ic.dAtoms = me.hashUtilsCls.intHash(ic.dAtoms, ic.hAtoms);
53559
+ if(viewSelectionAtomsCount < allAtomsCount) {
53560
+ //ic.dAtoms = me.hashUtilsCls.intHash(ic.dAtoms, ic.hAtoms);
53561
+ ic.dAtoms = me.hashUtilsCls.intHash(ic.dAtoms, ic.viewSelectionAtoms);
52398
53562
 
52399
53563
  ic.bShowHighlight = false;
52400
53564
  ic.opts['rotationcenter'] = 'highlight center';
@@ -54927,7 +56091,7 @@ class Control {
54927
56091
 
54928
56092
  else if(e.keyCode === 65 ) { // A, alternate
54929
56093
  if(Object.keys(ic.structures).length > 1) {
54930
- ic.alternateCls.alternateWrapper();
56094
+ ic.alternateCls.alternateWrapper();
54931
56095
  }
54932
56096
  }
54933
56097
 
@@ -55474,6 +56638,7 @@ class iCn3D {
55474
56638
  this.annoSnpClinVarCls = new AnnoSnpClinVar(this);
55475
56639
  this.annoSsbondCls = new AnnoSsbond(this);
55476
56640
  this.annoTransMemCls = new AnnoTransMem(this);
56641
+ this.domain3dCls = new Domain3d(this);
55477
56642
 
55478
56643
  this.addTrackCls = new AddTrack(this);
55479
56644
  this.annotationCls = new Annotation(this);
@@ -55726,7 +56891,7 @@ class iCn3DUI {
55726
56891
  //even when multiple iCn3D viewers are shown together.
55727
56892
  this.pre = this.cfg.divid + "_";
55728
56893
 
55729
- this.REVISION = '3.8.2';
56894
+ this.REVISION = '3.9.0';
55730
56895
 
55731
56896
  // In nodejs, iCn3D defines "window = {navigator: {}}"
55732
56897
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -56241,6 +57406,7 @@ exports.Delphi = Delphi;
56241
57406
  exports.DensityCifParser = DensityCifParser;
56242
57407
  exports.Diagram2d = Diagram2d;
56243
57408
  exports.Dialog = Dialog;
57409
+ exports.Domain3d = Domain3d;
56244
57410
  exports.Draw = Draw;
56245
57411
  exports.DrawGraph = DrawGraph;
56246
57412
  exports.Dsn6Parser = Dsn6Parser;