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.module.js CHANGED
@@ -12662,7 +12662,7 @@ class ShareLink {
12662
12662
  if(bPngHtml) url += "&random=" + parseInt(Math.random() * 1000); // generate a new shorten URL and thus image name everytime
12663
12663
  //var inputid =(ic.inputid) ? ic.inputid : "custom";
12664
12664
  let inputid = Object.keys(ic.structures).join('_');
12665
- if(inputid == 'STRU') {
12665
+ if(inputid == 'stru') {
12666
12666
  if(ic.filename) {
12667
12667
  inputid = ic.filename;
12668
12668
  }
@@ -17711,7 +17711,7 @@ class ViewInterPairs {
17711
17711
  ic.bGraph = true;
17712
17712
  // show only displayed set in 2D graph
17713
17713
  if(Object.keys(atomSet2).length + Object.keys(atomSet1).length > Object.keys(ic.dAtoms).length) {
17714
- ic.graphStr = ic.getGraphCls.getGraphDataForDisplayed();
17714
+ ic.graphStr = ic.selectionCls.getGraphDataForDisplayed();
17715
17715
  }
17716
17716
 
17717
17717
  if(ic.bD3 === undefined) {
@@ -20394,13 +20394,14 @@ class Scap {
20394
20394
  //snp: 6M0J_E_484_K,6M0J_E_501_Y,6M0J_E_417_N
20395
20395
  let snpStr = '';
20396
20396
  let snpArray = snp.split(','); //stru_chain_resi_snp
20397
- let atomHash = {}, snpResidArray = [];
20397
+ let atomHash = {}, snpResidArray = [], chainResi2pdb = {};
20398
20398
  for(let i = 0, il = snpArray.length; i < il; ++i) {
20399
20399
  let idArray = snpArray[i].split('_'); //stru_chain_resi_snp
20400
20400
 
20401
20401
  let resid = idArray[0] + '_' + idArray[1] + '_' + idArray[2];
20402
20402
  atomHash = me.hashUtilsCls.unionHash(atomHash, ic.residues[resid]);
20403
20403
  snpResidArray.push(resid);
20404
+ chainResi2pdb[idArray[1] + '_' + idArray[2]] = '';
20404
20405
 
20405
20406
  snpStr += idArray[1] + '_' + idArray[2] + '_' + idArray[3];
20406
20407
  if(i != il -1) snpStr += ',';
@@ -20434,7 +20435,7 @@ class Scap {
20434
20435
  /// let pdbStr = ic.saveFileCls.getPDBHeader() + ic.saveFileCls.getAtomPDB(ic.hAtoms);
20435
20436
  let pdbStr = ic.saveFileCls.getAtomPDB(ic.hAtoms);
20436
20437
 
20437
- let url = "https://www.ncbi.nlm.nih.gov/Structure/scap/scap.cgi";
20438
+ let url = me.htmlCls.baseUrl + "scap/scap.cgi";
20438
20439
 
20439
20440
  let pdbid = Object.keys(ic.structures)[0]; //Object.keys(ic.structures).toString();
20440
20441
  let dataObj = {'pdb': pdbStr, 'snp': snpStr, 'pdbid': pdbid, 'v': '2'};
@@ -20474,19 +20475,64 @@ class Scap {
20474
20475
  ic.chainsMapping[chainid][resid] = me.utilsCls.residueName2Abbr(atom.resn) + atom.resi;
20475
20476
  }
20476
20477
 
20478
+ //ic.hAtoms = {};
20479
+ //ic.loadPDBCls.loadPDB(pdbData, pdbid, false, false, bAddition);
20480
+ //let hAtom2 = me.hashUtilsCls.cloneHash(ic.hAtoms);
20481
+
20482
+ // get the mutant pdb
20483
+ let lines = pdbData.split('\n');
20484
+ let allChainResiHash = {};
20485
+ for (let i in lines) {
20486
+ let line = lines[i];
20487
+ let record = line.substr(0, 6);
20488
+
20489
+ if (record === 'ATOM ' || record === 'HETATM') {
20490
+ let chain = line.substr(20, 2).trim();
20491
+ if(chain === '') chain = 'A';
20492
+
20493
+ let resi = line.substr(22, 5).trim();
20494
+ let chainResi = chain + '_' + resi;
20495
+
20496
+ if(chainResi2pdb.hasOwnProperty(chainResi)) {
20497
+ chainResi2pdb[chainResi] += line + '\n';
20498
+ }
20499
+
20500
+ allChainResiHash[chainResi] = 1;
20501
+ }
20502
+ }
20503
+
20504
+ // get the full mutatnt PDB
20505
+ let pdbDataMutant = ic.saveFileCls.getAtomPDB(ic.atoms, false, false, false, chainResi2pdb);
20477
20506
  ic.hAtoms = {};
20478
- ic.loadPDBCls.loadPDB(pdbData, pdbid, false, false, bAddition);
20479
- let hAtom2 = me.hashUtilsCls.cloneHash(ic.hAtoms);
20507
+ let bMutation = true;
20508
+ ic.loadPDBCls.loadPDB(pdbDataMutant, pdbid, false, false, bMutation, bAddition);
20509
+ //let allAtoms2 = me.hashUtilsCls.cloneHash(ic.hAtoms);
20510
+
20511
+ ic.setStyleCls.setAtomStyleByOptions(ic.opts);
20512
+ ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
20513
+
20514
+ // get the mutant residues in the sphere
20515
+ let hAtom2 = {};
20516
+ for(let serial in ic.hAtoms) {
20517
+ let atom = ic.atoms[serial];
20518
+ let chainResi = atom.chain + '_' + atom.resi;
20519
+ if(allChainResiHash.hasOwnProperty(chainResi)) {
20520
+ hAtom2[serial] = 1;
20521
+ }
20522
+ }
20480
20523
 
20481
- ic.hAtoms = me.hashUtilsCls.unionHash(ic.hAtoms, hAtom1);
20482
- ic.dAtoms = ic.hAtoms;
20524
+ ic.hAtoms = me.hashUtilsCls.unionHash(hAtom1, hAtom2);
20525
+ //ic.hAtoms = me.hashUtilsCls.unionHash(hAtom1, allAtoms2);
20526
+ ic.dAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
20527
+ //ic.dAtoms = ic.hAtoms;
20483
20528
 
20484
20529
  ic.transformCls.zoominSelection();
20485
20530
  ic.setOptionCls.setStyle('proteins', 'stick');
20486
20531
 
20487
- ic.opts['color'] = 'chain';
20488
- ic.setColorCls.setColorByOptions(ic.opts, ic.dAtoms);
20532
+ //ic.opts['color'] = 'chain';
20533
+ //ic.setColorCls.setColorByOptions(ic.opts, ic.dAtoms);
20489
20534
  for(let serial in hAtom2) {
20535
+ //for(let serial in allAtoms2) {
20490
20536
  let atom = ic.atoms[serial];
20491
20537
  if(!atom.het) {
20492
20538
  // use the same color as the wild type
@@ -20513,13 +20559,19 @@ class Scap {
20513
20559
  }
20514
20560
  }
20515
20561
 
20562
+ // ic.hAtoms = me.hashUtilsCls.unionHash(hAtom1, hAtoms2);
20563
+ // ic.dAtoms = me.hashUtilsCls.cloneHash(ic.hAtoms);
20564
+ // //ic.dAtoms = ic.hAtoms;
20565
+
20566
+ // ic.transformCls.zoominSelection();
20567
+ // ic.setOptionCls.setStyle('proteins', 'stick');
20568
+
20516
20569
  if(bPdb) {
20517
- let pdbStr = '';
20518
- /// pdbStr += ic.saveFileCls.getPDBHeader();
20519
- pdbStr += ic.saveFileCls.getAtomPDB(ic.hAtoms);
20570
+ //let pdbStr = '';
20571
+ //pdbStr += ic.saveFileCls.getAtomPDB(ic.hAtoms);
20520
20572
 
20521
20573
  let file_pref =(ic.inputid) ? ic.inputid : "custom";
20522
- ic.saveFileCls.saveFile(file_pref + '_' + snpStr + '.pdb', 'text', [pdbStr]);
20574
+ ic.saveFileCls.saveFile(file_pref + '_' + snpStr + '.pdb', 'text', [pdbDataMutant]);
20523
20575
 
20524
20576
  ic.drawCls.draw();
20525
20577
  }
@@ -21220,23 +21272,23 @@ class LoadPDB {
21220
21272
  }
21221
21273
  else {
21222
21274
  // remove the last structure
21223
- if(ic.alertAlt) {
21224
- let nStru = ic.oriNStru + 1; //Object.keys(ic.structures).length;
21225
- let chainArray = ic.structures[nStru - 1];
21226
- for(let i = 0, il = (chainArray) ? chainArray.length : 0; i < il; ++i) {
21227
- for(let j in ic.chains[chainArray[i]]) {
21228
- delete ic.atoms[j];
21229
- delete ic.hAtoms[j];
21230
- delete ic.dAtoms[j];
21231
- }
21232
- delete ic.chains[chainArray[i]];
21233
- }
21234
-
21235
- delete ic.structures[nStru - 1];
21236
- }
21237
- else {
21275
+ // if(ic.alertAlt) {
21276
+ // let nStru = ic.oriNStru + 1; //Object.keys(ic.structures).length;
21277
+ // let chainArray = ic.structures[nStru - 1];
21278
+ // for(let i = 0, il = (chainArray) ? chainArray.length : 0; i < il; ++i) {
21279
+ // for(let j in ic.chains[chainArray[i]]) {
21280
+ // delete ic.atoms[j];
21281
+ // delete ic.hAtoms[j];
21282
+ // delete ic.dAtoms[j];
21283
+ // }
21284
+ // delete ic.chains[chainArray[i]];
21285
+ // }
21286
+
21287
+ // delete ic.structures[nStru - 1];
21288
+ // }
21289
+ // else {
21238
21290
  ic.oriNStru = (ic.structures) ? Object.keys(ic.structures).length : 0;
21239
- }
21291
+ // }
21240
21292
 
21241
21293
  moleculeNum = ic.oriNStru + 1; //Object.keys(ic.structures).length + 1;
21242
21294
  // Concatenation of two pdbs will have several atoms for the same serial
@@ -21253,12 +21305,12 @@ class LoadPDB {
21253
21305
 
21254
21306
  //let chainMissingResidueArray = {}
21255
21307
 
21256
- let id = (pdbid) ? pdbid : 'STRU';
21308
+ let id = (pdbid) ? pdbid : 'stru';
21257
21309
 
21258
21310
  let prevMissingChain = '';
21259
21311
  let CSerial, prevCSerial, OSerial, prevOSerial;
21260
21312
 
21261
- let structure = "STRU";
21313
+ let structure = "stru";
21262
21314
 
21263
21315
  let bHeader = false;
21264
21316
 
@@ -21274,18 +21326,19 @@ class LoadPDB {
21274
21326
 
21275
21327
  if(id == '') {
21276
21328
  if(bAppend) {
21277
- id = "STRU";
21329
+ id = "stru";
21278
21330
  }
21279
21331
  else {
21280
- //if(!ic.inputid) ic.inputid = 'STRU';
21281
- id = (ic.inputid && ic.inputid.indexOf('/') == -1) ? ic.inputid.substr(0, 10) : "STRU"; //ic.filename.substr(0, 4);
21332
+ //if(!ic.inputid) ic.inputid = 'stru';
21333
+ id = (ic.inputid && ic.inputid.indexOf('/') == -1) ? ic.inputid.substr(0, 10) : "stru"; //ic.filename.substr(0, 4);
21282
21334
  }
21283
21335
  }
21284
21336
 
21285
21337
  structure = id;
21286
21338
 
21287
- //if(id == 'STRU' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
21288
- if(id == 'STRU' || bMutation) { // bMutation: side chain prediction
21339
+ //if(id == 'stru' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
21340
+ //if(id == 'stru' || bMutation) { // bMutation: side chain prediction
21341
+ if(id == 'stru') {
21289
21342
  structure = (moleculeNum === 1) ? id : id + moleculeNum.toString();
21290
21343
  }
21291
21344
 
@@ -21355,17 +21408,17 @@ class LoadPDB {
21355
21408
  ic.ssbondpnts[id].push(resid1);
21356
21409
  ic.ssbondpnts[id].push(resid2);
21357
21410
  } else if (record === 'REMARK') {
21358
- let type = parseInt(line.substr(7, 3));
21411
+ let remarkType = parseInt(line.substr(7, 3));
21359
21412
 
21360
21413
  if(line.indexOf('1/2 of bilayer thickness:') !== -1) { // OPM transmembrane protein
21361
21414
  ic.halfBilayerSize = parseFloat(line.substr(line.indexOf(':') + 1).trim());
21362
21415
  }
21363
- else if (type == 210) {
21416
+ else if (remarkType == 210) {
21364
21417
  if((line.substr(11, 32).trim() == 'EXPERIMENT TYPE') && line.substr(45).trim() == 'NMR') {
21365
21418
  bNMR = true;
21366
21419
  }
21367
21420
  }
21368
- else if (type == 350 && line.substr(13, 5) == 'BIOMT') {
21421
+ else if (remarkType == 350 && line.substr(13, 5) == 'BIOMT') {
21369
21422
  let n = parseInt(line[18]) - 1;
21370
21423
  //var m = parseInt(line.substr(21, 2));
21371
21424
  let m = parseInt(line.substr(21, 2)) - 1; // start from 1
@@ -21377,7 +21430,7 @@ class LoadPDB {
21377
21430
  ic.biomtMatrices[m].elements[n + 12] = parseFloat(line.substr(54, 14));
21378
21431
  }
21379
21432
  // missing residues
21380
- else if (type == 465 && line.substr(18, 1) == ' ' && line.substr(20, 1) == ' ' && line.substr(21, 1) != 'S') {
21433
+ else if (remarkType == 465 && line.substr(18, 1) == ' ' && line.substr(20, 1) == ' ' && line.substr(21, 1) != 'S') {
21381
21434
  let resn = line.substr(15, 3);
21382
21435
  //let chain = line.substr(19, 1);
21383
21436
  let chain = line.substr(18, 2).trim();
@@ -21403,7 +21456,7 @@ class LoadPDB {
21403
21456
  }
21404
21457
 
21405
21458
  }
21406
- else if (type == 900 && ic.emd === undefined && line.substr(34).trim() == 'RELATED DB: EMDB') {
21459
+ else if (remarkType == 900 && ic.emd === undefined && line.substr(34).trim() == 'RELATED DB: EMDB') {
21407
21460
  //REMARK 900 RELATED ID: EMD-3906 RELATED DB: EMDB
21408
21461
  ic.emd = line.substr(23, 11).trim();
21409
21462
  }
@@ -21413,11 +21466,12 @@ class LoadPDB {
21413
21466
  ic.organism = ic.organism.substr(0, ic.organism.length - 1);
21414
21467
  } else if (record === 'ENDMDL') {
21415
21468
  ++moleculeNum;
21416
- id = 'STRU';
21469
+ id = 'stru';
21417
21470
 
21418
21471
  structure = id;
21419
- //if(id == 'STRU' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
21420
- if(id == 'STRU' || bMutation) { // bMutation: side chain prediction
21472
+ //if(id == 'stru' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
21473
+ //if(id == 'stru' || bMutation) { // bMutation: side chain prediction
21474
+ if(id == 'stru') {
21421
21475
  structure = (moleculeNum === 1) ? id : id + moleculeNum.toString();
21422
21476
  }
21423
21477
 
@@ -21439,8 +21493,9 @@ class LoadPDB {
21439
21493
  }
21440
21494
  } else if (record === 'ATOM ' || record === 'HETATM') {
21441
21495
  structure = id;
21442
- //if(id == 'STRU' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
21443
- if(id == 'STRU' || bMutation) { // bMutation: side chain prediction
21496
+ //if(id == 'stru' || bMutation || (bAppend && id.length != 4)) { // bMutation: side chain prediction
21497
+ //if(id == 'stru' || bMutation) { // bMutation: side chain prediction
21498
+ if(id == 'stru') {
21444
21499
  structure = (moleculeNum === 1) ? id : id + moleculeNum.toString();
21445
21500
  }
21446
21501
 
@@ -21692,23 +21747,9 @@ class LoadPDB {
21692
21747
  // remove the reference
21693
21748
  lines = null;
21694
21749
 
21695
- let curChain, curResi, curResAtoms = [];
21696
- // refresh for atoms in each residue
21697
- let refreshBonds = function(f) {
21698
- let n = curResAtoms.length;
21699
- for (let j = 0; j < n; ++j) {
21700
- let atom0 = curResAtoms[j];
21701
- for (let k = j + 1; k < n; ++k) {
21702
- let atom1 = curResAtoms[k];
21703
- if (atom0.alt === atom1.alt && me.utilsCls.hasCovalentBond(atom0, atom1)) {
21704
- //if (me.utilsCls.hasCovalentBond(atom0, atom1)) {
21705
- atom0.bonds.push(atom1.serial);
21706
- atom1.bonds.push(atom0.serial);
21707
- }
21708
- }
21709
- f && f(atom0);
21710
- }
21711
- };
21750
+ let firstAtom = ic.firstAtomObjCls.getFirstAtomObj(ic.hAtoms);
21751
+ let curChain = firstAtom.chain, curResi = firstAtom.resi, curResAtoms = [];
21752
+
21712
21753
  let pmin = new THREE.Vector3( 9999, 9999, 9999);
21713
21754
  let pmax = new THREE.Vector3(-9999,-9999,-9999);
21714
21755
  let psum = new THREE.Vector3();
@@ -21718,7 +21759,9 @@ class LoadPDB {
21718
21759
  let lipidResidHash = {};
21719
21760
 
21720
21761
  // assign atoms
21721
- for (let i in ic.atoms) {
21762
+ let prevCarbonArray = [firstAtom]; // add a dummy atom
21763
+ //for (let i in ic.atoms) {
21764
+ for (let i in ic.hAtoms) {
21722
21765
  let atom = ic.atoms[i];
21723
21766
  let coord = atom.coord;
21724
21767
  psum.add(coord);
@@ -21767,22 +21810,28 @@ class LoadPDB {
21767
21810
  atom.color = me.parasCls.atomColors[atom.elem];
21768
21811
  }
21769
21812
 
21770
- if (!(curChain === atom.chain && curResi === atom.resi)) {
21771
- // a new residue, add the residue-residue bond beides the regular bonds
21772
- refreshBonds(function (atom0) {
21773
- if (((atom0.name === 'C' && atom.name === 'N') || (atom0.name === 'O3\'' && atom.name === 'P')) && me.utilsCls.hasCovalentBond(atom0, atom)) {
21774
- atom0.bonds.push(atom.serial);
21775
- atom.bonds.push(atom0.serial);
21776
- }
21777
- });
21813
+ if(!(curChain === atom.chain && curResi === atom.resi)) {
21814
+ // a new residue, add the residue-residue bond beides the regular bonds
21815
+ this.refreshBonds(curResAtoms, prevCarbonArray[0]);
21816
+
21817
+ prevCarbonArray.splice(0, 1); // remove the first carbon
21818
+
21778
21819
  curChain = atom.chain;
21779
21820
  curResi = atom.resi;
21780
21821
  //curInsc = atom.insc;
21781
21822
  curResAtoms.length = 0;
21782
21823
  }
21783
21824
  curResAtoms.push(atom);
21825
+
21826
+ if(atom.name === 'C' || atom.name === 'O3\'') {
21827
+ prevCarbonArray.push(atom);
21828
+ }
21784
21829
  } // end of for
21785
21830
 
21831
+ // last residue
21832
+ //refreshBonds();
21833
+ this.refreshBonds(curResAtoms, prevCarbonArray[0]);
21834
+
21786
21835
  // reset lipid
21787
21836
  for(let resid in lipidResidHash) {
21788
21837
  let atomHash = ic.residues[resid];
@@ -21799,9 +21848,6 @@ class LoadPDB {
21799
21848
  }
21800
21849
  }
21801
21850
 
21802
- // last residue
21803
- refreshBonds();
21804
-
21805
21851
  ic.pmin = pmin;
21806
21852
  ic.pmax = pmax;
21807
21853
 
@@ -21837,6 +21883,28 @@ class LoadPDB {
21837
21883
  }
21838
21884
  }
21839
21885
 
21886
+ // refresh for atoms in each residue
21887
+ refreshBonds(curResAtoms, prevCarbon) { let ic = this.icn3d, me = ic.icn3dui;
21888
+ let n = curResAtoms.length;
21889
+ for (let j = 0; j < n; ++j) {
21890
+ let atom0 = curResAtoms[j];
21891
+ for (let k = j + 1; k < n; ++k) {
21892
+ let atom1 = curResAtoms[k];
21893
+ if (atom0.alt === atom1.alt && me.utilsCls.hasCovalentBond(atom0, atom1)) {
21894
+ //if (me.utilsCls.hasCovalentBond(atom0, atom1)) {
21895
+ atom0.bonds.push(atom1.serial);
21896
+ atom1.bonds.push(atom0.serial);
21897
+ }
21898
+ }
21899
+
21900
+ //f && f(atom0);
21901
+ if (prevCarbon && (prevCarbon.name === 'C' || prevCarbon.name === 'O3\'') && (atom0.name === 'N' || atom0.name === 'P') && me.utilsCls.hasCovalentBond(atom0, prevCarbon)) {
21902
+ atom0.bonds.push(prevCarbon.serial);
21903
+ prevCarbon.bonds.push(atom0.serial);
21904
+ }
21905
+ }
21906
+ }
21907
+
21840
21908
  adjustSeq(chainMissingResidueArray) { let ic = this.icn3d; ic.icn3dui;
21841
21909
  // adjust sequences
21842
21910
  for(let chainNum in ic.chainsSeq) {
@@ -22139,12 +22207,13 @@ class LoadAtomData {
22139
22207
  else { // mmdbid or mmcifid
22140
22208
  if(data.descr !== undefined) ic.molTitle += data.descr.name;
22141
22209
  if(type === 'mmdbid') {
22142
- let pdbidTmp = data.pdbId;
22210
+ let pdbidTmp = (isNaN(id)) ? id : data.pdbId;
22143
22211
  let chainHash = {};
22144
22212
 
22145
22213
  if(ic.alignmolid2color === undefined) ic.alignmolid2color = [];
22146
22214
 
22147
22215
  let molidCnt = 1;
22216
+
22148
22217
  for(let molid in data.moleculeInfor) {
22149
22218
  if(Object.keys(data.moleculeInfor[molid]).length === 0) continue;
22150
22219
 
@@ -22159,11 +22228,10 @@ class LoadAtomData {
22159
22228
  chainHash[chain] = 1;
22160
22229
  }
22161
22230
 
22162
- if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') {
22163
- //chainid += me.htmlCls.postfix;
22164
- chainid = pdbidTmp + me.htmlCls.postfix + '_' + chain;
22165
- }
22231
+ if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') ;
22166
22232
 
22233
+ //if(chainidInput && chainidInput.substr(chainidInput.indexOf('_') + 1) == chain) chainid = chainidInput;
22234
+
22167
22235
  let kind = data.moleculeInfor[molid].kind;
22168
22236
  let color = data.moleculeInfor[molid].color;
22169
22237
  let sid = data.moleculeInfor[molid].sid;
@@ -22229,7 +22297,7 @@ class LoadAtomData {
22229
22297
  let bSetResi = false;
22230
22298
 
22231
22299
  //if(mmdbId !== prevmmdbId) resiArray = [];
22232
- if(atm.chain === undefined &&(type === 'mmdbid' || type === 'align')) {
22300
+ if(atm.chain === undefined && (type === 'mmdbid' || type === 'align')) {
22233
22301
  if(type === 'mmdbid') {
22234
22302
  molid = atm.ids.m;
22235
22303
 
@@ -22295,14 +22363,13 @@ class LoadAtomData {
22295
22363
  atm.structure = mmdbId;
22296
22364
 
22297
22365
  if(type === 'mmdbid' &&((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t))
22298
- && alignType === 'query') {
22299
- atm.structure += me.htmlCls.postfix;
22300
- }
22366
+ && alignType === 'query') ;
22301
22367
  }
22302
22368
 
22303
22369
  structureNum = atm.structure;
22304
22370
 
22305
22371
  chainNum = structureNum + '_' + atm.chain;
22372
+
22306
22373
  //if(ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t && alignType === 'query') chainNum += me.htmlCls.postfix;
22307
22374
 
22308
22375
  //var resiCorrection = 0;
@@ -22605,10 +22672,7 @@ class LoadAtomData {
22605
22672
  let seqArray = data.sequences[chain];
22606
22673
  let chainid = id + '_' + chain;
22607
22674
 
22608
- if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') {
22609
- //chainid += me.htmlCls.postfix;
22610
- chainid = id + me.htmlCls.postfix + '_' + chain;
22611
- }
22675
+ if(((ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t)) && alignType === 'query') ;
22612
22676
 
22613
22677
  ic.ParserUtilsCls.getMissingResidues(seqArray, type, chainid); // assign ic.chainsSeq
22614
22678
  }
@@ -22663,6 +22727,7 @@ class LoadAtomData {
22663
22727
 
22664
22728
  ic.loadPDBCls.setSsbond();
22665
22729
  }
22730
+
22666
22731
  if(type === 'mmdbid' && Object.keys(ic.structures).length == 1) {
22667
22732
  let disulfideArray = data.disulfides;
22668
22733
 
@@ -24759,7 +24824,7 @@ class OpmParser {
24759
24824
  let thisClass = this;
24760
24825
  let url, dataType;
24761
24826
 
24762
- if(!pdbid) pdbid = 'STRU';
24827
+ if(!pdbid) pdbid = 'stru';
24763
24828
 
24764
24829
  url = "https://www.ncbi.nlm.nih.gov/Structure/mmdb/mmdb_strview.cgi?v=2&program=icn3d&opm&uid=" + pdbid.toLowerCase();
24765
24830
  dataType = "jsonp";
@@ -24957,6 +25022,7 @@ class MmdbParser {
24957
25022
  this.parseMmdbDataPart1(data, type);
24958
25023
 
24959
25024
  let id =(data.pdbId !== undefined) ? data.pdbId : data.mmdbId;
25025
+ if(chainid) id = chainid.substr(0, chainid.indexOf('_'));
24960
25026
 
24961
25027
  let hAtoms = ic.loadAtomDataCls.loadAtomDataIn(data, id, 'mmdbid', undefined, type, chainid, chainIndex, bLastQuery, bNoTransformNoSeqalign);
24962
25028
 
@@ -25380,12 +25446,19 @@ class RealignParser {
25380
25446
 
25381
25447
  ic.opts['color'] = 'grey';
25382
25448
  ic.setColorCls.setColorByOptions(ic.opts, ic.dAtoms);
25383
-
25384
- for(let index = 0, indexl = chainidArray.length - 1; index < indexl; ++index) {
25385
- let chainpair = chainidArray[0] + ',' + chainidArray[index + 1];
25449
+
25450
+ for(let index = 0, indexl = chainidArray.length - 1; index < indexl; ++index) {
25386
25451
  let fromStruct = chainidArray[index + 1].substr(0, chainidArray[index + 1].indexOf('_')); //.toUpperCase();
25387
25452
 
25388
- if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
25453
+ //if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
25454
+
25455
+ let chainTo = toStruct + chainidArray[0].substr(chainidArray[0].indexOf('_'));
25456
+ let chainFrom = fromStruct + chainidArray[index + 1].substr(chainidArray[index + 1].indexOf('_'));
25457
+
25458
+ chainidArray[0] = chainTo;
25459
+ chainidArray[index + 1] = chainFrom;
25460
+
25461
+ let chainpair = chainTo + ',' + chainFrom;
25389
25462
 
25390
25463
  if(!struct2SeqHash[chainpair]) continue;
25391
25464
 
@@ -25406,11 +25479,9 @@ class RealignParser {
25406
25479
  ic.realignResid[fromStruct].push({'resid':residArray2[i], 'resn':seq2[i]});
25407
25480
  }
25408
25481
 
25409
- let chainTo = chainidArray[0];
25410
- let chainFrom = chainidArray[index + 1];
25411
-
25412
25482
  let bChainAlign = true;
25413
25483
  // set ic.qt_start_end in alignCoords()
25484
+
25414
25485
  let hAtomsTmp = ic.ParserUtilsCls.alignCoords(coord2, coord1, fromStruct, undefined, chainTo, chainFrom, index + 1, bChainAlign);
25415
25486
  hAtoms = me.hashUtilsCls.unionHash(hAtoms, hAtomsTmp);
25416
25487
  }
@@ -25446,7 +25517,13 @@ class RealignParser {
25446
25517
  let fromStruct = chainidArray[index + 1].substr(0, chainidArray[index + 1].indexOf('_')); //.toUpperCase();
25447
25518
  if(!bRealign) fromStruct = fromStruct.toUpperCase();
25448
25519
 
25449
- if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
25520
+ //if(toStruct == fromStruct) fromStruct += me.htmlCls.postfix;
25521
+
25522
+ let chainTo = toStruct + chainidArray[0].substr(chainidArray[0].indexOf('_'));
25523
+ let chainFrom = fromStruct + chainidArray[index + 1].substr(chainidArray[index + 1].indexOf('_'));
25524
+
25525
+ chainidArray[0] = chainTo;
25526
+ chainidArray[index + 1] = chainFrom;
25450
25527
 
25451
25528
  let seq1 = struct2SeqHash[toStruct];
25452
25529
  let seq2 = struct2SeqHash[fromStruct];
@@ -25504,8 +25581,8 @@ class RealignParser {
25504
25581
  }
25505
25582
  }
25506
25583
 
25507
- let chainTo = chainidArray[0];
25508
- let chainFrom = chainidArray[index + 1];
25584
+ //let chainTo = chainidArray[0];
25585
+ //let chainFrom = chainidArray[index + 1];
25509
25586
 
25510
25587
  let bChainAlign = true;
25511
25588
  let hAtomsTmp = ic.ParserUtilsCls.alignCoords(coordsFrom, coordsTo, fromStruct, undefined, chainTo, chainFrom, index + 1, bChainAlign);
@@ -25539,7 +25616,7 @@ class RealignParser {
25539
25616
  // align seq
25540
25617
  //ic.hAtoms = ic.chainalignParserCls.setMsa(chainidArray, bRealign);
25541
25618
  ic.hAtoms = ic.chainalignParserCls.setMsa(chainidArray);
25542
-
25619
+
25543
25620
  ic.transformCls.zoominSelection();
25544
25621
 
25545
25622
 
@@ -25549,12 +25626,13 @@ class RealignParser {
25549
25626
  ic.opts['color'] = 'identity';
25550
25627
  //ic.setColorCls.setColorByOptions(ic.opts, ic.atoms);
25551
25628
  ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
25552
-
25629
+
25553
25630
  let name = 'protein_aligned';
25554
25631
  ic.selectionCls.saveSelection(name, name);
25555
-
25632
+
25556
25633
  ic.drawCls.draw();
25557
25634
  ic.hlUpdateCls.updateHlAll();
25635
+
25558
25636
  if(ic.deferredRealign !== undefined) ic.deferredRealign.resolve();
25559
25637
  }
25560
25638
  else {
@@ -25691,9 +25769,6 @@ class RealignParser {
25691
25769
  if(i == 0) {
25692
25770
  mmdbid_t = mmdbid;
25693
25771
  }
25694
- else if(mmdbid_t == mmdbid) {
25695
- mmdbid += me.htmlCls.postfix;
25696
- }
25697
25772
 
25698
25773
  let chainid = mmdbid + chainidArray[i].substr(pos);
25699
25774
  if(i == 0) chainid_t = chainid;
@@ -25701,7 +25776,6 @@ class RealignParser {
25701
25776
  if(!ic.chainsSeq[chainid]) {
25702
25777
  //alert("Please select one chain per structure and try it again...");
25703
25778
  //return;
25704
-
25705
25779
  continue;
25706
25780
  }
25707
25781
 
@@ -25739,8 +25813,8 @@ class RealignParser {
25739
25813
 
25740
25814
  // slave
25741
25815
  resiArray = predefinedResPair[1].split(",");
25742
- result = thisClass.getSeqCoorResid(resiArray, chainid, base);
25743
-
25816
+ result = thisClass.getSeqCoorResid(resiArray, chainid, base);
25817
+
25744
25818
  if(!struct2SeqHash[chainidpair][mmdbid]) struct2SeqHash[chainidpair][mmdbid] = '';
25745
25819
  if(!struct2CoorHash[chainidpair][mmdbid]) struct2CoorHash[chainidpair][mmdbid] = [];
25746
25820
  if(!struct2resid[chainidpair][mmdbid]) struct2resid[chainidpair][mmdbid] = [];
@@ -25905,8 +25979,11 @@ class RealignParser {
25905
25979
  let bFound = false;
25906
25980
  for(let serial in ic.residues[resid]) {
25907
25981
  let atom = ic.atoms[serial];
25908
- if((ic.proteins.hasOwnProperty(serial) && atom.name == "CA" && atom.elem == "C")
25909
- ||(ic.nucleotides.hasOwnProperty(serial) &&(atom.name == "O3'" || atom.name == "O3*") && atom.elem == "O") ) {
25982
+
25983
+ //if((ic.proteins.hasOwnProperty(serial) && atom.name == "CA" && atom.elem == "C")
25984
+ // ||(ic.nucleotides.hasOwnProperty(serial) &&(atom.name == "O3'" || atom.name == "O3*") && atom.elem == "O") ) {
25985
+ if((atom.name == "CA" && atom.elem == "C")
25986
+ ||((atom.name == "O3'" || atom.name == "O3*") && atom.elem == "O") ) {
25910
25987
  struct2CoorArray.push(atom.coord.clone());
25911
25988
  bFound = true;
25912
25989
  break;
@@ -25934,7 +26011,7 @@ class ChainalignParser {
25934
26011
  let mmdbid_t, mmdbid_q;
25935
26012
  mmdbid_t = chainidArray[0].substr(0, chainidArray[0].indexOf('_'));
25936
26013
  let bLastQuery = false;
25937
- if(mmdbid_t.length > 4) {
26014
+ if(mmdbid_t.length > 5) {
25938
26015
  let bAppend = false, bNoDssp = true;
25939
26016
  hAtoms = ic.pdbParserCls.loadPdbData(data1, mmdbid_t, false, bAppend, 'target', bLastQuery, bNoDssp);
25940
26017
  }
@@ -25947,7 +26024,10 @@ class ChainalignParser {
25947
26024
  if(i == data2Array.length - 1) bLastQuery = true;
25948
26025
  // each alignment has a chainIndex i
25949
26026
  mmdbid_q = chainidArray[i + 1].substr(0, chainidArray[i + 1].indexOf('_'));
25950
- if(mmdbid_q.length > 4) {
26027
+ //mmdbid_q = (mmdbid_q_tmp.length == 5) ? mmdbid_q_tmp.substr(0, 4) : mmdbid_q_tmp; // added postfixfor same PDB IDs
26028
+
26029
+ //if(mmdbid_q.length > 4) {
26030
+ if(mmdbid_q.length > 5) { // PDB ID plus postfix could be 5
25951
26031
  let bAppend = true, bNoDssp = true;
25952
26032
  hAtomsTmp = ic.pdbParserCls.loadPdbData(data2Array[i], mmdbid_q, false, bAppend, 'query', bLastQuery, bNoDssp);
25953
26033
  }
@@ -26385,10 +26465,21 @@ class ChainalignParser {
26385
26465
  let domainArray = (me.cfg.domainids) ? me.cfg.domainids.split(',') : [];
26386
26466
  if(domainArray.length < alignArray.length) domainArray = [];
26387
26467
 
26468
+ let struct2cnt = {};
26388
26469
  for(let i = 0, il = alignArray.length; i < il; ++i) {
26389
26470
  let chainid = alignArray[i];
26390
26471
  let pos = chainid.indexOf('_');
26391
- alignArray[i] = chainid.substr(0, pos).toUpperCase() + chainid.substr(pos);
26472
+ let struct = chainid.substr(0, pos).toUpperCase();
26473
+ if(!struct2cnt.hasOwnProperty(struct)) {
26474
+ struct2cnt[struct] = 1;
26475
+ }
26476
+ else {
26477
+ ++struct2cnt[struct];
26478
+ }
26479
+
26480
+ struct = (struct2cnt[struct] == 1) ? struct : struct + struct2cnt[struct];
26481
+
26482
+ alignArray[i] = struct + chainid.substr(pos);
26392
26483
  }
26393
26484
 
26394
26485
  ic.chainidArray = alignArray;
@@ -26401,7 +26492,7 @@ class ChainalignParser {
26401
26492
  let targetAjax;
26402
26493
 
26403
26494
  let url_t;
26404
- if(ic.mmdbid_t.length > 4) {
26495
+ if(ic.mmdbid_t.length > 5) {
26405
26496
  url_t = "https://alphafold.ebi.ac.uk/files/AF-" + ic.mmdbid_t + "-F1-model_v2.pdb";
26406
26497
 
26407
26498
  targetAjax = $.ajax({
@@ -26433,11 +26524,13 @@ class ChainalignParser {
26433
26524
  ic.pdbChainIndexHash = {};
26434
26525
  for(let index = 1, indexLen = alignArray.length; index < indexLen; ++index) {
26435
26526
  let pos2 = alignArray[index].indexOf('_');
26436
- ic.mmdbid_q = alignArray[index].substr(0, pos2).toUpperCase();
26527
+ let mmdbid_q_tmp = alignArray[index].substr(0, pos2).toUpperCase();
26528
+ ic.mmdbid_q = (mmdbid_q_tmp.length == 5) ? mmdbid_q_tmp.substr(0, 4) : mmdbid_q_tmp; // added postfix for same PDB IDs
26529
+
26437
26530
  ic.chain_q = alignArray[index].substr(pos2+1);
26438
26531
 
26439
26532
  let url_q, queryAjax;
26440
- if(ic.mmdbid_q.length > 4) {
26533
+ if(ic.mmdbid_q.length > 5) {
26441
26534
  url_q = "https://alphafold.ebi.ac.uk/files/AF-" + ic.mmdbid_q + "-F1-model_v2.pdb";
26442
26535
 
26443
26536
  queryAjax = $.ajax({
@@ -26462,7 +26555,9 @@ class ChainalignParser {
26462
26555
 
26463
26556
  for(let index = 1, indexLen = alignArray.length; index < indexLen; ++index) {
26464
26557
  let pos2 = alignArray[index].indexOf('_');
26465
- ic.mmdbid_q = alignArray[index].substr(0, pos2).toUpperCase();
26558
+ let mmdbid_q_tmp = alignArray[index].substr(0, pos2).toUpperCase();
26559
+ ic.mmdbid_q = (mmdbid_q_tmp.length == 5) ? mmdbid_q_tmp.substr(0, 4) : mmdbid_q_tmp; // added postfix for same PDB IDs
26560
+
26466
26561
  ic.chain_q = alignArray[index].substr(pos2+1);
26467
26562
 
26468
26563
  if(!me.cfg.resnum && !me.cfg.resdef) {
@@ -26487,7 +26582,7 @@ class ChainalignParser {
26487
26582
 
26488
26583
  ajaxArray.push(alignAjax);
26489
26584
 
26490
- ic.pdbChainIndexHash[index] = ic.mmdbid_q + "_" + ic.chain_q + "_" + ic.mmdbid_t + "_" + ic.chain_t;
26585
+ ic.pdbChainIndexHash[index] = mmdbid_q_tmp + "_" + ic.chain_q + "_" + ic.mmdbid_t + "_" + ic.chain_t;
26491
26586
  }
26492
26587
  else {
26493
26588
  // get the dynamic alignment after loading the structures
@@ -26517,7 +26612,7 @@ class ChainalignParser {
26517
26612
  // index = 0: the mmdb data of target
26518
26613
  let targetData = dataArray[0][0];
26519
26614
  let header = 'HEADER ' + mmdbid_t + '\n';
26520
- if(mmdbid_t.length > 4) targetData = header + targetData;
26615
+ if(mmdbid_t.length > 5) targetData = header + targetData;
26521
26616
 
26522
26617
  ic.t_trans_add = [];
26523
26618
  ic.q_trans_sub = [];
@@ -26536,7 +26631,7 @@ class ChainalignParser {
26536
26631
  let mmdbid_q = chainidArray[index].substr(0, pos).toUpperCase();
26537
26632
 
26538
26633
  let header = 'HEADER ' + mmdbid_q + '\n';
26539
- if(mmdbid_q.length > 4) queryData = header + queryData;
26634
+ if(mmdbid_q.length > 5) queryData = header + queryData;
26540
26635
 
26541
26636
  if(queryData !== undefined && JSON.stringify(queryData).indexOf('Oops there was a problem') === -1
26542
26637
  ) {
@@ -26711,9 +26806,9 @@ class ChainalignParser {
26711
26806
  }
26712
26807
 
26713
26808
  downloadMmdbAf(idlist, bQuery) { let ic = this.icn3d, me = ic.icn3dui;
26714
- let thisClass = this;
26809
+ let thisClass = this;
26715
26810
 
26716
- ic.deferredMmdbaf = $.Deferred(function() {
26811
+ ic.deferredMmdbaf = $.Deferred(function() {
26717
26812
  ic.structArray = idlist.split(',');
26718
26813
 
26719
26814
  let ajaxArray = [];
@@ -26722,7 +26817,7 @@ class ChainalignParser {
26722
26817
  let url_t, targetAjax;
26723
26818
  let structure = ic.structArray[i];
26724
26819
 
26725
- if(isNaN(structure) && structure.length > 4) {
26820
+ if(isNaN(structure) && structure.length > 5) {
26726
26821
  url_t = "https://alphafold.ebi.ac.uk/files/AF-" + ic.structArray[i] + "-F1-model_v2.pdb";
26727
26822
 
26728
26823
  targetAjax = $.ajax({
@@ -26771,7 +26866,7 @@ class ChainalignParser {
26771
26866
  for(let index = 0, indexl = structArray.length; index < indexl; ++index) {
26772
26867
  let queryData = dataArray[index][0];
26773
26868
  let header = 'HEADER ' + structArray[index] + '\n';
26774
- if(structArray[index].length > 4) queryData = header + queryData;
26869
+ if(structArray[index].length > 5) queryData = header + queryData;
26775
26870
 
26776
26871
  if(queryData !== undefined && JSON.stringify(queryData).indexOf('Oops there was a problem') === -1
26777
26872
  ) {
@@ -26801,7 +26896,8 @@ class ChainalignParser {
26801
26896
  bAppend = true;
26802
26897
  }
26803
26898
 
26804
- if(structArray[i].length > 4) {
26899
+ //if(structArray[i].length > 4) {
26900
+ if(structArray[i].length > 5) { // PDB ID plus postfix could be 5
26805
26901
  let bNoDssp = true;
26806
26902
  hAtomsTmp = ic.pdbParserCls.loadPdbData(queryDataArray[i], structArray[i], false, bAppend, targetOrQuery, bLastQuery, bNoDssp);
26807
26903
  }
@@ -28772,7 +28868,7 @@ class LoadScript {
28772
28868
  me.cfg.mmcifid = id;
28773
28869
  ic.mmcifParserCls.downloadMmcif(id);
28774
28870
  }
28775
- else if(command.indexOf('load mmdb') !== -1 || command.indexOf('load mmdb1') !== -1) {
28871
+ else if(command.indexOf('load mmdb ') !== -1 || command.indexOf('load mmdb1 ') !== -1) {
28776
28872
  me.cfg.mmdbid = id;
28777
28873
  me.cfg.bu = 1;
28778
28874
 
@@ -28788,13 +28884,13 @@ class LoadScript {
28788
28884
  me.cfg.mmdbafid = id;
28789
28885
  me.cfg.bu = 1;
28790
28886
 
28791
- ic.mmdbParserCls.downloadMmdbAf(id);
28887
+ ic.chainalignParserCls.downloadMmdbAf(id);
28792
28888
  }
28793
28889
  else if(command.indexOf('load mmdbaf0') !== -1) {
28794
28890
  me.cfg.mmdbafid = id;
28795
28891
  me.cfg.bu = 0;
28796
28892
 
28797
- ic.mmdbParserCls.downloadMmdbAf(id);
28893
+ ic.chainalignParserCls.downloadMmdbAf(id);
28798
28894
  }
28799
28895
  else if(command.indexOf('load gi') !== -1) {
28800
28896
  me.cfg.gi = id;
@@ -31439,7 +31535,7 @@ class AddTrack {
31439
31535
 
31440
31536
  //this.showNewTrack(chainid, title, text);
31441
31537
  //me.htmlCls.clickMenuCls.setLogCmd("add track | chainid " + chainid + " | title " + title + " | text " + this.simplifyText(text), true);
31442
- let result = this.getFullText(text);
31538
+ let result = thisClass.getFullText(text);
31443
31539
 
31444
31540
  thisClass.showNewTrack(chainid, title, result.text, undefined, undefined, 'custom', undefined, undefined, result.fromArray, result.toArray);
31445
31541
 
@@ -32529,7 +32625,19 @@ class AnnoCddSite {
32529
32625
  }
32530
32626
  }
32531
32627
  }
32532
- 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>';
32628
+
32629
+ let bCoordinates = false;
32630
+ for(let i = 0, il = adjustedResPosArray.length; i < il; ++i) {
32631
+ let resid = chnid + "_" + adjustedResPosArray[i];
32632
+ if(ic.residues.hasOwnProperty(resid)) {
32633
+ bCoordinates = true;
32634
+ break;
32635
+ }
32636
+ }
32637
+
32638
+ let linkStr = (bCoordinates) ? 'icn3d-link icn3d-blue' : '';
32639
+
32640
+ 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>';
32533
32641
  let htmlTmp3 = '<span class="icn3d-residueNum" title="residue count">' + resCnt.toString() + ' Res</span>';
32534
32642
  let htmlTmp = '<span class="icn3d-seqLine">';
32535
32643
  html3 += htmlTmp2 + htmlTmp3 + '<br>';
@@ -32685,7 +32793,25 @@ class AnnoCddSite {
32685
32793
  if(bDomain) pssmid2fromArray[pssmid] = fromArray;
32686
32794
  if(bDomain) pssmid2toArray[pssmid] = toArray;
32687
32795
 
32688
- 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>';
32796
+ let bCoordinates = false;
32797
+ for(let i = 0, il = fromArray.length; i < il; ++i) {
32798
+ let from = fromArray[i], to = toArray[i];
32799
+ for(let j = from; j <= to; ++j) {
32800
+ let resid = chnid + "_" + j;
32801
+ if(ic.residues.hasOwnProperty(resid)) {
32802
+ bCoordinates = true;
32803
+ break;
32804
+ }
32805
+ }
32806
+
32807
+ if(bCoordinates) {
32808
+ break;
32809
+ }
32810
+ }
32811
+
32812
+ let linkStr = (bCoordinates) ? 'icn3d-link icn3d-blue' : '';
32813
+
32814
+ 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>';
32689
32815
  let htmlTmp3 = '<span class="icn3d-residueNum" title="residue count">' + resCnt.toString() + ' Res</span>';
32690
32816
  html3 += htmlTmp2 + htmlTmp3 + '<br>';
32691
32817
  let htmlTmp = '<span class="icn3d-seqLine">';
@@ -32693,7 +32819,7 @@ class AnnoCddSite {
32693
32819
  if(bDomain) {
32694
32820
  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>';
32695
32821
  }
32696
- 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>';
32822
+ 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>';
32697
32823
  html2 += htmlTmp3 + htmlTmp;
32698
32824
  let pre = type + index.toString();
32699
32825
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
@@ -32719,7 +32845,7 @@ class AnnoCddSite {
32719
32845
  for(let i = 0, il = fromArray.length; i < il; ++i) {
32720
32846
  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);
32721
32847
  html2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
32722
- 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>';
32848
+ 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>';
32723
32849
  }
32724
32850
  }
32725
32851
  else { // with potential gaps
@@ -32738,7 +32864,7 @@ class AnnoCddSite {
32738
32864
  html2 += ic.showSeqCls.insertGapOverview(chnid, fromArray2[i]);
32739
32865
  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));
32740
32866
  html2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
32741
- 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>';
32867
+ 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>';
32742
32868
  }
32743
32869
  }
32744
32870
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
@@ -35801,14 +35927,14 @@ class SetSeqAlign {
35801
35927
  let residueid2 = chainid2 + '_' + resi;
35802
35928
  let ss1 = ic.secondaries[residueid1];
35803
35929
  let ss2 = ic.secondaries[residueid2];
35804
- if(ss2 !== undefined) {
35930
+ if(ss2) {
35805
35931
  ic.alnChainsAnno[chainid1][0].push(ss2);
35806
35932
  }
35807
35933
  else {
35808
35934
  ic.alnChainsAnno[chainid1][0].push('-');
35809
35935
  }
35810
35936
 
35811
- if(ss1 !== undefined) {
35937
+ if(ss1) {
35812
35938
  ic.alnChainsAnno[chainid1][1].push(ss1);
35813
35939
  }
35814
35940
  else {
@@ -35825,7 +35951,7 @@ class SetSeqAlign {
35825
35951
  ic.alnChainsAnno[chainid1][3].push(numberStr); // symbol: 10, 20, etc, empty for rest
35826
35952
 
35827
35953
  ++alignIndex;
35828
- } // end for(let j
35954
+ } // end for(let j
35829
35955
  } // end for(let i
35830
35956
 
35831
35957
  seqalign = {};
@@ -35858,11 +35984,6 @@ class SetSeqAlign {
35858
35984
  let chainLen = ic.chainsSeq[mmdbid2 + '_' + chain2].length;
35859
35985
  ic.qt_start_end[chainIndex] = {"q_start":1, "q_end": chainLen, "t_start":1, "t_end": chainLen};
35860
35986
  }
35861
-
35862
- if(mmdbid2 !== undefined && mmdbid2 === mmdbid1) {
35863
- //chainid1 += me.htmlCls.postfix;
35864
- chainid2 = mmdbid2 + me.htmlCls.postfix + "_" + chain2;
35865
- }
35866
35987
  }
35867
35988
  else {
35868
35989
  //var chainidArray = me.cfg.chainalign.split(',');
@@ -35883,10 +36004,7 @@ class SetSeqAlign {
35883
36004
  chainid1 = mmdbid1 + "_" + chain1;
35884
36005
  chainid2 = mmdbid2 + "_" + chain2;
35885
36006
 
35886
- if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) {
35887
- //chainid1 += me.htmlCls.postfix;
35888
- chainid2 = mmdbid2 + me.htmlCls.postfix + "_" + chain2;
35889
- }
36007
+ if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) ;
35890
36008
  }
35891
36009
 
35892
36010
  ic.conservedName1 = chainid1 + '_cons';
@@ -36124,6 +36242,7 @@ class SetSeqAlign {
36124
36242
  let resi2range_t = {}; // aaccumulative aligned residues in the template chain
36125
36243
  // start and end of MSA
36126
36244
  let start_t = 9999, end_t = -1;
36245
+
36127
36246
  for(let index = 1, indexl = chainidArray.length; index < indexl; ++index) {
36128
36247
  let chainIndex = index - 1;
36129
36248
  for(let i = 0, il = ic.qt_start_end[chainIndex].length; i < il; ++i) {
@@ -36225,7 +36344,7 @@ class SetSeqAlign {
36225
36344
 
36226
36345
  hAtoms = me.hashUtilsCls.unionHash(hAtoms, hAtomsTmp);
36227
36346
  }
36228
-
36347
+
36229
36348
  // 3. assign the varaible ic.alnChainsAnno
36230
36349
  for(let i = 0; i < 3 + 2*n; ++i) {
36231
36350
  if(ic.alnChainsAnno[chainid1][i] === undefined ) ic.alnChainsAnno[chainid1][i] = [];
@@ -36302,7 +36421,7 @@ class SetSeqAlign {
36302
36421
  resi = resiPos;
36303
36422
  }
36304
36423
  else {
36305
- if(ic.chainsSeq[chainid][resiPos] === undefined) {
36424
+ if(!ic.chainsSeq[chainid] || !ic.chainsSeq[chainid][resiPos]) {
36306
36425
  resi = '';
36307
36426
  }
36308
36427
  else {
@@ -36327,7 +36446,7 @@ class SetSeqAlign {
36327
36446
  }
36328
36447
  }
36329
36448
  else {
36330
- if(ic.chainsSeq[chainid][resiPos] === undefined) {
36449
+ if(!ic.chainsSeq[chainid] || !ic.chainsSeq[chainid][resiPos]) {
36331
36450
  resn = '';
36332
36451
  }
36333
36452
  else {
@@ -36343,18 +36462,21 @@ class SetSeqAlign {
36343
36462
  let nGap = 0;
36344
36463
 
36345
36464
  let pos_t; // position to add gap
36346
- for(let j = 0, jl = ic.alnChainsSeq[chainid1].length; j < jl; ++j) {
36347
- //add gap before the mapping region
36348
- if(parseInt(ic.alnChainsSeq[chainid1][j].resi) == resi_t) {
36349
- pos_t = j;
36350
- break;
36351
- }
36352
36465
 
36353
- if(ic.alnChainsSeq[chainid1][j].resn == '-') {
36354
- ++nGap;
36355
- }
36356
- else {
36357
- nGap = 0;
36466
+ if(ic.alnChainsSeq[chainid1]) {
36467
+ for(let j = 0, jl = ic.alnChainsSeq[chainid1].length; j < jl; ++j) {
36468
+ //add gap before the mapping region
36469
+ if(parseInt(ic.alnChainsSeq[chainid1][j].resi) == resi_t) {
36470
+ pos_t = j;
36471
+ break;
36472
+ }
36473
+
36474
+ if(ic.alnChainsSeq[chainid1][j].resn == '-') {
36475
+ ++nGap;
36476
+ }
36477
+ else {
36478
+ nGap = 0;
36479
+ }
36358
36480
  }
36359
36481
  }
36360
36482
 
@@ -36363,13 +36485,14 @@ class SetSeqAlign {
36363
36485
 
36364
36486
  addGapAllAlnChains(chainidArray, alignedChainIndice, chainid1, resi_t, len) { let ic = this.icn3d; ic.icn3dui;
36365
36487
  let result = this.getResiPosInTemplate(chainid1, resi_t);
36366
- let nGap = result.ngap, pos_t = result.pos;
36488
+ result.ngap; let pos_t = result.pos;
36367
36489
 
36368
36490
  // add gaps for all previously aligned sequences, not the current sequence, which is the last one
36369
36491
  for(let j = 0, jl = alignedChainIndice.length - 1; j < jl; ++j) {
36370
36492
  let chainidTmp = chainidArray[alignedChainIndice[j]];
36371
36493
  let gapResObject = this.getResObject(chainidTmp, true);
36372
- for(let k = 0, kl = len - nGap; k < kl; ++k) {
36494
+ //for(let k = 0, kl = len - nGap; k < kl; ++k) {
36495
+ for(let k = 0, kl = len; k < kl; ++k) {
36373
36496
  ic.alnChainsSeq[chainidTmp].splice(pos_t, 0, gapResObject);
36374
36497
  }
36375
36498
  }
@@ -36413,8 +36536,8 @@ class SetSeqAlign {
36413
36536
  pos2 = chainid.indexOf('_');
36414
36537
 
36415
36538
  //mmdbid1 = ic.mmdbid_t;
36416
- mmdbid1 = chainidArray[0].substr(0, pos1).toUpperCase();
36417
- mmdbid2 = chainid.substr(0, pos2).toUpperCase();
36539
+ mmdbid1 = chainidArray[0].substr(0, pos1); //.toUpperCase();
36540
+ mmdbid2 = chainid.substr(0, pos2); //.toUpperCase();
36418
36541
 
36419
36542
  chain1 = chainidArray[0].substr(pos1 + 1);
36420
36543
  chain2 = chainid.substr(pos2 + 1);
@@ -36427,9 +36550,7 @@ class SetSeqAlign {
36427
36550
  chainid1 = mmdbid1 + "_" + chain1;
36428
36551
  chainid2 = mmdbid2 + "_" + chain2;
36429
36552
 
36430
- if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) {
36431
- chainid2 = mmdbid2 + me.htmlCls.postfix + "_" + chain2;
36432
- }
36553
+ if(mmdbid2 !== undefined && mmdbid2 === ic.mmdbid_t) ;
36433
36554
 
36434
36555
  //ic.alnChainsSeq[chainid2] = [];
36435
36556
  ic.alnChains[chainid2] = {};
@@ -37267,7 +37388,7 @@ class ParserUtils {
37267
37388
 
37268
37389
  //var dxymax = npoint / 2.0 * step;
37269
37390
 
37270
- pdbid =(pdbid) ? pdbid.toUpperCase() : 'STRU';
37391
+ pdbid =(pdbid) ? pdbid.toUpperCase() : 'stru';
37271
37392
 
37272
37393
  ic.structures[pdbid].push(pdbid + '_MEM');
37273
37394
  ic.chains[pdbid + '_MEM'] = {};
@@ -37418,7 +37539,7 @@ class ParserUtils {
37418
37539
  $("#" + id + "_shrink").show();
37419
37540
 
37420
37541
  if(me.cfg.align !== undefined) {
37421
- let bShowHighlight = false;
37542
+ let bShowHighlight = false;
37422
37543
  let seqObj = me.htmlCls.alignSeqCls.getAlignSequencesAnnotations(Object.keys(ic.alnChains), undefined, undefined, bShowHighlight);
37423
37544
  $("#" + ic.pre + "dl_sequence2").html(seqObj.sequencesHtml);
37424
37545
  $("#" + ic.pre + "dl_sequence2").width(me.htmlCls.RESIDUE_WIDTH * seqObj.maxSeqCnt + 200);
@@ -37811,7 +37932,7 @@ class MmcifParser {
37811
37932
  //loadAtomDataIn. The deferred parameter was resolved after the parsing so that other javascript code can be executed.
37812
37933
  loadMmcifData(data, mmcifid) { let ic = this.icn3d; ic.icn3dui;
37813
37934
  if(!mmcifid) mmcifid = data.mmcif;
37814
- if(!mmcifid) mmcifid = 'STRU';
37935
+ if(!mmcifid) mmcifid = 'stru';
37815
37936
 
37816
37937
  if(data.atoms !== undefined) {
37817
37938
  ic.init();
@@ -38189,7 +38310,7 @@ class HlSeq {
38189
38310
  if(ic.bNCBI && ($(that).attr('domain') !== undefined || $(that).attr('feat') !== undefined || $(that).attr('3ddomain') !== undefined) ) {
38190
38311
  let residNCBI = chainid + '_' + (j+1).toString();
38191
38312
  // AlphaFold domains calculated on-the-fly have no conversion
38192
- if(structure.length > 4) {
38313
+ if(structure.length > 5) {
38193
38314
  residueid = residNCBI;
38194
38315
  }
38195
38316
  else if(ic.residNCBI2resid[residNCBI]) {
@@ -38236,7 +38357,7 @@ class HlSeq {
38236
38357
  if(ic.bNCBI) {
38237
38358
  let residNCBI = chainid + '_' +(parseInt(posArray[i])+1).toString();
38238
38359
  // AlphaFold domains calculated on-the-fly have no conversion
38239
- if(structure.length > 4) {
38360
+ if(structure.length > 5) {
38240
38361
  residueid = residNCBI;
38241
38362
  }
38242
38363
  else if(ic.residNCBI2resid[residNCBI]) {
@@ -40831,10 +40952,7 @@ class Diagram2d {
40831
40952
 
40832
40953
  let chainNameFinal =(chainNameHash[chainName] === 1) ? chainName : chainName + chainNameHash[chainName].toString();
40833
40954
  let chainid = mmdbid + '_' + chainNameFinal;
40834
- if(ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t && structureIndex === 0) {
40835
- //chainid += me.htmlCls.postfix;
40836
- chainid = mmdbid + me.htmlCls.postfix + '_' + chainNameFinal;
40837
- }
40955
+ if(ic.mmdbid_q !== undefined && ic.mmdbid_q === ic.mmdbid_t && structureIndex === 0) ;
40838
40956
 
40839
40957
  molid2chain[molid] = chainid;
40840
40958
  molid2color[molid] = color;
@@ -44949,6 +45067,8 @@ class ApplySsbonds {
44949
45067
  }
44950
45068
  }
44951
45069
 
45070
+ //https://github.com/mrdoob/three.js/blob/master/examples/webxr_vr_cubes.html
45071
+
44952
45072
  class VRButton {
44953
45073
  constructor(icn3d) {
44954
45074
  this.icn3d = icn3d;
@@ -44984,10 +45104,12 @@ class VRButton {
44984
45104
  }
44985
45105
 
44986
45106
  function onSessionEnded( /*event*/ ) {
44987
- // the display is weird somehow
44988
45107
  // reset orientation after VR
44989
45108
  ic.transformCls.resetOrientation();
44990
- //ic.transformCls.zoominSelection();
45109
+
45110
+ ic.bVr = false;
45111
+ //ic.mdl.scale.copy(new THREE.Vector3( 1, 1, 1 ));
45112
+
44991
45113
  ic.drawCls.draw();
44992
45114
 
44993
45115
  currentSession.removeEventListener( 'end', onSessionEnded );
@@ -45025,8 +45147,10 @@ class VRButton {
45025
45147
  ic.bImpo = false;
45026
45148
  //ic.bInstanced = false;
45027
45149
 
45028
- const bVr = true;
45029
- ic.drawCls.draw(bVr);
45150
+ ic.bVr = true;
45151
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(0.2));
45152
+
45153
+ ic.drawCls.draw(ic.bVr);
45030
45154
 
45031
45155
  if ( currentSession === null ) {
45032
45156
 
@@ -45127,29 +45251,6 @@ class VRButton {
45127
45251
  return button;
45128
45252
 
45129
45253
  } else {
45130
- /*
45131
- const message = document.createElement( 'a' );
45132
-
45133
- if ( window.isSecureContext === false ) {
45134
-
45135
- message.href = document.location.href.replace( /^http:/, 'https:' );
45136
- message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message
45137
-
45138
- } else {
45139
-
45140
- message.href = 'https://immersiveweb.dev/';
45141
- message.innerHTML = 'WEBXR NOT AVAILABLE';
45142
-
45143
- }
45144
-
45145
- message.style.left = 'calc(50% - 90px)';
45146
- message.style.width = '180px';
45147
- message.style.textDecoration = 'none';
45148
-
45149
- stylizeElement( message );
45150
-
45151
- return message;
45152
- */
45153
45254
  const message = document.createElement( 'span' );
45154
45255
  return message;
45155
45256
  }
@@ -45176,6 +45277,8 @@ class VRButton {
45176
45277
 
45177
45278
  }
45178
45279
 
45280
+ //https://github.com/mrdoob/three.js/blob/master/examples/webxr_ar_cones.html
45281
+
45179
45282
  class ARButton {
45180
45283
  constructor(icn3d) {
45181
45284
  this.icn3d = icn3d;
@@ -45247,6 +45350,13 @@ class ARButton {
45247
45350
  }
45248
45351
 
45249
45352
  function onSessionEnded( /*event*/ ) {
45353
+ // reset orientation after VR
45354
+ ic.transformCls.resetOrientation();
45355
+
45356
+ ic.bAr = false;
45357
+ //ic.mdl.scale.copy(new THREE.Vector3( 1, 1, 1 ));
45358
+
45359
+ ic.drawCls.draw();
45250
45360
 
45251
45361
  currentSession.removeEventListener( 'end', onSessionEnded );
45252
45362
 
@@ -45280,13 +45390,16 @@ class ARButton {
45280
45390
  };
45281
45391
 
45282
45392
  button.onclick = function () {
45283
- // imposter didn't work well in VR
45284
- //ic.bImpo = false;
45393
+ // imposter didn't work well in AR
45394
+ ic.bImpo = false;
45285
45395
 
45286
- ic.opts['background'] = 'transparent';
45396
+ // important to keet the background transparent
45397
+ ic.opts['background'] = 'transparent';
45287
45398
 
45288
- const bVr = true;
45289
- ic.drawCls.draw(bVr);
45399
+ ic.bAr = true;
45400
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(0.2));
45401
+
45402
+ ic.drawCls.draw(ic.bAr);
45290
45403
 
45291
45404
  if ( currentSession === null ) {
45292
45405
 
@@ -50507,6 +50620,220 @@ class XRControllerModelFactory {
50507
50620
 
50508
50621
  }
50509
50622
 
50623
+ //import * as THREE from './three/three.module.js';
50624
+
50625
+ // copied from https://github.com/NikLever/Learn-WebXR/blob/master/libs/ControllerGestures.js
50626
+ // created by Nik Lever
50627
+
50628
+ class ControllerGestures extends THREE.EventDispatcher{
50629
+ constructor( renderer ){
50630
+ super();
50631
+
50632
+ if (renderer === undefined){
50633
+ console.error('ControllerGestures must be passed a renderer');
50634
+ return;
50635
+ }
50636
+
50637
+ const clock = new THREE.Clock();
50638
+
50639
+ this.controller1 = renderer.xr.getController(0);
50640
+ this.controller1.userData.gestures = { index: 0 };
50641
+ this.controller1.userData.selectPressed = false;
50642
+ this.controller1.addEventListener( 'selectstart', onSelectStart );
50643
+ this.controller1.addEventListener( 'selectend', onSelectEnd );
50644
+
50645
+ this.controller2 = renderer.xr.getController(1);
50646
+ this.controller2.userData.gestures = { index: 1 };
50647
+ this.controller2.userData.selectPressed = false;
50648
+ this.controller2.addEventListener( 'selectstart', onSelectStart );
50649
+ this.controller2.addEventListener( 'selectend', onSelectEnd );
50650
+
50651
+ this.doubleClickLimit = 0.2;
50652
+ this.pressMinimum = 0.4;
50653
+ this.right = new THREE.Vector3(1,0,0);
50654
+ this.up = new THREE.Vector3(0,1,0);
50655
+
50656
+ this.type = 'unknown';
50657
+ this.touchCount = 0;
50658
+
50659
+ this.clock = clock;
50660
+
50661
+ const self = this;
50662
+
50663
+ function onSelectStart( ){
50664
+ const data = this.userData.gestures;
50665
+
50666
+ data.startPosition = undefined;
50667
+ data.startTime = clock.getElapsedTime();
50668
+
50669
+ if ( self.type.indexOf('tap') == -1) data.taps = 0;
50670
+
50671
+ self.type = 'unknown';
50672
+ this.userData.selectPressed = true;
50673
+
50674
+ self.touchCount++;
50675
+
50676
+ console.log( `onSelectStart touchCount: ${ self.touchCount }` );
50677
+ }
50678
+
50679
+ function onSelectEnd( ){
50680
+ const data = this.userData.gestures;
50681
+
50682
+ data.endTime = clock.getElapsedTime();
50683
+ const startToEnd = data.endTime - data.startTime;
50684
+
50685
+ //console.log(`ControllerGestures.onSelectEnd: startToEnd:${startToEnd.toFixed(2)} taps:${data.taps}`);
50686
+
50687
+ if (self.type === 'swipe'){
50688
+ const direction = ( self.controller1.position.y < data.startPosition.y) ? "DOWN" : "UP";
50689
+ self.dispatchEvent( { type:'swipe', direction } );
50690
+ self.type = 'unknown';
50691
+ }else if (self.type !== "pinch" && self.type !== "rotate" && self.type !== 'pan'){
50692
+ if ( startToEnd < self.doubleClickLimit ){
50693
+ self.type = "tap";
50694
+ data.taps++;
50695
+ }else if ( startToEnd > self.pressMinimum ){
50696
+ self.dispatchEvent( { type: 'press', position: self.controller1.position, matrixWorld: self.controller1.matrixWorld } );
50697
+ self.type = 'unknown';
50698
+ }
50699
+ }else {
50700
+ self.type = 'unknown';
50701
+ }
50702
+
50703
+ this.userData.selectPressed = false;
50704
+ data.startPosition = undefined;
50705
+
50706
+ self.touchCount--;
50707
+ }
50708
+ }
50709
+
50710
+ get multiTouch(){
50711
+ let result;
50712
+ if ( this.controller1 === undefined || this.controller2 === undefined ){
50713
+ result = false;
50714
+ }else {
50715
+ result = this.controller1.userData.selectPressed && this.controller2.userData.selectPressed;
50716
+ }
50717
+ const self = this;
50718
+ console.log( `ControllerGestures multiTouch: ${result} touchCount:${self.touchCount}`);
50719
+ return result;
50720
+ }
50721
+
50722
+ get touch(){
50723
+ let result;
50724
+ if ( this.controller1 === undefined || this.controller2 === undefined ){
50725
+ result = false;
50726
+ }else {
50727
+ result = this.controller1.userData.selectPressed || this.controller2.userData.selectPressed;
50728
+ }
50729
+ //console.log( `ControllerGestures touch: ${result}`);
50730
+ return result;
50731
+ }
50732
+
50733
+ get debugMsg(){
50734
+ return this.type;
50735
+ }
50736
+
50737
+ update(){
50738
+ const data1 = this.controller1.userData.gestures;
50739
+ const data2 = this.controller2.userData.gestures;
50740
+ const currentTime = this.clock.getElapsedTime();
50741
+
50742
+ let elapsedTime;
50743
+
50744
+ if (this.controller1.userData.selectPressed && data1.startPosition === undefined){
50745
+ elapsedTime = currentTime - data1.startTime;
50746
+ if (elapsedTime > 0.05 ) data1.startPosition = this.controller1.position.clone();
50747
+ }
50748
+
50749
+ if (this.controller2.userData.selectPressed && data2.startPosition === undefined){
50750
+ elapsedTime = currentTime - data2.startTime;
50751
+ if (elapsedTime > 0.05 ) data2.startPosition = this.controller2.position.clone();
50752
+ }
50753
+
50754
+ if (!this.controller1.userData.selectPressed && this.type === 'tap' ){
50755
+ //Only dispatch event after double click limit is passed
50756
+ elapsedTime = this.clock.getElapsedTime() - data1.endTime;
50757
+ if (elapsedTime > this.doubleClickLimit){
50758
+ console.log( `ControllerGestures.update dispatchEvent taps:${data1.taps}` );
50759
+ switch( data1.taps ){
50760
+ case 1:
50761
+ this.dispatchEvent( { type: 'tap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
50762
+ break;
50763
+ case 2:
50764
+ this.dispatchEvent( { type: 'doubletap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
50765
+ break;
50766
+ case 3:
50767
+ this.dispatchEvent( { type: 'tripletap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
50768
+ break;
50769
+ case 4:
50770
+ this.dispatchEvent( { type: 'quadtap', position: this.controller1.position, matrixWorld: this.controller1.matrixWorld } );
50771
+ break;
50772
+ }
50773
+ this.type = "unknown";
50774
+ data1.taps = 0;
50775
+ }
50776
+ }
50777
+
50778
+ if (this.type === 'unknown' && this.touch){
50779
+ if (data1.startPosition !== undefined){
50780
+ if (this.multiTouch){
50781
+ if (data2.startPosition !== undefined){
50782
+ //startPosition is undefined for 1/20 sec
50783
+ //test for pinch or rotate
50784
+ const startDistance = data1.startPosition.distanceTo( data2.startPosition );
50785
+ const currentDistance = this.controller1.position.distanceTo( this.controller2.position );
50786
+ const delta = currentDistance - startDistance;
50787
+ if ( Math.abs(delta) > 0.01 ){
50788
+ this.type = 'pinch';
50789
+ this.startDistance = this.controller1.position.distanceTo( this.controller2.position );
50790
+ this.dispatchEvent( { type: 'pinch', delta: 0, scale: 1, initialise: true } );
50791
+ }else {
50792
+ const v1 = data2.startPosition.clone().sub( data1.startPosition ).normalize();
50793
+ const v2 = this.controller2.position.clone().sub( this.controller1.position ).normalize();
50794
+ const theta = v1.angleTo( v2 );
50795
+ if (Math.abs(theta) > 0.2){
50796
+ this.type = 'rotate';
50797
+ this.startVector = v2.clone();
50798
+ this.dispatchEvent( { type: 'rotate', theta: 0, initialise: true } );
50799
+ }
50800
+ }
50801
+ }
50802
+ }else {
50803
+ //test for swipe or pan
50804
+ let dist = data1.startPosition.distanceTo( this.controller1.position );
50805
+ elapsedTime = this.clock.getElapsedTime() - data1.startTime;
50806
+ const velocity = dist/elapsedTime;
50807
+ //console.log(`dist:${dist.toFixed(3)} velocity:${velocity.toFixed(3)}`);
50808
+ if ( dist > 0.01 && velocity > 0.1 ){
50809
+ const v = this.controller1.position.clone().sub( data1.startPosition );
50810
+ let maxY = (Math.abs(v.y) > Math.abs(v.x)) && (Math.abs(v.y) > Math.abs(v.z));
50811
+ if ( maxY )this.type = "swipe";
50812
+ }else if (dist > 0.006 && velocity < 0.03){
50813
+ this.type = "pan";
50814
+ this.startPosition = this.controller1.position.clone();
50815
+ this.dispatchEvent( { type: 'pan', delta: new THREE.Vector3(), initialise: true } );
50816
+ }
50817
+ }
50818
+ }
50819
+ }else if (this.type === 'pinch'){
50820
+ const currentDistance = this.controller1.position.distanceTo( this.controller2.position );
50821
+ const delta = currentDistance - this.startDistance;
50822
+ const scale = currentDistance/this.startDistance;
50823
+ this.dispatchEvent( { type: 'pinch', delta, scale });
50824
+ }else if (this.type === 'rotate'){
50825
+ const v = this.controller2.position.clone().sub( this.controller1.position ).normalize();
50826
+ let theta = this.startVector.angleTo( v );
50827
+ const cross = this.startVector.clone().cross( v );
50828
+ if (this.up.dot(cross) > 0) theta = -theta;
50829
+ this.dispatchEvent( { type: 'rotate', theta } );
50830
+ }else if (this.type === 'pan'){
50831
+ const delta = this.controller1.position.clone().sub( this.startPosition );
50832
+ this.dispatchEvent( { type: 'pan', delta } );
50833
+ }
50834
+ }
50835
+ }
50836
+
50510
50837
  /**
50511
50838
  * @author Jiyao Wang <wangjiy@ncbi.nlm.nih.gov> / https://github.com/ncbi/icn3d
50512
50839
  */
@@ -50527,6 +50854,8 @@ class Scene {
50527
50854
 
50528
50855
  ic.cameraCls.setCamera();
50529
50856
 
50857
+ this.setVrAr();
50858
+
50530
50859
  if(ic.bSkipChemicalbinding === undefined || !ic.bSkipChemicalbinding) {
50531
50860
  ic.applyOtherCls.applyChemicalbindingOptions();
50532
50861
  }
@@ -50667,22 +50996,6 @@ class Scene {
50667
50996
  // highlight on impostors
50668
50997
  ic.mdl_ghost = new THREE.Object3D(); // Impostor display
50669
50998
  ic.scene_ghost.add(ic.mdl_ghost);
50670
-
50671
- // for VR view
50672
- let controller = ic.renderer.xr.getController( 0 );
50673
- ic.scene.add( controller );
50674
-
50675
- let controllerModelFactory = new XRControllerModelFactory();
50676
- let controllerGrip = ic.renderer.xr.getControllerGrip( 0 );
50677
- controllerGrip.add( controllerModelFactory.createControllerModel( controllerGrip ) );
50678
- ic.scene.add( controllerGrip );
50679
-
50680
- $("#" + me.pre + "VRButton").remove();
50681
- //document.body.appendChild( ic.VRButtonCls.createButton( ic.renderer ) );
50682
- $("#" + me.pre + "viewer").get(0).appendChild( ic.VRButtonCls.createButton( ic.renderer ) );
50683
-
50684
- $("#" + me.pre + "ARButton").remove();
50685
- $("#" + me.pre + "viewer").get(0).appendChild( ic.ARButtonCls.createButton( ic.renderer ) );
50686
50999
 
50687
51000
  // related to pk
50688
51001
  ic.objects = []; // define objects for pk, not all elements are used for pk
@@ -50714,8 +51027,196 @@ class Scene {
50714
51027
  ic.cams = {
50715
51028
  perspective: ic.perspectiveCamera,
50716
51029
  orthographic: ic.orthographicCamera,
50717
- };
51030
+ };
50718
51031
  };
51032
+
51033
+ setVrAr() { let ic = this.icn3d, me = ic.icn3dui;
51034
+
51035
+ // https://github.com/NikLever/Learn-WebXR/tree/master/start
51036
+ // https://github.com/mrdoob/three.js/blob/master/examples/webxr_ar_cones.html
51037
+ // https://github.com/mrdoob/three.js/blob/master/examples/webxr_vr_cubes.html
51038
+
51039
+ if(ic.bVr) {
51040
+ /*
51041
+ ic.raycasterVR = new THREE.Raycaster();
51042
+ ic.workingMatrix = new THREE.Matrix4();
51043
+ ic.workingVector = new THREE.Vector3();
51044
+ ic.origin = new THREE.Vector3();
51045
+
51046
+ let radius = 0.08;
51047
+ let geometry = new THREE.IcosahedronBufferGeometry( radius, 2 );
51048
+ ic.highlightVR = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: 0xffffff, side: THREE.BackSide } ) );
51049
+ ic.highlightVR.scale.set(1.2, 1.2, 1.2);
51050
+ */
51051
+ // modified from https://github.com/NikLever/Learn-WebXR/blob/master/complete/lecture3_7/app.js
51052
+ // add dolly to move camera
51053
+ ic.dolly = new THREE.Object3D();
51054
+ ic.dolly.position.z = 5;
51055
+ ic.dolly.add(ic.cam);
51056
+ ic.scene.add(ic.dolly);
51057
+
51058
+ ic.dummyCam = new THREE.Object3D();
51059
+ ic.cam.add(ic.dummyCam);
51060
+
51061
+ ic.clock = new THREE.Clock();
51062
+
51063
+ //controllers
51064
+ ic.controllers = this.getControllers();
51065
+
51066
+ ic.getInputSources = true; // default
51067
+
51068
+ function onSelectStart() {
51069
+ // this.children[0].scale.z = 10;
51070
+ this.userData.selectPressed = true;
51071
+ }
51072
+
51073
+ function onSelectEnd() {
51074
+ // this.children[0].scale.z = 0;
51075
+ // ic.highlightVR.visible = false;
51076
+ this.userData.selectPressed = false;
51077
+ }
51078
+ /*
51079
+ function buildController( data ) {
51080
+ let geometry, material;
51081
+
51082
+ switch ( data.targetRayMode ) {
51083
+ case 'tracked-pointer':
51084
+ geometry = new THREE.BufferGeometry();
51085
+ geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, - 1 ], 3 ) );
51086
+ geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( [ 0.5, 0.5, 0.5, 0, 0, 0 ], 3 ) );
51087
+
51088
+ material = new THREE.LineBasicMaterial( { vertexColors: true, blending: THREE.AdditiveBlending } );
51089
+
51090
+ return new THREE.Line( geometry, material );
51091
+
51092
+ case 'gaze':
51093
+ geometry = new THREE.RingGeometry( 0.02, 0.04, 32 ).translate( 0, 0, - 1 );
51094
+ material = new THREE.MeshBasicMaterial( { opacity: 0.5, transparent: true } );
51095
+ return new THREE.Mesh( geometry, material );
51096
+ }
51097
+ }
51098
+ */
51099
+ ic.controllers.forEach( (controller) => {
51100
+ controller.addEventListener( 'selectstart', onSelectStart );
51101
+ controller.addEventListener( 'selectend', onSelectEnd );
51102
+ /*
51103
+ controller.addEventListener( 'connected', function ( event ) {
51104
+ const mesh = buildController(event.data);
51105
+ mesh.scale.z = 0;
51106
+ this.add( mesh );
51107
+ } );
51108
+ controller.addEventListener( 'disconnected', function () {
51109
+ this.remove( this.children[ 0 ] );
51110
+ ic.controllers.forEach( (controllerTmp) => {
51111
+ controllerTmp = null;
51112
+ });
51113
+ //self.controllerGrip = null;
51114
+ } );
51115
+ */
51116
+ });
51117
+ }
51118
+ else if(ic.bAr) {
51119
+ //Add gestures here
51120
+ ic.gestures = new ControllerGestures(ic.renderer);
51121
+ ic.scene.add(ic.gestures.controller1);
51122
+ ic.scene.add(ic.gestures.controller2);
51123
+
51124
+ ic.gestures.addEventListener('tap', (ev) => {
51125
+ //if(!ic.mdl.visible) {
51126
+ // ic.mdl.visible = true;
51127
+ //}
51128
+
51129
+ const controller = ic.gestures.controller1;
51130
+ //ic.mdl.position.set( 0, 0, - 0.3 ).applyMatrix4( controller.matrixWorld );
51131
+ ic.mdl.position.set( -0.03, 0, - 0.3 ).applyMatrix4( controller.matrixWorld );
51132
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(0.1));
51133
+ ic.mdl.scale.copy(new THREE.Vector3( 0.001, 0.001, 0.001 ));
51134
+ });
51135
+
51136
+ ic.gestures.addEventListener('doubletap', (ev) => {
51137
+ const controller = ic.gestures.controller1;
51138
+ //ic.mdl.position.set( 0, 0, - 0.3 ).applyMatrix4( controller.matrixWorld );
51139
+ ic.mdl.position.set( -0.06, 0, - 0.6 ).applyMatrix4( controller.matrixWorld );
51140
+ //ic.mdl.scale.copy(ic.mdl.scale.multiplyScalar(10));
51141
+ ic.mdl.scale.copy(new THREE.Vector3( 0.005, 0.005, 0.005 ));
51142
+ });
51143
+ /*
51144
+ ic.gestures.addEventListener('swipe', (ev) => {
51145
+ // if(ic.mdl.visible) {
51146
+ // ic.mdl.visible = false;
51147
+ // }
51148
+ });
51149
+
51150
+ ic.gestures.addEventListener('pan', (ev) => {
51151
+ // if(ev.initialise !== undefined) {
51152
+ // thisClass.startPosition = ic.mdl.position.clone();
51153
+ // }
51154
+ // else {
51155
+ // const pos = thisClass.startPosition.clone().add(ev.delta.multiplyScalar(3));
51156
+ // ic.mdl.position.copy(pos);
51157
+ // }
51158
+ });
51159
+
51160
+ ic.gestures.addEventListener('pinch', (ev) => {
51161
+ // if(ev.initialise !== undefined) {
51162
+ // thisClass.startScale = ic.mdl.scale.clone();
51163
+ // }
51164
+ // else {
51165
+ // const scale = thisClass.startScale.clone().multiplyScalar(ev.scale);
51166
+ // ic.mdl.scale.copy(scale);
51167
+ // }
51168
+ });
51169
+
51170
+ ic.gestures.addEventListener('rotate', (ev) => {
51171
+ // if(ev.initialise !== undefined) {
51172
+ // thisClass.startQuaternion = ic.mdl.quaternion.clone();
51173
+ // }
51174
+ // else {
51175
+ // ic.mdl.quaternion.copy(thisClass.startQuaternion);
51176
+ // ic.mdl.rotateY(ev.theta);
51177
+ // }
51178
+ });
51179
+ */
51180
+ }
51181
+
51182
+ $("#" + me.pre + "VRButton").remove();
51183
+ $("#" + me.pre + "viewer").get(0).appendChild( ic.VRButtonCls.createButton( ic.renderer ) );
51184
+
51185
+ $("#" + me.pre + "ARButton").remove();
51186
+ $("#" + me.pre + "viewer").get(0).appendChild( ic.ARButtonCls.createButton( ic.renderer ) );
51187
+ }
51188
+
51189
+ getControllers() { let ic = this.icn3d; ic.icn3dui;
51190
+ const controllerModelFactory = new XRControllerModelFactory();
51191
+ /*
51192
+ const geometry = new THREE.BufferGeometry().setFromPoints( [
51193
+ new THREE.Vector3(0,0,0),
51194
+ new THREE.Vector3(0,0,-1)
51195
+ ]);
51196
+ const line = new THREE.Line( geometry );
51197
+ line.name = 'line';
51198
+ line.scale.z = 0;
51199
+ */
51200
+
51201
+ const controllers = [];
51202
+
51203
+ for(let i=0; i<=1; i++){
51204
+ const controller = ic.renderer.xr.getController( i );
51205
+ ic.dolly.add( controller );
51206
+
51207
+ // controller.add( line.clone() );
51208
+ controller.userData.selectPressed = false;
51209
+ ic.scene.add(controller);
51210
+
51211
+ controllers.push( controller );
51212
+
51213
+ const grip = ic.renderer.xr.getControllerGrip( i );
51214
+ grip.add( controllerModelFactory.createControllerModel( grip ));
51215
+ ic.scene.add( grip );
51216
+ }
51217
+
51218
+ return controllers;
51219
+ }
50719
51220
  }
50720
51221
 
50721
51222
  /**
@@ -51929,7 +52430,7 @@ class Draw {
51929
52430
  }
51930
52431
 
51931
52432
  //Draw the 3D structure. It rebuilds scene, applies previous color, applies the transformation, and renders the image.
51932
- draw(bVr) { let ic = this.icn3d, me = ic.icn3dui;
52433
+ draw(bVrAr) { let ic = this.icn3d, me = ic.icn3dui;
51933
52434
  if(ic.bRender && (!ic.hAtoms || Object.keys(ic.hAtoms) == 0)) ic.hAtoms = me.hashUtilsCls.cloneHash(ic.atoms);
51934
52435
 
51935
52436
  ic.sceneCls.rebuildScene();
@@ -51967,7 +52468,7 @@ class Draw {
51967
52468
  }
51968
52469
 
51969
52470
  this.applyTransformation(ic._zoomFactor, ic.mouseChange, ic.quaternion);
51970
- this.render(bVr);
52471
+ this.render(bVrAr);
51971
52472
  }
51972
52473
 
51973
52474
  ic.impostorCls.clearImpostors();
@@ -52000,10 +52501,10 @@ class Draw {
52000
52501
  }
52001
52502
 
52002
52503
  //Render the scene and objects into pixels.
52003
- render(bVr) { let ic = this.icn3d; ic.icn3dui;
52504
+ render(bVrAr) { let ic = this.icn3d; ic.icn3dui;
52004
52505
  let thisClass = this;
52005
52506
  // setAnimationLoop is required for VR
52006
- if(bVr) {
52507
+ if(bVrAr) {
52007
52508
  ic.renderer.setAnimationLoop( function() {
52008
52509
  thisClass.render_base();
52009
52510
  });
@@ -52013,8 +52514,39 @@ class Draw {
52013
52514
  }
52014
52515
  }
52015
52516
 
52517
+ handleController( controller, dt, selectPressed) { let ic = this.icn3d; ic.icn3dui;
52518
+ // modified from https://github.com/NikLever/Learn-WebXR/blob/master/complete/lecture3_7/app.js
52519
+ if ( selectPressed ){
52520
+ /*
52521
+ ic.workingMatrix.identity().extractRotation( controller.matrixWorld );
52522
+
52523
+ ic.raycasterVR.ray.origin.setFromMatrixPosition( controller.matrixWorld );
52524
+ ic.raycasterVR.ray.direction.set( 0, 0, - 1 ).applyMatrix4( ic.workingMatrix );
52525
+
52526
+ const intersects = ic.raycasterVR.intersectObjects( ic.objects );
52527
+
52528
+ if (intersects.length>0){
52529
+ intersects[0].object.add(ic.highlightVR);
52530
+ ic.highlightVR.visible = true;
52531
+ }else{
52532
+ ic.highlightVR.visible = false;
52533
+ }
52534
+ */
52535
+ const speed = 5; //2;
52536
+ const quaternion = ic.dolly.quaternion.clone();
52537
+ //ic.dolly.quaternion.copy(ic.dummyCam.getWorldQuaternion());
52538
+ ic.dummyCam.getWorldQuaternion(ic.dolly.quaternion);
52539
+ ic.dolly.translateZ(-dt * speed);
52540
+ //ic.dolly.position.y = 0; // limit to a plane
52541
+ ic.dolly.quaternion.copy(quaternion);
52542
+
52543
+ }
52544
+ }
52545
+
52016
52546
  //Render the scene and objects into pixels.
52017
52547
  render_base() { let ic = this.icn3d, me = ic.icn3dui;
52548
+ let thisClass = this;
52549
+
52018
52550
  if(me.bNode) return;
52019
52551
 
52020
52552
  let cam = (ic.bControlGl && !me.bNode) ? window.cam : ic.cam;
@@ -52029,10 +52561,88 @@ class Draw {
52029
52561
  }
52030
52562
 
52031
52563
  ic.renderer.setPixelRatio( window.devicePixelRatio ); // r71
52564
+
52565
+ if(ic.bVr) {
52566
+ let dt = 0.04; // ic.clock.getDelta();
52567
+
52568
+ if (ic.controllers){
52569
+ // let result = this.getThumbStickMove();
52570
+ // let y = result.y * -1;
52571
+ // let pressed = result.pressed;
52572
+
52573
+ for(let i = 0, il = ic.controllers.length; i < il; ++i) {
52574
+ let controller = ic.controllers[i];
52575
+ dt = (i % 2 == 0) ? dt : -dt; // dt * y;
52576
+ thisClass.handleController( controller, dt, controller.userData.selectPressed );
52577
+ //thisClass.handleController( controller, dt, pressed );
52578
+ }
52579
+ }
52580
+ }
52581
+ else if(ic.bAr) {
52582
+ if ( ic.renderer.xr.isPresenting ){
52583
+ ic.gestures.update();
52584
+ }
52585
+ }
52586
+
52032
52587
  if(ic.scene) {
52033
52588
  ic.renderer.render(ic.scene, cam);
52034
52589
  }
52035
52590
  }
52591
+
52592
+ getThumbStickMove() { let ic = this.icn3d; ic.icn3dui;
52593
+ let y = 0;
52594
+ let btnPressed = false;
52595
+
52596
+ if ( ic.renderer.xr.isPresenting ){
52597
+ const session = ic.renderer.xr.getSession();
52598
+ const inputSources = session.inputSources;
52599
+
52600
+ if ( ic.getInputSources ){
52601
+ //const info = [];
52602
+
52603
+ inputSources.forEach( inputSource => {
52604
+ const gp = inputSource.gamepad;
52605
+ gp.axes;
52606
+ gp.buttons;
52607
+ const mapping = gp.mapping;
52608
+ ic.useStandard = (mapping == 'xr-standard');
52609
+ inputSource.handedness;
52610
+ const profiles = inputSource.profiles;
52611
+ ic.gamepadType = "";
52612
+ profiles.forEach( profile => {
52613
+ if (profile.indexOf('touchpad')!=-1) ic.gamepadType = 'touchpad';
52614
+ if (profile.indexOf('thumbstick')!=-1) ic.gamepadType = 'thumbstick';
52615
+ });
52616
+ inputSource.targetRayMode;
52617
+ //info.push({ gamepad, handedness, profiles, targetRayMode });
52618
+ });
52619
+
52620
+ //console.log( JSON.stringify(info) );
52621
+
52622
+ ic.getInputSources = false;
52623
+ }else if (ic.useStandard && ic.gamepadType != ""){
52624
+ inputSources.forEach( inputSource => {
52625
+ const gp = inputSource.gamepad;
52626
+ const thumbstick = (ic.gamepadType=='thumbstick');
52627
+ //{"trigger":{"button":0},"touchpad":{"button":2,"xAxis":0,"yAxis":1}},
52628
+ //"squeeze":{"button":1},"thumbstick":{"button":3,"xAxis":2,"yAxis":3},"button":{"button":6}}}
52629
+ const xaxisOffset = (thumbstick) ? 2 : 0;
52630
+ const btnIndex = (thumbstick) ? 3 : 2;
52631
+ btnPressed = gp.buttons[btnIndex].pressed;
52632
+ // if ( inputSource.handedness == 'right') {
52633
+ // } else if ( inputSource.handedness == 'left') {
52634
+ // }
52635
+
52636
+ //https://beej.us/blog/data/javascript-gamepad/
52637
+ // x,y-axis values are between -1 and 1
52638
+ gp.axes[xaxisOffset];
52639
+ y = gp.axes[xaxisOffset + 1];
52640
+ });
52641
+ }
52642
+ }
52643
+
52644
+ return {'y': y, 'pressed': btnPressed};
52645
+ }
52036
52646
  }
52037
52647
 
52038
52648
  /**
@@ -52639,7 +53249,7 @@ class SaveFile {
52639
53249
  }
52640
53250
 
52641
53251
  //getAtomPDB: function(atomHash, bPqr, bPdb, bNoChem) { let ic = this.icn3d, me = ic.icn3dui;
52642
- getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader) { let ic = this.icn3d, me = ic.icn3dui;
53252
+ getAtomPDB(atomHash, bPqr, bNoChem, bNoHeader, chainResi2pdb) { let ic = this.icn3d, me = ic.icn3dui;
52643
53253
  let pdbStr = '';
52644
53254
 
52645
53255
  // get all phosphate groups in lipids
@@ -52784,9 +53394,19 @@ class SaveFile {
52784
53394
  let molNum = 1, prevStru = '';
52785
53395
  //pdbStr += '\n';
52786
53396
 
53397
+ let addedChainResiHash = {};
52787
53398
  for(let i in atomHash) {
52788
53399
  let atom = ic.atoms[i];
52789
53400
 
53401
+ let chainResi = atom.chain + '_' + atom.resi;
53402
+ if(chainResi2pdb && chainResi2pdb.hasOwnProperty(chainResi)) {
53403
+ if(!addedChainResiHash.hasOwnProperty(chainResi)) {
53404
+ pdbStr += chainResi2pdb[chainResi];
53405
+ addedChainResiHash[chainResi] = 1;
53406
+ }
53407
+ continue;
53408
+ }
53409
+
52790
53410
  // remove chemicals
52791
53411
  if(bNoChem && atom.het) continue;
52792
53412
 
@@ -52799,8 +53419,9 @@ class SaveFile {
52799
53419
 
52800
53420
  if(bMulStruc) pdbStr += 'MODEL ' + molNum + '\n';
52801
53421
 
52802
- // add header
52803
- if(!bNoHeader) pdbStr += this.getPDBHeader(molNum - 1, stru2header);
53422
+ // add header
53423
+ let mutantInfo = (chainResi2pdb) ? "Mutated chain_residue " + Object.keys(chainResi2pdb) + '; ' : '';
53424
+ if(!bNoHeader) pdbStr += this.getPDBHeader(molNum - 1, stru2header, mutantInfo);
52804
53425
 
52805
53426
  prevStru = atom.structure;
52806
53427
  ++molNum;
@@ -53028,21 +53649,25 @@ class SaveFile {
53028
53649
 
53029
53650
  return pdbStr;
53030
53651
  }
53031
- getPDBHeader(struNum, stru2header) { let ic = this.icn3d; ic.icn3dui;
53652
+ getPDBHeader(struNum, stru2header, mutantInfo) { let ic = this.icn3d; ic.icn3dui;
53032
53653
  if(struNum === undefined) struNum = 0;
53033
53654
 
53034
53655
  let pdbStr = '';
53035
53656
  let stru = Object.keys(ic.structures)[struNum];
53036
- pdbStr += 'HEADER PDB From iCn3D'.padEnd(62, ' ') + stru + '\n';
53657
+ let id = (mutantInfo) ? stru + '2' : stru;
53658
+ pdbStr += 'HEADER PDB From iCn3D'.padEnd(62, ' ') + id + '\n';
53037
53659
 
53038
53660
  if(struNum == 0) {
53039
53661
  let title =(ic.molTitle.length > 50) ? ic.molTitle.substr(0,47) + '...' : ic.molTitle;
53040
53662
  // remove quotes
53041
53663
  if(title.indexOf('"') != -1) title = '';
53664
+ if(mutantInfo) {
53665
+ title = mutantInfo + title;
53666
+ }
53042
53667
  pdbStr += 'TITLE ' + title + '\n';
53043
53668
  }
53044
53669
 
53045
- if(stru2header) {
53670
+ if(stru2header && stru2header[stru]) {
53046
53671
  pdbStr += stru2header[stru];
53047
53672
  }
53048
53673
 
@@ -53105,6 +53730,9 @@ class SaveFile {
53105
53730
  if(me.cfg.cid !== undefined) {
53106
53731
  url = "https://www.ncbi.nlm.nih.gov/pccompound/?term=";
53107
53732
  }
53733
+ else if(me.cfg.afid !== undefined) {
53734
+ url = "https://alphafold.ebi.ac.uk/search/text/";
53735
+ }
53108
53736
  else {
53109
53737
  //if(ic.inputid.indexOf(",") !== -1) {
53110
53738
  if(Object.keys(ic.structures).length > 1) {
@@ -53124,11 +53752,17 @@ class SaveFile {
53124
53752
 
53125
53753
  if(idArray.length === 1) {
53126
53754
  url += ic.inputid;
53127
- if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to Structure Summary " + ic.inputid + ": " + url, false);
53755
+ if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to " + ic.inputid + ": " + url, false);
53128
53756
  }
53129
53757
  else if(idArray.length === 2) {
53130
- url += idArray[0] + " OR " + idArray[1];
53131
- if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to structures " + idArray[0] + " and " + idArray[1] + ": " + url, false);
53758
+ if(me.cfg.afid) {
53759
+ url += idArray[0] + " " + idArray[1];
53760
+ }
53761
+ else {
53762
+ url += idArray[0] + " OR " + idArray[1];
53763
+ }
53764
+
53765
+ if(bLog) me.htmlCls.clickMenuCls.setLogCmd("link to structures " + idArray[0] + " and " + idArray[1] + ": " + url, false);
53132
53766
  }
53133
53767
  }
53134
53768
 
@@ -55808,7 +56442,7 @@ class SetMenu {
55808
56442
  html += "<ul>";
55809
56443
  html += me.htmlCls.setHtmlCls.getLink('mn1_vastplus', 'NCBI VAST+ (PDB Assembly)' + me.htmlCls.wifiStr);
55810
56444
  html += me.htmlCls.setHtmlCls.getLink('mn1_vast', 'NCBI VAST (PDB)' + me.htmlCls.wifiStr);
55811
- html += me.htmlCls.setHtmlCls.getLink('mn1_foldseek', 'Foldseek (PDB & AalphaFold)' + me.htmlCls.wifiStr);
56445
+ html += me.htmlCls.setHtmlCls.getLink('mn1_foldseek', 'Foldseek (PDB & AlphaFold)' + me.htmlCls.wifiStr);
55812
56446
  html += "</ul>";
55813
56447
 
55814
56448
  html += "<li><span>Retrieve by ID</span>";
@@ -56124,11 +56758,22 @@ class SetMenu {
56124
56758
 
56125
56759
  html += "<li>-</li>";
56126
56760
 
56761
+ let liStr = "<li><a href='";
56762
+
56127
56763
  html += "<li><span>VR & AR Hints</span>";
56128
56764
  html += "<ul>";
56129
- html += "<li><span>VR: VR Headsets</span>";
56130
- html += "<li><span>AR: Chrome in Android</span>";
56765
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#vr' target='_blank'>VR: VR Headsets</a></li>";
56766
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#ar' target='_blank'>AR: Chrome in Android</a></li>";
56131
56767
  html += "</ul>";
56768
+ /*
56769
+ let liStr = "<li><a href='";
56770
+
56771
+ html += "<ul class='icn3d-mn-item'>";
56772
+
56773
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#about' target='_blank'>About iCn3D<span style='font-size:0.9em'> " + me.REVISION + "</span></a></li>";
56774
+
56775
+ html += liStr + me.htmlCls.baseUrl + "icn3d/icn3d.html#gallery' target='_blank'>Live Gallery " + me.htmlCls.wifiStr + "</a></li>";
56776
+ */
56132
56777
 
56133
56778
  html += me.htmlCls.setHtmlCls.getLink('mn6_sidebyside', 'Side by Side');
56134
56779
 
@@ -58652,7 +59297,7 @@ class SetDialog {
58652
59297
  html += "</div>";
58653
59298
  html += me.htmlCls.divStr + "tracktab4'>";
58654
59299
  html += "Track Title: " + me.htmlCls.inputTextStr + "id='" + me.pre + "track_title' placeholder='track title' size=16> <br><br>";
58655
- 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>";
59300
+ 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>";
58656
59301
  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>";
58657
59302
  html += me.htmlCls.buttonStr + "addtrack_button4'>Add Track</button>";
58658
59303
  html += "</div>";
@@ -58977,6 +59622,11 @@ class Events {
58977
59622
  let pos = hostUrl.indexOf("?");
58978
59623
  hostUrl = (pos == -1) ? hostUrl : hostUrl.substr(0, pos);
58979
59624
 
59625
+ // some URLs from VAST search are like https://www.ncbi.nlm.nih.gov/Structure/vast/icn3d/
59626
+ if(hostUrl == 'https://www.ncbi.nlm.nih.gov/Structure/vast/icn3d/') {
59627
+ hostUrl = 'https://www.ncbi.nlm.nih.gov/Structure/icn3d/';
59628
+ }
59629
+
58980
59630
  ic.definedSetsCls.clickCustomAtoms();
58981
59631
  ic.definedSetsCls.clickCommand_apply();
58982
59632
  ic.definedSetsCls.clickModeswitch();
@@ -61148,6 +61798,7 @@ class AlignSeq {
61148
61798
  let index = 0,
61149
61799
  prevResCnt2nd = 0;
61150
61800
  let firstChainid, oriChainid;
61801
+
61151
61802
  // for(let i in ic.alnChains) {
61152
61803
  for (let m = 0, ml = alignChainArray.length; m < ml; ++m) {
61153
61804
  let i = alignChainArray[m];
@@ -61296,6 +61947,9 @@ class AlignSeq {
61296
61947
  resiHtmlArray[j] += '<span class="icn3d-sheet">&nbsp;</span>';
61297
61948
  }
61298
61949
  }
61950
+ else {
61951
+ resiHtmlArray[j] += '<span class="icn3d-sheet">&nbsp;</span>';
61952
+ }
61299
61953
  } else if (text == 'c') {
61300
61954
  resiHtmlArray[j] += '<span class="icn3d-coil">&nbsp;</span>';
61301
61955
  } else if (text == 'o') {
@@ -62352,17 +63006,20 @@ class Alternate {
62352
63006
  let viewSelectionAtomsCount = Object.keys(ic.viewSelectionAtoms).length;
62353
63007
  let allAtomsCount = Object.keys(ic.atoms).length;
62354
63008
 
62355
- ic.dAtoms = {};
63009
+ //ic.dAtoms = {};
63010
+
63011
+ // 1. alternate all structures
63012
+ //let moleculeArray = Object.keys(ic.structures);
62356
63013
 
62357
- // alternate all displayed structures
62358
- let moleculeArray = Object.keys(ic.structures);
62359
- // only alternate selected structures
62360
- // let structureHash = {};
62361
- // for(let i in ic.hAtoms) {
62362
- // let structure = ic.atoms[i].structure;
62363
- // structureHash[structure] = 1;
62364
- // }
62365
- // let moleculeArray = Object.keys(structureHash);
63014
+ // 2. only alternate displayed structures
63015
+ let structureHash = {};
63016
+ for(let i in ic.viewSelectionAtoms) {
63017
+ let structure = ic.atoms[i].structure;
63018
+ structureHash[structure] = 1;
63019
+ }
63020
+ let moleculeArray = Object.keys(structureHash);
63021
+
63022
+ ic.dAtoms = {};
62366
63023
 
62367
63024
  for(let i = 0, il = moleculeArray.length; i < il; ++i) {
62368
63025
  let structure = moleculeArray[i];
@@ -63106,7 +63763,7 @@ class ContactMap {
63106
63763
 
63107
63764
  let graphStr = '{\n';
63108
63765
 
63109
- let struc1 = (ic.structures.length > 0) ? ic.structures[0] : 'STRU';
63766
+ let struc1 = (ic.structures.length > 0) ? ic.structures[0] : 'stru';
63110
63767
  let len1 = nodeArray1.length,
63111
63768
  len2 = nodeArray2.length;
63112
63769
  let factor = 1;
@@ -65454,6 +66111,8 @@ class iCn3D {
65454
66111
  this.bNotLoadStructure = false;
65455
66112
 
65456
66113
  this.InputfileData = '';
66114
+ this.bVr = false; // cflag to indicate whether in VR state
66115
+ this.bAr = false; // cflag to indicate whether in VR state
65457
66116
 
65458
66117
  // default color range for Add Custom Color button in the Sequence & Annotation window
65459
66118
  this.startColor = 'blue';
@@ -65785,7 +66444,7 @@ class iCn3DUI {
65785
66444
  //even when multiple iCn3D viewers are shown together.
65786
66445
  this.pre = this.cfg.divid + "_";
65787
66446
 
65788
- this.REVISION = '3.12.3';
66447
+ this.REVISION = '3.12.5';
65789
66448
 
65790
66449
  // In nodejs, iCn3D defines "window = {navigator: {}}"
65791
66450
  this.bNode = (Object.keys(window).length < 2) ? true : false;
@@ -65953,6 +66612,11 @@ iCn3DUI.prototype.show3DStructure = function(pdbStr) { let me = this;
65953
66612
 
65954
66613
  if(pdbStr) { // input pdbStr
65955
66614
  ic.init();
66615
+
66616
+ ic.bInputfile = true;
66617
+ ic.InputfileType = 'pdb';
66618
+ ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + pdbStr : pdbStr;
66619
+
65956
66620
  ic.pdbParserCls.loadPdbData(pdbStr);
65957
66621
 
65958
66622
  if(me.cfg.resdef !== undefined && me.cfg.chains !== undefined) {