icn3d 3.54.3 → 3.54.4

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
@@ -2550,7 +2550,147 @@ var CIFTools;
2550
2550
  }
2551
2551
  return result(file);
2552
2552
  }
2553
+ /**
2554
+ * Small-molecule / crystallographic ("core") CIF support, e.g. files from COD.
2555
+ * Core CIF uses underscore names (_atom_site_label, _atom_site_fract_x) and fractional
2556
+ * coordinates, while iCn3D's loadCIF expects mmCIF names (_atom_site.label_atom_id)
2557
+ * and Cartesian coordinates. Without conversion getCategory("_atom_site") returns undefined.
2558
+ * This converts such a file to a minimal mmCIF text before it is parsed.
2559
+ */
2560
+ function isCoreCif(data) {
2561
+ return /^\s*_atom_site_fract_x\b/m.test(data) && !/^\s*_atom_site\./m.test(data);
2562
+ }
2563
+ // tokenize one CIF text keeping quotes; returns {tags:[...], loops:[{cols, rows}], singles:{}}
2564
+ function coreTokens(data) {
2565
+ var tokens = [], lines = data.split(/\r?\n/), i, line, m, re = /'([^']*)'(?=\s|$)|"([^"]*)"(?=\s|$)|(\S+)/g;
2566
+ for (i = 0; i < lines.length; i++) {
2567
+ line = lines[i];
2568
+ if (line.charAt(0) === ';') { // multi-line text field, skip it
2569
+ i++;
2570
+ while (i < lines.length && lines[i].charAt(0) !== ';') i++;
2571
+ tokens.push({ v: '?', t: 'text' });
2572
+ continue;
2573
+ }
2574
+ re.lastIndex = 0;
2575
+ while ((m = re.exec(line)) !== null) {
2576
+ if (m[3] !== undefined && m[3].charAt(0) === '#') break; // comment
2577
+ tokens.push(m[3] !== undefined ? { v: m[3] } : { v: (m[1] !== undefined ? m[1] : m[2]), t: 'quoted' });
2578
+ }
2579
+ }
2580
+ return tokens;
2581
+ }
2582
+ function coreToNumber(s) {
2583
+ var v = parseFloat(String(s).replace(/\(\d+\)$/, '')); // 0.7556(3) -> 0.7556
2584
+ return isNaN(v) ? 0 : v;
2585
+ }
2586
+ // symmetry operator like "1/2-x,y,1/2+z" -> function(frac) -> frac
2587
+ function coreSymop(str) {
2588
+ var parts = str.replace(/\s+/g, '').toLowerCase().split(','), rows = [], p, k, re = /([+-]?)(?:(\d+(?:\.\d+)?)(?:\/(\d+))?|([xyz]))/g, m, row, c;
2589
+ if (parts.length !== 3) return null;
2590
+ for (p = 0; p < 3; p++) {
2591
+ row = [0, 0, 0, 0]; // x y z const
2592
+ re.lastIndex = 0;
2593
+ while ((m = re.exec(parts[p])) !== null) {
2594
+ c = (m[1] === '-') ? -1 : 1;
2595
+ if (m[4]) { row['xyz'.indexOf(m[4])] += c; }
2596
+ else { row[3] += c * parseFloat(m[2]) / (m[3] ? parseFloat(m[3]) : 1); }
2597
+ }
2598
+ rows.push(row);
2599
+ }
2600
+ return function (f) {
2601
+ var out = [];
2602
+ for (k = 0; k < 3; k++) out.push(rows[k][0] * f[0] + rows[k][1] * f[1] + rows[k][2] * f[2] + rows[k][3]);
2603
+ return out;
2604
+ };
2605
+ }
2606
+ function convertCoreCif(data) {
2607
+ var tk = coreTokens(data), i = 0, n = tk.length, single = {}, loops = [], name = 'data_core', t, cols, rows, cur;
2608
+ function isTag(x) { return x && !x.t && x.v.charAt(0) === '_'; }
2609
+ while (i < n) {
2610
+ t = tk[i];
2611
+ if (!t.t && /^data_/i.test(t.v)) { name = t.v; i++; }
2612
+ else if (!t.t && /^loop_$/i.test(t.v)) {
2613
+ i++; cols = []; rows = [];
2614
+ while (i < n && isTag(tk[i])) cols.push(tk[i++].v.toLowerCase());
2615
+ cur = [];
2616
+ while (i < n && !isTag(tk[i]) && !(/^(loop_|data_)/i.test(tk[i].v) && !tk[i].t)) {
2617
+ cur.push(tk[i++].v);
2618
+ if (cur.length === cols.length) { rows.push(cur); cur = []; }
2619
+ }
2620
+ loops.push({ cols: cols, rows: rows });
2621
+ }
2622
+ else if (isTag(t)) { single[t.v.toLowerCase()] = (tk[i + 1] ? tk[i + 1].v : '?'); i += 2; }
2623
+ else i++;
2624
+ }
2625
+ var atoms = null, symops = [], l, c;
2626
+ for (l = 0; l < loops.length; l++) {
2627
+ if (loops[l].cols.indexOf('_atom_site_fract_x') >= 0) atoms = loops[l];
2628
+ c = loops[l].cols.indexOf('_symmetry_equiv_pos_as_xyz');
2629
+ if (c < 0) c = loops[l].cols.indexOf('_space_group_symop_operation_xyz');
2630
+ if (c >= 0) loops[l].rows.forEach(function (r) { var f = coreSymop(r[c]); if (f) symops.push(f); });
2631
+ }
2632
+ if (!atoms) return null;
2633
+ if (!symops.length) symops.push(coreSymop('x,y,z'));
2634
+
2635
+ // orthogonalization matrix (a along x, c* along z)
2636
+ var a = coreToNumber(single['_cell_length_a']), b = coreToNumber(single['_cell_length_b']), cc = coreToNumber(single['_cell_length_c']);
2637
+ var al = coreToNumber(single['_cell_angle_alpha'] || 90) * Math.PI / 180, be = coreToNumber(single['_cell_angle_beta'] || 90) * Math.PI / 180, ga = coreToNumber(single['_cell_angle_gamma'] || 90) * Math.PI / 180;
2638
+ var cosa = Math.cos(al), cosb = Math.cos(be), cosg = Math.cos(ga), sing = Math.sin(ga);
2639
+ var vol = Math.sqrt(1 - cosa * cosa - cosb * cosb - cosg * cosg + 2 * cosa * cosb * cosg);
2640
+ function toCart(f) {
2641
+ return [a * f[0] + b * cosg * f[1] + cc * cosb * f[2],
2642
+ b * sing * f[1] + cc * (cosa - cosb * cosg) / sing * f[2],
2643
+ cc * vol / sing * f[2]];
2644
+ }
2645
+ var col = {}; atoms.cols.forEach(function (x, k) { col[x] = k; });
2646
+ function get(r, key) { return (col[key] === undefined) ? '?' : r[col[key]]; }
2647
+
2648
+ var out = [name, '#', '_entry.id ' + name.substr(5), '#'];
2649
+ // core CIF "_cell_length_a" -> mmCIF "_cell.length_a" so iCn3D can parse the unit cell
2650
+ Object.keys(single).forEach(function (key) {
2651
+ if (/^_cell_/.test(key)) out.push(key.replace(/^_cell_/, '_cell.') + ' ' + String(single[key]).replace(/\(\d+\)$/, ''));
2652
+ });
2653
+ out.push('#');
2654
+ out.push(
2655
+ 'loop_', '_atom_site.group_PDB', '_atom_site.id', '_atom_site.type_symbol', '_atom_site.label_atom_id', '_atom_site.label_alt_id',
2656
+ '_atom_site.label_comp_id', '_atom_site.label_asym_id', '_atom_site.label_entity_id', '_atom_site.label_seq_id',
2657
+ '_atom_site.Cartn_x', '_atom_site.Cartn_y', '_atom_site.Cartn_z', '_atom_site.occupancy', '_atom_site.B_iso_or_equiv',
2658
+ '_atom_site.auth_seq_id', '_atom_site.auth_asym_id', '_atom_site.pdbx_PDB_model_num');
2659
+ var serial = 0, seen = [], r, s, f, g, k, dup, j, sym, el, lab, cart;
2660
+ for (r = 0; r < atoms.rows.length; r++) {
2661
+ var row = atoms.rows[r];
2662
+ if (get(row, '_atom_site_calc_flag') === 'dum') continue; // dummy atoms
2663
+ f = [coreToNumber(get(row, '_atom_site_fract_x')), coreToNumber(get(row, '_atom_site_fract_y')), coreToNumber(get(row, '_atom_site_fract_z'))];
2664
+ lab = get(row, '_atom_site_label');
2665
+ sym = get(row, '_atom_site_type_symbol'); if (sym === '?') sym = lab;
2666
+ el = (sym.match(/^[A-Za-z]{1,2}/) || ['X'])[0];
2667
+ el = el.charAt(0).toUpperCase() + el.substr(1).toLowerCase();
2668
+ var mySeen = [];
2669
+ for (s = 0; s < symops.length; s++) {
2670
+ g = symops[s](f).map(function (v) { return v - Math.floor(v + 1e-6); });
2671
+ dup = false;
2672
+ for (j = 0; j < mySeen.length; j++) {
2673
+ if (Math.abs(mySeen[j][0] - g[0]) < 1e-3 && Math.abs(mySeen[j][1] - g[1]) < 1e-3 && Math.abs(mySeen[j][2] - g[2]) < 1e-3) { dup = true; break; }
2674
+ }
2675
+ if (dup) continue;
2676
+ mySeen.push(g);
2677
+ cart = toCart(g);
2678
+ out.push(['HETATM', ++serial, el, lab, '.', 'MOL', 'A', 1, '.', cart[0].toFixed(3), cart[1].toFixed(3), cart[2].toFixed(3),
2679
+ coreToNumber(get(row, '_atom_site_occupancy') === '?' ? 1 : get(row, '_atom_site_occupancy')), '0.00', 1, 'A', 1].join(' '));
2680
+ }
2681
+ }
2682
+ out.push('#');
2683
+ return out.join('\n') + '\n';
2684
+ }
2553
2685
  function parse(data) {
2686
+ if (isCoreCif(data)) {
2687
+ var converted = convertCoreCif(data);
2688
+ if (converted) data = converted;
2689
+ }
2690
+ else {
2691
+ // "_cell_angle_alpha" -> "_cell.angle_alpha"
2692
+ data = data.replace(/^(\s*_cell)_(?=\w)/gm, '$1.');
2693
+ }
2554
2694
  return parseInternal(data);
2555
2695
  }
