icn3d 3.12.3 → 3.12.7

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.
package/icn3d.js CHANGED
@@ -11762,7 +11762,7 @@ class ShareLink {
11762
11762
  if(bPngHtml) url += "&random=" + parseInt(Math.random() * 1000); // generate a new shorten URL and thus image name everytime
11763
11763
  //var inputid =(ic.inputid) ? ic.inputid : "custom";
11764
11764
  let inputid = Object.keys(ic.structures).join('_');
11765
- if(inputid == 'STRU') {
11765
+ if(inputid == 'stru') {
11766
11766
  if(ic.filename) {
11767
11767
  inputid = ic.filename;
11768
11768
  }
@@ -16811,7 +16811,7 @@ class ViewInterPairs {
16811
16811
  ic.bGraph = true;
16812
16812
  // show only displayed set in 2D graph
16813
16813
  if(Object.keys(atomSet2).length + Object.keys(atomSet1).length > Object.keys(ic.dAtoms).length) {
16814
- ic.graphStr = ic.getGraphCls.getGraphDataForDisplayed();
16814
+ ic.graphStr = ic.selectionCls.getGraphDataForDisplayed();
16815
16815
  }
16816
16816
 
16817
16817
  if(ic.bD3 === undefined) {
@@ -19494,13 +19494,14 @@ class Scap {
19494
19494
  //snp: 6M0J_E_484_K,6M0J_E_501_Y,6M0J_E_417_N
19495
19495
  let snpStr = '';
19496
19496
  let snpArray = snp.split(','); //stru_chain_resi_snp
19497
- let atomHash = {}, snpResidArray = [];
19497
+ let atomHash = {}, snpResidArray = [], chainResi2pdb = {};
19498
19498
  for(let i = 0, il = snpArray.length; i < il; ++i) {
19499
19499
  let idArray = snpArray[i].split('_'); //stru_chain_resi_snp
19500
19500
 
19501
19501
  let resid = idArray[0] + '_' + idArray[1] + '_' + idArray[2];
19502
19502
  atomHash = me.hashUtilsCls.unionHash(atomHash, ic.residues[resid]);
19503
19503
  snpResidArray.push(resid);
19504
+ chainResi2pdb[idArray[1] + '_' + idArray[2]] = '';
19504
19505
 
19505
19506
  snpStr += idArray[1] + '_' + idArray[2] + '_' + idArray[3];
19506
19507
  if(i != il -1) snpStr += ',';
@@ -19534,7 +19535,7 @@ class Scap {
19534
19535
  /// let pdbStr = ic.saveFileCls.getPDBHeader() + ic.saveFileCls.getAtomPDB(ic.hAtoms);
19535
19536
  let pdbStr = ic.saveFileCls.getAtomPDB(ic.hAtoms);
19536
19537
 
19537
- let url = "https://www.ncbi.nlm.nih.gov/Structure/scap/scap.cgi";
19538
+ let url = me.htmlCls.baseUrl + "scap/scap.cgi";
19538
19539
 
19539
19540
  let pdbid = Object.keys(ic.structures)[0]; //Object.keys(ic.structures).toString();
19540
19541
  let dataObj = {'pdb': pdbStr, 'snp': snpStr, 'pdbid': pdbid, 'v': '2'};
@@ -19574,19 +19575,64 @@ class Scap {
19574
19575
  ic.chainsMapping[chainid][resid] = me.utilsCls.residueName2Abbr(atom.resn) + atom.resi;
19575
19576
  }
19576
19577
 
19578
+ //ic.hAtoms = {};
19579
+ //ic.loadPDBCls.loadPDB(pdbData, pdbid, false, false, bAddition);
19580
+ //let hAtom2 = me.hashUtilsCls.cloneHash(ic.hAtoms);
19581
+
19582
+ // get the mutant pdb
19583
+ let lines = pdbData.split('\n');
19584
+ let allChainResiHash = {};
19585
+ for (let i in lines) {
19586
+ let line = lines[i];
19587
+ let record = line.substr(0, 6);
19588
+
19589
+ if (record === 'ATOM ' || record === 'HETATM') {
19590
+ let chain = line.substr(20, 2).trim();
19591
+ if(chain === '') chain = 'A';
19592
+
19593
+ let resi = line.substr(22, 5).trim();
19594
+ let chainResi = chain + '_' + resi;
19595
+
19596
+ if(chainResi2pdb.hasOwnProperty(chainResi)) {
19597
+ chainResi2pdb[chainResi] += line + '\n';
19598
+ }
19599
+
19600
+ allChainResiHash[chainResi] = 1;
19601
+ }
19602
+ }
19603
+
19604
+ // get the full mutatnt PDB
19605
+ let pdbDataMutant = ic.saveFileCls.getAtomPDB(ic.atoms, false, false, false, chainResi2pdb);
19577
19606
  ic.hAtoms = {};
19578
- ic.loadPDBCls.loadPDB(pdbData, pdbid, false, false, bAddition);
19579
- let hAtom2 = me.hashUtilsCls.cloneHash(ic.hAtoms);
19607
+ let bMutation = true;
19608
+ ic.loadPDBCls.loadPDB(pdbDataMutant, pdbid, false, false, bMutation, bAddition);
19609
+ //let allAtoms2 = me.hashUtilsCls.cloneHash(ic.hAtoms);
19610
+
19611
+ ic.setStyleCls.setAtomStyleByOptions(ic.opts);
19612
+ ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
19613
+
19614
+ // get the mutant residues in the sphere
19615
+ let hAtom2 = {};
19616
+ for(let serial in ic.hAtoms) {
19617
+ let atom = ic.atoms[serial];
19618
+ let chainResi = atom.chain + '_' + atom.resi;
19619
+ if(allChainResiHash.hasOwnProperty(chainResi)) {
19620
+ hAtom2[serial] = 1;
19621
+ }
19622
+ }
19580
19623
 
19581
- ic.hAtoms = me.hashUtilsCls.unionHash(ic.hAtoms, hAtom1);
19582
- ic.dAtoms = ic.hAtoms;
19624
+ ic.hAtoms = me.hashUtilsCls.unionHash(hAtom1, hAtom2);
19625
+ //ic.hAtoms = me.hashUtilsCls.unionHash(hAtom1, allAtoms2);
19626
+ ic.dAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
19627
+ //ic.dAtoms = ic.hAtoms;
19583
19628
 
19584
19629
  ic.transformCls.zoominSelection();
19585
19630
  ic.setOptionCls.setStyle('proteins', 'stick');
19586
19631
 
19587
- ic.opts['color'] = 'chain';
19588
- ic.setColorCls.setColorByOptions(ic.opts, ic.dAtoms);
19632
+ //ic.opts['color'] = 'chain';
19633
+ //ic.setColorCls.setColorByOptions(ic.opts, ic.dAtoms);
19589
19634
  for(let serial in hAtom2) {
19635
+ //for(let serial in allAtoms2) {
19590
19636
  let atom = ic.atoms[serial];
19591
19637
  if(!atom.het) {
19592
19638
  // use the same color as the wild type
@@ -19613,13 +19659,19 @@ class Scap {
19613
19659
  }
19614
19660
  }
19615
19661
 
19662
+ // ic.hAtoms = me.hashUtilsCls.unionHash(hAtom1, hAtoms2);
19663
+ // ic.dAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
19664
+ // //ic.dAtoms = ic.hAtoms;
19665
+
19666
+ // ic.transformCls.zoominSelection();
19667
+ // ic.setOptionCls.setStyle('proteins', 'stick');
19668
+
19616
19669
  if(bPdb) {
19617
- let pdbStr = '';
19618
- /// pdbStr += ic.saveFileCls.getPDBHeader();
19619
- pdbStr += ic.saveFileCls.getAtomPDB(ic.hAtoms);
19670
+ //let pdbStr = '';
19671
+ //pdbStr += ic.saveFileCls.getAtomPDB(ic.hAtoms);
19620
19672
 
19621
19673
  let file_pref =(ic.inputid) ? ic.inputid : "custom";
19622
- ic.saveFileCls.saveFile(file_pref + '_' + snpStr + '.pdb', 'text', [pdbStr]);
19674
+ ic.saveFileCls.saveFile(file_pref + '_' + snpStr + '.pdb', 'text', [pdbDataMutant]);
19623
19675
 
19624
19676
  ic.drawCls.draw();
19625
19677
  }
@@ -20320,23 +20372,23 @@ class LoadPDB {
20320
20372
  }
20321
20373
  else {
20322
20374
  // remove the last structure
20323
- if(ic.alertAlt) {
20324
- let nStru = ic.oriNStru + 1; //Object.keys(ic.structures).length;
20325
- let chainArray = ic.structures[nStru - 1];
20326
- for(let i = 0, il = (chainArray) ? chainArray.length : 0; i < il; ++i) {
20327
- for(let j in ic.chains[chainArray[i]]) {
20328
- delete ic.atoms[j];
20329
- delete ic.hAtoms[j];
20330
- delete ic.dAtoms[j];
20331
- }
20332
- delete ic.chains[chainArray[i]];
20333
- }
20334
-
20335
- delete ic.structures[nStru - 1];
20336
- }
20337
- else {
20375
+ // if(ic.alertAlt) {
20376
+ // let nStru = ic.oriNStru + 1; //Object.keys(ic.structures).length;
20377
+ // let chainArray = ic.structures[nStru - 1];
20378
+ // for(let i = 0, il = (chainArray) ? chainArray.length : 0; i < il; ++i) {
20379
+ // for(let j in ic.chains[chainArray[i]]) {
20380
+ // delete ic.atoms[j];
20381
+ // delete ic.hAtoms[j];
20382
+ // delete ic.dAtoms[j];
20383
+ // }
20384
+ // delete ic.chains[chainArray[i]];
20385
+ // }
20386
+
20387
+ // delete ic.structures[nStru - 1];
20388
+ // }
20389
+ // else {
20338
20390
  ic.oriNStru = (ic.structures) ? Object.keys(ic.structures).length : 0;
20339
- }
20391
+ // }
20340
20392
 
20341
20393
  moleculeNum = ic.oriNStru + 1; //Object.keys(ic.structures).length + 1;
20342
20394
  // Concatenation of two pdbs will have several atoms for the same serial
@@ -20353,12 +20405,12 @@ class LoadPDB {
20353
20405
 
20354
20406
  //let chainMissingResidueArray = {}
20355
20407
 
20356
- let id = (pdbid) ? pdbid : 'STRU';
20408
+ let id = (pdbid) ? pdbid : 'stru';
20357
20409
 
20358
20410
  let prevMissingChain = '';
20359
20411
  let CSerial, prevCSerial, OSerial, prevOSerial;
20360
20412
 
20361
- let structure = "STRU";
20413
+ let structure = "stru";
20362
20414
 
20363
20415
  let bHeader = false;
20364
20416
 
@@ -20374,18 +20426,19 @@ class LoadPDB {
20374
20426
 
20375
20427
  if(id == '') {
20376
20428
  if(bAppend) {
20377
- id = "STRU";
20429
+ id = "stru";
20378
20430
  }
20379
20431
  else {
20380
- //if(!ic.inputid) ic.inputid = 'STRU';
20381
- id = (ic.inputid && ic.inputid.indexOf('/') == -1) ? ic.inputid.substr(0, 10) : "STRU"; //ic.filename.substr(0, 4);
20432
+ //if(!ic.inputid) ic.inputid = 'stru';
20433
+ id = (ic.inputid && ic.inputid.indexOf('/') == -1) ? ic.inputid.substr(0, 10) : "stru"; //ic.filename.substr(0, 4);
20382
20434
  }
20383
20435
  }
20384
20436
 
20385
20437
  structure = id;
20386
20438
 
20387
- //if(id == 'STRU' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
20388
- if(id == 'STRU' || bMutation) { // bMutation: side chain prediction
20439
+ //if(id == 'stru' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
20440
+ //if(id == 'stru' || bMutation) { // bMutation: side chain prediction
20441
+ if(id == 'stru') {
20389
20442
  structure = (moleculeNum === 1) ? id : id + moleculeNum.toString();
20390
20443
  }
20391
20444
 
@@ -20455,17 +20508,17 @@ class LoadPDB {
20455
20508
  ic.ssbondpnts[id].push(resid1);
20456
20509
  ic.ssbondpnts[id].push(resid2);
20457
20510
  } else if (record === 'REMARK') {
20458
- let type = parseInt(line.substr(7, 3));
20511
+ let remarkType = parseInt(line.substr(7, 3));
20459
20512
 
20460
20513
  if(line.indexOf('1/2 of bilayer thickness:') !== -1) { // OPM transmembrane protein
20461
20514
  ic.halfBilayerSize = parseFloat(line.substr(line.indexOf(':') + 1).trim());
20462
20515
  }
20463
- else if (type == 210) {
20516
+ else if (remarkType == 210) {
20464
20517
  if((line.substr(11, 32).trim() == 'EXPERIMENT TYPE') && line.substr(45).trim() == 'NMR') {
20465
20518
  bNMR = true;
20466
20519
  }
20467
20520
  }
20468
- else if (type == 350 && line.substr(13, 5) == 'BIOMT') {
20521
+ else if (remarkType == 350 && line.substr(13, 5) == 'BIOMT') {
20469
20522
  let n = parseInt(line[18]) - 1;
20470
20523
  //var m = parseInt(line.substr(21, 2));
20471
20524
  let m = parseInt(line.substr(21, 2)) - 1; // start from 1
@@ -20477,7 +20530,7 @@ class LoadPDB {
20477
20530
  ic.biomtMatrices[m].elements[n + 12] = parseFloat(line.substr(54, 14));
20478
20531
  }
20479
20532
  // missing residues
20480
- else if (type == 465 && line.substr(18, 1) == ' ' && line.substr(20, 1) == ' ' && line.substr(21, 1) != 'S') {
20533
+ else if (remarkType == 465 && line.substr(18, 1) == ' ' && line.substr(20, 1) == ' ' && line.substr(21, 1) != 'S') {
20481
20534
  let resn = line.substr(15, 3);
20482
20535
  //let chain = line.substr(19, 1);
20483
20536
  let chain = line.substr(18, 2).trim();
@@ -20503,7 +20556,7 @@ class LoadPDB {
20503
20556
  }
20504
20557
 
20505
20558
  }
20506
- else if (type == 900 && ic.emd === undefined && line.substr(34).trim() == 'RELATED DB: EMDB') {
20559
+ else if (remarkType == 900 && ic.emd === undefined && line.substr(34).trim() == 'RELATED DB: EMDB') {
20507
20560
  //REMARK 900 RELATED ID: EMD-3906 RELATED DB: EMDB
20508
20561
  ic.emd = line.substr(23, 11).trim();
20509
20562
  }
@@ -20513,11 +20566,12 @@ class LoadPDB {
20513
20566
  ic.organism = ic.organism.substr(0, ic.organism.length - 1);
20514
20567
  } else if (record === 'ENDMDL') {
20515
20568
  ++moleculeNum;
20516
- id = 'STRU';
20569
+ id = 'stru';
20517
20570
 
20518
20571
  structure = id;
20519
- //if(id == 'STRU' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
20520
- if(id == 'STRU' || bMutation) { // bMutation: side chain prediction
20572
+ //if(id == 'stru' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
20573
+ //if(id == 'stru' || bMutation) { // bMutation: side chain prediction
20574
+ if(id == 'stru') {
20521
20575
  structure = (moleculeNum === 1) ? id : id + moleculeNum.toString();
20522
20576
  }
20523
20577
 
@@ -20539,8 +20593,9 @@ class LoadPDB {
20539
20593
  }
20540
20594
  } else if (record === 'ATOM ' || record === 'HETATM') {
20541
20595
  structure = id;
20542
- //if(id == 'STRU' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
20543
- if(id == 'STRU' || bMutation) { // bMutation: side chain prediction
20596
+ //if(id == 'stru' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
20597
+ //if(id == 'stru' || bMutation) { // bMutation: side chain prediction
20598
+ if(id == 'stru') {
20544
20599
  structure = (moleculeNum === 1) ? id : id + moleculeNum.toString();
20545
20600
  }
20546
20601
 
@@ -20792,23 +20847,9 @@ class LoadPDB {
20792
20847
  // remove the reference
20793
20848
  lines = null;
20794
20849
 
20795
- let curChain, curResi, curResAtoms = [];
20796
- // refresh for atoms in each residue
20797
- let refreshBonds = function(f) {
20798
- let n = curResAtoms.length;
20799
- for (let j = 0; j < n; ++j) {
20800
- let atom0 = curResAtoms[j];
20801
- for (let k = j + 1; k < n; ++k) {
20802
- let atom1 = curResAtoms[k];
20803
- if (atom0.alt === atom1.alt && me.utilsCls.hasCovalentBond(atom0, atom1)) {
20804
- //if (me.utilsCls.hasCovalentBond(atom0, atom1)) {
20805
- atom0.bonds.push(atom1.serial);
20806
- atom1.bonds.push(atom0.serial);
20807
- }
20808
- }
20809
- f && f(atom0);
20810
- }
20811
- };
20850
+ let firstAtom = ic.firstAtomObjCls.getFirstAtomObj(ic.hAtoms);
20851
+ let curChain = firstAtom.chain, curResi = firstAtom.resi, curResAtoms = [];
20852
+
20812
20853
  let pmin = new THREE.Vector3( 9999, 9999, 9999);
20813
20854
  let pmax = new THREE.Vector3(-9999,-9999,-9999);
20814
20855
  let psum = new THREE.Vector3();
@@ -20818,7 +20859,9 @@ class LoadPDB {
20818
20859
  let lipidResidHash = {};
20819
20860
 
20820
20861
  // assign atoms
20821
- for (let i in ic.atoms) {
20862
+ let prevCarbonArray = [firstAtom]; // add a dummy atom
20863
+ //for (let i in ic.atoms) {
20864
+ for (let i in ic.hAtoms) {
20822
20865
  let atom = ic.atoms[i];
20823
20866
  let coord = atom.coord;
20824
20867
  psum.add(coord);
@@ -20867,22 +20910,28 @@ class LoadPDB {
20867
20910
  atom.color = me.parasCls.atomColors[atom.elem];
20868
20911
  }
20869
20912
 
20870
- if (!(curChain === atom.chain && curResi === atom.resi)) {
20871
- // a new residue, add the residue-residue bond beides the regular bonds
20872
- refreshBonds(function (atom0) {
20873
- if (((atom0.name === 'C' && atom.name === 'N') || (atom0.name === 'O3\'' && atom.name === 'P')) && me.utilsCls.hasCovalentBond(atom0, atom)) {
20874
- atom0.bonds.push(atom.serial);
20875
- atom.bonds.push(atom0.serial);
20876
- }
20877
- });
20913
+ if(!(curChain === atom.chain && curResi === atom.resi)) {
20914
+ // a new residue, add the residue-residue bond beides the regular bonds
20915
+ this.refreshBonds(curResAtoms, prevCarbonArray[0]);
20916
+
20917
+ prevCarbonArray.splice(0, 1); // remove the first carbon
20918
+
20878
20919
  curChain = atom.chain;
20879
20920
  curResi = atom.resi;
20880
20921
  //curInsc = atom.insc;
20881
20922
  curResAtoms.length = 0;
20882
20923
  }
20883
20924
  curResAtoms.push(atom);
20925
+
20926
+ if(atom.name === 'C' || atom.name === 'O3\'') {
20927
+ prevCarbonArray.push(atom);
20928
+ }
20884
20929
  } // end of for
20885
20930
 
20931
+ // last residue
20932
+ //refreshBonds();
20933
+ this.refreshBonds(curResAtoms, prevCarbonArray[0]);
20934
+
20886
20935
  // reset lipid
20887
20936
  for(let resid in lipidResidHash) {
20888
20937
  let atomHash = ic.residues[resid];
@@ -20899,9 +20948,6 @@ class LoadPDB {
20899
20948
  }
20900
20949
  }
20901
20950
 
20902
- // last residue
20903
- refreshBonds();
20904
-
20905
20951
  ic.pmin = pmin;
20906
20952
  ic.pmax = pmax;
20907
20953
 
@@ -20937,6 +20983,28 @@ class LoadPDB {
20937
20983
  }
20938
20984
  }
20939
20985
 
20986
+ // refresh for atoms in each residue
20987
+ refreshBonds(curResAtoms, prevCarbon) { let ic = this.icn3d, me = ic.icn3dui;
20988
+ let n = curResAtoms.length;
20989
+ for (let j = 0; j < n; ++j) {
20990
+ let atom0 = curResAtoms[j];
20991
+ for (let k = j + 1; k < n; ++k) {
20992
+ let atom1 = curResAtoms[k];
20993
+ if (atom0.alt === atom1.alt && me.utilsCls.hasCovalentBond(atom0, atom1)) {
20994
+ //if (me.utilsCls.hasCovalentBond(atom0, atom1)) {
20995
+ atom0.bonds.push(atom1.serial);
20996
+ atom1.bonds.push(atom0.serial);
20997
+ }
20998
+ }
20999
+
21000
+ //f && f(atom0);
21001
+ if (prevCarbon && (prevCarbon.name === 'C' || prevCarbon.name === 'O3\'') && (atom0.name === 'N' || atom0.name === 'P') && me.utilsCls.hasCovalentBond(atom0, prevCarbon)) {
21002
+ atom0.bonds.push(prevCarbon.serial);
21003
+ prevCarbon.bonds.push(atom0.serial);
21004
+ }
21005
+ }
21006
+ }
21007
+
20940
21008
  adjustSeq(chainMissingResidueArray) { let ic = this.icn3d; ic.icn3dui;
20941
21009
  // adjust sequences
20942
21010
  for(let chainNum in ic.chainsSeq) {
@@ -21239,12 +21307,13 @@ class LoadAtomData {
21239
21307
  else { // mmdbid or mmcifid
21240
21308
  if(data.descr !== undefined) ic.molTitle += data.descr.name;
21241
21309
  if(type === 'mmdbid') {
21242
- let pdbidTmp = data.pdbId;
21310
+ let pdbidTmp = (isNaN(id)) ? id : data.pdbId;
21243
21311
  let chainHash = {};
21244
21312
 
21245
21313
  if(ic.alignmolid2color === undefined) ic.alignmolid2color = [];
21246
21314
 
21247
21315
  let molidCnt = 1;
21316
+
21248
21317
  for(let molid in data.moleculeInfor) {
21249
21318
  if(Object.keys(data.moleculeInfor[molid]).length === 0) continue;
21250
21319
 
@@ -21259,11 +21328,10 @@ class LoadAtomData {
21259
21328
  chainHash[chain] = 1;
21260
21329
  }
21261
21330
 
21262
- if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') {
21263
- //chainid += me.htmlCls.postfix;
21264
- chainid = pdbidTmp + me.htmlCls.postfix + '_' + chain;
21265
- }
21331
+ if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') ;
21266
21332
 
21333
+ //if(chainidInput && chainidInput.substr(chainidInput.indexOf('_') + 1) == chain) chainid = chainidInput;
21334
+
21267
21335
  let kind = data.moleculeInfor[molid].kind;
21268
21336
  let color = data.moleculeInfor[molid].color;
21269
21337
  let sid = data.moleculeInfor[molid].sid;
@@ -21329,7 +21397,7 @@ class LoadAtomData {
21329
21397
  let bSetResi = false;
21330
21398
 
21331
21399
  //if(mmdbId !== prevmmdbId) resiArray = [];
21332
- if(atm.chain === undefined &&(type === 'mmdbid' || type === 'align')) {
21400
+ if(atm.chain === undefined && (type === 'mmdbid' || type === 'align')) {
21333
21401
  if(type === 'mmdbid') {
21334
21402
  molid = atm.ids.m;
21335
21403
 
@@ -21395,14 +21463,13 @@ class LoadAtomData {
21395
21463
  atm.structure = mmdbId;
21396
21464
 
21397
21465
  if(type === 'mmdbid' &&((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t))
21398
- && alignType === 'query') {
21399
- atm.structure += me.htmlCls.postfix;
21400
- }
21466
+ && alignType === 'query') ;
21401
21467
  }
21402
21468
 
21403
21469
  structureNum = atm.structure;
21404
21470
 
21405
21471
  chainNum = structureNum + '_' + atm.chain;
21472
+
21406
21473
  //if(ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t && alignType === 'query') chainNum += me.htmlCls.postfix;
21407
21474
 
21408
21475
  //var resiCorrection = 0;
@@ -21705,10 +21772,7 @@ class LoadAtomData {
21705
21772
  let seqArray = data.sequences[chain];
21706
21773
  let chainid = id + '_' + chain;
21707
21774
 
21708
- if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') {
21709
- //chainid += me.htmlCls.postfix;
21710
- chainid = id + me.htmlCls.postfix + '_' + chain;
21711
- }
21775
+ if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') ;
21712
21776
 
21713
21777
  ic.ParserUtilsCls.getMissingResidues(seqArray, type, chainid); // assign ic.chainsSeq
21714
21778
  }
@@ -21763,6 +21827,7 @@ class LoadAtomData {
21763
21827
 
21764
21828
  ic.loadPDBCls.setSsbond();
21765
21829
  }
21830
+
21766
21831
  if(type === 'mmdbid' && Object.keys(ic.structures).length == 1) {
21767
21832
  let disulfideArray = data.disulfides;
21768
21833
 
@@ -23859,7 +23924,7 @@ class OpmParser {
23859
23924
  let thisClass = this;
23860
23925
  let url, dataType;
23861
23926
 
23862
- if(!pdbid) pdbid = 'STRU';
23927
+ if(!pdbid) pdbid = 'stru';
23863
23928
 
23864
23929
  url = "https://www.ncbi.nlm.nih.gov/Structure/mmdb/mmdb_strview.cgi?v=2&program=icn3d&opm&uid=" + pdbid.toLowerCase();
23865
23930
  dataType = "jsonp";
@@ -24057,6 +24122,7 @@ class MmdbParser {
24057
24122
  this.parseMmdbDataPart1(data, type);
24058
24123
 
24059
24124
  let id =(data.pdbId !== undefined) ? data.pdbId : data.mmdbId;
24125
+ if(chainid) id = chainid.substr(0, chainid.indexOf('_'));
24060
24126
 
24061
24127
  let hAtoms = ic.loadAtomDataCls.loadAtomDataIn(data, id, 'mmdbid', undefined, type, chainid, chainIndex, bLastQuery, bNoTransformNoSeqalign);
24062
24128
 
@@ -24480,12 +24546,19 @@ class RealignParser {
24480
24546
 
24481
24547
  ic.opts['color'] = 'grey';
24482
24548
  ic.setColorCls.setColorByOptions(ic.opts, ic.dAtoms);
24483
-
24484
- for(let index = 0, indexl = chainidArray.length - 1; index < indexl; ++index) {
24485
- let chainpair = chainidArray[0] + ',' + chainidArray[index + 1];
24549
+
24550
+ for(let index = 0, indexl = chainidArray.length - 1; index < indexl; ++index) {
24486
24551
  let fromStruct = chainidArray[index + 1].substr(0, chainidArray[index + 1].indexOf('_')); //.toUpperCase();
24487
24552
 
24488
- if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
24553
+ //if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
24554
+
24555
+ let chainTo = toStruct + chainidArray[0].substr(chainidArray[0].indexOf('_'));
24556
+ let chainFrom = fromStruct + chainidArray[index + 1].substr(chainidArray[index + 1].indexOf('_'));
24557
+
24558
+ chainidArray[0] = chainTo;
24559
+ chainidArray[index + 1] = chainFrom;
24560
+
24561
+ let chainpair = chainTo + ',' + chainFrom;
24489
24562
 
24490
24563
  if(!struct2SeqHash[chainpair]) continue;
24491
24564
 
@@ -24506,11 +24579,9 @@ class RealignParser {
24506
24579
  ic.realignResid[fromStruct].push({'resid':residArray2[i], 'resn':seq2[i]});
24507
24580
  }
24508
24581
 
24509
- let chainTo = chainidArray[0];
24510
- let chainFrom = chainidArray[index + 1];
24511
-
24512
24582
  let bChainAlign = true;
24513
24583
  // set ic.qt_start_end in alignCoords()
24584
+
24514
24585
  let hAtomsTmp = ic.ParserUtilsCls.alignCoords(coord2, coord1, fromStruct, undefined, chainTo, chainFrom, index + 1, bChainAlign);
24515
24586
  hAtoms = me.hashUtilsCls.unionHash(hAtoms, hAtomsTmp);
24516
24587
  }
@@ -24546,7 +24617,13 @@ class RealignParser {
24546
24617
  let fromStruct = chainidArray[index + 1].substr(0, chainidArray[index + 1].indexOf('_')); //.toUpperCase();
24547
24618
  if(!bRealign) fromStruct = fromStruct.toUpperCase();
24548
24619
 
24549
- if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
24620
+ //if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
24621
+
24622
+ let chainTo = toStruct + chainidArray[0].substr(chainidArray[0].indexOf('_'));
24623
+ let chainFrom = fromStruct + chainidArray[index + 1].substr(chainidArray[index + 1].indexOf('_'));
24624
+
24625
+ chainidArray[0] = chainTo;
24626
+ chainidArray[index + 1] = chainFrom;
24550
24627
 
24551
24628
  let seq1 = struct2SeqHash[toStruct];
24552
24629
  let seq2 = struct2SeqHash[fromStruct];
@@ -24604,8 +24681,8 @@ class RealignParser {
24604
24681
  }
24605
24682
  }
24606
24683
 
24607
- let chainTo = chainidArray[0];
24608
- let chainFrom = chainidArray[index + 1];
24684
+ //let chainTo = chainidArray[0];
24685
+ //let chainFrom = chainidArray[index + 1];
24609
24686
 
24610
24687
  let bChainAlign = true;
24611
24688
  let hAtomsTmp = ic.ParserUtilsCls.alignCoords(coordsFrom, coordsTo, fromStruct, undefined, chainTo, chainFrom, index + 1, bChainAlign);
@@ -24639,7 +24716,7 @@ class RealignParser {
24639
24716
  // align seq
24640
24717
  //ic.hAtoms = ic.chainalignParserCls.setMsa(chainidArray, bRealign);
24641
24718
  ic.hAtoms = ic.chainalignParserCls.setMsa(chainidArray);
24642
-
24719
+
24643
24720
  ic.transformCls.zoominSelection();
24644
24721
 
24645
24722
 
@@ -24649,12 +24726,13 @@ class RealignParser {
24649
24726
  ic.opts['color'] = 'identity';
24650
24727
  //ic.setColorCls.setColorByOptions(ic.opts, ic.atoms);
24651
24728
  ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
24652
-
24729
+
24653
24730
  let name = 'protein_aligned';
24654
24731
  ic.selectionCls.saveSelection(name, name);
24655
-
24732
+
24656
24733
  ic.drawCls.draw();
24657
24734
  ic.hlUpdateCls.updateHlAll();
24735
+
24658
24736
  if(ic.deferredRealign !== undefined) ic.deferredRealign.resolve();
24659
24737
  }
24660
24738
  else {
@@ -24791,9 +24869,6 @@ class RealignParser {
24791
24869
  if(i == 0) {
24792
24870
  mmdbid_t = mmdbid;
24793
24871
  }
24794
- else if(mmdbid_t == mmdbid) {
24795
- mmdbid += me.htmlCls.postfix;
24796
- }
24797
24872
 
24798
24873
  let chainid = mmdbid + chainidArray[i].substr(pos);
24799
24874
  if(i == 0) chainid_t = chainid;
@@ -24801,7 +24876,6 @@ class RealignParser {
24801
24876
  if(!ic.chainsSeq[chainid]) {
24802
24877
  //var aaa = 1; //alert("Please select one chain per structure and try it again...");
24803
24878
  //return;
24804
-
24805
24879
  continue;
24806
24880
  }
24807
24881
 
@@ -24839,8 +24913,8 @@ class RealignParser {
24839
24913
 
24840
24914
  // slave
24841
24915
  resiArray = predefinedResPair[1].split(",");
24842
- result = thisClass.getSeqCoorResid(resiArray, chainid, base);
24843
-
24916
+ result = thisClass.getSeqCoorResid(resiArray, chainid, base);
24917
+
24844
24918
  if(!struct2SeqHash[chainidpair][mmdbid]) struct2SeqHash[chainidpair][mmdbid] = '';
24845
24919
  if(!struct2CoorHash[chainidpair][mmdbid]) struct2CoorHash[chainidpair][mmdbid] = [];
24846
24920
  if(!struct2resid[chainidpair][mmdbid]) struct2resid[chainidpair][mmdbid] = [];
@@ -25005,8 +25079,11 @@ class RealignParser {
25005
25079
  let bFound = false;
25006
25080
  for(let serial in ic.residues[resid]) {
25007
25081
  let atom = ic.atoms[serial];
25008
- if((ic.proteins.hasOwnProperty(serial) && atom.name == "CA" && atom.elem == "C")
25009
- ||(ic.nucleotides.hasOwnProperty(serial) &&(atom.name == "O3'" || atom.name == "O3*") && atom.elem == "O") ) {
25082
+
25083
+ //if((ic.proteins.hasOwnProperty(serial) && atom.name == "CA" && atom.elem == "C")
25084
+ // ||(ic.nucleotides.hasOwnProperty(serial) &&(atom.name == "O3'" || atom.name == "O3*") && atom.elem == "O") ) {
25085
+ if((atom.name == "CA" && atom.elem == "C")
25086
+ ||((atom.name == "O3'" || atom.name == "O3*") && atom.elem == "O") ) {
25010
25087
  struct2CoorArray.push(atom.coord.clone());
25011
25088
  bFound = true;
25012
25089
  break;
@@ -25034,7 +25111,7 @@ class ChainalignParser {
25034
25111
  let mmdbid_t, mmdbid_q;
25035
25112
  mmdbid_t = chainidArray[0].substr(0, chainidArray[0].indexOf('_'));
25036
25113
  let bLastQuery = false;
25037
- if(mmdbid_t.length > 4) {
25114
+ if(mmdbid_t.length > 5) {
25038
25115
  let bAppend = false, bNoDssp = true;
25039
25116
  hAtoms = ic.pdbParserCls.loadPdbData(data1, mmdbid_t, false, bAppend, 'target', bLastQuery, bNoDssp);
25040
25117
  }
@@ -25047,7 +25124,10 @@ class ChainalignParser {
25047
25124
  if(i == data2Array.length - 1) bLastQuery = true;
25048
25125
  // each alignment has a chainIndex i
25049
25126
  mmdbid_q = chainidArray[i + 1].substr(0, chainidArray[i + 1].indexOf('_'));
25050
- if(mmdbid_q.length > 4) {
25127
+ //mmdbid_q = (mmdbid_q_tmp.length == 5) ? mmdbid_q_tmp.substr(0, 4) : mmdbid_q_tmp; // added postfixfor same PDB IDs
25128
+
25129
+ //if(mmdbid_q.length > 4) {
25130
+ if(mmdbid_q.length > 5) { // PDB ID plus postfix could be 5
25051
25131
  let bAppend = true, bNoDssp = true;
25052
25132
  hAtomsTmp = ic.pdbParserCls.loadPdbData(data2Array[i], mmdbid_q, false, bAppend, 'query', bLastQuery, bNoDssp);
25053
25133
  }
@@ -25485,10 +25565,21 @@ class ChainalignParser {
25485
25565
  let domainArray = (me.cfg.domainids) ? me.cfg.domainids.split(',') : [];
25486
25566
  if(domainArray.length < alignArray.length) domainArray = [];
25487
25567
 
25568
+ let struct2cnt = {};
25488
25569
  for(let i = 0, il = alignArray.length; i < il; ++i) {
25489
25570
  let chainid = alignArray[i];
25490
25571
  let pos = chainid.indexOf('_');
25491
- alignArray[i] = chainid.substr(0, pos).toUpperCase() + chainid.substr(pos);
25572
+ let struct = chainid.substr(0, pos).toUpperCase();
25573
+ if(!struct2cnt.hasOwnProperty(struct)) {
25574
+ struct2cnt[struct] = 1;
25575
+ }
25576
+ else {
25577
+ ++struct2cnt[struct];
25578
+ }
25579
+
25580
+ struct = (struct2cnt[struct] == 1) ? struct : struct + struct2cnt[struct];
25581
+
25582
+ alignArray[i] = struct + chainid.substr(pos);
25492
25583
  }
25493
25584
 
25494
25585
  ic.chainidArray = alignArray;
@@ -25501,7 +25592,7 @@ class ChainalignParser {
25501
25592
  let targetAjax;
25502
25593
 
25503
25594
  let url_t;
25504
- if(ic.mmdbid_t.length > 4) {
25595
+ if(ic.mmdbid_t.length > 5) {
25505
25596
  url_t = "https://alphafold.ebi.ac.uk/files/AF-" + ic.mmdbid_t + "-F1-model_v2.pdb";
25506
25597
 
25507
25598
  targetAjax = $.ajax({
@@ -25533,11 +25624,13 @@ class ChainalignParser {
25533
25624
  ic.pdbChainIndexHash = {};
25534
25625
  for(let index = 1, indexLen = alignArray.length; index < indexLen; ++index) {
25535
25626
  let pos2 = alignArray[index].indexOf('_');
25536
- ic.mmdbid_q = alignArray[index].substr(0, pos2).toUpperCase();
25627
+ let mmdbid_q_tmp = alignArray[index].substr(0, pos2).toUpperCase();
25628
+ ic.mmdbid_q = (mmdbid_q_tmp.length == 5) ? mmdbid_q_tmp.substr(0, 4) : mmdbid_q_tmp; // added postfix for same PDB IDs
25629
+
25537
25630
  ic.chain_q = alignArray[index].substr(pos2+1);
25538
25631
 
25539
25632
  let url_q, queryAjax;
25540
- if(ic.mmdbid_q.length > 4) {
25633
+ if(ic.mmdbid_q.length > 5) {
25541
25634
  url_q = "https://alphafold.ebi.ac.uk/files/AF-" + ic.mmdbid_q + "-F1-model_v2.pdb";
25542
25635
 
25543
25636
  queryAjax = $.ajax({
@@ -25562,7 +25655,9 @@ class ChainalignParser {
25562
25655
 
25563
25656
  for(let index = 1, indexLen = alignArray.length; index < indexLen; ++index) {
25564
25657
  let pos2 = alignArray[index].indexOf('_');
25565
- ic.mmdbid_q = alignArray[index].substr(0, pos2).toUpperCase();
25658
+ let mmdbid_q_tmp = alignArray[index].substr(0, pos2).toUpperCase();
25659
+ ic.mmdbid_q = (mmdbid_q_tmp.length == 5) ? mmdbid_q_tmp.substr(0, 4) : mmdbid_q_tmp; // added postfix for same PDB IDs
25660
+
25566
25661
  ic.chain_q = alignArray[index].substr(pos2+1);
25567
25662
 
25568
25663
  if(!me.cfg.resnum && !me.cfg.resdef) {
@@ -25587,7 +25682,7 @@ class ChainalignParser {
25587
25682
 
25588
25683
  ajaxArray.push(alignAjax);
25589
25684
 
25590
- ic.pdbChainIndexHash[index] = ic.mmdbid_q + "_" + ic.chain_q + "_" + ic.mmdbid_t + "_" + ic.chain_t;
25685
+ ic.pdbChainIndexHash[index] = mmdbid_q_tmp + "_" + ic.chain_q + "_" + ic.mmdbid_t + "_" + ic.chain_t;
25591
25686
  }
25592
25687
  else {
25593
25688
  // get the dynamic alignment after loading the structures
@@ -25617,7 +25712,7 @@ class ChainalignParser {
25617
25712
  // index = 0: the mmdb data of target
25618
25713
  let targetData = dataArray[0][0];
25619
25714
  let header = 'HEADER ' + mmdbid_t + '\n';
25620
- if(mmdbid_t.length > 4) targetData = header + targetData;
25715
+ if(mmdbid_t.length > 5) targetData = header + targetData;
25621
25716
 
25622
25717
  ic.t_trans_add = [];
25623
25718
  ic.q_trans_sub = [];
@@ -25636,7 +25731,7 @@ class ChainalignParser {
25636
25731
  let mmdbid_q = chainidArray[index].substr(0, pos).toUpperCase();
25637
25732
 
25638
25733
  let header = 'HEADER ' + mmdbid_q + '\n';
25639
- if(mmdbid_q.length > 4) queryData = header + queryData;
25734
+ if(mmdbid_q.length > 5) queryData = header + queryData;
25640
25735
 
25641
25736
  if(queryData !== undefined && JSON.stringify(queryData).indexOf('Oops there was a problem') === -1
25642
25737
  ) {
@@ -25811,9 +25906,9 @@ class ChainalignParser {
25811
25906
  }
25812
25907
 
25813
25908
  downloadMmdbAf(idlist, bQuery) { let ic = this.icn3d, me = ic.icn3dui;
25814
- let thisClass = this;
25909
+ let thisClass = this;
25815
25910
 
25816
- ic.deferredMmdbaf = $.Deferred(function() {
25911
+ ic.deferredMmdbaf = $.Deferred(function() {
25817
25912
  ic.structArray = idlist.split(',');
25818
25913
 
25819
25914
  let ajaxArray = [];
@@ -25822,7 +25917,7 @@ class ChainalignParser {
25822
25917
  let url_t, targetAjax;
25823
25918
  let structure = ic.structArray[i];
25824
25919
 
25825
- if(isNaN(structure) && structure.length > 4) {
25920
+ if(isNaN(structure) && structure.length > 5) {
25826
25921
  url_t = "https://alphafold.ebi.ac.uk/files/AF-" + ic.structArray[i] + "-F1-model_v2.pdb";
25827
25922
 
25828
25923
  targetAjax = $.ajax({
@@ -25871,7 +25966,7 @@ class ChainalignParser {
25871
25966
  for(let index = 0, indexl = structArray.length; index < indexl; ++index) {
25872
25967
  let queryData = dataArray[index][0];
25873
25968
  let header = 'HEADER ' + structArray[index] + '\n';
25874
- if(structArray[index].length > 4) queryData = header + queryData;
25969
+ if(structArray[index].length > 5) queryData = header + queryData;
25875
25970
 
25876
25971
  if(queryData !== undefined && JSON.stringify(queryData).indexOf('Oops there was a problem') === -1
25877
25972
  ) {
@@ -25901,7 +25996,8 @@ class ChainalignParser {
25901
25996
  bAppend = true;
25902
25997
  }
25903
25998
 
25904
- if(structArray[i].length > 4) {
25999
+ //if(structArray[i].length > 4) {
26000
+ if(structArray[i].length > 5) { // PDB ID plus postfix could be 5
25905
26001
  let bNoDssp = true;
25906
26002
  hAtomsTmp = ic.pdbParserCls.loadPdbData(queryDataArray[i], structArray[i], false, bAppend, targetOrQuery, bLastQuery, bNoDssp);
25907
26003
  }
@@ -27872,7 +27968,7 @@ class LoadScript {
27872
27968
  me.cfg.mmcifid = id;
27873
27969
  ic.mmcifParserCls.downloadMmcif(id);
27874
27970
  }
27875
- else if(command.indexOf('load mmdb') !== -1 || command.indexOf('load mmdb1') !== -1) {
27971
+ else if(command.indexOf('load mmdb ') !== -1 || command.indexOf('load mmdb1 ') !== -1) {
27876
27972
  me.cfg.mmdbid = id;
27877
27973
  me.cfg.bu = 1;
27878
27974
 
@@ -27888,13 +27984,13 @@ class LoadScript {
27888
27984
  me.cfg.mmdbafid = id;
27889
27985
  me.cfg.bu = 1;
27890
27986
 
27891
- ic.mmdbParserCls.downloadMmdbAf(id);
27987
+ ic.chainalignParserCls.downloadMmdbAf(id);
27892
27988
  }
27893
27989
  else if(command.indexOf('load mmdbaf0') !== -1) {
27894
27990
  me.cfg.mmdbafid = id;
27895
27991
  me.cfg.bu = 0;
27896
27992
 
27897
- ic.mmdbParserCls.downloadMmdbAf(id);
27993
+ ic.chainalignParserCls.downloadMmdbAf(id);
27898
27994
  }
27899
27995
  else if(command.indexOf('load gi') !== -1) {
27900
27996
  me.cfg.gi = id;
@@ -30539,7 +30635,7 @@ class AddTrack {
30539
30635
 
30540
30636
  //this.showNewTrack(chainid, title, text);
30541
30637
  //me.htmlCls.clickMenuCls.setLogCmd("add track | chainid " + chainid + " | title " + title + " | text " + this.simplifyText(text), true);
30542
- let result = this.getFullText(text);
30638
+ let result = thisClass.getFullText(text);
30543
30639
 
30544
30640
  thisClass.showNewTrack(chainid, title, result.text, undefined, undefined, 'custom', undefined, undefined, result.fromArray, result.toArray);
30545
30641
 
@@ -31629,7 +31725,19 @@ class AnnoCddSite {
31629
31725
  }
31630
31726
  }
31631
31727
  }
31632
- let htmlTmp2 = '<div class="icn3d-seqTitle icn3d-link icn3d-blue" site="site" posarray="' + adjustedResPosArray.toString() + '" shorttitle="' + title + '" setname="' + chnid + '_site_' + index + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
31728
+
31729
+ let bCoordinates = false;
31730
+ for(let i = 0, il = adjustedResPosArray.length; i < il; ++i) {
31731
+ let resid = chnid + "_" + adjustedResPosArray[i];
31732
+ if(ic.residues.hasOwnProperty(resid)) {
31733
+ bCoordinates = true;
31734
+ break;
31735
+ }
31736
+ }
31737
+
31738
+ let linkStr = (bCoordinates) ? 'icn3d-link icn3d-blue' : '';
31739
+
31740
+ let htmlTmp2 = '<div class="icn3d-seqTitle ' + linkStr + '" site="site" posarray="' + adjustedResPosArray.toString() + '" shorttitle="' + title + '" setname="' + chnid + '_site_' + index + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
31633
31741
  let htmlTmp3 = '<span class="icn3d-residueNum" title="residue count">' + resCnt.toString() + ' Res</span>';
31634
31742
  let htmlTmp = '<span class="icn3d-seqLine">';
31635
31743
  html3 += htmlTmp2 + htmlTmp3 + '<br>';
@@ -31785,7 +31893,25 @@ class AnnoCddSite {
31785
31893
  if(bDomain) pssmid2fromArray[pssmid] = fromArray;
31786
31894
  if(bDomain) pssmid2toArray[pssmid] = toArray;
31787
31895
 
31788
- let htmlTmp2 = '<div class="icn3d-seqTitle icn3d-link icn3d-blue" ' + type + '="' + acc + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" setname="' + setname + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
31896
+ let bCoordinates = false;
31897
+ for(let i = 0, il = fromArray.length; i < il; ++i) {
31898
+ let from = fromArray[i], to = toArray[i];
31899
+ for(let j = from; j <= to; ++j) {
31900
+ let resid = chnid + "_" + j;
31901
+ if(ic.residues.hasOwnProperty(resid)) {
31902
+ bCoordinates = true;
31903
+ break;
31904
+ }
31905
+ }
31906
+
31907
+ if(bCoordinates) {
31908
+ break;
31909
+ }
31910
+ }
31911
+
31912
+ let linkStr = (bCoordinates) ? 'icn3d-link icn3d-blue' : '';
31913
+
31914
+ let htmlTmp2 = '<div class="icn3d-seqTitle ' + linkStr + '" ' + type + '="' + acc + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" setname="' + setname + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
31789
31915
  let htmlTmp3 = '<span class="icn3d-residueNum" title="residue count">' + resCnt.toString() + ' Res</span>';
31790
31916
  html3 += htmlTmp2 + htmlTmp3 + '<br>';
31791
31917
  let htmlTmp = '<span class="icn3d-seqLine">';
@@ -31793,7 +31919,7 @@ class AnnoCddSite {
31793
31919
  if(bDomain) {
31794
31920
  html2 += '<div style="width:20px; display:inline-block;"><span id="' + ic.pre + chnid + '_' + acc + '_' + r + '_cddseq_expand" class="ui-icon ui-icon-plus icn3d-expand icn3d-link" style="width:15px;" title="Expand"></span><span id="' + ic.pre + chnid + '_' + acc + '_' + r + '_cddseq_shrink" class="ui-icon ui-icon-minus icn3d-shrink icn3d-link" style="display:none; width:15px;" title="Shrink"></span></div>';
31795
31921
  }
31796
- html2 += '<div style="width:' + titleSpace + 'px!important;" class="icn3d-seqTitle icn3d-link icn3d-blue" ' + type + '="' + acc + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
31922
+ html2 += '<div style="width:' + titleSpace + 'px!important;" class="icn3d-seqTitle ' + linkStr + '" ' + type + '="' + acc + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
31797
31923
  html2 += htmlTmp3 + htmlTmp;
31798
31924
  let pre = type + index.toString();
31799
31925
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
@@ -31819,7 +31945,7 @@ class AnnoCddSite {
31819
31945
  for(let i = 0, il = fromArray.length; i < il; ++i) {
31820
31946
  let emptyWidth =(i == 0) ? Math.round(ic.seqAnnWidth *(fromArray[i] - ic.baseResi[chnid] - 1) / ic.maxAnnoLength) : Math.round(ic.seqAnnWidth *(fromArray[i] - toArray[i-1] - 1) / ic.maxAnnoLength);
31821
31947
  html2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
31822
- html2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray[i] - fromArray[i] + 1) / ic.maxAnnoLength) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" domain="' +(index+1).toString() + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" id="' + chnid + '_domain_' + index + '_' + r + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + domain + ' </div>';
31948
+ html2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray[i] - fromArray[i] + 1) / ic.maxAnnoLength) + 'px;" class="icn3d-seqTitle ' + linkStr + '" domain="' +(index+1).toString() + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" id="' + chnid + '_domain_' + index + '_' + r + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + domain + ' </div>';
31823
31949
  }
31824
31950
  }
31825
31951
  else { // with potential gaps
@@ -31838,7 +31964,7 @@ class AnnoCddSite {
31838
31964
  html2 += ic.showSeqCls.insertGapOverview(chnid, fromArray2[i]);
31839
31965
  let emptyWidth =(i == 0) ? Math.round(ic.seqAnnWidth *(fromArray2[i] - ic.baseResi[chnid] - 1) /(ic.maxAnnoLength + ic.nTotalGap)) : Math.round(ic.seqAnnWidth *(fromArray2[i] - toArray2[i-1] - 1) /(ic.maxAnnoLength + ic.nTotalGap));
31840
31966
  html2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
31841
- html2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray2[i] - fromArray2[i] + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" domain="' +(index+1).toString() + '" from="' + fromArray2 + '" to="' + toArray2 + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" id="' + chnid + '_domain_' + index + '_' + r + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + domain + ' </div>';
31967
+ html2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray2[i] - fromArray2[i] + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle ' + linkStr + '" domain="' +(index+1).toString() + '" from="' + fromArray2 + '" to="' + toArray2 + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" id="' + chnid + '_domain_' + index + '_' + r + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + domain + ' </div>';
31842
31968
  }
31843
31969
  }
31844
31970
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
@@ -34901,14 +35027,14 @@ class SetSeqAlign {
34901
35027
  let residueid2 = chainid2 + '_' + resi;
34902
35028
  let ss1 = ic.secondaries[residueid1];
34903
35029
  let ss2 = ic.secondaries[residueid2];
34904
- if(ss2 !== undefined) {
35030
+ if(ss2) {
34905
35031
  ic.alnChainsAnno[chainid1][0].push(ss2);
34906
35032
  }
34907
35033
  else {
34908
35034
  ic.alnChainsAnno[chainid1][0].push('-');
34909
35035
  }
34910
35036
 
34911
- if(ss1 !== undefined) {
35037
+ if(ss1) {
34912
35038
  ic.alnChainsAnno[chainid1][1].push(ss1);
34913
35039
  }
34914
35040
  else {
@@ -34925,7 +35051,7 @@ class SetSeqAlign {
34925
35051
  ic.alnChainsAnno[chainid1][3].push(numberStr); // symbol: 10, 20, etc, empty for rest
34926
35052
 
34927
35053
  ++alignIndex;
34928
- } // end for(let j
35054
+ } // end for(let j
34929
35055
  } // end for(let i
34930
35056
 
34931
35057
  seqalign = {};
@@ -34958,11 +35084,6 @@ class SetSeqAlign {
34958
35084
  let chainLen = ic.chainsSeq[mmdbid2 + '_' + chain2].length;
34959
35085
  ic.qt_start_end[chainIndex] = {"q_start":1, "q_end": chainLen, "t_start":1, "t_end": chainLen};
34960
35086
  }
34961
-
34962
- if(mmdbid2 !== undefined && mmdbid2 === mmdbid1) {
34963
- //chainid1 += me.htmlCls.postfix;
34964
- chainid2 = mmdbid2 + me.htmlCls.postfix + "_" + chain2;
34965
- }
34966
35087
  }
34967
35088
  else {
34968
35089
  //var chainidArray = me.cfg.chainalign.split(',');
@@ -34983,10 +35104,7 @@ class SetSeqAlign {
34983
35104
  chainid1 = mmdbid1 + "_" + chain1;
34984
35105
  chainid2 = mmdbid2 + "_" + chain2;
34985
35106
 
34986
- if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) {
34987
- //chainid1 += me.htmlCls.postfix;
34988
- chainid2 = mmdbid2 + me.htmlCls.postfix + "_" + chain2;
34989
- }
35107
+ if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) ;
34990
35108
  }
34991
35109
 
34992
35110
  ic.conservedName1 = chainid1 + '_cons';
@@ -35224,6 +35342,7 @@ class SetSeqAlign {
35224
35342
  let resi2range_t = {}; // aaccumulative aligned residues in the template chain
35225
35343
  // start and end of MSA
35226
35344
  let start_t = 9999, end_t = -1;
35345
+
35227
35346
  for(let index = 1, indexl = chainidArray.length; index < indexl; ++index) {
35228
35347
  let chainIndex = index - 1;
35229
35348
  for(let i = 0, il = ic.qt_start_end[chainIndex].length; i < il; ++i) {
@@ -35325,7 +35444,7 @@ class SetSeqAlign {
35325
35444
 
35326
35445
  hAtoms = me.hashUtilsCls.unionHash(hAtoms, hAtomsTmp);
35327
35446
  }
35328
-
35447
+
35329
35448
  // 3. assign the varaible ic.alnChainsAnno
35330
35449
  for(let i = 0; i < 3 + 2*n; ++i) {
35331
35450
  if(ic.alnChainsAnno[chainid1][i] === undefined ) ic.alnChainsAnno[chainid1][i] = [];
@@ -35402,7 +35521,7 @@ class SetSeqAlign {
35402
35521
  resi = resiPos;
35403
35522
  }
35404
35523
  else {
35405
- if(ic.chainsSeq[chainid][resiPos] === undefined) {
35524
+ if(!ic.chainsSeq[chainid] || !ic.chainsSeq[chainid][resiPos]) {
35406
35525
  resi = '';
35407
35526
  }
35408
35527
  else {
@@ -35427,7 +35546,7 @@ class SetSeqAlign {
35427
35546
  }
35428
35547
  }
35429
35548
  else {
35430
- if(ic.chainsSeq[chainid][resiPos] === undefined) {
35549
+ if(!ic.chainsSeq[chainid] || !ic.chainsSeq[chainid][resiPos]) {
35431
35550
  resn = '';
35432
35551
  }
35433
35552
  else {
@@ -35443,18 +35562,21 @@ class SetSeqAlign {
35443
35562
  let nGap = 0;
35444
35563
 
35445
35564
  let pos_t; // position to add gap
35446
- for(let j = 0, jl = ic.alnChainsSeq[chainid1].length; j < jl; ++j) {
35447
- //add gap before the mapping region
35448
- if(parseInt(ic.alnChainsSeq[chainid1][j].resi) == resi_t) {
35449
- pos_t = j;
35450
- break;
35451
- }
35452
35565
 
35453
- if(ic.alnChainsSeq[chainid1][j].resn == '-') {
35454
- ++nGap;
35455
- }
35456
- else {
35457
- nGap = 0;
35566
+ if(ic.alnChainsSeq[chainid1]) {
35567
+ for(let j = 0, jl = ic.alnChainsSeq[chainid1].length; j < jl; ++j) {
35568
+ //add gap before the mapping region
35569
+ if(parseInt(ic.alnChainsSeq[chainid1][j].resi) == resi_t) {
35570
+ pos_t = j;
35571
+ break;
35572
+ }
35573
+
35574
+ if(ic.alnChainsSeq[chainid1][j].resn == '-') {
35575
+ ++nGap;
35576
+ }
35577
+ else {
35578
+ nGap = 0;
35579
+ }
35458
35580
  }
35459
35581
  }
35460
35582
 
@@ -35463,13 +35585,14 @@ class SetSeqAlign {
35463
35585
 
35464
35586
  addGapAllAlnChains(chainidArray, alignedChainIndice, chainid1, resi_t, len) { let ic = this.icn3d; ic.icn3dui;
35465
35587
  let result = this.getResiPosInTemplate(chainid1, resi_t);
35466
- let nGap = result.ngap, pos_t = result.pos;
35588
+ result.ngap; let pos_t = result.pos;
35467
35589
 
35468
35590
  // add gaps for all previously aligned sequences, not the current sequence, which is the last one
35469
35591
  for(let j = 0, jl = alignedChainIndice.length - 1; j < jl; ++j) {
35470
35592
  let chainidTmp = chainidArray[alignedChainIndice[j]];
35471
35593
  let gapResObject = this.getResObject(chainidTmp, true);
35472
- for(let k = 0, kl = len - nGap; k < kl; ++k) {
35594
+ //for(let k = 0, kl = len - nGap; k < kl; ++k) {
35595
+ for(let k = 0, kl = len; k < kl; ++k) {
35473
35596
  ic.alnChainsSeq[chainidTmp].splice(pos_t, 0, gapResObject);
35474
35597
  }
35475
35598
  }
@@ -35513,8 +35636,8 @@ class SetSeqAlign {
35513
35636
  pos2 = chainid.indexOf('_');
35514
35637
 
35515
35638
  //mmdbid1 = ic.mmdbid_t;
35516
- mmdbid1 = chainidArray[0].substr(0, pos1).toUpperCase();
35517
- mmdbid2 = chainid.substr(0, pos2).toUpperCase();
35639
+ mmdbid1 = chainidArray[0].substr(0, pos1); //.toUpperCase();
35640
+ mmdbid2 = chainid.substr(0, pos2); //.toUpperCase();
35518
35641
 
35519
35642
  chain1 = chainidArray[0].substr(pos1 + 1);
35520
35643
  chain2 = chainid.substr(pos2 + 1);
@@ -35527,9 +35650,7 @@ class SetSeqAlign {
35527
35650
  chainid1 = mmdbid1 + "_" + chain1;
35528
35651
  chainid2 = mmdbid2 + "_" + chain2;
35529
35652
 
35530
- if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) {
35531
- chainid2 = mmdbid2 + me.htmlCls.postfix + "_" + chain2;
35532
- }
35653
+ if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) ;
35533
35654
 
35534
35655
  //ic.alnChainsSeq[chainid2] = [];
35535
35656
  ic.alnChains[chainid2] = {};
@@ -36367,7 +36488,7 @@ class ParserUtils {
36367
36488
 
36368
36489
  //var dxymax = npoint / 2.0 * step;
36369
36490
 
36370
- pdbid =(pdbid) ? pdbid.toUpperCase() : 'STRU';
36491
+ pdbid =(pdbid) ? pdbid.toUpperCase() : 'stru';
36371
36492
 
36372
36493
  ic.structures[pdbid].push(pdbid + '_MEM');
36373
36494
  ic.chains[pdbid + '_MEM'] = {};
@@ -36518,7 +36639,7 @@ class ParserUtils {
36518
36639
  $("#" + id + "_shrink").show();
36519
36640
 
36520
36641
  if(me.cfg.align !== undefined) {
36521
- let bShowHighlight = false;
36642
+ let bShowHighlight = false;
36522
36643
  let seqObj = me.htmlCls.alignSeqCls.getAlignSequencesAnnotations(Object.keys(ic.alnChains), undefined, undefined, bShowHighlight);
36523
36644
  $("#" + ic.pre + "dl_sequence2").html(seqObj.sequencesHtml);
36524
36645
  $("#" + ic.pre + "dl_sequence2").width(me.htmlCls.RESIDUE_WIDTH * seqObj.maxSeqCnt + 200);
@@ -36911,7 +37032,7 @@ class MmcifParser {
36911
37032
  //loadAtomDataIn. The deferred parameter was resolved after the parsing so that other javascript code can be executed.
36912
37033
  loadMmcifData(data, mmcifid) { let ic = this.icn3d; ic.icn3dui;
36913
37034
  if(!mmcifid) mmcifid = data.mmcif;
36914
- if(!mmcifid) mmcifid = 'STRU';
37035
+ if(!mmcifid) mmcifid = 'stru';
36915
37036
 
36916
37037
  if(data.atoms !== undefined) {
36917
37038
  ic.init();
@@ -37289,7 +37410,7 @@ class HlSeq {
37289
37410
  if(ic.bNCBI && ($(that).attr('domain') !== undefined || $(that).attr('feat') !== undefined || $(that).attr('3ddomain') !== undefined) ) {
37290
37411
  let residNCBI = chainid + '_' + (j+1).toString();
37291
37412
  // AlphaFold domains calculated on-the-fly have no conversion
37292
- if(structure.length > 4) {
37413
+ if(structure.length > 5) {
37293
37414
  residueid = residNCBI;
37294
37415
  }
37295
37416
  else if(ic.residNCBI2resid[residNCBI]) {
@@ -37336,7 +37457,7 @@ class HlSeq {
37336
37457
  if(ic.bNCBI) {
37337
37458
  let residNCBI = chainid + '_' +(parseInt(posArray[i])+1).toString();
37338
37459
  // AlphaFold domains calculated on-the-fly have no conversion
37339
- if(structure.length > 4) {
37460
+ if(structure.length > 5) {
37340
37461
  residueid = residNCBI;
37341
37462
  }
37342
37463
  else if(ic.residNCBI2resid[residNCBI]) {
@@ -39931,10 +40052,7 @@ class Diagram2d {
39931
40052
 
39932
40053
  let chainNameFinal =(chainNameHash[chainName] === 1) ? chainName : chainName + chainNameHash[chainName].toString();
39933
40054
  let chainid = mmdbid + '_' + chainNameFinal;
39934
- if(ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t && structureIndex === 0) {
39935
- //chainid += me.htmlCls.postfix;
39936
- chainid = mmdbid + me.htmlCls.postfix + '_' + chainNameFinal;
39937
- }
40055
+ if(ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t && structureIndex === 0) ;
39938
40056
 
39939
40057
  molid2chain[molid] = chainid;
39940
40058
  molid2color[molid] = color;
@@ -44049,6 +44167,8 @@ class ApplySsbonds {
44049
44167
  }
44050
44168
  }
44051
44169
 
44170
+ //https://github.com/mrdoob/three.js/blob/master/examples/webxr_vr_cubes.html
44171
+
44052
44172
  class VRButton {
44053
44173
  constructor(icn3d) {
44054
44174
  this.icn3d = icn3d;
@@ -44084,10 +44204,12 @@ class VRButton {
44084
44204
  }
44085
44205
 
44086
44206
  function onSessionEnded( /*event*/ ) {
44087
- // the display is weird somehow
44088
44207
  // reset orientation after VR
44089
44208
  ic.transformCls.resetOrientation();
44090
- //ic.transformCls.zoominSelection();
44209
+
44210
+ ic.bVr = false;
44211
+ //ic.mdl.scale.copy(new THREE.Vector3( 1, 1, 1 ));
44212
+
44091
44213
  ic.drawCls.draw();
44092
44214
 
44093
44215
  currentSession.removeEventListener( 'end', onSessionEnded );
@@ -44125,8 +44247,10 @@ class VRButton {
44125
44247
  ic.bImpo = false;
44126
44248
  //ic.bInstanced = false;
44127
44249
 
44128
- const bVr = true;
44129
- ic.drawCls.draw(bVr);
44250
+ ic.bVr = true;
44251
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(0.2));
44252
+
44253
+ ic.drawCls.draw(ic.bVr);
44130
44254
 
44131
44255
  if ( currentSession === null ) {
44132
44256
 
@@ -44227,29 +44351,6 @@ class VRButton {
44227
44351
  return button;
44228
44352
 
44229
44353
  } else {
44230
- /*
44231
- const message = document.createElement( 'a' );
44232
-
44233
- if ( window.isSecureContext === false ) {
44234
-
44235
- message.href = document.location.href.replace( /^http:/, 'https:' );
44236
- message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message
44237
-
44238
- } else {
44239
-
44240
- message.href = 'https://immersiveweb.dev/';
44241
- message.innerHTML = 'WEBXR NOT AVAILABLE';
44242
-
44243
- }
44244
-
44245
- message.style.left = 'calc(50% - 90px)';
44246
- message.style.width = '180px';
44247
- message.style.textDecoration = 'none';
44248
-
44249
- stylizeElement( message );
44250
-
44251
- return message;
44252
- */
44253
44354
  const message = document.createElement( 'span' );
44254
44355
  return message;
44255
44356
  }
@@ -44276,6 +44377,8 @@ class VRButton {
44276
44377
 
44277
44378
  }
44278
44379
 
44380
+ //https://github.com/mrdoob/three.js/blob/master/examples/webxr_ar_cones.html
44381
+
44279
44382
  class ARButton {
44280
44383
  constructor(icn3d) {
44281
44384
  this.icn3d = icn3d;
@@ -44347,6 +44450,13 @@ class ARButton {
44347
44450
  }
44348
44451
 
44349
44452
  function onSessionEnded( /*event*/ ) {
44453
+ // reset orientation after VR
44454
+ ic.transformCls.resetOrientation();
44455
+
44456
+ ic.bAr = false;
44457
+ //ic.mdl.scale.copy(new THREE.Vector3( 1, 1, 1 ));
44458
+
44459
+ ic.drawCls.draw();
44350
44460
 
44351
44461
  currentSession.removeEventListener( 'end', onSessionEnded );
44352
44462
 
@@ -44380,13 +44490,16 @@ class ARButton {
44380
44490
  };
44381
44491
 
44382
44492
  button.onclick = function () {
44383
- // imposter didn't work well in VR
44384
- //ic.bImpo = false;
44493
+ // imposter didn't work well in AR
44494
+ ic.bImpo = false;
44385
44495
 
44386
- ic.opts['background'] = 'transparent';
44496
+ // important to keet the background transparent
44497
+ ic.opts['background'] = 'transparent';
44387
44498
 
44388
- const bVr = true;
44389
- ic.drawCls.draw(bVr);
44499
+ ic.bAr = true;
44500
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(0.2));
44501
+
44502
+ ic.drawCls.draw(ic.bAr);
44390
44503
 
44391
44504
  if ( currentSession === null ) {
44392
44505
 
@@ -49607,6 +49720,220 @@ class XRControllerModelFactory {
49607
49720
 
49608
49721
  }
49609
49722
 
49723
+ //import * as THREE from './three/three.module.js';
49724
+
49725
+ // copied from https://github.com/NikLever/Learn-WebXR/blob/master/libs/ControllerGestures.js
49726
+ // created by Nik Lever
49727
+
49728
+ class ControllerGestures extends THREE.EventDispatcher{
49729
+ constructor( renderer ){
49730
+ super();
49731
+
49732
+ if (renderer === undefined){
49733
+ console.error('ControllerGestures must be passed a renderer');
49734
+ return;
49735
+ }
49736
+
49737
+ const clock = new THREE.Clock();
49738
+
49739
+ this.controller1 = renderer.xr.getController(0);
49740
+ this.controller1.userData.gestures = { index: 0 };
49741
+ this.controller1.userData.selectPressed = false;
49742
+ this.controller1.addEventListener( 'selectstart', onSelectStart );
49743
+ this.controller1.addEventListener( 'selectend', onSelectEnd );
49744
+
49745
+ this.controller2 = renderer.xr.getController(1);
49746
+ this.controller2.userData.gestures = { index: 1 };
49747
+ this.controller2.userData.selectPressed = false;
49748
+ this.controller2.addEventListener( 'selectstart', onSelectStart );
49749
+ this.controller2.addEventListener( 'selectend', onSelectEnd );
49750
+
49751
+ this.doubleClickLimit = 0.2;
49752
+ this.pressMinimum = 0.4;
49753
+ this.right = new THREE.Vector3(1,0,0);
49754
+ this.up = new THREE.Vector3(0,1,0);
49755
+
49756
+ this.type = 'unknown';
49757
+ this.touchCount = 0;
49758
+
49759
+ this.clock = clock;
49760
+
49761
+ const self = this;
49762
+
49763
+ function onSelectStart( ){
49764
+ const data = this.userData.gestures;
49765
+
49766
+ data.startPosition = undefined;
49767
+ data.startTime = clock.getElapsedTime();
49768
+
49769
+ if ( self.type.indexOf('tap') == -1) data.taps = 0;
49770
+
49771
+ self.type = 'unknown';
49772
+ this.userData.selectPressed = true;
49773
+
49774
+ self.touchCount++;
49775
+
49776
+ console.log( `onSelectStart touchCount: ${ self.touchCount }` );
49777
+ }
49778
+
49779
+ function onSelectEnd( ){
49780
+ const data = this.userData.gestures;
49781
+
49782
+ data.endTime = clock.getElapsedTime();
49783
+ const startToEnd = data.endTime - data.startTime;
49784
+
49785
+ //console.log(`ControllerGestures.onSelectEnd: startToEnd:${startToEnd.toFixed(2)} taps:${data.taps}`);
49786
+
49787
+ if (self.type === 'swipe'){
49788
+ const direction = ( self.controller1.position.y < data.startPosition.y) ? "DOWN" : "UP";
49789
+ self.dispatchEvent( { type:'swipe', direction } );
49790
+ self.type = 'unknown';
49791
+ }else if (self.type !== "pinch" && self.type !== "rotate" && self.type !== 'pan'){
49792
+ if ( startToEnd < self.doubleClickLimit ){
49793
+ self.type = "tap";
49794
+ data.taps++;
49795
+ }else if ( startToEnd > self.pressMinimum ){
49796
+ self.dispatchEvent( { type: 'press', position: self.controller1.position, matrixWorld: self.controller1.matrixWorld } );
49797
+ self.type = 'unknown';
49798
+ }
49799
+ }else {
49800
+ self.type = 'unknown';
49801
+ }
49802
+
49803
+ this.userData.selectPressed = false;
49804
+ data.startPosition = undefined;
49805
+
49806
+ self.touchCount--;
49807
+ }
49808
+ }
49809
+
49810
+ get multiTouch(){
49811
+ let result;
49812
+ if ( this.controller1 === undefined || this.controller2 === undefined ){
49813
+ result = false;
49814
+ }else {
49815
+ result = this.controller1.userData.selectPressed && this.controller2.userData.selectPressed;
49816
+ }
49817
+ const self = this;
49818
+ console.log( `ControllerGestures multiTouch: ${result} touchCount:${self.touchCount}`);
49819
+ return result;
49820
+ }
49821
+
49822
+ get touch(){
49823
+ let result;
49824
+ if ( this.controller1 === undefined || this.controller2 === undefined ){
49825
+ result = false;
49826
+ }else {
49827
+ result = this.controller1.userData.selectPressed || this.controller2.userData.selectPressed;
49828
+ }
49829
+ //console.log( `ControllerGestures touch: ${result}`);
49830
+ return result;
49831
+ }
49832
+
49833
+ get debugMsg(){
49834
+ return this.type;
49835
+ }
49836
+
49837
+ update(){
49838
+ const data1 = this.controller1.userData.gestures;
49839
+ const data2 = this.controller2.userData.gestures;
49840
+ const currentTime = this.clock.getElapsedTime();
49841
+
49842
+ let elapsedTime;
49843
+
49844
+ if (this.controller1.userData.selectPressed && data1.startPosition === undefined){
49845
+ elapsedTime = currentTime - data1.startTime;
49846
+ if (elapsedTime > 0.05 ) data1.startPosition = this.controller1.position.clone();
49847
+ }
49848
+
49849
+ if (this.controller2.userData.selectPressed && data2.startPosition === undefined){
49850
+ elapsedTime = currentTime - data2.startTime;
49851
+ if (elapsedTime > 0.05 ) data2.startPosition = this.controller2.position.clone();
49852
+ }
49853
+
49854
+ if (!this.controller1.userData.selectPressed && this.type === 'tap' ){
49855
+ //Only dispatch event after double click limit is passed
49856
+ elapsedTime = this.clock.getElapsedTime() - data1.endTime;
49857
+ if (elapsedTime > this.doubleClickLimit){
49858
+ console.log( `ControllerGestures.update dispatchEvent taps:${data1.taps}` );
49859
+ switch( data1.taps ){
49860
+ case 1:
49861
+ this.dispatchEvent( { type: 'tap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
49862
+ break;
49863
+ case 2:
49864
+ this.dispatchEvent( { type: 'doubletap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
49865
+ break;
49866
+ case 3:
49867
+ this.dispatchEvent( { type: 'tripletap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
49868
+ break;
49869
+ case 4:
49870
+ this.dispatchEvent( { type: 'quadtap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
49871
+ break;
49872
+ }
49873
+ this.type = "unknown";
49874
+ data1.taps = 0;
49875
+ }
49876
+ }
49877
+
49878
+ if (this.type === 'unknown' && this.touch){
49879
+ if (data1.startPosition !== undefined){
49880
+ if (this.multiTouch){
49881
+ if (data2.startPosition !== undefined){
49882
+ //startPosition is undefined for 1/20 sec
49883
+ //test for pinch or rotate
49884
+ const startDistance = data1.startPosition.distanceTo( data2.startPosition );
49885
+ const currentDistance = this.controller1.position.distanceTo( this.controller2.position );
49886
+ const delta = currentDistance - startDistance;
49887
+ if ( Math.abs(delta) > 0.01 ){
49888
+ this.type = 'pinch';
49889
+ this.startDistance = this.controller1.position.distanceTo( this.controller2.position );
49890
+ this.dispatchEvent( { type: 'pinch', delta: 0, scale: 1, initialise: true } );
49891
+ }else {
49892
+ const v1 = data2.startPosition.clone().sub( data1.startPosition ).normalize();
49893
+ const v2 = this.controller2.position.clone().sub( this.controller1.position ).normalize();
49894
+ const theta = v1.angleTo( v2 );
49895
+ if (Math.abs(theta) > 0.2){
49896
+ this.type = 'rotate';
49897
+ this.startVector = v2.clone();
49898
+ this.dispatchEvent( { type: 'rotate', theta: 0, initialise: true } );
49899
+ }
49900
+ }
49901
+ }
49902
+ }else {
49903
+ //test for swipe or pan
49904
+ let dist = data1.startPosition.distanceTo( this.controller1.position );
49905
+ elapsedTime = this.clock.getElapsedTime() - data1.startTime;
49906
+ const velocity = dist/elapsedTime;
49907
+ //console.log(`dist:${dist.toFixed(3)} velocity:${velocity.toFixed(3)}`);
49908
+ if ( dist > 0.01 && velocity > 0.1 ){
49909
+ const v = this.controller1.position.clone().sub( data1.startPosition );
49910
+ let maxY = (Math.abs(v.y) > Math.abs(v.x)) && (Math.abs(v.y) > Math.abs(v.z));
49911
+ if ( maxY )this.type = "swipe";
49912
+ }else if (dist > 0.006 && velocity < 0.03){
49913
+ this.type = "pan";
49914
+ this.startPosition = this.controller1.position.clone();
49915
+ this.dispatchEvent( { type: 'pan', delta: new THREE.Vector3(), initialise: true } );
49916
+ }
49917
+ }
49918
+ }
49919
+ }else if (this.type === 'pinch'){
49920
+ const currentDistance = this.controller1.position.distanceTo( this.controller2.position );
49921
+ const delta = currentDistance - this.startDistance;
49922
+ const scale = currentDistance/this.startDistance;
49923
+ this.dispatchEvent( { type: 'pinch', delta, scale });
49924
+ }else if (this.type === 'rotate'){
49925
+ const v = this.controller2.position.clone().sub( this.controller1.position ).normalize();
49926
+ let theta = this.startVector.angleTo( v );
49927
+ const cross = this.startVector.clone().cross( v );
49928
+ if (this.up.dot(cross) > 0) theta = -theta;
49929
+ this.dispatchEvent( { type: 'rotate', theta } );
49930
+ }else if (this.type === 'pan'){
49931
+ const delta = this.controller1.position.clone().sub( this.startPosition );
49932
+ this.dispatchEvent( { type: 'pan', delta } );
49933
+ }
49934
+ }
49935
+ }
49936
+
49610
49937
  /**
49611
49938
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
49612
49939
  */
@@ -49627,6 +49954,8 @@ class Scene {
49627
49954
 
49628
49955
  ic.cameraCls.setCamera();
49629
49956
 
49957
+ this.setVrAr();
49958
+
49630
49959
  if(ic.bSkipChemicalbinding === undefined || !ic.bSkipChemicalbinding) {
49631
49960
  ic.applyOtherCls.applyChemicalbindingOptions();
49632
49961
  }
@@ -49767,22 +50096,6 @@ class Scene {
49767
50096
  // highlight on impostors
49768
50097
  ic.mdl_ghost = new THREE.Object3D(); // Impostor display
49769
50098
  ic.scene_ghost.add(ic.mdl_ghost);
49770
-
49771
- // for VR view
49772
- let controller = ic.renderer.xr.getController( 0 );
49773
- ic.scene.add( controller );
49774
-
49775
- let controllerModelFactory = new XRControllerModelFactory();
49776
- let controllerGrip = ic.renderer.xr.getControllerGrip( 0 );
49777
- controllerGrip.add( controllerModelFactory.createControllerModel( controllerGrip ) );
49778
- ic.scene.add( controllerGrip );
49779
-
49780
- $("#" + me.pre + "VRButton").remove();
49781
- //document.body.appendChild( ic.VRButtonCls.createButton( ic.renderer ) );
49782
- $("#" + me.pre + "viewer").get(0).appendChild( ic.VRButtonCls.createButton( ic.renderer ) );
49783
-
49784
- $("#" + me.pre + "ARButton").remove();
49785
- $("#" + me.pre + "viewer").get(0).appendChild( ic.ARButtonCls.createButton( ic.renderer ) );
49786
50099
 
49787
50100
  // related to pk
49788
50101
  ic.objects = []; // define objects for pk, not all elements are used for pk
@@ -49814,8 +50127,196 @@ class Scene {
49814
50127
  ic.cams = {
49815
50128
  perspective: ic.perspectiveCamera,
49816
50129
  orthographic: ic.orthographicCamera,
49817
- };
50130
+ };
49818
50131
  };
50132
+
50133
+ setVrAr() { let ic = this.icn3d, me = ic.icn3dui;
50134
+
50135
+ // https://github.com/NikLever/Learn-WebXR/tree/master/start
50136
+ // https://github.com/mrdoob/three.js/blob/master/examples/webxr_ar_cones.html
50137
+ // https://github.com/mrdoob/three.js/blob/master/examples/webxr_vr_cubes.html
50138
+
50139
+ if(ic.bVr) {
50140
+ /*
50141
+ ic.raycasterVR = new THREE.Raycaster();
50142
+ ic.workingMatrix = new THREE.Matrix4();
50143
+ ic.workingVector = new THREE.Vector3();
50144
+ ic.origin = new THREE.Vector3();
50145
+
50146
+ let radius = 0.08;
50147
+ let geometry = new THREE.IcosahedronBufferGeometry( radius, 2 );
50148
+ ic.highlightVR = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: 0xffffff, side: THREE.BackSide } ) );
50149
+ ic.highlightVR.scale.set(1.2, 1.2, 1.2);
50150
+ */
50151
+ // modified from https://github.com/NikLever/Learn-WebXR/blob/master/complete/lecture3_7/app.js
50152
+ // add dolly to move camera
50153
+ ic.dolly = new THREE.Object3D();
50154
+ ic.dolly.position.z = 5;
50155
+ ic.dolly.add(ic.cam);
50156
+ ic.scene.add(ic.dolly);
50157
+
50158
+ ic.dummyCam = new THREE.Object3D();
50159
+ ic.cam.add(ic.dummyCam);
50160
+
50161
+ ic.clock = new THREE.Clock();
50162
+
50163
+ //controllers
50164
+ ic.controllers = this.getControllers();
50165
+
50166
+ ic.getInputSources = true; // default
50167
+
50168
+ function onSelectStart() {
50169
+ // this.children[0].scale.z = 10;
50170
+ this.userData.selectPressed = true;
50171
+ }
50172
+
50173
+ function onSelectEnd() {
50174
+ // this.children[0].scale.z = 0;
50175
+ // ic.highlightVR.visible = false;
50176
+ this.userData.selectPressed = false;
50177
+ }
50178
+ /*
50179
+ function buildController( data ) {
50180
+ let geometry, material;
50181
+
50182
+ switch ( data.targetRayMode ) {
50183
+ case 'tracked-pointer':
50184
+ geometry = new THREE.BufferGeometry();
50185
+ geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, - 1 ], 3 ) );
50186
+ geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( [ 0.5, 0.5, 0.5, 0, 0, 0 ], 3 ) );
50187
+
50188
+ material = new THREE.LineBasicMaterial( { vertexColors: true, blending: THREE.AdditiveBlending } );
50189
+
50190
+ return new THREE.Line( geometry, material );
50191
+
50192
+ case 'gaze':
50193
+ geometry = new THREE.RingGeometry( 0.02, 0.04, 32 ).translate( 0, 0, - 1 );
50194
+ material = new THREE.MeshBasicMaterial( { opacity: 0.5, transparent: true } );
50195
+ return new THREE.Mesh( geometry, material );
50196
+ }
50197
+ }
50198
+ */
50199
+ ic.controllers.forEach( (controller) => {
50200
+ controller.addEventListener( 'selectstart', onSelectStart );
50201
+ controller.addEventListener( 'selectend', onSelectEnd );
50202
+ /*
50203
+ controller.addEventListener( 'connected', function ( event ) {
50204
+ const mesh = buildController(event.data);
50205
+ mesh.scale.z = 0;
50206
+ this.add( mesh );
50207
+ } );
50208
+ controller.addEventListener( 'disconnected', function () {
50209
+ this.remove( this.children[ 0 ] );
50210
+ ic.controllers.forEach( (controllerTmp) => {
50211
+ controllerTmp = null;
50212
+ });
50213
+ //self.controllerGrip = null;
50214
+ } );
50215
+ */
50216
+ });
50217
+ }
50218
+ else if(ic.bAr) {
50219
+ //Add gestures here
50220
+ ic.gestures = new ControllerGestures(ic.renderer);
50221
+ ic.scene.add(ic.gestures.controller1);
50222
+ ic.scene.add(ic.gestures.controller2);
50223
+
50224
+ ic.gestures.addEventListener('tap', (ev) => {
50225
+ //if(!ic.mdl.visible) {
50226
+ // ic.mdl.visible = true;
50227
+ //}
50228
+
50229
+ const controller = ic.gestures.controller1;
50230
+ //ic.mdl.position.set( 0, 0, - 0.3 ).applyMatrix4( controller.matrixWorld );
50231
+ ic.mdl.position.set( -0.03, 0, - 0.3 ).applyMatrix4( controller.matrixWorld );
50232
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(0.1));
50233
+ ic.mdl.scale.copy(new THREE.Vector3( 0.001, 0.001, 0.001 ));
50234
+ });
50235
+
50236
+ ic.gestures.addEventListener('doubletap', (ev) => {
50237
+ const controller = ic.gestures.controller1;
50238
+ //ic.mdl.position.set( 0, 0, - 0.3 ).applyMatrix4( controller.matrixWorld );
50239
+ ic.mdl.position.set( -0.06, 0, - 0.6 ).applyMatrix4( controller.matrixWorld );
50240
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(10));
50241
+ ic.mdl.scale.copy(new THREE.Vector3( 0.005, 0.005, 0.005 ));
50242
+ });
50243
+ /*
50244
+ ic.gestures.addEventListener('swipe', (ev) => {
50245
+ // if(ic.mdl.visible) {
50246
+ // ic.mdl.visible = false;
50247
+ // }
50248
+ });
50249
+
50250
+ ic.gestures.addEventListener('pan', (ev) => {
50251
+ // if(ev.initialise !== undefined) {
50252
+ // thisClass.startPosition = ic.mdl.position.clone();
50253
+ // }
50254
+ // else {
50255
+ // const pos = thisClass.startPosition.clone().add(ev.delta.multiplyScalar(3));
50256
+ // ic.mdl.position.copy(pos);
50257
+ // }
50258
+ });
50259
+
50260
+ ic.gestures.addEventListener('pinch', (ev) => {
50261
+ // if(ev.initialise !== undefined) {
50262
+ // thisClass.startScale = ic.mdl.scale.clone();
50263
+ // }
50264
+ // else {
50265
+ // const scale = thisClass.startScale.clone().multiplyScalar(ev.scale);
50266
+ // ic.mdl.scale.copy(scale);
50267
+ // }
50268
+ });
50269
+
50270
+ ic.gestures.addEventListener('rotate', (ev) => {
50271
+ // if(ev.initialise !== undefined) {
50272
+ // thisClass.startQuaternion = ic.mdl.quaternion.clone();
50273
+ // }
50274
+ // else {
50275
+ // ic.mdl.quaternion.copy(thisClass.startQuaternion);
50276
+ // ic.mdl.rotateY(ev.theta);
50277
+ // }
50278
+ });
50279
+ */
50280
+ }
50281
+
50282
+ $("#" + me.pre + "VRButton").remove();
50283
+ $("#" + me.pre + "viewer").get(0).appendChild( ic.VRButtonCls.createButton( ic.renderer ) );
50284
+
50285
+ $("#" + me.pre + "ARButton").remove();
50286
+ $("#" + me.pre + "viewer").get(0).appendChild( ic.ARButtonCls.createButton( ic.renderer ) );
50287
+ }
50288
+
50289
+ getControllers() { let ic = this.icn3d; ic.icn3dui;
50290
+ const controllerModelFactory = new XRControllerModelFactory();
50291
+ /*
50292
+ const geometry = new THREE.BufferGeometry().setFromPoints( [
50293
+ new THREE.Vector3(0,0,0),
50294
+ new THREE.Vector3(0,0,-1)
50295
+ ]);
50296
+ const line = new THREE.Line( geometry );
50297
+ line.name = 'line';
50298
+ line.scale.z = 0;
50299
+ */
50300
+
50301
+ const controllers = [];
50302
+
50303
+ for(let i=0; i<=1; i++){
50304
+ const controller = ic.renderer.xr.getController( i );
50305
+ ic.dolly.add( controller );
50306
+
50307
+ // controller.add( line.clone() );
50308
+ controller.userData.selectPressed = false;
50309
+ ic.scene.add(controller);
50310
+
50311
+ controllers.push( controller );
50312
+
50313
+ const grip = ic.renderer.xr.getControllerGrip( i );
50314
+ grip.add( controllerModelFactory.createControllerModel( grip ));
50315
+ ic.scene.add( grip );
50316
+ }
50317
+
50318
+ return controllers;
50319
+ }
49819
50320
  }
49820
50321
 
49821
50322
  /**
@@ -51029,7 +51530,7 @@ class Draw {
51029
51530
  }
51030
51531
 
51031
51532
  //Draw the 3D structure. It rebuilds scene, applies previous color, applies the transformation, and renders the image.
51032
- draw(bVr) { let ic = this.icn3d, me = ic.icn3dui;
51533
+ draw(bVrAr) { let ic = this.icn3d, me = ic.icn3dui;
51033
51534
  if(ic.bRender && (!ic.hAtoms || Object.keys(ic.hAtoms) == 0)) ic.hAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
51034
51535
 
51035
51536
  ic.sceneCls.rebuildScene();
@@ -51067,7 +51568,7 @@ class Draw {
51067
51568
  }
51068
51569
 
51069
51570
  this.applyTransformation(ic._zoomFactor, ic.mouseChange, ic.quaternion);
51070
- this.render(bVr);
51571
+ this.render(bVrAr);
51071
51572
  }
51072
51573
 
51073
51574
  ic.impostorCls.clearImpostors();
@@ -51100,10 +51601,10 @@ class Draw {
51100
51601
  }
51101
51602
 
51102
51603
  //Render the scene and objects into pixels.
51103
- render(bVr) { let ic = this.icn3d; ic.icn3dui;
51604
+ render(bVrAr) { let ic = this.icn3d; ic.icn3dui;
51104
51605
  let thisClass = this;
51105
51606
  // setAnimationLoop is required for VR
51106
- if(bVr) {
51607
+ if(bVrAr) {
51107
51608
  ic.renderer.setAnimationLoop( function() {
51108
51609
  thisClass.render_base();
51109
51610
  });
@@ -51113,8 +51614,39 @@ class Draw {
51113
51614
  }
51114
51615
  }
51115
51616
 
51617
+ handleController( controller, dt, selectPressed) { let ic = this.icn3d; ic.icn3dui;
51618
+ // modified from https://github.com/NikLever/Learn-WebXR/blob/master/complete/lecture3_7/app.js
51619
+ if ( selectPressed ){
51620
+ /*
51621
+ ic.workingMatrix.identity().extractRotation( controller.matrixWorld );
51622
+
51623
+ ic.raycasterVR.ray.origin.setFromMatrixPosition( controller.matrixWorld );
51624
+ ic.raycasterVR.ray.direction.set( 0, 0, - 1 ).applyMatrix4( ic.workingMatrix );
51625
+
51626
+ const intersects = ic.raycasterVR.intersectObjects( ic.objects );
51627
+
51628
+ if (intersects.length>0){
51629
+ intersects[0].object.add(ic.highlightVR);
51630
+ ic.highlightVR.visible = true;
51631
+ }else{
51632
+ ic.highlightVR.visible = false;
51633
+ }
51634
+ */
51635
+ const speed = 5; //2;
51636
+ const quaternion = ic.dolly.quaternion.clone();
51637
+ //ic.dolly.quaternion.copy(ic.dummyCam.getWorldQuaternion());
51638
+ ic.dummyCam.getWorldQuaternion(ic.dolly.quaternion);
51639
+ ic.dolly.translateZ(-dt * speed);
51640
+ //ic.dolly.position.y = 0; // limit to a plane
51641
+ ic.dolly.quaternion.copy(quaternion);
51642
+
51643
+ }
51644
+ }
51645
+
51116
51646
  //Render the scene and objects into pixels.
51117
51647
  render_base() { let ic = this.icn3d, me = ic.icn3dui;
51648
+ let thisClass = this;
51649
+
51118
51650
  if(me.bNode) return;
51119
51651
 
51120
51652
  let cam = (ic.bControlGl && !me.bNode) ? window.cam : ic.cam;
@@ -51129,10 +51661,88 @@ class Draw {
51129
51661
  }
51130
51662
 
51131
51663
  ic.renderer.setPixelRatio( window.devicePixelRatio ); // r71
51664
+
51665
+ if(ic.bVr) {
51666
+ let dt = 0.04; // ic.clock.getDelta();
51667
+
51668
+ if (ic.controllers){
51669
+ // let result = this.getThumbStickMove();
51670
+ // let y = result.y * -1;
51671
+ // let pressed = result.pressed;
51672
+
51673
+ for(let i = 0, il = ic.controllers.length; i < il; ++i) {
51674
+ let controller = ic.controllers[i];
51675
+ dt = (i % 2 == 0) ? dt : -dt; // dt * y;
51676
+ thisClass.handleController( controller, dt, controller.userData.selectPressed );
51677
+ //thisClass.handleController( controller, dt, pressed );
51678
+ }
51679
+ }
51680
+ }
51681
+ else if(ic.bAr) {
51682
+ if ( ic.renderer.xr.isPresenting ){
51683
+ ic.gestures.update();
51684
+ }
51685
+ }
51686
+
51132
51687
  if(ic.scene) {
51133
51688
  ic.renderer.render(ic.scene, cam);
51134
51689
  }
51135
51690
  }
51691
+
51692
+ getThumbStickMove() { let ic = this.icn3d; ic.icn3dui;
51693
+ let y = 0;
51694
+ let btnPressed = false;
51695
+
51696
+ if ( ic.renderer.xr.isPresenting ){
51697
+ const session = ic.renderer.xr.getSession();
51698
+ const inputSources = session.inputSources;
51699
+
51700
+ if ( ic.getInputSources ){
51701
+ //const info = [];
51702
+
51703
+ inputSources.forEach( inputSource => {
51704
+ const gp = inputSource.gamepad;
51705
+ gp.axes;
51706
+ gp.buttons;
51707
+ const mapping = gp.mapping;
51708
+ ic.useStandard = (mapping == 'xr-standard');
51709
+ inputSource.handedness;
51710
+ const profiles = inputSource.profiles;
51711
+ ic.gamepadType = "";
51712
+ profiles.forEach( profile => {
51713
+ if (profile.indexOf('touchpad')!=-1) ic.gamepadType = 'touchpad';
51714
+ if (profile.indexOf('thumbstick')!=-1) ic.gamepadType = 'thumbstick';
51715
+ });
51716
+ inputSource.targetRayMode;
51717
+ //info.push({ gamepad, handedness, profiles, targetRayMode });
51718
+ });
51719
+
51720
+ //console.log( JSON.stringify(info) );
51721
+
51722
+ ic.getInputSources = false;
51723
+ }else if (ic.useStandard && ic.gamepadType != ""){
51724
+ inputSources.forEach( inputSource => {
51725
+ const gp = inputSource.gamepad;
51726
+ const thumbstick = (ic.gamepadType=='thumbstick');
51727
+ //{"trigger":{"button":0},"touchpad":{"button":2,"xAxis":0,"yAxis":1}},
51728
+ //"squeeze":{"button":1},"thumbstick":{"button":3,"xAxis":2,"yAxis":3},"button":{"button":6}}}
51729
+ const xaxisOffset = (thumbstick) ? 2 : 0;
51730
+ const btnIndex = (thumbstick) ? 3 : 2;
51731
+ btnPressed = gp.buttons[btnIndex].pressed;
51732
+ // if ( inputSource.handedness == 'right') {
51733
+ // } else if ( inputSource.handedness == 'left') {
51734
+ // }
51735
+
51736
+ //https://beej.us/blog/data/javascript-gamepad/
51737
+ // x,y-axis values are between -1 and 1
51738
+ gp.axes[xaxisOffset];
51739
+ y = gp.axes[xaxisOffset + 1];
51740
+ });
51741
+ }
51742
+ }
51743
+
51744
+ return {'y': y, 'pressed': btnPressed};
51745
+ }
51136
51746
  }
51137
51747
 
51138
51748
  /**
@@ -51739,7 +52349,7 @@ class SaveFile {
51739
52349
  }
51740
52350
 
51741
52351
  //getAtomPDB: function(atomHash, bPqr, bPdb, bNoChem) { let ic = this.icn3d, me = ic.icn3dui;
51742
- getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader) { let ic = this.icn3d, me = ic.icn3dui;
52352
+ getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb) { let ic = this.icn3d, me = ic.icn3dui;
51743
52353
  let pdbStr = '';
51744
52354
 
51745
52355
  // get all phosphate groups in lipids
@@ -51884,9 +52494,19 @@ class SaveFile {
51884
52494
  let molNum = 1, prevStru = '';
51885
52495
  //pdbStr += '\n';
51886
52496
 
52497
+ let addedChainResiHash = {};
51887
52498
  for(let i in atomHash) {
51888
52499
  let atom = ic.atoms[i];
51889
52500
 
52501
+ let chainResi = atom.chain + '_' + atom.resi;
52502
+ if(chainResi2pdb && chainResi2pdb.hasOwnProperty(chainResi)) {
52503
+ if(!addedChainResiHash.hasOwnProperty(chainResi)) {
52504
+ pdbStr += chainResi2pdb[chainResi];
52505
+ addedChainResiHash[chainResi] = 1;
52506
+ }
52507
+ continue;
52508
+ }
52509
+
51890
52510
  // remove chemicals
51891
52511
  if(bNoChem && atom.het) continue;
51892
52512
 
@@ -51899,8 +52519,9 @@ class SaveFile {
51899
52519
 
51900
52520
  if(bMulStruc) pdbStr += 'MODEL ' + molNum + '\n';
51901
52521
 
51902
- // add header
51903
- if(!bNoHeader) pdbStr += this.getPDBHeader(molNum - 1, stru2header);
52522
+ // add header
52523
+ let mutantInfo = (chainResi2pdb) ? "Mutated chain_residue " + Object.keys(chainResi2pdb) + '; ' : '';
52524
+ if(!bNoHeader) pdbStr += this.getPDBHeader(molNum - 1, stru2header, mutantInfo);
51904
52525
 
51905
52526
  prevStru = atom.structure;
51906
52527
  ++molNum;
@@ -52128,21 +52749,25 @@ class SaveFile {
52128
52749
 
52129
52750
  return pdbStr;
52130
52751
  }
52131
- getPDBHeader(struNum, stru2header) { let ic = this.icn3d; ic.icn3dui;
52752
+ getPDBHeader(struNum, stru2header, mutantInfo) { let ic = this.icn3d; ic.icn3dui;
52132
52753
  if(struNum === undefined) struNum = 0;
52133
52754
 
52134
52755
  let pdbStr = '';
52135
52756
  let stru = Object.keys(ic.structures)[struNum];
52136
- pdbStr += 'HEADER PDB From iCn3D'.padEnd(62, ' ') + stru + '\n';
52757
+ let id = (mutantInfo) ? stru + '2' : stru;
52758
+ pdbStr += 'HEADER PDB From iCn3D'.padEnd(62, ' ') + id + '\n';
52137
52759
 
52138
52760
  if(struNum == 0) {
52139
52761
  let title =(ic.molTitle.length > 50) ? ic.molTitle.substr(0,47) + '...' : ic.molTitle;
52140
52762
  // remove quotes
52141
52763
  if(title.indexOf('"') != -1) title = '';
52764
+ if(mutantInfo) {
52765
+ title = mutantInfo + title;
52766
+ }
52142
52767
  pdbStr += 'TITLE ' + title + '\n';
52143
52768
  }
52144
52769
 
52145
- if(stru2header) {
52770
+ if(stru2header && stru2header[stru]) {
52146
52771
  pdbStr += stru2header[stru];
52147
52772
  }
52148
52773
 
@@ -52205,6 +52830,9 @@ class SaveFile {
52205
52830
  if(me.cfg.cid !== undefined) {
52206
52831
  url = "https://www.ncbi.nlm.nih.gov/pccompound/?term=";
52207
52832
  }
52833
+ else if(me.cfg.afid !== undefined) {
52834
+ url = "https://alphafold.ebi.ac.uk/search/text/";
52835
+ }
52208
52836
  else {
52209
52837
  //if(ic.inputid.indexOf(",") !== -1) {
52210
52838
  if(Object.keys(ic.structures).length > 1) {
@@ -52224,11 +52852,17 @@ class SaveFile {
52224
52852
 
52225
52853
  if(idArray.length === 1) {
52226
52854
  url += ic.inputid;
52227
- if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to Structure Summary " + ic.inputid + ": " + url, false);
52855
+ if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to " + ic.inputid + ": " + url, false);
52228
52856
  }
52229
52857
  else if(idArray.length === 2) {
52230
- url += idArray[0] + " OR " + idArray[1];
52231
- if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to structures " + idArray[0] + " and " + idArray[1] + ": " + url, false);
52858
+ if(me.cfg.afid) {
52859
+ url += idArray[0] + " " + idArray[1];
52860
+ }
52861
+ else {
52862
+ url += idArray[0] + " OR " + idArray[1];
52863
+ }
52864
+
52865
+ if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to structures " + idArray[0] + " and " + idArray[1] + ": " + url, false);
52232
52866
  }
52233
52867
  }
52234
52868
 
@@ -54908,7 +55542,7 @@ class SetMenu {
54908
55542
  html += "<ul>";
54909
55543
  html += me.htmlCls.setHtmlCls.getLink('mn1_vastplus', 'NCBI VAST+ (PDB Assembly)' + me.htmlCls.wifiStr);
54910
55544
  html += me.htmlCls.setHtmlCls.getLink('mn1_vast', 'NCBI VAST (PDB)' + me.htmlCls.wifiStr);
54911
- html += me.htmlCls.setHtmlCls.getLink('mn1_foldseek', 'Foldseek (PDB & AalphaFold)' + me.htmlCls.wifiStr);
55545
+ html += me.htmlCls.setHtmlCls.getLink('mn1_foldseek', 'Foldseek (PDB & AlphaFold)' + me.htmlCls.wifiStr);
54912
55546
  html += "</ul>";
54913
55547
 
54914
55548
  html += "<li><span>Retrieve by ID</span>";
@@ -55224,11 +55858,22 @@ class SetMenu {
55224
55858
 
55225
55859
  html += "<li>-</li>";
55226
55860
 
55861
+ let liStr = "<li><a href='";
55862
+
55227
55863
  html += "<li><span>VR & AR Hints</span>";
55228
55864
  html += "<ul>";
55229
- html += "<li><span>VR: VR Headsets</span>";
55230
- html += "<li><span>AR: Chrome in Android</span>";
55865
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#vr' target='_blank'>VR: VR Headsets</a></li>";
55866
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#ar' target='_blank'>AR: Chrome in Android</a></li>";
55231
55867
  html += "</ul>";
55868
+ /*
55869
+ let liStr = "<li><a href='";
55870
+
55871
+ html += "<ul class='icn3d-mn-item'>";
55872
+
55873
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#about' target='_blank'>About iCn3D<span style='font-size:0.9em'> " + me.REVISION + "</span></a></li>";
55874
+
55875
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#gallery' target='_blank'>Live Gallery " + me.htmlCls.wifiStr + "</a></li>";
55876
+ */
55232
55877
 
55233
55878
  html += me.htmlCls.setHtmlCls.getLink('mn6_sidebyside', 'Side by Side');
55234
55879
 
@@ -57752,7 +58397,7 @@ class SetDialog {
57752
58397
  html += "</div>";
57753
58398
  html += me.htmlCls.divStr + "tracktab4'>";
57754
58399
  html += "Track Title: " + me.htmlCls.inputTextStr + "id='" + me.pre + "track_title' placeholder='track title' size=16> <br><br>";
57755
- html += "Track Text(e.g., \"152 G, 155-156 RR\" defines a character \"G\" at the position 152 and two continuous characters \"RR\" at positions from 155 to 156. The starting position is 1): <br>";
58400
+ html += "Track Text (e.g., \"2 G, 5-6 RR\" defines a character \"G\" at the position 2 and two continuous characters \"RR\" at positions from 5 to 6. The starting position is 1): <br>";
57756
58401
  html += "<textarea id='" + me.pre + "track_text' rows='5' style='width: 100%; height: " +(2*me.htmlCls.MENU_HEIGHT) + "px; padding: 0px; border: 0px;'></textarea><br><br>";
57757
58402
  html += me.htmlCls.buttonStr + "addtrack_button4'>Add Track</button>";
57758
58403
  html += "</div>";
@@ -58077,6 +58722,11 @@ class Events {
58077
58722
  let pos = hostUrl.indexOf("?");
58078
58723
  hostUrl = (pos == -1) ? hostUrl : hostUrl.substr(0, pos);
58079
58724
 
58725
+ // some URLs from VAST search are like https://www.ncbi.nlm.nih.gov/Structure/vast/icn3d/
58726
+ if(hostUrl == 'https://www.ncbi.nlm.nih.gov/Structure/vast/icn3d/') {
58727
+ hostUrl = 'https://www.ncbi.nlm.nih.gov/Structure/icn3d/';
58728
+ }
58729
+
58080
58730
  ic.definedSetsCls.clickCustomAtoms();
58081
58731
  ic.definedSetsCls.clickCommand_apply();
58082
58732
  ic.definedSetsCls.clickModeswitch();
@@ -60248,6 +60898,7 @@ class AlignSeq {
60248
60898
  let index = 0,
60249
60899
  prevResCnt2nd = 0;
60250
60900
  let firstChainid, oriChainid;
60901
+
60251
60902
  // for(let i in ic.alnChains) {
60252
60903
  for (let m = 0, ml = alignChainArray.length; m < ml; ++m) {
60253
60904
  let i = alignChainArray[m];
@@ -60396,6 +61047,9 @@ class AlignSeq {
60396
61047
  resiHtmlArray[j] += '<span class="icn3d-sheet">&nbsp;</span>';
60397
61048
  }
60398
61049
  }
61050
+ else {
61051
+ resiHtmlArray[j] += '<span class="icn3d-sheet">&nbsp;</span>';
61052
+ }
60399
61053
  } else if (text == 'c') {
60400
61054
  resiHtmlArray[j] += '<span class="icn3d-coil">&nbsp;</span>';
60401
61055
  } else if (text == 'o') {
@@ -61452,17 +62106,20 @@ class Alternate {
61452
62106
  let viewSelectionAtomsCount = Object.keys(ic.viewSelectionAtoms).length;
61453
62107
  let allAtomsCount = Object.keys(ic.atoms).length;
61454
62108
 
61455
- ic.dAtoms = {};
62109
+ //ic.dAtoms = {};
62110
+
62111
+ // 1. alternate all structures
62112
+ //let moleculeArray = Object.keys(ic.structures);
61456
62113
 
61457
- // alternate all displayed structures
61458
- let moleculeArray = Object.keys(ic.structures);
61459
- // only alternate selected structures
61460
- // let structureHash = {};
61461
- // for(let i in ic.hAtoms) {
61462
- // let structure = ic.atoms[i].structure;
61463
- // structureHash[structure] = 1;
61464
- // }
61465
- // let moleculeArray = Object.keys(structureHash);
62114
+ // 2. only alternate displayed structures
62115
+ let structureHash = {};
62116
+ for(let i in ic.viewSelectionAtoms) {
62117
+ let structure = ic.atoms[i].structure;
62118
+ structureHash[structure] = 1;
62119
+ }
62120
+ let moleculeArray = Object.keys(structureHash);
62121
+
62122
+ ic.dAtoms = {};
61466
62123
 
61467
62124
  for(let i = 0, il = moleculeArray.length; i < il; ++i) {
61468
62125
  let structure = moleculeArray[i];
@@ -62206,7 +62863,7 @@ class ContactMap {
62206
62863
 
62207
62864
  let graphStr = '{\n';
62208
62865
 
62209
- let struc1 = (ic.structures.length > 0) ? ic.structures[0] : 'STRU';
62866
+ let struc1 = (ic.structures.length > 0) ? ic.structures[0] : 'stru';
62210
62867
  let len1 = nodeArray1.length,
62211
62868
  len2 = nodeArray2.length;
62212
62869
  let factor = 1;
@@ -64554,6 +65211,8 @@ class iCn3D {
64554
65211
  this.bNotLoadStructure = false;
64555
65212
 
64556
65213
  this.InputfileData = '';
65214
+ this.bVr = false; // cflag to indicate whether in VR state
65215
+ this.bAr = false; // cflag to indicate whether in VR state
64557
65216
 
64558
65217
  // default color range for Add Custom Color button in the Sequence & Annotation window
64559
65218
  this.startColor = 'blue';
@@ -64885,7 +65544,7 @@ class iCn3DUI {
64885
65544
  //even when multiple iCn3D viewers are shown together.
64886
65545
  this.pre = this.cfg.divid + "_";
64887
65546
 
64888
- this.REVISION = '3.12.3';
65547
+ this.REVISION = '3.12.5';
64889
65548
 
64890
65549
  // In nodejs, iCn3D defines "window = {navigator: {}}"
64891
65550
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -65053,6 +65712,11 @@ iCn3DUI.prototype.show3DStructure = function(pdbStr) { let me = this;
65053
65712
 
65054
65713
  if(pdbStr) { // input pdbStr
65055
65714
  ic.init();
65715
+
65716
+ ic.bInputfile = true;
65717
+ ic.InputfileType = 'pdb';
65718
+ ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + pdbStr : pdbStr;
65719
+
65056
65720
  ic.pdbParserCls.loadPdbData(pdbStr);
65057
65721
 
65058
65722
  if(me.cfg.resdef !== undefined && me.cfg.chains !== undefined) {