2556
2696
  Text.parse = parse;
@@ -60275,6 +60415,16 @@ class ClickMenu {
60275
60415
  thisClass.setLogCmd('set axis off', true);
60276
60416
  });
60277
60417
 
60418
+ me.myEventCls.onIds("#" + me.pre + "mn6_showucYes", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
60419
+ ic.setOptionCls.setOption('unitcell', 'yes');
60420
+ thisClass.setLogCmd('set unitcell on', true);
60421
+ });
60422
+
60423
+ me.myEventCls.onIds("#" + me.pre + "mn6_showucNo", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
60424
+ ic.setOptionCls.setOption('unitcell', 'no');
60425
+ thisClass.setLogCmd('set unitcell off', true);
60426
+ });
60427
+
60278
60428
  me.myEventCls.onIds("#" + me.pre + "mn6_symmetry", "click", async function(e) { let ic = me.icn3d; //e.preventDefault();
60279
60429
  ic.bAxisOnly = false;
60280
60430
  await ic.symdCls.retrieveSymmetry(Object.keys(ic.structures)[0]);
@@ -61594,6 +61744,15 @@ class SetMenu {
61594
61744
  html += "</ul>";
61595
61745
  html += "</li>";
61596
61746
 
61747
+ // if(me.cfg.mmcifid !== undefined) {
61748
+ html += this.getMenuText('mn2_unitcell', 'Unit Cell', undefined, undefined, 1);
61749
+ html += "<ul>";
61750
+ html += this.getRadio('mn6_showuc', 'mn6_showucYes', 'Show', undefined, undefined, 2);
61751
+ html += this.getRadio('mn6_showuc', 'mn6_showucNo', 'Hide', true, undefined, 2);
61752
+ html += "</ul>";
61753
+ html += "</li>";
61754
+ // }
61755
+
61597
61756
  html += this.getMenuSep();
61598
61757
 
61599
61758
  html += this.getMenuText('mn2_resetwrap', 'Reset', undefined, 1, 1);
@@ -82557,6 +82716,57 @@ class Axes {
82557
82716
 
82558
82717
  return axisPos;
82559
82718
  }
82719
+
82720
+ buildUnitcell() { let ic = this.icn3d, me = ic.icn3dui;
82721
+ if(!ic.cell) {
82722
+ var aaa = 1; //alert("Unit cell information is available for structures from mmCIF files. Please load a structure from mmCIF file.");
82723
+ return;
82724
+ }
82725
+
82726
+ let a = ic.cell.length_a;
82727
+ let b = ic.cell.length_b;
82728
+ let c = ic.cell.length_c;
82729
+ let alpha = ic.cell.angle_alpha;
82730
+ let beta = ic.cell.angle_beta;
82731
+ let gamma = ic.cell.angle_gamma;
82732
+
82733
+ const rad = d => d * Math.PI / 180;
82734
+ const ca = Math.cos(rad(alpha));
82735
+ const cb = Math.cos(rad(beta));
82736
+ const cg = Math.cos(rad(gamma));
82737
+ const sg = Math.sin(rad(gamma));
82738
+
82739
+ // Lattice vectors
82740
+ const va = [a, 0, 0];
82741
+ const vb = [b * cg, b * sg, 0];
82742
+ const cx = c * cb;
82743
+ const cy = c * (ca - cb * cg) / sg;
82744
+ const cz = Math.sqrt(c * c - cx * cx - cy * cy);
82745
+ const vc = [cx, cy, cz];
82746
+
82747
+ // 8 corners, indexed by bits (i, j, k) -> i*va + j*vb + k*vc
82748
+ const corner = (i, j, k) => [0, 1, 2].map(n => i * va[n] + j * vb[n] + k * vc[n]);
82749
+
82750
+ let color = me.htmlCls.ORANGE, radius = 0.1;
82751
+ let atomColor = me.parasCls.thr(color);
82752
+
82753
+ for (let i = 0; i < 2; i++) {
82754
+ for (let j = 0; j < 2; j++) {
82755
+ ic.analysisCls.addLine(corner(0, i, j)[0], corner(0, i, j)[1], corner(0, i, j)[2], corner(1, i, j)[0], corner(1, i, j)[1], corner(1, i, j)[2], color, false, 'unitcell', radius); // edges along a
82756
+ ic.analysisCls.addLine(corner(i, 0, j)[0], corner(i, 0, j)[1], corner(i, 0, j)[2], corner(i, 1, j)[0], corner(i, 1, j)[1], corner(i, 1, j)[2], color, false, 'unitcell', radius); // edges along b
82757
+ ic.analysisCls.addLine(corner(i, j, 0)[0], corner(i, j, 0)[1], corner(i, j, 0)[2], corner(i, j, 1)[0], corner(i, j, 1)[1], corner(i, j, 1)[2], color, false, 'unitcell', radius); // edges along c
82758
+ }
82759
+ }
82760
+
82761
+ // add sphere at each corner
82762
+ for (let i = 0; i < 2; i++) {
82763
+ for (let j = 0; j < 2; j++) {
82764
+ for (let k = 0; k < 2; k++) {
82765
+ ic.sphereCls.createSphereBase(new Vector3$1(corner(i, j, k)[0], corner(i, j, k)[1], corner(i, j, k)[2]), atomColor, radius, 1, false, false, 1);
82766
+ }
82767
+ }
82768
+ }
82769
+ }
82560
82770
  }
82561
82771
 
82562
82772
  /**
@@ -86553,7 +86763,7 @@ class ApplyOther {
86553
86763
  ic.applyCenterCls.applyCenterOptions(options);
86554
86764
 
86555
86765
  ic.axesCls.buildAllAxes(undefined, true);
86556
-
86766
+
86557
86767
  switch (options.axis.toLowerCase()) {
86558
86768
  case 'yes':
86559
86769
  ic.axis = true;
@@ -86564,6 +86774,19 @@ class ApplyOther {
86564
86774
  ic.axis = false;
86565
86775
  break;
86566
86776
  }
86777
+
86778
+ ic.lines['unitcell'] = []; // reset
86779
+
86780
+ switch (options.unitcell.toLowerCase()) {
86781
+ case 'yes':
86782
+ ic.unitcell = true;
86783
+ ic.axesCls.buildUnitcell();
86784
+
86785
+ break;
86786
+ case 'no':
86787
+ ic.unitcell = false;
86788
+ break;
86789
+ }
86567
86790
  switch (options.pk.toLowerCase()) {
86568
86791
  case 'atom':
86569
86792
  ic.pk = 1;
@@ -127084,7 +127307,10 @@ class PdbParser {
127084
127307
 
127085
127308
  ic.saveFileCls.showTitle();
127086
127309
 
127087
- if(me.cfg.rotate !== undefined) ic.resizeCanvasCls.rotStruc(me.cfg.rotate, true);
127310
+ // if(me.cfg.rotate !== undefined) {
127311
+ if(me.cfg.rotate !== undefined && !(me.cfg.url && me.cfg.url.indexOf('membranome') != -1)) {
127312
+ ic.resizeCanvasCls.rotStruc(me.cfg.rotate, true);
127313
+ }
127088
127314
 
127089
127315
  if(bAppend && Object.keys(ic.structures).length == 1 && !me.bNode) {
127090
127316
  // show all
@@ -131337,6 +131563,7 @@ class ParserUtils {
131337
131563
  let angle = -0.5 * Math.PI;
131338
131564
  ic.transformCls.setRotation(axis, angle);
131339
131565
  }
131566
+
131340
131567
  //if(Object.keys(ic.structures).length > 1) {
131341
131568
  // $("#" + ic.pre + "alternate").show();
131342
131569
  //}
@@ -135299,6 +135526,16 @@ class LoadCIF {
135299
135526
  = m24Array = m31Array = m32Array = m33Array = m34Array = [];
135300
135527
  }
135301
135528
 
135529
+ if(block.getCategory("_cell")) {
135530
+ ic.cell = {};
135531
+ ic.cell.length_a = parseFloat(block.getCategory("_cell").getColumn("length_a").getString(0));
135532
+ ic.cell.length_b = parseFloat(block.getCategory("_cell").getColumn("length_b").getString(0));
135533
+ ic.cell.length_c = parseFloat(block.getCategory("_cell").getColumn("length_c").getString(0));
135534
+ ic.cell.angle_alpha = parseFloat(block.getCategory("_cell").getColumn("angle_alpha").getString(0));
135535
+ ic.cell.angle_beta = parseFloat(block.getCategory("_cell").getColumn("angle_beta").getString(0));
135536
+ ic.cell.angle_gamma = parseFloat(block.getCategory("_cell").getColumn("angle_gamma").getString(0));
135537
+ }
135538
+
135302
135539
  // if (record === 'ENDMDL') {
135303
135540
  // ++moleculeNum;
135304
135541
  // id = ic.defaultPdbId;
@@ -136917,6 +137154,12 @@ class ApplyCommand {
136917
137154
  ic.opts['axis'] = 'no';
136918
137155
  ic.pc1 = false;
136919
137156
  }
137157
+ else if(command == 'set unitcell on') {
137158
+ ic.opts['unitcell'] = 'yes';
137159
+ }
137160
+ else if(command == 'set unitcell off') {
137161
+ ic.opts['unitcell'] = 'no';
137162
+ }
136920
137163
  else if(command == 'set fog on') {
136921
137164
  ic.opts['fog'] = 'yes';
136922
137165
  ic.fogCls.setFog(true);
@@ -147399,9 +147642,10 @@ class Diagram2d {
147399
147642
  // get the residues in the selection
147400
147643
  let ncbiresid = data;
147401
147644
  let resid = ic.ncbi2resid[ncbiresid];
147402
- let resi = resid.substr(resid.lastIndexOf('_') + 1);
147403
147645
 
147404
- if(ic.ig2ddgm_chainid && ic.resid2refnum && ic.resid2refnum[resid]) {
147646
+ if(resid && ic.ig2ddgm_chainid && ic.resid2refnum && ic.resid2refnum[resid]) {
147647
+ let resi = resid.substr(resid.lastIndexOf('_') + 1);
147648
+
147405
147649
  let refnumLabel = ic.resid2refnum[resid];
147406
147650
  let refnumStr = ic.refnumCls.rmStrandFromRefnumlabel(refnumLabel);
147407
147651
 
@@ -151174,7 +151418,8 @@ class SaveFile {
151174
151418
  idName = "PDB ID";
151175
151419
  }
151176
151420
  else if(bAlphaFold) {
151177
- idName = "AlphaFold ID";
151421
+ // idName = "AlphaFold ID";
151422
+ idName = "ID";
151178
151423
  }
151179
151424
 
151180
151425
  if(structureidArray.length > 1) {
@@ -154095,6 +154340,7 @@ class iCn3D {
154095
154340
  this.radiusArraySphere = new Array();
154096
154341
 
154097
154342
  this.axis = false; // used to turn on and off xyz axes
154343
+ this.unitcell = false; // used to turn on and off unit cell
154098
154344
 
154099
154345
  // pk
154100
154346
  //If its value is 1, selecting an atom will select the atom. If its value is 2, selecting an atom
@@ -154216,6 +154462,7 @@ class iCn3D {
154216
154462
  this.optsOri['clbonds'] = 'yes'; //yes, no
154217
154463
  this.optsOri['rotationcenter'] = 'molecule center'; //molecule center, pick center, display center
154218
154464
  this.optsOri['axis'] = 'no'; //yes, no
154465
+ this.optsOri['unitcell'] = 'no'; //yes, no
154219
154466
  this.optsOri['fog'] = 'no'; //yes, no
154220
154467
  this.optsOri['slab'] = 'no'; //yes, no
154221
154468
  this.optsOri['pk'] = 'residue'; //no, atom, residue, strand, chain
@@ -154633,7 +154880,7 @@ class iCn3DUI {
154633
154880
  //even when multiple iCn3D viewers are shown together.
154634
154881
  this.pre = this.cfg.divid + "_";
154635
154882
 
154636
- this.REVISION = '3.54.1';
154883
+ this.REVISION = '3.54.2';
154637
154884
 
154638
154885
  // In nodejs, iCn3D defines "window = {navigator: {}}", and added window = {navigator: {}, "__THREE__":"177"}
154639
154886
  this.bNode = (Object.keys(window).length < 3) ? true : false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icn3d",
3
- "version": "3.54.3",
3
+ "version": "3.54.4",
4
4
  "main": "./icn3d.js",
5
5
  "exports": {
6
6
  ".": {